summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gn/AUTHORS4
-rw-r--r--gn/README.md125
-rw-r--r--gn/base/bind.h457
-rw-r--r--gn/base/bind_internal.h912
-rw-r--r--gn/base/callback.h142
-rw-r--r--gn/base/callback_forward.h27
-rw-r--r--gn/base/callback_internal.cc93
-rw-r--r--gn/base/callback_internal.h172
-rw-r--r--gn/base/command_line.cc486
-rw-r--r--gn/base/command_line.h255
-rw-r--r--gn/base/compiler_specific.h231
-rw-r--r--gn/base/containers/flat_tree.h1004
-rw-r--r--gn/base/containers/span.h453
-rw-r--r--gn/base/containers/vector_buffer.h163
-rw-r--r--gn/base/environment.cc237
-rw-r--r--gn/base/environment.h86
-rw-r--r--gn/base/files/file.cc132
-rw-r--r--gn/base/files/file.h356
-rw-r--r--gn/base/files/file_enumerator_win.cc192
-rw-r--r--gn/base/files/file_path.cc669
-rw-r--r--gn/base/files/file_path.h426
-rw-r--r--gn/base/files/file_path_constants.cc25
-rw-r--r--gn/base/files/file_posix.cc434
-rw-r--r--gn/base/files/file_util.cc263
-rw-r--r--gn/base/files/file_util.h395
-rw-r--r--gn/base/files/file_util_posix.cc787
-rw-r--r--gn/base/files/file_util_win.cc697
-rw-r--r--gn/base/files/file_win.cc381
-rw-r--r--gn/base/files/platform_file.h43
-rw-r--r--gn/base/files/scoped_file.cc49
-rw-r--r--gn/base/gtest_prod_util.h16
-rw-r--r--gn/base/json/json_parser.cc747
-rw-r--r--gn/base/json/json_parser.h260
-rw-r--r--gn/base/json/json_reader.cc119
-rw-r--r--gn/base/json/json_reader.h134
-rw-r--r--gn/base/json/json_value_converter.cc36
-rw-r--r--gn/base/json/json_value_converter.h512
-rw-r--r--gn/base/json/json_writer.cc177
-rw-r--r--gn/base/json/json_writer.h74
-rw-r--r--gn/base/json/string_escape.cc167
-rw-r--r--gn/base/json/string_escape.h55
-rw-r--r--gn/base/logging.cc330
-rw-r--r--gn/base/logging.h956
-rw-r--r--gn/base/macros.h94
-rw-r--r--gn/base/md5.cc299
-rw-r--r--gn/base/md5.h76
-rw-r--r--gn/base/memory/raw_scoped_refptr_mismatch_checker.h52
-rw-r--r--gn/base/numerics/safe_math_shared_impl.h237
-rw-r--r--gn/base/optional.h922
-rw-r--r--gn/base/posix/safe_strerror.cc126
-rw-r--r--gn/base/stl_util.h406
-rw-r--r--gn/base/strings/char_traits.h92
-rw-r--r--gn/base/strings/string16.cc87
-rw-r--r--gn/base/strings/string16.h203
-rw-r--r--gn/base/strings/string_number_conversions.cc458
-rw-r--r--gn/base/strings/string_number_conversions.h152
-rw-r--r--gn/base/strings/string_piece.cc440
-rw-r--r--gn/base/strings/string_piece.h439
-rw-r--r--gn/base/strings/string_piece_forward.h24
-rw-r--r--gn/base/strings/string_split.cc268
-rw-r--r--gn/base/strings/string_split.h122
-rw-r--r--gn/base/strings/string_tokenizer.h251
-rw-r--r--gn/base/strings/string_util.cc1101
-rw-r--r--gn/base/strings/string_util.h455
-rw-r--r--gn/base/strings/string_util_constants.cc59
-rw-r--r--gn/base/strings/string_util_posix.h41
-rw-r--r--gn/base/strings/string_util_win.h48
-rw-r--r--gn/base/strings/stringprintf.cc190
-rw-r--r--gn/base/strings/stringprintf.h60
-rw-r--r--gn/base/strings/utf_offset_string_conversions.cc266
-rw-r--r--gn/base/strings/utf_offset_string_conversions.h111
-rw-r--r--gn/base/strings/utf_string_conversion_utils.cc153
-rw-r--r--gn/base/strings/utf_string_conversion_utils.h99
-rw-r--r--gn/base/strings/utf_string_conversions.cc333
-rw-r--r--gn/base/strings/utf_string_conversions.h48
-rw-r--r--gn/base/template_util.h156
-rw-r--r--gn/base/values.cc1297
-rw-r--r--gn/base/values.h757
-rw-r--r--gn/base/win/registry.cc613
-rw-r--r--gn/base/win/registry.h248
-rw-r--r--gn/base/win/scoped_handle.cc35
-rw-r--r--gn/base/win/scoped_handle.h172
-rw-r--r--gn/base/win/windows_types.h254
-rw-r--r--gn/build/build_aix.ninja.template9
-rw-r--r--gn/build/build_haiku.ninja.template13
-rw-r--r--gn/build/build_linux.ninja.template8
-rw-r--r--gn/build/build_mac.ninja.template8
-rw-r--r--gn/build/build_openbsd.ninja.template8
-rw-r--r--gn/build/build_win.ninja.template25
-rwxr-xr-xgn/build/gen.py757
-rw-r--r--gn/docs/cross_compiles.md9
-rw-r--r--gn/docs/language.md9
-rw-r--r--gn/docs/quick_start.md318
-rw-r--r--gn/docs/reference.md1089
-rw-r--r--gn/examples/ios/.gn2
-rw-r--r--gn/examples/ios/BUILD.gn10
-rw-r--r--gn/examples/ios/app/AppDelegate.h9
-rw-r--r--gn/examples/ios/app/AppDelegate.m32
-rw-r--r--gn/examples/ios/app/BUILD.gn67
-rw-r--r--gn/examples/ios/app/Bar.swift8
-rw-r--r--gn/examples/ios/app/Baz.swift2
-rw-r--r--gn/examples/ios/app/Foo-Bridging-Header.h0
-rw-r--r--gn/examples/ios/app/Foo.swift12
-rw-r--r--gn/examples/ios/app/FooWrapper.swift10
-rw-r--r--gn/examples/ios/app/SceneDelegate.h11
-rw-r--r--gn/examples/ios/app/SceneDelegate.m29
-rw-r--r--gn/examples/ios/app/ViewController.h9
-rw-r--r--gn/examples/ios/app/ViewController.m31
-rw-r--r--gn/examples/ios/app/main.m15
-rw-r--r--gn/examples/ios/app/resources/Info.plist64
-rw-r--r--gn/examples/ios/app/resources/LaunchScreen.storyboard25
-rw-r--r--gn/examples/ios/app/resources/Main.storyboard24
-rw-r--r--gn/examples/ios/build/BUILD.gn88
-rw-r--r--gn/examples/ios/build/BUILDCONFIG.gn43
-rw-r--r--gn/examples/ios/build/config/ios/BUILD.gn24
-rw-r--r--gn/examples/ios/build/config/ios/bundle_identifier_prefix.gni8
-rw-r--r--gn/examples/ios/build/config/ios/deployment_target.gni9
-rw-r--r--gn/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist12
-rw-r--r--gn/examples/ios/build/config/ios/resources/Info.plist22
-rw-r--r--gn/examples/ios/build/config/ios/resources/compiler-Info.plist33
-rw-r--r--gn/examples/ios/build/config/ios/scripts/compile_storyboard.py53
-rw-r--r--gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py119
-rw-r--r--gn/examples/ios/build/config/ios/scripts/generate_umbrella_header.py54
-rw-r--r--gn/examples/ios/build/config/ios/scripts/merge_plist.py134
-rw-r--r--gn/examples/ios/build/config/ios/scripts/sdk_info.py147
-rw-r--r--gn/examples/ios/build/config/ios/sdk_info.gni14
-rw-r--r--gn/examples/ios/build/config/ios/templates/ios_app_bundle.gni147
-rw-r--r--gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni121
-rw-r--r--gn/examples/ios/build/config/ios/templates/ios_framework_bundle.gni152
-rw-r--r--gn/examples/ios/build/config/ios/templates/merge_plist.gni85
-rw-r--r--gn/examples/ios/build/config/ios/templates/storyboards.gni31
-rwxr-xr-xgn/examples/ios/build/toolchain/apple/swiftc.py183
-rw-r--r--gn/examples/ios/build/toolchain/ios/BUILD.gn167
-rw-r--r--gn/examples/ios/build/toolchain/mac/BUILD.gn119
-rw-r--r--gn/examples/ios/host/BUILD.gn13
-rw-r--r--gn/examples/ios/host/main.cc71
-rw-r--r--gn/examples/ios/shared/BUILD.gn18
-rw-r--r--gn/examples/ios/shared/hello_shared.h13
-rw-r--r--gn/examples/ios/shared/hello_shared.m22
-rw-r--r--gn/examples/rust_example/BUILD.gn4
-rw-r--r--gn/examples/rust_example/README.txt4
-rw-r--r--gn/examples/rust_example/build/BUILD.gn36
-rw-r--r--gn/examples/rust_example/hello_world/bar/src/BUILD.gn1
-rw-r--r--gn/examples/rust_example/hello_world/foo/src/BUILD.gn9
-rw-r--r--gn/examples/rust_example/hello_world/src/BUILD.gn9
-rw-r--r--gn/examples/simple_build/BUILD.gn4
-rw-r--r--gn/examples/simple_build/build/BUILD.gn10
-rw-r--r--gn/examples/simple_build/build/BUILDCONFIG.gn3
-rw-r--r--gn/examples/simple_build/build/toolchain/BUILD.gn44
-rw-r--r--gn/examples/simple_build/tutorial/README.md4
-rw-r--r--gn/examples/simple_build/tutorial/tutorial.cc10
-rw-r--r--gn/infra/README.recipes.md22
-rw-r--r--gn/infra/config/recipes.cfg2
-rw-r--r--gn/infra/recipe_modules/macos_sdk/__init__.py4
-rw-r--r--gn/infra/recipe_modules/macos_sdk/api.py10
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json6
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.py1
-rw-r--r--gn/infra/recipe_modules/target/__init__.py7
-rw-r--r--gn/infra/recipe_modules/target/api.py98
-rw-r--r--gn/infra/recipe_modules/target/examples/full.expected/linux.json22
-rw-r--r--gn/infra/recipe_modules/target/examples/full.expected/mac.json22
-rw-r--r--gn/infra/recipe_modules/target/examples/full.expected/win.json22
-rw-r--r--gn/infra/recipe_modules/target/examples/full.py31
-rw-r--r--gn/infra/recipe_modules/windows_sdk/__init__.py2
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json4
-rw-r--r--gn/infra/recipes/gn.expected/ci_linux.json328
-rw-r--r--gn/infra/recipes/gn.expected/ci_mac.json285
-rw-r--r--gn/infra/recipes/gn.expected/ci_win.json79
-rw-r--r--gn/infra/recipes/gn.expected/cipd_exists.json391
-rw-r--r--gn/infra/recipes/gn.expected/cipd_register.json405
-rw-r--r--gn/infra/recipes/gn.expected/cq_linux.json270
-rw-r--r--gn/infra/recipes/gn.expected/cq_mac.json217
-rw-r--r--gn/infra/recipes/gn.expected/cq_win.json44
-rw-r--r--gn/infra/recipes/gn.py297
-rw-r--r--gn/misc/emacs/gn-mode.el192
-rwxr-xr-xgn/misc/help_as_html.py (renamed from gn/tools/gn/misc/help_as_html.py)0
-rw-r--r--gn/misc/tm/GN.tmLanguage102
-rw-r--r--gn/misc/tm/GN.tmPreferences (renamed from gn/tools/gn/misc/tm/GN.tmPreferences)0
-rw-r--r--gn/misc/vim/README.md65
-rw-r--r--gn/misc/vim/autoload/gn.vim (renamed from gn/tools/gn/misc/vim/autoload/gn.vim)0
-rw-r--r--gn/misc/vim/ftdetect/gnfiletype.vim (renamed from gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim)0
-rw-r--r--gn/misc/vim/ftplugin/gn.vim (renamed from gn/tools/gn/misc/vim/ftplugin/gn.vim)0
-rw-r--r--gn/misc/vim/gn-format.py62
-rw-r--r--gn/misc/vim/syntax/gn.vim86
-rw-r--r--gn/src/base/atomic_ref_count.h (renamed from gn/base/atomic_ref_count.h)0
-rw-r--r--gn/src/base/command_line.cc491
-rw-r--r--gn/src/base/command_line.h254
-rw-r--r--gn/src/base/compiler_specific.h76
-rw-r--r--gn/src/base/containers/circular_deque.h (renamed from gn/base/containers/circular_deque.h)0
-rw-r--r--gn/src/base/containers/flat_map.h (renamed from gn/base/containers/flat_map.h)0
-rw-r--r--gn/src/base/containers/flat_set.h (renamed from gn/base/containers/flat_set.h)0
-rw-r--r--gn/src/base/containers/flat_tree.h1004
-rw-r--r--gn/src/base/containers/queue.h (renamed from gn/base/containers/queue.h)0
-rw-r--r--gn/src/base/containers/span.h453
-rw-r--r--gn/src/base/containers/stack.h (renamed from gn/base/containers/stack.h)0
-rw-r--r--gn/src/base/containers/vector_buffer.h163
-rw-r--r--gn/src/base/environment.cc243
-rw-r--r--gn/src/base/environment.h86
-rw-r--r--gn/src/base/files/file.cc116
-rw-r--r--gn/src/base/files/file.h289
-rw-r--r--gn/src/base/files/file_enumerator.cc (renamed from gn/base/files/file_enumerator.cc)0
-rw-r--r--gn/src/base/files/file_enumerator.h (renamed from gn/base/files/file_enumerator.h)0
-rw-r--r--gn/src/base/files/file_enumerator_posix.cc (renamed from gn/base/files/file_enumerator_posix.cc)0
-rw-r--r--gn/src/base/files/file_enumerator_win.cc193
-rw-r--r--gn/src/base/files/file_path.cc639
-rw-r--r--gn/src/base/files/file_path.h394
-rw-r--r--gn/src/base/files/file_path_constants.cc27
-rw-r--r--gn/src/base/files/file_posix.cc392
-rw-r--r--gn/src/base/files/file_util.cc244
-rw-r--r--gn/src/base/files/file_util.h350
-rw-r--r--gn/src/base/files/file_util_linux.cc (renamed from gn/base/files/file_util_linux.cc)0
-rw-r--r--gn/src/base/files/file_util_posix.cc664
-rw-r--r--gn/src/base/files/file_util_win.cc635
-rw-r--r--gn/src/base/files/file_win.cc324
-rw-r--r--gn/src/base/files/platform_file.h43
-rw-r--r--gn/src/base/files/scoped_file.cc49
-rw-r--r--gn/src/base/files/scoped_file.h (renamed from gn/base/files/scoped_file.h)0
-rw-r--r--gn/src/base/files/scoped_temp_dir.cc (renamed from gn/base/files/scoped_temp_dir.cc)0
-rw-r--r--gn/src/base/files/scoped_temp_dir.h (renamed from gn/base/files/scoped_temp_dir.h)0
-rw-r--r--gn/src/base/gtest_prod_util.h12
-rw-r--r--gn/src/base/json/json_parser.cc747
-rw-r--r--gn/src/base/json/json_parser.h260
-rw-r--r--gn/src/base/json/json_reader.cc118
-rw-r--r--gn/src/base/json/json_reader.h133
-rw-r--r--gn/src/base/json/json_value_converter.cc36
-rw-r--r--gn/src/base/json/json_value_converter.h515
-rw-r--r--gn/src/base/json/json_writer.cc175
-rw-r--r--gn/src/base/json/json_writer.h67
-rw-r--r--gn/src/base/json/string_escape.cc156
-rw-r--r--gn/src/base/json/string_escape.h54
-rw-r--r--gn/src/base/logging.cc325
-rw-r--r--gn/src/base/logging.h922
-rw-r--r--gn/src/base/mac/bundle_locations.h (renamed from gn/base/mac/bundle_locations.h)0
-rw-r--r--gn/src/base/mac/mac_logging.h (renamed from gn/base/mac/mac_logging.h)0
-rw-r--r--gn/src/base/mac/mac_logging.mm (renamed from gn/base/mac/mac_logging.mm)0
-rw-r--r--gn/src/base/mac/scoped_cftyperef.h (renamed from gn/base/mac/scoped_cftyperef.h)0
-rw-r--r--gn/src/base/mac/scoped_typeref.h (renamed from gn/base/mac/scoped_typeref.h)0
-rw-r--r--gn/src/base/macros.h52
-rw-r--r--gn/src/base/md5.cc301
-rw-r--r--gn/src/base/md5.h77
-rw-r--r--gn/src/base/memory/free_deleter.h (renamed from gn/base/memory/free_deleter.h)0
-rw-r--r--gn/src/base/memory/ptr_util.h (renamed from gn/base/memory/ptr_util.h)0
-rw-r--r--gn/src/base/memory/raw_scoped_refptr_mismatch_checker.h52
-rw-r--r--gn/src/base/memory/ref_counted.cc (renamed from gn/base/memory/ref_counted.cc)0
-rw-r--r--gn/src/base/memory/ref_counted.h (renamed from gn/base/memory/ref_counted.h)0
-rw-r--r--gn/src/base/memory/scoped_policy.h (renamed from gn/base/memory/scoped_policy.h)0
-rw-r--r--gn/src/base/memory/scoped_refptr.h (renamed from gn/base/memory/scoped_refptr.h)0
-rw-r--r--gn/src/base/memory/weak_ptr.cc (renamed from gn/base/memory/weak_ptr.cc)0
-rw-r--r--gn/src/base/memory/weak_ptr.h (renamed from gn/base/memory/weak_ptr.h)0
-rw-r--r--gn/src/base/numerics/checked_math.h (renamed from gn/base/numerics/checked_math.h)0
-rw-r--r--gn/src/base/numerics/checked_math_impl.h (renamed from gn/base/numerics/checked_math_impl.h)0
-rw-r--r--gn/src/base/numerics/clamped_math.h (renamed from gn/base/numerics/clamped_math.h)0
-rw-r--r--gn/src/base/numerics/clamped_math_impl.h (renamed from gn/base/numerics/clamped_math_impl.h)0
-rw-r--r--gn/src/base/numerics/math_constants.h (renamed from gn/base/numerics/math_constants.h)0
-rw-r--r--gn/src/base/numerics/ranges.h (renamed from gn/base/numerics/ranges.h)0
-rw-r--r--gn/src/base/numerics/safe_conversions.h (renamed from gn/base/numerics/safe_conversions.h)0
-rw-r--r--gn/src/base/numerics/safe_conversions_impl.h (renamed from gn/base/numerics/safe_conversions_impl.h)0
-rw-r--r--gn/src/base/numerics/safe_math.h (renamed from gn/base/numerics/safe_math.h)0
-rw-r--r--gn/src/base/numerics/safe_math_clang_gcc_impl.h (renamed from gn/base/numerics/safe_math_clang_gcc_impl.h)0
-rw-r--r--gn/src/base/numerics/safe_math_shared_impl.h236
-rw-r--r--gn/src/base/posix/eintr_wrapper.h (renamed from gn/base/posix/eintr_wrapper.h)0
-rw-r--r--gn/src/base/posix/file_descriptor_shuffle.cc (renamed from gn/base/posix/file_descriptor_shuffle.cc)0
-rw-r--r--gn/src/base/posix/file_descriptor_shuffle.h (renamed from gn/base/posix/file_descriptor_shuffle.h)0
-rw-r--r--gn/src/base/posix/safe_strerror.cc126
-rw-r--r--gn/src/base/posix/safe_strerror.h (renamed from gn/base/posix/safe_strerror.h)0
-rw-r--r--gn/src/base/scoped_clear_errno.h (renamed from gn/base/scoped_clear_errno.h)0
-rw-r--r--gn/src/base/scoped_generic.h (renamed from gn/base/scoped_generic.h)0
-rw-r--r--gn/src/base/sha1.cc (renamed from gn/base/sha1.cc)0
-rw-r--r--gn/src/base/sha1.h (renamed from gn/base/sha1.h)0
-rw-r--r--gn/src/base/stl_util.h272
-rw-r--r--gn/src/base/strings/string_number_conversions.cc458
-rw-r--r--gn/src/base/strings/string_number_conversions.h131
-rw-r--r--gn/src/base/strings/string_split.cc260
-rw-r--r--gn/src/base/strings/string_split.h122
-rw-r--r--gn/src/base/strings/string_tokenizer.h250
-rw-r--r--gn/src/base/strings/string_util.cc1047
-rw-r--r--gn/src/base/strings/string_util.h424
-rw-r--r--gn/src/base/strings/string_util_constants.cc57
-rw-r--r--gn/src/base/strings/string_util_posix.h25
-rw-r--r--gn/src/base/strings/string_util_win.h29
-rw-r--r--gn/src/base/strings/stringize_macros.h (renamed from gn/base/strings/stringize_macros.h)0
-rw-r--r--gn/src/base/strings/stringprintf.cc143
-rw-r--r--gn/src/base/strings/stringprintf.h42
-rw-r--r--gn/src/base/strings/utf_offset_string_conversions.cc266
-rw-r--r--gn/src/base/strings/utf_offset_string_conversions.h111
-rw-r--r--gn/src/base/strings/utf_string_conversion_utils.cc132
-rw-r--r--gn/src/base/strings/utf_string_conversion_utils.h81
-rw-r--r--gn/src/base/strings/utf_string_conversions.cc195
-rw-r--r--gn/src/base/strings/utf_string_conversions.h30
-rw-r--r--gn/src/base/sys_byteorder.h (renamed from gn/base/sys_byteorder.h)0
-rw-r--r--gn/src/base/template_util.h42
-rw-r--r--gn/src/base/third_party/icu/LICENSE (renamed from gn/base/third_party/icu/LICENSE)0
-rw-r--r--gn/src/base/third_party/icu/README.chromium (renamed from gn/base/third_party/icu/README.chromium)0
-rw-r--r--gn/src/base/third_party/icu/icu_utf.cc (renamed from gn/base/third_party/icu/icu_utf.cc)0
-rw-r--r--gn/src/base/third_party/icu/icu_utf.h (renamed from gn/base/third_party/icu/icu_utf.h)0
-rw-r--r--gn/src/base/timer/elapsed_timer.cc (renamed from gn/base/timer/elapsed_timer.cc)0
-rw-r--r--gn/src/base/timer/elapsed_timer.h (renamed from gn/base/timer/elapsed_timer.h)0
-rw-r--r--gn/src/base/value_iterators.cc (renamed from gn/base/value_iterators.cc)0
-rw-r--r--gn/src/base/value_iterators.h (renamed from gn/base/value_iterators.h)0
-rw-r--r--gn/src/base/values.cc1309
-rw-r--r--gn/src/base/values.h763
-rw-r--r--gn/src/base/win/registry.cc623
-rw-r--r--gn/src/base/win/registry.h252
-rw-r--r--gn/src/base/win/scoped_handle.cc33
-rw-r--r--gn/src/base/win/scoped_handle.h172
-rw-r--r--gn/src/base/win/scoped_process_information.cc (renamed from gn/base/win/scoped_process_information.cc)0
-rw-r--r--gn/src/base/win/scoped_process_information.h (renamed from gn/base/win/scoped_process_information.h)0
-rw-r--r--gn/src/base/win/win_util.h30
-rw-r--r--gn/src/gn/action_target_generator.cc223
-rw-r--r--gn/src/gn/action_target_generator.h41
-rw-r--r--gn/src/gn/action_target_generator_unittest.cc122
-rw-r--r--gn/src/gn/action_values.cc31
-rw-r--r--gn/src/gn/action_values.h70
-rw-r--r--gn/src/gn/analyzer.cc492
-rw-r--r--gn/src/gn/analyzer.h104
-rw-r--r--gn/src/gn/analyzer_unittest.cc755
-rw-r--r--gn/src/gn/args.cc437
-rw-r--r--gn/src/gn/args.h147
-rw-r--r--gn/src/gn/args_unittest.cc81
-rw-r--r--gn/src/gn/binary_target_generator.cc248
-rw-r--r--gn/src/gn/binary_target_generator.h41
-rw-r--r--gn/src/gn/build_settings.cc76
-rw-r--r--gn/src/gn/build_settings.h152
-rw-r--r--gn/src/gn/builder.cc602
-rw-r--r--gn/src/gn/builder.h146
-rw-r--r--gn/src/gn/builder_record.cc67
-rw-r--r--gn/src/gn/builder_record.h110
-rw-r--r--gn/src/gn/builder_unittest.cc247
-rw-r--r--gn/src/gn/bundle_data.cc195
-rw-r--r--gn/src/gn/bundle_data.h214
-rw-r--r--gn/src/gn/bundle_data_target_generator.cc95
-rw-r--r--gn/src/gn/bundle_data_target_generator.h32
-rw-r--r--gn/src/gn/bundle_file_rule.cc110
-rw-r--r--gn/src/gn/bundle_file_rule.h56
-rw-r--r--gn/src/gn/c_include_iterator.cc176
-rw-r--r--gn/src/gn/c_include_iterator.h61
-rw-r--r--gn/src/gn/c_include_iterator_unittest.cc181
-rw-r--r--gn/src/gn/c_substitution_type.cc140
-rw-r--r--gn/src/gn/c_substitution_type.h57
-rw-r--r--gn/src/gn/c_tool.cc266
-rw-r--r--gn/src/gn/c_tool.h126
-rw-r--r--gn/src/gn/command_analyze.cc148
-rw-r--r--gn/src/gn/command_args.cc511
-rw-r--r--gn/src/gn/command_check.cc285
-rw-r--r--gn/src/gn/command_clean.cc139
-rw-r--r--gn/src/gn/command_clean_stale.cc98
-rw-r--r--gn/src/gn/command_desc.cc716
-rw-r--r--gn/src/gn/command_format.cc1437
-rw-r--r--gn/src/gn/command_format.h36
-rw-r--r--gn/src/gn/command_format_unittest.cc138
-rw-r--r--gn/src/gn/command_gen.cc695
-rw-r--r--gn/src/gn/command_help.cc376
-rw-r--r--gn/src/gn/command_ls.cc104
-rw-r--r--gn/src/gn/command_meta.cc170
-rw-r--r--gn/src/gn/command_outputs.cc155
-rw-r--r--gn/src/gn/command_path.cc413
-rw-r--r--gn/src/gn/command_refs.cc453
-rw-r--r--gn/src/gn/commands.cc644
-rw-r--r--gn/src/gn/commands.h247
-rw-r--r--gn/src/gn/commands_unittest.cc35
-rw-r--r--gn/src/gn/compile_commands_writer.cc365
-rw-r--r--gn/src/gn/compile_commands_writer.h42
-rw-r--r--gn/src/gn/compile_commands_writer_unittest.cc640
-rw-r--r--gn/src/gn/config.cc50
-rw-r--r--gn/src/gn/config.h69
-rw-r--r--gn/src/gn/config_unittest.cc85
-rw-r--r--gn/src/gn/config_values.cc52
-rw-r--r--gn/src/gn/config_values.h108
-rw-r--r--gn/src/gn/config_values_extractors.cc34
-rw-r--r--gn/src/gn/config_values_extractors.h102
-rw-r--r--gn/src/gn/config_values_extractors_unittest.cc140
-rw-r--r--gn/src/gn/config_values_generator.cc170
-rw-r--r--gn/src/gn/config_values_generator.h46
-rw-r--r--gn/src/gn/copy_target_generator.cc44
-rw-r--r--gn/src/gn/copy_target_generator.h27
-rw-r--r--gn/src/gn/create_bundle_target_generator.cc318
-rw-r--r--gn/src/gn/create_bundle_target_generator.h46
-rw-r--r--gn/src/gn/deps_iterator.cc53
-rw-r--r--gn/src/gn/deps_iterator.h72
-rw-r--r--gn/src/gn/desc_builder.cc891
-rw-r--r--gn/src/gn/desc_builder.h27
-rw-r--r--gn/src/gn/eclipse_writer.cc171
-rw-r--r--gn/src/gn/eclipse_writer.h (renamed from gn/tools/gn/eclipse_writer.h)0
-rw-r--r--gn/src/gn/err.cc200
-rw-r--r--gn/src/gn/err.h105
-rw-r--r--gn/src/gn/escape.cc312
-rw-r--r--gn/src/gn/escape.h90
-rw-r--r--gn/src/gn/escape_unittest.cc112
-rw-r--r--gn/src/gn/exec_process.cc289
-rw-r--r--gn/src/gn/exec_process.h35
-rw-r--r--gn/src/gn/exec_process_unittest.cc123
-rw-r--r--gn/src/gn/file_writer.cc108
-rw-r--r--gn/src/gn/file_writer.h74
-rw-r--r--gn/src/gn/file_writer_unittest.cc44
-rw-r--r--gn/src/gn/filesystem_utils.cc1077
-rw-r--r--gn/src/gn/filesystem_utils.h305
-rw-r--r--gn/src/gn/filesystem_utils_unittest.cc868
-rw-r--r--gn/src/gn/format_test_data/001.gn (renamed from gn/tools/gn/format_test_data/001.gn)0
-rw-r--r--gn/src/gn/format_test_data/001.golden (renamed from gn/tools/gn/format_test_data/001.golden)0
-rw-r--r--gn/src/gn/format_test_data/002.gn (renamed from gn/tools/gn/format_test_data/002.gn)0
-rw-r--r--gn/src/gn/format_test_data/002.golden (renamed from gn/tools/gn/format_test_data/002.golden)0
-rw-r--r--gn/src/gn/format_test_data/003.gn (renamed from gn/tools/gn/format_test_data/003.gn)0
-rw-r--r--gn/src/gn/format_test_data/003.golden8
-rw-r--r--gn/src/gn/format_test_data/004.gn (renamed from gn/tools/gn/format_test_data/004.gn)0
-rw-r--r--gn/src/gn/format_test_data/004.golden11
-rw-r--r--gn/src/gn/format_test_data/005.gn (renamed from gn/tools/gn/format_test_data/005.gn)0
-rw-r--r--gn/src/gn/format_test_data/005.golden (renamed from gn/tools/gn/format_test_data/005.golden)0
-rw-r--r--gn/src/gn/format_test_data/006.gn (renamed from gn/tools/gn/format_test_data/006.gn)0
-rw-r--r--gn/src/gn/format_test_data/006.golden (renamed from gn/tools/gn/format_test_data/006.golden)0
-rw-r--r--gn/src/gn/format_test_data/007.gn (renamed from gn/tools/gn/format_test_data/007.gn)0
-rw-r--r--gn/src/gn/format_test_data/007.golden9
-rw-r--r--gn/src/gn/format_test_data/008.gn (renamed from gn/tools/gn/format_test_data/008.gn)0
-rw-r--r--gn/src/gn/format_test_data/008.golden3
-rw-r--r--gn/src/gn/format_test_data/009.gn (renamed from gn/tools/gn/format_test_data/009.gn)0
-rw-r--r--gn/src/gn/format_test_data/009.golden5
-rw-r--r--gn/src/gn/format_test_data/010.gn (renamed from gn/tools/gn/format_test_data/010.gn)0
-rw-r--r--gn/src/gn/format_test_data/010.golden5
-rw-r--r--gn/src/gn/format_test_data/011.gn (renamed from gn/tools/gn/format_test_data/011.gn)0
-rw-r--r--gn/src/gn/format_test_data/011.golden7
-rw-r--r--gn/src/gn/format_test_data/012.gn (renamed from gn/tools/gn/format_test_data/012.gn)0
-rw-r--r--gn/src/gn/format_test_data/012.golden16
-rw-r--r--gn/src/gn/format_test_data/013.gn (renamed from gn/tools/gn/format_test_data/013.gn)0
-rw-r--r--gn/src/gn/format_test_data/013.golden (renamed from gn/tools/gn/format_test_data/013.golden)0
-rw-r--r--gn/src/gn/format_test_data/014.gn (renamed from gn/tools/gn/format_test_data/014.gn)0
-rw-r--r--gn/src/gn/format_test_data/014.golden (renamed from gn/tools/gn/format_test_data/014.golden)0
-rw-r--r--gn/src/gn/format_test_data/015.gn (renamed from gn/tools/gn/format_test_data/015.gn)0
-rw-r--r--gn/src/gn/format_test_data/015.golden4
-rw-r--r--gn/src/gn/format_test_data/016.gn (renamed from gn/tools/gn/format_test_data/016.gn)0
-rw-r--r--gn/src/gn/format_test_data/016.golden (renamed from gn/tools/gn/format_test_data/016.golden)0
-rw-r--r--gn/src/gn/format_test_data/017.gn (renamed from gn/tools/gn/format_test_data/017.gn)0
-rw-r--r--gn/src/gn/format_test_data/017.golden (renamed from gn/tools/gn/format_test_data/017.golden)0
-rw-r--r--gn/src/gn/format_test_data/018.gn (renamed from gn/tools/gn/format_test_data/018.gn)0
-rw-r--r--gn/src/gn/format_test_data/018.golden (renamed from gn/tools/gn/format_test_data/018.golden)0
-rw-r--r--gn/src/gn/format_test_data/019.gn23
-rw-r--r--gn/src/gn/format_test_data/019.golden23
-rw-r--r--gn/src/gn/format_test_data/020.gn (renamed from gn/tools/gn/format_test_data/020.gn)0
-rw-r--r--gn/src/gn/format_test_data/020.golden (renamed from gn/tools/gn/format_test_data/020.golden)0
-rw-r--r--gn/src/gn/format_test_data/021.gn (renamed from gn/tools/gn/format_test_data/021.gn)0
-rw-r--r--gn/src/gn/format_test_data/021.golden (renamed from gn/tools/gn/format_test_data/021.golden)0
-rw-r--r--gn/src/gn/format_test_data/022.gn (renamed from gn/tools/gn/format_test_data/022.gn)0
-rw-r--r--gn/src/gn/format_test_data/022.golden (renamed from gn/tools/gn/format_test_data/022.golden)0
-rw-r--r--gn/src/gn/format_test_data/023.gn (renamed from gn/tools/gn/format_test_data/023.gn)0
-rw-r--r--gn/src/gn/format_test_data/023.golden (renamed from gn/tools/gn/format_test_data/023.golden)0
-rw-r--r--gn/src/gn/format_test_data/024.gn (renamed from gn/tools/gn/format_test_data/024.gn)0
-rw-r--r--gn/src/gn/format_test_data/024.golden (renamed from gn/tools/gn/format_test_data/024.golden)0
-rw-r--r--gn/src/gn/format_test_data/025.gn (renamed from gn/tools/gn/format_test_data/025.gn)0
-rw-r--r--gn/src/gn/format_test_data/025.golden (renamed from gn/tools/gn/format_test_data/025.golden)0
-rw-r--r--gn/src/gn/format_test_data/026.gn (renamed from gn/tools/gn/format_test_data/026.gn)0
-rw-r--r--gn/src/gn/format_test_data/026.golden (renamed from gn/tools/gn/format_test_data/026.golden)0
-rw-r--r--gn/src/gn/format_test_data/027.gn (renamed from gn/tools/gn/format_test_data/027.gn)0
-rw-r--r--gn/src/gn/format_test_data/027.golden (renamed from gn/tools/gn/format_test_data/027.golden)0
-rw-r--r--gn/src/gn/format_test_data/028.gn (renamed from gn/tools/gn/format_test_data/028.gn)0
-rw-r--r--gn/src/gn/format_test_data/028.golden (renamed from gn/tools/gn/format_test_data/028.golden)0
-rw-r--r--gn/src/gn/format_test_data/029.gn (renamed from gn/tools/gn/format_test_data/029.gn)0
-rw-r--r--gn/src/gn/format_test_data/029.golden (renamed from gn/tools/gn/format_test_data/029.golden)0
-rw-r--r--gn/src/gn/format_test_data/030.gn (renamed from gn/tools/gn/format_test_data/030.gn)0
-rw-r--r--gn/src/gn/format_test_data/030.golden (renamed from gn/tools/gn/format_test_data/030.golden)0
-rw-r--r--gn/src/gn/format_test_data/031.gn (renamed from gn/tools/gn/format_test_data/031.gn)0
-rw-r--r--gn/src/gn/format_test_data/031.golden (renamed from gn/tools/gn/format_test_data/031.golden)0
-rw-r--r--gn/src/gn/format_test_data/032.gn (renamed from gn/tools/gn/format_test_data/032.gn)0
-rw-r--r--gn/src/gn/format_test_data/032.golden (renamed from gn/tools/gn/format_test_data/032.golden)0
-rw-r--r--gn/src/gn/format_test_data/033.gn (renamed from gn/tools/gn/format_test_data/033.gn)0
-rw-r--r--gn/src/gn/format_test_data/033.golden (renamed from gn/tools/gn/format_test_data/033.golden)0
-rw-r--r--gn/src/gn/format_test_data/034.gn (renamed from gn/tools/gn/format_test_data/034.gn)0
-rw-r--r--gn/src/gn/format_test_data/035.gn (renamed from gn/tools/gn/format_test_data/035.gn)0
-rw-r--r--gn/src/gn/format_test_data/035.golden (renamed from gn/tools/gn/format_test_data/035.golden)0
-rw-r--r--gn/src/gn/format_test_data/036.gn (renamed from gn/tools/gn/format_test_data/036.gn)0
-rw-r--r--gn/src/gn/format_test_data/036.golden (renamed from gn/tools/gn/format_test_data/036.golden)0
-rw-r--r--gn/src/gn/format_test_data/037.gn (renamed from gn/tools/gn/format_test_data/037.gn)0
-rw-r--r--gn/src/gn/format_test_data/037.golden (renamed from gn/tools/gn/format_test_data/037.golden)0
-rw-r--r--gn/src/gn/format_test_data/038.gn (renamed from gn/tools/gn/format_test_data/038.gn)0
-rw-r--r--gn/src/gn/format_test_data/038.golden (renamed from gn/tools/gn/format_test_data/038.golden)0
-rw-r--r--gn/src/gn/format_test_data/039.gn (renamed from gn/tools/gn/format_test_data/039.gn)0
-rw-r--r--gn/src/gn/format_test_data/039.golden (renamed from gn/tools/gn/format_test_data/039.golden)0
-rw-r--r--gn/src/gn/format_test_data/040.gn9
-rw-r--r--gn/src/gn/format_test_data/040.golden10
-rw-r--r--gn/src/gn/format_test_data/041.gn (renamed from gn/tools/gn/format_test_data/041.gn)0
-rw-r--r--gn/src/gn/format_test_data/041.golden (renamed from gn/tools/gn/format_test_data/041.golden)0
-rw-r--r--gn/src/gn/format_test_data/042.gn (renamed from gn/tools/gn/format_test_data/042.gn)0
-rw-r--r--gn/src/gn/format_test_data/042.golden96
-rw-r--r--gn/src/gn/format_test_data/043.gn (renamed from gn/tools/gn/format_test_data/043.gn)0
-rw-r--r--gn/src/gn/format_test_data/043.golden (renamed from gn/tools/gn/format_test_data/043.golden)0
-rw-r--r--gn/src/gn/format_test_data/044.gn (renamed from gn/tools/gn/format_test_data/044.golden)0
-rw-r--r--gn/src/gn/format_test_data/044.golden11
-rw-r--r--gn/src/gn/format_test_data/045.gn (renamed from gn/tools/gn/format_test_data/045.gn)0
-rw-r--r--gn/src/gn/format_test_data/045.golden (renamed from gn/tools/gn/format_test_data/045.golden)0
-rw-r--r--gn/src/gn/format_test_data/046.gn (renamed from gn/tools/gn/format_test_data/046.gn)0
-rw-r--r--gn/src/gn/format_test_data/046.golden (renamed from gn/tools/gn/format_test_data/046.golden)0
-rw-r--r--gn/src/gn/format_test_data/047.gn (renamed from gn/tools/gn/format_test_data/047.gn)0
-rw-r--r--gn/src/gn/format_test_data/047.golden (renamed from gn/tools/gn/format_test_data/047.golden)0
-rw-r--r--gn/src/gn/format_test_data/048.gn19
-rw-r--r--gn/src/gn/format_test_data/048.golden19
-rw-r--r--gn/src/gn/format_test_data/049.gn (renamed from gn/tools/gn/format_test_data/049.gn)0
-rw-r--r--gn/src/gn/format_test_data/050.gn (renamed from gn/tools/gn/format_test_data/050.gn)0
-rw-r--r--gn/src/gn/format_test_data/050.golden (renamed from gn/tools/gn/format_test_data/050.golden)0
-rw-r--r--gn/src/gn/format_test_data/051.gn (renamed from gn/tools/gn/format_test_data/051.gn)0
-rw-r--r--gn/src/gn/format_test_data/051.golden (renamed from gn/tools/gn/format_test_data/051.golden)0
-rw-r--r--gn/src/gn/format_test_data/052.gn (renamed from gn/tools/gn/format_test_data/052.gn)0
-rw-r--r--gn/src/gn/format_test_data/052.golden (renamed from gn/tools/gn/format_test_data/052.golden)0
-rw-r--r--gn/src/gn/format_test_data/053.gn (renamed from gn/tools/gn/format_test_data/053.gn)0
-rw-r--r--gn/src/gn/format_test_data/053.golden (renamed from gn/tools/gn/format_test_data/053.golden)0
-rw-r--r--gn/src/gn/format_test_data/054.gn (renamed from gn/tools/gn/format_test_data/054.gn)0
-rw-r--r--gn/src/gn/format_test_data/054.golden (renamed from gn/tools/gn/format_test_data/054.golden)0
-rw-r--r--gn/src/gn/format_test_data/055.gn (renamed from gn/tools/gn/format_test_data/055.gn)0
-rw-r--r--gn/src/gn/format_test_data/055.golden (renamed from gn/tools/gn/format_test_data/055.golden)0
-rw-r--r--gn/src/gn/format_test_data/056.gn (renamed from gn/tools/gn/format_test_data/056.gn)0
-rw-r--r--gn/src/gn/format_test_data/056.golden (renamed from gn/tools/gn/format_test_data/056.golden)0
-rw-r--r--gn/src/gn/format_test_data/057.gn (renamed from gn/tools/gn/format_test_data/057.gn)0
-rw-r--r--gn/src/gn/format_test_data/057.golden (renamed from gn/tools/gn/format_test_data/057.golden)0
-rw-r--r--gn/src/gn/format_test_data/058.gn (renamed from gn/tools/gn/format_test_data/058.gn)0
-rw-r--r--gn/src/gn/format_test_data/058.golden (renamed from gn/tools/gn/format_test_data/058.golden)0
-rw-r--r--gn/src/gn/format_test_data/059.gn (renamed from gn/tools/gn/format_test_data/059.gn)0
-rw-r--r--gn/src/gn/format_test_data/059.golden (renamed from gn/tools/gn/format_test_data/059.golden)0
-rw-r--r--gn/src/gn/format_test_data/060.gn (renamed from gn/tools/gn/format_test_data/060.gn)0
-rw-r--r--gn/src/gn/format_test_data/060.golden (renamed from gn/tools/gn/format_test_data/060.golden)0
-rw-r--r--gn/src/gn/format_test_data/061.gn (renamed from gn/tools/gn/format_test_data/061.gn)0
-rw-r--r--gn/src/gn/format_test_data/061.golden (renamed from gn/tools/gn/format_test_data/061.golden)0
-rw-r--r--gn/src/gn/format_test_data/062.gn128
-rw-r--r--gn/src/gn/format_test_data/062.golden136
-rw-r--r--gn/src/gn/format_test_data/063.gn50
-rw-r--r--gn/src/gn/format_test_data/063.golden50
-rw-r--r--gn/src/gn/format_test_data/064.gn (renamed from gn/tools/gn/format_test_data/064.gn)0
-rw-r--r--gn/src/gn/format_test_data/064.golden3
-rw-r--r--gn/src/gn/format_test_data/065.gn (renamed from gn/tools/gn/format_test_data/065.gn)0
-rw-r--r--gn/src/gn/format_test_data/065.golden (renamed from gn/tools/gn/format_test_data/065.golden)0
-rw-r--r--gn/src/gn/format_test_data/066.gn (renamed from gn/tools/gn/format_test_data/066.gn)0
-rw-r--r--gn/src/gn/format_test_data/066.golden28
-rw-r--r--gn/src/gn/format_test_data/067.gn (renamed from gn/tools/gn/format_test_data/067.gn)0
-rw-r--r--gn/src/gn/format_test_data/067.golden (renamed from gn/tools/gn/format_test_data/067.golden)0
-rw-r--r--gn/src/gn/format_test_data/068.gn (renamed from gn/tools/gn/format_test_data/068.gn)0
-rw-r--r--gn/src/gn/format_test_data/068.golden (renamed from gn/tools/gn/format_test_data/068.golden)0
-rw-r--r--gn/src/gn/format_test_data/069.gn (renamed from gn/tools/gn/format_test_data/069.gn)0
-rw-r--r--gn/src/gn/format_test_data/069.golden (renamed from gn/tools/gn/format_test_data/069.golden)0
-rw-r--r--gn/src/gn/format_test_data/070.gn (renamed from gn/tools/gn/format_test_data/070.gn)0
-rw-r--r--gn/src/gn/format_test_data/070.golden (renamed from gn/tools/gn/format_test_data/070.golden)0
-rw-r--r--gn/src/gn/format_test_data/071.gn (renamed from gn/tools/gn/format_test_data/071.gn)0
-rw-r--r--gn/src/gn/format_test_data/071.golden (renamed from gn/tools/gn/format_test_data/071.golden)0
-rw-r--r--gn/src/gn/format_test_data/072.gn (renamed from gn/tools/gn/format_test_data/072.gn)0
-rw-r--r--gn/src/gn/format_test_data/072.golden (renamed from gn/tools/gn/format_test_data/072.golden)0
-rw-r--r--gn/src/gn/format_test_data/073.gn (renamed from gn/tools/gn/format_test_data/073.gn)0
-rw-r--r--gn/src/gn/format_test_data/073.golden (renamed from gn/tools/gn/format_test_data/073.golden)0
-rw-r--r--gn/src/gn/format_test_data/074.gn (renamed from gn/tools/gn/format_test_data/074.gn)0
-rw-r--r--gn/src/gn/format_test_data/074.golden (renamed from gn/tools/gn/format_test_data/074.golden)0
-rw-r--r--gn/src/gn/format_test_data/075.gn (renamed from gn/tools/gn/format_test_data/075.gn)0
-rw-r--r--gn/src/gn/format_test_data/075.golden (renamed from gn/tools/gn/format_test_data/075.golden)0
-rw-r--r--gn/src/gn/format_test_data/076.gn12
-rw-r--r--gn/src/gn/format_test_data/076.golden14
-rw-r--r--gn/src/gn/format_test_data/077.gn6
-rw-r--r--gn/src/gn/format_test_data/077.golden6
-rw-r--r--gn/src/gn/format_test_data/078.gn15
-rw-r--r--gn/src/gn/format_test_data/078.golden16
-rw-r--r--gn/src/gn/format_test_data/079.gn14
-rw-r--r--gn/src/gn/format_test_data/079.golden18
-rw-r--r--gn/src/gn/format_test_data/080.gn33
-rw-r--r--gn/src/gn/format_test_data/080.golden34
-rw-r--r--gn/src/gn/format_test_data/081.gn18
-rw-r--r--gn/src/gn/format_test_data/081.golden21
-rw-r--r--gn/src/gn/format_test_data/082.gn34
-rw-r--r--gn/src/gn/format_test_data/082.golden34
-rw-r--r--gn/src/gn/format_test_data/083.gn7
-rw-r--r--gn/src/gn/format_test_data/083.golden7
-rw-r--r--gn/src/gn/frameworks_utils.cc25
-rw-r--r--gn/src/gn/frameworks_utils.h15
-rw-r--r--gn/src/gn/frameworks_utils_unittest.cc17
-rw-r--r--gn/src/gn/function_exec_script.cc278
-rw-r--r--gn/src/gn/function_filter.cc124
-rw-r--r--gn/src/gn/function_filter_unittest.cc244
-rw-r--r--gn/src/gn/function_foreach.cc112
-rw-r--r--gn/src/gn/function_foreach_unittest.cc100
-rw-r--r--gn/src/gn/function_forward_variables_from.cc246
-rw-r--r--gn/src/gn/function_forward_variables_from_unittest.cc244
-rw-r--r--gn/src/gn/function_get_label_info.cc144
-rw-r--r--gn/src/gn/function_get_label_info_unittest.cc107
-rw-r--r--gn/src/gn/function_get_path_info.cc251
-rw-r--r--gn/src/gn/function_get_path_info_unittest.cc120
-rw-r--r--gn/src/gn/function_get_target_outputs.cc139
-rw-r--r--gn/src/gn/function_get_target_outputs_unittest.cc107
-rw-r--r--gn/src/gn/function_process_file_template.cc115
-rw-r--r--gn/src/gn/function_process_file_template_unittest.cc64
-rw-r--r--gn/src/gn/function_read_file.cc78
-rw-r--r--gn/src/gn/function_rebase_path.cc288
-rw-r--r--gn/src/gn/function_rebase_path_unittest.cc183
-rw-r--r--gn/src/gn/function_set_default_toolchain.cc88
-rw-r--r--gn/src/gn/function_set_defaults.cc76
-rw-r--r--gn/src/gn/function_template.cc226
-rw-r--r--gn/src/gn/function_template_unittest.cc29
-rw-r--r--gn/src/gn/function_toolchain.cc928
-rw-r--r--gn/src/gn/function_toolchain_unittest.cc159
-rw-r--r--gn/src/gn/function_write_file.cc106
-rw-r--r--gn/src/gn/function_write_file_unittest.cc111
-rw-r--r--gn/src/gn/functions.cc1498
-rw-r--r--gn/src/gn/functions.h554
-rw-r--r--gn/src/gn/functions_target.cc1015
-rw-r--r--gn/src/gn/functions_target_rust_unittest.cc324
-rw-r--r--gn/src/gn/functions_target_unittest.cc206
-rw-r--r--gn/src/gn/functions_unittest.cc459
-rw-r--r--gn/src/gn/general_tool.cc51
-rw-r--r--gn/src/gn/general_tool.h46
-rw-r--r--gn/src/gn/generated_file_target_generator.cc165
-rw-r--r--gn/src/gn/generated_file_target_generator.h49
-rw-r--r--gn/src/gn/gn_main.cc85
-rw-r--r--gn/src/gn/group_target_generator.cc23
-rw-r--r--gn/src/gn/group_target_generator.h27
-rw-r--r--gn/src/gn/hash_table_base.h503
-rw-r--r--gn/src/gn/hash_table_base_unittest.cc356
-rw-r--r--gn/src/gn/header_checker.cc642
-rw-r--r--gn/src/gn/header_checker.h208
-rw-r--r--gn/src/gn/header_checker_unittest.cc427
-rw-r--r--gn/src/gn/import_manager.cc162
-rw-r--r--gn/src/gn/import_manager.h54
-rw-r--r--gn/src/gn/inherited_libraries.cc74
-rw-r--r--gn/src/gn/inherited_libraries.h71
-rw-r--r--gn/src/gn/inherited_libraries_unittest.cc135
-rw-r--r--gn/src/gn/input_conversion.cc359
-rw-r--r--gn/src/gn/input_conversion.h (renamed from gn/tools/gn/input_conversion.h)0
-rw-r--r--gn/src/gn/input_conversion_unittest.cc275
-rw-r--r--gn/src/gn/input_file.cc26
-rw-r--r--gn/src/gn/input_file.h65
-rw-r--r--gn/src/gn/input_file_manager.cc341
-rw-r--r--gn/src/gn/input_file_manager.h175
-rw-r--r--gn/src/gn/item.cc60
-rw-r--r--gn/src/gn/item.h80
-rw-r--r--gn/src/gn/json_project_writer.cc514
-rw-r--r--gn/src/gn/json_project_writer.h39
-rw-r--r--gn/src/gn/json_project_writer_unittest.cc719
-rw-r--r--gn/src/gn/label.cc331
-rw-r--r--gn/src/gn/label.h143
-rw-r--r--gn/src/gn/label_pattern.cc275
-rw-r--r--gn/src/gn/label_pattern.h82
-rw-r--r--gn/src/gn/label_pattern_unittest.cc103
-rw-r--r--gn/src/gn/label_ptr.h79
-rw-r--r--gn/src/gn/label_unittest.cc140
-rw-r--r--gn/src/gn/lib_file.cc23
-rw-r--r--gn/src/gn/lib_file.h54
-rw-r--r--gn/src/gn/loader.cc448
-rw-r--r--gn/src/gn/loader.h187
-rw-r--r--gn/src/gn/loader_unittest.cc393
-rw-r--r--gn/src/gn/location.cc69
-rw-r--r--gn/src/gn/location.h (renamed from gn/tools/gn/location.h)0
-rw-r--r--gn/src/gn/metadata.cc273
-rw-r--r--gn/src/gn/metadata.h90
-rw-r--r--gn/src/gn/metadata_unittest.cc233
-rw-r--r--gn/src/gn/metadata_walk.cc24
-rw-r--r--gn/src/gn/metadata_walk.h26
-rw-r--r--gn/src/gn/metadata_walk_unittest.cc211
-rw-r--r--gn/src/gn/ninja_action_target_writer.cc250
-rw-r--r--gn/src/gn/ninja_action_target_writer.h62
-rw-r--r--gn/src/gn/ninja_action_target_writer_unittest.cc488
-rw-r--r--gn/src/gn/ninja_binary_target_writer.cc391
-rw-r--r--gn/src/gn/ninja_binary_target_writer.h88
-rw-r--r--gn/src/gn/ninja_binary_target_writer_unittest.cc183
-rw-r--r--gn/src/gn/ninja_build_writer.cc642
-rw-r--r--gn/src/gn/ninja_build_writer.h81
-rw-r--r--gn/src/gn/ninja_build_writer_unittest.cc255
-rw-r--r--gn/src/gn/ninja_bundle_data_target_writer.cc33
-rw-r--r--gn/src/gn/ninja_bundle_data_target_writer.h23
-rw-r--r--gn/src/gn/ninja_bundle_data_target_writer_unittest.cc56
-rw-r--r--gn/src/gn/ninja_c_binary_target_writer.cc909
-rw-r--r--gn/src/gn/ninja_c_binary_target_writer.h112
-rw-r--r--gn/src/gn/ninja_c_binary_target_writer_unittest.cc1922
-rw-r--r--gn/src/gn/ninja_copy_target_writer.cc124
-rw-r--r--gn/src/gn/ninja_copy_target_writer.h27
-rw-r--r--gn/src/gn/ninja_copy_target_writer_unittest.cc126
-rw-r--r--gn/src/gn/ninja_create_bundle_target_writer.cc373
-rw-r--r--gn/src/gn/ninja_create_bundle_target_writer.h71
-rw-r--r--gn/src/gn/ninja_create_bundle_target_writer_unittest.cc487
-rw-r--r--gn/src/gn/ninja_generated_file_target_writer.cc97
-rw-r--r--gn/src/gn/ninja_generated_file_target_writer.h25
-rw-r--r--gn/src/gn/ninja_generated_file_target_writer_unittest.cc69
-rw-r--r--gn/src/gn/ninja_group_target_writer.cc37
-rw-r--r--gn/src/gn/ninja_group_target_writer.h23
-rw-r--r--gn/src/gn/ninja_group_target_writer_unittest.cc60
-rw-r--r--gn/src/gn/ninja_rust_binary_target_writer.cc323
-rw-r--r--gn/src/gn/ninja_rust_binary_target_writer.h42
-rw-r--r--gn/src/gn/ninja_rust_binary_target_writer_unittest.cc892
-rw-r--r--gn/src/gn/ninja_target_command_util.cc177
-rw-r--r--gn/src/gn/ninja_target_command_util.h114
-rw-r--r--gn/src/gn/ninja_target_command_util_unittest.cc96
-rw-r--r--gn/src/gn/ninja_target_writer.cc341
-rw-r--r--gn/src/gn/ninja_target_writer.h72
-rw-r--r--gn/src/gn/ninja_target_writer_unittest.cc163
-rw-r--r--gn/src/gn/ninja_toolchain_writer.cc141
-rw-r--r--gn/src/gn/ninja_toolchain_writer.h61
-rw-r--r--gn/src/gn/ninja_toolchain_writer_unittest.cc39
-rw-r--r--gn/src/gn/ninja_tools.cc82
-rw-r--r--gn/src/gn/ninja_tools.h44
-rw-r--r--gn/src/gn/ninja_utils.cc30
-rw-r--r--gn/src/gn/ninja_utils.h (renamed from gn/tools/gn/ninja_utils.h)0
-rw-r--r--gn/src/gn/ninja_writer.cc51
-rw-r--r--gn/src/gn/ninja_writer.h (renamed from gn/tools/gn/ninja_writer.h)0
-rw-r--r--gn/src/gn/operators.cc744
-rw-r--r--gn/src/gn/operators.h (renamed from gn/tools/gn/operators.h)0
-rw-r--r--gn/src/gn/operators_unittest.cc413
-rw-r--r--gn/src/gn/ordered_set.h (renamed from gn/tools/gn/ordered_set.h)0
-rw-r--r--gn/src/gn/output_conversion.cc177
-rw-r--r--gn/src/gn/output_conversion.h26
-rw-r--r--gn/src/gn/output_conversion_unittest.cc351
-rw-r--r--gn/src/gn/output_file.cc39
-rw-r--r--gn/src/gn/output_file.h63
-rw-r--r--gn/src/gn/parse_node_value_adapter.cc44
-rw-r--r--gn/src/gn/parse_node_value_adapter.h55
-rw-r--r--gn/src/gn/parse_tree.cc1257
-rw-r--r--gn/src/gn/parse_tree.h613
-rw-r--r--gn/src/gn/parse_tree_unittest.cc307
-rw-r--r--gn/src/gn/parser.cc941
-rw-r--r--gn/src/gn/parser.h158
-rw-r--r--gn/src/gn/parser_unittest.cc734
-rw-r--r--gn/src/gn/path_output.cc176
-rw-r--r--gn/src/gn/path_output.h93
-rw-r--r--gn/src/gn/path_output_unittest.cc276
-rw-r--r--gn/src/gn/pattern.cc223
-rw-r--r--gn/src/gn/pattern.h92
-rw-r--r--gn/src/gn/pattern_unittest.cc66
-rw-r--r--gn/src/gn/pool.cc45
-rw-r--r--gn/src/gn/pool.h41
-rw-r--r--gn/src/gn/qt_creator_writer.cc296
-rw-r--r--gn/src/gn/qt_creator_writer.h56
-rw-r--r--gn/src/gn/runtime_deps.cc319
-rw-r--r--gn/src/gn/runtime_deps.h31
-rw-r--r--gn/src/gn/runtime_deps_unittest.cc448
-rw-r--r--gn/src/gn/rust_project_writer.cc451
-rw-r--r--gn/src/gn/rust_project_writer.h37
-rw-r--r--gn/src/gn/rust_project_writer_helpers.h140
-rw-r--r--gn/src/gn/rust_project_writer_helpers_unittest.cc359
-rw-r--r--gn/src/gn/rust_project_writer_unittest.cc524
-rw-r--r--gn/src/gn/rust_substitution_type.cc42
-rw-r--r--gn/src/gn/rust_substitution_type.h27
-rw-r--r--gn/src/gn/rust_tool.cc123
-rw-r--r--gn/src/gn/rust_tool.h58
-rw-r--r--gn/src/gn/rust_values.cc9
-rw-r--r--gn/src/gn/rust_values.h68
-rw-r--r--gn/src/gn/rust_values_generator.cc191
-rw-r--r--gn/src/gn/rust_values_generator.h39
-rw-r--r--gn/src/gn/rust_variables.cc113
-rw-r--r--gn/src/gn/rust_variables.h38
-rw-r--r--gn/src/gn/scheduler.cc183
-rw-r--r--gn/src/gn/scheduler.h156
-rw-r--r--gn/src/gn/scope.cc573
-rw-r--r--gn/src/gn/scope.h387
-rw-r--r--gn/src/gn/scope_per_file_provider.cc128
-rw-r--r--gn/src/gn/scope_per_file_provider.h53
-rw-r--r--gn/src/gn/scope_per_file_provider_unittest.cc58
-rw-r--r--gn/src/gn/scope_unittest.cc335
-rw-r--r--gn/src/gn/settings.cc29
-rw-r--r--gn/src/gn/settings.h116
-rw-r--r--gn/src/gn/setup.cc1028
-rw-r--r--gn/src/gn/setup.h214
-rw-r--r--gn/src/gn/setup_unittest.cc94
-rw-r--r--gn/src/gn/source_dir.cc155
-rw-r--r--gn/src/gn/source_dir.h154
-rw-r--r--gn/src/gn/source_dir_unittest.cc208
-rw-r--r--gn/src/gn/source_file.cc140
-rw-r--r--gn/src/gn/source_file.h172
-rw-r--r--gn/src/gn/source_file_unittest.cc20
-rw-r--r--gn/src/gn/standard_out.cc342
-rw-r--r--gn/src/gn/standard_out.h (renamed from gn/tools/gn/standard_out.h)0
-rw-r--r--gn/src/gn/string_atom.cc215
-rw-r--r--gn/src/gn/string_atom.h179
-rw-r--r--gn/src/gn/string_atom_unittest.cc148
-rw-r--r--gn/src/gn/string_output_buffer.cc117
-rw-r--r--gn/src/gn/string_output_buffer.h94
-rw-r--r--gn/src/gn/string_output_buffer_unittest.cc134
-rw-r--r--gn/src/gn/string_utils.cc356
-rw-r--r--gn/src/gn/string_utils.h56
-rw-r--r--gn/src/gn/string_utils_unittest.cc159
-rw-r--r--gn/src/gn/substitution_list.cc69
-rw-r--r--gn/src/gn/substitution_list.h46
-rw-r--r--gn/src/gn/substitution_pattern.cc144
-rw-r--r--gn/src/gn/substitution_pattern.h79
-rw-r--r--gn/src/gn/substitution_pattern_unittest.cc73
-rw-r--r--gn/src/gn/substitution_type.cc204
-rw-r--r--gn/src/gn/substitution_type.h120
-rw-r--r--gn/src/gn/substitution_writer.cc587
-rw-r--r--gn/src/gn/substitution_writer.h238
-rw-r--r--gn/src/gn/substitution_writer_unittest.cc325
-rw-r--r--gn/src/gn/swift_values.cc70
-rw-r--r--gn/src/gn/swift_values.h91
-rw-r--r--gn/src/gn/swift_values_generator.cc60
-rw-r--r--gn/src/gn/swift_values_generator.h34
-rw-r--r--gn/src/gn/swift_variables.cc44
-rw-r--r--gn/src/gn/swift_variables.h26
-rw-r--r--gn/src/gn/switches.cc356
-rw-r--r--gn/src/gn/switches.h131
-rw-r--r--gn/src/gn/target.cc1170
-rw-r--r--gn/src/gn/target.h518
-rw-r--r--gn/src/gn/target_generator.cc451
-rw-r--r--gn/src/gn/target_generator.h86
-rw-r--r--gn/src/gn/target_unittest.cc1539
-rw-r--r--gn/src/gn/template.cc128
-rw-r--r--gn/src/gn/template.h (renamed from gn/tools/gn/template.h)0
-rw-r--r--gn/src/gn/template_unittest.cc93
-rw-r--r--gn/src/gn/test_with_scheduler.cc8
-rw-r--r--gn/src/gn/test_with_scheduler.h27
-rw-r--r--gn/src/gn/test_with_scope.cc349
-rw-r--r--gn/src/gn/test_with_scope.h128
-rw-r--r--gn/src/gn/token.cc29
-rw-r--r--gn/src/gn/token.h88
-rw-r--r--gn/src/gn/tokenizer.cc417
-rw-r--r--gn/src/gn/tokenizer.h108
-rw-r--r--gn/src/gn/tokenizer_unittest.cc237
-rw-r--r--gn/src/gn/tool.cc407
-rw-r--r--gn/src/gn/tool.h304
-rw-r--r--gn/src/gn/toolchain.cc140
-rw-r--r--gn/src/gn/toolchain.h129
-rw-r--r--gn/src/gn/trace.cc334
-rw-r--r--gn/src/gn/trace.h (renamed from gn/tools/gn/trace.h)0
-rw-r--r--gn/src/gn/unique_vector.h143
-rw-r--r--gn/src/gn/unique_vector_unittest.cc45
-rw-r--r--gn/src/gn/value.cc262
-rw-r--r--gn/src/gn/value.h137
-rw-r--r--gn/src/gn/value_extractors.cc323
-rw-r--r--gn/src/gn/value_extractors.h100
-rw-r--r--gn/src/gn/value_unittest.cc60
-rw-r--r--gn/src/gn/variables.cc2342
-rw-r--r--gn/src/gn/variables.h379
-rw-r--r--gn/src/gn/vector_utils.h103
-rw-r--r--gn/src/gn/vector_utils_unittest.cc47
-rw-r--r--gn/src/gn/version.cc80
-rw-r--r--gn/src/gn/version.h37
-rw-r--r--gn/src/gn/version_unittest.cc33
-rw-r--r--gn/src/gn/visibility.cc120
-rw-r--r--gn/src/gn/visibility.h72
-rw-r--r--gn/src/gn/visibility_unittest.cc71
-rw-r--r--gn/src/gn/visual_studio_utils.cc127
-rw-r--r--gn/src/gn/visual_studio_utils.h (renamed from gn/tools/gn/visual_studio_utils.h)0
-rw-r--r--gn/src/gn/visual_studio_utils_unittest.cc102
-rw-r--r--gn/src/gn/visual_studio_writer.cc916
-rw-r--r--gn/src/gn/visual_studio_writer.h168
-rw-r--r--gn/src/gn/visual_studio_writer_unittest.cc200
-rw-r--r--gn/src/gn/xcode_object.cc1118
-rw-r--r--gn/src/gn/xcode_object.h512
-rw-r--r--gn/src/gn/xcode_object_unittest.cc435
-rw-r--r--gn/src/gn/xcode_writer.cc1029
-rw-r--r--gn/src/gn/xcode_writer.h83
-rw-r--r--gn/src/gn/xml_element_writer.cc114
-rw-r--r--gn/src/gn/xml_element_writer.h124
-rw-r--r--gn/src/gn/xml_element_writer_unittest.cc93
-rw-r--r--gn/src/util/auto_reset_event.h (renamed from gn/util/auto_reset_event.h)0
-rw-r--r--gn/src/util/build_config.h177
-rw-r--r--gn/src/util/exe_path.cc119
-rw-r--r--gn/src/util/exe_path.h (renamed from gn/util/exe_path.h)0
-rw-r--r--gn/src/util/msg_loop.cc75
-rw-r--r--gn/src/util/msg_loop.h47
-rw-r--r--gn/src/util/semaphore.cc91
-rw-r--r--gn/src/util/semaphore.h53
-rw-r--r--gn/src/util/sys_info.cc85
-rw-r--r--gn/src/util/sys_info.h (renamed from gn/util/sys_info.h)0
-rw-r--r--gn/src/util/test/gn_test.cc182
-rw-r--r--gn/src/util/test/test.h194
-rw-r--r--gn/src/util/ticks.cc95
-rw-r--r--gn/src/util/ticks.h (renamed from gn/util/ticks.h)0
-rw-r--r--gn/src/util/worker_pool.cc150
-rw-r--r--gn/src/util/worker_pool.h37
-rwxr-xr-xgn/tools/find_unreachable.py78
-rw-r--r--gn/tools/gn/action_target_generator.cc221
-rw-r--r--gn/tools/gn/action_target_generator.h41
-rw-r--r--gn/tools/gn/action_target_generator_unittest.cc122
-rw-r--r--gn/tools/gn/action_values.cc31
-rw-r--r--gn/tools/gn/action_values.h70
-rw-r--r--gn/tools/gn/analyzer.cc489
-rw-r--r--gn/tools/gn/analyzer.h104
-rw-r--r--gn/tools/gn/analyzer_unittest.cc597
-rw-r--r--gn/tools/gn/args.cc422
-rw-r--r--gn/tools/gn/args.h147
-rw-r--r--gn/tools/gn/args_unittest.cc81
-rw-r--r--gn/tools/gn/binary_target_generator.cc235
-rw-r--r--gn/tools/gn/binary_target_generator.h41
-rw-r--r--gn/tools/gn/build_settings.cc75
-rw-r--r--gn/tools/gn/build_settings.h142
-rw-r--r--gn/tools/gn/builder.cc602
-rw-r--r--gn/tools/gn/builder.h146
-rw-r--r--gn/tools/gn/builder_record.cc67
-rw-r--r--gn/tools/gn/builder_record.h110
-rw-r--r--gn/tools/gn/builder_unittest.cc244
-rw-r--r--gn/tools/gn/bundle_data.cc180
-rw-r--r--gn/tools/gn/bundle_data.h198
-rw-r--r--gn/tools/gn/bundle_data_target_generator.cc95
-rw-r--r--gn/tools/gn/bundle_data_target_generator.h32
-rw-r--r--gn/tools/gn/bundle_file_rule.cc110
-rw-r--r--gn/tools/gn/bundle_file_rule.h56
-rw-r--r--gn/tools/gn/c_include_iterator.cc172
-rw-r--r--gn/tools/gn/c_include_iterator.h57
-rw-r--r--gn/tools/gn/c_include_iterator_unittest.cc178
-rw-r--r--gn/tools/gn/c_substitution_type.cc85
-rw-r--r--gn/tools/gn/c_substitution_type.h44
-rw-r--r--gn/tools/gn/c_tool.cc232
-rw-r--r--gn/tools/gn/c_tool.h137
-rw-r--r--gn/tools/gn/command_analyze.cc134
-rw-r--r--gn/tools/gn/command_args.cc511
-rw-r--r--gn/tools/gn/command_check.cc265
-rw-r--r--gn/tools/gn/command_clean.cc130
-rw-r--r--gn/tools/gn/command_desc.cc687
-rw-r--r--gn/tools/gn/command_format.cc1246
-rw-r--r--gn/tools/gn/command_format.h38
-rw-r--r--gn/tools/gn/command_format_unittest.cc118
-rw-r--r--gn/tools/gn/command_gen.cc531
-rw-r--r--gn/tools/gn/command_help.cc375
-rw-r--r--gn/tools/gn/command_ls.cc108
-rw-r--r--gn/tools/gn/command_meta.cc170
-rw-r--r--gn/tools/gn/command_path.cc413
-rw-r--r--gn/tools/gn/command_refs.cc501
-rw-r--r--gn/tools/gn/commands.cc559
-rw-r--r--gn/tools/gn/commands.h210
-rw-r--r--gn/tools/gn/compile_commands_writer.cc327
-rw-r--r--gn/tools/gn/compile_commands_writer.h41
-rw-r--r--gn/tools/gn/compile_commands_writer_unittest.cc647
-rw-r--r--gn/tools/gn/config.cc50
-rw-r--r--gn/tools/gn/config.h71
-rw-r--r--gn/tools/gn/config_unittest.cc85
-rw-r--r--gn/tools/gn/config_values.cc48
-rw-r--r--gn/tools/gn/config_values.h94
-rw-r--r--gn/tools/gn/config_values_extractors.cc34
-rw-r--r--gn/tools/gn/config_values_extractors.h102
-rw-r--r--gn/tools/gn/config_values_extractors_unittest.cc137
-rw-r--r--gn/tools/gn/config_values_generator.cc125
-rw-r--r--gn/tools/gn/config_values_generator.h46
-rw-r--r--gn/tools/gn/copy_target_generator.cc44
-rw-r--r--gn/tools/gn/copy_target_generator.h27
-rw-r--r--gn/tools/gn/create_bundle_target_generator.cc303
-rw-r--r--gn/tools/gn/create_bundle_target_generator.h45
-rw-r--r--gn/tools/gn/deps_iterator.cc53
-rw-r--r--gn/tools/gn/deps_iterator.h72
-rw-r--r--gn/tools/gn/desc_builder.cc817
-rw-r--r--gn/tools/gn/desc_builder.h27
-rw-r--r--gn/tools/gn/eclipse_writer.cc171
-rw-r--r--gn/tools/gn/err.cc192
-rw-r--r--gn/tools/gn/err.h99
-rw-r--r--gn/tools/gn/escape.cc253
-rw-r--r--gn/tools/gn/escape.h77
-rw-r--r--gn/tools/gn/escape_unittest.cc71
-rw-r--r--gn/tools/gn/exec_process.cc281
-rw-r--r--gn/tools/gn/exec_process.h36
-rw-r--r--gn/tools/gn/exec_process_unittest.cc123
-rw-r--r--gn/tools/gn/filesystem_utils.cc1103
-rw-r--r--gn/tools/gn/filesystem_utils.h305
-rw-r--r--gn/tools/gn/filesystem_utils_unittest.cc868
-rw-r--r--gn/tools/gn/format_test_data/003.golden10
-rw-r--r--gn/tools/gn/format_test_data/004.golden13
-rw-r--r--gn/tools/gn/format_test_data/007.golden11
-rw-r--r--gn/tools/gn/format_test_data/008.golden5
-rw-r--r--gn/tools/gn/format_test_data/009.golden9
-rw-r--r--gn/tools/gn/format_test_data/010.golden9
-rw-r--r--gn/tools/gn/format_test_data/011.golden13
-rw-r--r--gn/tools/gn/format_test_data/012.golden22
-rw-r--r--gn/tools/gn/format_test_data/015.golden6
-rw-r--r--gn/tools/gn/format_test_data/019.gn23
-rw-r--r--gn/tools/gn/format_test_data/019.golden23
-rw-r--r--gn/tools/gn/format_test_data/040.gn9
-rw-r--r--gn/tools/gn/format_test_data/042.golden110
-rw-r--r--gn/tools/gn/format_test_data/044.gn10
-rw-r--r--gn/tools/gn/format_test_data/048.gn19
-rw-r--r--gn/tools/gn/format_test_data/048.golden19
-rw-r--r--gn/tools/gn/format_test_data/062.gn122
-rw-r--r--gn/tools/gn/format_test_data/062.golden132
-rw-r--r--gn/tools/gn/format_test_data/063.gn36
-rw-r--r--gn/tools/gn/format_test_data/063.golden36
-rw-r--r--gn/tools/gn/format_test_data/064.golden5
-rw-r--r--gn/tools/gn/format_test_data/066.golden30
-rw-r--r--gn/tools/gn/function_exec_script.cc271
-rw-r--r--gn/tools/gn/function_foreach.cc112
-rw-r--r--gn/tools/gn/function_foreach_unittest.cc100
-rw-r--r--gn/tools/gn/function_forward_variables_from.cc249
-rw-r--r--gn/tools/gn/function_forward_variables_from_unittest.cc244
-rw-r--r--gn/tools/gn/function_get_label_info.cc142
-rw-r--r--gn/tools/gn/function_get_label_info_unittest.cc107
-rw-r--r--gn/tools/gn/function_get_path_info.cc251
-rw-r--r--gn/tools/gn/function_get_path_info_unittest.cc120
-rw-r--r--gn/tools/gn/function_get_target_outputs.cc139
-rw-r--r--gn/tools/gn/function_get_target_outputs_unittest.cc107
-rw-r--r--gn/tools/gn/function_process_file_template.cc115
-rw-r--r--gn/tools/gn/function_process_file_template_unittest.cc64
-rw-r--r--gn/tools/gn/function_read_file.cc78
-rw-r--r--gn/tools/gn/function_rebase_path.cc296
-rw-r--r--gn/tools/gn/function_rebase_path_unittest.cc183
-rw-r--r--gn/tools/gn/function_set_default_toolchain.cc87
-rw-r--r--gn/tools/gn/function_set_defaults.cc76
-rw-r--r--gn/tools/gn/function_template.cc226
-rw-r--r--gn/tools/gn/function_template_unittest.cc29
-rw-r--r--gn/tools/gn/function_toolchain.cc831
-rw-r--r--gn/tools/gn/function_toolchain_unittest.cc119
-rw-r--r--gn/tools/gn/function_write_file.cc107
-rw-r--r--gn/tools/gn/function_write_file_unittest.cc107
-rw-r--r--gn/tools/gn/functions.cc1391
-rw-r--r--gn/tools/gn/functions.h538
-rw-r--r--gn/tools/gn/functions_target.cc984
-rw-r--r--gn/tools/gn/functions_target_rust_unittest.cc373
-rw-r--r--gn/tools/gn/functions_target_unittest.cc196
-rw-r--r--gn/tools/gn/functions_unittest.cc216
-rw-r--r--gn/tools/gn/general_tool.cc51
-rw-r--r--gn/tools/gn/general_tool.h46
-rw-r--r--gn/tools/gn/generated_file_target_generator.cc165
-rw-r--r--gn/tools/gn/generated_file_target_generator.h49
-rw-r--r--gn/tools/gn/gn_main.cc85
-rw-r--r--gn/tools/gn/group_target_generator.cc23
-rw-r--r--gn/tools/gn/group_target_generator.h27
-rw-r--r--gn/tools/gn/header_checker.cc625
-rw-r--r--gn/tools/gn/header_checker.h206
-rw-r--r--gn/tools/gn/header_checker_unittest.cc403
-rw-r--r--gn/tools/gn/import_manager.cc162
-rw-r--r--gn/tools/gn/import_manager.h54
-rw-r--r--gn/tools/gn/inherited_libraries.cc74
-rw-r--r--gn/tools/gn/inherited_libraries.h71
-rw-r--r--gn/tools/gn/inherited_libraries_unittest.cc135
-rw-r--r--gn/tools/gn/input_conversion.cc359
-rw-r--r--gn/tools/gn/input_conversion_unittest.cc275
-rw-r--r--gn/tools/gn/input_file.cc26
-rw-r--r--gn/tools/gn/input_file.h65
-rw-r--r--gn/tools/gn/input_file_manager.cc332
-rw-r--r--gn/tools/gn/input_file_manager.h155
-rw-r--r--gn/tools/gn/item.cc60
-rw-r--r--gn/tools/gn/item.h82
-rw-r--r--gn/tools/gn/json_project_writer.cc220
-rw-r--r--gn/tools/gn/json_project_writer.h33
-rw-r--r--gn/tools/gn/json_project_writer_unittest.cc137
-rw-r--r--gn/tools/gn/label.cc323
-rw-r--r--gn/tools/gn/label.h109
-rw-r--r--gn/tools/gn/label_pattern.cc276
-rw-r--r--gn/tools/gn/label_pattern.h80
-rw-r--r--gn/tools/gn/label_pattern_unittest.cc86
-rw-r--r--gn/tools/gn/label_ptr.h79
-rw-r--r--gn/tools/gn/label_unittest.cc95
-rw-r--r--gn/tools/gn/lib_file.cc23
-rw-r--r--gn/tools/gn/lib_file.h54
-rw-r--r--gn/tools/gn/loader.cc437
-rw-r--r--gn/tools/gn/loader.h178
-rw-r--r--gn/tools/gn/loader_unittest.cc262
-rw-r--r--gn/tools/gn/location.cc69
-rw-r--r--gn/tools/gn/metadata.cc271
-rw-r--r--gn/tools/gn/metadata.h90
-rw-r--r--gn/tools/gn/metadata_unittest.cc233
-rw-r--r--gn/tools/gn/metadata_walk.cc24
-rw-r--r--gn/tools/gn/metadata_walk.h26
-rw-r--r--gn/tools/gn/metadata_walk_unittest.cc211
-rw-r--r--gn/tools/gn/misc/emacs/gn-mode.el192
-rw-r--r--gn/tools/gn/misc/tm/GN.tmLanguage102
-rw-r--r--gn/tools/gn/misc/vim/README.md29
-rw-r--r--gn/tools/gn/misc/vim/gn-format.py59
-rw-r--r--gn/tools/gn/misc/vim/syntax/gn.vim85
-rw-r--r--gn/tools/gn/ninja_action_target_writer.cc239
-rw-r--r--gn/tools/gn/ninja_action_target_writer.h62
-rw-r--r--gn/tools/gn/ninja_action_target_writer_unittest.cc475
-rw-r--r--gn/tools/gn/ninja_binary_target_writer.cc208
-rw-r--r--gn/tools/gn/ninja_binary_target_writer.h70
-rw-r--r--gn/tools/gn/ninja_binary_target_writer_unittest.cc108
-rw-r--r--gn/tools/gn/ninja_build_writer.cc626
-rw-r--r--gn/tools/gn/ninja_build_writer.h81
-rw-r--r--gn/tools/gn/ninja_build_writer_unittest.cc255
-rw-r--r--gn/tools/gn/ninja_bundle_data_target_writer.cc33
-rw-r--r--gn/tools/gn/ninja_bundle_data_target_writer.h23
-rw-r--r--gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc56
-rw-r--r--gn/tools/gn/ninja_c_binary_target_writer.cc714
-rw-r--r--gn/tools/gn/ninja_c_binary_target_writer.h107
-rw-r--r--gn/tools/gn/ninja_c_binary_target_writer_unittest.cc1187
-rw-r--r--gn/tools/gn/ninja_copy_target_writer.cc118
-rw-r--r--gn/tools/gn/ninja_copy_target_writer.h27
-rw-r--r--gn/tools/gn/ninja_copy_target_writer_unittest.cc94
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer.cc357
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer.h71
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc471
-rw-r--r--gn/tools/gn/ninja_generated_file_target_writer.cc92
-rw-r--r--gn/tools/gn/ninja_generated_file_target_writer.h25
-rw-r--r--gn/tools/gn/ninja_generated_file_target_writer_unittest.cc60
-rw-r--r--gn/tools/gn/ninja_group_target_writer.cc32
-rw-r--r--gn/tools/gn/ninja_group_target_writer.h23
-rw-r--r--gn/tools/gn/ninja_group_target_writer_unittest.cc51
-rw-r--r--gn/tools/gn/ninja_rust_binary_target_writer.cc247
-rw-r--r--gn/tools/gn/ninja_rust_binary_target_writer.h37
-rw-r--r--gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc578
-rw-r--r--gn/tools/gn/ninja_target_command_util.cc177
-rw-r--r--gn/tools/gn/ninja_target_command_util.h84
-rw-r--r--gn/tools/gn/ninja_target_writer.cc329
-rw-r--r--gn/tools/gn/ninja_target_writer.h72
-rw-r--r--gn/tools/gn/ninja_target_writer_unittest.cc163
-rw-r--r--gn/tools/gn/ninja_toolchain_writer.cc142
-rw-r--r--gn/tools/gn/ninja_toolchain_writer.h61
-rw-r--r--gn/tools/gn/ninja_toolchain_writer_unittest.cc39
-rw-r--r--gn/tools/gn/ninja_utils.cc30
-rw-r--r--gn/tools/gn/ninja_writer.cc52
-rw-r--r--gn/tools/gn/operators.cc785
-rw-r--r--gn/tools/gn/operators_unittest.cc424
-rw-r--r--gn/tools/gn/output_conversion.cc178
-rw-r--r--gn/tools/gn/output_conversion.h26
-rw-r--r--gn/tools/gn/output_conversion_unittest.cc351
-rw-r--r--gn/tools/gn/output_file.cc39
-rw-r--r--gn/tools/gn/output_file.h63
-rw-r--r--gn/tools/gn/parse_node_value_adapter.cc44
-rw-r--r--gn/tools/gn/parse_node_value_adapter.h55
-rw-r--r--gn/tools/gn/parse_tree.cc941
-rw-r--r--gn/tools/gn/parse_tree.h567
-rw-r--r--gn/tools/gn/parse_tree_unittest.cc255
-rw-r--r--gn/tools/gn/parser.cc942
-rw-r--r--gn/tools/gn/parser.h157
-rw-r--r--gn/tools/gn/parser_unittest.cc735
-rw-r--r--gn/tools/gn/path_output.cc169
-rw-r--r--gn/tools/gn/path_output.h92
-rw-r--r--gn/tools/gn/path_output_unittest.cc276
-rw-r--r--gn/tools/gn/pattern.cc189
-rw-r--r--gn/tools/gn/pattern.h90
-rw-r--r--gn/tools/gn/pattern_unittest.cc64
-rw-r--r--gn/tools/gn/pool.cc45
-rw-r--r--gn/tools/gn/pool.h41
-rw-r--r--gn/tools/gn/qt_creator_writer.cc302
-rw-r--r--gn/tools/gn/qt_creator_writer.h56
-rw-r--r--gn/tools/gn/runtime_deps.cc314
-rw-r--r--gn/tools/gn/runtime_deps.h28
-rw-r--r--gn/tools/gn/runtime_deps_unittest.cc448
-rw-r--r--gn/tools/gn/rust_substitution_type.cc48
-rw-r--r--gn/tools/gn/rust_substitution_type.h29
-rw-r--r--gn/tools/gn/rust_tool.cc156
-rw-r--r--gn/tools/gn/rust_tool.h98
-rw-r--r--gn/tools/gn/rust_values.cc9
-rw-r--r--gn/tools/gn/rust_values.h67
-rw-r--r--gn/tools/gn/rust_values_generator.cc206
-rw-r--r--gn/tools/gn/rust_values_generator.h39
-rw-r--r--gn/tools/gn/rust_variables.cc129
-rw-r--r--gn/tools/gn/rust_variables.h38
-rw-r--r--gn/tools/gn/scheduler.cc189
-rw-r--r--gn/tools/gn/scheduler.h156
-rw-r--r--gn/tools/gn/scope.cc593
-rw-r--r--gn/tools/gn/scope.h397
-rw-r--r--gn/tools/gn/scope_per_file_provider.cc116
-rw-r--r--gn/tools/gn/scope_per_file_provider.h51
-rw-r--r--gn/tools/gn/scope_per_file_provider_unittest.cc55
-rw-r--r--gn/tools/gn/scope_unittest.cc335
-rw-r--r--gn/tools/gn/settings.cc29
-rw-r--r--gn/tools/gn/settings.h115
-rw-r--r--gn/tools/gn/setup.cc862
-rw-r--r--gn/tools/gn/setup.h189
-rw-r--r--gn/tools/gn/setup_unittest.cc45
-rw-r--r--gn/tools/gn/source_dir.cc150
-rw-r--r--gn/tools/gn/source_dir.h153
-rw-r--r--gn/tools/gn/source_dir_unittest.cc208
-rw-r--r--gn/tools/gn/source_file.cc122
-rw-r--r--gn/tools/gn/source_file.h141
-rw-r--r--gn/tools/gn/source_file_unittest.cc20
-rw-r--r--gn/tools/gn/standard_out.cc342
-rw-r--r--gn/tools/gn/string_utils.cc347
-rw-r--r--gn/tools/gn/string_utils.h53
-rw-r--r--gn/tools/gn/string_utils_unittest.cc159
-rw-r--r--gn/tools/gn/substitution_list.cc69
-rw-r--r--gn/tools/gn/substitution_list.h46
-rw-r--r--gn/tools/gn/substitution_pattern.cc144
-rw-r--r--gn/tools/gn/substitution_pattern.h79
-rw-r--r--gn/tools/gn/substitution_pattern_unittest.cc73
-rw-r--r--gn/tools/gn/substitution_type.cc193
-rw-r--r--gn/tools/gn/substitution_type.h117
-rw-r--r--gn/tools/gn/substitution_writer.cc598
-rw-r--r--gn/tools/gn/substitution_writer.h238
-rw-r--r--gn/tools/gn/substitution_writer_unittest.cc321
-rw-r--r--gn/tools/gn/switches.cc310
-rw-r--r--gn/tools/gn/switches.h118
-rw-r--r--gn/tools/gn/target.cc985
-rw-r--r--gn/tools/gn/target.h453
-rw-r--r--gn/tools/gn/target_generator.cc444
-rw-r--r--gn/tools/gn/target_generator.h86
-rw-r--r--gn/tools/gn/target_unittest.cc1310
-rw-r--r--gn/tools/gn/template.cc123
-rw-r--r--gn/tools/gn/template_unittest.cc93
-rw-r--r--gn/tools/gn/test_with_scheduler.cc8
-rw-r--r--gn/tools/gn/test_with_scheduler.h27
-rw-r--r--gn/tools/gn/test_with_scope.cc253
-rw-r--r--gn/tools/gn/test_with_scope.h123
-rw-r--r--gn/tools/gn/token.cc22
-rw-r--r--gn/tools/gn/token.h85
-rw-r--r--gn/tools/gn/tokenizer.cc402
-rw-r--r--gn/tools/gn/tokenizer.h90
-rw-r--r--gn/tools/gn/tokenizer_unittest.cc205
-rw-r--r--gn/tools/gn/tool.cc344
-rw-r--r--gn/tools/gn/tool.h244
-rw-r--r--gn/tools/gn/toolchain.cc140
-rw-r--r--gn/tools/gn/toolchain.h129
-rw-r--r--gn/tools/gn/trace.cc334
-rw-r--r--gn/tools/gn/unique_vector.h163
-rw-r--r--gn/tools/gn/unique_vector_unittest.cc45
-rw-r--r--gn/tools/gn/value.cc272
-rw-r--r--gn/tools/gn/value.h137
-rw-r--r--gn/tools/gn/value_extractors.cc249
-rw-r--r--gn/tools/gn/value_extractors.h89
-rw-r--r--gn/tools/gn/value_unittest.cc60
-rw-r--r--gn/tools/gn/variables.cc2219
-rw-r--r--gn/tools/gn/variables.h352
-rw-r--r--gn/tools/gn/visibility.cc116
-rw-r--r--gn/tools/gn/visibility.h68
-rw-r--r--gn/tools/gn/visibility_unittest.cc53
-rw-r--r--gn/tools/gn/visual_studio_utils.cc127
-rw-r--r--gn/tools/gn/visual_studio_utils_unittest.cc102
-rw-r--r--gn/tools/gn/visual_studio_writer.cc913
-rw-r--r--gn/tools/gn/visual_studio_writer.h168
-rw-r--r--gn/tools/gn/visual_studio_writer_unittest.cc200
-rw-r--r--gn/tools/gn/xcode_object.cc990
-rw-r--r--gn/tools/gn/xcode_object.h465
-rw-r--r--gn/tools/gn/xcode_object_unittest.cc435
-rw-r--r--gn/tools/gn/xcode_writer.cc664
-rw-r--r--gn/tools/gn/xcode_writer.h89
-rw-r--r--gn/tools/gn/xml_element_writer.cc114
-rw-r--r--gn/tools/gn/xml_element_writer.h124
-rw-r--r--gn/tools/gn/xml_element_writer_unittest.cc93
-rw-r--r--gn/util/build_config.h196
-rw-r--r--gn/util/exe_path.cc77
-rw-r--r--gn/util/msg_loop.cc76
-rw-r--r--gn/util/msg_loop.h47
-rw-r--r--gn/util/semaphore.cc95
-rw-r--r--gn/util/semaphore.h53
-rw-r--r--gn/util/sys_info.cc79
-rw-r--r--gn/util/task.h13
-rw-r--r--gn/util/test/gn_test.cc162
-rw-r--r--gn/util/test/test.h195
-rw-r--r--gn/util/ticks.cc93
-rw-r--r--gn/util/worker_pool.cc150
-rw-r--r--gn/util/worker_pool.h37
1210 files changed, 110228 insertions, 98561 deletions
diff --git a/gn/AUTHORS b/gn/AUTHORS
index 17b4959f9ce..2e9eb5ac5ee 100644
--- a/gn/AUTHORS
+++ b/gn/AUTHORS
@@ -9,6 +9,7 @@
# See python fnmatch module documentation for more information.
Google Inc. <*@google.com>
+HyperConnect Inc. <*@hpcnt.com>
IBM Inc. <*@*.ibm.com>
Loongson Technology Corporation Limited. <*@loongson.cn>
MIPS Technologies, Inc. <*@mips.com>
@@ -42,4 +43,7 @@ Saikrishna Arcot <saiarcot895@gmail.com>
Tim Niederhausen <tim@rnc-ag.de>
Tomas Popela <tomas.popela@gmail.com>
Tripta Gupta <tripta.g@samsung.com>
+Wink Saville <wink@saville.com>
Yuriy Taraday <yorik.sar@gmail.com>
+Oleksandr Motsok <boramaabak@gmail.com>
+Ihor Karavan <ihorkaravan96@gmail.com>
diff --git a/gn/README.md b/gn/README.md
index a8d9fa3724f..d6c91184c78 100644
--- a/gn/README.md
+++ b/gn/README.md
@@ -5,18 +5,90 @@ GN is a meta-build system that generates build files for
Related resources:
- * Documentation in [docs/](https://gn.googlesource.com/gn/+/master/docs/).
+ * Documentation in [docs/](https://gn.googlesource.com/gn/+/master/docs/). In
+ particular [GN Quick Start
+ guide](https://gn.googlesource.com/gn/+/master/docs/quick_start.md)
+ and the [reference](https://gn.googlesource.com/gn/+/master/docs/reference.md)
+ (the latter is all builtin help converted to a single file).
* An introductory [presentation](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing).
* The [mailing list](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev).
+## What GN is for
+
+GN is currently used as the build system for Chromium, Fuchsia, and related
+projects. Some strengths of GN are:
+
+ * It is designed for large projects and large teams. It scales efficiently to
+ many thousands of build files and tens of thousands of source files.
+
+ * It has a readable, clean syntax. Once a build is set-up, it is generally
+ easy for people with no backround in GN to make basic edits to the build.
+
+ * It is designed for multi-platform projects. It can cleanly express many
+ complicated build variants across different platforms. A single build
+ invocation can target multiple platforms.
+
+ * It supports multiple parallel output directories, each with their own
+ configuration. This allows a developer to maintain builds targeting debug,
+ release, or different platforms in parallel without forced rebuilds when
+ switching.
+
+ * It has a focus on correctness. GN checks for the correct dependencies,
+ inputs, and outputs to the extent possible, and has a number of tools to
+ allow developers to ensure the build evolves as desired (for example, `gn
+ check`, `testonly`, `assert_no_deps`).
+
+ * It has comprehensive build-in help available from the command-line.
+
+Although small projects successfully use GN, the focus on large projects has
+some disadvanages:
+
+ * GN has the goal of being minimally expressive. Although it can be quite
+ flexible, a design goal is to direct members of a large team (who may not
+ have much knowledge about the build) down an easy-to-understand, well-lit
+ path. This isn't necessarily the correct trade-off for smaller projects.
+
+ * The minimal build configuration is relatively heavyweight. There are several
+ files required and the exact way all compilers are linkers are run must be
+ specified in the configuration (see "Examples" below). There is no default
+ compiler configuration.
+
+ * It is not easily composable. GN is designed to compile a single large
+ project with relatively uniform settings and rules. Projects like Chromium
+ do bring together multiple repositories from multiple teams, but the
+ projects must agree on some conventions in the build files to allow this to
+ work.
+
+ * GN is designed with the expectation that the developers building a project
+ want to compile an identical configuration. So while builds can integrate
+ with the user's environment like the CXX and CFLAGS variables if they want,
+ this is not the default and most project's builds do not do this. The result
+ is that many GN projects do not integrate well with other systems like
+ ebuild.
+
+ * There is no simple release scheme (see "Versioning and distribution" below).
+ Projects are expected to manage the version of GN they require. Getting an
+ appropriate GN binary can be a hurdle for new contributors to a project.
+ Since it is relatively uncommon, it can be more difficult to find
+ information and examples.
+
+GN can generate Ninja build files for C, C++, Rust, Objective C, and Swift
+source on most popular platforms. Other languages can be compiled using the
+general "action" rules which are executed by Python or another scripting
+language (Google does this to compile Java and Go). But because this is not as
+clean, generally GN is only used when the bulk of the build is in one of the
+main built-in languages.
+
## Getting a binary
You can download the latest version of GN binary for
[Linux](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest),
[macOS](https://chrome-infra-packages.appspot.com/dl/gn/gn/mac-amd64/+/latest) and
-[Windows](https://chrome-infra-packages.appspot.com/dl/gn/gn/windows-amd64/+/latest).
+[Windows](https://chrome-infra-packages.appspot.com/dl/gn/gn/windows-amd64/+/latest)
+from Google's build infrastructure (see "Versioning and distribution" below for
+how this is expected to work).
-Alternatively, you can build GN from source:
+Alternatively, you can build GN from source with a C++17 compiler:
git clone https://gn.googlesource.com/gn
cd gn
@@ -38,6 +110,13 @@ and `AR`.
There is a simple example in [examples/simple_build](examples/simple_build)
directory that is a good place to get started with the minimal configuration.
+To build and run the simple example with the default gcc compiler:
+
+ cd examples/simple_build
+ ../../out/gn gen -C out
+ ninja -C out
+ ./out/hello
+
For a maximal configuration see the Chromium setup:
* [.gn](https://cs.chromium.org/chromium/src/.gn)
* [BUILDCONFIG.gn](https://cs.chromium.org/chromium/src/build/config/BUILDCONFIG.gn)
@@ -68,12 +147,16 @@ version of how to patch is:
Then, to upload a change for review:
git commit
- git cl upload --gerrit
+ git push origin HEAD:refs/for/master
+
+The first time you do this you'll get an error from the server about a missing
+change-ID. Follow the directions in the error message to install the change-ID
+hook and run `git commit --amend` to apply the hook to the current commit.
When revising a change, use:
git commit --amend
- git cl upload --gerrit
+ git push origin HEAD:refs/for/master
which will add the new changes to the existing code review, rather than creating
a new one.
@@ -88,3 +171,35 @@ project').
You may ask questions and follow along with GN's development on Chromium's
[gn-dev@](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev)
Google Group.
+
+## Versioning and distribution
+
+Most open-source projects are designed to use the developer's computer's current
+toolchain such as compiler, linker, and build tool. But the large
+centrally controlled projects that GN is designed for typically want a more
+hermetic environment. They will ensure that developers are using a specific
+compatible toolchain that is versioned with the code
+
+As a result, GN expects that the project choose the appropriate version of GN
+that will work with each version of the project. There is no "current stable
+version" of GN that is expected to work for all projects.
+
+As a result, the GN developers to not maintain any packages in any of the
+various packaging systems (Debian, RedHat, HomeBrew, etc.). Some of these
+systems to have GN packages, but they are maintained by third parties and you
+should use at your own risk. Instead, we recommend you refer your checkout
+tooling to download binaries for a specific hash from [Google's build
+infrastructure](https://chrome-infra-packages.appspot.com/p/gn/gn) or compile
+your own.
+
+GN does not guarantee the backwards-compatibility of new versions and has no
+branches or versioning scheme beyond the sequence of commits to the master git
+branch (which is expected to be stable).
+
+In practice, however, GN is very backwards-compatible. The core functionality
+has been stable for many years and there is enough GN code at Google alone to
+make non-backwards-compatible changes very difficult, even if they were
+desirable.
+
+There have been discussions about adding a versioning scheme with some
+guarantees about backwards-compatibility, but nothing has yet been implemented.
diff --git a/gn/base/bind.h b/gn/base/bind.h
deleted file mode 100644
index 71df0fe952a..00000000000
--- a/gn/base/bind.h
+++ /dev/null
@@ -1,457 +0,0 @@
-// 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.
-
-#ifndef BASE_BIND_H_
-#define BASE_BIND_H_
-
-#include <utility>
-
-#include "base/bind_internal.h"
-
-// -----------------------------------------------------------------------------
-// Usage documentation
-// -----------------------------------------------------------------------------
-//
-// Overview:
-// base::BindOnce() and base::BindRepeating() are helpers for creating
-// base::OnceCallback and base::RepeatingCallback objects respectively.
-//
-// For a runnable object of n-arity, the base::Bind*() family allows partial
-// application of the first m arguments. The remaining n - m arguments must be
-// passed when invoking the callback with Run().
-//
-// // The first argument is bound at callback creation; the remaining
-// // two must be passed when calling Run() on the callback object.
-// base::OnceCallback<void(int, long)> cb = base::BindOnce(
-// [](short x, int y, long z) { return x * y * z; }, 42);
-//
-// When binding to a method, the receiver object must also be specified at
-// callback creation time. When Run() is invoked, the method will be invoked on
-// the specified receiver object.
-//
-// class C : public base::RefCounted<C> { void F(); };
-// auto instance = base::MakeRefCounted<C>();
-// auto cb = base::BindOnce(&C::F, instance);
-// cb.Run(); // Identical to instance->F()
-//
-// base::Bind is currently a type alias for base::BindRepeating(). In the
-// future, we expect to flip this to default to base::BindOnce().
-//
-// See //docs/callback.md for the full documentation.
-//
-// -----------------------------------------------------------------------------
-// Implementation notes
-// -----------------------------------------------------------------------------
-//
-// If you're reading the implementation, before proceeding further, you should
-// read the top comment of base/bind_internal.h for a definition of common
-// terms and concepts.
-
-namespace base {
-
-namespace internal {
-
-// IsOnceCallback<T> is a std::true_type if |T| is a OnceCallback.
-template <typename T>
-struct IsOnceCallback : std::false_type {};
-
-template <typename Signature>
-struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {};
-
-// Helper to assert that parameter |i| of type |Arg| can be bound, which means:
-// - |Arg| can be retained internally as |Storage|.
-// - |Arg| can be forwarded as |Unwrapped| to |Param|.
-template <size_t i,
- typename Arg,
- typename Storage,
- typename Unwrapped,
- typename Param>
-struct AssertConstructible {
- private:
- static constexpr bool param_is_forwardable =
- std::is_constructible<Param, Unwrapped>::value;
- // Unlike the check for binding into storage below, the check for
- // forwardability drops the const qualifier for repeating callbacks. This is
- // to try to catch instances where std::move()--which forwards as a const
- // reference with repeating callbacks--is used instead of base::Passed().
- static_assert(
- param_is_forwardable ||
- !std::is_constructible<Param, std::decay_t<Unwrapped>&&>::value,
- "Bound argument |i| is move-only but will be forwarded by copy. "
- "Ensure |Arg| is bound using base::Passed(), not std::move().");
- static_assert(
- param_is_forwardable,
- "Bound argument |i| of type |Arg| cannot be forwarded as "
- "|Unwrapped| to the bound functor, which declares it as |Param|.");
-
- static constexpr bool arg_is_storable =
- std::is_constructible<Storage, Arg>::value;
- static_assert(arg_is_storable ||
- !std::is_constructible<Storage, std::decay_t<Arg>&&>::value,
- "Bound argument |i| is move-only but will be bound by copy. "
- "Ensure |Arg| is mutable and bound using std::move().");
- static_assert(arg_is_storable,
- "Bound argument |i| of type |Arg| cannot be converted and "
- "bound as |Storage|.");
-};
-
-// Takes three same-length TypeLists, and applies AssertConstructible for each
-// triples.
-template <typename Index,
- typename Args,
- typename UnwrappedTypeList,
- typename ParamsList>
-struct AssertBindArgsValidity;
-
-template <size_t... Ns,
- typename... Args,
- typename... Unwrapped,
- typename... Params>
-struct AssertBindArgsValidity<std::index_sequence<Ns...>,
- TypeList<Args...>,
- TypeList<Unwrapped...>,
- TypeList<Params...>>
- : AssertConstructible<Ns, Args, std::decay_t<Args>, Unwrapped, Params>... {
- static constexpr bool ok = true;
-};
-
-// The implementation of TransformToUnwrappedType below.
-template <bool is_once, typename T>
-struct TransformToUnwrappedTypeImpl;
-
-template <typename T>
-struct TransformToUnwrappedTypeImpl<true, T> {
- using StoredType = std::decay_t<T>;
- using ForwardType = StoredType&&;
- using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
-};
-
-template <typename T>
-struct TransformToUnwrappedTypeImpl<false, T> {
- using StoredType = std::decay_t<T>;
- using ForwardType = const StoredType&;
- using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
-};
-
-// Transform |T| into `Unwrapped` type, which is passed to the target function.
-// Example:
-// In is_once == true case,
-// `int&&` -> `int&&`,
-// `const int&` -> `int&&`,
-// `OwnedWrapper<int>&` -> `int*&&`.
-// In is_once == false case,
-// `int&&` -> `const int&`,
-// `const int&` -> `const int&`,
-// `OwnedWrapper<int>&` -> `int* const &`.
-template <bool is_once, typename T>
-using TransformToUnwrappedType =
- typename TransformToUnwrappedTypeImpl<is_once, T>::Unwrapped;
-
-// Transforms |Args| into `Unwrapped` types, and packs them into a TypeList.
-// If |is_method| is true, tries to dereference the first argument to support
-// smart pointers.
-template <bool is_once, bool is_method, typename... Args>
-struct MakeUnwrappedTypeListImpl {
- using Type = TypeList<TransformToUnwrappedType<is_once, Args>...>;
-};
-
-// Performs special handling for this pointers.
-// Example:
-// int* -> int*,
-// std::unique_ptr<int> -> int*.
-template <bool is_once, typename Receiver, typename... Args>
-struct MakeUnwrappedTypeListImpl<is_once, true, Receiver, Args...> {
- using UnwrappedReceiver = TransformToUnwrappedType<is_once, Receiver>;
- using Type = TypeList<decltype(&*std::declval<UnwrappedReceiver>()),
- TransformToUnwrappedType<is_once, Args>...>;
-};
-
-template <bool is_once, bool is_method, typename... Args>
-using MakeUnwrappedTypeList =
- typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type;
-
-} // namespace internal
-
-// Bind as OnceCallback.
-template <typename Functor, typename... Args>
-inline OnceCallback<MakeUnboundRunType<Functor, Args...>> BindOnce(
- Functor&& functor,
- Args&&... args) {
- static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||
- (std::is_rvalue_reference<Functor&&>() &&
- !std::is_const<std::remove_reference_t<Functor>>()),
- "BindOnce requires non-const rvalue for OnceCallback binding."
- " I.e.: base::BindOnce(std::move(callback)).");
-
- // This block checks if each |args| matches to the corresponding params of the
- // target function. This check does not affect the behavior of Bind, but its
- // error message should be more readable.
- using Helper = internal::BindTypeHelper<Functor, Args...>;
- using FunctorTraits = typename Helper::FunctorTraits;
- using BoundArgsList = typename Helper::BoundArgsList;
- using UnwrappedArgsList =
- internal::MakeUnwrappedTypeList<true, FunctorTraits::is_method,
- Args&&...>;
- using BoundParamsList = typename Helper::BoundParamsList;
- static_assert(internal::AssertBindArgsValidity<
- std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
- UnwrappedArgsList, BoundParamsList>::ok,
- "The bound args need to be convertible to the target params.");
-
- using BindState = internal::MakeBindStateType<Functor, Args...>;
- using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
- using Invoker = internal::Invoker<BindState, UnboundRunType>;
- using CallbackType = OnceCallback<UnboundRunType>;
-
- // Store the invoke func into PolymorphicInvoke before casting it to
- // InvokeFuncStorage, so that we can ensure its type matches to
- // PolymorphicInvoke, to which CallbackType will cast back.
- using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
- PolymorphicInvoke invoke_func = &Invoker::RunOnce;
-
- using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
- return CallbackType(new BindState(
- reinterpret_cast<InvokeFuncStorage>(invoke_func),
- std::forward<Functor>(functor), std::forward<Args>(args)...));
-}
-
-// Bind as RepeatingCallback.
-template <typename Functor, typename... Args>
-inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>> BindRepeating(
- Functor&& functor,
- Args&&... args) {
- static_assert(
- !internal::IsOnceCallback<std::decay_t<Functor>>(),
- "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
-
- // This block checks if each |args| matches to the corresponding params of the
- // target function. This check does not affect the behavior of Bind, but its
- // error message should be more readable.
- using Helper = internal::BindTypeHelper<Functor, Args...>;
- using FunctorTraits = typename Helper::FunctorTraits;
- using BoundArgsList = typename Helper::BoundArgsList;
- using UnwrappedArgsList =
- internal::MakeUnwrappedTypeList<false, FunctorTraits::is_method,
- Args&&...>;
- using BoundParamsList = typename Helper::BoundParamsList;
- static_assert(internal::AssertBindArgsValidity<
- std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
- UnwrappedArgsList, BoundParamsList>::ok,
- "The bound args need to be convertible to the target params.");
-
- using BindState = internal::MakeBindStateType<Functor, Args...>;
- using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
- using Invoker = internal::Invoker<BindState, UnboundRunType>;
- using CallbackType = RepeatingCallback<UnboundRunType>;
-
- // Store the invoke func into PolymorphicInvoke before casting it to
- // InvokeFuncStorage, so that we can ensure its type matches to
- // PolymorphicInvoke, to which CallbackType will cast back.
- using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
- PolymorphicInvoke invoke_func = &Invoker::Run;
-
- using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
- return CallbackType(new BindState(
- reinterpret_cast<InvokeFuncStorage>(invoke_func),
- std::forward<Functor>(functor), std::forward<Args>(args)...));
-}
-
-// Unannotated Bind.
-// TODO(tzik): Deprecate this and migrate to OnceCallback and
-// RepeatingCallback, once they get ready.
-template <typename Functor, typename... Args>
-inline Callback<MakeUnboundRunType<Functor, Args...>> Bind(Functor&& functor,
- Args&&... args) {
- return base::BindRepeating(std::forward<Functor>(functor),
- std::forward<Args>(args)...);
-}
-
-// Special cases for binding to a base::Callback without extra bound arguments.
-template <typename Signature>
-OnceCallback<Signature> BindOnce(OnceCallback<Signature> closure) {
- return closure;
-}
-
-template <typename Signature>
-RepeatingCallback<Signature> BindRepeating(
- RepeatingCallback<Signature> closure) {
- return closure;
-}
-
-template <typename Signature>
-Callback<Signature> Bind(Callback<Signature> closure) {
- return closure;
-}
-
-// Unretained() allows Bind() to bind a non-refcounted class, and to disable
-// refcounting on arguments that are refcounted objects.
-//
-// EXAMPLE OF Unretained():
-//
-// class Foo {
-// public:
-// void func() { cout << "Foo:f" << endl; }
-// };
-//
-// // In some function somewhere.
-// Foo foo;
-// Closure foo_callback =
-// Bind(&Foo::func, Unretained(&foo));
-// foo_callback.Run(); // Prints "Foo:f".
-//
-// Without the Unretained() wrapper on |&foo|, the above call would fail
-// to compile because Foo does not support the AddRef() and Release() methods.
-template <typename T>
-static inline internal::UnretainedWrapper<T> Unretained(T* o) {
- return internal::UnretainedWrapper<T>(o);
-}
-
-// RetainedRef() accepts a ref counted object and retains a reference to it.
-// When the callback is called, the object is passed as a raw pointer.
-//
-// EXAMPLE OF RetainedRef():
-//
-// void foo(RefCountedBytes* bytes) {}
-//
-// scoped_refptr<RefCountedBytes> bytes = ...;
-// Closure callback = Bind(&foo, base::RetainedRef(bytes));
-// callback.Run();
-//
-// Without RetainedRef, the scoped_refptr would try to implicitly convert to
-// a raw pointer and fail compilation:
-//
-// Closure callback = Bind(&foo, bytes); // ERROR!
-template <typename T>
-static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
- return internal::RetainedRefWrapper<T>(o);
-}
-template <typename T>
-static inline internal::RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> o) {
- return internal::RetainedRefWrapper<T>(std::move(o));
-}
-
-// ConstRef() allows binding a constant reference to an argument rather
-// than a copy.
-//
-// EXAMPLE OF ConstRef():
-//
-// void foo(int arg) { cout << arg << endl }
-//
-// int n = 1;
-// Closure no_ref = Bind(&foo, n);
-// Closure has_ref = Bind(&foo, ConstRef(n));
-//
-// no_ref.Run(); // Prints "1"
-// has_ref.Run(); // Prints "1"
-//
-// n = 2;
-// no_ref.Run(); // Prints "1"
-// has_ref.Run(); // Prints "2"
-//
-// Note that because ConstRef() takes a reference on |n|, |n| must outlive all
-// its bound callbacks.
-template <typename T>
-static inline internal::ConstRefWrapper<T> ConstRef(const T& o) {
- return internal::ConstRefWrapper<T>(o);
-}
-
-// Owned() transfers ownership of an object to the Callback resulting from
-// bind; the object will be deleted when the Callback is deleted.
-//
-// EXAMPLE OF Owned():
-//
-// void foo(int* arg) { cout << *arg << endl }
-//
-// int* pn = new int(1);
-// Closure foo_callback = Bind(&foo, Owned(pn));
-//
-// foo_callback.Run(); // Prints "1"
-// foo_callback.Run(); // Prints "1"
-// *n = 2;
-// foo_callback.Run(); // Prints "2"
-//
-// foo_callback.Reset(); // |pn| is deleted. Also will happen when
-// // |foo_callback| goes out of scope.
-//
-// Without Owned(), someone would have to know to delete |pn| when the last
-// reference to the Callback is deleted.
-template <typename T>
-static inline internal::OwnedWrapper<T> Owned(T* o) {
- return internal::OwnedWrapper<T>(o);
-}
-
-// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr)
-// through a Callback. Logically, this signifies a destructive transfer of
-// the state of the argument into the target function. Invoking
-// Callback::Run() twice on a Callback that was created with a Passed()
-// argument will CHECK() because the first invocation would have already
-// transferred ownership to the target function.
-//
-// Note that Passed() is not necessary with BindOnce(), as std::move() does the
-// same thing. Avoid Passed() in favor of std::move() with BindOnce().
-//
-// EXAMPLE OF Passed():
-//
-// void TakesOwnership(std::unique_ptr<Foo> arg) { }
-// std::unique_ptr<Foo> CreateFoo() { return std::make_unique<Foo>();
-// }
-//
-// auto f = std::make_unique<Foo>();
-//
-// // |cb| is given ownership of Foo(). |f| is now NULL.
-// // You can use std::move(f) in place of &f, but it's more verbose.
-// Closure cb = Bind(&TakesOwnership, Passed(&f));
-//
-// // Run was never called so |cb| still owns Foo() and deletes
-// // it on Reset().
-// cb.Reset();
-//
-// // |cb| is given a new Foo created by CreateFoo().
-// cb = Bind(&TakesOwnership, Passed(CreateFoo()));
-//
-// // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
-// // no longer owns Foo() and, if reset, would not delete Foo().
-// cb.Run(); // Foo() is now transferred to |arg| and deleted.
-// cb.Run(); // This CHECK()s since Foo() already been used once.
-//
-// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and
-// is best suited for use with the return value of a function or other temporary
-// rvalues. The second takes a pointer to the scoper and is just syntactic sugar
-// to avoid having to write Passed(std::move(scoper)).
-//
-// Both versions of Passed() prevent T from being an lvalue reference. The first
-// via use of enable_if, and the second takes a T* which will not bind to T&.
-template <typename T,
- std::enable_if_t<!std::is_lvalue_reference<T>::value>* = nullptr>
-static inline internal::PassedWrapper<T> Passed(T&& scoper) {
- return internal::PassedWrapper<T>(std::move(scoper));
-}
-template <typename T>
-static inline internal::PassedWrapper<T> Passed(T* scoper) {
- return internal::PassedWrapper<T>(std::move(*scoper));
-}
-
-// IgnoreResult() is used to adapt a function or Callback with a return type to
-// one with a void return. This is most useful if you have a function with,
-// say, a pesky ignorable bool return that you want to use with PostTask or
-// something else that expect a Callback with a void return.
-//
-// EXAMPLE OF IgnoreResult():
-//
-// int DoSomething(int arg) { cout << arg << endl; }
-//
-// // Assign to a Callback with a void return type.
-// Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething));
-// cb->Run(1); // Prints "1".
-//
-// // Prints "1" on |ml|.
-// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1);
-template <typename T>
-static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
- return internal::IgnoreResultHelper<T>(std::move(data));
-}
-
-} // namespace base
-
-#endif // BASE_BIND_H_
diff --git a/gn/base/bind_internal.h b/gn/base/bind_internal.h
deleted file mode 100644
index 0dc59e60c9a..00000000000
--- a/gn/base/bind_internal.h
+++ /dev/null
@@ -1,912 +0,0 @@
-// 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.
-
-#ifndef BASE_BIND_INTERNAL_H_
-#define BASE_BIND_INTERNAL_H_
-
-#include <stddef.h>
-
-#include <type_traits>
-#include <utility>
-
-#include "base/callback_internal.h"
-#include "base/memory/raw_scoped_refptr_mismatch_checker.h"
-#include "base/memory/weak_ptr.h"
-#include "base/template_util.h"
-#include "util/build_config.h"
-
-// See base/callback.h for user documentation.
-//
-//
-// CONCEPTS:
-// Functor -- A movable type representing something that should be called.
-// All function pointers and Callback<> are functors even if the
-// invocation syntax differs.
-// RunType -- A function type (as opposed to function _pointer_ type) for
-// a Callback<>::Run(). Usually just a convenience typedef.
-// (Bound)Args -- A set of types that stores the arguments.
-//
-// Types:
-// ForceVoidReturn<> -- Helper class for translating function signatures to
-// equivalent forms with a "void" return type.
-// FunctorTraits<> -- Type traits used to determine the correct RunType and
-// invocation manner for a Functor. This is where function
-// signature adapters are applied.
-// InvokeHelper<> -- Take a Functor + arguments and actully invokes it.
-// Handle the differing syntaxes needed for WeakPtr<>
-// support. This is separate from Invoker to avoid creating
-// multiple version of Invoker<>.
-// Invoker<> -- Unwraps the curried parameters and executes the Functor.
-// BindState<> -- Stores the curried parameters, and is the main entry point
-// into the Bind() system.
-
-namespace base {
-
-template <typename T>
-struct IsWeakReceiver;
-
-template <typename>
-struct BindUnwrapTraits;
-
-template <typename Functor, typename BoundArgsTuple, typename SFINAE = void>
-struct CallbackCancellationTraits;
-
-namespace internal {
-
-template <typename Functor, typename SFINAE = void>
-struct FunctorTraits;
-
-template <typename T>
-class UnretainedWrapper {
- public:
- explicit UnretainedWrapper(T* o) : ptr_(o) {}
- T* get() const { return ptr_; }
-
- private:
- T* ptr_;
-};
-
-template <typename T>
-class ConstRefWrapper {
- public:
- explicit ConstRefWrapper(const T& o) : ptr_(&o) {}
- const T& get() const { return *ptr_; }
-
- private:
- const T* ptr_;
-};
-
-template <typename T>
-class RetainedRefWrapper {
- public:
- explicit RetainedRefWrapper(T* o) : ptr_(o) {}
- explicit RetainedRefWrapper(scoped_refptr<T> o) : ptr_(std::move(o)) {}
- T* get() const { return ptr_.get(); }
-
- private:
- scoped_refptr<T> ptr_;
-};
-
-template <typename T>
-struct IgnoreResultHelper {
- explicit IgnoreResultHelper(T functor) : functor_(std::move(functor)) {}
- explicit operator bool() const { return !!functor_; }
-
- T functor_;
-};
-
-// An alternate implementation is to avoid the destructive copy, and instead
-// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to
-// a class that is essentially a std::unique_ptr<>.
-//
-// The current implementation has the benefit though of leaving ParamTraits<>
-// fully in callback_internal.h as well as avoiding type conversions during
-// storage.
-template <typename T>
-class OwnedWrapper {
- public:
- explicit OwnedWrapper(T* o) : ptr_(o) {}
- ~OwnedWrapper() { delete ptr_; }
- T* get() const { return ptr_; }
- OwnedWrapper(OwnedWrapper&& other) {
- ptr_ = other.ptr_;
- other.ptr_ = NULL;
- }
-
- private:
- mutable T* ptr_;
-};
-
-// PassedWrapper is a copyable adapter for a scoper that ignores const.
-//
-// It is needed to get around the fact that Bind() takes a const reference to
-// all its arguments. Because Bind() takes a const reference to avoid
-// unnecessary copies, it is incompatible with movable-but-not-copyable
-// types; doing a destructive "move" of the type into Bind() would violate
-// the const correctness.
-//
-// This conundrum cannot be solved without either C++11 rvalue references or
-// a O(2^n) blowup of Bind() templates to handle each combination of regular
-// types and movable-but-not-copyable types. Thus we introduce a wrapper type
-// that is copyable to transmit the correct type information down into
-// BindState<>. Ignoring const in this type makes sense because it is only
-// created when we are explicitly trying to do a destructive move.
-//
-// Two notes:
-// 1) PassedWrapper supports any type that has a move constructor, however
-// the type will need to be specifically whitelisted in order for it to be
-// bound to a Callback. We guard this explicitly at the call of Passed()
-// to make for clear errors. Things not given to Passed() will be forwarded
-// and stored by value which will not work for general move-only types.
-// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL"
-// scoper to a Callback and allow the Callback to execute once.
-template <typename T>
-class PassedWrapper {
- public:
- explicit PassedWrapper(T&& scoper)
- : is_valid_(true), scoper_(std::move(scoper)) {}
- PassedWrapper(PassedWrapper&& other)
- : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {}
- T Take() const {
- CHECK(is_valid_);
- is_valid_ = false;
- return std::move(scoper_);
- }
-
- private:
- mutable bool is_valid_;
- mutable T scoper_;
-};
-
-template <typename T>
-using Unwrapper = BindUnwrapTraits<std::decay_t<T>>;
-
-template <typename T>
-decltype(auto) Unwrap(T&& o) {
- return Unwrapper<T>::Unwrap(std::forward<T>(o));
-}
-
-// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
-// method. It is used internally by Bind() to select the correct
-// InvokeHelper that will no-op itself in the event the WeakPtr<> for
-// the target object is invalidated.
-//
-// The first argument should be the type of the object that will be received by
-// the method.
-template <bool is_method, typename... Args>
-struct IsWeakMethod : std::false_type {};
-
-template <typename T, typename... Args>
-struct IsWeakMethod<true, T, Args...> : IsWeakReceiver<T> {};
-
-// Packs a list of types to hold them in a single type.
-template <typename... Types>
-struct TypeList {};
-
-// Used for DropTypeListItem implementation.
-template <size_t n, typename List>
-struct DropTypeListItemImpl;
-
-// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.
-template <size_t n, typename T, typename... List>
-struct DropTypeListItemImpl<n, TypeList<T, List...>>
- : DropTypeListItemImpl<n - 1, TypeList<List...>> {};
-
-template <typename T, typename... List>
-struct DropTypeListItemImpl<0, TypeList<T, List...>> {
- using Type = TypeList<T, List...>;
-};
-
-template <>
-struct DropTypeListItemImpl<0, TypeList<>> {
- using Type = TypeList<>;
-};
-
-// A type-level function that drops |n| list item from given TypeList.
-template <size_t n, typename List>
-using DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type;
-
-// Used for TakeTypeListItem implementation.
-template <size_t n, typename List, typename... Accum>
-struct TakeTypeListItemImpl;
-
-// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.
-template <size_t n, typename T, typename... List, typename... Accum>
-struct TakeTypeListItemImpl<n, TypeList<T, List...>, Accum...>
- : TakeTypeListItemImpl<n - 1, TypeList<List...>, Accum..., T> {};
-
-template <typename T, typename... List, typename... Accum>
-struct TakeTypeListItemImpl<0, TypeList<T, List...>, Accum...> {
- using Type = TypeList<Accum...>;
-};
-
-template <typename... Accum>
-struct TakeTypeListItemImpl<0, TypeList<>, Accum...> {
- using Type = TypeList<Accum...>;
-};
-
-// A type-level function that takes first |n| list item from given TypeList.
-// E.g. TakeTypeListItem<3, TypeList<A, B, C, D>> is evaluated to
-// TypeList<A, B, C>.
-template <size_t n, typename List>
-using TakeTypeListItem = typename TakeTypeListItemImpl<n, List>::Type;
-
-// Used for ConcatTypeLists implementation.
-template <typename List1, typename List2>
-struct ConcatTypeListsImpl;
-
-template <typename... Types1, typename... Types2>
-struct ConcatTypeListsImpl<TypeList<Types1...>, TypeList<Types2...>> {
- using Type = TypeList<Types1..., Types2...>;
-};
-
-// A type-level function that concats two TypeLists.
-template <typename List1, typename List2>
-using ConcatTypeLists = typename ConcatTypeListsImpl<List1, List2>::Type;
-
-// Used for MakeFunctionType implementation.
-template <typename R, typename ArgList>
-struct MakeFunctionTypeImpl;
-
-template <typename R, typename... Args>
-struct MakeFunctionTypeImpl<R, TypeList<Args...>> {
- // MSVC 2013 doesn't support Type Alias of function types.
- // Revisit this after we update it to newer version.
- typedef R Type(Args...);
-};
-
-// A type-level function that constructs a function type that has |R| as its
-// return type and has TypeLists items as its arguments.
-template <typename R, typename ArgList>
-using MakeFunctionType = typename MakeFunctionTypeImpl<R, ArgList>::Type;
-
-// Used for ExtractArgs and ExtractReturnType.
-template <typename Signature>
-struct ExtractArgsImpl;
-
-template <typename R, typename... Args>
-struct ExtractArgsImpl<R(Args...)> {
- using ReturnType = R;
- using ArgsList = TypeList<Args...>;
-};
-
-// A type-level function that extracts function arguments into a TypeList.
-// E.g. ExtractArgs<R(A, B, C)> is evaluated to TypeList<A, B, C>.
-template <typename Signature>
-using ExtractArgs = typename ExtractArgsImpl<Signature>::ArgsList;
-
-// A type-level function that extracts the return type of a function.
-// E.g. ExtractReturnType<R(A, B, C)> is evaluated to R.
-template <typename Signature>
-using ExtractReturnType = typename ExtractArgsImpl<Signature>::ReturnType;
-
-template <typename Callable,
- typename Signature = decltype(&Callable::operator())>
-struct ExtractCallableRunTypeImpl;
-
-template <typename Callable, typename R, typename... Args>
-struct ExtractCallableRunTypeImpl<Callable, R (Callable::*)(Args...)> {
- using Type = R(Args...);
-};
-
-template <typename Callable, typename R, typename... Args>
-struct ExtractCallableRunTypeImpl<Callable, R (Callable::*)(Args...) const> {
- using Type = R(Args...);
-};
-
-// Evaluated to RunType of the given callable type.
-// Example:
-// auto f = [](int, char*) { return 0.1; };
-// ExtractCallableRunType<decltype(f)>
-// is evaluated to
-// double(int, char*);
-template <typename Callable>
-using ExtractCallableRunType =
- typename ExtractCallableRunTypeImpl<Callable>::Type;
-
-// IsCallableObject<Functor> is std::true_type if |Functor| has operator().
-// Otherwise, it's std::false_type.
-// Example:
-// IsCallableObject<void(*)()>::value is false.
-//
-// struct Foo {};
-// IsCallableObject<void(Foo::*)()>::value is false.
-//
-// int i = 0;
-// auto f = [i]() {};
-// IsCallableObject<decltype(f)>::value is false.
-template <typename Functor, typename SFINAE = void>
-struct IsCallableObject : std::false_type {};
-
-template <typename Callable>
-struct IsCallableObject<Callable, void_t<decltype(&Callable::operator())>>
- : std::true_type {};
-
-// HasRefCountedTypeAsRawPtr selects true_type when any of the |Args| is a raw
-// pointer to a RefCounted type.
-// Implementation note: This non-specialized case handles zero-arity case only.
-// Non-zero-arity cases should be handled by the specialization below.
-template <typename... Args>
-struct HasRefCountedTypeAsRawPtr : std::false_type {};
-
-// Implementation note: Select true_type if the first parameter is a raw pointer
-// to a RefCounted type. Otherwise, skip the first parameter and check rest of
-// parameters recursively.
-template <typename T, typename... Args>
-struct HasRefCountedTypeAsRawPtr<T, Args...>
- : std::conditional_t<NeedsScopedRefptrButGetsRawPtr<T>::value,
- std::true_type,
- HasRefCountedTypeAsRawPtr<Args...>> {};
-
-// ForceVoidReturn<>
-//
-// Set of templates that support forcing the function return type to void.
-template <typename Sig>
-struct ForceVoidReturn;
-
-template <typename R, typename... Args>
-struct ForceVoidReturn<R(Args...)> {
- using RunType = void(Args...);
-};
-
-// FunctorTraits<>
-//
-// See description at top of file.
-template <typename Functor, typename SFINAE>
-struct FunctorTraits;
-
-// For empty callable types.
-// This specialization is intended to allow binding captureless lambdas by
-// base::Bind(), based on the fact that captureless lambdas are empty while
-// capturing lambdas are not. This also allows any functors as far as it's an
-// empty class.
-// Example:
-//
-// // Captureless lambdas are allowed.
-// []() {return 42;};
-//
-// // Capturing lambdas are *not* allowed.
-// int x;
-// [x]() {return x;};
-//
-// // Any empty class with operator() is allowed.
-// struct Foo {
-// void operator()() const {}
-// // No non-static member variable and no virtual functions.
-// };
-template <typename Functor>
-struct FunctorTraits<Functor,
- std::enable_if_t<IsCallableObject<Functor>::value &&
- std::is_empty<Functor>::value>> {
- using RunType = ExtractCallableRunType<Functor>;
- static constexpr bool is_method = false;
- static constexpr bool is_nullable = false;
-
- template <typename RunFunctor, typename... RunArgs>
- static ExtractReturnType<RunType> Invoke(RunFunctor&& functor,
- RunArgs&&... args) {
- return std::forward<RunFunctor>(functor)(std::forward<RunArgs>(args)...);
- }
-};
-
-// For functions.
-template <typename R, typename... Args>
-struct FunctorTraits<R (*)(Args...)> {
- using RunType = R(Args...);
- static constexpr bool is_method = false;
- static constexpr bool is_nullable = true;
-
- template <typename Function, typename... RunArgs>
- static R Invoke(Function&& function, RunArgs&&... args) {
- return std::forward<Function>(function)(std::forward<RunArgs>(args)...);
- }
-};
-
-#if defined(OS_WIN) && !defined(ARCH_CPU_X86_64)
-
-// For functions.
-template <typename R, typename... Args>
-struct FunctorTraits<R(__stdcall*)(Args...)> {
- using RunType = R(Args...);
- static constexpr bool is_method = false;
- static constexpr bool is_nullable = true;
-
- template <typename... RunArgs>
- static R Invoke(R(__stdcall* function)(Args...), RunArgs&&... args) {
- return function(std::forward<RunArgs>(args)...);
- }
-};
-
-// For functions.
-template <typename R, typename... Args>
-struct FunctorTraits<R(__fastcall*)(Args...)> {
- using RunType = R(Args...);
- static constexpr bool is_method = false;
- static constexpr bool is_nullable = true;
-
- template <typename... RunArgs>
- static R Invoke(R(__fastcall* function)(Args...), RunArgs&&... args) {
- return function(std::forward<RunArgs>(args)...);
- }
-};
-
-#endif // defined(OS_WIN) && !defined(ARCH_CPU_X86_64)
-
-// For methods.
-template <typename R, typename Receiver, typename... Args>
-struct FunctorTraits<R (Receiver::*)(Args...)> {
- using RunType = R(Receiver*, Args...);
- static constexpr bool is_method = true;
- static constexpr bool is_nullable = true;
-
- template <typename Method, typename ReceiverPtr, typename... RunArgs>
- static R Invoke(Method method,
- ReceiverPtr&& receiver_ptr,
- RunArgs&&... args) {
- return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);
- }
-};
-
-// For const methods.
-template <typename R, typename Receiver, typename... Args>
-struct FunctorTraits<R (Receiver::*)(Args...) const> {
- using RunType = R(const Receiver*, Args...);
- static constexpr bool is_method = true;
- static constexpr bool is_nullable = true;
-
- template <typename Method, typename ReceiverPtr, typename... RunArgs>
- static R Invoke(Method method,
- ReceiverPtr&& receiver_ptr,
- RunArgs&&... args) {
- return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);
- }
-};
-
-#ifdef __cpp_noexcept_function_type
-// noexcept makes a distinct function type in C++17.
-// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and
-// different in C++17.
-template <typename R, typename... Args>
-struct FunctorTraits<R (*)(Args...) noexcept> : FunctorTraits<R (*)(Args...)> {
-};
-
-template <typename R, typename Receiver, typename... Args>
-struct FunctorTraits<R (Receiver::*)(Args...) noexcept>
- : FunctorTraits<R (Receiver::*)(Args...)> {};
-
-template <typename R, typename Receiver, typename... Args>
-struct FunctorTraits<R (Receiver::*)(Args...) const noexcept>
- : FunctorTraits<R (Receiver::*)(Args...) const> {};
-#endif
-
-// For IgnoreResults.
-template <typename T>
-struct FunctorTraits<IgnoreResultHelper<T>> : FunctorTraits<T> {
- using RunType =
- typename ForceVoidReturn<typename FunctorTraits<T>::RunType>::RunType;
-
- template <typename IgnoreResultType, typename... RunArgs>
- static void Invoke(IgnoreResultType&& ignore_result_helper,
- RunArgs&&... args) {
- FunctorTraits<T>::Invoke(
- std::forward<IgnoreResultType>(ignore_result_helper).functor_,
- std::forward<RunArgs>(args)...);
- }
-};
-
-// For OnceCallbacks.
-template <typename R, typename... Args>
-struct FunctorTraits<OnceCallback<R(Args...)>> {
- using RunType = R(Args...);
- static constexpr bool is_method = false;
- static constexpr bool is_nullable = true;
-
- template <typename CallbackType, typename... RunArgs>
- static R Invoke(CallbackType&& callback, RunArgs&&... args) {
- DCHECK(!callback.is_null());
- return std::forward<CallbackType>(callback).Run(
- std::forward<RunArgs>(args)...);
- }
-};
-
-// For RepeatingCallbacks.
-template <typename R, typename... Args>
-struct FunctorTraits<RepeatingCallback<R(Args...)>> {
- using RunType = R(Args...);
- static constexpr bool is_method = false;
- static constexpr bool is_nullable = true;
-
- template <typename CallbackType, typename... RunArgs>
- static R Invoke(CallbackType&& callback, RunArgs&&... args) {
- DCHECK(!callback.is_null());
- return std::forward<CallbackType>(callback).Run(
- std::forward<RunArgs>(args)...);
- }
-};
-
-template <typename Functor>
-using MakeFunctorTraits = FunctorTraits<std::decay_t<Functor>>;
-
-// InvokeHelper<>
-//
-// There are 2 logical InvokeHelper<> specializations: normal, WeakCalls.
-//
-// The normal type just calls the underlying runnable.
-//
-// WeakCalls need special syntax that is applied to the first argument to check
-// if they should no-op themselves.
-template <bool is_weak_call, typename ReturnType>
-struct InvokeHelper;
-
-template <typename ReturnType>
-struct InvokeHelper<false, ReturnType> {
- template <typename Functor, typename... RunArgs>
- static inline ReturnType MakeItSo(Functor&& functor, RunArgs&&... args) {
- using Traits = MakeFunctorTraits<Functor>;
- return Traits::Invoke(std::forward<Functor>(functor),
- std::forward<RunArgs>(args)...);
- }
-};
-
-template <typename ReturnType>
-struct InvokeHelper<true, ReturnType> {
- // WeakCalls are only supported for functions with a void return type.
- // Otherwise, the function result would be undefined if the the WeakPtr<>
- // is invalidated.
- static_assert(std::is_void<ReturnType>::value,
- "weak_ptrs can only bind to methods without return values");
-
- template <typename Functor, typename BoundWeakPtr, typename... RunArgs>
- static inline void MakeItSo(Functor&& functor,
- BoundWeakPtr&& weak_ptr,
- RunArgs&&... args) {
- if (!weak_ptr)
- return;
- using Traits = MakeFunctorTraits<Functor>;
- Traits::Invoke(std::forward<Functor>(functor),
- std::forward<BoundWeakPtr>(weak_ptr),
- std::forward<RunArgs>(args)...);
- }
-};
-
-// Invoker<>
-//
-// See description at the top of the file.
-template <typename StorageType, typename UnboundRunType>
-struct Invoker;
-
-template <typename StorageType, typename R, typename... UnboundArgs>
-struct Invoker<StorageType, R(UnboundArgs...)> {
- static R RunOnce(BindStateBase* base,
- PassingTraitsType<UnboundArgs>... unbound_args) {
- // Local references to make debugger stepping easier. If in a debugger,
- // you really want to warp ahead and step through the
- // InvokeHelper<>::MakeItSo() call below.
- StorageType* storage = static_cast<StorageType*>(base);
- static constexpr size_t num_bound_args =
- std::tuple_size<decltype(storage->bound_args_)>::value;
- return RunImpl(std::move(storage->functor_),
- std::move(storage->bound_args_),
- std::make_index_sequence<num_bound_args>(),
- std::forward<UnboundArgs>(unbound_args)...);
- }
-
- static R Run(BindStateBase* base,
- PassingTraitsType<UnboundArgs>... unbound_args) {
- // Local references to make debugger stepping easier. If in a debugger,
- // you really want to warp ahead and step through the
- // InvokeHelper<>::MakeItSo() call below.
- const StorageType* storage = static_cast<StorageType*>(base);
- static constexpr size_t num_bound_args =
- std::tuple_size<decltype(storage->bound_args_)>::value;
- return RunImpl(storage->functor_, storage->bound_args_,
- std::make_index_sequence<num_bound_args>(),
- std::forward<UnboundArgs>(unbound_args)...);
- }
-
- private:
- template <typename Functor, typename BoundArgsTuple, size_t... indices>
- static inline R RunImpl(Functor&& functor,
- BoundArgsTuple&& bound,
- std::index_sequence<indices...>,
- UnboundArgs&&... unbound_args) {
- static constexpr bool is_method = MakeFunctorTraits<Functor>::is_method;
-
- using DecayedArgsTuple = std::decay_t<BoundArgsTuple>;
- static constexpr bool is_weak_call =
- IsWeakMethod<is_method,
- std::tuple_element_t<indices, DecayedArgsTuple>...>();
-
- return InvokeHelper<is_weak_call, R>::MakeItSo(
- std::forward<Functor>(functor),
- Unwrap(std::get<indices>(std::forward<BoundArgsTuple>(bound)))...,
- std::forward<UnboundArgs>(unbound_args)...);
- }
-};
-
-// Extracts necessary type info from Functor and BoundArgs.
-// Used to implement MakeUnboundRunType, BindOnce and BindRepeating.
-template <typename Functor, typename... BoundArgs>
-struct BindTypeHelper {
- static constexpr size_t num_bounds = sizeof...(BoundArgs);
- using FunctorTraits = MakeFunctorTraits<Functor>;
-
- // Example:
- // When Functor is `double (Foo::*)(int, const std::string&)`, and BoundArgs
- // is a template pack of `Foo*` and `int16_t`:
- // - RunType is `double(Foo*, int, const std::string&)`,
- // - ReturnType is `double`,
- // - RunParamsList is `TypeList<Foo*, int, const std::string&>`,
- // - BoundParamsList is `TypeList<Foo*, int>`,
- // - UnboundParamsList is `TypeList<const std::string&>`,
- // - BoundArgsList is `TypeList<Foo*, int16_t>`,
- // - UnboundRunType is `double(const std::string&)`.
- using RunType = typename FunctorTraits::RunType;
- using ReturnType = ExtractReturnType<RunType>;
-
- using RunParamsList = ExtractArgs<RunType>;
- using BoundParamsList = TakeTypeListItem<num_bounds, RunParamsList>;
- using UnboundParamsList = DropTypeListItem<num_bounds, RunParamsList>;
-
- using BoundArgsList = TypeList<BoundArgs...>;
-
- using UnboundRunType = MakeFunctionType<ReturnType, UnboundParamsList>;
-};
-
-template <typename Functor>
-std::enable_if_t<FunctorTraits<Functor>::is_nullable, bool> IsNull(
- const Functor& functor) {
- return !functor;
-}
-
-template <typename Functor>
-std::enable_if_t<!FunctorTraits<Functor>::is_nullable, bool> IsNull(
- const Functor&) {
- return false;
-}
-
-// Used by ApplyCancellationTraits below.
-template <typename Functor, typename BoundArgsTuple, size_t... indices>
-bool ApplyCancellationTraitsImpl(const Functor& functor,
- const BoundArgsTuple& bound_args,
- std::index_sequence<indices...>) {
- return CallbackCancellationTraits<Functor, BoundArgsTuple>::IsCancelled(
- functor, std::get<indices>(bound_args)...);
-}
-
-// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns
-// true if the callback |base| represents is canceled.
-template <typename BindStateType>
-bool ApplyCancellationTraits(const BindStateBase* base) {
- const BindStateType* storage = static_cast<const BindStateType*>(base);
- static constexpr size_t num_bound_args =
- std::tuple_size<decltype(storage->bound_args_)>::value;
- return ApplyCancellationTraitsImpl(
- storage->functor_, storage->bound_args_,
- std::make_index_sequence<num_bound_args>());
-};
-
-// BindState<>
-//
-// This stores all the state passed into Bind().
-template <typename Functor, typename... BoundArgs>
-struct BindState final : BindStateBase {
- using IsCancellable = std::integral_constant<
- bool,
- CallbackCancellationTraits<Functor,
- std::tuple<BoundArgs...>>::is_cancellable>;
-
- template <typename ForwardFunctor, typename... ForwardBoundArgs>
- explicit BindState(BindStateBase::InvokeFuncStorage invoke_func,
- ForwardFunctor&& functor,
- ForwardBoundArgs&&... bound_args)
- // IsCancellable is std::false_type if
- // CallbackCancellationTraits<>::IsCancelled returns always false.
- // Otherwise, it's std::true_type.
- : BindState(IsCancellable{},
- invoke_func,
- std::forward<ForwardFunctor>(functor),
- std::forward<ForwardBoundArgs>(bound_args)...) {}
-
- Functor functor_;
- std::tuple<BoundArgs...> bound_args_;
-
- private:
- template <typename ForwardFunctor, typename... ForwardBoundArgs>
- explicit BindState(std::true_type,
- BindStateBase::InvokeFuncStorage invoke_func,
- ForwardFunctor&& functor,
- ForwardBoundArgs&&... bound_args)
- : BindStateBase(invoke_func,
- &Destroy,
- &ApplyCancellationTraits<BindState>),
- functor_(std::forward<ForwardFunctor>(functor)),
- bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
- DCHECK(!IsNull(functor_));
- }
-
- template <typename ForwardFunctor, typename... ForwardBoundArgs>
- explicit BindState(std::false_type,
- BindStateBase::InvokeFuncStorage invoke_func,
- ForwardFunctor&& functor,
- ForwardBoundArgs&&... bound_args)
- : BindStateBase(invoke_func, &Destroy),
- functor_(std::forward<ForwardFunctor>(functor)),
- bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
- DCHECK(!IsNull(functor_));
- }
-
- ~BindState() = default;
-
- static void Destroy(const BindStateBase* self) {
- delete static_cast<const BindState*>(self);
- }
-};
-
-// Used to implement MakeBindStateType.
-template <bool is_method, typename Functor, typename... BoundArgs>
-struct MakeBindStateTypeImpl;
-
-template <typename Functor, typename... BoundArgs>
-struct MakeBindStateTypeImpl<false, Functor, BoundArgs...> {
- static_assert(!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value,
- "A parameter is a refcounted type and needs scoped_refptr.");
- using Type = BindState<std::decay_t<Functor>, std::decay_t<BoundArgs>...>;
-};
-
-template <typename Functor>
-struct MakeBindStateTypeImpl<true, Functor> {
- using Type = BindState<std::decay_t<Functor>>;
-};
-
-template <typename Functor, typename Receiver, typename... BoundArgs>
-struct MakeBindStateTypeImpl<true, Functor, Receiver, BoundArgs...> {
- private:
- using DecayedReceiver = std::decay_t<Receiver>;
-
- static_assert(!std::is_array<std::remove_reference_t<Receiver>>::value,
- "First bound argument to a method cannot be an array.");
- static_assert(
- !std::is_pointer<DecayedReceiver>::value ||
- IsRefCountedType<std::remove_pointer_t<DecayedReceiver>>::value,
- "Receivers may not be raw pointers. If using a raw pointer here is safe"
- " and has no lifetime concerns, use base::Unretained() and document why"
- " it's safe.");
- static_assert(!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value,
- "A parameter is a refcounted type and needs scoped_refptr.");
-
- public:
- using Type = BindState<
- std::decay_t<Functor>,
- std::conditional_t<std::is_pointer<DecayedReceiver>::value,
- scoped_refptr<std::remove_pointer_t<DecayedReceiver>>,
- DecayedReceiver>,
- std::decay_t<BoundArgs>...>;
-};
-
-template <typename Functor, typename... BoundArgs>
-using MakeBindStateType =
- typename MakeBindStateTypeImpl<MakeFunctorTraits<Functor>::is_method,
- Functor,
- BoundArgs...>::Type;
-
-} // namespace internal
-
-// An injection point to control |this| pointer behavior on a method invocation.
-// If IsWeakReceiver<> is true_type for |T| and |T| is used for a receiver of a
-// method, base::Bind cancels the method invocation if the receiver is tested as
-// false.
-// E.g. Foo::bar() is not called:
-// struct Foo : base::SupportsWeakPtr<Foo> {
-// void bar() {}
-// };
-//
-// WeakPtr<Foo> oo = nullptr;
-// base::Bind(&Foo::bar, oo).Run();
-template <typename T>
-struct IsWeakReceiver : std::false_type {};
-
-template <typename T>
-struct IsWeakReceiver<internal::ConstRefWrapper<T>> : IsWeakReceiver<T> {};
-
-template <typename T>
-struct IsWeakReceiver<WeakPtr<T>> : std::true_type {};
-
-// An injection point to control how bound objects passed to the target
-// function. BindUnwrapTraits<>::Unwrap() is called for each bound objects right
-// before the target function is invoked.
-template <typename>
-struct BindUnwrapTraits {
- template <typename T>
- static T&& Unwrap(T&& o) {
- return std::forward<T>(o);
- }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::UnretainedWrapper<T>> {
- static T* Unwrap(const internal::UnretainedWrapper<T>& o) { return o.get(); }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::ConstRefWrapper<T>> {
- static const T& Unwrap(const internal::ConstRefWrapper<T>& o) {
- return o.get();
- }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::RetainedRefWrapper<T>> {
- static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::OwnedWrapper<T>> {
- static T* Unwrap(const internal::OwnedWrapper<T>& o) { return o.get(); }
-};
-
-template <typename T>
-struct BindUnwrapTraits<internal::PassedWrapper<T>> {
- static T Unwrap(const internal::PassedWrapper<T>& o) { return o.Take(); }
-};
-
-// CallbackCancellationTraits allows customization of Callback's cancellation
-// semantics. By default, callbacks are not cancellable. A specialization should
-// set is_cancellable = true and implement an IsCancelled() that returns if the
-// callback should be cancelled.
-template <typename Functor, typename BoundArgsTuple, typename SFINAE>
-struct CallbackCancellationTraits {
- static constexpr bool is_cancellable = false;
-};
-
-// Specialization for method bound to weak pointer receiver.
-template <typename Functor, typename... BoundArgs>
-struct CallbackCancellationTraits<
- Functor,
- std::tuple<BoundArgs...>,
- std::enable_if_t<
- internal::IsWeakMethod<internal::FunctorTraits<Functor>::is_method,
- BoundArgs...>::value>> {
- static constexpr bool is_cancellable = true;
-
- template <typename Receiver, typename... Args>
- static bool IsCancelled(const Functor&,
- const Receiver& receiver,
- const Args&...) {
- return !receiver;
- }
-};
-
-// Specialization for a nested bind.
-template <typename Signature, typename... BoundArgs>
-struct CallbackCancellationTraits<OnceCallback<Signature>,
- std::tuple<BoundArgs...>> {
- static constexpr bool is_cancellable = true;
-
- template <typename Functor>
- static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
- return functor.IsCancelled();
- }
-};
-
-template <typename Signature, typename... BoundArgs>
-struct CallbackCancellationTraits<RepeatingCallback<Signature>,
- std::tuple<BoundArgs...>> {
- static constexpr bool is_cancellable = true;
-
- template <typename Functor>
- static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
- return functor.IsCancelled();
- }
-};
-
-// Returns a RunType of bound functor.
-// E.g. MakeUnboundRunType<R(A, B, C), A, B> is evaluated to R(C).
-template <typename Functor, typename... BoundArgs>
-using MakeUnboundRunType =
- typename internal::BindTypeHelper<Functor, BoundArgs...>::UnboundRunType;
-
-} // namespace base
-
-#endif // BASE_BIND_INTERNAL_H_
diff --git a/gn/base/callback.h b/gn/base/callback.h
deleted file mode 100644
index 1b84f403969..00000000000
--- a/gn/base/callback.h
+++ /dev/null
@@ -1,142 +0,0 @@
-// 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: Header files that do not require the full definition of Callback or
-// Closure should #include "base/callback_forward.h" instead of this file.
-
-#ifndef BASE_CALLBACK_H_
-#define BASE_CALLBACK_H_
-
-#include "base/callback_forward.h"
-#include "base/callback_internal.h"
-
-// -----------------------------------------------------------------------------
-// Usage documentation
-// -----------------------------------------------------------------------------
-//
-// Overview:
-// A callback is similar in concept to a function pointer: it wraps a runnable
-// object such as a function, method, lambda, or even another callback, allowing
-// the runnable object to be invoked later via the callback object.
-//
-// Unlike function pointers, callbacks are created with base::BindOnce() or
-// base::BindRepeating() and support partial function application.
-//
-// A base::OnceCallback may be Run() at most once; a base::RepeatingCallback may
-// be Run() any number of times. |is_null()| is guaranteed to return true for a
-// moved-from callback.
-//
-// // The lambda takes two arguments, but the first argument |x| is bound at
-// // callback creation.
-// base::OnceCallback<int(int)> cb = base::BindOnce([] (int x, int y) {
-// return x + y;
-// }, 1);
-// // Run() only needs the remaining unbound argument |y|.
-// printf("1 + 2 = %d\n", std::move(cb).Run(2)); // Prints 3
-// printf("cb is null? %s\n",
-// cb.is_null() ? "true" : "false"); // Prints true
-// std::move(cb).Run(2); // Crashes since |cb| has already run.
-//
-// Callbacks also support cancellation. A common use is binding the receiver
-// object as a WeakPtr<T>. If that weak pointer is invalidated, calling Run()
-// will be a no-op. Note that |is_cancelled()| and |is_null()| are distinct:
-// simply cancelling a callback will not also make it null.
-//
-// base::Callback is currently a type alias for base::RepeatingCallback. In the
-// future, we expect to flip this to default to base::OnceCallback.
-//
-// See //docs/callback.md for the full documentation.
-
-namespace base {
-
-template <typename R, typename... Args>
-class OnceCallback<R(Args...)> : public internal::CallbackBase {
- public:
- using RunType = R(Args...);
- using PolymorphicInvoke = R (*)(internal::BindStateBase*,
- internal::PassingTraitsType<Args>...);
-
- constexpr OnceCallback() = default;
-
- explicit OnceCallback(internal::BindStateBase* bind_state)
- : internal::CallbackBase(bind_state) {}
-
- OnceCallback(const OnceCallback&) = delete;
- OnceCallback& operator=(const OnceCallback&) = delete;
-
- OnceCallback(OnceCallback&&) noexcept = default;
- OnceCallback& operator=(OnceCallback&&) noexcept = default;
-
- OnceCallback(RepeatingCallback<RunType> other)
- : internal::CallbackBase(std::move(other)) {}
-
- OnceCallback& operator=(RepeatingCallback<RunType> other) {
- static_cast<internal::CallbackBase&>(*this) = std::move(other);
- return *this;
- }
-
- bool Equals(const OnceCallback& other) const { return EqualsInternal(other); }
-
- R Run(Args... args) const& {
- static_assert(!sizeof(*this),
- "OnceCallback::Run() may only be invoked on a non-const "
- "rvalue, i.e. std::move(callback).Run().");
- NOTREACHED();
- }
-
- R Run(Args... args) && {
- // Move the callback instance into a local variable before the invocation,
- // that ensures the internal state is cleared after the invocation.
- // It's not safe to touch |this| after the invocation, since running the
- // bound function may destroy |this|.
- OnceCallback cb = std::move(*this);
- PolymorphicInvoke f =
- reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
- return f(cb.bind_state_.get(), std::forward<Args>(args)...);
- }
-};
-
-template <typename R, typename... Args>
-class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
- public:
- using RunType = R(Args...);
- using PolymorphicInvoke = R (*)(internal::BindStateBase*,
- internal::PassingTraitsType<Args>...);
-
- constexpr RepeatingCallback() = default;
-
- explicit RepeatingCallback(internal::BindStateBase* bind_state)
- : internal::CallbackBaseCopyable(bind_state) {}
-
- // Copyable and movable.
- RepeatingCallback(const RepeatingCallback&) = default;
- RepeatingCallback& operator=(const RepeatingCallback&) = default;
- RepeatingCallback(RepeatingCallback&&) noexcept = default;
- RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default;
-
- bool Equals(const RepeatingCallback& other) const {
- return EqualsInternal(other);
- }
-
- R Run(Args... args) const& {
- PolymorphicInvoke f =
- reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
- return f(this->bind_state_.get(), std::forward<Args>(args)...);
- }
-
- R Run(Args... args) && {
- // Move the callback instance into a local variable before the invocation,
- // that ensures the internal state is cleared after the invocation.
- // It's not safe to touch |this| after the invocation, since running the
- // bound function may destroy |this|.
- RepeatingCallback cb = std::move(*this);
- PolymorphicInvoke f =
- reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
- return f(cb.bind_state_.get(), std::forward<Args>(args)...);
- }
-};
-
-} // namespace base
-
-#endif // BASE_CALLBACK_H_
diff --git a/gn/base/callback_forward.h b/gn/base/callback_forward.h
deleted file mode 100644
index f1851c4fbbf..00000000000
--- a/gn/base/callback_forward.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.
-
-#ifndef BASE_CALLBACK_FORWARD_H_
-#define BASE_CALLBACK_FORWARD_H_
-
-namespace base {
-
-template <typename Signature>
-class OnceCallback;
-
-template <typename Signature>
-class RepeatingCallback;
-
-template <typename Signature>
-using Callback = RepeatingCallback<Signature>;
-
-// Syntactic sugar to make Callback<void()> easier to declare since it
-// will be used in a lot of APIs with delayed execution.
-using OnceClosure = OnceCallback<void()>;
-using RepeatingClosure = RepeatingCallback<void()>;
-using Closure = Callback<void()>;
-
-} // namespace base
-
-#endif // BASE_CALLBACK_FORWARD_H_
diff --git a/gn/base/callback_internal.cc b/gn/base/callback_internal.cc
deleted file mode 100644
index c52d8afaba2..00000000000
--- a/gn/base/callback_internal.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// 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 "base/callback_internal.h"
-
-#include "base/logging.h"
-
-namespace base {
-namespace internal {
-
-namespace {
-
-bool ReturnFalse(const BindStateBase*) {
- return false;
-}
-
-} // namespace
-
-void BindStateBaseRefCountTraits::Destruct(const BindStateBase* bind_state) {
- bind_state->destructor_(bind_state);
-}
-
-BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*))
- : BindStateBase(polymorphic_invoke, destructor, &ReturnFalse) {}
-
-BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*),
- bool (*is_cancelled)(const BindStateBase*))
- : polymorphic_invoke_(polymorphic_invoke),
- destructor_(destructor),
- is_cancelled_(is_cancelled) {}
-
-CallbackBase::CallbackBase(CallbackBase&& c) noexcept = default;
-CallbackBase& CallbackBase::operator=(CallbackBase&& c) noexcept = default;
-CallbackBase::CallbackBase(const CallbackBaseCopyable& c)
- : bind_state_(c.bind_state_) {}
-
-CallbackBase& CallbackBase::operator=(const CallbackBaseCopyable& c) {
- bind_state_ = c.bind_state_;
- return *this;
-}
-
-CallbackBase::CallbackBase(CallbackBaseCopyable&& c) noexcept
- : bind_state_(std::move(c.bind_state_)) {}
-
-CallbackBase& CallbackBase::operator=(CallbackBaseCopyable&& c) noexcept {
- bind_state_ = std::move(c.bind_state_);
- return *this;
-}
-
-void CallbackBase::Reset() {
- // NULL the bind_state_ last, since it may be holding the last ref to whatever
- // object owns us, and we may be deleted after that.
- bind_state_ = nullptr;
-}
-
-bool CallbackBase::IsCancelled() const {
- DCHECK(bind_state_);
- return bind_state_->IsCancelled();
-}
-
-bool CallbackBase::EqualsInternal(const CallbackBase& other) const {
- return bind_state_ == other.bind_state_;
-}
-
-CallbackBase::CallbackBase(BindStateBase* bind_state)
- : bind_state_(bind_state ? AdoptRef(bind_state) : nullptr) {
- DCHECK(!bind_state_.get() || bind_state_->HasOneRef());
-}
-
-CallbackBase::~CallbackBase() = default;
-
-CallbackBaseCopyable::CallbackBaseCopyable(const CallbackBaseCopyable& c)
- : CallbackBase(nullptr) {
- bind_state_ = c.bind_state_;
-}
-
-CallbackBaseCopyable::CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept =
- default;
-
-CallbackBaseCopyable& CallbackBaseCopyable::operator=(
- const CallbackBaseCopyable& c) {
- bind_state_ = c.bind_state_;
- return *this;
-}
-
-CallbackBaseCopyable& CallbackBaseCopyable::operator=(
- CallbackBaseCopyable&& c) noexcept = default;
-
-} // namespace internal
-} // namespace base
diff --git a/gn/base/callback_internal.h b/gn/base/callback_internal.h
deleted file mode 100644
index 7e5180f317a..00000000000
--- a/gn/base/callback_internal.h
+++ /dev/null
@@ -1,172 +0,0 @@
-// 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 contains utility functions and classes that help the
-// implementation, and management of the Callback objects.
-
-#ifndef BASE_CALLBACK_INTERNAL_H_
-#define BASE_CALLBACK_INTERNAL_H_
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-
-namespace base {
-
-struct FakeBindState;
-
-namespace internal {
-
-class CallbackBase;
-class CallbackBaseCopyable;
-
-class BindStateBase;
-
-template <typename Functor, typename... BoundArgs>
-struct BindState;
-
-struct BindStateBaseRefCountTraits {
- static void Destruct(const BindStateBase*);
-};
-
-template <typename T, bool IsScalar = std::is_scalar<T>::value>
-struct PassingTraits;
-
-template <typename T>
-struct PassingTraits<T, false> {
- using Type = T&&;
-};
-
-template <typename T>
-struct PassingTraits<T, true> {
- using Type = T;
-};
-
-template <typename T>
-using PassingTraitsType = typename PassingTraits<T>::Type;
-
-// BindStateBase is used to provide an opaque handle that the Callback
-// class can use to represent a function object with bound arguments. It
-// behaves as an existential type that is used by a corresponding
-// DoInvoke function to perform the function execution. This allows
-// us to shield the Callback class from the types of the bound argument via
-// "type erasure."
-// At the base level, the only task is to add reference counting data. Don't use
-// RefCountedThreadSafe since it requires the destructor to be a virtual method.
-// Creating a vtable for every BindState template instantiation results in a lot
-// of bloat. Its only task is to call the destructor which can be done with a
-// function pointer.
-class BindStateBase
- : public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
- public:
- REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
-
- using InvokeFuncStorage = void (*)();
-
- private:
- BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*));
- BindStateBase(InvokeFuncStorage polymorphic_invoke,
- void (*destructor)(const BindStateBase*),
- bool (*is_cancelled)(const BindStateBase*));
-
- ~BindStateBase() = default;
-
- friend struct BindStateBaseRefCountTraits;
- friend class RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits>;
-
- friend class CallbackBase;
- friend class CallbackBaseCopyable;
-
- // Whitelist subclasses that access the destructor of BindStateBase.
- template <typename Functor, typename... BoundArgs>
- friend struct BindState;
- friend struct ::base::FakeBindState;
-
- bool IsCancelled() const { return is_cancelled_(this); }
-
- // In C++, it is safe to cast function pointers to function pointers of
- // another type. It is not okay to use void*. We create a InvokeFuncStorage
- // that that can store our function pointer, and then cast it back to
- // the original type on usage.
- InvokeFuncStorage polymorphic_invoke_;
-
- // Pointer to a function that will properly destroy |this|.
- void (*destructor_)(const BindStateBase*);
- bool (*is_cancelled_)(const BindStateBase*);
-
- DISALLOW_COPY_AND_ASSIGN(BindStateBase);
-};
-
-// Holds the Callback methods that don't require specialization to reduce
-// template bloat.
-// CallbackBase<MoveOnly> is a direct base class of MoveOnly callbacks, and
-// CallbackBase<Copyable> uses CallbackBase<MoveOnly> for its implementation.
-class CallbackBase {
- public:
- CallbackBase(CallbackBase&& c) noexcept;
- CallbackBase& operator=(CallbackBase&& c) noexcept;
-
- explicit CallbackBase(const CallbackBaseCopyable& c);
- CallbackBase& operator=(const CallbackBaseCopyable& c);
-
- explicit CallbackBase(CallbackBaseCopyable&& c) noexcept;
- CallbackBase& operator=(CallbackBaseCopyable&& c) noexcept;
-
- // Returns true if Callback is null (doesn't refer to anything).
- bool is_null() const { return !bind_state_; }
- explicit operator bool() const { return !is_null(); }
-
- // Returns true if the callback invocation will be nop due to an cancellation.
- // It's invalid to call this on uninitialized callback.
- bool IsCancelled() const;
-
- // Returns the Callback into an uninitialized state.
- void Reset();
-
- protected:
- using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
-
- // Returns true if this callback equals |other|. |other| may be null.
- bool EqualsInternal(const CallbackBase& other) const;
-
- constexpr inline CallbackBase();
-
- // Allow initializing of |bind_state_| via the constructor to avoid default
- // initialization of the scoped_refptr.
- explicit CallbackBase(BindStateBase* bind_state);
-
- InvokeFuncStorage polymorphic_invoke() const {
- return bind_state_->polymorphic_invoke_;
- }
-
- // Force the destructor to be instantiated inside this translation unit so
- // that our subclasses will not get inlined versions. Avoids more template
- // bloat.
- ~CallbackBase();
-
- scoped_refptr<BindStateBase> bind_state_;
-};
-
-constexpr CallbackBase::CallbackBase() = default;
-
-// CallbackBase<Copyable> is a direct base class of Copyable Callbacks.
-class CallbackBaseCopyable : public CallbackBase {
- public:
- CallbackBaseCopyable(const CallbackBaseCopyable& c);
- CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept;
- CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c);
- CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c) noexcept;
-
- protected:
- constexpr CallbackBaseCopyable() = default;
- explicit CallbackBaseCopyable(BindStateBase* bind_state)
- : CallbackBase(bind_state) {}
- ~CallbackBaseCopyable() = default;
-};
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_CALLBACK_INTERNAL_H_
diff --git a/gn/base/command_line.cc b/gn/base/command_line.cc
deleted file mode 100644
index 792d322fe1e..00000000000
--- a/gn/base/command_line.cc
+++ /dev/null
@@ -1,486 +0,0 @@
-// 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 "base/command_line.h"
-
-#include <algorithm>
-#include <ostream>
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/stl_util.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_tokenizer.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-
-#include <shellapi.h>
-#endif
-
-namespace base {
-
-CommandLine* CommandLine::current_process_commandline_ = nullptr;
-
-namespace {
-
-const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
-const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
-
-// Since we use a lazy match, make sure that longer versions (like "--") are
-// listed before shorter versions (like "-") of similar prefixes.
-#if defined(OS_WIN)
-// By putting slash last, we can control whether it is treaded as a switch
-// value by changing the value of switch_prefix_count to be one less than
-// the array size.
-const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-// Unixes don't use slash as a switch.
-const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
-#endif
-size_t switch_prefix_count = arraysize(kSwitchPrefixes);
-
-size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
- for (size_t i = 0; i < switch_prefix_count; ++i) {
- CommandLine::StringType prefix(kSwitchPrefixes[i]);
- if (string.compare(0, prefix.length(), prefix) == 0)
- return prefix.length();
- }
- return 0;
-}
-
-// Fills in |switch_string| and |switch_value| if |string| is a switch.
-// This will preserve the input switch prefix in the output |switch_string|.
-bool IsSwitch(const CommandLine::StringType& string,
- CommandLine::StringType* switch_string,
- CommandLine::StringType* switch_value) {
- switch_string->clear();
- switch_value->clear();
- size_t prefix_length = GetSwitchPrefixLength(string);
- if (prefix_length == 0 || prefix_length == string.length())
- return false;
-
- const size_t equals_position = string.find(kSwitchValueSeparator);
- *switch_string = string.substr(0, equals_position);
- if (equals_position != CommandLine::StringType::npos)
- *switch_value = string.substr(equals_position + 1);
- return true;
-}
-
-// Append switches and arguments, keeping switches before arguments
-// if handle_switches is true.
-void AppendSwitchesAndArguments(CommandLine* command_line,
- const CommandLine::StringVector& argv,
- bool handle_switches) {
- bool parse_switches = handle_switches;
- for (size_t i = 1; i < argv.size(); ++i) {
- CommandLine::StringType arg = argv[i];
-#if defined(OS_WIN)
- TrimWhitespace(arg, TRIM_ALL, &arg);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- TrimWhitespaceASCII(arg, TRIM_ALL, &arg);
-#endif
-
- CommandLine::StringType switch_string;
- CommandLine::StringType switch_value;
- parse_switches &= (arg != kSwitchTerminator);
- if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
-#if defined(OS_WIN)
- command_line->AppendSwitchNative(UTF16ToASCII(switch_string),
- switch_value);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- command_line->AppendSwitchNative(switch_string, switch_value);
-#else
-#error Unsupported platform
-#endif
- } else {
- command_line->AppendArgNative(arg);
- }
- }
-}
-
-#if defined(OS_WIN)
-// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*.
-string16 QuoteForCommandLineToArgvW(const string16& arg,
- bool quote_placeholders) {
- // We follow the quoting rules of CommandLineToArgvW.
- // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
- string16 quotable_chars(L" \\\"");
- // We may also be required to quote '%', which is commonly used in a command
- // line as a placeholder. (It may be substituted for a string with spaces.)
- if (quote_placeholders)
- quotable_chars.push_back(L'%');
- if (arg.find_first_of(quotable_chars) == string16::npos) {
- // No quoting necessary.
- return arg;
- }
-
- string16 out;
- out.push_back(L'"');
- for (size_t i = 0; i < arg.size(); ++i) {
- if (arg[i] == '\\') {
- // Find the extent of this run of backslashes.
- size_t start = i, end = start + 1;
- for (; end < arg.size() && arg[end] == '\\'; ++end) {
- }
- size_t backslash_count = end - start;
-
- // Backslashes are escapes only if the run is followed by a double quote.
- // Since we also will end the string with a double quote, we escape for
- // either a double quote or the end of the string.
- if (end == arg.size() || arg[end] == '"') {
- // To quote, we need to output 2x as many backslashes.
- backslash_count *= 2;
- }
- for (size_t j = 0; j < backslash_count; ++j)
- out.push_back('\\');
-
- // Advance i to one before the end to balance i++ in loop.
- i = end - 1;
- } else if (arg[i] == '"') {
- out.push_back('\\');
- out.push_back('"');
- } else {
- out.push_back(arg[i]);
- }
- }
- out.push_back('"');
-
- return out;
-}
-#endif
-
-} // namespace
-
-CommandLine::CommandLine(NoProgram no_program)
- : argv_(1), begin_args_(1), parse_switches_(true) {}
-
-CommandLine::CommandLine(const FilePath& program)
- : argv_(1), begin_args_(1), parse_switches_(true) {
- SetProgram(program);
-}
-
-CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
- : argv_(1), begin_args_(1), parse_switches_(true) {
- InitFromArgv(argc, argv);
-}
-
-CommandLine::CommandLine(const StringVector& argv)
- : argv_(1), begin_args_(1), parse_switches_(true) {
- InitFromArgv(argv);
-}
-
-CommandLine::CommandLine(const CommandLine& other) = default;
-
-CommandLine& CommandLine::operator=(const CommandLine& other) = default;
-
-CommandLine::~CommandLine() = default;
-
-#if defined(OS_WIN)
-// static
-void CommandLine::set_slash_is_not_a_switch() {
- // The last switch prefix should be slash, so adjust the size to skip it.
- DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0);
- switch_prefix_count = arraysize(kSwitchPrefixes) - 1;
-}
-
-// static
-void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
- DCHECK(!current_process_commandline_);
- current_process_commandline_ = new CommandLine(NO_PROGRAM);
- // On Windows we need to convert the command line arguments to string16.
- base::CommandLine::StringVector argv_vector;
- for (int i = 0; i < argc; ++i)
- argv_vector.push_back(UTF8ToUTF16(argv[i]));
- current_process_commandline_->InitFromArgv(argv_vector);
-}
-#endif
-
-// static
-bool CommandLine::Init(int argc, const char* const* argv) {
- if (current_process_commandline_) {
- // If this is intentional, Reset() must be called first. If we are using
- // the shared build mode, we have to share a single object across multiple
- // shared libraries.
- return false;
- }
-
- current_process_commandline_ = new CommandLine(NO_PROGRAM);
-#if defined(OS_WIN)
- current_process_commandline_->ParseFromString(::GetCommandLineW());
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- current_process_commandline_->InitFromArgv(argc, argv);
-#else
-#error Unsupported platform
-#endif
-
- return true;
-}
-
-// static
-void CommandLine::Reset() {
- DCHECK(current_process_commandline_);
- delete current_process_commandline_;
- current_process_commandline_ = nullptr;
-}
-
-// static
-CommandLine* CommandLine::ForCurrentProcess() {
- DCHECK(current_process_commandline_);
- return current_process_commandline_;
-}
-
-// static
-bool CommandLine::InitializedForCurrentProcess() {
- return !!current_process_commandline_;
-}
-
-#if defined(OS_WIN)
-// static
-CommandLine CommandLine::FromString(const string16& command_line) {
- CommandLine cmd(NO_PROGRAM);
- cmd.ParseFromString(command_line);
- return cmd;
-}
-#endif
-
-void CommandLine::InitFromArgv(int argc,
- const CommandLine::CharType* const* argv) {
- StringVector new_argv;
- for (int i = 0; i < argc; ++i)
- new_argv.push_back(argv[i]);
- InitFromArgv(new_argv);
-}
-
-void CommandLine::InitFromArgv(const StringVector& argv) {
- argv_ = StringVector(1);
- switches_.clear();
- begin_args_ = 1;
- SetProgram(argv.empty() ? FilePath() : FilePath(argv[0]));
- AppendSwitchesAndArguments(this, argv, parse_switches_);
-}
-
-FilePath CommandLine::GetProgram() const {
- return FilePath(argv_[0]);
-}
-
-void CommandLine::SetProgram(const FilePath& program) {
-#if defined(OS_WIN)
- TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]);
-#else
-#error Unsupported platform
-#endif
-}
-
-bool CommandLine::HasSwitch(const base::StringPiece& switch_string) const {
- DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
- return ContainsKey(switches_, switch_string);
-}
-
-bool CommandLine::HasSwitch(const char switch_constant[]) const {
- return HasSwitch(base::StringPiece(switch_constant));
-}
-
-std::string CommandLine::GetSwitchValueASCII(
- const base::StringPiece& switch_string) const {
- StringType value = GetSwitchValueNative(switch_string);
- if (!IsStringASCII(value)) {
- DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
- return std::string();
- }
-#if defined(OS_WIN)
- return UTF16ToASCII(value);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- return value;
-#endif
-}
-
-FilePath CommandLine::GetSwitchValuePath(
- const base::StringPiece& switch_string) const {
- return FilePath(GetSwitchValueNative(switch_string));
-}
-
-CommandLine::StringType CommandLine::GetSwitchValueNative(
- const base::StringPiece& switch_string) const {
- DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
- auto result = switches_.find(switch_string);
- return result == switches_.end() ? StringType() : result->second;
-}
-
-void CommandLine::AppendSwitch(const std::string& switch_string) {
- AppendSwitchNative(switch_string, StringType());
-}
-
-void CommandLine::AppendSwitchPath(const std::string& switch_string,
- const FilePath& path) {
- AppendSwitchNative(switch_string, path.value());
-}
-
-void CommandLine::AppendSwitchNative(const std::string& switch_string,
- const CommandLine::StringType& value) {
-#if defined(OS_WIN)
- const std::string switch_key = ToLowerASCII(switch_string);
- StringType combined_switch_string(ASCIIToUTF16(switch_key));
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- const std::string& switch_key = switch_string;
- StringType combined_switch_string(switch_key);
-#endif
- size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
- auto insertion =
- switches_.insert(make_pair(switch_key.substr(prefix_length), value));
- if (!insertion.second)
- insertion.first->second = value;
- // Preserve existing switch prefixes in |argv_|; only append one if necessary.
- if (prefix_length == 0)
- combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
- if (!value.empty())
- combined_switch_string += kSwitchValueSeparator + value;
- // Append the switch and update the switches/arguments divider |begin_args_|.
- argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
-}
-
-void CommandLine::AppendSwitchASCII(const std::string& switch_string,
- const std::string& value_string) {
-#if defined(OS_WIN)
- AppendSwitchNative(switch_string, ASCIIToUTF16(value_string));
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- AppendSwitchNative(switch_string, value_string);
-#else
-#error Unsupported platform
-#endif
-}
-
-void CommandLine::CopySwitchesFrom(const CommandLine& source,
- const char* const switches[],
- size_t count) {
- for (size_t i = 0; i < count; ++i) {
- if (source.HasSwitch(switches[i]))
- AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i]));
- }
-}
-
-CommandLine::StringVector CommandLine::GetArgs() const {
- // Gather all arguments after the last switch (may include kSwitchTerminator).
- StringVector args(argv_.begin() + begin_args_, argv_.end());
- // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
- StringVector::iterator switch_terminator =
- std::find(args.begin(), args.end(), kSwitchTerminator);
- if (switch_terminator != args.end())
- args.erase(switch_terminator);
- return args;
-}
-
-void CommandLine::AppendArg(const std::string& value) {
-#if defined(OS_WIN)
- DCHECK(IsStringUTF8(value));
- AppendArgNative(UTF8ToWide(value));
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- AppendArgNative(value);
-#else
-#error Unsupported platform
-#endif
-}
-
-void CommandLine::AppendArgPath(const FilePath& path) {
- AppendArgNative(path.value());
-}
-
-void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
- argv_.push_back(value);
-}
-
-void CommandLine::AppendArguments(const CommandLine& other,
- bool include_program) {
- if (include_program)
- SetProgram(other.GetProgram());
- AppendSwitchesAndArguments(this, other.argv(), parse_switches_);
-}
-
-void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
- if (wrapper.empty())
- return;
- // Split the wrapper command based on whitespace (with quoting).
- using CommandLineTokenizer =
- StringTokenizerT<StringType, StringType::const_iterator>;
- CommandLineTokenizer tokenizer(wrapper, FILE_PATH_LITERAL(" "));
- tokenizer.set_quote_chars(FILE_PATH_LITERAL("'\""));
- std::vector<StringType> wrapper_argv;
- while (tokenizer.GetNext())
- wrapper_argv.emplace_back(tokenizer.token());
-
- // Prepend the wrapper and update the switches/arguments |begin_args_|.
- argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end());
- begin_args_ += wrapper_argv.size();
-}
-
-#if defined(OS_WIN)
-void CommandLine::ParseFromString(const string16& command_line) {
- string16 command_line_string;
- TrimWhitespace(command_line, TRIM_ALL, &command_line_string);
- if (command_line_string.empty())
- return;
-
- int num_args = 0;
- wchar_t** args = NULL;
- args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
-
- DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
- << UTF16ToUTF8(command_line);
- InitFromArgv(num_args, args);
- LocalFree(args);
-}
-#endif
-
-CommandLine::StringType CommandLine::GetCommandLineStringInternal(
- bool quote_placeholders) const {
- StringType string(argv_[0]);
-#if defined(OS_WIN)
- string = QuoteForCommandLineToArgvW(string, quote_placeholders);
-#endif
- StringType params(GetArgumentsStringInternal(quote_placeholders));
- if (!params.empty()) {
- string.append(StringType(FILE_PATH_LITERAL(" ")));
- string.append(params);
- }
- return string;
-}
-
-CommandLine::StringType CommandLine::GetArgumentsStringInternal(
- bool quote_placeholders) const {
- StringType params;
- // Append switches and arguments.
- bool parse_switches = parse_switches_;
- for (size_t i = 1; i < argv_.size(); ++i) {
- StringType arg = argv_[i];
- StringType switch_string;
- StringType switch_value;
- parse_switches &= arg != kSwitchTerminator;
- if (i > 1)
- params.append(StringType(FILE_PATH_LITERAL(" ")));
- if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
- params.append(switch_string);
- if (!switch_value.empty()) {
-#if defined(OS_WIN)
- switch_value =
- QuoteForCommandLineToArgvW(switch_value, quote_placeholders);
-#endif
- params.append(kSwitchValueSeparator + switch_value);
- }
- } else {
-#if defined(OS_WIN)
- arg = QuoteForCommandLineToArgvW(arg, quote_placeholders);
-#endif
- params.append(arg);
- }
- }
- return params;
-}
-
-} // namespace base
diff --git a/gn/base/command_line.h b/gn/base/command_line.h
deleted file mode 100644
index 01614576832..00000000000
--- a/gn/base/command_line.h
+++ /dev/null
@@ -1,255 +0,0 @@
-// 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 works with command lines: building and parsing.
-// Arguments with prefixes ('--', '-', and on Windows, '/') are switches.
-// Switches will precede all other arguments without switch prefixes.
-// Switches can optionally have values, delimited by '=', e.g., "-switch=value".
-// An argument of "--" will terminate switch parsing during initialization,
-// interpreting subsequent tokens as non-switch arguments, regardless of prefix.
-
-// There is a singleton read-only CommandLine that represents the command line
-// that the current process was started with. It must be initialized in main().
-
-#ifndef BASE_COMMAND_LINE_H_
-#define BASE_COMMAND_LINE_H_
-
-#include <stddef.h>
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "util/build_config.h"
-
-namespace base {
-
-class FilePath;
-
-class CommandLine {
- public:
-#if defined(OS_WIN)
- // The native command line string type.
- using StringType = string16;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- using StringType = std::string;
-#endif
-
- using CharType = StringType::value_type;
- using StringVector = std::vector<StringType>;
- using SwitchMap = std::map<std::string, StringType, std::less<>>;
-
- // A constructor for CommandLines that only carry switches and arguments.
- enum NoProgram { NO_PROGRAM };
- explicit CommandLine(NoProgram no_program);
-
- // Construct a new command line with |program| as argv[0].
- explicit CommandLine(const FilePath& program);
-
- // Construct a new command line from an argument list.
- CommandLine(int argc, const CharType* const* argv);
- explicit CommandLine(const StringVector& argv);
-
- // Override copy and assign to ensure |switches_by_stringpiece_| is valid.
- CommandLine(const CommandLine& other);
- CommandLine& operator=(const CommandLine& other);
-
- ~CommandLine();
-
-#if defined(OS_WIN)
- // By default this class will treat command-line arguments beginning with
- // slashes as switches on Windows, but not other platforms.
- //
- // If this behavior is inappropriate for your application, you can call this
- // function BEFORE initializing the current process' global command line
- // object and the behavior will be the same as Posix systems (only hyphens
- // begin switches, everything else will be an arg).
- static void set_slash_is_not_a_switch();
-
- // Normally when the CommandLine singleton is initialized it gets the command
- // line via the GetCommandLineW API and then uses the shell32 API
- // CommandLineToArgvW to parse the command line and convert it back to
- // argc and argv. Tests who don't want this dependency on shell32 and need
- // to honor the arguments passed in should use this function.
- static void InitUsingArgvForTesting(int argc, const char* const* argv);
-#endif
-
- // Initialize the current process CommandLine singleton. On Windows, ignores
- // its arguments (we instead parse GetCommandLineW() directly) because we
- // don't trust the CRT's parsing of the command line, but it still must be
- // called to set up the command line. Returns false if initialization has
- // already occurred, and true otherwise. Only the caller receiving a 'true'
- // return value should take responsibility for calling Reset.
- static bool Init(int argc, const char* const* argv);
-
- // Destroys the current process CommandLine singleton. This is necessary if
- // you want to reset the base library to its initial state (for example, in an
- // outer library that needs to be able to terminate, and be re-initialized).
- // If Init is called only once, as in main(), Reset() is not necessary.
- // Do not call this in tests. Use base::test::ScopedCommandLine instead.
- static void Reset();
-
- // Get the singleton CommandLine representing the current process's
- // command line. Note: returned value is mutable, but not thread safe;
- // only mutate if you know what you're doing!
- static CommandLine* ForCurrentProcess();
-
- // Returns true if the CommandLine has been initialized for the given process.
- static bool InitializedForCurrentProcess();
-
-#if defined(OS_WIN)
- static CommandLine FromString(const string16& command_line);
-#endif
-
- // Initialize from an argv vector.
- void InitFromArgv(int argc, const CharType* const* argv);
- void InitFromArgv(const StringVector& argv);
-
- // Constructs and returns the represented command line string.
- // CAUTION! This should be avoided on POSIX because quoting behavior is
- // unclear.
- StringType GetCommandLineString() const {
- return GetCommandLineStringInternal(false);
- }
-
-#if defined(OS_WIN)
- // Constructs and returns the represented command line string. Assumes the
- // command line contains placeholders (eg, %1) and quotes any program or
- // argument with a '%' in it. This should be avoided unless the placeholder is
- // required by an external interface (eg, the Windows registry), because it is
- // not generally safe to replace it with an arbitrary string. If possible,
- // placeholders should be replaced *before* converting the command line to a
- // string.
- StringType GetCommandLineStringWithPlaceholders() const {
- return GetCommandLineStringInternal(true);
- }
-#endif
-
- // Constructs and returns the represented arguments string.
- // CAUTION! This should be avoided on POSIX because quoting behavior is
- // unclear.
- StringType GetArgumentsString() const {
- return GetArgumentsStringInternal(false);
- }
-
-#if defined(OS_WIN)
- // Constructs and returns the represented arguments string. Assumes the
- // command line contains placeholders (eg, %1) and quotes any argument with a
- // '%' in it. This should be avoided unless the placeholder is required by an
- // external interface (eg, the Windows registry), because it is not generally
- // safe to replace it with an arbitrary string. If possible, placeholders
- // should be replaced *before* converting the arguments to a string.
- StringType GetArgumentsStringWithPlaceholders() const {
- return GetArgumentsStringInternal(true);
- }
-#endif
-
- // Returns the original command line string as a vector of strings.
- const StringVector& argv() const { return argv_; }
-
- // Get and Set the program part of the command line string (the first item).
- FilePath GetProgram() const;
- void SetProgram(const FilePath& program);
-
- // Enables/disables the parsing of switches for future argument appending.
- // True by default, but can be set to false to ensure that no re-ordering
- // is done.
- void SetParseSwitches(bool parse_switches) {
- parse_switches_ = parse_switches;
- }
-
- // Returns true if this command line contains the given switch.
- // Switch names must be lowercase.
- // The second override provides an optimized version to avoid inlining codegen
- // at every callsite to find the length of the constant and construct a
- // StringPiece.
- bool HasSwitch(const StringPiece& switch_string) const;
- bool HasSwitch(const char switch_constant[]) const;
-
- // Returns the value associated with the given switch. If the switch has no
- // value or isn't present, this method returns the empty string.
- // Switch names must be lowercase.
- std::string GetSwitchValueASCII(const StringPiece& switch_string) const;
- FilePath GetSwitchValuePath(const StringPiece& switch_string) const;
- StringType GetSwitchValueNative(const StringPiece& switch_string) const;
-
- // Get a copy of all switches, along with their values.
- const SwitchMap& GetSwitches() const { return switches_; }
-
- // Append a switch [with optional value] to the command line.
- // Note: Switches will precede arguments regardless of appending order.
- void AppendSwitch(const std::string& switch_string);
- void AppendSwitchPath(const std::string& switch_string, const FilePath& path);
- void AppendSwitchNative(const std::string& switch_string,
- const StringType& value);
- void AppendSwitchASCII(const std::string& switch_string,
- const std::string& value);
-
- // Copy a set of switches (and any values) from another command line.
- // Commonly used when launching a subprocess.
- void CopySwitchesFrom(const CommandLine& source,
- const char* const switches[],
- size_t count);
-
- // Get the remaining arguments to the command.
- StringVector GetArgs() const;
-
- // Append an argument to the command line. Note that the argument is quoted
- // properly such that it is interpreted as one argument to the target command.
- // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8.
- // Note: Switches will precede arguments regardless of appending order.
- void AppendArg(const std::string& value);
- void AppendArgPath(const FilePath& value);
- void AppendArgNative(const StringType& value);
-
- // Append the switches and arguments from another command line to this one.
- // If |include_program| is true, include |other|'s program as well.
- void AppendArguments(const CommandLine& other, bool include_program);
-
- // Insert a command before the current command.
- // Common for debuggers, like "gdb --args".
- void PrependWrapper(const StringType& wrapper);
-
-#if defined(OS_WIN)
- // Initialize by parsing the given command line string.
- // The program name is assumed to be the first item in the string.
- void ParseFromString(const string16& command_line);
-#endif
-
- private:
- // Disallow default constructor; a program name must be explicitly specified.
- CommandLine() = delete;
- // Allow the copy constructor. A common pattern is to copy of the current
- // process's command line and then add some flags to it. For example:
- // CommandLine cl(*CommandLine::ForCurrentProcess());
- // cl.AppendSwitch(...);
-
- // Internal version of GetCommandLineString. If |quote_placeholders| is true,
- // also quotes parts with '%' in them.
- StringType GetCommandLineStringInternal(bool quote_placeholders) const;
-
- // Internal version of GetArgumentsString. If |quote_placeholders| is true,
- // also quotes parts with '%' in them.
- StringType GetArgumentsStringInternal(bool quote_placeholders) const;
-
- // The singleton CommandLine representing the current process's command line.
- static CommandLine* current_process_commandline_;
-
- // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
- StringVector argv_;
-
- // Parsed-out switch keys and values.
- SwitchMap switches_;
-
- // The index after the program and switches, any arguments start here.
- size_t begin_args_;
-
- // Whether or not to parse arguments that look like switches as switches.
- bool parse_switches_;
-};
-
-} // namespace base
-
-#endif // BASE_COMMAND_LINE_H_
diff --git a/gn/base/compiler_specific.h b/gn/base/compiler_specific.h
deleted file mode 100644
index 42241976483..00000000000
--- a/gn/base/compiler_specific.h
+++ /dev/null
@@ -1,231 +0,0 @@
-// 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 BASE_COMPILER_SPECIFIC_H_
-#define BASE_COMPILER_SPECIFIC_H_
-
-#include "util/build_config.h"
-
-#if defined(COMPILER_MSVC)
-
-// For _Printf_format_string_.
-#include <sal.h>
-
-// Macros for suppressing and disabling warnings on MSVC.
-//
-// Warning numbers are enumerated at:
-// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
-//
-// The warning pragma:
-// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
-//
-// Using __pragma instead of #pragma inside macros:
-// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
-
-// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
-// for the next line of the source file.
-#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress : n))
-
-// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
-// The warning remains disabled until popped by MSVC_POP_WARNING.
-#define MSVC_PUSH_DISABLE_WARNING(n) \
- __pragma(warning(push)) __pragma(warning(disable : n))
-
-// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level
-// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all
-// warnings.
-#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
-
-// Pop effects of innermost MSVC_PUSH_* macro.
-#define MSVC_POP_WARNING() __pragma(warning(pop))
-
-#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off))
-#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on))
-
-#else // Not MSVC
-
-#define _Printf_format_string_
-#define MSVC_SUPPRESS_WARNING(n)
-#define MSVC_PUSH_DISABLE_WARNING(n)
-#define MSVC_PUSH_WARNING_LEVEL(n)
-#define MSVC_POP_WARNING()
-#define MSVC_DISABLE_OPTIMIZE()
-#define MSVC_ENABLE_OPTIMIZE()
-
-#endif // COMPILER_MSVC
-
-// Annotate a variable indicating it's ok if the variable is not used.
-// (Typically used to silence a compiler warning when the assignment
-// is important for some other reason.)
-// Use like:
-// int x = ...;
-// ALLOW_UNUSED_LOCAL(x);
-#define ALLOW_UNUSED_LOCAL(x) (void)x
-
-// Annotate a typedef or function indicating it's ok if it's not used.
-// Use like:
-// typedef Foo Bar ALLOW_UNUSED_TYPE;
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define ALLOW_UNUSED_TYPE __attribute__((unused))
-#else
-#define ALLOW_UNUSED_TYPE
-#endif
-
-// Annotate a function indicating it should not be inlined.
-// Use like:
-// NOINLINE void DoStuff() { ... }
-#if defined(COMPILER_GCC)
-#define NOINLINE __attribute__((noinline))
-#elif defined(COMPILER_MSVC)
-#define NOINLINE __declspec(noinline)
-#else
-#define NOINLINE
-#endif
-
-#if COMPILER_GCC && defined(NDEBUG)
-#define ALWAYS_INLINE inline __attribute__((__always_inline__))
-#elif COMPILER_MSVC && defined(NDEBUG)
-#define ALWAYS_INLINE __forceinline
-#else
-#define ALWAYS_INLINE inline
-#endif
-
-// Specify memory alignment for structs, classes, etc.
-// Use like:
-// class ALIGNAS(16) MyClass { ... }
-// ALIGNAS(16) int array[4];
-//
-// In most places you can use the C++11 keyword "alignas", which is preferred.
-//
-// But compilers have trouble mixing __attribute__((...)) syntax with
-// alignas(...) syntax.
-//
-// Doesn't work in clang or gcc:
-// struct alignas(16) __attribute__((packed)) S { char c; };
-// Works in clang but not gcc:
-// struct __attribute__((packed)) alignas(16) S2 { char c; };
-// Works in clang and gcc:
-// struct alignas(16) S3 { char c; } __attribute__((packed));
-//
-// There are also some attributes that must be specified *before* a class
-// definition: visibility (used for exporting functions/classes) is one of
-// these attributes. This means that it is not possible to use alignas() with a
-// class that is marked as exported.
-#if defined(COMPILER_MSVC)
-#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
-#elif defined(COMPILER_GCC)
-#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
-#endif
-
-// Annotate a function indicating the caller must examine the return value.
-// Use like:
-// int foo() WARN_UNUSED_RESULT;
-// To explicitly ignore a result, see |ignore_result()| in base/macros.h.
-#undef WARN_UNUSED_RESULT
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#else
-#define WARN_UNUSED_RESULT
-#endif
-
-// Tell the compiler a function is using a printf-style format string.
-// |format_param| is the one-based index of the format string parameter;
-// |dots_param| is the one-based index of the "..." parameter.
-// For v*printf functions (which take a va_list), pass 0 for dots_param.
-// (This is undocumented but matches what the system C headers do.)
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define PRINTF_FORMAT(format_param, dots_param) \
- __attribute__((format(printf, format_param, dots_param)))
-#else
-#define PRINTF_FORMAT(format_param, dots_param)
-#endif
-
-// WPRINTF_FORMAT is the same, but for wide format strings.
-// This doesn't appear to yet be implemented in any compiler.
-// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
-#define WPRINTF_FORMAT(format_param, dots_param)
-// If available, it would look like:
-// __attribute__((format(wprintf, format_param, dots_param)))
-
-// Sanitizers annotations.
-#if defined(__has_attribute)
-#if __has_attribute(no_sanitize)
-#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))
-#endif
-#endif
-#if !defined(NO_SANITIZE)
-#define NO_SANITIZE(what)
-#endif
-
-// MemorySanitizer annotations.
-#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
-#include <sanitizer/msan_interface.h>
-
-// Mark a memory region fully initialized.
-// Use this to annotate code that deliberately reads uninitialized data, for
-// example a GC scavenging root set pointers from the stack.
-#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)
-
-// Check a memory region for initializedness, as if it was being used here.
-// If any bits are uninitialized, crash with an MSan report.
-// Use this to sanitize data which MSan won't be able to track, e.g. before
-// passing data to another process via shared memory.
-#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
- __msan_check_mem_is_initialized(p, size)
-#else // MEMORY_SANITIZER
-#define MSAN_UNPOISON(p, size)
-#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
-#endif // MEMORY_SANITIZER
-
-// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons.
-#if !defined(DISABLE_CFI_PERF)
-#if defined(__clang__) && defined(OFFICIAL_BUILD)
-#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi")))
-#else
-#define DISABLE_CFI_PERF
-#endif
-#endif
-
-// Macro useful for writing cross-platform function pointers.
-#if !defined(CDECL)
-#if defined(OS_WIN)
-#define CDECL __cdecl
-#else // defined(OS_WIN)
-#define CDECL
-#endif // defined(OS_WIN)
-#endif // !defined(CDECL)
-
-// Macro for hinting that an expression is likely to be false.
-#if !defined(UNLIKELY)
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define UNLIKELY(x) __builtin_expect(!!(x), 0)
-#else
-#define UNLIKELY(x) (x)
-#endif // defined(COMPILER_GCC)
-#endif // !defined(UNLIKELY)
-
-#if !defined(LIKELY)
-#if defined(COMPILER_GCC) || defined(__clang__)
-#define LIKELY(x) __builtin_expect(!!(x), 1)
-#else
-#define LIKELY(x) (x)
-#endif // defined(COMPILER_GCC)
-#endif // !defined(LIKELY)
-
-// Compiler feature-detection.
-// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
-#if defined(__has_feature)
-#define HAS_FEATURE(FEATURE) __has_feature(FEATURE)
-#else
-#define HAS_FEATURE(FEATURE) 0
-#endif
-
-// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
-#if defined(__clang__)
-#define FALLTHROUGH [[clang::fallthrough]]
-#else
-#define FALLTHROUGH
-#endif
-
-#endif // BASE_COMPILER_SPECIFIC_H_
diff --git a/gn/base/containers/flat_tree.h b/gn/base/containers/flat_tree.h
deleted file mode 100644
index 7856e242336..00000000000
--- a/gn/base/containers/flat_tree.h
+++ /dev/null
@@ -1,1004 +0,0 @@
-// Copyright 2017 The Chromium 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 BASE_CONTAINERS_FLAT_TREE_H_
-#define BASE_CONTAINERS_FLAT_TREE_H_
-
-#include <algorithm>
-#include <iterator>
-#include <type_traits>
-#include <vector>
-
-#include "base/template_util.h"
-
-namespace base {
-
-enum FlatContainerDupes {
- KEEP_FIRST_OF_DUPES,
- KEEP_LAST_OF_DUPES,
-};
-
-namespace internal {
-
-// This is a convenience method returning true if Iterator is at least a
-// ForwardIterator and thus supports multiple passes over a range.
-template <class Iterator>
-constexpr bool is_multipass() {
- return std::is_base_of<
- std::forward_iterator_tag,
- typename std::iterator_traits<Iterator>::iterator_category>::value;
-}
-
-// This algorithm is like unique() from the standard library except it
-// selects only the last of consecutive values instead of the first.
-template <class Iterator, class BinaryPredicate>
-Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) {
- Iterator replacable = std::adjacent_find(first, last, compare);
-
- // No duplicate elements found.
- if (replacable == last)
- return last;
-
- first = std::next(replacable);
-
- // Last element is a duplicate but all others are unique.
- if (first == last)
- return replacable;
-
- // This loop is based on std::adjacent_find but std::adjacent_find doesn't
- // quite cut it.
- for (Iterator next = std::next(first); next != last; ++next, ++first) {
- if (!compare(*first, *next))
- *replacable++ = std::move(*first);
- }
-
- // Last element should be copied unconditionally.
- *replacable++ = std::move(*first);
- return replacable;
-}
-
-// Uses SFINAE to detect whether type has is_transparent member.
-template <typename T, typename = void>
-struct IsTransparentCompare : std::false_type {};
-template <typename T>
-struct IsTransparentCompare<T, void_t<typename T::is_transparent>>
- : std::true_type {};
-
-// Implementation -------------------------------------------------------------
-
-// Implementation of a sorted vector for backing flat_set and flat_map. Do not
-// use directly.
-//
-// The use of "value" in this is like std::map uses, meaning it's the thing
-// contained (in the case of map it's a <Kay, Mapped> pair). The Key is how
-// things are looked up. In the case of a set, Key == Value. In the case of
-// a map, the Key is a component of a Value.
-//
-// The helper class GetKeyFromValue provides the means to extract a key from a
-// value for comparison purposes. It should implement:
-// const Key& operator()(const Value&).
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-class flat_tree {
- private:
- using underlying_type = std::vector<Value>;
-
- public:
- // --------------------------------------------------------------------------
- // Types.
- //
- using key_type = Key;
- using key_compare = KeyCompare;
- using value_type = Value;
-
- // Wraps the templated key comparison to compare values.
- class value_compare : public key_compare {
- public:
- value_compare() = default;
-
- template <class Cmp>
- explicit value_compare(Cmp&& compare_arg)
- : KeyCompare(std::forward<Cmp>(compare_arg)) {}
-
- bool operator()(const value_type& left, const value_type& right) const {
- GetKeyFromValue extractor;
- return key_compare::operator()(extractor(left), extractor(right));
- }
- };
-
- using pointer = typename underlying_type::pointer;
- using const_pointer = typename underlying_type::const_pointer;
- using reference = typename underlying_type::reference;
- using const_reference = typename underlying_type::const_reference;
- using size_type = typename underlying_type::size_type;
- using difference_type = typename underlying_type::difference_type;
- using iterator = typename underlying_type::iterator;
- using const_iterator = typename underlying_type::const_iterator;
- using reverse_iterator = typename underlying_type::reverse_iterator;
- using const_reverse_iterator =
- typename underlying_type::const_reverse_iterator;
-
- // --------------------------------------------------------------------------
- // Lifetime.
- //
- // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity
- // and take O(N * log(N)) + O(N) if extra memory is available (N is a range
- // length).
- //
- // Assume that move constructors invalidate iterators and references.
- //
- // The constructors that take ranges, lists, and vectors do not require that
- // the input be sorted.
-
- flat_tree();
- explicit flat_tree(const key_compare& comp);
-
- template <class InputIterator>
- flat_tree(InputIterator first,
- InputIterator last,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
- const key_compare& comp = key_compare());
-
- flat_tree(const flat_tree&);
- flat_tree(flat_tree&&) noexcept = default;
-
- flat_tree(std::vector<value_type> items,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
- const key_compare& comp = key_compare());
-
- flat_tree(std::initializer_list<value_type> ilist,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
- const key_compare& comp = key_compare());
-
- ~flat_tree();
-
- // --------------------------------------------------------------------------
- // Assignments.
- //
- // Assume that move assignment invalidates iterators and references.
-
- flat_tree& operator=(const flat_tree&);
- flat_tree& operator=(flat_tree&&);
- // Takes the first if there are duplicates in the initializer list.
- flat_tree& operator=(std::initializer_list<value_type> ilist);
-
- // --------------------------------------------------------------------------
- // Memory management.
- //
- // Beware that shrink_to_fit() simply forwards the request to the
- // underlying_type and its implementation is free to optimize otherwise and
- // leave capacity() to be greater that its size.
- //
- // reserve() and shrink_to_fit() invalidate iterators and references.
-
- void reserve(size_type new_capacity);
- size_type capacity() const;
- void shrink_to_fit();
-
- // --------------------------------------------------------------------------
- // Size management.
- //
- // clear() leaves the capacity() of the flat_tree unchanged.
-
- void clear();
-
- size_type size() const;
- size_type max_size() const;
- bool empty() const;
-
- // --------------------------------------------------------------------------
- // Iterators.
-
- iterator begin();
- const_iterator begin() const;
- const_iterator cbegin() const;
-
- iterator end();
- const_iterator end() const;
- const_iterator cend() const;
-
- reverse_iterator rbegin();
- const_reverse_iterator rbegin() const;
- const_reverse_iterator crbegin() const;
-
- reverse_iterator rend();
- const_reverse_iterator rend() const;
- const_reverse_iterator crend() const;
-
- // --------------------------------------------------------------------------
- // Insert operations.
- //
- // Assume that every operation invalidates iterators and references.
- // Insertion of one element can take O(size). Capacity of flat_tree grows in
- // an implementation-defined manner.
- //
- // NOTE: Prefer to build a new flat_tree from a std::vector (or similar)
- // instead of calling insert() repeatedly.
-
- std::pair<iterator, bool> insert(const value_type& val);
- std::pair<iterator, bool> insert(value_type&& val);
-
- iterator insert(const_iterator position_hint, const value_type& x);
- iterator insert(const_iterator position_hint, value_type&& x);
-
- // This method inserts the values from the range [first, last) into the
- // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can
- // overwrite existing values.
- template <class InputIterator>
- void insert(InputIterator first,
- InputIterator last,
- FlatContainerDupes dupes = KEEP_FIRST_OF_DUPES);
-
- template <class... Args>
- std::pair<iterator, bool> emplace(Args&&... args);
-
- template <class... Args>
- iterator emplace_hint(const_iterator position_hint, Args&&... args);
-
- // --------------------------------------------------------------------------
- // Erase operations.
- //
- // Assume that every operation invalidates iterators and references.
- //
- // erase(position), erase(first, last) can take O(size).
- // erase(key) may take O(size) + O(log(size)).
- //
- // Prefer base::EraseIf() or some other variation on erase(remove(), end())
- // idiom when deleting multiple non-consecutive elements.
-
- iterator erase(iterator position);
- iterator erase(const_iterator position);
- iterator erase(const_iterator first, const_iterator last);
- template <typename K>
- size_type erase(const K& key);
-
- // --------------------------------------------------------------------------
- // Comparators.
-
- key_compare key_comp() const;
- value_compare value_comp() const;
-
- // --------------------------------------------------------------------------
- // Search operations.
- //
- // Search operations have O(log(size)) complexity.
-
- template <typename K>
- size_type count(const K& key) const;
-
- template <typename K>
- iterator find(const K& key);
-
- template <typename K>
- const_iterator find(const K& key) const;
-
- template <typename K>
- std::pair<iterator, iterator> equal_range(const K& key);
-
- template <typename K>
- std::pair<const_iterator, const_iterator> equal_range(const K& key) const;
-
- template <typename K>
- iterator lower_bound(const K& key);
-
- template <typename K>
- const_iterator lower_bound(const K& key) const;
-
- template <typename K>
- iterator upper_bound(const K& key);
-
- template <typename K>
- const_iterator upper_bound(const K& key) const;
-
- // --------------------------------------------------------------------------
- // General operations.
- //
- // Assume that swap invalidates iterators and references.
- //
- // Implementation note: currently we use operator==() and operator<() on
- // std::vector, because they have the same contract we need, so we use them
- // directly for brevity and in case it is more optimal than calling equal()
- // and lexicograhpical_compare(). If the underlying container type is changed,
- // this code may need to be modified.
-
- void swap(flat_tree& other) noexcept;
-
- friend bool operator==(const flat_tree& lhs, const flat_tree& rhs) {
- return lhs.impl_.body_ == rhs.impl_.body_;
- }
-
- friend bool operator!=(const flat_tree& lhs, const flat_tree& rhs) {
- return !(lhs == rhs);
- }
-
- friend bool operator<(const flat_tree& lhs, const flat_tree& rhs) {
- return lhs.impl_.body_ < rhs.impl_.body_;
- }
-
- friend bool operator>(const flat_tree& lhs, const flat_tree& rhs) {
- return rhs < lhs;
- }
-
- friend bool operator>=(const flat_tree& lhs, const flat_tree& rhs) {
- return !(lhs < rhs);
- }
-
- friend bool operator<=(const flat_tree& lhs, const flat_tree& rhs) {
- return !(lhs > rhs);
- }
-
- friend void swap(flat_tree& lhs, flat_tree& rhs) noexcept { lhs.swap(rhs); }
-
- protected:
- // Emplaces a new item into the tree that is known not to be in it. This
- // is for implementing map operator[].
- template <class... Args>
- iterator unsafe_emplace(const_iterator position, Args&&... args);
-
- // Attempts to emplace a new element with key |key|. Only if |key| is not yet
- // present, construct value_type from |args| and insert it. Returns an
- // iterator to the element with key |key| and a bool indicating whether an
- // insertion happened.
- template <class K, class... Args>
- std::pair<iterator, bool> emplace_key_args(const K& key, Args&&... args);
-
- // Similar to |emplace_key_args|, but checks |hint| first as a possible
- // insertion position.
- template <class K, class... Args>
- std::pair<iterator, bool> emplace_hint_key_args(const_iterator hint,
- const K& key,
- Args&&... args);
-
- private:
- // Helper class for e.g. lower_bound that can compare a value on the left
- // to a key on the right.
- struct KeyValueCompare {
- // The key comparison object must outlive this class.
- explicit KeyValueCompare(const key_compare& key_comp)
- : key_comp_(key_comp) {}
-
- template <typename T, typename U>
- bool operator()(const T& lhs, const U& rhs) const {
- return key_comp_(extract_if_value_type(lhs), extract_if_value_type(rhs));
- }
-
- private:
- const key_type& extract_if_value_type(const value_type& v) const {
- GetKeyFromValue extractor;
- return extractor(v);
- }
-
- template <typename K>
- const K& extract_if_value_type(const K& k) const {
- return k;
- }
-
- const key_compare& key_comp_;
- };
-
- const flat_tree& as_const() { return *this; }
-
- iterator const_cast_it(const_iterator c_it) {
- auto distance = std::distance(cbegin(), c_it);
- return std::next(begin(), distance);
- }
-
- // This method is inspired by both std::map::insert(P&&) and
- // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent
- // element is not present yet, otherwise it overwrites. It returns an iterator
- // to the modified element and a flag indicating whether insertion or
- // assignment happened.
- template <class V>
- std::pair<iterator, bool> insert_or_assign(V&& val) {
- auto position = lower_bound(GetKeyFromValue()(val));
-
- if (position == end() || value_comp()(val, *position))
- return {impl_.body_.emplace(position, std::forward<V>(val)), true};
-
- *position = std::forward<V>(val);
- return {position, false};
- }
-
- // This method is similar to insert_or_assign, with the following differences:
- // - Instead of searching [begin(), end()) it only searches [first, last).
- // - In case no equivalent element is found, val is appended to the end of the
- // underlying body and an iterator to the next bigger element in [first,
- // last) is returned.
- template <class V>
- std::pair<iterator, bool> append_or_assign(iterator first,
- iterator last,
- V&& val) {
- auto position = std::lower_bound(first, last, val, value_comp());
-
- if (position == last || value_comp()(val, *position)) {
- // emplace_back might invalidate position, which is why distance needs to
- // be cached.
- const difference_type distance = std::distance(begin(), position);
- impl_.body_.emplace_back(std::forward<V>(val));
- return {std::next(begin(), distance), true};
- }
-
- *position = std::forward<V>(val);
- return {position, false};
- }
-
- // This method is similar to insert, with the following differences:
- // - Instead of searching [begin(), end()) it only searches [first, last).
- // - In case no equivalent element is found, val is appended to the end of the
- // underlying body and an iterator to the next bigger element in [first,
- // last) is returned.
- template <class V>
- std::pair<iterator, bool> append_unique(iterator first,
- iterator last,
- V&& val) {
- auto position = std::lower_bound(first, last, val, value_comp());
-
- if (position == last || value_comp()(val, *position)) {
- // emplace_back might invalidate position, which is why distance needs to
- // be cached.
- const difference_type distance = std::distance(begin(), position);
- impl_.body_.emplace_back(std::forward<V>(val));
- return {std::next(begin(), distance), true};
- }
-
- return {position, false};
- }
-
- void sort_and_unique(iterator first,
- iterator last,
- FlatContainerDupes dupes) {
- // Preserve stability for the unique code below.
- std::stable_sort(first, last, impl_.get_value_comp());
-
- auto comparator = [this](const value_type& lhs, const value_type& rhs) {
- // lhs is already <= rhs due to sort, therefore
- // !(lhs < rhs) <=> lhs == rhs.
- return !impl_.get_value_comp()(lhs, rhs);
- };
-
- iterator erase_after;
- switch (dupes) {
- case KEEP_FIRST_OF_DUPES:
- erase_after = std::unique(first, last, comparator);
- break;
- case KEEP_LAST_OF_DUPES:
- erase_after = LastUnique(first, last, comparator);
- break;
- }
- erase(erase_after, last);
- }
-
- // To support comparators that may not be possible to default-construct, we
- // have to store an instance of Compare. Using this to store all internal
- // state of flat_tree and using private inheritance to store compare lets us
- // take advantage of an empty base class optimization to avoid extra space in
- // the common case when Compare has no state.
- struct Impl : private value_compare {
- Impl() = default;
-
- template <class Cmp, class... Body>
- explicit Impl(Cmp&& compare_arg, Body&&... underlying_type_args)
- : value_compare(std::forward<Cmp>(compare_arg)),
- body_(std::forward<Body>(underlying_type_args)...) {}
-
- const value_compare& get_value_comp() const { return *this; }
- const key_compare& get_key_comp() const { return *this; }
-
- underlying_type body_;
- } impl_;
-
- // If the compare is not transparent we want to construct key_type once.
- template <typename K>
- using KeyTypeOrK = typename std::
- conditional<IsTransparentCompare<key_compare>::value, K, key_type>::type;
-};
-
-// ----------------------------------------------------------------------------
-// Lifetime.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree() = default;
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
- const KeyCompare& comp)
- : impl_(comp) {}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class InputIterator>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
- InputIterator first,
- InputIterator last,
- FlatContainerDupes dupe_handling,
- const KeyCompare& comp)
- : impl_(comp, first, last) {
- sort_and_unique(begin(), end(), dupe_handling);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
- const flat_tree&) = default;
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
- std::vector<value_type> items,
- FlatContainerDupes dupe_handling,
- const KeyCompare& comp)
- : impl_(comp, std::move(items)) {
- sort_and_unique(begin(), end(), dupe_handling);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
- std::initializer_list<value_type> ilist,
- FlatContainerDupes dupe_handling,
- const KeyCompare& comp)
- : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default;
-
-// ----------------------------------------------------------------------------
-// Assignments.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
- const flat_tree&) -> flat_tree& = default;
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(flat_tree &&)
- -> flat_tree& = default;
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
- std::initializer_list<value_type> ilist) -> flat_tree& {
- impl_.body_ = ilist;
- sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES);
- return *this;
-}
-
-// ----------------------------------------------------------------------------
-// Memory management.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::reserve(
- size_type new_capacity) {
- impl_.body_.reserve(new_capacity);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::capacity() const
- -> size_type {
- return impl_.body_.capacity();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::shrink_to_fit() {
- impl_.body_.shrink_to_fit();
-}
-
-// ----------------------------------------------------------------------------
-// Size management.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::clear() {
- impl_.body_.clear();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::size() const
- -> size_type {
- return impl_.body_.size();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::max_size() const
- -> size_type {
- return impl_.body_.max_size();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-bool flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::empty() const {
- return impl_.body_.empty();
-}
-
-// ----------------------------------------------------------------------------
-// Iterators.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::begin() -> iterator {
- return impl_.body_.begin();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::begin() const
- -> const_iterator {
- return impl_.body_.begin();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::cbegin() const
- -> const_iterator {
- return impl_.body_.cbegin();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::end() -> iterator {
- return impl_.body_.end();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::end() const
- -> const_iterator {
- return impl_.body_.end();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::cend() const
- -> const_iterator {
- return impl_.body_.cend();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rbegin()
- -> reverse_iterator {
- return impl_.body_.rbegin();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rbegin() const
- -> const_reverse_iterator {
- return impl_.body_.rbegin();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::crbegin() const
- -> const_reverse_iterator {
- return impl_.body_.crbegin();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rend()
- -> reverse_iterator {
- return impl_.body_.rend();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rend() const
- -> const_reverse_iterator {
- return impl_.body_.rend();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::crend() const
- -> const_reverse_iterator {
- return impl_.body_.crend();
-}
-
-// ----------------------------------------------------------------------------
-// Insert operations.
-//
-// Currently we use position_hint the same way as eastl or boost:
-// https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set.h#L493
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
- const value_type& val) -> std::pair<iterator, bool> {
- return emplace_key_args(GetKeyFromValue()(val), val);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
- value_type&& val) -> std::pair<iterator, bool> {
- return emplace_key_args(GetKeyFromValue()(val), std::move(val));
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
- const_iterator position_hint,
- const value_type& val) -> iterator {
- return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), val)
- .first;
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
- const_iterator position_hint,
- value_type&& val) -> iterator {
- return emplace_hint_key_args(position_hint, GetKeyFromValue()(val),
- std::move(val))
- .first;
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class InputIterator>
-void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
- InputIterator first,
- InputIterator last,
- FlatContainerDupes dupes) {
- if (first == last)
- return;
-
- // Cache results whether existing elements should be overwritten and whether
- // inserting new elements happens immediately or will be done in a batch.
- const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES;
- const bool insert_inplace =
- is_multipass<InputIterator>() && std::next(first) == last;
-
- if (insert_inplace) {
- if (overwrite_existing) {
- for (; first != last; ++first)
- insert_or_assign(*first);
- } else
- std::copy(first, last, std::inserter(*this, end()));
- return;
- }
-
- // Provide a convenience lambda to obtain an iterator pointing past the last
- // old element. This needs to be dymanic due to possible re-allocations.
- const size_type original_size = size();
- auto middle = [this, original_size]() {
- return std::next(begin(), original_size);
- };
-
- // For batch updates initialize the first insertion point.
- difference_type pos_first_new = original_size;
-
- // Loop over the input range while appending new values and overwriting
- // existing ones, if applicable. Keep track of the first insertion point.
- if (overwrite_existing) {
- for (; first != last; ++first) {
- std::pair<iterator, bool> result =
- append_or_assign(begin(), middle(), *first);
- if (result.second) {
- pos_first_new =
- std::min(pos_first_new, std::distance(begin(), result.first));
- }
- }
- } else {
- for (; first != last; ++first) {
- std::pair<iterator, bool> result =
- append_unique(begin(), middle(), *first);
- if (result.second) {
- pos_first_new =
- std::min(pos_first_new, std::distance(begin(), result.first));
- }
- }
- }
-
- // The new elements might be unordered and contain duplicates, so post-process
- // the just inserted elements and merge them with the rest, inserting them at
- // the previously found spot.
- sort_and_unique(middle(), end(), dupes);
- std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(),
- value_comp());
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class... Args>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace(Args&&... args)
- -> std::pair<iterator, bool> {
- return insert(value_type(std::forward<Args>(args)...));
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class... Args>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_hint(
- const_iterator position_hint,
- Args&&... args) -> iterator {
- return insert(position_hint, value_type(std::forward<Args>(args)...));
-}
-
-// ----------------------------------------------------------------------------
-// Erase operations.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(
- iterator position) -> iterator {
- return impl_.body_.erase(position);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(
- const_iterator position) -> iterator {
- return impl_.body_.erase(position);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(const K& val)
- -> size_type {
- auto eq_range = equal_range(val);
- auto res = std::distance(eq_range.first, eq_range.second);
- erase(eq_range.first, eq_range.second);
- return res;
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(
- const_iterator first,
- const_iterator last) -> iterator {
- return impl_.body_.erase(first, last);
-}
-
-// ----------------------------------------------------------------------------
-// Comparators.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::key_comp() const
- -> key_compare {
- return impl_.get_key_comp();
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::value_comp() const
- -> value_compare {
- return impl_.get_value_comp();
-}
-
-// ----------------------------------------------------------------------------
-// Search operations.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::count(
- const K& key) const -> size_type {
- auto eq_range = equal_range(key);
- return std::distance(eq_range.first, eq_range.second);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::find(const K& key)
- -> iterator {
- return const_cast_it(as_const().find(key));
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::find(
- const K& key) const -> const_iterator {
- auto eq_range = equal_range(key);
- return (eq_range.first == eq_range.second) ? end() : eq_range.first;
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::equal_range(
- const K& key) -> std::pair<iterator, iterator> {
- auto res = as_const().equal_range(key);
- return {const_cast_it(res.first), const_cast_it(res.second)};
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::equal_range(
- const K& key) const -> std::pair<const_iterator, const_iterator> {
- auto lower = lower_bound(key);
-
- GetKeyFromValue extractor;
- if (lower == end() || impl_.get_key_comp()(key, extractor(*lower)))
- return {lower, lower};
-
- return {lower, std::next(lower)};
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::lower_bound(
- const K& key) -> iterator {
- return const_cast_it(as_const().lower_bound(key));
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::lower_bound(
- const K& key) const -> const_iterator {
- static_assert(std::is_convertible<const KeyTypeOrK<K>&, const K&>::value,
- "Requested type cannot be bound to the container's key_type "
- "which is required for a non-transparent compare.");
-
- const KeyTypeOrK<K>& key_ref = key;
-
- KeyValueCompare key_value(impl_.get_key_comp());
- return std::lower_bound(begin(), end(), key_ref, key_value);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::upper_bound(
- const K& key) -> iterator {
- return const_cast_it(as_const().upper_bound(key));
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <typename K>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::upper_bound(
- const K& key) const -> const_iterator {
- static_assert(std::is_convertible<const KeyTypeOrK<K>&, const K&>::value,
- "Requested type cannot be bound to the container's key_type "
- "which is required for a non-transparent compare.");
-
- const KeyTypeOrK<K>& key_ref = key;
-
- KeyValueCompare key_value(impl_.get_key_comp());
- return std::upper_bound(begin(), end(), key_ref, key_value);
-}
-
-// ----------------------------------------------------------------------------
-// General operations.
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::swap(
- flat_tree& other) noexcept {
- std::swap(impl_, other.impl_);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class... Args>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::unsafe_emplace(
- const_iterator position,
- Args&&... args) -> iterator {
- return impl_.body_.emplace(position, std::forward<Args>(args)...);
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class K, class... Args>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_key_args(
- const K& key,
- Args&&... args) -> std::pair<iterator, bool> {
- auto lower = lower_bound(key);
- if (lower == end() || key_comp()(key, GetKeyFromValue()(*lower)))
- return {unsafe_emplace(lower, std::forward<Args>(args)...), true};
- return {lower, false};
-}
-
-template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
-template <class K, class... Args>
-auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_hint_key_args(
- const_iterator hint,
- const K& key,
- Args&&... args) -> std::pair<iterator, bool> {
- GetKeyFromValue extractor;
- if ((hint == begin() || key_comp()(extractor(*std::prev(hint)), key))) {
- if (hint == end() || key_comp()(key, extractor(*hint))) {
- // *(hint - 1) < key < *hint => key did not exist and hint is correct.
- return {unsafe_emplace(hint, std::forward<Args>(args)...), true};
- }
- if (!key_comp()(extractor(*hint), key)) {
- // key == *hint => no-op, return correct hint.
- return {const_cast_it(hint), false};
- }
- }
- // hint was not helpful, dispatch to hintless version.
- return emplace_key_args(key, std::forward<Args>(args)...);
-}
-
-// For containers like sets, the key is the same as the value. This implements
-// the GetKeyFromValue template parameter to flat_tree for this case.
-template <class Key>
-struct GetKeyFromValueIdentity {
- const Key& operator()(const Key& k) const { return k; }
-};
-
-} // namespace internal
-
-// ----------------------------------------------------------------------------
-// Free functions.
-
-// Erases all elements that match predicate. It has O(size) complexity.
-template <class Key,
- class Value,
- class GetKeyFromValue,
- class KeyCompare,
- typename Predicate>
-void EraseIf(base::internal::flat_tree<Key, Value, GetKeyFromValue, KeyCompare>&
- container,
- Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-} // namespace base
-
-#endif // BASE_CONTAINERS_FLAT_TREE_H_
diff --git a/gn/base/containers/span.h b/gn/base/containers/span.h
deleted file mode 100644
index c9718619bcc..00000000000
--- a/gn/base/containers/span.h
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2017 The Chromium 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 BASE_CONTAINERS_SPAN_H_
-#define BASE_CONTAINERS_SPAN_H_
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <array>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/stl_util.h"
-
-namespace base {
-
-// [views.constants]
-constexpr size_t dynamic_extent = static_cast<size_t>(-1);
-
-template <typename T, size_t Extent = dynamic_extent>
-class span;
-
-namespace internal {
-
-template <typename T>
-struct IsSpanImpl : std::false_type {};
-
-template <typename T, size_t Extent>
-struct IsSpanImpl<span<T, Extent>> : std::true_type {};
-
-template <typename T>
-using IsSpan = IsSpanImpl<std::decay_t<T>>;
-
-template <typename T>
-struct IsStdArrayImpl : std::false_type {};
-
-template <typename T, size_t N>
-struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
-
-template <typename T>
-using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
-
-template <typename T>
-using IsCArray = std::is_array<std::remove_reference_t<T>>;
-
-template <typename From, typename To>
-using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
-
-template <typename Container, typename T>
-using ContainerHasConvertibleData = IsLegalDataConversion<
- std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>,
- T>;
-
-template <typename Container>
-using ContainerHasIntegralSize =
- std::is_integral<decltype(base::size(std::declval<Container>()))>;
-
-template <typename From, size_t FromExtent, typename To, size_t ToExtent>
-using EnableIfLegalSpanConversion =
- std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
- IsLegalDataConversion<From, To>::value>;
-
-// SFINAE check if Array can be converted to a span<T>.
-template <typename Array, size_t N, typename T, size_t Extent>
-using EnableIfSpanCompatibleArray =
- std::enable_if_t<(Extent == dynamic_extent || Extent == N) &&
- ContainerHasConvertibleData<Array, T>::value>;
-
-// SFINAE check if Container can be converted to a span<T>.
-template <typename Container, typename T>
-using EnableIfSpanCompatibleContainer =
- std::enable_if_t<!internal::IsSpan<Container>::value &&
- !internal::IsStdArray<Container>::value &&
- !internal::IsCArray<Container>::value &&
- ContainerHasConvertibleData<Container, T>::value &&
- ContainerHasIntegralSize<Container>::value>;
-
-} // namespace internal
-
-// A span is a value type that represents an array of elements of type T. Since
-// it only consists of a pointer to memory with an associated size, it is very
-// light-weight. It is cheap to construct, copy, move and use spans, so that
-// users are encouraged to use it as a pass-by-value parameter. A span does not
-// own the underlying memory, so care must be taken to ensure that a span does
-// not outlive the backing store.
-//
-// span is somewhat analogous to StringPiece, but with arbitrary element types,
-// allowing mutation if T is non-const.
-//
-// span is implicitly convertible from C++ arrays, as well as most [1]
-// container-like types that provide a data() and size() method (such as
-// std::vector<T>). A mutable span<T> can also be implicitly converted to an
-// immutable span<const T>.
-//
-// Consider using a span for functions that take a data pointer and size
-// parameter: it allows the function to still act on an array-like type, while
-// allowing the caller code to be a bit more concise.
-//
-// For read-only data access pass a span<const T>: the caller can supply either
-// a span<const T> or a span<T>, while the callee will have a read-only view.
-// For read-write access a mutable span<T> is required.
-//
-// Without span:
-// Read-Only:
-// // std::string HexEncode(const uint8_t* data, size_t size);
-// std::vector<uint8_t> data_buffer = GenerateData();
-// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
-//
-// Mutable:
-// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
-// char str_buffer[100];
-// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
-//
-// With span:
-// Read-Only:
-// // std::string HexEncode(base::span<const uint8_t> data);
-// std::vector<uint8_t> data_buffer = GenerateData();
-// std::string r = HexEncode(data_buffer);
-//
-// Mutable:
-// // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
-// char str_buffer[100];
-// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
-//
-// Spans with "const" and pointers
-// -------------------------------
-//
-// Const and pointers can get confusing. Here are vectors of pointers and their
-// corresponding spans:
-//
-// const std::vector<int*> => base::span<int* const>
-// std::vector<const int*> => base::span<const int*>
-// const std::vector<const int*> => base::span<const int* const>
-//
-// Differences from the working group proposal
-// -------------------------------------------
-//
-// https://wg21.link/P0122 is the latest working group proposal, Chromium
-// currently implements R7. Differences between the proposal and the
-// implementation are documented in subsections below.
-//
-// Differences from [span.objectrep]:
-// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
-// std::byte
-//
-// Differences in constants and types:
-// - index_type is aliased to size_t
-//
-// Differences from [span.sub]:
-// - using size_t instead of ptrdiff_t for indexing
-//
-// Differences from [span.obs]:
-// - using size_t instead of ptrdiff_t to represent size()
-//
-// Differences from [span.elem]:
-// - using size_t instead of ptrdiff_t for indexing
-//
-// Furthermore, all constructors and methods are marked noexcept due to the lack
-// of exceptions in Chromium.
-//
-// Due to the lack of class template argument deduction guides in C++14
-// appropriate make_span() utility functions are provided.
-
-// [span], class template span
-template <typename T, size_t Extent>
-class span {
- public:
- using element_type = T;
- using value_type = std::remove_cv_t<T>;
- using index_type = size_t;
- using difference_type = ptrdiff_t;
- using pointer = T*;
- using reference = T&;
- using iterator = T*;
- using const_iterator = const T*;
- using reverse_iterator = std::reverse_iterator<iterator>;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
- static constexpr index_type extent = Extent;
-
- // [span.cons], span constructors, copy, assignment, and destructor
- constexpr span() noexcept : data_(nullptr), size_(0) {
- static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
- }
-
- constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) {
- CHECK(Extent == dynamic_extent || Extent == size);
- }
-
- // Artificially templatized to break ambiguity for span(ptr, 0).
- template <typename = void>
- constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
- // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
- CHECK(begin <= end);
- }
-
- template <
- size_t N,
- typename = internal::EnableIfSpanCompatibleArray<T (&)[N], N, T, Extent>>
- constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {}
-
- template <
- size_t N,
- typename = internal::
- EnableIfSpanCompatibleArray<std::array<value_type, N>&, N, T, Extent>>
- constexpr span(std::array<value_type, N>& array) noexcept
- : span(base::data(array), N) {}
-
- template <size_t N,
- typename = internal::EnableIfSpanCompatibleArray<
- const std::array<value_type, N>&,
- N,
- T,
- Extent>>
- constexpr span(const std::array<value_type, N>& array) noexcept
- : span(base::data(array), N) {}
-
- // Conversion from a container that has compatible base::data() and integral
- // base::size().
- template <typename Container,
- typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
- constexpr span(Container& container) noexcept
- : span(base::data(container), base::size(container)) {}
-
- template <
- typename Container,
- typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
- span(const Container& container) noexcept
- : span(base::data(container), base::size(container)) {}
-
- constexpr span(const span& other) noexcept = default;
-
- // Conversions from spans of compatible types and extents: this allows a
- // span<T> to be seamlessly used as a span<const T>, but not the other way
- // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
- template <
- typename U,
- size_t OtherExtent,
- typename =
- internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
- constexpr span(const span<U, OtherExtent>& other)
- : span(other.data(), other.size()) {}
-
- constexpr span& operator=(const span& other) noexcept = default;
- ~span() noexcept = default;
-
- // [span.sub], span subviews
- template <size_t Count>
- constexpr span<T, Count> first() const noexcept {
- static_assert(Extent == dynamic_extent || Count <= Extent,
- "Count must not exceed Extent");
- CHECK(Extent != dynamic_extent || Count <= size());
- return {data(), Count};
- }
-
- template <size_t Count>
- constexpr span<T, Count> last() const noexcept {
- static_assert(Extent == dynamic_extent || Count <= Extent,
- "Count must not exceed Extent");
- CHECK(Extent != dynamic_extent || Count <= size());
- return {data() + (size() - Count), Count};
- }
-
- template <size_t Offset, size_t Count = dynamic_extent>
- constexpr span<T,
- (Count != dynamic_extent
- ? Count
- : (Extent != dynamic_extent ? Extent - Offset
- : dynamic_extent))>
- subspan() const noexcept {
- static_assert(Extent == dynamic_extent || Offset <= Extent,
- "Offset must not exceed Extent");
- static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
- Count <= Extent - Offset,
- "Count must not exceed Extent - Offset");
- CHECK(Extent != dynamic_extent || Offset <= size());
- CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
- Count <= size() - Offset);
- return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
- }
-
- constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
- // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
- CHECK(count <= size());
- return {data(), count};
- }
-
- constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
- // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
- CHECK(count <= size());
- return {data() + (size() - count), count};
- }
-
- constexpr span<T, dynamic_extent> subspan(size_t offset,
- size_t count = dynamic_extent) const
- noexcept {
- // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
- CHECK(offset <= size());
- CHECK(count == dynamic_extent || count <= size() - offset);
- return {data() + offset, count != dynamic_extent ? count : size() - offset};
- }
-
- // [span.obs], span observers
- constexpr size_t size() const noexcept { return size_; }
- constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
- constexpr bool empty() const noexcept { return size() == 0; }
-
- // [span.elem], span element access
- constexpr T& operator[](size_t idx) const noexcept {
- // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
- CHECK(idx < size());
- return *(data() + idx);
- }
-
- constexpr T& operator()(size_t idx) const noexcept {
- // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
- CHECK(idx < size());
- return *(data() + idx);
- }
-
- constexpr T* data() const noexcept { return data_; }
-
- // [span.iter], span iterator support
- constexpr iterator begin() const noexcept { return data(); }
- constexpr iterator end() const noexcept { return data() + size(); }
-
- constexpr const_iterator cbegin() const noexcept { return begin(); }
- constexpr const_iterator cend() const noexcept { return end(); }
-
- constexpr reverse_iterator rbegin() const noexcept {
- return reverse_iterator(end());
- }
- constexpr reverse_iterator rend() const noexcept {
- return reverse_iterator(begin());
- }
-
- constexpr const_reverse_iterator crbegin() const noexcept {
- return const_reverse_iterator(cend());
- }
- constexpr const_reverse_iterator crend() const noexcept {
- return const_reverse_iterator(cbegin());
- }
-
- private:
- T* data_;
- size_t size_;
-};
-
-// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
-// definition is required.
-template <class T, size_t Extent>
-constexpr size_t span<T, Extent>::extent;
-
-// [span.comparison], span comparison operators
-// Relational operators. Equality is a element-wise comparison.
-template <typename T, size_t X, typename U, size_t Y>
-constexpr bool operator==(span<T, X> lhs, span<U, Y> rhs) noexcept {
- return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend());
-}
-
-template <typename T, size_t X, typename U, size_t Y>
-constexpr bool operator!=(span<T, X> lhs, span<U, Y> rhs) noexcept {
- return !(lhs == rhs);
-}
-
-template <typename T, size_t X, typename U, size_t Y>
-constexpr bool operator<(span<T, X> lhs, span<U, Y> rhs) noexcept {
- return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
- rhs.cend());
-}
-
-template <typename T, size_t X, typename U, size_t Y>
-constexpr bool operator<=(span<T, X> lhs, span<U, Y> rhs) noexcept {
- return !(rhs < lhs);
-}
-
-template <typename T, size_t X, typename U, size_t Y>
-constexpr bool operator>(span<T, X> lhs, span<U, Y> rhs) noexcept {
- return rhs < lhs;
-}
-
-template <typename T, size_t X, typename U, size_t Y>
-constexpr bool operator>=(span<T, X> lhs, span<U, Y> rhs) noexcept {
- return !(lhs < rhs);
-}
-
-// [span.objectrep], views of object representation
-template <typename T, size_t X>
-span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
-as_bytes(span<T, X> s) noexcept {
- return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
-}
-
-template <typename T,
- size_t X,
- typename = std::enable_if_t<!std::is_const<T>::value>>
-span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
-as_writable_bytes(span<T, X> s) noexcept {
- return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
-}
-
-// Type-deducing helpers for constructing a span.
-template <typename T>
-constexpr span<T> make_span(T* data, size_t size) noexcept {
- return {data, size};
-}
-
-template <typename T>
-constexpr span<T> make_span(T* begin, T* end) noexcept {
- return {begin, end};
-}
-
-template <typename T, size_t N>
-constexpr span<T, N> make_span(T (&array)[N]) noexcept {
- return array;
-}
-
-template <typename T, size_t N>
-constexpr span<T, N> make_span(std::array<T, N>& array) noexcept {
- return array;
-}
-
-template <typename T, size_t N>
-constexpr span<const T, N> make_span(const std::array<T, N>& array) noexcept {
- return array;
-}
-
-template <typename Container,
- typename T = typename Container::value_type,
- typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
-constexpr span<T> make_span(Container& container) noexcept {
- return container;
-}
-
-template <
- typename Container,
- typename T = const typename Container::value_type,
- typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
-constexpr span<T> make_span(const Container& container) noexcept {
- return container;
-}
-
-template <typename T, size_t X>
-constexpr span<T, X> make_span(const span<T, X>& span) noexcept {
- return span;
-}
-
-} // namespace base
-
-#endif // BASE_CONTAINERS_SPAN_H_
diff --git a/gn/base/containers/vector_buffer.h b/gn/base/containers/vector_buffer.h
deleted file mode 100644
index a72c1ed95e8..00000000000
--- a/gn/base/containers/vector_buffer.h
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2017 The Chromium 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 BASE_CONTAINERS_VECTOR_BUFFERS_H_
-#define BASE_CONTAINERS_VECTOR_BUFFERS_H_
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <type_traits>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/macros.h"
-
-namespace base {
-namespace internal {
-
-// Internal implementation detail of base/containers.
-//
-// Implements a vector-like buffer that holds a certain capacity of T. Unlike
-// std::vector, VectorBuffer never constructs or destructs its arguments, and
-// can't change sizes. But it does implement templates to assist in efficient
-// moving and destruction of those items manually.
-//
-// In particular, the destructor function does not iterate over the items if
-// there is no destructor. Moves should be implemented as a memcpy/memmove for
-// trivially copyable objects (POD) otherwise, it should be a std::move if
-// possible, and as a last resort it falls back to a copy. This behavior is
-// similar to std::vector.
-//
-// No special consideration is done for noexcept move constructors since
-// we compile without exceptions.
-//
-// The current API does not support moving overlapping ranges.
-template <typename T>
-class VectorBuffer {
- public:
- constexpr VectorBuffer() = default;
-
-#if defined(__clang__) && !defined(__native_client__)
- // This constructor converts an uninitialized void* to a T* which triggers
- // clang Control Flow Integrity. Since this is as-designed, disable.
- __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
-#endif
- VectorBuffer(size_t count)
- : buffer_(reinterpret_cast<T*>(malloc(sizeof(T) * count))),
- capacity_(count) {
- }
- VectorBuffer(VectorBuffer&& other) noexcept
- : buffer_(other.buffer_), capacity_(other.capacity_) {
- other.buffer_ = nullptr;
- other.capacity_ = 0;
- }
-
- ~VectorBuffer() { free(buffer_); }
-
- VectorBuffer& operator=(VectorBuffer&& other) {
- free(buffer_);
- buffer_ = other.buffer_;
- capacity_ = other.capacity_;
-
- other.buffer_ = nullptr;
- other.capacity_ = 0;
- return *this;
- }
-
- size_t capacity() const { return capacity_; }
-
- T& operator[](size_t i) { return buffer_[i]; }
- const T& operator[](size_t i) const { return buffer_[i]; }
- T* begin() { return buffer_; }
- T* end() { return &buffer_[capacity_]; }
-
- // DestructRange ------------------------------------------------------------
-
- // Trivially destructible objects need not have their destructors called.
- template <typename T2 = T,
- typename std::enable_if<std::is_trivially_destructible<T2>::value,
- int>::type = 0>
- void DestructRange(T* begin, T* end) {}
-
- // Non-trivially destructible objects must have their destructors called
- // individually.
- template <typename T2 = T,
- typename std::enable_if<!std::is_trivially_destructible<T2>::value,
- int>::type = 0>
- void DestructRange(T* begin, T* end) {
- while (begin != end) {
- begin->~T();
- begin++;
- }
- }
-
- // MoveRange ----------------------------------------------------------------
- //
- // The destructor will be called (as necessary) for all moved types. The
- // ranges must not overlap.
- //
- // The parameters and begin and end (one past the last) of the input buffer,
- // and the address of the first element to copy to. There must be sufficient
- // room in the destination for all items in the range [begin, end).
-
- // Trivially copyable types can use memcpy. trivially copyable implies
- // that there is a trivial destructor as we don't have to call it.
- template <typename T2 = T,
- typename std::enable_if<base::is_trivially_copyable<T2>::value,
- int>::type = 0>
- static void MoveRange(T* from_begin, T* from_end, T* to) {
- DCHECK(!RangesOverlap(from_begin, from_end, to));
- memcpy(to, from_begin, (from_end - from_begin) * sizeof(T));
- }
-
- // Not trivially copyable, but movable: call the move constructor and
- // destruct the original.
- template <typename T2 = T,
- typename std::enable_if<std::is_move_constructible<T2>::value &&
- !base::is_trivially_copyable<T2>::value,
- int>::type = 0>
- static void MoveRange(T* from_begin, T* from_end, T* to) {
- DCHECK(!RangesOverlap(from_begin, from_end, to));
- while (from_begin != from_end) {
- new (to) T(std::move(*from_begin));
- from_begin->~T();
- from_begin++;
- to++;
- }
- }
-
- // Not movable, not trivially copyable: call the copy constructor and
- // destruct the original.
- template <typename T2 = T,
- typename std::enable_if<!std::is_move_constructible<T2>::value &&
- !base::is_trivially_copyable<T2>::value,
- int>::type = 0>
- static void MoveRange(T* from_begin, T* from_end, T* to) {
- DCHECK(!RangesOverlap(from_begin, from_end, to));
- while (from_begin != from_end) {
- new (to) T(*from_begin);
- from_begin->~T();
- from_begin++;
- to++;
- }
- }
-
- private:
- static bool RangesOverlap(const T* from_begin,
- const T* from_end,
- const T* to) {
- return !(to >= from_end || to + (from_end - from_begin) <= from_begin);
- }
-
- T* buffer_ = nullptr;
- size_t capacity_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(VectorBuffer);
-};
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_
diff --git a/gn/base/environment.cc b/gn/base/environment.cc
deleted file mode 100644
index 7f0e5d9d5d1..00000000000
--- a/gn/base/environment.cc
+++ /dev/null
@@ -1,237 +0,0 @@
-// 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 "base/environment.h"
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <stdlib.h>
-#endif
-
-namespace base {
-
-namespace {
-
-class EnvironmentImpl : public Environment {
- public:
- bool GetVar(StringPiece variable_name, std::string* result) override {
- if (GetVarImpl(variable_name, result))
- return true;
-
- // Some commonly used variable names are uppercase while others
- // are lowercase, which is inconsistent. Let's try to be helpful
- // and look for a variable name with the reverse case.
- // I.e. HTTP_PROXY may be http_proxy for some users/systems.
- char first_char = variable_name[0];
- std::string alternate_case_var;
- if (IsAsciiLower(first_char))
- alternate_case_var = ToUpperASCII(variable_name);
- else if (IsAsciiUpper(first_char))
- alternate_case_var = ToLowerASCII(variable_name);
- else
- return false;
- return GetVarImpl(alternate_case_var, result);
- }
-
- bool SetVar(StringPiece variable_name,
- const std::string& new_value) override {
- return SetVarImpl(variable_name, new_value);
- }
-
- bool UnSetVar(StringPiece variable_name) override {
- return UnSetVarImpl(variable_name);
- }
-
- private:
- bool GetVarImpl(StringPiece variable_name, std::string* result) {
-#if defined(OS_WIN)
- DWORD value_length =
- ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr, 0);
- if (value_length == 0)
- return false;
- if (result) {
- std::unique_ptr<wchar_t[]> value(new wchar_t[value_length]);
- ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(),
- value_length);
- *result = WideToUTF8(value.get());
- }
- return true;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- const char* env_value = getenv(variable_name.data());
- if (!env_value)
- return false;
- // Note that the variable may be defined but empty.
- if (result)
- *result = env_value;
- return true;
-#endif
- }
-
- bool SetVarImpl(StringPiece variable_name, const std::string& new_value) {
-#if defined(OS_WIN)
- // On success, a nonzero value is returned.
- return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(),
- UTF8ToWide(new_value).c_str());
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- // On success, zero is returned.
- return !setenv(variable_name.data(), new_value.c_str(), 1);
-#endif
- }
-
- bool UnSetVarImpl(StringPiece variable_name) {
-#if defined(OS_WIN)
- // On success, a nonzero value is returned.
- return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- // On success, zero is returned.
- return !unsetenv(variable_name.data());
-#endif
- }
-};
-
-// Parses a null-terminated input string of an environment block. The key is
-// placed into the given string, and the total length of the line, including
-// the terminating null, is returned.
-size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
- NativeEnvironmentString* key) {
- // Skip to the equals or end of the string, this is the key.
- size_t cur = 0;
- while (input[cur] && input[cur] != '=')
- cur++;
- *key = NativeEnvironmentString(&input[0], cur);
-
- // Now just skip to the end of the string.
- while (input[cur])
- cur++;
- return cur + 1;
-}
-
-} // namespace
-
-namespace env_vars {
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-// On Posix systems, this variable contains the location of the user's home
-// directory. (e.g, /home/username/).
-const char kHome[] = "HOME";
-#endif
-
-} // namespace env_vars
-
-Environment::~Environment() = default;
-
-// static
-std::unique_ptr<Environment> Environment::Create() {
- return std::make_unique<EnvironmentImpl>();
-}
-
-bool Environment::HasVar(StringPiece variable_name) {
- return GetVar(variable_name, nullptr);
-}
-
-#if defined(OS_WIN)
-
-string16 AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes) {
- string16 result;
-
- // First copy all unmodified values to the output.
- size_t cur_env = 0;
- string16 key;
- while (env[cur_env]) {
- const wchar_t* line = &env[cur_env];
- size_t line_length = ParseEnvLine(line, &key);
-
- // Keep only values not specified in the change vector.
- EnvironmentMap::const_iterator found_change = changes.find(key);
- if (found_change == changes.end())
- result.append(line, line_length);
-
- cur_env += line_length;
- }
-
- // Now append all modified and new values.
- for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end();
- ++i) {
- if (!i->second.empty()) {
- result.append(i->first);
- result.push_back('=');
- result.append(i->second);
- result.push_back(0);
- }
- }
-
- // An additional null marks the end of the list. We always need a double-null
- // in case nothing was added above.
- if (result.empty())
- result.push_back(0);
- result.push_back(0);
- return result;
-}
-
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
- const EnvironmentMap& changes) {
- std::string value_storage; // Holds concatenated null-terminated strings.
- std::vector<size_t> result_indices; // Line indices into value_storage.
-
- // First build up all of the unchanged environment strings. These are
- // null-terminated of the form "key=value".
- std::string key;
- for (size_t i = 0; env[i]; i++) {
- size_t line_length = ParseEnvLine(env[i], &key);
-
- // Keep only values not specified in the change vector.
- EnvironmentMap::const_iterator found_change = changes.find(key);
- if (found_change == changes.end()) {
- result_indices.push_back(value_storage.size());
- value_storage.append(env[i], line_length);
- }
- }
-
- // Now append all modified and new values.
- for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end();
- ++i) {
- if (!i->second.empty()) {
- result_indices.push_back(value_storage.size());
- value_storage.append(i->first);
- value_storage.push_back('=');
- value_storage.append(i->second);
- value_storage.push_back(0);
- }
- }
-
- size_t pointer_count_required =
- result_indices.size() + 1 + // Null-terminated array of pointers.
- (value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
- std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
-
- // The string storage goes after the array of pointers.
- char* storage_data =
- reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
- if (!value_storage.empty())
- memcpy(storage_data, value_storage.data(), value_storage.size());
-
- // Fill array of pointers at the beginning of the result.
- for (size_t i = 0; i < result_indices.size(); i++)
- result[i] = &storage_data[result_indices[i]];
- result[result_indices.size()] = 0; // Null terminator.
-
- return result;
-}
-
-#endif // OS_WIN
-
-} // namespace base
diff --git a/gn/base/environment.h b/gn/base/environment.h
deleted file mode 100644
index 82af9878dac..00000000000
--- a/gn/base/environment.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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.
-
-#ifndef BASE_ENVIRONMENT_H_
-#define BASE_ENVIRONMENT_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "util/build_config.h"
-
-namespace base {
-
-namespace env_vars {
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-extern const char kHome[];
-#endif
-
-} // namespace env_vars
-
-class Environment {
- public:
- virtual ~Environment();
-
- // Returns the appropriate platform-specific instance.
- static std::unique_ptr<Environment> Create();
-
- // Gets an environment variable's value and stores it in |result|.
- // Returns false if the key is unset.
- virtual bool GetVar(StringPiece variable_name, std::string* result) = 0;
-
- // Syntactic sugar for GetVar(variable_name, nullptr);
- virtual bool HasVar(StringPiece variable_name);
-
- // Returns true on success, otherwise returns false.
- virtual bool SetVar(StringPiece variable_name,
- const std::string& new_value) = 0;
-
- // Returns true on success, otherwise returns false.
- virtual bool UnSetVar(StringPiece variable_name) = 0;
-};
-
-#if defined(OS_WIN)
-
-typedef string16 NativeEnvironmentString;
-typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
- EnvironmentMap;
-
-// Returns a modified environment vector constructed from the given environment
-// and the list of changes given in |changes|. Each key in the environment is
-// matched against the first element of the pairs. In the event of a match, the
-// value is replaced by the second of the pair, unless the second is empty, in
-// which case the key-value is removed.
-//
-// This Windows version takes and returns a Windows-style environment block
-// which is a concatenated list of null-terminated 16-bit strings. The end is
-// marked by a double-null terminator. The size of the returned string will
-// include the terminators.
-string16 AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes);
-
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-typedef std::string NativeEnvironmentString;
-typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
- EnvironmentMap;
-
-// See general comments for the Windows version above.
-//
-// This Posix version takes and returns a Posix-style environment block, which
-// is a null-terminated list of pointers to null-terminated strings. The
-// returned array will have appended to it the storage for the array itself so
-// there is only one pointer to manage, but this means that you can't copy the
-// array without keeping the original around.
-std::unique_ptr<char*[]> AlterEnvironment(const char* const* env,
- const EnvironmentMap& changes);
-
-#endif
-
-} // namespace base
-
-#endif // BASE_ENVIRONMENT_H_
diff --git a/gn/base/files/file.cc b/gn/base/files/file.cc
deleted file mode 100644
index 98fa3a6597c..00000000000
--- a/gn/base/files/file.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// 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.
-
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "util/build_config.h"
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <errno.h>
-#endif
-
-namespace base {
-
-File::Info::Info() : size(0), is_directory(false), is_symbolic_link(false) {}
-
-File::Info::~Info() = default;
-
-File::File()
- : error_details_(FILE_ERROR_FAILED), created_(false), async_(false) {}
-
-#if !defined(OS_NACL)
-File::File(const FilePath& path, uint32_t flags)
- : error_details_(FILE_OK), created_(false), async_(false) {
- Initialize(path, flags);
-}
-#endif
-
-File::File(PlatformFile platform_file)
- : file_(platform_file),
- error_details_(FILE_OK),
- created_(false),
- async_(false) {
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
- DCHECK_GE(platform_file, -1);
-#endif
-}
-
-File::File(Error error_details)
- : error_details_(error_details), created_(false), async_(false) {}
-
-File::File(File&& other)
- : file_(other.TakePlatformFile()),
- error_details_(other.error_details()),
- created_(other.created()),
- async_(other.async_) {}
-
-File::~File() {
- // Go through the AssertIOAllowed logic.
- Close();
-}
-
-// static
-File File::CreateForAsyncHandle(PlatformFile platform_file) {
- File file(platform_file);
- // It would be nice if we could validate that |platform_file| was opened with
- // FILE_FLAG_OVERLAPPED on Windows but this doesn't appear to be possible.
- file.async_ = true;
- return file;
-}
-
-File& File::operator=(File&& other) {
- Close();
- SetPlatformFile(other.TakePlatformFile());
- error_details_ = other.error_details();
- created_ = other.created();
- async_ = other.async_;
- return *this;
-}
-
-#if !defined(OS_NACL)
-void File::Initialize(const FilePath& path, uint32_t flags) {
- if (path.ReferencesParent()) {
-#if defined(OS_WIN)
- ::SetLastError(ERROR_ACCESS_DENIED);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- errno = EACCES;
-#else
-#error Unsupported platform
-#endif
- error_details_ = FILE_ERROR_ACCESS_DENIED;
- return;
- }
- DoInitialize(path, flags);
-}
-#endif
-
-std::string File::ErrorToString(Error error) {
- switch (error) {
- case FILE_OK:
- return "FILE_OK";
- case FILE_ERROR_FAILED:
- return "FILE_ERROR_FAILED";
- case FILE_ERROR_IN_USE:
- return "FILE_ERROR_IN_USE";
- case FILE_ERROR_EXISTS:
- return "FILE_ERROR_EXISTS";
- case FILE_ERROR_NOT_FOUND:
- return "FILE_ERROR_NOT_FOUND";
- case FILE_ERROR_ACCESS_DENIED:
- return "FILE_ERROR_ACCESS_DENIED";
- case FILE_ERROR_TOO_MANY_OPENED:
- return "FILE_ERROR_TOO_MANY_OPENED";
- case FILE_ERROR_NO_MEMORY:
- return "FILE_ERROR_NO_MEMORY";
- case FILE_ERROR_NO_SPACE:
- return "FILE_ERROR_NO_SPACE";
- case FILE_ERROR_NOT_A_DIRECTORY:
- return "FILE_ERROR_NOT_A_DIRECTORY";
- case FILE_ERROR_INVALID_OPERATION:
- return "FILE_ERROR_INVALID_OPERATION";
- case FILE_ERROR_SECURITY:
- return "FILE_ERROR_SECURITY";
- case FILE_ERROR_ABORT:
- return "FILE_ERROR_ABORT";
- case FILE_ERROR_NOT_A_FILE:
- return "FILE_ERROR_NOT_A_FILE";
- case FILE_ERROR_NOT_EMPTY:
- return "FILE_ERROR_NOT_EMPTY";
- case FILE_ERROR_INVALID_URL:
- return "FILE_ERROR_INVALID_URL";
- case FILE_ERROR_IO:
- return "FILE_ERROR_IO";
- case FILE_ERROR_MAX:
- break;
- }
-
- NOTREACHED();
- return "";
-}
-
-} // namespace base
diff --git a/gn/base/files/file.h b/gn/base/files/file.h
deleted file mode 100644
index 61239adb8e7..00000000000
--- a/gn/base/files/file.h
+++ /dev/null
@@ -1,356 +0,0 @@
-// 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 BASE_FILES_FILE_H_
-#define BASE_FILES_FILE_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/files/platform_file.h"
-#include "base/files/scoped_file.h"
-#include "base/macros.h"
-#include "util/build_config.h"
-#include "util/ticks.h"
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <sys/stat.h>
-#endif
-
-namespace base {
-
-#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
- defined(OS_ANDROID) && __ANDROID_API__ < 21
-typedef struct stat stat_wrapper_t;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-typedef struct stat64 stat_wrapper_t;
-#endif
-
-// Thin wrapper around an OS-level file.
-// Note that this class does not provide any support for asynchronous IO, other
-// than the ability to create asynchronous handles on Windows.
-//
-// Note about const: this class does not attempt to determine if the underlying
-// file system object is affected by a particular method in order to consider
-// that method const or not. Only methods that deal with member variables in an
-// obvious non-modifying way are marked as const. Any method that forward calls
-// to the OS is not considered const, even if there is no apparent change to
-// member variables.
-class File {
- public:
- // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one
- // of the five (possibly combining with other flags) when opening or creating
- // a file.
- // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior
- // will be consistent with O_APPEND on POSIX.
- // FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on
- // creation on POSIX; for existing files, consider using Lock().
- enum Flags {
- FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
- FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not
- // already exist.
- FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file.
- FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
- FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it
- // exists.
- FLAG_READ = 1 << 5,
- FLAG_WRITE = 1 << 6,
- FLAG_APPEND = 1 << 7,
- FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE.
- FLAG_EXCLUSIVE_WRITE = 1 << 9,
- FLAG_ASYNC = 1 << 10,
- FLAG_TEMPORARY = 1 << 11, // Used on Windows only.
- FLAG_HIDDEN = 1 << 12, // Used on Windows only.
- FLAG_DELETE_ON_CLOSE = 1 << 13,
- FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only.
- FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only.
- FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags.
- FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only.
- FLAG_EXECUTE = 1 << 18, // Used on Windows only.
- FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only.
- FLAG_CAN_DELETE_ON_CLOSE = 1 << 20, // Requests permission to delete a file
- // via DeleteOnClose() (Windows only).
- // See DeleteOnClose() for details.
- };
-
- // This enum has been recorded in multiple histograms using PlatformFileError
- // enum. If the order of the fields needs to change, please ensure that those
- // histograms are obsolete or have been moved to a different enum.
- //
- // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a
- // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser
- // policy doesn't allow the operation to be executed.
- enum Error {
- FILE_OK = 0,
- FILE_ERROR_FAILED = -1,
- FILE_ERROR_IN_USE = -2,
- FILE_ERROR_EXISTS = -3,
- FILE_ERROR_NOT_FOUND = -4,
- FILE_ERROR_ACCESS_DENIED = -5,
- FILE_ERROR_TOO_MANY_OPENED = -6,
- FILE_ERROR_NO_MEMORY = -7,
- FILE_ERROR_NO_SPACE = -8,
- FILE_ERROR_NOT_A_DIRECTORY = -9,
- FILE_ERROR_INVALID_OPERATION = -10,
- FILE_ERROR_SECURITY = -11,
- FILE_ERROR_ABORT = -12,
- FILE_ERROR_NOT_A_FILE = -13,
- FILE_ERROR_NOT_EMPTY = -14,
- FILE_ERROR_INVALID_URL = -15,
- FILE_ERROR_IO = -16,
- // Put new entries here and increment FILE_ERROR_MAX.
- FILE_ERROR_MAX = -17
- };
-
- // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux.
- enum Whence { FROM_BEGIN = 0, FROM_CURRENT = 1, FROM_END = 2 };
-
- // Used to hold information about a given file.
- // If you add more fields to this structure (platform-specific fields are OK),
- // make sure to update all functions that use it in file_util_{win|posix}.cc,
- // too, and the ParamTraits<base::File::Info> implementation in
- // ipc/ipc_message_utils.cc.
- struct Info {
- Info();
- ~Info();
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
- // Fills this struct with values from |stat_info|.
- void FromStat(const stat_wrapper_t& stat_info);
-#endif
-
- // The size of the file in bytes. Undefined when is_directory is true.
- int64_t size;
-
- // True if the file corresponds to a directory.
- bool is_directory;
-
- // True if the file corresponds to a symbolic link. For Windows currently
- // not supported and thus always false.
- bool is_symbolic_link;
-
- // The last modified time of a file.
- Ticks last_modified;
-
- // The last accessed time of a file.
- Ticks last_accessed;
-
- // The creation time of a file.
- Ticks creation_time;
- };
-
- File();
-
- // Creates or opens the given file. This will fail with 'access denied' if the
- // |path| contains path traversal ('..') components.
- File(const FilePath& path, uint32_t flags);
-
- // Takes ownership of |platform_file|.
- explicit File(PlatformFile platform_file);
-
- // Creates an object with a specific error_details code.
- explicit File(Error error_details);
-
- File(File&& other);
-
- ~File();
-
- // Takes ownership of |platform_file|.
- static File CreateForAsyncHandle(PlatformFile platform_file);
-
- File& operator=(File&& other);
-
- // Creates or opens the given file.
- void Initialize(const FilePath& path, uint32_t flags);
-
- // Returns |true| if the handle / fd wrapped by this object is valid. This
- // method doesn't interact with the file system (and is safe to be called from
- // ThreadRestrictions::SetIOAllowed(false) threads).
- bool IsValid() const;
-
- // Returns true if a new file was created (or an old one truncated to zero
- // length to simulate a new file, which can happen with
- // FLAG_CREATE_ALWAYS), and false otherwise.
- bool created() const { return created_; }
-
- // Returns the OS result of opening this file. Note that the way to verify
- // the success of the operation is to use IsValid(), not this method:
- // File file(path, flags);
- // if (!file.IsValid())
- // return;
- Error error_details() const { return error_details_; }
-
- PlatformFile GetPlatformFile() const;
- PlatformFile TakePlatformFile();
-
- // Destroying this object closes the file automatically.
- void Close();
-
- // Changes current position in the file to an |offset| relative to an origin
- // defined by |whence|. Returns the resultant current position in the file
- // (relative to the start) or -1 in case of error.
- int64_t Seek(Whence whence, int64_t offset);
-
- // Reads the given number of bytes (or until EOF is reached) starting with the
- // given offset. Returns the number of bytes read, or -1 on error. Note that
- // this function makes a best effort to read all data on all platforms, so it
- // is not intended for stream oriented files but instead for cases when the
- // normal expectation is that actually |size| bytes are read unless there is
- // an error.
- int Read(int64_t offset, char* data, int size);
-
- // Same as above but without seek.
- int ReadAtCurrentPos(char* data, int size);
-
- // Reads the given number of bytes (or until EOF is reached) starting with the
- // given offset, but does not make any effort to read all data on all
- // platforms. Returns the number of bytes read, or -1 on error.
- int ReadNoBestEffort(int64_t offset, char* data, int size);
-
- // Same as above but without seek.
- int ReadAtCurrentPosNoBestEffort(char* data, int size);
-
- // Writes the given buffer into the file at the given offset, overwritting any
- // data that was previously there. Returns the number of bytes written, or -1
- // on error. Note that this function makes a best effort to write all data on
- // all platforms. |data| can be nullptr when |size| is 0.
- // Ignores the offset and writes to the end of the file if the file was opened
- // with FLAG_APPEND.
- int Write(int64_t offset, const char* data, int size);
-
- // Save as above but without seek.
- int WriteAtCurrentPos(const char* data, int size);
-
- // Save as above but does not make any effort to write all data on all
- // platforms. Returns the number of bytes written, or -1 on error.
- int WriteAtCurrentPosNoBestEffort(const char* data, int size);
-
- // Returns the current size of this file, or a negative number on failure.
- int64_t GetLength();
-
- // Truncates the file to the given length. If |length| is greater than the
- // current size of the file, the file is extended with zeros. If the file
- // doesn't exist, |false| is returned.
- bool SetLength(int64_t length);
-
- // Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows:
- // FlushFileBuffers).
- // Calling Flush() does not guarantee file integrity and thus is not a valid
- // substitute for file integrity checks and recovery codepaths for malformed
- // files. It can also be *really* slow, so avoid blocking on Flush(),
- // especially please don't block shutdown on Flush().
- // Latency percentiles of Flush() across all platforms as of July 2016:
- // 50 % > 5 ms
- // 10 % > 58 ms
- // 1 % > 357 ms
- // 0.1 % > 1.8 seconds
- // 0.01 % > 7.6 seconds
- bool Flush();
-
- // Returns some basic information for the given file.
- bool GetInfo(Info* info);
-
-#if !defined(OS_FUCHSIA) // Fuchsia's POSIX API does not support file locking.
-
- // Attempts to take an exclusive write lock on the file. Returns immediately
- // (i.e. does not wait for another process to unlock the file). If the lock
- // was obtained, the result will be FILE_OK. A lock only guarantees
- // that other processes may not also take a lock on the same file with the
- // same API - it may still be opened, renamed, unlinked, etc.
- //
- // Common semantics:
- // * Locks are held by processes, but not inherited by child processes.
- // * Locks are released by the OS on file close or process termination.
- // * Locks are reliable only on local filesystems.
- // * Duplicated file handles may also write to locked files.
- // Windows-specific semantics:
- // * Locks are mandatory for read/write APIs, advisory for mapping APIs.
- // * Within a process, locking the same file (by the same or new handle)
- // will fail.
- // POSIX-specific semantics:
- // * Locks are advisory only.
- // * Within a process, locking the same file (by the same or new handle)
- // will succeed.
- // * Closing any descriptor on a given file releases the lock.
- Error Lock();
-
- // Unlock a file previously locked.
- Error Unlock();
-
-#endif // !defined(OS_FUCHSIA)
-
- // Returns a new object referencing this file for use within the current
- // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File
- // object that was created or initialized with this flag will have unlinked
- // the underlying file when it was created or opened. On Windows, the
- // underlying file is deleted when the last handle to it is closed.
- File Duplicate() const;
-
- bool async() const { return async_; }
-
-#if defined(OS_WIN)
- // Sets or clears the DeleteFile disposition on the handle. Returns true if
- // the disposition was set or cleared, as indicated by |delete_on_close|.
- //
- // Microsoft Windows deletes a file only when the last handle to the
- // underlying kernel object is closed when the DeleteFile disposition has been
- // set by any handle holder. This disposition is be set by:
- // - Calling the Win32 DeleteFile function with the path to a file.
- // - Opening/creating a file with FLAG_DELETE_ON_CLOSE.
- // - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently
- // calling DeleteOnClose(true).
- //
- // In all cases, all pre-existing handles to the file must have been opened
- // with FLAG_SHARE_DELETE.
- //
- // So:
- // - Use FLAG_SHARE_DELETE when creating/opening a file to allow another
- // entity on the system to cause it to be deleted when it is closed. (Note:
- // another entity can delete the file the moment after it is closed, so not
- // using this permission doesn't provide any protections.)
- // - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use.
- // The OS will ensure it is deleted even in the face of process termination.
- // - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter
- // the DeleteFile disposition on an open handle. This fine-grained control
- // allows for marking a file for deletion during processing so that it is
- // deleted in the event of untimely process termination, and then clearing
- // this state once the file is suitable for persistence.
- bool DeleteOnClose(bool delete_on_close);
-#endif
-
-#if defined(OS_WIN)
- static Error OSErrorToFileError(DWORD last_error);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- static Error OSErrorToFileError(int saved_errno);
-#endif
-
- // Gets the last global error (errno or GetLastError()) and converts it to the
- // closest base::File::Error equivalent via OSErrorToFileError(). The returned
- // value is only trustworthy immediately after another base::File method
- // fails. base::File never resets the global error to zero.
- static Error GetLastFileError();
-
- // Converts an error value to a human-readable form. Used for logging.
- static std::string ErrorToString(Error error);
-
- private:
- // Creates or opens the given file. Only called if |path| has no
- // traversal ('..') components.
- void DoInitialize(const FilePath& path, uint32_t flags);
-
- void SetPlatformFile(PlatformFile file);
-
- ScopedPlatformFile file_;
-
- Error error_details_;
- bool created_;
- bool async_;
-
- DISALLOW_COPY_AND_ASSIGN(File);
-};
-
-} // namespace base
-
-#endif // BASE_FILES_FILE_H_
diff --git a/gn/base/files/file_enumerator_win.cc b/gn/base/files/file_enumerator_win.cc
deleted file mode 100644
index 803b7bd4437..00000000000
--- a/gn/base/files/file_enumerator_win.cc
+++ /dev/null
@@ -1,192 +0,0 @@
-// 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 "base/files/file_enumerator.h"
-
-#include <shlwapi.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "base/logging.h"
-
-namespace base {
-
-namespace {
-
-FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
- const FilePath& root_path,
- const FilePath::StringType& pattern) {
- // MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy
- // collects all files and filters them manually.
- switch (policy) {
- case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
- return root_path.Append(pattern);
- case FileEnumerator::FolderSearchPolicy::ALL:
- return root_path.Append(L"*");
- }
- NOTREACHED();
- return {};
-}
-
-} // namespace
-
-// FileEnumerator::FileInfo ----------------------------------------------------
-
-FileEnumerator::FileInfo::FileInfo() {
- memset(&find_data_, 0, sizeof(find_data_));
-}
-
-bool FileEnumerator::FileInfo::IsDirectory() const {
- return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-}
-
-FilePath FileEnumerator::FileInfo::GetName() const {
- return FilePath(find_data_.cFileName);
-}
-
-int64_t FileEnumerator::FileInfo::GetSize() const {
- ULARGE_INTEGER size;
- size.HighPart = find_data_.nFileSizeHigh;
- size.LowPart = find_data_.nFileSizeLow;
- DCHECK_LE(size.QuadPart,
- static_cast<ULONGLONG>(std::numeric_limits<int64_t>::max()));
- return static_cast<int64_t>(size.QuadPart);
-}
-
-Ticks FileEnumerator::FileInfo::GetLastModifiedTime() const {
- return *reinterpret_cast<const uint64_t*>(&find_data_.ftLastWriteTime);
-}
-
-// FileEnumerator --------------------------------------------------------------
-
-FileEnumerator::FileEnumerator(const FilePath& root_path,
- bool recursive,
- int file_type)
- : FileEnumerator(root_path,
- recursive,
- file_type,
- FilePath::StringType(),
- FolderSearchPolicy::MATCH_ONLY) {}
-
-FileEnumerator::FileEnumerator(const FilePath& root_path,
- bool recursive,
- int file_type,
- const FilePath::StringType& pattern)
- : FileEnumerator(root_path,
- recursive,
- file_type,
- pattern,
- FolderSearchPolicy::MATCH_ONLY) {}
-
-FileEnumerator::FileEnumerator(const FilePath& root_path,
- bool recursive,
- int file_type,
- const FilePath::StringType& pattern,
- FolderSearchPolicy folder_search_policy)
- : recursive_(recursive),
- file_type_(file_type),
- pattern_(!pattern.empty() ? pattern : L"*"),
- folder_search_policy_(folder_search_policy) {
- // INCLUDE_DOT_DOT must not be specified if recursive.
- DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
- memset(&find_data_, 0, sizeof(find_data_));
- pending_paths_.push(root_path);
-}
-
-FileEnumerator::~FileEnumerator() {
- if (find_handle_ != INVALID_HANDLE_VALUE)
- FindClose(find_handle_);
-}
-
-FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
- if (!has_find_data_) {
- NOTREACHED();
- return FileInfo();
- }
- FileInfo ret;
- memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
- return ret;
-}
-
-FilePath FileEnumerator::Next() {
- while (has_find_data_ || !pending_paths_.empty()) {
- if (!has_find_data_) {
- // The last find FindFirstFile operation is done, prepare a new one.
- root_path_ = pending_paths_.top();
- pending_paths_.pop();
-
- // Start a new find operation.
- const FilePath src =
- BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
- find_handle_ = FindFirstFileEx(src.value().c_str(),
- FindExInfoBasic, // Omit short name.
- &find_data_, FindExSearchNameMatch,
- nullptr, FIND_FIRST_EX_LARGE_FETCH);
- has_find_data_ = true;
- } else {
- // Search for the next file/directory.
- if (!FindNextFile(find_handle_, &find_data_)) {
- FindClose(find_handle_);
- find_handle_ = INVALID_HANDLE_VALUE;
- }
- }
-
- if (INVALID_HANDLE_VALUE == find_handle_) {
- has_find_data_ = false;
-
- // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
- // applies pattern for all subfolders.
- if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
- // This is reached when we have finished a directory and are advancing
- // to the next one in the queue. We applied the pattern (if any) to the
- // files in the root search directory, but for those directories which
- // were matched, we want to enumerate all files inside them. This will
- // happen when the handle is empty.
- pattern_ = L"*";
- }
-
- continue;
- }
-
- const FilePath filename(find_data_.cFileName);
- if (ShouldSkip(filename))
- continue;
-
- const bool is_dir =
- (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- const FilePath abs_path = root_path_.Append(filename);
-
- // Check if directory should be processed recursive.
- if (is_dir && recursive_) {
- // If |cur_file| is a directory, and we are doing recursive searching,
- // add it to pending_paths_ so we scan it after we finish scanning this
- // directory. However, don't do recursion through reparse points or we
- // may end up with an infinite cycle.
- DWORD attributes = GetFileAttributes(abs_path.value().c_str());
- if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
- pending_paths_.push(abs_path);
- }
-
- if (IsTypeMatched(is_dir) && IsPatternMatched(filename))
- return abs_path;
- }
- return FilePath();
-}
-
-bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
- switch (folder_search_policy_) {
- case FolderSearchPolicy::MATCH_ONLY:
- // MATCH_ONLY policy filters by pattern on search request, so all found
- // files already fits to pattern.
- return true;
- case FolderSearchPolicy::ALL:
- // ALL policy enumerates all files, we need to check pattern match
- // manually.
- return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE;
- }
- NOTREACHED();
- return false;
-}
-
-} // namespace base
diff --git a/gn/base/files/file_path.cc b/gn/base/files/file_path.cc
deleted file mode 100644
index 014bc9e0cf8..00000000000
--- a/gn/base/files/file_path.cc
+++ /dev/null
@@ -1,669 +0,0 @@
-// 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 "base/files/file_path.h"
-
-#include <string.h>
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-#if defined(OS_MACOSX)
-#include "base/mac/scoped_cftyperef.h"
-#include "base/third_party/icu/icu_utf.h"
-#endif
-
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_MACOSX)
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-namespace base {
-
-using StringType = FilePath::StringType;
-using StringPieceType = FilePath::StringPieceType;
-
-namespace {
-
-const char* const kCommonDoubleExtensionSuffixes[] = {"gz", "z", "bz2", "bz"};
-const char* const kCommonDoubleExtensions[] = {"user.js"};
-
-const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
-
-// If this FilePath contains a drive letter specification, returns the
-// position of the last character of the drive letter specification,
-// otherwise returns npos. This can only be true on Windows, when a pathname
-// begins with a letter followed by a colon. On other platforms, this always
-// returns npos.
-StringPieceType::size_type FindDriveLetter(StringPieceType path) {
-#if defined(FILE_PATH_USES_DRIVE_LETTERS)
- // This is dependent on an ASCII-based character set, but that's a
- // reasonable assumption. iswalpha can be too inclusive here.
- if (path.length() >= 2 && path[1] == L':' &&
- ((path[0] >= L'A' && path[0] <= L'Z') ||
- (path[0] >= L'a' && path[0] <= L'z'))) {
- return 1;
- }
-#endif // FILE_PATH_USES_DRIVE_LETTERS
- return StringType::npos;
-}
-
-#if defined(FILE_PATH_USES_DRIVE_LETTERS)
-bool EqualDriveLetterCaseInsensitive(StringPieceType a, StringPieceType b) {
- size_t a_letter_pos = FindDriveLetter(a);
- size_t b_letter_pos = FindDriveLetter(b);
-
- if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos)
- return a == b;
-
- StringPieceType a_letter(a.substr(0, a_letter_pos + 1));
- StringPieceType b_letter(b.substr(0, b_letter_pos + 1));
- if (!StartsWith(a_letter, b_letter, CompareCase::INSENSITIVE_ASCII))
- return false;
-
- StringPieceType a_rest(a.substr(a_letter_pos + 1));
- StringPieceType b_rest(b.substr(b_letter_pos + 1));
- return a_rest == b_rest;
-}
-#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
-
-bool IsPathAbsolute(StringPieceType path) {
-#if defined(FILE_PATH_USES_DRIVE_LETTERS)
- StringType::size_type letter = FindDriveLetter(path);
- if (letter != StringType::npos) {
- // Look for a separator right after the drive specification.
- return path.length() > letter + 1 &&
- FilePath::IsSeparator(path[letter + 1]);
- }
- // Look for a pair of leading separators.
- return path.length() > 1 && FilePath::IsSeparator(path[0]) &&
- FilePath::IsSeparator(path[1]);
-#else // FILE_PATH_USES_DRIVE_LETTERS
- // Look for a separator in the first position.
- return path.length() > 0 && FilePath::IsSeparator(path[0]);
-#endif // FILE_PATH_USES_DRIVE_LETTERS
-}
-
-bool AreAllSeparators(const StringType& input) {
- for (StringType::const_iterator it = input.begin(); it != input.end(); ++it) {
- if (!FilePath::IsSeparator(*it))
- return false;
- }
-
- return true;
-}
-
-// Find the position of the '.' that separates the extension from the rest
-// of the file name. The position is relative to BaseName(), not value().
-// Returns npos if it can't find an extension.
-StringType::size_type FinalExtensionSeparatorPosition(const StringType& path) {
- // Special case "." and ".."
- if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory)
- return StringType::npos;
-
- return path.rfind(FilePath::kExtensionSeparator);
-}
-
-// Same as above, but allow a second extension component of up to 4
-// characters when the rightmost extension component is a common double
-// extension (gz, bz2, Z). For example, foo.tar.gz or foo.tar.Z would have
-// extension components of '.tar.gz' and '.tar.Z' respectively.
-StringType::size_type ExtensionSeparatorPosition(const StringType& path) {
- const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path);
-
- // No extension, or the extension is the whole filename.
- if (last_dot == StringType::npos || last_dot == 0U)
- return last_dot;
-
- const StringType::size_type penultimate_dot =
- path.rfind(FilePath::kExtensionSeparator, last_dot - 1);
- const StringType::size_type last_separator = path.find_last_of(
- FilePath::kSeparators, last_dot - 1, FilePath::kSeparatorsLength - 1);
-
- if (penultimate_dot == StringType::npos ||
- (last_separator != StringType::npos &&
- penultimate_dot < last_separator)) {
- return last_dot;
- }
-
- for (size_t i = 0; i < arraysize(kCommonDoubleExtensions); ++i) {
- StringType extension(path, penultimate_dot + 1);
- if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i]))
- return penultimate_dot;
- }
-
- StringType extension(path, last_dot + 1);
- for (size_t i = 0; i < arraysize(kCommonDoubleExtensionSuffixes); ++i) {
- if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) {
- if ((last_dot - penultimate_dot) <= 5U &&
- (last_dot - penultimate_dot) > 1U) {
- return penultimate_dot;
- }
- }
- }
-
- return last_dot;
-}
-
-// Returns true if path is "", ".", or "..".
-bool IsEmptyOrSpecialCase(const StringType& path) {
- // Special cases "", ".", and ".."
- if (path.empty() || path == FilePath::kCurrentDirectory ||
- path == FilePath::kParentDirectory) {
- return true;
- }
-
- return false;
-}
-
-} // namespace
-
-FilePath::FilePath() = default;
-
-FilePath::FilePath(const FilePath& that) = default;
-FilePath::FilePath(FilePath&& that) noexcept = default;
-
-FilePath::FilePath(StringPieceType path) {
- path.CopyToString(&path_);
- StringType::size_type nul_pos = path_.find(kStringTerminator);
- if (nul_pos != StringType::npos)
- path_.erase(nul_pos, StringType::npos);
-}
-
-FilePath::~FilePath() = default;
-
-FilePath& FilePath::operator=(const FilePath& that) = default;
-
-FilePath& FilePath::operator=(FilePath&& that) = default;
-
-bool FilePath::operator==(const FilePath& that) const {
-#if defined(FILE_PATH_USES_DRIVE_LETTERS)
- return EqualDriveLetterCaseInsensitive(this->path_, that.path_);
-#else // defined(FILE_PATH_USES_DRIVE_LETTERS)
- return path_ == that.path_;
-#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
-}
-
-bool FilePath::operator!=(const FilePath& that) const {
-#if defined(FILE_PATH_USES_DRIVE_LETTERS)
- return !EqualDriveLetterCaseInsensitive(this->path_, that.path_);
-#else // defined(FILE_PATH_USES_DRIVE_LETTERS)
- return path_ != that.path_;
-#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
-}
-
-std::ostream& operator<<(std::ostream& out, const FilePath& file_path) {
- return out << file_path.value();
-}
-
-// static
-bool FilePath::IsSeparator(CharType character) {
- for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
- if (character == kSeparators[i]) {
- return true;
- }
- }
-
- return false;
-}
-
-void FilePath::GetComponents(std::vector<StringType>* components) const {
- DCHECK(components);
- if (!components)
- return;
- components->clear();
- if (value().empty())
- return;
-
- std::vector<StringType> ret_val;
- FilePath current = *this;
- FilePath base;
-
- // Capture path components.
- while (current != current.DirName()) {
- base = current.BaseName();
- if (!AreAllSeparators(base.value()))
- ret_val.push_back(base.value());
- current = current.DirName();
- }
-
- // Capture root, if any.
- base = current.BaseName();
- if (!base.value().empty() && base.value() != kCurrentDirectory)
- ret_val.push_back(current.BaseName().value());
-
- // Capture drive letter, if any.
- FilePath dir = current.DirName();
- StringType::size_type letter = FindDriveLetter(dir.value());
- if (letter != StringType::npos) {
- ret_val.push_back(StringType(dir.value(), 0, letter + 1));
- }
-
- *components = std::vector<StringType>(ret_val.rbegin(), ret_val.rend());
-}
-
-bool FilePath::IsParent(const FilePath& child) const {
- return AppendRelativePath(child, nullptr);
-}
-
-bool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const {
- std::vector<StringType> parent_components;
- std::vector<StringType> child_components;
- GetComponents(&parent_components);
- child.GetComponents(&child_components);
-
- if (parent_components.empty() ||
- parent_components.size() >= child_components.size())
- return false;
-
- std::vector<StringType>::const_iterator parent_comp =
- parent_components.begin();
- std::vector<StringType>::const_iterator child_comp = child_components.begin();
-
-#if defined(FILE_PATH_USES_DRIVE_LETTERS)
- // Windows can access case sensitive filesystems, so component
- // comparisions must be case sensitive, but drive letters are
- // never case sensitive.
- if ((FindDriveLetter(*parent_comp) != StringType::npos) &&
- (FindDriveLetter(*child_comp) != StringType::npos)) {
- if (!StartsWith(*parent_comp, *child_comp, CompareCase::INSENSITIVE_ASCII))
- return false;
- ++parent_comp;
- ++child_comp;
- }
-#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
-
- while (parent_comp != parent_components.end()) {
- if (*parent_comp != *child_comp)
- return false;
- ++parent_comp;
- ++child_comp;
- }
-
- if (path != nullptr) {
- for (; child_comp != child_components.end(); ++child_comp) {
- *path = path->Append(*child_comp);
- }
- }
- return true;
-}
-
-// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
-// guaranteed to not modify their input strings, and in fact are implemented
-// differently in this regard on different platforms. Don't use them, but
-// adhere to their behavior.
-FilePath FilePath::DirName() const {
- FilePath new_path(path_);
- new_path.StripTrailingSeparatorsInternal();
-
- // The drive letter, if any, always needs to remain in the output. If there
- // is no drive letter, as will always be the case on platforms which do not
- // support drive letters, letter will be npos, or -1, so the comparisons and
- // resizes below using letter will still be valid.
- StringType::size_type letter = FindDriveLetter(new_path.path_);
-
- StringType::size_type last_separator = new_path.path_.find_last_of(
- kSeparators, StringType::npos, kSeparatorsLength - 1);
- if (last_separator == StringType::npos) {
- // path_ is in the current directory.
- new_path.path_.resize(letter + 1);
- } else if (last_separator == letter + 1) {
- // path_ is in the root directory.
- new_path.path_.resize(letter + 2);
- } else if (last_separator == letter + 2 &&
- IsSeparator(new_path.path_[letter + 1])) {
- // path_ is in "//" (possibly with a drive letter); leave the double
- // separator intact indicating alternate root.
- new_path.path_.resize(letter + 3);
- } else if (last_separator != 0) {
- // path_ is somewhere else, trim the basename.
- new_path.path_.resize(last_separator);
- }
-
- new_path.StripTrailingSeparatorsInternal();
- if (!new_path.path_.length())
- new_path.path_ = kCurrentDirectory;
-
- return new_path;
-}
-
-FilePath FilePath::BaseName() const {
- FilePath new_path(path_);
- new_path.StripTrailingSeparatorsInternal();
-
- // The drive letter, if any, is always stripped.
- StringType::size_type letter = FindDriveLetter(new_path.path_);
- if (letter != StringType::npos) {
- new_path.path_.erase(0, letter + 1);
- }
-
- // Keep everything after the final separator, but if the pathname is only
- // one character and it's a separator, leave it alone.
- StringType::size_type last_separator = new_path.path_.find_last_of(
- kSeparators, StringType::npos, kSeparatorsLength - 1);
- if (last_separator != StringType::npos &&
- last_separator < new_path.path_.length() - 1) {
- new_path.path_.erase(0, last_separator + 1);
- }
-
- return new_path;
-}
-
-StringType FilePath::Extension() const {
- FilePath base(BaseName());
- const StringType::size_type dot = ExtensionSeparatorPosition(base.path_);
- if (dot == StringType::npos)
- return StringType();
-
- return base.path_.substr(dot, StringType::npos);
-}
-
-StringType FilePath::FinalExtension() const {
- FilePath base(BaseName());
- const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_);
- if (dot == StringType::npos)
- return StringType();
-
- return base.path_.substr(dot, StringType::npos);
-}
-
-FilePath FilePath::RemoveExtension() const {
- if (Extension().empty())
- return *this;
-
- const StringType::size_type dot = ExtensionSeparatorPosition(path_);
- if (dot == StringType::npos)
- return *this;
-
- return FilePath(path_.substr(0, dot));
-}
-
-FilePath FilePath::RemoveFinalExtension() const {
- if (FinalExtension().empty())
- return *this;
-
- const StringType::size_type dot = FinalExtensionSeparatorPosition(path_);
- if (dot == StringType::npos)
- return *this;
-
- return FilePath(path_.substr(0, dot));
-}
-
-FilePath FilePath::InsertBeforeExtension(StringPieceType suffix) const {
- if (suffix.empty())
- return FilePath(path_);
-
- if (IsEmptyOrSpecialCase(BaseName().value()))
- return FilePath();
-
- StringType ext = Extension();
- StringType ret = RemoveExtension().value();
- suffix.AppendToString(&ret);
- ret.append(ext);
- return FilePath(ret);
-}
-
-FilePath FilePath::InsertBeforeExtensionASCII(StringPiece suffix) const {
- DCHECK(IsStringASCII(suffix));
-#if defined(OS_WIN)
- return InsertBeforeExtension(ASCIIToUTF16(suffix));
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- return InsertBeforeExtension(suffix);
-#endif
-}
-
-FilePath FilePath::AddExtension(StringPieceType extension) const {
- if (IsEmptyOrSpecialCase(BaseName().value()))
- return FilePath();
-
- // If the new extension is "" or ".", then just return the current FilePath.
- if (extension.empty() ||
- (extension.size() == 1 && extension[0] == kExtensionSeparator))
- return *this;
-
- StringType str = path_;
- if (extension[0] != kExtensionSeparator &&
- *(str.end() - 1) != kExtensionSeparator) {
- str.append(1, kExtensionSeparator);
- }
- extension.AppendToString(&str);
- return FilePath(str);
-}
-
-FilePath FilePath::ReplaceExtension(StringPieceType extension) const {
- if (IsEmptyOrSpecialCase(BaseName().value()))
- return FilePath();
-
- FilePath no_ext = RemoveExtension();
- // If the new extension is "" or ".", then just remove the current extension.
- if (extension.empty() ||
- (extension.size() == 1 && extension[0] == kExtensionSeparator))
- return no_ext;
-
- StringType str = no_ext.value();
- if (extension[0] != kExtensionSeparator)
- str.append(1, kExtensionSeparator);
- extension.AppendToString(&str);
- return FilePath(str);
-}
-
-FilePath FilePath::Append(StringPieceType component) const {
- StringPieceType appended = component;
- StringType without_nuls;
-
- StringType::size_type nul_pos = component.find(kStringTerminator);
- if (nul_pos != StringPieceType::npos) {
- component.substr(0, nul_pos).CopyToString(&without_nuls);
- appended = StringPieceType(without_nuls);
- }
-
- DCHECK(!IsPathAbsolute(appended));
-
- if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
- // Append normally doesn't do any normalization, but as a special case,
- // when appending to kCurrentDirectory, just return a new path for the
- // component argument. Appending component to kCurrentDirectory would
- // serve no purpose other than needlessly lengthening the path, and
- // it's likely in practice to wind up with FilePath objects containing
- // only kCurrentDirectory when calling DirName on a single relative path
- // component.
- return FilePath(appended);
- }
-
- FilePath new_path(path_);
- new_path.StripTrailingSeparatorsInternal();
-
- // Don't append a separator if the path is empty (indicating the current
- // directory) or if the path component is empty (indicating nothing to
- // append).
- if (!appended.empty() && !new_path.path_.empty()) {
- // Don't append a separator if the path still ends with a trailing
- // separator after stripping (indicating the root directory).
- if (!IsSeparator(new_path.path_.back())) {
- // Don't append a separator if the path is just a drive letter.
- if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
- new_path.path_.append(1, kSeparators[0]);
- }
- }
- }
-
- appended.AppendToString(&new_path.path_);
- return new_path;
-}
-
-FilePath FilePath::Append(const FilePath& component) const {
- return Append(component.value());
-}
-
-FilePath FilePath::AppendASCII(StringPiece component) const {
- DCHECK(base::IsStringASCII(component));
-#if defined(OS_WIN)
- return Append(ASCIIToUTF16(component));
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- return Append(component);
-#endif
-}
-
-bool FilePath::IsAbsolute() const {
- return IsPathAbsolute(path_);
-}
-
-bool FilePath::EndsWithSeparator() const {
- if (empty())
- return false;
- return IsSeparator(path_.back());
-}
-
-FilePath FilePath::AsEndingWithSeparator() const {
- if (EndsWithSeparator() || path_.empty())
- return *this;
-
- StringType path_str;
- path_str.reserve(path_.length() + 1); // Only allocate string once.
-
- path_str = path_;
- path_str.append(&kSeparators[0], 1);
- return FilePath(path_str);
-}
-
-FilePath FilePath::StripTrailingSeparators() const {
- FilePath new_path(path_);
- new_path.StripTrailingSeparatorsInternal();
-
- return new_path;
-}
-
-bool FilePath::ReferencesParent() const {
- if (path_.find(kParentDirectory) == StringType::npos) {
- // GetComponents is quite expensive, so avoid calling it in the majority
- // of cases where there isn't a kParentDirectory anywhere in the path.
- return false;
- }
-
- std::vector<StringType> components;
- GetComponents(&components);
-
- std::vector<StringType>::const_iterator it = components.begin();
- for (; it != components.end(); ++it) {
- const StringType& component = *it;
- // Windows has odd, undocumented behavior with path components containing
- // only whitespace and . characters. So, if all we see is . and
- // whitespace, then we treat any .. sequence as referencing parent.
- // For simplicity we enforce this on all platforms.
- if (component.find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) ==
- std::string::npos &&
- component.find(kParentDirectory) != std::string::npos) {
- return true;
- }
- }
- return false;
-}
-
-#if defined(OS_WIN)
-
-string16 FilePath::LossyDisplayName() const {
- return path_;
-}
-
-std::string FilePath::MaybeAsASCII() const {
- if (base::IsStringASCII(path_))
- return UTF16ToASCII(path_);
- return std::string();
-}
-
-std::string FilePath::AsUTF8Unsafe() const {
- return WideToUTF8(value());
-}
-
-string16 FilePath::AsUTF16Unsafe() const {
- return value();
-}
-
-// static
-FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) {
- return FilePath(UTF8ToWide(utf8));
-}
-
-// static
-FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) {
- return FilePath(utf16);
-}
-
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-// See file_path.h for a discussion of the encoding of paths on POSIX
-// platforms. These encoding conversion functions are not quite correct.
-
-std::string FilePath::MaybeAsASCII() const {
- if (base::IsStringASCII(path_))
- return path_;
- return std::string();
-}
-
-std::string FilePath::AsUTF8Unsafe() const {
- return value();
-}
-
-string16 FilePath::AsUTF16Unsafe() const {
- return UTF8ToUTF16(value());
-}
-
-// static
-FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) {
- return FilePath(utf8);
-}
-
-// static
-FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) {
- return FilePath(UTF16ToUTF8(utf16));
-}
-
-#endif // defined(OS_WIN)
-
-void FilePath::StripTrailingSeparatorsInternal() {
- // If there is no drive letter, start will be 1, which will prevent stripping
- // the leading separator if there is only one separator. If there is a drive
- // letter, start will be set appropriately to prevent stripping the first
- // separator following the drive letter, if a separator immediately follows
- // the drive letter.
- StringType::size_type start = FindDriveLetter(path_) + 2;
-
- StringType::size_type last_stripped = StringType::npos;
- for (StringType::size_type pos = path_.length();
- pos > start && IsSeparator(path_[pos - 1]); --pos) {
- // If the string only has two separators and they're at the beginning,
- // don't strip them, unless the string began with more than two separators.
- if (pos != start + 1 || last_stripped == start + 2 ||
- !IsSeparator(path_[start - 1])) {
- path_.resize(pos - 1);
- last_stripped = pos;
- }
- }
-}
-
-FilePath FilePath::NormalizePathSeparators() const {
- return NormalizePathSeparatorsTo(kSeparators[0]);
-}
-
-FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const {
-#if defined(FILE_PATH_USES_WIN_SEPARATORS)
- DCHECK_NE(kSeparators + kSeparatorsLength,
- std::find(kSeparators, kSeparators + kSeparatorsLength, separator));
- StringType copy = path_;
- for (size_t i = 0; i < kSeparatorsLength; ++i) {
- std::replace(copy.begin(), copy.end(), kSeparators[i], separator);
- }
- return FilePath(copy);
-#else
- return *this;
-#endif
-}
-
-} // namespace base
diff --git a/gn/base/files/file_path.h b/gn/base/files/file_path.h
deleted file mode 100644
index 1717cba713f..00000000000
--- a/gn/base/files/file_path.h
+++ /dev/null
@@ -1,426 +0,0 @@
-// 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.
-
-// FilePath is a container for pathnames stored in a platform's native string
-// type, providing containers for manipulation in according with the
-// platform's conventions for pathnames. It supports the following path
-// types:
-//
-// POSIX Windows
-// --------------- ----------------------------------
-// Fundamental type char[] wchar_t[]
-// Encoding unspecified* UTF-16
-// Separator / \, tolerant of /
-// Drive letters no case-insensitive A-Z followed by :
-// Alternate root // (surprise!) \\, for UNC paths
-//
-// * The encoding need not be specified on POSIX systems, although some
-// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8.
-// Chrome OS also uses UTF-8.
-// Linux does not specify an encoding, but in practice, the locale's
-// character set may be used.
-//
-// For more arcane bits of path trivia, see below.
-//
-// FilePath objects are intended to be used anywhere paths are. An
-// application may pass FilePath objects around internally, masking the
-// underlying differences between systems, only differing in implementation
-// where interfacing directly with the system. For example, a single
-// OpenFile(const FilePath &) function may be made available, allowing all
-// callers to operate without regard to the underlying implementation. On
-// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might
-// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This
-// allows each platform to pass pathnames around without requiring conversions
-// between encodings, which has an impact on performance, but more imporantly,
-// has an impact on correctness on platforms that do not have well-defined
-// encodings for pathnames.
-//
-// Several methods are available to perform common operations on a FilePath
-// object, such as determining the parent directory (DirName), isolating the
-// final path component (BaseName), and appending a relative pathname string
-// to an existing FilePath object (Append). These methods are highly
-// recommended over attempting to split and concatenate strings directly.
-// These methods are based purely on string manipulation and knowledge of
-// platform-specific pathname conventions, and do not consult the filesystem
-// at all, making them safe to use without fear of blocking on I/O operations.
-// These methods do not function as mutators but instead return distinct
-// instances of FilePath objects, and are therefore safe to use on const
-// objects. The objects themselves are safe to share between threads.
-//
-// To aid in initialization of FilePath objects from string literals, a
-// FILE_PATH_LITERAL macro is provided, which accounts for the difference
-// between char[]-based pathnames on POSIX systems and wchar_t[]-based
-// pathnames on Windows.
-//
-// As a precaution against premature truncation, paths can't contain NULs.
-//
-// Because a FilePath object should not be instantiated at the global scope,
-// instead, use a FilePath::CharType[] and initialize it with
-// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the
-// character array. Example:
-//
-// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt");
-// |
-// | void Function() {
-// | FilePath log_file_path(kLogFileName);
-// | [...]
-// | }
-//
-// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even
-// when the UI language is RTL. This means you always need to pass filepaths
-// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the
-// RTL UI.
-//
-// This is a very common source of bugs, please try to keep this in mind.
-//
-// ARCANE BITS OF PATH TRIVIA
-//
-// - A double leading slash is actually part of the POSIX standard. Systems
-// are allowed to treat // as an alternate root, as Windows does for UNC
-// (network share) paths. Most POSIX systems don't do anything special
-// with two leading slashes, but FilePath handles this case properly
-// in case it ever comes across such a system. FilePath needs this support
-// for Windows UNC paths, anyway.
-// References:
-// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname")
-// and 4.12 ("Pathname Resolution"), available at:
-// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
-// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12
-//
-// - Windows treats c:\\ the same way it treats \\. This was intended to
-// allow older applications that require drive letters to support UNC paths
-// like \\server\share\path, by permitting c:\\server\share\path as an
-// equivalent. Since the OS treats these paths specially, FilePath needs
-// to do the same. Since Windows can use either / or \ as the separator,
-// FilePath treats c://, c:\\, //, and \\ all equivalently.
-// Reference:
-// The Old New Thing, "Why is a drive letter permitted in front of UNC
-// paths (sometimes)?", available at:
-// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx
-
-#ifndef BASE_FILES_FILE_PATH_H_
-#define BASE_FILES_FILE_PATH_H_
-
-#include <stddef.h>
-
-#include <iosfwd>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "util/build_config.h"
-
-// Windows-style drive letter support and pathname separator characters can be
-// enabled and disabled independently, to aid testing. These #defines are
-// here so that the same setting can be used in both the implementation and
-// in the unit test.
-#if defined(OS_WIN)
-#define FILE_PATH_USES_DRIVE_LETTERS
-#define FILE_PATH_USES_WIN_SEPARATORS
-#endif // OS_WIN
-
-// To print path names portably use PRIsFP (based on PRIuS and friends from
-// C99 and format_macros.h) like this:
-// base::StringPrintf("Path is %" PRIsFP ".\n", path.value().c_str());
-#if defined(OS_WIN)
-#define PRIsFP "ls"
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#define PRIsFP "s"
-#endif // OS_WIN
-
-namespace base {
-
-class Pickle;
-class PickleIterator;
-
-// An abstraction to isolate users from the differences between native
-// pathnames on different platforms.
-class FilePath {
- public:
-#if defined(OS_WIN)
- // On Windows, for Unicode-aware applications, native pathnames are wchar_t
- // arrays encoded in UTF-16.
- typedef std::wstring StringType;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- // On most platforms, native pathnames are char arrays, and the encoding
- // may or may not be specified. On Mac OS X, native pathnames are encoded
- // in UTF-8.
- typedef std::string StringType;
-#endif // OS_WIN
-
- typedef BasicStringPiece<StringType> StringPieceType;
- typedef StringType::value_type CharType;
-
- // Null-terminated array of separators used to separate components in
- // hierarchical paths. Each character in this array is a valid separator,
- // but kSeparators[0] is treated as the canonical separator and will be used
- // when composing pathnames.
- static const CharType kSeparators[];
-
- // arraysize(kSeparators).
- static const size_t kSeparatorsLength;
-
- // A special path component meaning "this directory."
- static const CharType kCurrentDirectory[];
-
- // A special path component meaning "the parent directory."
- static const CharType kParentDirectory[];
-
- // The character used to identify a file extension.
- static const CharType kExtensionSeparator;
-
- FilePath();
- FilePath(const FilePath& that);
- explicit FilePath(StringPieceType path);
- ~FilePath();
- FilePath& operator=(const FilePath& that);
-
- // Constructs FilePath with the contents of |that|, which is left in valid but
- // unspecified state.
- FilePath(FilePath&& that) noexcept;
- // Replaces the contents with those of |that|, which is left in valid but
- // unspecified state.
- FilePath& operator=(FilePath&& that);
-
- bool operator==(const FilePath& that) const;
-
- bool operator!=(const FilePath& that) const;
-
- // Required for some STL containers and operations
- bool operator<(const FilePath& that) const { return path_ < that.path_; }
-
- const StringType& value() const { return path_; }
-
- bool empty() const { return path_.empty(); }
-
- void clear() { path_.clear(); }
-
- // Returns true if |character| is in kSeparators.
- static bool IsSeparator(CharType character);
-
- // Returns a vector of all of the components of the provided path. It is
- // equivalent to calling DirName().value() on the path's root component,
- // and BaseName().value() on each child component.
- //
- // To make sure this is lossless so we can differentiate absolute and
- // relative paths, the root slash will be included even though no other
- // slashes will be. The precise behavior is:
- //
- // Posix: "/foo/bar" -> [ "/", "foo", "bar" ]
- // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ]
- void GetComponents(std::vector<FilePath::StringType>* components) const;
-
- // Returns true if this FilePath is a strict parent of the |child|. Absolute
- // and relative paths are accepted i.e. is /foo parent to /foo/bar and
- // is foo parent to foo/bar. Does not convert paths to absolute, follow
- // symlinks or directory navigation (e.g. ".."). A path is *NOT* its own
- // parent.
- bool IsParent(const FilePath& child) const;
-
- // If IsParent(child) holds, appends to path (if non-NULL) the
- // relative path to child and returns true. For example, if parent
- // holds "/Users/johndoe/Library/Application Support", child holds
- // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and
- // *path holds "/Users/johndoe/Library/Caches", then after
- // parent.AppendRelativePath(child, path) is called *path will hold
- // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise,
- // returns false.
- bool AppendRelativePath(const FilePath& child, FilePath* path) const;
-
- // Returns a FilePath corresponding to the directory containing the path
- // named by this object, stripping away the file component. If this object
- // only contains one component, returns a FilePath identifying
- // kCurrentDirectory. If this object already refers to the root directory,
- // returns a FilePath identifying the root directory. Please note that this
- // doesn't resolve directory navigation, e.g. the result for "../a" is "..".
- FilePath DirName() const WARN_UNUSED_RESULT;
-
- // Returns a FilePath corresponding to the last path component of this
- // object, either a file or a directory. If this object already refers to
- // the root directory, returns a FilePath identifying the root directory;
- // this is the only situation in which BaseName will return an absolute path.
- FilePath BaseName() const WARN_UNUSED_RESULT;
-
- // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
- // the file has no extension. If non-empty, Extension() will always start
- // with precisely one ".". The following code should always work regardless
- // of the value of path. For common double-extensions like .tar.gz and
- // .user.js, this method returns the combined extension. For a single
- // component, use FinalExtension().
- // new_path = path.RemoveExtension().value().append(path.Extension());
- // ASSERT(new_path == path.value());
- // NOTE: this is different from the original file_util implementation which
- // returned the extension without a leading "." ("jpg" instead of ".jpg")
- StringType Extension() const WARN_UNUSED_RESULT;
-
- // Returns the path's file extension, as in Extension(), but will
- // never return a double extension.
- //
- // TODO(davidben): Check all our extension-sensitive code to see if
- // we can rename this to Extension() and the other to something like
- // LongExtension(), defaulting to short extensions and leaving the
- // long "extensions" to logic like base::GetUniquePathNumber().
- StringType FinalExtension() const WARN_UNUSED_RESULT;
-
- // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
- // NOTE: this is slightly different from the similar file_util implementation
- // which returned simply 'jojo'.
- FilePath RemoveExtension() const WARN_UNUSED_RESULT;
-
- // Removes the path's file extension, as in RemoveExtension(), but
- // ignores double extensions.
- FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT;
-
- // Inserts |suffix| after the file name portion of |path| but before the
- // extension. Returns "" if BaseName() == "." or "..".
- // Examples:
- // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
- // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
- // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
- // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
- FilePath InsertBeforeExtension(StringPieceType suffix) const
- WARN_UNUSED_RESULT;
- FilePath InsertBeforeExtensionASCII(StringPiece suffix) const
- WARN_UNUSED_RESULT;
-
- // Adds |extension| to |file_name|. Returns the current FilePath if
- // |extension| is empty. Returns "" if BaseName() == "." or "..".
- FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT;
-
- // Replaces the extension of |file_name| with |extension|. If |file_name|
- // does not have an extension, then |extension| is added. If |extension| is
- // empty, then the extension is removed from |file_name|.
- // Returns "" if BaseName() == "." or "..".
- FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT;
-
- // Returns a FilePath by appending a separator and the supplied path
- // component to this object's path. Append takes care to avoid adding
- // excessive separators if this object's path already ends with a separator.
- // If this object's path is kCurrentDirectory, a new FilePath corresponding
- // only to |component| is returned. |component| must be a relative path;
- // it is an error to pass an absolute path.
- FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT;
- FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT;
-
- // Although Windows StringType is std::wstring, since the encoding it uses for
- // paths is well defined, it can handle ASCII path components as well.
- // Mac uses UTF8, and since ASCII is a subset of that, it works there as well.
- // On Linux, although it can use any 8-bit encoding for paths, we assume that
- // ASCII is a valid subset, regardless of the encoding, since many operating
- // system paths will always be ASCII.
- FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT;
-
- // Returns true if this FilePath contains an absolute path. On Windows, an
- // absolute path begins with either a drive letter specification followed by
- // a separator character, or with two separator characters. On POSIX
- // platforms, an absolute path begins with a separator character.
- bool IsAbsolute() const;
-
- // Returns true if the patch ends with a path separator character.
- bool EndsWithSeparator() const WARN_UNUSED_RESULT;
-
- // Returns a copy of this FilePath that ends with a trailing separator. If
- // the input path is empty, an empty FilePath will be returned.
- FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT;
-
- // Returns a copy of this FilePath that does not end with a trailing
- // separator.
- FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT;
-
- // Returns true if this FilePath contains an attempt to reference a parent
- // directory (e.g. has a path component that is "..").
- bool ReferencesParent() const;
-
- // Return a Unicode human-readable version of this path.
- // Warning: you can *not*, in general, go from a display name back to a real
- // path. Only use this when displaying paths to users, not just when you
- // want to stuff a string16 into some other API.
- string16 LossyDisplayName() const;
-
- // Return the path as ASCII, or the empty string if the path is not ASCII.
- // This should only be used for cases where the FilePath is representing a
- // known-ASCII filename.
- std::string MaybeAsASCII() const;
-
- // Return the path as UTF-8.
- //
- // This function is *unsafe* as there is no way to tell what encoding is
- // used in file names on POSIX systems other than Mac and Chrome OS,
- // although UTF-8 is practically used everywhere these days. To mitigate
- // the encoding issue, this function internally calls
- // SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS,
- // per assumption that the current locale's encoding is used in file
- // names, but this isn't a perfect solution.
- //
- // Once it becomes safe to to stop caring about non-UTF-8 file names,
- // the SysNativeMBToWide() hack will be removed from the code, along
- // with "Unsafe" in the function name.
- std::string AsUTF8Unsafe() const;
-
- // Similar to AsUTF8Unsafe, but returns UTF-16 instead.
- string16 AsUTF16Unsafe() const;
-
- // Returns a FilePath object from a path name in UTF-8. This function
- // should only be used for cases where you are sure that the input
- // string is UTF-8.
- //
- // Like AsUTF8Unsafe(), this function is unsafe. This function
- // internally calls SysWideToNativeMB() on POSIX systems other than Mac
- // and Chrome OS, to mitigate the encoding issue. See the comment at
- // AsUTF8Unsafe() for details.
- static FilePath FromUTF8Unsafe(StringPiece utf8);
-
- // Similar to FromUTF8Unsafe, but accepts UTF-16 instead.
- static FilePath FromUTF16Unsafe(StringPiece16 utf16);
-
- // Normalize all path separators to backslash on Windows
- // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
- FilePath NormalizePathSeparators() const;
-
- // Normalize all path separattors to given type on Windows
- // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
- FilePath NormalizePathSeparatorsTo(CharType separator) const;
-
- private:
- // Remove trailing separators from this object. If the path is absolute, it
- // will never be stripped any more than to refer to the absolute root
- // directory, so "////" will become "/", not "". A leading pair of
- // separators is never stripped, to support alternate roots. This is used to
- // support UNC paths on Windows.
- void StripTrailingSeparatorsInternal();
-
- StringType path_;
-};
-
-std::ostream& operator<<(std::ostream& out, const FilePath& file_path);
-
-} // namespace base
-
-// Macros for string literal initialization of FilePath::CharType[], and for
-// using a FilePath::CharType[] in a printf-style format string.
-#if defined(OS_WIN)
-#define FILE_PATH_LITERAL(x) L##x
-#define PRFilePath "ls"
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#define FILE_PATH_LITERAL(x) x
-#define PRFilePath "s"
-#endif // OS_WIN
-
-namespace std {
-
-template <>
-struct hash<base::FilePath> {
- typedef base::FilePath argument_type;
- typedef std::size_t result_type;
- result_type operator()(argument_type const& f) const {
- return hash<base::FilePath::StringType>()(f.value());
- }
-};
-
-} // namespace std
-
-#endif // BASE_FILES_FILE_PATH_H_
diff --git a/gn/base/files/file_path_constants.cc b/gn/base/files/file_path_constants.cc
deleted file mode 100644
index fc7d6636420..00000000000
--- a/gn/base/files/file_path_constants.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-
-namespace base {
-
-#if defined(FILE_PATH_USES_WIN_SEPARATORS)
-const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
-#else // FILE_PATH_USES_WIN_SEPARATORS
-const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
-#endif // FILE_PATH_USES_WIN_SEPARATORS
-
-const size_t FilePath::kSeparatorsLength = arraysize(kSeparators);
-
-const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
-const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
-
-const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
-
-} // namespace base
diff --git a/gn/base/files/file_posix.cc b/gn/base/files/file_posix.cc
deleted file mode 100644
index ed9a5e2a8e3..00000000000
--- a/gn/base/files/file_posix.cc
+++ /dev/null
@@ -1,434 +0,0 @@
-// 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 "base/files/file.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-namespace base {
-
-// Make sure our Whence mappings match the system headers.
-static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
- File::FROM_END == SEEK_END,
- "whence mapping must match the system headers");
-
-namespace {
-
-#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
- defined(OS_ANDROID) && __ANDROID_API__ < 21
-int CallFstat(int fd, stat_wrapper_t* sb) {
- return fstat(fd, sb);
-}
-#else
-int CallFstat(int fd, stat_wrapper_t* sb) {
- return fstat64(fd, sb);
-}
-#endif
-
-// NaCl doesn't provide the following system calls, so either simulate them or
-// wrap them in order to minimize the number of #ifdef's in this file.
-#if !defined(OS_NACL) && !defined(OS_AIX)
-bool IsOpenAppend(PlatformFile file) {
- return (fcntl(file, F_GETFL) & O_APPEND) != 0;
-}
-
-int CallFtruncate(PlatformFile file, int64_t length) {
- return HANDLE_EINTR(ftruncate(file, length));
-}
-
-#if !defined(OS_FUCHSIA)
-File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
- struct flock lock;
- lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0; // Lock entire file.
- if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
- return File::GetLastFileError();
- return File::FILE_OK;
-}
-#endif
-
-#else // defined(OS_NACL) && !defined(OS_AIX)
-
-bool IsOpenAppend(PlatformFile file) {
- // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
- // standard and always appends if the file is opened with O_APPEND, just
- // return false here.
- return false;
-}
-
-int CallFtruncate(PlatformFile file, int64_t length) {
- NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
- return 0;
-}
-
-File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
- NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
- return File::FILE_ERROR_INVALID_OPERATION;
-}
-#endif // defined(OS_NACL)
-
-} // namespace
-
-void File::Info::FromStat(const stat_wrapper_t& stat_info) {
- is_directory = S_ISDIR(stat_info.st_mode);
- is_symbolic_link = S_ISLNK(stat_info.st_mode);
- size = stat_info.st_size;
-
-#if defined(OS_MACOSX)
- time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
- int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
- time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
- int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
- time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
- int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
-#elif defined(OS_AIX)
- time_t last_modified_sec = stat_info.st_mtime;
- int64_t last_modified_nsec = 0;
- time_t last_accessed_sec = stat_info.st_atime;
- int64_t last_accessed_nsec = 0;
- time_t creation_time_sec = stat_info.st_ctime;
- int64_t creation_time_nsec = 0;
-#elif defined(OS_POSIX)
- time_t last_modified_sec = stat_info.st_mtim.tv_sec;
- int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
- time_t last_accessed_sec = stat_info.st_atim.tv_sec;
- int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
- time_t creation_time_sec = stat_info.st_ctim.tv_sec;
- int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
-#else
-#error
-#endif
-
- constexpr uint64_t kNano = 1'000'000'000;
- last_modified = last_modified_sec * kNano + last_modified_nsec;
- last_accessed = last_accessed_sec * kNano + last_accessed_nsec;
- creation_time = creation_time_sec * kNano + creation_time_nsec;
-}
-
-bool File::IsValid() const {
- return file_.is_valid();
-}
-
-PlatformFile File::GetPlatformFile() const {
- return file_.get();
-}
-
-PlatformFile File::TakePlatformFile() {
- return file_.release();
-}
-
-void File::Close() {
- if (!IsValid())
- return;
-
- file_.reset();
-}
-
-int64_t File::Seek(Whence whence, int64_t offset) {
- DCHECK(IsValid());
-
- static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
- return lseek(file_.get(), static_cast<off_t>(offset),
- static_cast<int>(whence));
-}
-
-int File::Read(int64_t offset, char* data, int size) {
- DCHECK(IsValid());
- if (size < 0)
- return -1;
-
- int bytes_read = 0;
- int rv;
- do {
- rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read,
- offset + bytes_read));
- if (rv <= 0)
- break;
-
- bytes_read += rv;
- } while (bytes_read < size);
-
- return bytes_read ? bytes_read : rv;
-}
-
-int File::ReadAtCurrentPos(char* data, int size) {
- DCHECK(IsValid());
- if (size < 0)
- return -1;
-
- int bytes_read = 0;
- int rv;
- do {
- rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
- if (rv <= 0)
- break;
-
- bytes_read += rv;
- } while (bytes_read < size);
-
- return bytes_read ? bytes_read : rv;
-}
-
-int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
- DCHECK(IsValid());
- return HANDLE_EINTR(pread(file_.get(), data, size, offset));
-}
-
-int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
- DCHECK(IsValid());
- if (size < 0)
- return -1;
-
- return HANDLE_EINTR(read(file_.get(), data, size));
-}
-
-int File::Write(int64_t offset, const char* data, int size) {
- if (IsOpenAppend(file_.get()))
- return WriteAtCurrentPos(data, size);
-
- DCHECK(IsValid());
- if (size < 0)
- return -1;
-
- int bytes_written = 0;
- int rv;
- do {
- rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
- size - bytes_written, offset + bytes_written));
- if (rv <= 0)
- break;
-
- bytes_written += rv;
- } while (bytes_written < size);
-
- return bytes_written ? bytes_written : rv;
-}
-
-int File::WriteAtCurrentPos(const char* data, int size) {
- DCHECK(IsValid());
- if (size < 0)
- return -1;
-
- int bytes_written = 0;
- int rv;
- do {
- rv = HANDLE_EINTR(
- write(file_.get(), data + bytes_written, size - bytes_written));
- if (rv <= 0)
- break;
-
- bytes_written += rv;
- } while (bytes_written < size);
-
- return bytes_written ? bytes_written : rv;
-}
-
-int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
- DCHECK(IsValid());
- if (size < 0)
- return -1;
-
- return HANDLE_EINTR(write(file_.get(), data, size));
-}
-
-int64_t File::GetLength() {
- DCHECK(IsValid());
-
- stat_wrapper_t file_info;
- if (CallFstat(file_.get(), &file_info))
- return -1;
-
- return file_info.st_size;
-}
-
-bool File::SetLength(int64_t length) {
- DCHECK(IsValid());
-
- return !CallFtruncate(file_.get(), length);
-}
-
-bool File::GetInfo(Info* info) {
- DCHECK(IsValid());
-
- stat_wrapper_t file_info;
- if (CallFstat(file_.get(), &file_info))
- return false;
-
- info->FromStat(file_info);
- return true;
-}
-
-#if !defined(OS_FUCHSIA)
-File::Error File::Lock() {
- return CallFcntlFlock(file_.get(), true);
-}
-
-File::Error File::Unlock() {
- return CallFcntlFlock(file_.get(), false);
-}
-#endif
-
-File File::Duplicate() const {
- if (!IsValid())
- return File();
-
- PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
- if (other_fd == -1)
- return File(File::GetLastFileError());
-
- File other(other_fd);
- if (async())
- other.async_ = true;
- return other;
-}
-
-// Static.
-File::Error File::OSErrorToFileError(int saved_errno) {
- switch (saved_errno) {
- case EACCES:
- case EISDIR:
- case EROFS:
- case EPERM:
- return FILE_ERROR_ACCESS_DENIED;
- case EBUSY:
-#if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
- case ETXTBSY:
-#endif
- return FILE_ERROR_IN_USE;
- case EEXIST:
- return FILE_ERROR_EXISTS;
- case EIO:
- return FILE_ERROR_IO;
- case ENOENT:
- return FILE_ERROR_NOT_FOUND;
- case ENFILE: // fallthrough
- case EMFILE:
- return FILE_ERROR_TOO_MANY_OPENED;
- case ENOMEM:
- return FILE_ERROR_NO_MEMORY;
- case ENOSPC:
- return FILE_ERROR_NO_SPACE;
- case ENOTDIR:
- return FILE_ERROR_NOT_A_DIRECTORY;
- default:
- // This function should only be called for errors.
- DCHECK_NE(0, saved_errno);
- return FILE_ERROR_FAILED;
- }
-}
-
-// NaCl doesn't implement system calls to open files directly.
-#if !defined(OS_NACL)
-// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
-void File::DoInitialize(const FilePath& path, uint32_t flags) {
- DCHECK(!IsValid());
-
- int open_flags = 0;
- if (flags & FLAG_CREATE)
- open_flags = O_CREAT | O_EXCL;
-
- created_ = false;
-
- if (flags & FLAG_CREATE_ALWAYS) {
- DCHECK(!open_flags);
- DCHECK(flags & FLAG_WRITE);
- open_flags = O_CREAT | O_TRUNC;
- }
-
- if (flags & FLAG_OPEN_TRUNCATED) {
- DCHECK(!open_flags);
- DCHECK(flags & FLAG_WRITE);
- open_flags = O_TRUNC;
- }
-
- if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
- NOTREACHED();
- errno = EOPNOTSUPP;
- error_details_ = FILE_ERROR_FAILED;
- return;
- }
-
- if (flags & FLAG_WRITE && flags & FLAG_READ) {
- open_flags |= O_RDWR;
- } else if (flags & FLAG_WRITE) {
- open_flags |= O_WRONLY;
- } else if (!(flags & FLAG_READ) && !(flags & FLAG_WRITE_ATTRIBUTES) &&
- !(flags & FLAG_APPEND) && !(flags & FLAG_OPEN_ALWAYS)) {
- NOTREACHED();
- }
-
- if (flags & FLAG_TERMINAL_DEVICE)
- open_flags |= O_NOCTTY | O_NDELAY;
-
- if (flags & FLAG_APPEND && flags & FLAG_READ)
- open_flags |= O_APPEND | O_RDWR;
- else if (flags & FLAG_APPEND)
- open_flags |= O_APPEND | O_WRONLY;
-
- static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
-
- int mode = S_IRUSR | S_IWUSR;
- int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
-
- if (flags & FLAG_OPEN_ALWAYS) {
- if (descriptor < 0) {
- open_flags |= O_CREAT;
- if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
- open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
-
- descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
- if (descriptor >= 0)
- created_ = true;
- }
- }
-
- if (descriptor < 0) {
- error_details_ = File::GetLastFileError();
- return;
- }
-
- if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
- created_ = true;
-
- if (flags & FLAG_DELETE_ON_CLOSE)
- unlink(path.value().c_str());
-
- async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
- error_details_ = FILE_OK;
- file_.reset(descriptor);
-}
-#endif // !defined(OS_NACL)
-
-bool File::Flush() {
- DCHECK(IsValid());
-
-#if defined(OS_LINUX)
- return !HANDLE_EINTR(fdatasync(file_.get()));
-#else
- return !HANDLE_EINTR(fsync(file_.get()));
-#endif
-}
-
-void File::SetPlatformFile(PlatformFile file) {
- DCHECK(!file_.is_valid());
- file_.reset(file);
-}
-
-// static
-File::Error File::GetLastFileError() {
- return base::File::OSErrorToFileError(errno);
-}
-
-} // namespace base
diff --git a/gn/base/files/file_util.cc b/gn/base/files/file_util.cc
deleted file mode 100644
index 9a98a0b81e0..00000000000
--- a/gn/base/files/file_util.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-// 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 "base/files/file_util.h"
-
-#if defined(OS_WIN)
-#include <io.h>
-#endif
-#include <stdio.h>
-
-#include <fstream>
-#include <limits>
-
-#include "base/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-namespace base {
-
-#if !defined(OS_NACL_NONSFI)
-namespace {
-
-// The maximum number of 'uniquified' files we will try to create.
-// This is used when the filename we're trying to download is already in use,
-// so we create a new unique filename by appending " (nnn)" before the
-// extension, where 1 <= nnn <= kMaxUniqueFiles.
-// Also used by code that cleans up said files.
-static const int kMaxUniqueFiles = 100;
-
-} // namespace
-
-int64_t ComputeDirectorySize(const FilePath& root_path) {
- int64_t running_size = 0;
- FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
- while (!file_iter.Next().empty())
- running_size += file_iter.GetInfo().GetSize();
- return running_size;
-}
-
-bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
- // We open the file in binary format even if they are text files because
- // we are just comparing that bytes are exactly same in both files and not
- // doing anything smart with text formatting.
- std::ifstream file1(filename1.value().c_str(),
- std::ios::in | std::ios::binary);
- std::ifstream file2(filename2.value().c_str(),
- std::ios::in | std::ios::binary);
-
- // Even if both files aren't openable (and thus, in some sense, "equal"),
- // any unusable file yields a result of "false".
- if (!file1.is_open() || !file2.is_open())
- return false;
-
- const int BUFFER_SIZE = 2056;
- char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
- do {
- file1.read(buffer1, BUFFER_SIZE);
- file2.read(buffer2, BUFFER_SIZE);
-
- if ((file1.eof() != file2.eof()) || (file1.gcount() != file2.gcount()) ||
- (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
- file1.close();
- file2.close();
- return false;
- }
- } while (!file1.eof() || !file2.eof());
-
- file1.close();
- file2.close();
- return true;
-}
-
-bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
- std::ifstream file1(filename1.value().c_str(), std::ios::in);
- std::ifstream file2(filename2.value().c_str(), std::ios::in);
-
- // Even if both files aren't openable (and thus, in some sense, "equal"),
- // any unusable file yields a result of "false".
- if (!file1.is_open() || !file2.is_open())
- return false;
-
- do {
- std::string line1, line2;
- getline(file1, line1);
- getline(file2, line2);
-
- // Check for mismatched EOF states, or any error state.
- if ((file1.eof() != file2.eof()) || file1.bad() || file2.bad()) {
- return false;
- }
-
- // Trim all '\r' and '\n' characters from the end of the line.
- std::string::size_type end1 = line1.find_last_not_of("\r\n");
- if (end1 == std::string::npos)
- line1.clear();
- else if (end1 + 1 < line1.length())
- line1.erase(end1 + 1);
-
- std::string::size_type end2 = line2.find_last_not_of("\r\n");
- if (end2 == std::string::npos)
- line2.clear();
- else if (end2 + 1 < line2.length())
- line2.erase(end2 + 1);
-
- if (line1 != line2)
- return false;
- } while (!file1.eof() || !file2.eof());
-
- return true;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
-bool ReadFileToStringWithMaxSize(const FilePath& path,
- std::string* contents,
- size_t max_size) {
- if (contents)
- contents->clear();
- if (path.ReferencesParent())
- return false;
- FILE* file = OpenFile(path, "rb");
- if (!file) {
- return false;
- }
-
- // Many files supplied in |path| have incorrect size (proc files etc).
- // Hence, the file is read sequentially as opposed to a one-shot read, using
- // file size as a hint for chunk size if available.
- constexpr int64_t kDefaultChunkSize = 1 << 16;
- int64_t chunk_size;
-#if !defined(OS_NACL_NONSFI)
- if (!GetFileSize(path, &chunk_size) || chunk_size <= 0)
- chunk_size = kDefaultChunkSize - 1;
- // We need to attempt to read at EOF for feof flag to be set so here we
- // use |chunk_size| + 1.
- chunk_size = std::min<uint64_t>(chunk_size, max_size) + 1;
-#else
- chunk_size = kDefaultChunkSize;
-#endif // !defined(OS_NACL_NONSFI)
- size_t bytes_read_this_pass;
- size_t bytes_read_so_far = 0;
- bool read_status = true;
- std::string local_contents;
- local_contents.resize(chunk_size);
-
- while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1,
- chunk_size, file)) > 0) {
- if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
- // Read more than max_size bytes, bail out.
- bytes_read_so_far = max_size;
- read_status = false;
- break;
- }
- // In case EOF was not reached, iterate again but revert to the default
- // chunk size.
- if (bytes_read_so_far == 0)
- chunk_size = kDefaultChunkSize;
-
- bytes_read_so_far += bytes_read_this_pass;
- // Last fread syscall (after EOF) can be avoided via feof, which is just a
- // flag check.
- if (feof(file))
- break;
- local_contents.resize(bytes_read_so_far + chunk_size);
- }
- read_status = read_status && !ferror(file);
- CloseFile(file);
- if (contents) {
- contents->swap(local_contents);
- contents->resize(bytes_read_so_far);
- }
-
- return read_status;
-}
-
-bool ReadFileToString(const FilePath& path, std::string* contents) {
- return ReadFileToStringWithMaxSize(path, contents,
- std::numeric_limits<size_t>::max());
-}
-
-#if !defined(OS_NACL_NONSFI)
-bool IsDirectoryEmpty(const FilePath& dir_path) {
- FileEnumerator files(dir_path, false,
- FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
- if (files.Next().empty())
- return true;
- return false;
-}
-
-FILE* CreateAndOpenTemporaryFile(FilePath* path) {
- FilePath directory;
- if (!GetTempDir(&directory))
- return nullptr;
-
- return CreateAndOpenTemporaryFileInDir(directory, path);
-}
-
-bool CreateDirectory(const FilePath& full_path) {
- return CreateDirectoryAndGetError(full_path, nullptr);
-}
-
-bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
- File::Info info;
- if (!GetFileInfo(file_path, &info))
- return false;
- *file_size = info.size;
- return true;
-}
-
-#endif // !defined(OS_NACL_NONSFI)
-
-bool CloseFile(FILE* file) {
- if (file == nullptr)
- return true;
- return fclose(file) == 0;
-}
-
-#if !defined(OS_NACL_NONSFI)
-bool TruncateFile(FILE* file) {
- if (file == nullptr)
- return false;
- long current_offset = ftell(file);
- if (current_offset == -1)
- return false;
-#if defined(OS_WIN)
- int fd = _fileno(file);
- if (_chsize(fd, current_offset) != 0)
- return false;
-#else
- int fd = fileno(file);
- if (ftruncate(fd, current_offset) != 0)
- return false;
-#endif
- return true;
-}
-
-int GetUniquePathNumber(const FilePath& path,
- const FilePath::StringType& suffix) {
- bool have_suffix = !suffix.empty();
- if (!PathExists(path) &&
- (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
- return 0;
- }
-
- FilePath new_path;
- for (int count = 1; count <= kMaxUniqueFiles; ++count) {
- new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
- if (!PathExists(new_path) &&
- (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
- return count;
- }
- }
-
- return -1;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
-} // namespace base
diff --git a/gn/base/files/file_util.h b/gn/base/files/file_util.h
deleted file mode 100644
index e22f40f8f53..00000000000
--- a/gn/base/files/file_util.h
+++ /dev/null
@@ -1,395 +0,0 @@
-// 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 contains utility functions for dealing with the local
-// filesystem.
-
-#ifndef BASE_FILES_FILE_UTIL_H_
-#define BASE_FILES_FILE_UTIL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include <set>
-#include <string>
-#include <vector>
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
-
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "base/strings/string16.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include "base/win/windows_types.h"
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#endif
-
-namespace base {
-
-class Environment;
-
-//-----------------------------------------------------------------------------
-// Functions that involve filesystem access or modification:
-
-// Returns an absolute version of a relative path. Returns an empty path on
-// error. On POSIX, this function fails if the path does not exist. This
-// function can result in I/O so it can be slow.
-FilePath MakeAbsoluteFilePath(const FilePath& input);
-
-// Returns the total number of bytes used by all the files under |root_path|.
-// If the path does not exist the function returns 0.
-//
-// This function is implemented using the FileEnumerator class so it is not
-// particularly speedy in any platform.
-int64_t ComputeDirectorySize(const FilePath& root_path);
-
-// Deletes the given path, whether it's a file or a directory.
-// If it's a directory, it's perfectly happy to delete all of the
-// directory's contents. Passing true to recursive deletes
-// subdirectories and their contents as well.
-// Returns true if successful, false otherwise. It is considered successful
-// to attempt to delete a file that does not exist.
-//
-// In posix environment and if |path| is a symbolic link, this deletes only
-// the symlink. (even if the symlink points to a non-existent file)
-//
-// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
-// TO "rm -rf", SO USE WITH CAUTION.
-bool DeleteFile(const FilePath& path, bool recursive);
-
-#if defined(OS_WIN)
-// Schedules to delete the given path, whether it's a file or a directory, until
-// the operating system is restarted.
-// Note:
-// 1) The file/directory to be deleted should exist in a temp folder.
-// 2) The directory to be deleted must be empty.
-bool DeleteFileAfterReboot(const FilePath& path);
-#endif
-
-// Renames file |from_path| to |to_path|. Both paths must be on the same
-// volume, or the function will fail. Destination file will be created
-// if it doesn't exist. Prefer this function over Move when dealing with
-// temporary files. On Windows it preserves attributes of the target file.
-// Returns true on success, leaving *error unchanged.
-// Returns false on failure and sets *error appropriately, if it is non-NULL.
-bool ReplaceFile(const FilePath& from_path,
- const FilePath& to_path,
- File::Error* error);
-
-// Returns true if the given path exists on the local filesystem,
-// false otherwise.
-bool PathExists(const FilePath& path);
-
-// Returns true if the given path is writable by the user, false otherwise.
-bool PathIsWritable(const FilePath& path);
-
-// Returns true if the given path exists and is a directory, false otherwise.
-bool DirectoryExists(const FilePath& path);
-
-// Returns true if the contents of the two files given are equal, false
-// otherwise. If either file can't be read, returns false.
-bool ContentsEqual(const FilePath& filename1, const FilePath& filename2);
-
-// Returns true if the contents of the two text files given are equal, false
-// otherwise. This routine treats "\r\n" and "\n" as equivalent.
-bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2);
-
-// Reads the file at |path| into |contents| and returns true on success and
-// false on error. For security reasons, a |path| containing path traversal
-// components ('..') is treated as a read error and |contents| is set to empty.
-// In case of I/O error, |contents| holds the data that could be read from the
-// file before the error occurred.
-// |contents| may be NULL, in which case this function is useful for its side
-// effect of priming the disk cache (could be used for unit tests).
-bool ReadFileToString(const FilePath& path, std::string* contents);
-
-// Reads the file at |path| into |contents| and returns true on success and
-// false on error. For security reasons, a |path| containing path traversal
-// components ('..') is treated as a read error and |contents| is set to empty.
-// In case of I/O error, |contents| holds the data that could be read from the
-// file before the error occurred. When the file size exceeds |max_size|, the
-// function returns false with |contents| holding the file truncated to
-// |max_size|.
-// |contents| may be NULL, in which case this function is useful for its side
-// effect of priming the disk cache (could be used for unit tests).
-bool ReadFileToStringWithMaxSize(const FilePath& path,
- std::string* contents,
- size_t max_size);
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
-// in |buffer|. This function is protected against EINTR and partial reads.
-// Returns true iff |bytes| bytes have been successfully read from |fd|.
-bool ReadFromFD(int fd, char* buffer, size_t bytes);
-
-// Performs the same function as CreateAndOpenTemporaryFileInDir(), but returns
-// the file-descriptor directly, rather than wrapping it into a FILE. Returns
-// -1 on failure.
-int CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir, FilePath* path);
-
-#endif // OS_POSIX || OS_FUCHSIA
-
-#if defined(OS_POSIX)
-
-// Creates a symbolic link at |symlink| pointing to |target|. Returns
-// false on failure.
-bool CreateSymbolicLink(const FilePath& target, const FilePath& symlink);
-
-// Reads the given |symlink| and returns where it points to in |target|.
-// Returns false upon failure.
-bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);
-
-// Bits and masks of the file permission.
-enum FilePermissionBits {
- FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO,
- FILE_PERMISSION_USER_MASK = S_IRWXU,
- FILE_PERMISSION_GROUP_MASK = S_IRWXG,
- FILE_PERMISSION_OTHERS_MASK = S_IRWXO,
-
- FILE_PERMISSION_READ_BY_USER = S_IRUSR,
- FILE_PERMISSION_WRITE_BY_USER = S_IWUSR,
- FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR,
- FILE_PERMISSION_READ_BY_GROUP = S_IRGRP,
- FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP,
- FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP,
- FILE_PERMISSION_READ_BY_OTHERS = S_IROTH,
- FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH,
- FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH,
-};
-
-// Reads the permission of the given |path|, storing the file permission
-// bits in |mode|. If |path| is symbolic link, |mode| is the permission of
-// a file which the symlink points to.
-bool GetPosixFilePermissions(const FilePath& path, int* mode);
-// Sets the permission of the given |path|. If |path| is symbolic link, sets
-// the permission of a file which the symlink points to.
-bool SetPosixFilePermissions(const FilePath& path, int mode);
-
-// Returns true iff |executable| can be found in any directory specified by the
-// environment variable in |env|.
-bool ExecutableExistsInPath(Environment* env,
- const FilePath::StringType& executable);
-
-#endif // OS_POSIX
-
-// Returns true if the given directory is empty
-bool IsDirectoryEmpty(const FilePath& dir_path);
-
-// Get the temporary directory provided by the system.
-//
-// WARNING: In general, you should use CreateTemporaryFile variants below
-// instead of this function. Those variants will ensure that the proper
-// permissions are set so that other users on the system can't edit them while
-// they're open (which can lead to security issues).
-bool GetTempDir(FilePath* path);
-
-// Creates a temporary file. The full path is placed in |path|, and the
-// function returns true if was successful in creating the file. The file will
-// be empty and all handles closed after this function returns.
-bool CreateTemporaryFile(FilePath* path);
-
-// Same as CreateTemporaryFile but the file is created in |dir|.
-bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file);
-
-// Create and open a temporary file. File is opened for read/write.
-// The full path is placed in |path|.
-// Returns a handle to the opened file or NULL if an error occurred.
-FILE* CreateAndOpenTemporaryFile(FilePath* path);
-
-// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
-FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path);
-
-// Create a new directory. If prefix is provided, the new directory name is in
-// the format of prefixyyyy.
-// NOTE: prefix is ignored in the POSIX implementation.
-// If success, return true and output the full path of the directory created.
-bool CreateNewTempDirectory(const FilePath::StringType& prefix,
- FilePath* new_temp_path);
-
-// Create a directory within another directory.
-// Extra characters will be appended to |prefix| to ensure that the
-// new directory does not have the same name as an existing directory.
-bool CreateTemporaryDirInDir(const FilePath& base_dir,
- const FilePath::StringType& prefix,
- FilePath* new_dir);
-
-// Creates a directory, as well as creating any parent directories, if they
-// don't exist. Returns 'true' on successful creation, or if the directory
-// already exists. The directory is only readable by the current user.
-// Returns true on success, leaving *error unchanged.
-// Returns false on failure and sets *error appropriately, if it is non-NULL.
-bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error);
-
-// Backward-compatible convenience method for the above.
-bool CreateDirectory(const FilePath& full_path);
-
-// Returns the file size. Returns true on success.
-bool GetFileSize(const FilePath& file_path, int64_t* file_size);
-
-// Sets |real_path| to |path| with symbolic links and junctions expanded.
-// On windows, make sure the path starts with a lettered drive.
-// |path| must reference a file. Function will fail if |path| points to
-// a directory or to a nonexistent path. On windows, this function will
-// fail if |path| is a junction or symlink that points to an empty file,
-// or if |real_path| would be longer than MAX_PATH characters.
-bool NormalizeFilePath(const FilePath& path, FilePath* real_path);
-
-#if defined(OS_WIN)
-
-// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."),
-// return in |drive_letter_path| the equivalent path that starts with
-// a drive letter ("C:\..."). Return false if no such path exists.
-bool DevicePathToDriveLetterPath(const FilePath& device_path,
- FilePath* drive_letter_path);
-
-// Given an existing file in |path|, set |real_path| to the path
-// in native NT format, of the form "\Device\HarddiskVolumeXX\..".
-// Returns false if the path can not be found. Empty files cannot
-// be resolved with this function.
-bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path);
-#endif
-
-// This function will return if the given file is a symlink or not.
-bool IsLink(const FilePath& file_path);
-
-// Returns information about the given file path.
-bool GetFileInfo(const FilePath& file_path, File::Info* info);
-
-// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The
-// underlying file descriptor (POSIX) or handle (Windows) is unconditionally
-// configured to not be propagated to child processes.
-FILE* OpenFile(const FilePath& filename, const char* mode);
-
-// Closes file opened by OpenFile. Returns true on success.
-bool CloseFile(FILE* file);
-
-// Associates a standard FILE stream with an existing File. Note that this
-// functions take ownership of the existing File.
-FILE* FileToFILE(File file, const char* mode);
-
-// Truncates an open file to end at the location of the current file pointer.
-// This is a cross-platform analog to Windows' SetEndOfFile() function.
-bool TruncateFile(FILE* file);
-
-// Reads at most the given number of bytes from the file into the buffer.
-// Returns the number of read bytes, or -1 on error.
-int ReadFile(const FilePath& filename, char* data, int max_size);
-
-// Writes the given buffer into the file, overwriting any data that was
-// previously there. Returns the number of bytes written, or -1 on error.
-int WriteFile(const FilePath& filename, const char* data, int size);
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-// Appends |data| to |fd|. Does not close |fd| when done. Returns true iff
-// |size| bytes of |data| were written to |fd|.
-bool WriteFileDescriptor(const int fd, const char* data, int size);
-#endif
-
-// Appends |data| to |filename|. Returns true iff |size| bytes of |data| were
-// written to |filename|.
-bool AppendToFile(const FilePath& filename, const char* data, int size);
-
-// Gets the current working directory for the process.
-bool GetCurrentDirectory(FilePath* path);
-
-// Sets the current working directory for the process.
-bool SetCurrentDirectory(const FilePath& path);
-
-// Attempts to find a number that can be appended to the |path| to make it
-// unique. If |path| does not exist, 0 is returned. If it fails to find such
-// a number, -1 is returned. If |suffix| is not empty, also checks the
-// existence of it with the given suffix.
-int GetUniquePathNumber(const FilePath& path,
- const FilePath::StringType& suffix);
-
-// Sets the given |fd| to non-blocking mode.
-// Returns true if it was able to set it in the non-blocking mode, otherwise
-// false.
-bool SetNonBlocking(int fd);
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-// Creates a non-blocking, close-on-exec pipe.
-// This creates a non-blocking pipe that is not intended to be shared with any
-// child process. This will be done atomically if the operating system supports
-// it. Returns true if it was able to create the pipe, otherwise false.
-bool CreateLocalNonBlockingPipe(int fds[2]);
-
-// Sets the given |fd| to close-on-exec mode.
-// Returns true if it was able to set it in the close-on-exec mode, otherwise
-// false.
-bool SetCloseOnExec(int fd);
-
-// Test that |path| can only be changed by a given user and members of
-// a given set of groups.
-// Specifically, test that all parts of |path| under (and including) |base|:
-// * Exist.
-// * Are owned by a specific user.
-// * Are not writable by all users.
-// * Are owned by a member of a given set of groups, or are not writable by
-// their group.
-// * Are not symbolic links.
-// This is useful for checking that a config file is administrator-controlled.
-// |base| must contain |path|.
-bool VerifyPathControlledByUser(const base::FilePath& base,
- const base::FilePath& path,
- uid_t owner_uid,
- const std::set<gid_t>& group_gids);
-#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-// Is |path| writable only by a user with administrator privileges?
-// This function uses Mac OS conventions. The super user is assumed to have
-// uid 0, and the administrator group is assumed to be named "admin".
-// Testing that |path|, and every parent directory including the root of
-// the filesystem, are owned by the superuser, controlled by the group
-// "admin", are not writable by all users, and contain no symbolic links.
-// Will return false if |path| does not exist.
-bool VerifyPathControlledByAdmin(const base::FilePath& path);
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
-
-// Returns the maximum length of path component on the volume containing
-// the directory |path|, in the number of FilePath::CharType, or -1 on failure.
-int GetMaximumPathComponentLength(const base::FilePath& path);
-
-#if defined(OS_LINUX) || defined(OS_AIX)
-// Broad categories of file systems as returned by statfs() on Linux.
-enum FileSystemType {
- FILE_SYSTEM_UNKNOWN, // statfs failed.
- FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS.
- FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2
- FILE_SYSTEM_NFS,
- FILE_SYSTEM_SMB,
- FILE_SYSTEM_CODA,
- FILE_SYSTEM_MEMORY, // in-memory file system
- FILE_SYSTEM_CGROUP, // cgroup control.
- FILE_SYSTEM_OTHER, // any other value.
- FILE_SYSTEM_TYPE_COUNT
-};
-
-// Attempts determine the FileSystemType for |path|.
-// Returns false if |path| doesn't exist.
-bool GetFileSystemType(const FilePath& path, FileSystemType* type);
-#endif
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-// Get a temporary directory for shared memory files. The directory may depend
-// on whether the destination is intended for executable files, which in turn
-// depends on how /dev/shmem was mounted. As a result, you must supply whether
-// you intend to create executable shmem segments so this function can find
-// an appropriate location.
-bool GetShmemTempDir(bool executable, FilePath* path);
-#endif
-
-} // namespace base
-
-#endif // BASE_FILES_FILE_UTIL_H_
diff --git a/gn/base/files/file_util_posix.cc b/gn/base/files/file_util_posix.cc
deleted file mode 100644
index eb07e64f8e2..00000000000
--- a/gn/base/files/file_util_posix.cc
+++ /dev/null
@@ -1,787 +0,0 @@
-// 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 "base/files/file_util.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "base/command_line.h"
-#include "base/containers/stack.h"
-#include "base/environment.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/files/scoped_file.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/stl_util.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-#if defined(OS_MACOSX)
-#include <AvailabilityMacros.h>
-#endif
-
-#if !defined(OS_IOS)
-#include <grp.h>
-#endif
-
-// We need to do this on AIX due to some inconsistencies in how AIX
-// handles XOPEN_SOURCE and ALL_SOURCE.
-#if defined(OS_AIX)
-extern "C" char* mkdtemp(char* path);
-#endif
-
-namespace base {
-
-namespace {
-
-#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
- defined(OS_ANDROID) && __ANDROID_API__ < 21
-int CallStat(const char* path, stat_wrapper_t* sb) {
- return stat(path, sb);
-}
-int CallLstat(const char* path, stat_wrapper_t* sb) {
- return lstat(path, sb);
-}
-#else
-int CallStat(const char* path, stat_wrapper_t* sb) {
- return stat64(path, sb);
-}
-int CallLstat(const char* path, stat_wrapper_t* sb) {
- return lstat64(path, sb);
-}
-#endif
-
-#if !defined(OS_NACL_NONSFI)
-// Helper for VerifyPathControlledByUser.
-bool VerifySpecificPathControlledByUser(const FilePath& path,
- uid_t owner_uid,
- const std::set<gid_t>& group_gids) {
- stat_wrapper_t stat_info;
- if (CallLstat(path.value().c_str(), &stat_info) != 0) {
- DPLOG(ERROR) << "Failed to get information on path " << path.value();
- return false;
- }
-
- if (S_ISLNK(stat_info.st_mode)) {
- DLOG(ERROR) << "Path " << path.value() << " is a symbolic link.";
- return false;
- }
-
- if (stat_info.st_uid != owner_uid) {
- DLOG(ERROR) << "Path " << path.value() << " is owned by the wrong user.";
- return false;
- }
-
- if ((stat_info.st_mode & S_IWGRP) &&
- !ContainsKey(group_gids, stat_info.st_gid)) {
- DLOG(ERROR) << "Path " << path.value()
- << " is writable by an unprivileged group.";
- return false;
- }
-
- if (stat_info.st_mode & S_IWOTH) {
- DLOG(ERROR) << "Path " << path.value() << " is writable by any user.";
- return false;
- }
-
- return true;
-}
-
-std::string TempFileName() {
- return std::string(".org.chromium.Chromium.XXXXXX");
-}
-
-#if defined(OS_LINUX) || defined(OS_AIX)
-// Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC.
-// This depends on the mount options used for /dev/shm, which vary among
-// different Linux distributions and possibly local configuration. It also
-// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm
-// but its kernel allows mprotect with PROT_EXEC anyway.
-bool DetermineDevShmExecutable() {
- bool result = false;
- FilePath path;
-
- ScopedFD fd(
- CreateAndOpenFdForTemporaryFileInDir(FilePath("/dev/shm"), &path));
- if (fd.is_valid()) {
- DeleteFile(path, false);
- long sysconf_result = sysconf(_SC_PAGESIZE);
- CHECK_GE(sysconf_result, 0);
- size_t pagesize = static_cast<size_t>(sysconf_result);
- CHECK_GE(sizeof(pagesize), sizeof(sysconf_result));
- void* mapping = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0);
- if (mapping != MAP_FAILED) {
- if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0)
- result = true;
- munmap(mapping, pagesize);
- }
- }
- return result;
-}
-#endif // defined(OS_LINUX) || defined(OS_AIX)
-
-bool AdvanceEnumeratorWithStat(FileEnumerator* traversal,
- FilePath* out_next_path,
- struct stat* out_next_stat) {
- DCHECK(out_next_path);
- DCHECK(out_next_stat);
- *out_next_path = traversal->Next();
- if (out_next_path->empty())
- return false;
-
- *out_next_stat = traversal->GetInfo().stat();
- return true;
-}
-
-bool CopyFileContents(File* infile, File* outfile) {
- static constexpr size_t kBufferSize = 32768;
- std::vector<char> buffer(kBufferSize);
-
- for (;;) {
- ssize_t bytes_read = infile->ReadAtCurrentPos(buffer.data(), buffer.size());
- if (bytes_read < 0)
- return false;
- if (bytes_read == 0)
- return true;
- // Allow for partial writes
- ssize_t bytes_written_per_read = 0;
- do {
- ssize_t bytes_written_partial = outfile->WriteAtCurrentPos(
- &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read);
- if (bytes_written_partial < 0)
- return false;
-
- bytes_written_per_read += bytes_written_partial;
- } while (bytes_written_per_read < bytes_read);
- }
-
- NOTREACHED();
- return false;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
-#if !defined(OS_MACOSX)
-// Appends |mode_char| to |mode| before the optional character set encoding; see
-// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for
-// details.
-std::string AppendModeCharacter(StringPiece mode, char mode_char) {
- std::string result(mode.as_string());
- size_t comma_pos = result.find(',');
- result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1,
- mode_char);
- return result;
-}
-#endif
-
-} // namespace
-
-#if !defined(OS_NACL_NONSFI)
-FilePath MakeAbsoluteFilePath(const FilePath& input) {
- char full_path[PATH_MAX];
- if (realpath(input.value().c_str(), full_path) == nullptr)
- return FilePath();
- return FilePath(full_path);
-}
-
-// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
-// which works both with and without the recursive flag. I'm not sure we need
-// that functionality. If not, remove from file_util_win.cc, otherwise add it
-// here.
-bool DeleteFile(const FilePath& path, bool recursive) {
- const char* path_str = path.value().c_str();
- stat_wrapper_t file_info;
- if (CallLstat(path_str, &file_info) != 0) {
- // The Windows version defines this condition as success.
- return (errno == ENOENT || errno == ENOTDIR);
- }
- if (!S_ISDIR(file_info.st_mode))
- return (unlink(path_str) == 0);
- if (!recursive)
- return (rmdir(path_str) == 0);
-
- bool success = true;
- stack<std::string> directories;
- directories.push(path.value());
- FileEnumerator traversal(path, true,
- FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
- FileEnumerator::SHOW_SYM_LINKS);
- for (FilePath current = traversal.Next(); !current.empty();
- current = traversal.Next()) {
- if (traversal.GetInfo().IsDirectory())
- directories.push(current.value());
- else
- success &= (unlink(current.value().c_str()) == 0);
- }
-
- while (!directories.empty()) {
- FilePath dir = FilePath(directories.top());
- directories.pop();
- success &= (rmdir(dir.value().c_str()) == 0);
- }
- return success;
-}
-
-bool ReplaceFile(const FilePath& from_path,
- const FilePath& to_path,
- File::Error* error) {
- if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
- return true;
- if (error)
- *error = File::GetLastFileError();
- return false;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
-bool CreateLocalNonBlockingPipe(int fds[2]) {
-#if defined(OS_LINUX)
- return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0;
-#else
- int raw_fds[2];
- if (pipe(raw_fds) != 0)
- return false;
- ScopedFD fd_out(raw_fds[0]);
- ScopedFD fd_in(raw_fds[1]);
- if (!SetCloseOnExec(fd_out.get()))
- return false;
- if (!SetCloseOnExec(fd_in.get()))
- return false;
- if (!SetNonBlocking(fd_out.get()))
- return false;
- if (!SetNonBlocking(fd_in.get()))
- return false;
- fds[0] = fd_out.release();
- fds[1] = fd_in.release();
- return true;
-#endif
-}
-
-bool SetNonBlocking(int fd) {
- const int flags = fcntl(fd, F_GETFL);
- if (flags == -1)
- return false;
- if (flags & O_NONBLOCK)
- return true;
- if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1)
- return false;
- return true;
-}
-
-bool SetCloseOnExec(int fd) {
-#if defined(OS_NACL_NONSFI)
- const int flags = 0;
-#else
- const int flags = fcntl(fd, F_GETFD);
- if (flags == -1)
- return false;
- if (flags & FD_CLOEXEC)
- return true;
-#endif // defined(OS_NACL_NONSFI)
- if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1)
- return false;
- return true;
-}
-
-bool PathExists(const FilePath& path) {
- return access(path.value().c_str(), F_OK) == 0;
-}
-
-#if !defined(OS_NACL_NONSFI)
-bool PathIsWritable(const FilePath& path) {
- return access(path.value().c_str(), W_OK) == 0;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
-bool DirectoryExists(const FilePath& path) {
- stat_wrapper_t file_info;
- if (CallStat(path.value().c_str(), &file_info) != 0)
- return false;
- return S_ISDIR(file_info.st_mode);
-}
-
-bool ReadFromFD(int fd, char* buffer, size_t bytes) {
- size_t total_read = 0;
- while (total_read < bytes) {
- ssize_t bytes_read =
- HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
- if (bytes_read <= 0)
- break;
- total_read += bytes_read;
- }
- return total_read == bytes;
-}
-
-#if !defined(OS_NACL_NONSFI)
-
-int CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory,
- FilePath* path) {
- *path = directory.Append(TempFileName());
- const std::string& tmpdir_string = path->value();
- // this should be OK since mkstemp just replaces characters in place
- char* buffer = const_cast<char*>(tmpdir_string.c_str());
-
- return HANDLE_EINTR(mkstemp(buffer));
-}
-
-#if !defined(OS_FUCHSIA)
-bool CreateSymbolicLink(const FilePath& target_path,
- const FilePath& symlink_path) {
- DCHECK(!symlink_path.empty());
- DCHECK(!target_path.empty());
- return ::symlink(target_path.value().c_str(), symlink_path.value().c_str()) !=
- -1;
-}
-
-bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) {
- DCHECK(!symlink_path.empty());
- DCHECK(target_path);
- char buf[PATH_MAX];
- ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf));
-
- if (count <= 0) {
- target_path->clear();
- return false;
- }
-
- *target_path = FilePath(FilePath::StringType(buf, count));
- return true;
-}
-
-bool GetPosixFilePermissions(const FilePath& path, int* mode) {
- DCHECK(mode);
-
- stat_wrapper_t file_info;
- // Uses stat(), because on symbolic link, lstat() does not return valid
- // permission bits in st_mode
- if (CallStat(path.value().c_str(), &file_info) != 0)
- return false;
-
- *mode = file_info.st_mode & FILE_PERMISSION_MASK;
- return true;
-}
-
-bool SetPosixFilePermissions(const FilePath& path, int mode) {
- DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0);
-
- // Calls stat() so that we can preserve the higher bits like S_ISGID.
- stat_wrapper_t stat_buf;
- if (CallStat(path.value().c_str(), &stat_buf) != 0)
- return false;
-
- // Clears the existing permission bits, and adds the new ones.
- mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK;
- updated_mode_bits |= mode & FILE_PERMISSION_MASK;
-
- if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0)
- return false;
-
- return true;
-}
-
-bool ExecutableExistsInPath(Environment* env,
- const FilePath::StringType& executable) {
- std::string path;
- if (!env->GetVar("PATH", &path)) {
- LOG(ERROR) << "No $PATH variable. Assuming no " << executable << ".";
- return false;
- }
-
- for (const StringPiece& cur_path :
- SplitStringPiece(path, ":", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
- FilePath file(cur_path);
- int permissions;
- if (GetPosixFilePermissions(file.Append(executable), &permissions) &&
- (permissions & FILE_PERMISSION_EXECUTE_BY_USER))
- return true;
- }
- return false;
-}
-
-#endif // !OS_FUCHSIA
-
-bool GetTempDir(FilePath* path) {
- const char* tmp = getenv("TMPDIR");
- if (tmp) {
- *path = FilePath(tmp);
- return true;
- }
-
- *path = FilePath("/tmp");
- return true;
-}
-
-#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm.
-FilePath GetHomeDir() {
- const char* home_dir = getenv("HOME");
- if (home_dir && home_dir[0])
- return FilePath(home_dir);
-
- FilePath rv;
- if (GetTempDir(&rv))
- return rv;
-
- // Last resort.
- return FilePath("/tmp");
-}
-#endif // !defined(OS_MACOSX)
-
-bool CreateTemporaryFile(FilePath* path) {
- FilePath directory;
- if (!GetTempDir(&directory))
- return false;
- int fd = CreateAndOpenFdForTemporaryFileInDir(directory, path);
- if (fd < 0)
- return false;
- close(fd);
- return true;
-}
-
-FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
- int fd = CreateAndOpenFdForTemporaryFileInDir(dir, path);
- if (fd < 0)
- return nullptr;
-
- FILE* file = fdopen(fd, "a+");
- if (!file)
- close(fd);
- return file;
-}
-
-bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
- int fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);
- return ((fd >= 0) && !IGNORE_EINTR(close(fd)));
-}
-
-static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
- const FilePath::StringType& name_tmpl,
- FilePath* new_dir) {
- DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
- << "Directory name template must contain \"XXXXXX\".";
-
- FilePath sub_dir = base_dir.Append(name_tmpl);
- std::string sub_dir_string = sub_dir.value();
-
- // this should be OK since mkdtemp just replaces characters in place
- char* buffer = const_cast<char*>(sub_dir_string.c_str());
- char* dtemp = mkdtemp(buffer);
- if (!dtemp) {
- DPLOG(ERROR) << "mkdtemp";
- return false;
- }
- *new_dir = FilePath(dtemp);
- return true;
-}
-
-bool CreateTemporaryDirInDir(const FilePath& base_dir,
- const FilePath::StringType& prefix,
- FilePath* new_dir) {
- FilePath::StringType mkdtemp_template = prefix;
- mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
- return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
-}
-
-bool CreateNewTempDirectory(const FilePath::StringType& prefix,
- FilePath* new_temp_path) {
- FilePath tmpdir;
- if (!GetTempDir(&tmpdir))
- return false;
-
- return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path);
-}
-
-bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
- std::vector<FilePath> subpaths;
-
- // Collect a list of all parent directories.
- FilePath last_path = full_path;
- subpaths.push_back(full_path);
- for (FilePath path = full_path.DirName(); path.value() != last_path.value();
- path = path.DirName()) {
- subpaths.push_back(path);
- last_path = path;
- }
-
- // Iterate through the parents and create the missing ones.
- for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
- i != subpaths.rend(); ++i) {
- if (DirectoryExists(*i))
- continue;
- if (mkdir(i->value().c_str(), 0700) == 0)
- continue;
- // Mkdir failed, but it might have failed with EEXIST, or some other error
- // due to the the directory appearing out of thin air. This can occur if
- // two processes are trying to create the same file system tree at the same
- // time. Check to see if it exists and make sure it is a directory.
- int saved_errno = errno;
- if (!DirectoryExists(*i)) {
- if (error)
- *error = File::OSErrorToFileError(saved_errno);
- return false;
- }
- }
- return true;
-}
-
-bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
- FilePath real_path_result = MakeAbsoluteFilePath(path);
- if (real_path_result.empty())
- return false;
-
- // To be consistant with windows, fail if |real_path_result| is a
- // directory.
- if (DirectoryExists(real_path_result))
- return false;
-
- *normalized_path = real_path_result;
- return true;
-}
-
-// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
-// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948
-bool IsLink(const FilePath& file_path) {
- stat_wrapper_t st;
- // If we can't lstat the file, it's safe to assume that the file won't at
- // least be a 'followable' link.
- if (CallLstat(file_path.value().c_str(), &st) != 0)
- return false;
- return S_ISLNK(st.st_mode);
-}
-
-bool GetFileInfo(const FilePath& file_path, File::Info* results) {
- stat_wrapper_t file_info;
- if (CallStat(file_path.value().c_str(), &file_info) != 0)
- return false;
-
- results->FromStat(file_info);
- return true;
-}
-#endif // !defined(OS_NACL_NONSFI)
-
-FILE* OpenFile(const FilePath& filename, const char* mode) {
- // 'e' is unconditionally added below, so be sure there is not one already
- // present before a comma in |mode|.
- DCHECK(
- strchr(mode, 'e') == nullptr ||
- (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ',')));
- FILE* result = nullptr;
-#if defined(OS_MACOSX)
- // macOS does not provide a mode character to set O_CLOEXEC; see
- // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html.
- const char* the_mode = mode;
-#else
- std::string mode_with_e(AppendModeCharacter(mode, 'e'));
- const char* the_mode = mode_with_e.c_str();
-#endif
- do {
- result = fopen(filename.value().c_str(), the_mode);
- } while (!result && errno == EINTR);
-#if defined(OS_MACOSX)
- // Mark the descriptor as close-on-exec.
- if (result)
- SetCloseOnExec(fileno(result));
-#endif
- return result;
-}
-
-// NaCl doesn't implement system calls to open files directly.
-#if !defined(OS_NACL)
-FILE* FileToFILE(File file, const char* mode) {
- FILE* stream = fdopen(file.GetPlatformFile(), mode);
- if (stream)
- file.TakePlatformFile();
- return stream;
-}
-#endif // !defined(OS_NACL)
-
-int ReadFile(const FilePath& filename, char* data, int max_size) {
- int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
- if (fd < 0)
- return -1;
-
- ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size));
- if (IGNORE_EINTR(close(fd)) < 0)
- return -1;
- return bytes_read;
-}
-
-int WriteFile(const FilePath& filename, const char* data, int size) {
- int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
- if (fd < 0)
- return -1;
-
- int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1;
- if (IGNORE_EINTR(close(fd)) < 0)
- return -1;
- return bytes_written;
-}
-
-bool WriteFileDescriptor(const int fd, const char* data, int size) {
- // Allow for partial writes.
- ssize_t bytes_written_total = 0;
- for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
- bytes_written_total += bytes_written_partial) {
- bytes_written_partial = HANDLE_EINTR(
- write(fd, data + bytes_written_total, size - bytes_written_total));
- if (bytes_written_partial < 0)
- return false;
- }
-
- return true;
-}
-
-#if !defined(OS_NACL_NONSFI)
-
-bool AppendToFile(const FilePath& filename, const char* data, int size) {
- bool ret = true;
- int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND));
- if (fd < 0) {
- return false;
- }
-
- // This call will either write all of the data or return false.
- if (!WriteFileDescriptor(fd, data, size)) {
- ret = false;
- }
-
- if (IGNORE_EINTR(close(fd)) < 0) {
- return false;
- }
-
- return ret;
-}
-
-bool GetCurrentDirectory(FilePath* dir) {
- char system_buffer[PATH_MAX] = "";
- if (!getcwd(system_buffer, sizeof(system_buffer))) {
- NOTREACHED();
- return false;
- }
- *dir = FilePath(system_buffer);
- return true;
-}
-
-bool SetCurrentDirectory(const FilePath& path) {
- return chdir(path.value().c_str()) == 0;
-}
-
-bool VerifyPathControlledByUser(const FilePath& base,
- const FilePath& path,
- uid_t owner_uid,
- const std::set<gid_t>& group_gids) {
- if (base != path && !base.IsParent(path)) {
- DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \""
- << base.value() << "\", path = \"" << path.value() << "\"";
- return false;
- }
-
- std::vector<FilePath::StringType> base_components;
- std::vector<FilePath::StringType> path_components;
-
- base.GetComponents(&base_components);
- path.GetComponents(&path_components);
-
- std::vector<FilePath::StringType>::const_iterator ib, ip;
- for (ib = base_components.begin(), ip = path_components.begin();
- ib != base_components.end(); ++ib, ++ip) {
- // |base| must be a subpath of |path|, so all components should match.
- // If these CHECKs fail, look at the test that base is a parent of
- // path at the top of this function.
- DCHECK(ip != path_components.end());
- DCHECK(*ip == *ib);
- }
-
- FilePath current_path = base;
- if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids))
- return false;
-
- for (; ip != path_components.end(); ++ip) {
- current_path = current_path.Append(*ip);
- if (!VerifySpecificPathControlledByUser(current_path, owner_uid,
- group_gids))
- return false;
- }
- return true;
-}
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-bool VerifyPathControlledByAdmin(const FilePath& path) {
- const unsigned kRootUid = 0;
- const FilePath kFileSystemRoot("/");
-
- // The name of the administrator group on mac os.
- const char* const kAdminGroupNames[] = {"admin", "wheel"};
-
- std::set<gid_t> allowed_group_ids;
- for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) {
- struct group* group_record = getgrnam(kAdminGroupNames[i]);
- if (!group_record) {
- DPLOG(ERROR) << "Could not get the group ID of group \""
- << kAdminGroupNames[i] << "\".";
- continue;
- }
-
- allowed_group_ids.insert(group_record->gr_gid);
- }
-
- return VerifyPathControlledByUser(kFileSystemRoot, path, kRootUid,
- allowed_group_ids);
-}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
-
-int GetMaximumPathComponentLength(const FilePath& path) {
- return pathconf(path.value().c_str(), _PC_NAME_MAX);
-}
-
-bool GetShmemTempDir(bool executable, FilePath* path) {
-#if defined(OS_LINUX) || defined(OS_AIX)
- bool use_dev_shm = true;
- if (executable) {
- static const bool s_dev_shm_executable = DetermineDevShmExecutable();
- use_dev_shm = s_dev_shm_executable;
- }
- if (use_dev_shm) {
- *path = FilePath("/dev/shm");
- return true;
- }
-#endif // defined(OS_LINUX) || defined(OS_AIX)
- return GetTempDir(path);
-}
-
-#if !defined(OS_MACOSX)
-// Mac has its own implementation, this is for all other Posix systems.
-bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
- File infile;
- infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);
- if (!infile.IsValid())
- return false;
-
- File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS);
- if (!outfile.IsValid())
- return false;
-
- return CopyFileContents(&infile, &outfile);
-}
-#endif // !defined(OS_MACOSX)
-
-#endif // !defined(OS_NACL_NONSFI)
-} // namespace base
diff --git a/gn/base/files/file_util_win.cc b/gn/base/files/file_util_win.cc
deleted file mode 100644
index 34d328b5fca..00000000000
--- a/gn/base/files/file_util_win.cc
+++ /dev/null
@@ -1,697 +0,0 @@
-// 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 "base/files/file_util.h"
-
-#include <windows.h>
-
-#include <io.h>
-#include <psapi.h>
-#include <shellapi.h>
-#include <shlobj.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <time.h>
-#include <winsock2.h>
-
-#include <algorithm>
-#include <limits>
-#include <string>
-
-#include "base/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/win/scoped_handle.h"
-
-// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
-// "Community Additions" comment on MSDN here:
-// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
-#define SystemFunction036 NTAPI SystemFunction036
-#include <NTSecAPI.h>
-#undef SystemFunction036
-
-namespace base {
-
-namespace {
-
-const DWORD kFileShareAll =
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
-
-// Deletes all files and directories in a path.
-// Returns ERROR_SUCCESS on success or the Windows error code corresponding to
-// the first error encountered.
-DWORD DeleteFileRecursive(const FilePath& path,
- const FilePath::StringType& pattern,
- bool recursive) {
- FileEnumerator traversal(path, false,
- FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
- pattern);
- DWORD result = ERROR_SUCCESS;
- for (FilePath current = traversal.Next(); !current.empty();
- current = traversal.Next()) {
- // Try to clear the read-only bit if we find it.
- FileEnumerator::FileInfo info = traversal.GetInfo();
- if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
- (recursive || !info.IsDirectory())) {
- ::SetFileAttributes(
- current.value().c_str(),
- info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
- }
-
- DWORD this_result = ERROR_SUCCESS;
- if (info.IsDirectory()) {
- if (recursive) {
- this_result = DeleteFileRecursive(current, pattern, true);
- if (this_result == ERROR_SUCCESS &&
- !::RemoveDirectory(current.value().c_str())) {
- this_result = ::GetLastError();
- }
- }
- } else if (!::DeleteFile(current.value().c_str())) {
- this_result = ::GetLastError();
- }
- if (result == ERROR_SUCCESS)
- result = this_result;
- }
- return result;
-}
-
-// Appends |mode_char| to |mode| before the optional character set encoding; see
-// https://msdn.microsoft.com/library/yeby3zcb.aspx for details.
-void AppendModeCharacter(base::char16 mode_char, base::string16* mode) {
- size_t comma_pos = mode->find(L',');
- mode->insert(comma_pos == base::string16::npos ? mode->length() : comma_pos,
- 1, mode_char);
-}
-
-// Returns ERROR_SUCCESS on success, or a Windows error code on failure.
-DWORD DoDeleteFile(const FilePath& path, bool recursive) {
- if (path.empty())
- return ERROR_SUCCESS;
-
- if (path.value().length() >= MAX_PATH)
- return ERROR_BAD_PATHNAME;
-
- // Handle any path with wildcards.
- if (path.BaseName().value().find_first_of(L"*?") !=
- FilePath::StringType::npos) {
- return DeleteFileRecursive(path.DirName(), path.BaseName().value(),
- recursive);
- }
-
- // Report success if the file or path does not exist.
- const DWORD attr = ::GetFileAttributes(path.value().c_str());
- if (attr == INVALID_FILE_ATTRIBUTES) {
- const DWORD error_code = ::GetLastError();
- return (error_code == ERROR_FILE_NOT_FOUND ||
- error_code == ERROR_PATH_NOT_FOUND)
- ? ERROR_SUCCESS
- : error_code;
- }
-
- // Clear the read-only bit if it is set.
- if ((attr & FILE_ATTRIBUTE_READONLY) &&
- !::SetFileAttributes(path.value().c_str(),
- attr & ~FILE_ATTRIBUTE_READONLY)) {
- return ::GetLastError();
- }
-
- // Perform a simple delete on anything that isn't a directory.
- if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
- return ::DeleteFile(path.value().c_str()) ? ERROR_SUCCESS
- : ::GetLastError();
- }
-
- if (recursive) {
- const DWORD error_code = DeleteFileRecursive(path, L"*", true);
- if (error_code != ERROR_SUCCESS)
- return error_code;
- }
- return ::RemoveDirectory(path.value().c_str()) ? ERROR_SUCCESS
- : ::GetLastError();
-}
-
-std::string RandomDataToGUIDString(const uint64_t bytes[2]) {
- return base::StringPrintf(
- "%08x-%04x-%04x-%04x-%012llx", static_cast<unsigned int>(bytes[0] >> 32),
- static_cast<unsigned int>((bytes[0] >> 16) & 0x0000ffff),
- static_cast<unsigned int>(bytes[0] & 0x0000ffff),
- static_cast<unsigned int>(bytes[1] >> 48),
- bytes[1] & 0x0000ffff'ffffffffULL);
-}
-
-void RandBytes(void* output, size_t output_length) {
- char* output_ptr = static_cast<char*>(output);
- while (output_length > 0) {
- const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min(
- output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max())));
- const bool success =
- RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE;
- CHECK(success);
- output_length -= output_bytes_this_pass;
- output_ptr += output_bytes_this_pass;
- }
-}
-
-std::string GenerateGUID() {
- uint64_t sixteen_bytes[2];
- // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the
- // base version directly, and to prevent the dependency from base/ to crypto/.
- RandBytes(&sixteen_bytes, sizeof(sixteen_bytes));
-
- // Set the GUID to version 4 as described in RFC 4122, section 4.4.
- // The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,
- // where y is one of [8, 9, A, B].
-
- // Clear the version bits and set the version to 4:
- sixteen_bytes[0] &= 0xffffffff'ffff0fffULL;
- sixteen_bytes[0] |= 0x00000000'00004000ULL;
-
- // Set the two most significant bits (bits 6 and 7) of the
- // clock_seq_hi_and_reserved to zero and one, respectively:
- sixteen_bytes[1] &= 0x3fffffff'ffffffffULL;
- sixteen_bytes[1] |= 0x80000000'00000000ULL;
-
- return RandomDataToGUIDString(sixteen_bytes);
-}
-
-} // namespace
-
-FilePath MakeAbsoluteFilePath(const FilePath& input) {
- wchar_t file_path[MAX_PATH];
- if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH))
- return FilePath();
- return FilePath(file_path);
-}
-
-bool DeleteFile(const FilePath& path, bool recursive) {
- static constexpr char kRecursive[] = "DeleteFile.Recursive";
- static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive";
- const StringPiece operation(recursive ? kRecursive : kNonRecursive);
-
- // Metrics for delete failures tracked in https://crbug.com/599084. Delete may
- // fail for a number of reasons. Log some metrics relating to failures in the
- // current code so that any improvements or regressions resulting from
- // subsequent code changes can be detected.
- const DWORD error = DoDeleteFile(path, recursive);
- return error == ERROR_SUCCESS;
-}
-
-bool DeleteFileAfterReboot(const FilePath& path) {
- if (path.value().length() >= MAX_PATH)
- return false;
-
- return MoveFileEx(path.value().c_str(), NULL,
- MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING) !=
- FALSE;
-}
-
-bool ReplaceFile(const FilePath& from_path,
- const FilePath& to_path,
- File::Error* error) {
- // Try a simple move first. It will only succeed when |to_path| doesn't
- // already exist.
- if (::MoveFile(from_path.value().c_str(), to_path.value().c_str()))
- return true;
- File::Error move_error = File::OSErrorToFileError(GetLastError());
-
- // Try the full-blown replace if the move fails, as ReplaceFile will only
- // succeed when |to_path| does exist. When writing to a network share, we may
- // not be able to change the ACLs. Ignore ACL errors then
- // (REPLACEFILE_IGNORE_MERGE_ERRORS).
- if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
- REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
- return true;
- }
- // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
- // |to_path| does not exist. In this case, the more relevant error comes
- // from the call to MoveFile.
- if (error) {
- File::Error replace_error = File::OSErrorToFileError(GetLastError());
- *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error
- : replace_error;
- }
- return false;
-}
-
-bool PathExists(const FilePath& path) {
- return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
-}
-
-bool PathIsWritable(const FilePath& path) {
- HANDLE dir =
- CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-
- if (dir == INVALID_HANDLE_VALUE)
- return false;
-
- CloseHandle(dir);
- return true;
-}
-
-bool DirectoryExists(const FilePath& path) {
- DWORD fileattr = GetFileAttributes(path.value().c_str());
- if (fileattr != INVALID_FILE_ATTRIBUTES)
- return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
- return false;
-}
-
-bool GetTempDir(FilePath* path) {
- wchar_t temp_path[MAX_PATH + 1];
- DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
- if (path_len >= MAX_PATH || path_len <= 0)
- return false;
- // TODO(evanm): the old behavior of this function was to always strip the
- // trailing slash. We duplicate this here, but it shouldn't be necessary
- // when everyone is using the appropriate FilePath APIs.
- *path = FilePath(temp_path).StripTrailingSeparators();
- return true;
-}
-
-bool CreateTemporaryFile(FilePath* path) {
- FilePath temp_file;
-
- if (!GetTempDir(path))
- return false;
-
- if (CreateTemporaryFileInDir(*path, &temp_file)) {
- *path = temp_file;
- return true;
- }
-
- return false;
-}
-
-// On POSIX we have semantics to create and open a temporary file
-// atomically.
-// TODO(jrg): is there equivalent call to use on Windows instead of
-// going 2-step?
-FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
- if (!CreateTemporaryFileInDir(dir, path)) {
- return NULL;
- }
- // Open file in binary mode, to avoid problems with fwrite. On Windows
- // it replaces \n's with \r\n's, which may surprise you.
- // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
- return OpenFile(*path, "wb+");
-}
-
-bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
- // Use GUID instead of ::GetTempFileName() to generate unique file names.
- // "Due to the algorithm used to generate file names, GetTempFileName can
- // perform poorly when creating a large number of files with the same prefix.
- // In such cases, it is recommended that you construct unique file names based
- // on GUIDs."
- // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx
-
- FilePath temp_name;
- bool create_file_success = false;
-
- // Although it is nearly impossible to get a duplicate name with GUID, we
- // still use a loop here in case it happens.
- for (int i = 0; i < 100; ++i) {
- temp_name = dir.Append(ASCIIToUTF16(base::GenerateGUID()) + L".tmp");
- File file(temp_name,
- File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
- if (file.IsValid()) {
- file.Close();
- create_file_success = true;
- break;
- }
- }
-
- if (!create_file_success) {
- DPLOG(WARNING) << "Failed to get temporary file name in "
- << UTF16ToUTF8(dir.value());
- return false;
- }
-
- wchar_t long_temp_name[MAX_PATH + 1];
- DWORD long_name_len =
- GetLongPathName(temp_name.value().c_str(), long_temp_name, MAX_PATH);
- if (long_name_len > MAX_PATH || long_name_len == 0) {
- // GetLongPathName() failed, but we still have a temporary file.
- *temp_file = std::move(temp_name);
- return true;
- }
-
- FilePath::StringType long_temp_name_str;
- long_temp_name_str.assign(long_temp_name, long_name_len);
- *temp_file = FilePath(std::move(long_temp_name_str));
- return true;
-}
-
-bool CreateTemporaryDirInDir(const FilePath& base_dir,
- const FilePath::StringType& prefix,
- FilePath* new_dir) {
- FilePath path_to_create;
-
- for (int count = 0; count < 50; ++count) {
- // Try create a new temporary directory with random generated name. If
- // the one exists, keep trying another path name until we reach some limit.
- string16 new_dir_name;
- new_dir_name.assign(prefix);
- new_dir_name.append(IntToString16(::GetCurrentProcessId()));
- new_dir_name.push_back('_');
- new_dir_name.append(UTF8ToUTF16(GenerateGUID()));
-
- path_to_create = base_dir.Append(new_dir_name);
- if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
- *new_dir = path_to_create;
- return true;
- }
- }
-
- return false;
-}
-
-bool CreateNewTempDirectory(const FilePath::StringType& prefix,
- FilePath* new_temp_path) {
- FilePath system_temp_dir;
- if (!GetTempDir(&system_temp_dir))
- return false;
-
- return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
-}
-
-bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
- // If the path exists, we've succeeded if it's a directory, failed otherwise.
- const wchar_t* full_path_str = full_path.value().c_str();
- DWORD fileattr = ::GetFileAttributes(full_path_str);
- if (fileattr != INVALID_FILE_ATTRIBUTES) {
- if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- return true;
- }
- DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
- << "conflicts with existing file.";
- if (error) {
- *error = File::FILE_ERROR_NOT_A_DIRECTORY;
- }
- return false;
- }
-
- // Invariant: Path does not exist as file or directory.
-
- // Attempt to create the parent recursively. This will immediately return
- // true if it already exists, otherwise will create all required parent
- // directories starting with the highest-level missing parent.
- FilePath parent_path(full_path.DirName());
- if (parent_path.value() == full_path.value()) {
- if (error) {
- *error = File::FILE_ERROR_NOT_FOUND;
- }
- return false;
- }
- if (!CreateDirectoryAndGetError(parent_path, error)) {
- DLOG(WARNING) << "Failed to create one of the parent directories.";
- if (error) {
- DCHECK(*error != File::FILE_OK);
- }
- return false;
- }
-
- if (!::CreateDirectory(full_path_str, NULL)) {
- DWORD error_code = ::GetLastError();
- if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
- // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we
- // were racing with someone creating the same directory, or a file
- // with the same path. If DirectoryExists() returns true, we lost the
- // race to create the same directory.
- return true;
- } else {
- if (error)
- *error = File::OSErrorToFileError(error_code);
- DLOG(WARNING) << "Failed to create directory " << full_path_str
- << ", last error is " << error_code << ".";
- return false;
- }
- } else {
- return true;
- }
-}
-
-bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
- FilePath mapped_file;
- if (!NormalizeToNativeFilePath(path, &mapped_file))
- return false;
- // NormalizeToNativeFilePath() will return a path that starts with
- // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath()
- // will find a drive letter which maps to the path's device, so
- // that we return a path starting with a drive letter.
- return DevicePathToDriveLetterPath(mapped_file, real_path);
-}
-
-bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
- FilePath* out_drive_letter_path) {
- // Get the mapping of drive letters to device paths.
- const int kDriveMappingSize = 1024;
- wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
- if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
- DLOG(ERROR) << "Failed to get drive mapping.";
- return false;
- }
-
- // The drive mapping is a sequence of null terminated strings.
- // The last string is empty.
- wchar_t* drive_map_ptr = drive_mapping;
- wchar_t device_path_as_string[MAX_PATH];
- wchar_t drive[] = L" :";
-
- // For each string in the drive mapping, get the junction that links
- // to it. If that junction is a prefix of |device_path|, then we
- // know that |drive| is the real path prefix.
- while (*drive_map_ptr) {
- drive[0] = drive_map_ptr[0]; // Copy the drive letter.
-
- if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
- FilePath device_path(device_path_as_string);
- if (device_path == nt_device_path ||
- device_path.IsParent(nt_device_path)) {
- *out_drive_letter_path =
- FilePath(drive + nt_device_path.value().substr(
- wcslen(device_path_as_string)));
- return true;
- }
- }
- // Move to the next drive letter string, which starts one
- // increment after the '\0' that terminates the current string.
- while (*drive_map_ptr++) {
- }
- }
-
- // No drive matched. The path does not start with a device junction
- // that is mounted as a drive letter. This means there is no drive
- // letter path to the volume that holds |device_path|, so fail.
- return false;
-}
-
-bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
- // In Vista, GetFinalPathNameByHandle() would give us the real path
- // from a file handle. If we ever deprecate XP, consider changing the
- // code below to a call to GetFinalPathNameByHandle(). The method this
- // function uses is explained in the following msdn article:
- // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
- win::ScopedHandle file_handle(::CreateFile(path.value().c_str(), GENERIC_READ,
- kFileShareAll, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL));
- if (!file_handle.IsValid())
- return false;
-
- // Create a file mapping object. Can't easily use MemoryMappedFile, because
- // we only map the first byte, and need direct access to the handle. You can
- // not map an empty file, this call fails in that case.
- win::ScopedHandle file_map_handle(
- ::CreateFileMapping(file_handle.Get(), NULL, PAGE_READONLY, 0,
- 1, // Just one byte. No need to look at the data.
- NULL));
- if (!file_map_handle.IsValid())
- return false;
-
- // Use a view of the file to get the path to the file.
- void* file_view =
- MapViewOfFile(file_map_handle.Get(), FILE_MAP_READ, 0, 0, 1);
- if (!file_view)
- return false;
-
- // The expansion of |path| into a full path may make it longer.
- // GetMappedFileName() will fail if the result is longer than MAX_PATH.
- // Pad a bit to be safe. If kMaxPathLength is ever changed to be less
- // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
- // not return kMaxPathLength. This would mean that only part of the
- // path fit in |mapped_file_path|.
- const int kMaxPathLength = MAX_PATH + 10;
- wchar_t mapped_file_path[kMaxPathLength];
- bool success = false;
- HANDLE cp = GetCurrentProcess();
- if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
- *nt_path = FilePath(mapped_file_path);
- success = true;
- }
- ::UnmapViewOfFile(file_view);
- return success;
-}
-
-// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
-// them if we do decide to.
-bool IsLink(const FilePath& file_path) {
- return false;
-}
-
-bool GetFileInfo(const FilePath& file_path, File::Info* results) {
- WIN32_FILE_ATTRIBUTE_DATA attr;
- if (!GetFileAttributesEx(file_path.value().c_str(), GetFileExInfoStandard,
- &attr)) {
- return false;
- }
-
- ULARGE_INTEGER size;
- size.HighPart = attr.nFileSizeHigh;
- size.LowPart = attr.nFileSizeLow;
- results->size = size.QuadPart;
-
- results->is_directory =
- (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- results->last_modified = *reinterpret_cast<uint64_t*>(&attr.ftLastWriteTime);
- results->last_accessed = *reinterpret_cast<uint64_t*>(&attr.ftLastAccessTime);
- results->creation_time = *reinterpret_cast<uint64_t*>(&attr.ftCreationTime);
-
- return true;
-}
-
-FILE* OpenFile(const FilePath& filename, const char* mode) {
- // 'N' is unconditionally added below, so be sure there is not one already
- // present before a comma in |mode|.
- DCHECK(
- strchr(mode, 'N') == nullptr ||
- (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ',')));
- string16 w_mode = ASCIIToUTF16(mode);
- AppendModeCharacter(L'N', &w_mode);
- return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
-}
-
-FILE* FileToFILE(File file, const char* mode) {
- if (!file.IsValid())
- return NULL;
- int fd =
- _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0);
- if (fd < 0)
- return NULL;
- file.TakePlatformFile();
- FILE* stream = _fdopen(fd, mode);
- if (!stream)
- _close(fd);
- return stream;
-}
-
-int ReadFile(const FilePath& filename, char* data, int max_size) {
- win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
- OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
- NULL));
- if (!file.IsValid())
- return -1;
-
- DWORD read;
- if (::ReadFile(file.Get(), data, max_size, &read, NULL))
- return read;
-
- return -1;
-}
-
-int WriteFile(const FilePath& filename, const char* data, int size) {
- win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0,
- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- NULL));
- if (!file.IsValid()) {
- DPLOG(WARNING) << "CreateFile failed for path "
- << UTF16ToUTF8(filename.value());
- return -1;
- }
-
- DWORD written;
- BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
- if (result && static_cast<int>(written) == size)
- return written;
-
- if (!result) {
- // WriteFile failed.
- DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value())
- << " failed";
- } else {
- // Didn't write all the bytes.
- DLOG(WARNING) << "wrote" << written << " bytes to "
- << UTF16ToUTF8(filename.value()) << " expected " << size;
- }
- return -1;
-}
-
-bool AppendToFile(const FilePath& filename, const char* data, int size) {
- win::ScopedHandle file(CreateFile(filename.value().c_str(), FILE_APPEND_DATA,
- 0, NULL, OPEN_EXISTING, 0, NULL));
- if (!file.IsValid()) {
- return false;
- }
-
- DWORD written;
- BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
- if (result && static_cast<int>(written) == size)
- return true;
-
- return false;
-}
-
-bool GetCurrentDirectory(FilePath* dir) {
- wchar_t system_buffer[MAX_PATH];
- system_buffer[0] = 0;
- DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
- if (len == 0 || len > MAX_PATH)
- return false;
- // TODO(evanm): the old behavior of this function was to always strip the
- // trailing slash. We duplicate this here, but it shouldn't be necessary
- // when everyone is using the appropriate FilePath APIs.
- std::wstring dir_str(system_buffer);
- *dir = FilePath(dir_str).StripTrailingSeparators();
- return true;
-}
-
-bool SetCurrentDirectory(const FilePath& directory) {
- return ::SetCurrentDirectory(directory.value().c_str()) != 0;
-}
-
-int GetMaximumPathComponentLength(const FilePath& path) {
- wchar_t volume_path[MAX_PATH];
- if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
- volume_path, arraysize(volume_path))) {
- return -1;
- }
-
- DWORD max_length = 0;
- if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL,
- NULL, 0)) {
- return -1;
- }
-
- // Length of |path| with path separator appended.
- size_t prefix = path.StripTrailingSeparators().value().size() + 1;
- // The whole path string must be shorter than MAX_PATH. That is, it must be
- // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1).
- int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix));
- return std::min(whole_path_limit, static_cast<int>(max_length));
-}
-
-bool SetNonBlocking(int fd) {
- unsigned long nonblocking = 1;
- if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0)
- return true;
- return false;
-}
-
-} // namespace base
diff --git a/gn/base/files/file_win.cc b/gn/base/files/file_win.cc
deleted file mode 100644
index 7ca4461e94f..00000000000
--- a/gn/base/files/file_win.cc
+++ /dev/null
@@ -1,381 +0,0 @@
-// 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 "base/files/file.h"
-
-#include <io.h>
-#include <stdint.h>
-
-#include "base/logging.h"
-
-#include <windows.h>
-
-namespace base {
-
-// Make sure our Whence mappings match the system headers.
-static_assert(File::FROM_BEGIN == FILE_BEGIN &&
- File::FROM_CURRENT == FILE_CURRENT &&
- File::FROM_END == FILE_END,
- "whence mapping must match the system headers");
-
-bool File::IsValid() const {
- return file_.IsValid();
-}
-
-PlatformFile File::GetPlatformFile() const {
- return file_.Get();
-}
-
-PlatformFile File::TakePlatformFile() {
- return file_.Take();
-}
-
-void File::Close() {
- if (!file_.IsValid())
- return;
-
- file_.Close();
-}
-
-int64_t File::Seek(Whence whence, int64_t offset) {
- DCHECK(IsValid());
-
- LARGE_INTEGER distance, res;
- distance.QuadPart = offset;
- DWORD move_method = static_cast<DWORD>(whence);
- if (!SetFilePointerEx(file_.Get(), distance, &res, move_method))
- return -1;
- return res.QuadPart;
-}
-
-int File::Read(int64_t offset, char* data, int size) {
- DCHECK(IsValid());
- DCHECK(!async_);
- if (size < 0)
- return -1;
-
- LARGE_INTEGER offset_li;
- offset_li.QuadPart = offset;
-
- OVERLAPPED overlapped = {0};
- overlapped.Offset = offset_li.LowPart;
- overlapped.OffsetHigh = offset_li.HighPart;
-
- DWORD bytes_read;
- if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped))
- return bytes_read;
- if (ERROR_HANDLE_EOF == GetLastError())
- return 0;
-
- return -1;
-}
-
-int File::ReadAtCurrentPos(char* data, int size) {
- DCHECK(IsValid());
- DCHECK(!async_);
- if (size < 0)
- return -1;
-
- DWORD bytes_read;
- if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL))
- return bytes_read;
- if (ERROR_HANDLE_EOF == GetLastError())
- return 0;
-
- return -1;
-}
-
-int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
- // TODO(dbeam): trace this separately?
- return Read(offset, data, size);
-}
-
-int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
- // TODO(dbeam): trace this separately?
- return ReadAtCurrentPos(data, size);
-}
-
-int File::Write(int64_t offset, const char* data, int size) {
- DCHECK(IsValid());
- DCHECK(!async_);
-
- LARGE_INTEGER offset_li;
- offset_li.QuadPart = offset;
-
- OVERLAPPED overlapped = {0};
- overlapped.Offset = offset_li.LowPart;
- overlapped.OffsetHigh = offset_li.HighPart;
-
- DWORD bytes_written;
- if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped))
- return bytes_written;
-
- return -1;
-}
-
-int File::WriteAtCurrentPos(const char* data, int size) {
- DCHECK(IsValid());
- DCHECK(!async_);
- if (size < 0)
- return -1;
-
- DWORD bytes_written;
- if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL))
- return bytes_written;
-
- return -1;
-}
-
-int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
- return WriteAtCurrentPos(data, size);
-}
-
-int64_t File::GetLength() {
- DCHECK(IsValid());
-
- LARGE_INTEGER size;
- if (!::GetFileSizeEx(file_.Get(), &size))
- return -1;
-
- return static_cast<int64_t>(size.QuadPart);
-}
-
-bool File::SetLength(int64_t length) {
- DCHECK(IsValid());
-
- // Get the current file pointer.
- LARGE_INTEGER file_pointer;
- LARGE_INTEGER zero;
- zero.QuadPart = 0;
- if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT))
- return false;
-
- LARGE_INTEGER length_li;
- length_li.QuadPart = length;
- // If length > file size, SetFilePointerEx() should extend the file
- // with zeroes on all Windows standard file systems (NTFS, FATxx).
- if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN))
- return false;
-
- // Set the new file length and move the file pointer to its old position.
- // This is consistent with ftruncate()'s behavior, even when the file
- // pointer points to a location beyond the end of the file.
- // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
- // promised by the interface (nor was promised by PlatformFile). See if this
- // implementation detail can be removed.
- return ((::SetEndOfFile(file_.Get()) != FALSE) &&
- (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) !=
- FALSE));
-}
-
-bool File::GetInfo(Info* info) {
- DCHECK(IsValid());
-
- BY_HANDLE_FILE_INFORMATION file_info;
- if (!GetFileInformationByHandle(file_.Get(), &file_info))
- return false;
-
- LARGE_INTEGER size;
- size.HighPart = file_info.nFileSizeHigh;
- size.LowPart = file_info.nFileSizeLow;
- info->size = size.QuadPart;
- info->is_directory =
- (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- info->is_symbolic_link = false; // Windows doesn't have symbolic links.
- info->last_modified =
- *reinterpret_cast<uint64_t*>(&file_info.ftLastWriteTime);
- info->last_accessed =
- *reinterpret_cast<uint64_t*>(&file_info.ftLastAccessTime);
- info->creation_time = *reinterpret_cast<uint64_t*>(&file_info.ftCreationTime);
- return true;
-}
-
-File::Error File::Lock() {
- DCHECK(IsValid());
-
- BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
- if (!result)
- return GetLastFileError();
- return FILE_OK;
-}
-
-File::Error File::Unlock() {
- DCHECK(IsValid());
-
- BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
- if (!result)
- return GetLastFileError();
- return FILE_OK;
-}
-
-File File::Duplicate() const {
- if (!IsValid())
- return File();
-
- HANDLE other_handle = nullptr;
-
- if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
- GetPlatformFile(),
- GetCurrentProcess(), // hTargetProcessHandle
- &other_handle,
- 0, // dwDesiredAccess ignored due to SAME_ACCESS
- FALSE, // !bInheritHandle
- DUPLICATE_SAME_ACCESS)) {
- return File(GetLastFileError());
- }
-
- File other(other_handle);
- if (async())
- other.async_ = true;
- return other;
-}
-
-bool File::DeleteOnClose(bool delete_on_close) {
- FILE_DISPOSITION_INFO disposition = {delete_on_close ? TRUE : FALSE};
- return ::SetFileInformationByHandle(GetPlatformFile(), FileDispositionInfo,
- &disposition, sizeof(disposition)) != 0;
-}
-
-// Static.
-File::Error File::OSErrorToFileError(DWORD last_error) {
- switch (last_error) {
- case ERROR_SHARING_VIOLATION:
- return FILE_ERROR_IN_USE;
- case ERROR_ALREADY_EXISTS:
- case ERROR_FILE_EXISTS:
- return FILE_ERROR_EXISTS;
- case ERROR_FILE_NOT_FOUND:
- case ERROR_PATH_NOT_FOUND:
- return FILE_ERROR_NOT_FOUND;
- case ERROR_ACCESS_DENIED:
- return FILE_ERROR_ACCESS_DENIED;
- case ERROR_TOO_MANY_OPEN_FILES:
- return FILE_ERROR_TOO_MANY_OPENED;
- case ERROR_OUTOFMEMORY:
- case ERROR_NOT_ENOUGH_MEMORY:
- return FILE_ERROR_NO_MEMORY;
- case ERROR_HANDLE_DISK_FULL:
- case ERROR_DISK_FULL:
- case ERROR_DISK_RESOURCES_EXHAUSTED:
- return FILE_ERROR_NO_SPACE;
- case ERROR_USER_MAPPED_FILE:
- return FILE_ERROR_INVALID_OPERATION;
- case ERROR_NOT_READY:
- case ERROR_SECTOR_NOT_FOUND:
- case ERROR_DEV_NOT_EXIST:
- case ERROR_IO_DEVICE:
- case ERROR_FILE_CORRUPT:
- case ERROR_DISK_CORRUPT:
- return FILE_ERROR_IO;
- default:
- // This function should only be called for errors.
- DCHECK_NE(static_cast<DWORD>(ERROR_SUCCESS), last_error);
- return FILE_ERROR_FAILED;
- }
-}
-
-void File::DoInitialize(const FilePath& path, uint32_t flags) {
- DCHECK(!IsValid());
-
- DWORD disposition = 0;
-
- if (flags & FLAG_OPEN)
- disposition = OPEN_EXISTING;
-
- if (flags & FLAG_CREATE) {
- DCHECK(!disposition);
- disposition = CREATE_NEW;
- }
-
- if (flags & FLAG_OPEN_ALWAYS) {
- DCHECK(!disposition);
- disposition = OPEN_ALWAYS;
- }
-
- if (flags & FLAG_CREATE_ALWAYS) {
- DCHECK(!disposition);
- DCHECK(flags & FLAG_WRITE);
- disposition = CREATE_ALWAYS;
- }
-
- if (flags & FLAG_OPEN_TRUNCATED) {
- DCHECK(!disposition);
- DCHECK(flags & FLAG_WRITE);
- disposition = TRUNCATE_EXISTING;
- }
-
- if (!disposition) {
- ::SetLastError(ERROR_INVALID_PARAMETER);
- error_details_ = FILE_ERROR_FAILED;
- NOTREACHED();
- return;
- }
-
- DWORD access = 0;
- if (flags & FLAG_WRITE)
- access = GENERIC_WRITE;
- if (flags & FLAG_APPEND) {
- DCHECK(!access);
- access = FILE_APPEND_DATA;
- }
- if (flags & FLAG_READ)
- access |= GENERIC_READ;
- if (flags & FLAG_WRITE_ATTRIBUTES)
- access |= FILE_WRITE_ATTRIBUTES;
- if (flags & FLAG_EXECUTE)
- access |= GENERIC_EXECUTE;
- if (flags & FLAG_CAN_DELETE_ON_CLOSE)
- access |= DELETE;
-
- DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
- if (!(flags & FLAG_EXCLUSIVE_WRITE))
- sharing |= FILE_SHARE_WRITE;
- if (flags & FLAG_SHARE_DELETE)
- sharing |= FILE_SHARE_DELETE;
-
- DWORD create_flags = 0;
- if (flags & FLAG_ASYNC)
- create_flags |= FILE_FLAG_OVERLAPPED;
- if (flags & FLAG_TEMPORARY)
- create_flags |= FILE_ATTRIBUTE_TEMPORARY;
- if (flags & FLAG_HIDDEN)
- create_flags |= FILE_ATTRIBUTE_HIDDEN;
- if (flags & FLAG_DELETE_ON_CLOSE)
- create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
- if (flags & FLAG_BACKUP_SEMANTICS)
- create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
- if (flags & FLAG_SEQUENTIAL_SCAN)
- create_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
-
- file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL, disposition,
- create_flags, NULL));
-
- if (file_.IsValid()) {
- error_details_ = FILE_OK;
- async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
-
- if (flags & (FLAG_OPEN_ALWAYS))
- created_ = (ERROR_ALREADY_EXISTS != GetLastError());
- else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
- created_ = true;
- } else {
- error_details_ = GetLastFileError();
- }
-}
-
-bool File::Flush() {
- DCHECK(IsValid());
- return ::FlushFileBuffers(file_.Get()) != FALSE;
-}
-
-void File::SetPlatformFile(PlatformFile file) {
- file_.Set(file);
-}
-
-// static
-File::Error File::GetLastFileError() {
- return File::OSErrorToFileError(GetLastError());
-}
-
-} // namespace base
diff --git a/gn/base/files/platform_file.h b/gn/base/files/platform_file.h
deleted file mode 100644
index cba2fc8ad6c..00000000000
--- a/gn/base/files/platform_file.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 The Chromium 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 BASE_FILES_PLATFORM_FILE_H_
-#define BASE_FILES_PLATFORM_FILE_H_
-
-#include "base/files/scoped_file.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_handle.h"
-#include "base/win/windows_types.h"
-#endif
-
-// This file defines platform-independent types for dealing with
-// platform-dependent files. If possible, use the higher-level base::File class
-// rather than these primitives.
-
-namespace base {
-
-#if defined(OS_WIN)
-
-using PlatformFile = HANDLE;
-using ScopedPlatformFile = ::base::win::ScopedHandle;
-
-// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a
-// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically
-// disallowed in constexpr. Visual Studio accepts this, however.
-const PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE;
-
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-using PlatformFile = int;
-using ScopedPlatformFile = ::base::ScopedFD;
-
-constexpr PlatformFile kInvalidPlatformFile = -1;
-
-#endif
-
-} // namespace base
-
-#endif // BASE_FILES_PLATFORM_FILE_H_
diff --git a/gn/base/files/scoped_file.cc b/gn/base/files/scoped_file.cc
deleted file mode 100644
index 11afedd695e..00000000000
--- a/gn/base/files/scoped_file.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 "base/files/scoped_file.h"
-
-#include "base/logging.h"
-#include "util/build_config.h"
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <errno.h>
-#include <unistd.h>
-
-#include "base/posix/eintr_wrapper.h"
-#endif
-
-namespace base {
-namespace internal {
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-
-// static
-void ScopedFDCloseTraits::Free(int fd) {
- // It's important to crash here.
- // There are security implications to not closing a file descriptor
- // properly. As file descriptors are "capabilities", keeping them open
- // would make the current process keep access to a resource. Much of
- // Chrome relies on being able to "drop" such access.
- // It's especially problematic on Linux with the setuid sandbox, where
- // a single open directory would bypass the entire security model.
- int ret = IGNORE_EINTR(close(fd));
-
-#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FUCHSIA) || \
- defined(OS_ANDROID)
- // NB: Some file descriptors can return errors from close() e.g. network
- // filesystems such as NFS and Linux input devices. On Linux, macOS, and
- // Fuchsia's POSIX layer, errors from close other than EBADF do not indicate
- // failure to actually close the fd.
- if (ret != 0 && errno != EBADF)
- ret = 0;
-#endif
-
- PCHECK(0 == ret);
-}
-
-#endif // OS_POSIX || OS_FUCHSIA
-
-} // namespace internal
-} // namespace base
diff --git a/gn/base/gtest_prod_util.h b/gn/base/gtest_prod_util.h
deleted file mode 100644
index 664dded781f..00000000000
--- a/gn/base/gtest_prod_util.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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 BASE_GTEST_PROD_UTIL_H_
-#define BASE_GTEST_PROD_UTIL_H_
-
-// TODO: Remove me.
-#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
- friend class test_case_name##test_name
-
-// TODO: Remove me.
-#define FORWARD_DECLARE_TEST(test_case_name, test_name) \
- class test_case_name##test_name
-
-#endif // BASE_GTEST_PROD_UTIL_H_
diff --git a/gn/base/json/json_parser.cc b/gn/base/json/json_parser.cc
deleted file mode 100644
index 713f69223b1..00000000000
--- a/gn/base/json/json_parser.cc
+++ /dev/null
@@ -1,747 +0,0 @@
-// 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 "base/json/json_parser.h"
-
-#include <cmath>
-#include <utility>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversion_utils.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/third_party/icu/icu_utf.h"
-#include "base/values.h"
-
-namespace base {
-namespace internal {
-
-namespace {
-
-const int32_t kExtendedASCIIStart = 0x80;
-
-// Simple class that checks for maximum recursion/"stack overflow."
-class StackMarker {
- public:
- StackMarker(int max_depth, int* depth)
- : max_depth_(max_depth), depth_(depth) {
- ++(*depth_);
- DCHECK_LE(*depth_, max_depth_);
- }
- ~StackMarker() { --(*depth_); }
-
- bool IsTooDeep() const { return *depth_ >= max_depth_; }
-
- private:
- const int max_depth_;
- int* const depth_;
-
- DISALLOW_COPY_AND_ASSIGN(StackMarker);
-};
-
-constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD;
-
-} // namespace
-
-// This is U+FFFD.
-const char kUnicodeReplacementString[] = "\xEF\xBF\xBD";
-
-JSONParser::JSONParser(int options, int max_depth)
- : options_(options),
- max_depth_(max_depth),
- index_(0),
- stack_depth_(0),
- line_number_(0),
- index_last_line_(0),
- error_code_(JSONReader::JSON_NO_ERROR),
- error_line_(0),
- error_column_(0) {
- CHECK_LE(max_depth, JSONReader::kStackMaxDepth);
-}
-
-JSONParser::~JSONParser() = default;
-
-Optional<Value> JSONParser::Parse(StringPiece input) {
- input_ = input;
- index_ = 0;
- line_number_ = 1;
- index_last_line_ = 0;
-
- error_code_ = JSONReader::JSON_NO_ERROR;
- error_line_ = 0;
- error_column_ = 0;
-
- // ICU and ReadUnicodeCharacter() use int32_t for lengths, so ensure
- // that the index_ will not overflow when parsing.
- if (!base::IsValueInRangeForNumericType<int32_t>(input.length())) {
- ReportError(JSONReader::JSON_TOO_LARGE, 0);
- return nullopt;
- }
-
- // When the input JSON string starts with a UTF-8 Byte-Order-Mark,
- // advance the start position to avoid the ParseNextToken function mis-
- // treating a Unicode BOM as an invalid character and returning NULL.
- ConsumeIfMatch("\xEF\xBB\xBF");
-
- // Parse the first and any nested tokens.
- Optional<Value> root(ParseNextToken());
- if (!root)
- return nullopt;
-
- // Make sure the input stream is at an end.
- if (GetNextToken() != T_END_OF_INPUT) {
- ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1);
- return nullopt;
- }
-
- return root;
-}
-
-JSONReader::JsonParseError JSONParser::error_code() const {
- return error_code_;
-}
-
-std::string JSONParser::GetErrorMessage() const {
- return FormatErrorMessage(error_line_, error_column_,
- JSONReader::ErrorCodeToString(error_code_));
-}
-
-int JSONParser::error_line() const {
- return error_line_;
-}
-
-int JSONParser::error_column() const {
- return error_column_;
-}
-
-// StringBuilder ///////////////////////////////////////////////////////////////
-
-JSONParser::StringBuilder::StringBuilder() : StringBuilder(nullptr) {}
-
-JSONParser::StringBuilder::StringBuilder(const char* pos)
- : pos_(pos), length_(0) {}
-
-JSONParser::StringBuilder::~StringBuilder() = default;
-
-JSONParser::StringBuilder& JSONParser::StringBuilder::operator=(
- StringBuilder&& other) = default;
-
-void JSONParser::StringBuilder::Append(uint32_t point) {
- DCHECK(IsValidCharacter(point));
-
- if (point < kExtendedASCIIStart && !string_) {
- DCHECK_EQ(static_cast<char>(point), pos_[length_]);
- ++length_;
- } else {
- Convert();
- if (UNLIKELY(point == kUnicodeReplacementPoint)) {
- string_->append(kUnicodeReplacementString);
- } else {
- WriteUnicodeCharacter(point, &*string_);
- }
- }
-}
-
-void JSONParser::StringBuilder::Convert() {
- if (string_)
- return;
- string_.emplace(pos_, length_);
-}
-
-std::string JSONParser::StringBuilder::DestructiveAsString() {
- if (string_)
- return std::move(*string_);
- return std::string(pos_, length_);
-}
-
-// JSONParser private //////////////////////////////////////////////////////////
-
-Optional<StringPiece> JSONParser::PeekChars(int count) {
- if (static_cast<size_t>(index_) + count > input_.length())
- return nullopt;
- // Using StringPiece::substr() is significantly slower (according to
- // base_perftests) than constructing a substring manually.
- return StringPiece(input_.data() + index_, count);
-}
-
-Optional<char> JSONParser::PeekChar() {
- Optional<StringPiece> chars = PeekChars(1);
- if (chars)
- return (*chars)[0];
- return nullopt;
-}
-
-Optional<StringPiece> JSONParser::ConsumeChars(int count) {
- Optional<StringPiece> chars = PeekChars(count);
- if (chars)
- index_ += count;
- return chars;
-}
-
-Optional<char> JSONParser::ConsumeChar() {
- Optional<StringPiece> chars = ConsumeChars(1);
- if (chars)
- return (*chars)[0];
- return nullopt;
-}
-
-const char* JSONParser::pos() {
- CHECK_LE(static_cast<size_t>(index_), input_.length());
- return input_.data() + index_;
-}
-
-JSONParser::Token JSONParser::GetNextToken() {
- EatWhitespaceAndComments();
-
- Optional<char> c = PeekChar();
- if (!c)
- return T_END_OF_INPUT;
-
- switch (*c) {
- case '{':
- return T_OBJECT_BEGIN;
- case '}':
- return T_OBJECT_END;
- case '[':
- return T_ARRAY_BEGIN;
- case ']':
- return T_ARRAY_END;
- case '"':
- return T_STRING;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '-':
- return T_NUMBER;
- case 't':
- return T_BOOL_TRUE;
- case 'f':
- return T_BOOL_FALSE;
- case 'n':
- return T_NULL;
- case ',':
- return T_LIST_SEPARATOR;
- case ':':
- return T_OBJECT_PAIR_SEPARATOR;
- default:
- return T_INVALID_TOKEN;
- }
-}
-
-void JSONParser::EatWhitespaceAndComments() {
- while (Optional<char> c = PeekChar()) {
- switch (*c) {
- case '\r':
- case '\n':
- index_last_line_ = index_;
- // Don't increment line_number_ twice for "\r\n".
- if (!(c == '\n' && index_ > 0 && input_[index_ - 1] == '\r')) {
- ++line_number_;
- }
- FALLTHROUGH;
- case ' ':
- case '\t':
- ConsumeChar();
- break;
- case '/':
- if (!EatComment())
- return;
- break;
- default:
- return;
- }
- }
-}
-
-bool JSONParser::EatComment() {
- Optional<StringPiece> comment_start = ConsumeChars(2);
- if (!comment_start)
- return false;
-
- if (comment_start == "//") {
- // Single line comment, read to newline.
- while (Optional<char> c = PeekChar()) {
- if (c == '\n' || c == '\r')
- return true;
- ConsumeChar();
- }
- } else if (comment_start == "/*") {
- char previous_char = '\0';
- // Block comment, read until end marker.
- while (Optional<char> c = PeekChar()) {
- if (previous_char == '*' && c == '/') {
- // EatWhitespaceAndComments will inspect pos(), which will still be on
- // the last / of the comment, so advance once more (which may also be
- // end of input).
- ConsumeChar();
- return true;
- }
- previous_char = *ConsumeChar();
- }
-
- // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT.
- }
-
- return false;
-}
-
-Optional<Value> JSONParser::ParseNextToken() {
- return ParseToken(GetNextToken());
-}
-
-Optional<Value> JSONParser::ParseToken(Token token) {
- switch (token) {
- case T_OBJECT_BEGIN:
- return ConsumeDictionary();
- case T_ARRAY_BEGIN:
- return ConsumeList();
- case T_STRING:
- return ConsumeString();
- case T_NUMBER:
- return ConsumeNumber();
- case T_BOOL_TRUE:
- case T_BOOL_FALSE:
- case T_NULL:
- return ConsumeLiteral();
- default:
- ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
- return nullopt;
- }
-}
-
-Optional<Value> JSONParser::ConsumeDictionary() {
- if (ConsumeChar() != '{') {
- ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
- return nullopt;
- }
-
- StackMarker depth_check(max_depth_, &stack_depth_);
- if (depth_check.IsTooDeep()) {
- ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0);
- return nullopt;
- }
-
- std::vector<Value::DictStorage::value_type> dict_storage;
-
- Token token = GetNextToken();
- while (token != T_OBJECT_END) {
- if (token != T_STRING) {
- ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1);
- return nullopt;
- }
-
- // First consume the key.
- StringBuilder key;
- if (!ConsumeStringRaw(&key)) {
- return nullopt;
- }
-
- // Read the separator.
- token = GetNextToken();
- if (token != T_OBJECT_PAIR_SEPARATOR) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
-
- // The next token is the value. Ownership transfers to |dict|.
- ConsumeChar();
- Optional<Value> value = ParseNextToken();
- if (!value) {
- // ReportError from deeper level.
- return nullopt;
- }
-
- dict_storage.emplace_back(key.DestructiveAsString(),
- std::make_unique<Value>(std::move(*value)));
-
- token = GetNextToken();
- if (token == T_LIST_SEPARATOR) {
- ConsumeChar();
- token = GetNextToken();
- if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
- ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
- return nullopt;
- }
- } else if (token != T_OBJECT_END) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
- return nullopt;
- }
- }
-
- ConsumeChar(); // Closing '}'.
-
- return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES));
-}
-
-Optional<Value> JSONParser::ConsumeList() {
- if (ConsumeChar() != '[') {
- ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
- return nullopt;
- }
-
- StackMarker depth_check(max_depth_, &stack_depth_);
- if (depth_check.IsTooDeep()) {
- ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0);
- return nullopt;
- }
-
- Value::ListStorage list_storage;
-
- Token token = GetNextToken();
- while (token != T_ARRAY_END) {
- Optional<Value> item = ParseToken(token);
- if (!item) {
- // ReportError from deeper level.
- return nullopt;
- }
-
- list_storage.push_back(std::move(*item));
-
- token = GetNextToken();
- if (token == T_LIST_SEPARATOR) {
- ConsumeChar();
- token = GetNextToken();
- if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
- ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
- return nullopt;
- }
- } else if (token != T_ARRAY_END) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
- }
-
- ConsumeChar(); // Closing ']'.
-
- return Value(std::move(list_storage));
-}
-
-Optional<Value> JSONParser::ConsumeString() {
- StringBuilder string;
- if (!ConsumeStringRaw(&string))
- return nullopt;
-
- return Value(string.DestructiveAsString());
-}
-
-bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
- if (ConsumeChar() != '"') {
- ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
- return false;
- }
-
- // StringBuilder will internally build a StringPiece unless a UTF-16
- // conversion occurs, at which point it will perform a copy into a
- // std::string.
- StringBuilder string(pos());
-
- while (PeekChar()) {
- uint32_t next_char = 0;
- if (!ReadUnicodeCharacter(input_.data(),
- static_cast<int32_t>(input_.length()), &index_,
- &next_char) ||
- !IsValidCharacter(next_char)) {
- if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
- ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
- return false;
- }
- ConsumeChar();
- string.Append(kUnicodeReplacementPoint);
- continue;
- }
-
- if (next_char == '"') {
- ConsumeChar();
- *out = std::move(string);
- return true;
- } else if (next_char != '\\') {
- // If this character is not an escape sequence...
- ConsumeChar();
- string.Append(next_char);
- } else {
- // And if it is an escape sequence, the input string will be adjusted
- // (either by combining the two characters of an encoded escape sequence,
- // or with a UTF conversion), so using StringPiece isn't possible -- force
- // a conversion.
- string.Convert();
-
- // Read past the escape '\' and ensure there's a character following.
- Optional<StringPiece> escape_sequence = ConsumeChars(2);
- if (!escape_sequence) {
- ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
- return false;
- }
-
- switch ((*escape_sequence)[1]) {
- // Allowed esape sequences:
- case 'x': { // UTF-8 sequence.
- // UTF-8 \x escape sequences are not allowed in the spec, but they
- // are supported here for backwards-compatiblity with the old parser.
- escape_sequence = ConsumeChars(2);
- if (!escape_sequence) {
- ReportError(JSONReader::JSON_INVALID_ESCAPE, -2);
- return false;
- }
-
- int hex_digit = 0;
- if (!HexStringToInt(*escape_sequence, &hex_digit) ||
- !IsValidCharacter(hex_digit)) {
- ReportError(JSONReader::JSON_INVALID_ESCAPE, -2);
- return false;
- }
-
- string.Append(hex_digit);
- break;
- }
- case 'u': { // UTF-16 sequence.
- // UTF units are of the form \uXXXX.
- uint32_t code_point;
- if (!DecodeUTF16(&code_point)) {
- ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
- return false;
- }
- string.Append(code_point);
- break;
- }
- case '"':
- string.Append('"');
- break;
- case '\\':
- string.Append('\\');
- break;
- case '/':
- string.Append('/');
- break;
- case 'b':
- string.Append('\b');
- break;
- case 'f':
- string.Append('\f');
- break;
- case 'n':
- string.Append('\n');
- break;
- case 'r':
- string.Append('\r');
- break;
- case 't':
- string.Append('\t');
- break;
- case 'v': // Not listed as valid escape sequence in the RFC.
- string.Append('\v');
- break;
- // All other escape squences are illegal.
- default:
- ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
- return false;
- }
- }
- }
-
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
- return false;
-}
-
-// Entry is at the first X in \uXXXX.
-bool JSONParser::DecodeUTF16(uint32_t* out_code_point) {
- Optional<StringPiece> escape_sequence = ConsumeChars(4);
- if (!escape_sequence)
- return false;
-
- // Consume the UTF-16 code unit, which may be a high surrogate.
- int code_unit16_high = 0;
- if (!HexStringToInt(*escape_sequence, &code_unit16_high))
- return false;
-
- // If this is a high surrogate, consume the next code unit to get the
- // low surrogate.
- if (CBU16_IS_SURROGATE(code_unit16_high)) {
- // Make sure this is the high surrogate. If not, it's an encoding
- // error.
- if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high))
- return false;
-
- // Make sure that the token has more characters to consume the
- // lower surrogate.
- if (!ConsumeIfMatch("\\u"))
- return false;
-
- escape_sequence = ConsumeChars(4);
- if (!escape_sequence)
- return false;
-
- int code_unit16_low = 0;
- if (!HexStringToInt(*escape_sequence, &code_unit16_low))
- return false;
-
- if (!CBU16_IS_TRAIL(code_unit16_low))
- return false;
-
- uint32_t code_point =
- CBU16_GET_SUPPLEMENTARY(code_unit16_high, code_unit16_low);
- if (!IsValidCharacter(code_point))
- return false;
-
- *out_code_point = code_point;
- } else {
- // Not a surrogate.
- DCHECK(CBU16_IS_SINGLE(code_unit16_high));
- if (!IsValidCharacter(code_unit16_high)) {
- if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
- return false;
- }
- *out_code_point = kUnicodeReplacementPoint;
- return true;
- }
-
- *out_code_point = code_unit16_high;
- }
-
- return true;
-}
-
-Optional<Value> JSONParser::ConsumeNumber() {
- const char* num_start = pos();
- const int start_index = index_;
- int end_index = start_index;
-
- if (PeekChar() == '-')
- ConsumeChar();
-
- if (!ReadInt(false)) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
- end_index = index_;
-
- // The optional fraction part.
- if (PeekChar() == '.') {
- ConsumeChar();
- if (!ReadInt(true)) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
- end_index = index_;
- }
-
- // Optional exponent part.
- Optional<char> c = PeekChar();
- if (c == 'e' || c == 'E') {
- ConsumeChar();
- if (PeekChar() == '-' || PeekChar() == '+') {
- ConsumeChar();
- }
- if (!ReadInt(true)) {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
- end_index = index_;
- }
-
- // ReadInt is greedy because numbers have no easily detectable sentinel,
- // so save off where the parser should be on exit (see Consume invariant at
- // the top of the header), then make sure the next token is one which is
- // valid.
- int exit_index = index_;
-
- switch (GetNextToken()) {
- case T_OBJECT_END:
- case T_ARRAY_END:
- case T_LIST_SEPARATOR:
- case T_END_OF_INPUT:
- break;
- default:
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
-
- index_ = exit_index;
-
- StringPiece num_string(num_start, end_index - start_index);
-
- int num_int;
- if (StringToInt(num_string, &num_int))
- return Value(num_int);
-
- return nullopt;
-}
-
-bool JSONParser::ReadInt(bool allow_leading_zeros) {
- size_t len = 0;
- char first = 0;
-
- while (Optional<char> c = PeekChar()) {
- if (!IsAsciiDigit(c))
- break;
-
- if (len == 0)
- first = *c;
-
- ++len;
- ConsumeChar();
- }
-
- if (len == 0)
- return false;
-
- if (!allow_leading_zeros && len > 1 && first == '0')
- return false;
-
- return true;
-}
-
-Optional<Value> JSONParser::ConsumeLiteral() {
- if (ConsumeIfMatch("true")) {
- return Value(true);
- } else if (ConsumeIfMatch("false")) {
- return Value(false);
- } else if (ConsumeIfMatch("null")) {
- return Value(Value::Type::NONE);
- } else {
- ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
- return nullopt;
- }
-}
-
-bool JSONParser::ConsumeIfMatch(StringPiece match) {
- if (match == PeekChars(match.size())) {
- ConsumeChars(match.size());
- return true;
- }
- return false;
-}
-
-void JSONParser::ReportError(JSONReader::JsonParseError code,
- int column_adjust) {
- error_code_ = code;
- error_line_ = line_number_;
- error_column_ = index_ - index_last_line_ + column_adjust;
-}
-
-// static
-std::string JSONParser::FormatErrorMessage(int line,
- int column,
- const std::string& description) {
- if (line || column) {
- return StringPrintf("Line: %i, column: %i, %s", line, column,
- description.c_str());
- }
- return description;
-}
-
-} // namespace internal
-} // namespace base
diff --git a/gn/base/json/json_parser.h b/gn/base/json/json_parser.h
deleted file mode 100644
index 1289560983b..00000000000
--- a/gn/base/json/json_parser.h
+++ /dev/null
@@ -1,260 +0,0 @@
-// 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 BASE_JSON_JSON_PARSER_H_
-#define BASE_JSON_JSON_PARSER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/json/json_reader.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-class Value;
-
-namespace internal {
-
-class JSONParserTest;
-
-// The implementation behind the JSONReader interface. This class is not meant
-// to be used directly; it encapsulates logic that need not be exposed publicly.
-//
-// This parser guarantees O(n) time through the input string. Iteration happens
-// on the byte level, with the functions ConsumeChars() and ConsumeChar(). The
-// conversion from byte to JSON token happens without advancing the parser in
-// GetNextToken/ParseToken, that is tokenization operates on the current parser
-// position without advancing.
-//
-// Built on top of these are a family of Consume functions that iterate
-// internally. Invariant: on entry of a Consume function, the parser is wound
-// to the first byte of a valid JSON token. On exit, it is on the first byte
-// after the token that was just consumed, which would likely be the first byte
-// of the next token.
-class JSONParser {
- public:
- JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth);
- ~JSONParser();
-
- // Parses the input string according to the set options and returns the
- // result as a Value.
- // Wrap this in base::FooValue::From() to check the Value is of type Foo and
- // convert to a FooValue at the same time.
- Optional<Value> Parse(StringPiece input);
-
- // Returns the error code.
- JSONReader::JsonParseError error_code() const;
-
- // Returns the human-friendly error message.
- std::string GetErrorMessage() const;
-
- // Returns the error line number if parse error happened. Otherwise always
- // returns 0.
- int error_line() const;
-
- // Returns the error column number if parse error happened. Otherwise always
- // returns 0.
- int error_column() const;
-
- private:
- enum Token {
- T_OBJECT_BEGIN, // {
- T_OBJECT_END, // }
- T_ARRAY_BEGIN, // [
- T_ARRAY_END, // ]
- T_STRING,
- T_NUMBER,
- T_BOOL_TRUE, // true
- T_BOOL_FALSE, // false
- T_NULL, // null
- T_LIST_SEPARATOR, // ,
- T_OBJECT_PAIR_SEPARATOR, // :
- T_END_OF_INPUT,
- T_INVALID_TOKEN,
- };
-
- // A helper class used for parsing strings. One optimization performed is to
- // create base::Value with a StringPiece to avoid unnecessary std::string
- // copies. This is not possible if the input string needs to be decoded from
- // UTF-16 to UTF-8, or if an escape sequence causes characters to be skipped.
- // This class centralizes that logic.
- class StringBuilder {
- public:
- // Empty constructor. Used for creating a builder with which to assign to.
- StringBuilder();
-
- // |pos| is the beginning of an input string, excluding the |"|.
- explicit StringBuilder(const char* pos);
-
- ~StringBuilder();
-
- StringBuilder& operator=(StringBuilder&& other);
-
- // Appends the Unicode code point |point| to the string, either by
- // increasing the |length_| of the string if the string has not been
- // converted, or by appending the UTF8 bytes for the code point.
- void Append(uint32_t point);
-
- // Converts the builder from its default StringPiece to a full std::string,
- // performing a copy. Once a builder is converted, it cannot be made a
- // StringPiece again.
- void Convert();
-
- // Returns the builder as a string, invalidating all state. This allows
- // the internal string buffer representation to be destructively moved
- // in cases where the builder will not be needed any more.
- std::string DestructiveAsString();
-
- private:
- // The beginning of the input string.
- const char* pos_;
-
- // Number of bytes in |pos_| that make up the string being built.
- size_t length_;
-
- // The copied string representation. Will be unset until Convert() is
- // called.
- base::Optional<std::string> string_;
- };
-
- // Returns the next |count| bytes of the input stream, or nullopt if fewer
- // than |count| bytes remain.
- Optional<StringPiece> PeekChars(int count);
-
- // Calls PeekChars() with a |count| of 1.
- Optional<char> PeekChar();
-
- // Returns the next |count| bytes of the input stream, or nullopt if fewer
- // than |count| bytes remain, and advances the parser position by |count|.
- Optional<StringPiece> ConsumeChars(int count);
-
- // Calls ConsumeChars() with a |count| of 1.
- Optional<char> ConsumeChar();
-
- // Returns a pointer to the current character position.
- const char* pos();
-
- // Skips over whitespace and comments to find the next token in the stream.
- // This does not advance the parser for non-whitespace or comment chars.
- Token GetNextToken();
-
- // Consumes whitespace characters and comments until the next non-that is
- // encountered.
- void EatWhitespaceAndComments();
- // Helper function that consumes a comment, assuming that the parser is
- // currently wound to a '/'.
- bool EatComment();
-
- // Calls GetNextToken() and then ParseToken().
- Optional<Value> ParseNextToken();
-
- // Takes a token that represents the start of a Value ("a structural token"
- // in RFC terms) and consumes it, returning the result as a Value.
- Optional<Value> ParseToken(Token token);
-
- // Assuming that the parser is currently wound to '{', this parses a JSON
- // object into a Value.
- Optional<Value> ConsumeDictionary();
-
- // Assuming that the parser is wound to '[', this parses a JSON list into a
- // Value.
- Optional<Value> ConsumeList();
-
- // Calls through ConsumeStringRaw and wraps it in a value.
- Optional<Value> ConsumeString();
-
- // Assuming that the parser is wound to a double quote, this parses a string,
- // decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on
- // success and places result into |out|. Returns false on failure with
- // error information set.
- bool ConsumeStringRaw(StringBuilder* out);
- // Helper function for ConsumeStringRaw() that consumes the next four or 10
- // bytes (parser is wound to the first character of a HEX sequence, with the
- // potential for consuming another \uXXXX for a surrogate). Returns true on
- // success and places the code point |out_code_point|, and false on failure.
- bool DecodeUTF16(uint32_t* out_code_point);
-
- // Assuming that the parser is wound to the start of a valid JSON number,
- // this parses and converts it to either an int or double value.
- Optional<Value> ConsumeNumber();
- // Helper that reads characters that are ints. Returns true if a number was
- // read and false on error.
- bool ReadInt(bool allow_leading_zeros);
-
- // Consumes the literal values of |true|, |false|, and |null|, assuming the
- // parser is wound to the first character of any of those.
- Optional<Value> ConsumeLiteral();
-
- // Helper function that returns true if the byte squence |match| can be
- // consumed at the current parser position. Returns false if there are fewer
- // than |match|-length bytes or if the sequence does not match, and the
- // parser state is unchanged.
- bool ConsumeIfMatch(StringPiece match);
-
- // Sets the error information to |code| at the current column, based on
- // |index_| and |index_last_line_|, with an optional positive/negative
- // adjustment by |column_adjust|.
- void ReportError(JSONReader::JsonParseError code, int column_adjust);
-
- // Given the line and column number of an error, formats one of the error
- // message contants from json_reader.h for human display.
- static std::string FormatErrorMessage(int line,
- int column,
- const std::string& description);
-
- // base::JSONParserOptions that control parsing.
- const int options_;
-
- // Maximum depth to parse.
- const int max_depth_;
-
- // The input stream being parsed. Note: Not guaranteed to NUL-terminated.
- StringPiece input_;
-
- // The index in the input stream to which the parser is wound.
- int index_;
-
- // The number of times the parser has recursed (current stack depth).
- int stack_depth_;
-
- // The line number that the parser is at currently.
- int line_number_;
-
- // The last value of |index_| on the previous line.
- int index_last_line_;
-
- // Error information.
- JSONReader::JsonParseError error_code_;
- int error_line_;
- int error_column_;
-
- friend class JSONParserTest;
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, NextChar);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeDictionary);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeList);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeString);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters);
- FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidUTF16EscapeSequence);
-
- DISALLOW_COPY_AND_ASSIGN(JSONParser);
-};
-
-// Used when decoding and an invalid utf-8 sequence is encountered.
-extern const char kUnicodeReplacementString[];
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_JSON_JSON_PARSER_H_
diff --git a/gn/base/json/json_reader.cc b/gn/base/json/json_reader.cc
deleted file mode 100644
index 3d5475e8065..00000000000
--- a/gn/base/json/json_reader.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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 "base/json/json_reader.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/json/json_parser.h"
-#include "base/logging.h"
-#include "base/optional.h"
-#include "base/values.h"
-
-namespace base {
-
-// Chosen to support 99.9% of documents found in the wild late 2016.
-// http://crbug.com/673263
-const int JSONReader::kStackMaxDepth = 200;
-
-// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError.
-static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000,
- "JSONReader error out of bounds");
-
-const char JSONReader::kInvalidEscape[] = "Invalid escape sequence.";
-const char JSONReader::kSyntaxError[] = "Syntax error.";
-const char JSONReader::kUnexpectedToken[] = "Unexpected token.";
-const char JSONReader::kTrailingComma[] = "Trailing comma not allowed.";
-const char JSONReader::kTooMuchNesting[] = "Too much nesting.";
-const char JSONReader::kUnexpectedDataAfterRoot[] =
- "Unexpected data after root element.";
-const char JSONReader::kUnsupportedEncoding[] =
- "Unsupported encoding. JSON must be UTF-8.";
-const char JSONReader::kUnquotedDictionaryKey[] =
- "Dictionary keys must be quoted.";
-const char JSONReader::kInputTooLarge[] = "Input string is too large (>2GB).";
-
-JSONReader::JSONReader(int options, int max_depth)
- : parser_(new internal::JSONParser(options, max_depth)) {}
-
-JSONReader::~JSONReader() = default;
-
-// static
-std::unique_ptr<Value> JSONReader::Read(StringPiece json,
- int options,
- int max_depth) {
- internal::JSONParser parser(options, max_depth);
- Optional<Value> root = parser.Parse(json);
- return root ? std::make_unique<Value>(std::move(*root)) : nullptr;
-}
-
-// static
-std::unique_ptr<Value> JSONReader::ReadAndReturnError(
- StringPiece json,
- int options,
- int* error_code_out,
- std::string* error_msg_out,
- int* error_line_out,
- int* error_column_out) {
- internal::JSONParser parser(options);
- Optional<Value> root = parser.Parse(json);
- if (!root) {
- if (error_code_out)
- *error_code_out = parser.error_code();
- if (error_msg_out)
- *error_msg_out = parser.GetErrorMessage();
- if (error_line_out)
- *error_line_out = parser.error_line();
- if (error_column_out)
- *error_column_out = parser.error_column();
- }
-
- return root ? std::make_unique<Value>(std::move(*root)) : nullptr;
-}
-
-// static
-std::string JSONReader::ErrorCodeToString(JsonParseError error_code) {
- switch (error_code) {
- case JSON_NO_ERROR:
- return std::string();
- case JSON_INVALID_ESCAPE:
- return kInvalidEscape;
- case JSON_SYNTAX_ERROR:
- return kSyntaxError;
- case JSON_UNEXPECTED_TOKEN:
- return kUnexpectedToken;
- case JSON_TRAILING_COMMA:
- return kTrailingComma;
- case JSON_TOO_MUCH_NESTING:
- return kTooMuchNesting;
- case JSON_UNEXPECTED_DATA_AFTER_ROOT:
- return kUnexpectedDataAfterRoot;
- case JSON_UNSUPPORTED_ENCODING:
- return kUnsupportedEncoding;
- case JSON_UNQUOTED_DICTIONARY_KEY:
- return kUnquotedDictionaryKey;
- case JSON_TOO_LARGE:
- return kInputTooLarge;
- case JSON_PARSE_ERROR_COUNT:
- break;
- }
- NOTREACHED();
- return std::string();
-}
-
-std::unique_ptr<Value> JSONReader::ReadToValue(StringPiece json) {
- Optional<Value> value = parser_->Parse(json);
- return value ? std::make_unique<Value>(std::move(*value)) : nullptr;
-}
-
-JSONReader::JsonParseError JSONReader::error_code() const {
- return parser_->error_code();
-}
-
-std::string JSONReader::GetErrorMessage() const {
- return parser_->GetErrorMessage();
-}
-
-} // namespace base
diff --git a/gn/base/json/json_reader.h b/gn/base/json/json_reader.h
deleted file mode 100644
index c4f7c8fc738..00000000000
--- a/gn/base/json/json_reader.h
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 JSON parser. Converts strings of JSON into a Value object (see
-// base/values.h).
-// http://www.ietf.org/rfc/rfc4627.txt?number=4627
-//
-// Known limitations/deviations from the RFC:
-// - Only knows how to parse ints within the range of a signed 32 bit int and
-// decimal numbers within a double.
-// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16
-// (BE or LE) and UTF-32 (BE or LE) as well.
-// - We limit nesting to 100 levels to prevent stack overflow (this is allowed
-// by the RFC).
-// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data
-// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input
-// UTF-8 string for the JSONReader::JsonToValue() function may start with a
-// UTF-8 BOM (0xEF, 0xBB, 0xBF).
-// To avoid the function from mis-treating a UTF-8 BOM as an invalid
-// character, the function skips a Unicode BOM at the beginning of the
-// Unicode string (converted from the input UTF-8 string) before parsing it.
-//
-// TODO(tc): Add a parsing option to to relax object keys being wrapped in
-// double quotes
-// TODO(tc): Add an option to disable comment stripping
-
-#ifndef BASE_JSON_JSON_READER_H_
-#define BASE_JSON_JSON_READER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-class Value;
-
-namespace internal {
-class JSONParser;
-}
-
-enum JSONParserOptions {
- // Parses the input strictly according to RFC 4627, except for where noted
- // above.
- JSON_PARSE_RFC = 0,
-
- // Allows commas to exist after the last element in structures.
- JSON_ALLOW_TRAILING_COMMAS = 1 << 0,
-
- // If set the parser replaces invalid characters with the Unicode replacement
- // character (U+FFFD). If not set, invalid characters trigger a hard error and
- // parsing fails.
- JSON_REPLACE_INVALID_CHARACTERS = 1 << 1,
-};
-
-class JSONReader {
- public:
- static const int kStackMaxDepth;
-
- // Error codes during parsing.
- enum JsonParseError {
- JSON_NO_ERROR = 0,
- JSON_INVALID_ESCAPE,
- JSON_SYNTAX_ERROR,
- JSON_UNEXPECTED_TOKEN,
- JSON_TRAILING_COMMA,
- JSON_TOO_MUCH_NESTING,
- JSON_UNEXPECTED_DATA_AFTER_ROOT,
- JSON_UNSUPPORTED_ENCODING,
- JSON_UNQUOTED_DICTIONARY_KEY,
- JSON_TOO_LARGE,
- JSON_PARSE_ERROR_COUNT
- };
-
- // String versions of parse error codes.
- static const char kInvalidEscape[];
- static const char kSyntaxError[];
- static const char kUnexpectedToken[];
- static const char kTrailingComma[];
- static const char kTooMuchNesting[];
- static const char kUnexpectedDataAfterRoot[];
- static const char kUnsupportedEncoding[];
- static const char kUnquotedDictionaryKey[];
- static const char kInputTooLarge[];
-
- // Constructs a reader.
- JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth);
-
- ~JSONReader();
-
- // Reads and parses |json|, returning a Value.
- // If |json| is not a properly formed JSON string, returns nullptr.
- // Wrap this in base::FooValue::From() to check the Value is of type Foo and
- // convert to a FooValue at the same time.
- static std::unique_ptr<Value> Read(StringPiece json,
- int options = JSON_PARSE_RFC,
- int max_depth = kStackMaxDepth);
-
- // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out|
- // are optional. If specified and nullptr is returned, they will be populated
- // an error code and a formatted error message (including error location if
- // appropriate). Otherwise, they will be unmodified.
- static std::unique_ptr<Value> ReadAndReturnError(
- StringPiece json,
- int options, // JSONParserOptions
- int* error_code_out,
- std::string* error_msg_out,
- int* error_line_out = nullptr,
- int* error_column_out = nullptr);
-
- // Converts a JSON parse error code into a human readable message.
- // Returns an empty string if error_code is JSON_NO_ERROR.
- static std::string ErrorCodeToString(JsonParseError error_code);
-
- // Non-static version of Read() above.
- std::unique_ptr<Value> ReadToValue(StringPiece json);
-
- // Returns the error code if the last call to ReadToValue() failed.
- // Returns JSON_NO_ERROR otherwise.
- JsonParseError error_code() const;
-
- // Converts error_code_ to a human-readable string, including line and column
- // numbers if appropriate.
- std::string GetErrorMessage() const;
-
- private:
- std::unique_ptr<internal::JSONParser> parser_;
-};
-
-} // namespace base
-
-#endif // BASE_JSON_JSON_READER_H_
diff --git a/gn/base/json/json_value_converter.cc b/gn/base/json/json_value_converter.cc
deleted file mode 100644
index 55506e6b592..00000000000
--- a/gn/base/json/json_value_converter.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 "base/json/json_value_converter.h"
-
-namespace base {
-namespace internal {
-
-bool BasicValueConverter<int>::Convert(const base::Value& value,
- int* field) const {
- return value.GetAsInteger(field);
-}
-
-bool BasicValueConverter<std::string>::Convert(const base::Value& value,
- std::string* field) const {
- return value.GetAsString(field);
-}
-
-bool BasicValueConverter<string16>::Convert(const base::Value& value,
- string16* field) const {
- return value.GetAsString(field);
-}
-
-bool BasicValueConverter<double>::Convert(const base::Value& value,
- double* field) const {
- return value.GetAsDouble(field);
-}
-
-bool BasicValueConverter<bool>::Convert(const base::Value& value,
- bool* field) const {
- return value.GetAsBoolean(field);
-}
-
-} // namespace internal
-} // namespace base
diff --git a/gn/base/json/json_value_converter.h b/gn/base/json/json_value_converter.h
deleted file mode 100644
index d91bead803f..00000000000
--- a/gn/base/json/json_value_converter.h
+++ /dev/null
@@ -1,512 +0,0 @@
-// 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 BASE_JSON_JSON_VALUE_CONVERTER_H_
-#define BASE_JSON_JSON_VALUE_CONVERTER_H_
-
-#include <stddef.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "base/values.h"
-
-// JSONValueConverter converts a JSON value into a C++ struct in a
-// lightweight way.
-//
-// Usage:
-// For real examples, you may want to refer to _unittest.cc file.
-//
-// Assume that you have a struct like this:
-// struct Message {
-// int foo;
-// std::string bar;
-// static void RegisterJSONConverter(
-// JSONValueConverter<Message>* converter);
-// };
-//
-// And you want to parse a json data into this struct. First, you
-// need to declare RegisterJSONConverter() method in your struct.
-// // static
-// void Message::RegisterJSONConverter(
-// JSONValueConverter<Message>* converter) {
-// converter->RegisterIntField("foo", &Message::foo);
-// converter->RegisterStringField("bar", &Message::bar);
-// }
-//
-// Then, you just instantiate your JSONValueConverter of your type and call
-// Convert() method.
-// Message message;
-// JSONValueConverter<Message> converter;
-// converter.Convert(json, &message);
-//
-// Convert() returns false when it fails. Here "fail" means that the value is
-// structurally different from expected, such like a string value appears
-// for an int field. Do not report failures for missing fields.
-// Also note that Convert() will modify the passed |message| even when it
-// fails for performance reason.
-//
-// For nested field, the internal message also has to implement the registration
-// method. Then, just use RegisterNestedField() from the containing struct's
-// RegisterJSONConverter method.
-// struct Nested {
-// Message foo;
-// static void RegisterJSONConverter(...) {
-// ...
-// converter->RegisterNestedField("foo", &Nested::foo);
-// }
-// };
-//
-// For repeated field, we just assume std::vector<std::unique_ptr<ElementType>>
-// for its container and you can put RegisterRepeatedInt or some other types.
-// Use RegisterRepeatedMessage for nested repeated fields.
-//
-// Sometimes JSON format uses string representations for other types such
-// like enum, timestamp, or URL. You can use RegisterCustomField method
-// and specify a function to convert a StringPiece to your type.
-// bool ConvertFunc(StringPiece s, YourEnum* result) {
-// // do something and return true if succeed...
-// }
-// struct Message {
-// YourEnum ye;
-// ...
-// static void RegisterJSONConverter(...) {
-// ...
-// converter->RegsiterCustomField<YourEnum>(
-// "your_enum", &Message::ye, &ConvertFunc);
-// }
-// };
-
-namespace base {
-
-template <typename StructType>
-class JSONValueConverter;
-
-namespace internal {
-
-template <typename StructType>
-class FieldConverterBase {
- public:
- explicit FieldConverterBase(const std::string& path) : field_path_(path) {}
- virtual ~FieldConverterBase() = default;
- virtual bool ConvertField(const base::Value& value,
- StructType* obj) const = 0;
- const std::string& field_path() const { return field_path_; }
-
- private:
- std::string field_path_;
- DISALLOW_COPY_AND_ASSIGN(FieldConverterBase);
-};
-
-template <typename FieldType>
-class ValueConverter {
- public:
- virtual ~ValueConverter() = default;
- virtual bool Convert(const base::Value& value, FieldType* field) const = 0;
-};
-
-template <typename StructType, typename FieldType>
-class FieldConverter : public FieldConverterBase<StructType> {
- public:
- explicit FieldConverter(const std::string& path,
- FieldType StructType::*field,
- ValueConverter<FieldType>* converter)
- : FieldConverterBase<StructType>(path),
- field_pointer_(field),
- value_converter_(converter) {}
-
- bool ConvertField(const base::Value& value, StructType* dst) const override {
- return value_converter_->Convert(value, &(dst->*field_pointer_));
- }
-
- private:
- FieldType StructType::*field_pointer_;
- std::unique_ptr<ValueConverter<FieldType>> value_converter_;
- DISALLOW_COPY_AND_ASSIGN(FieldConverter);
-};
-
-template <typename FieldType>
-class BasicValueConverter;
-
-template <>
-class BasicValueConverter<int> : public ValueConverter<int> {
- public:
- BasicValueConverter() = default;
-
- bool Convert(const base::Value& value, int* field) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
-};
-
-template <>
-class BasicValueConverter<std::string> : public ValueConverter<std::string> {
- public:
- BasicValueConverter() = default;
-
- bool Convert(const base::Value& value, std::string* field) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
-};
-
-template <>
-class BasicValueConverter<string16> : public ValueConverter<string16> {
- public:
- BasicValueConverter() = default;
-
- bool Convert(const base::Value& value, string16* field) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
-};
-
-template <>
-class BasicValueConverter<double> : public ValueConverter<double> {
- public:
- BasicValueConverter() = default;
-
- bool Convert(const base::Value& value, double* field) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
-};
-
-template <>
-class BasicValueConverter<bool> : public ValueConverter<bool> {
- public:
- BasicValueConverter() = default;
-
- bool Convert(const base::Value& value, bool* field) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
-};
-
-template <typename FieldType>
-class ValueFieldConverter : public ValueConverter<FieldType> {
- public:
- typedef bool (*ConvertFunc)(const base::Value* value, FieldType* field);
-
- explicit ValueFieldConverter(ConvertFunc convert_func)
- : convert_func_(convert_func) {}
-
- bool Convert(const base::Value& value, FieldType* field) const override {
- return convert_func_(&value, field);
- }
-
- private:
- ConvertFunc convert_func_;
-
- DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter);
-};
-
-template <typename FieldType>
-class CustomFieldConverter : public ValueConverter<FieldType> {
- public:
- typedef bool (*ConvertFunc)(StringPiece value, FieldType* field);
-
- explicit CustomFieldConverter(ConvertFunc convert_func)
- : convert_func_(convert_func) {}
-
- bool Convert(const base::Value& value, FieldType* field) const override {
- std::string string_value;
- return value.GetAsString(&string_value) &&
- convert_func_(string_value, field);
- }
-
- private:
- ConvertFunc convert_func_;
-
- DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter);
-};
-
-template <typename NestedType>
-class NestedValueConverter : public ValueConverter<NestedType> {
- public:
- NestedValueConverter() = default;
-
- bool Convert(const base::Value& value, NestedType* field) const override {
- return converter_.Convert(value, field);
- }
-
- private:
- JSONValueConverter<NestedType> converter_;
- DISALLOW_COPY_AND_ASSIGN(NestedValueConverter);
-};
-
-template <typename Element>
-class RepeatedValueConverter
- : public ValueConverter<std::vector<std::unique_ptr<Element>>> {
- public:
- RepeatedValueConverter() = default;
-
- bool Convert(const base::Value& value,
- std::vector<std::unique_ptr<Element>>* field) const override {
- const base::ListValue* list = NULL;
- if (!value.GetAsList(&list)) {
- // The field is not a list.
- return false;
- }
-
- field->reserve(list->GetSize());
- for (size_t i = 0; i < list->GetSize(); ++i) {
- const base::Value* element = NULL;
- if (!list->Get(i, &element))
- continue;
-
- std::unique_ptr<Element> e(new Element);
- if (basic_converter_.Convert(*element, e.get())) {
- field->push_back(std::move(e));
- } else {
- return false;
- }
- }
- return true;
- }
-
- private:
- BasicValueConverter<Element> basic_converter_;
- DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter);
-};
-
-template <typename NestedType>
-class RepeatedMessageConverter
- : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
- public:
- RepeatedMessageConverter() = default;
-
- bool Convert(const base::Value& value,
- std::vector<std::unique_ptr<NestedType>>* field) const override {
- const base::ListValue* list = NULL;
- if (!value.GetAsList(&list))
- return false;
-
- field->reserve(list->GetSize());
- for (size_t i = 0; i < list->GetSize(); ++i) {
- const base::Value* element = NULL;
- if (!list->Get(i, &element))
- continue;
-
- std::unique_ptr<NestedType> nested(new NestedType);
- if (converter_.Convert(*element, nested.get())) {
- field->push_back(std::move(nested));
- } else {
- return false;
- }
- }
- return true;
- }
-
- private:
- JSONValueConverter<NestedType> converter_;
- DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter);
-};
-
-template <typename NestedType>
-class RepeatedCustomValueConverter
- : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
- public:
- typedef bool (*ConvertFunc)(const base::Value* value, NestedType* field);
-
- explicit RepeatedCustomValueConverter(ConvertFunc convert_func)
- : convert_func_(convert_func) {}
-
- bool Convert(const base::Value& value,
- std::vector<std::unique_ptr<NestedType>>* field) const override {
- const base::ListValue* list = NULL;
- if (!value.GetAsList(&list))
- return false;
-
- field->reserve(list->GetSize());
- for (size_t i = 0; i < list->GetSize(); ++i) {
- const base::Value* element = NULL;
- if (!list->Get(i, &element))
- continue;
-
- std::unique_ptr<NestedType> nested(new NestedType);
- if ((*convert_func_)(element, nested.get())) {
- field->push_back(std::move(nested));
- } else {
- return false;
- }
- }
- return true;
- }
-
- private:
- ConvertFunc convert_func_;
- DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter);
-};
-
-} // namespace internal
-
-template <class StructType>
-class JSONValueConverter {
- public:
- JSONValueConverter() { StructType::RegisterJSONConverter(this); }
-
- void RegisterIntField(const std::string& field_name, int StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, int>>(
- field_name, field, new internal::BasicValueConverter<int>));
- }
-
- void RegisterStringField(const std::string& field_name,
- std::string StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, std::string>>(
- field_name, field, new internal::BasicValueConverter<std::string>));
- }
-
- void RegisterStringField(const std::string& field_name,
- string16 StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, string16>>(
- field_name, field, new internal::BasicValueConverter<string16>));
- }
-
- void RegisterBoolField(const std::string& field_name,
- bool StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, bool>>(
- field_name, field, new internal::BasicValueConverter<bool>));
- }
-
- void RegisterDoubleField(const std::string& field_name,
- double StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, double>>(
- field_name, field, new internal::BasicValueConverter<double>));
- }
-
- template <class NestedType>
- void RegisterNestedField(const std::string& field_name,
- NestedType StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, NestedType>>(
- field_name, field, new internal::NestedValueConverter<NestedType>));
- }
-
- template <typename FieldType>
- void RegisterCustomField(const std::string& field_name,
- FieldType StructType::*field,
- bool (*convert_func)(StringPiece, FieldType*)) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, FieldType>>(
- field_name, field,
- new internal::CustomFieldConverter<FieldType>(convert_func)));
- }
-
- template <typename FieldType>
- void RegisterCustomValueField(const std::string& field_name,
- FieldType StructType::*field,
- bool (*convert_func)(const base::Value*,
- FieldType*)) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<StructType, FieldType>>(
- field_name, field,
- new internal::ValueFieldConverter<FieldType>(convert_func)));
- }
-
- void RegisterRepeatedInt(
- const std::string& field_name,
- std::vector<std::unique_ptr<int>> StructType::*field) {
- fields_.push_back(std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<int>>>>(
- field_name, field, new internal::RepeatedValueConverter<int>));
- }
-
- void RegisterRepeatedString(
- const std::string& field_name,
- std::vector<std::unique_ptr<std::string>> StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<std::string>>>>(
- field_name, field,
- new internal::RepeatedValueConverter<std::string>));
- }
-
- void RegisterRepeatedString(
- const std::string& field_name,
- std::vector<std::unique_ptr<string16>> StructType::*field) {
- fields_.push_back(std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<string16>>>>(
- field_name, field, new internal::RepeatedValueConverter<string16>));
- }
-
- void RegisterRepeatedDouble(
- const std::string& field_name,
- std::vector<std::unique_ptr<double>> StructType::*field) {
- fields_.push_back(std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<double>>>>(
- field_name, field, new internal::RepeatedValueConverter<double>));
- }
-
- void RegisterRepeatedBool(
- const std::string& field_name,
- std::vector<std::unique_ptr<bool>> StructType::*field) {
- fields_.push_back(std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<bool>>>>(
- field_name, field, new internal::RepeatedValueConverter<bool>));
- }
-
- template <class NestedType>
- void RegisterRepeatedCustomValue(
- const std::string& field_name,
- std::vector<std::unique_ptr<NestedType>> StructType::*field,
- bool (*convert_func)(const base::Value*, NestedType*)) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<NestedType>>>>(
- field_name, field,
- new internal::RepeatedCustomValueConverter<NestedType>(
- convert_func)));
- }
-
- template <class NestedType>
- void RegisterRepeatedMessage(
- const std::string& field_name,
- std::vector<std::unique_ptr<NestedType>> StructType::*field) {
- fields_.push_back(
- std::make_unique<internal::FieldConverter<
- StructType, std::vector<std::unique_ptr<NestedType>>>>(
- field_name, field,
- new internal::RepeatedMessageConverter<NestedType>));
- }
-
- bool Convert(const base::Value& value, StructType* output) const {
- const DictionaryValue* dictionary_value = NULL;
- if (!value.GetAsDictionary(&dictionary_value))
- return false;
-
- for (size_t i = 0; i < fields_.size(); ++i) {
- const internal::FieldConverterBase<StructType>* field_converter =
- fields_[i].get();
- const base::Value* field = NULL;
- if (dictionary_value->Get(field_converter->field_path(), &field)) {
- if (!field_converter->ConvertField(*field, output)) {
- return false;
- }
- }
- }
- return true;
- }
-
- private:
- std::vector<std::unique_ptr<internal::FieldConverterBase<StructType>>>
- fields_;
-
- DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
-};
-
-} // namespace base
-
-#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_
diff --git a/gn/base/json/json_writer.cc b/gn/base/json/json_writer.cc
deleted file mode 100644
index 656a87c22ee..00000000000
--- a/gn/base/json/json_writer.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// 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 "base/json/json_writer.h"
-
-#include <stdint.h>
-
-#include <cmath>
-#include <limits>
-
-#include "base/json/string_escape.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "util/build_config.h"
-
-namespace base {
-
-#if defined(OS_WIN)
-const char kPrettyPrintLineEnding[] = "\r\n";
-#else
-const char kPrettyPrintLineEnding[] = "\n";
-#endif
-
-// static
-bool JSONWriter::Write(const Value& node, std::string* json) {
- return WriteWithOptions(node, 0, json);
-}
-
-// static
-bool JSONWriter::WriteWithOptions(const Value& node,
- int options,
- std::string* json) {
- json->clear();
- // Is there a better way to estimate the size of the output?
- json->reserve(1024);
-
- JSONWriter writer(options, json);
- bool result = writer.BuildJSONString(node, 0U);
-
- if (options & OPTIONS_PRETTY_PRINT)
- json->append(kPrettyPrintLineEnding);
-
- return result;
-}
-
-JSONWriter::JSONWriter(int options, std::string* json)
- : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
- omit_double_type_preservation_(
- (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0),
- pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
- json_string_(json) {
- DCHECK(json);
-}
-
-bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
- switch (node.type()) {
- case Value::Type::NONE: {
- json_string_->append("null");
- return true;
- }
-
- case Value::Type::BOOLEAN: {
- bool value;
- bool result = node.GetAsBoolean(&value);
- DCHECK(result);
- json_string_->append(value ? "true" : "false");
- return result;
- }
-
- case Value::Type::INTEGER: {
- int value;
- bool result = node.GetAsInteger(&value);
- DCHECK(result);
- json_string_->append(IntToString(value));
- return result;
- }
-
- case Value::Type::STRING: {
- std::string value;
- bool result = node.GetAsString(&value);
- DCHECK(result);
- EscapeJSONString(value, true, json_string_);
- return result;
- }
-
- case Value::Type::LIST: {
- json_string_->push_back('[');
- if (pretty_print_)
- json_string_->push_back(' ');
-
- const ListValue* list = nullptr;
- bool first_value_has_been_output = false;
- bool result = node.GetAsList(&list);
- DCHECK(result);
- for (const auto& value : *list) {
- if (omit_binary_values_ && value.type() == Value::Type::BINARY)
- continue;
-
- if (first_value_has_been_output) {
- json_string_->push_back(',');
- if (pretty_print_)
- json_string_->push_back(' ');
- }
-
- if (!BuildJSONString(value, depth))
- result = false;
-
- first_value_has_been_output = true;
- }
-
- if (pretty_print_)
- json_string_->push_back(' ');
- json_string_->push_back(']');
- return result;
- }
-
- case Value::Type::DICTIONARY: {
- json_string_->push_back('{');
- if (pretty_print_)
- json_string_->append(kPrettyPrintLineEnding);
-
- const DictionaryValue* dict = nullptr;
- bool first_value_has_been_output = false;
- bool result = node.GetAsDictionary(&dict);
- DCHECK(result);
- for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
- itr.Advance()) {
- if (omit_binary_values_ && itr.value().type() == Value::Type::BINARY) {
- continue;
- }
-
- if (first_value_has_been_output) {
- json_string_->push_back(',');
- if (pretty_print_)
- json_string_->append(kPrettyPrintLineEnding);
- }
-
- if (pretty_print_)
- IndentLine(depth + 1U);
-
- EscapeJSONString(itr.key(), true, json_string_);
- json_string_->push_back(':');
- if (pretty_print_)
- json_string_->push_back(' ');
-
- if (!BuildJSONString(itr.value(), depth + 1U))
- result = false;
-
- first_value_has_been_output = true;
- }
-
- if (pretty_print_) {
- json_string_->append(kPrettyPrintLineEnding);
- IndentLine(depth);
- }
-
- json_string_->push_back('}');
- return result;
- }
-
- case Value::Type::BINARY:
- // Successful only if we're allowed to omit it.
- DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
- return omit_binary_values_;
- }
- NOTREACHED();
- return false;
-}
-
-void JSONWriter::IndentLine(size_t depth) {
- json_string_->append(depth * 3U, ' ');
-}
-
-} // namespace base
diff --git a/gn/base/json/json_writer.h b/gn/base/json/json_writer.h
deleted file mode 100644
index 7edd3a68680..00000000000
--- a/gn/base/json/json_writer.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// 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 BASE_JSON_JSON_WRITER_H_
-#define BASE_JSON_JSON_WRITER_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/macros.h"
-
-namespace base {
-
-class Value;
-
-class JSONWriter {
- public:
- enum Options {
- // This option instructs the writer that if a Binary value is encountered,
- // the value (and key if within a dictionary) will be omitted from the
- // output, and success will be returned. Otherwise, if a binary value is
- // encountered, failure will be returned.
- OPTIONS_OMIT_BINARY_VALUES = 1 << 0,
-
- // This option instructs the writer to write doubles that have no fractional
- // part as a normal integer (i.e., without using exponential notation
- // or appending a '.0') as long as the value is within the range of a
- // 64-bit int.
- OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1,
-
- // Return a slightly nicer formatted json string (pads with whitespace to
- // help with readability).
- OPTIONS_PRETTY_PRINT = 1 << 2,
- };
-
- // Given a root node, generates a JSON string and puts it into |json|.
- // The output string is overwritten and not appended.
- //
- // TODO(tc): Should we generate json if it would be invalid json (e.g.,
- // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
- // values)? Return true on success and false on failure.
- static bool Write(const Value& node, std::string* json);
-
- // Same as above but with |options| which is a bunch of JSONWriter::Options
- // bitwise ORed together. Return true on success and false on failure.
- static bool WriteWithOptions(const Value& node,
- int options,
- std::string* json);
-
- private:
- JSONWriter(int options, std::string* json);
-
- // Called recursively to build the JSON string. When completed,
- // |json_string_| will contain the JSON.
- bool BuildJSONString(const Value& node, size_t depth);
-
- // Adds space to json_string_ for the indent level.
- void IndentLine(size_t depth);
-
- bool omit_binary_values_;
- bool omit_double_type_preservation_;
- bool pretty_print_;
-
- // Where we write JSON data as we generate it.
- std::string* json_string_;
-
- DISALLOW_COPY_AND_ASSIGN(JSONWriter);
-};
-
-} // namespace base
-
-#endif // BASE_JSON_JSON_WRITER_H_
diff --git a/gn/base/json/string_escape.cc b/gn/base/json/string_escape.cc
deleted file mode 100644
index 471a9d30cfa..00000000000
--- a/gn/base/json/string_escape.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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 "base/json/string_escape.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-#include <string>
-
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversion_utils.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/third_party/icu/icu_utf.h"
-
-namespace base {
-
-namespace {
-
-// Format string for printing a \uXXXX escape sequence.
-const char kU16EscapeFormat[] = "\\u%04X";
-
-// The code point to output for an invalid input code unit.
-const uint32_t kReplacementCodePoint = 0xFFFD;
-
-// Used below in EscapeSpecialCodePoint().
-static_assert('<' == 0x3C, "less than sign must be 0x3c");
-
-// Try to escape the |code_point| if it is a known special character. If
-// successful, returns true and appends the escape sequence to |dest|. This
-// isn't required by the spec, but it's more readable by humans.
-bool EscapeSpecialCodePoint(uint32_t code_point, std::string* dest) {
- // WARNING: if you add a new case here, you need to update the reader as well.
- // Note: \v is in the reader, but not here since the JSON spec doesn't
- // allow it.
- switch (code_point) {
- case '\b':
- dest->append("\\b");
- break;
- case '\f':
- dest->append("\\f");
- break;
- case '\n':
- dest->append("\\n");
- break;
- case '\r':
- dest->append("\\r");
- break;
- case '\t':
- dest->append("\\t");
- break;
- case '\\':
- dest->append("\\\\");
- break;
- case '"':
- dest->append("\\\"");
- break;
- // Escape < to prevent script execution; escaping > is not necessary and
- // not doing so save a few bytes.
- case '<':
- dest->append("\\u003C");
- break;
- // Escape the "Line Separator" and "Paragraph Separator" characters, since
- // they should be treated like a new line \r or \n.
- case 0x2028:
- dest->append("\\u2028");
- break;
- case 0x2029:
- dest->append("\\u2029");
- break;
- default:
- return false;
- }
- return true;
-}
-
-template <typename S>
-bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) {
- bool did_replacement = false;
-
- if (put_in_quotes)
- dest->push_back('"');
-
- // Casting is necessary because ICU uses int32_t. Try and do so safely.
- CHECK_LE(str.length(),
- static_cast<size_t>(std::numeric_limits<int32_t>::max()));
- const int32_t length = static_cast<int32_t>(str.length());
-
- for (int32_t i = 0; i < length; ++i) {
- uint32_t code_point;
- if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) ||
- code_point == static_cast<decltype(code_point)>(CBU_SENTINEL) ||
- !IsValidCharacter(code_point)) {
- code_point = kReplacementCodePoint;
- did_replacement = true;
- }
-
- if (EscapeSpecialCodePoint(code_point, dest))
- continue;
-
- // Escape non-printing characters.
- if (code_point < 32)
- base::StringAppendF(dest, kU16EscapeFormat, code_point);
- else
- WriteUnicodeCharacter(code_point, dest);
- }
-
- if (put_in_quotes)
- dest->push_back('"');
-
- return !did_replacement;
-}
-
-} // namespace
-
-bool EscapeJSONString(StringPiece str, bool put_in_quotes, std::string* dest) {
- return EscapeJSONStringImpl(str, put_in_quotes, dest);
-}
-
-bool EscapeJSONString(StringPiece16 str,
- bool put_in_quotes,
- std::string* dest) {
- return EscapeJSONStringImpl(str, put_in_quotes, dest);
-}
-
-std::string GetQuotedJSONString(StringPiece str) {
- std::string dest;
- bool ok = EscapeJSONStringImpl(str, true, &dest);
- DCHECK(ok);
- return dest;
-}
-
-std::string GetQuotedJSONString(StringPiece16 str) {
- std::string dest;
- bool ok = EscapeJSONStringImpl(str, true, &dest);
- DCHECK(ok);
- return dest;
-}
-
-std::string EscapeBytesAsInvalidJSONString(StringPiece str,
- bool put_in_quotes) {
- std::string dest;
-
- if (put_in_quotes)
- dest.push_back('"');
-
- for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) {
- unsigned char c = *it;
- if (EscapeSpecialCodePoint(c, &dest))
- continue;
-
- if (c < 32 || c > 126)
- base::StringAppendF(&dest, kU16EscapeFormat, c);
- else
- dest.push_back(*it);
- }
-
- if (put_in_quotes)
- dest.push_back('"');
-
- return dest;
-}
-
-} // namespace base
diff --git a/gn/base/json/string_escape.h b/gn/base/json/string_escape.h
deleted file mode 100644
index d318b6aa1c1..00000000000
--- a/gn/base/json/string_escape.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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.
-
-// This file defines utility functions for escaping strings suitable for JSON.
-
-#ifndef BASE_JSON_STRING_ESCAPE_H_
-#define BASE_JSON_STRING_ESCAPE_H_
-
-#include <string>
-
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units and
-// characters will pass through from the input to the output. Invalid code
-// units and characters will be replaced with the U+FFFD replacement character.
-// This function returns true if no replacement was necessary and false if
-// there was a lossy replacement. On return, |dest| will contain a valid UTF-8
-// JSON string.
-//
-// Non-printing control characters will be escaped as \uXXXX sequences for
-// readability.
-//
-// If |put_in_quotes| is true, then a leading and trailing double-quote mark
-// will be appended to |dest| as well.
-bool EscapeJSONString(StringPiece str, bool put_in_quotes, std::string* dest);
-
-// Performs a similar function to the UTF-8 StringPiece version above,
-// converting UTF-16 code units to UTF-8 code units and escaping non-printing
-// control characters. On return, |dest| will contain a valid UTF-8 JSON string.
-bool EscapeJSONString(StringPiece16 str, bool put_in_quotes, std::string* dest);
-
-// Helper functions that wrap the above two functions but return the value
-// instead of appending. |put_in_quotes| is always true.
-std::string GetQuotedJSONString(StringPiece str);
-std::string GetQuotedJSONString(StringPiece16 str);
-
-// Given an arbitrary byte string |str|, this will escape all non-ASCII bytes
-// as \uXXXX escape sequences. This function is *NOT* meant to be used with
-// Unicode strings and does not validate |str| as one.
-//
-// CAVEAT CALLER: The output of this function may not be valid JSON, since
-// JSON requires escape sequences to be valid UTF-16 code units. This output
-// will be mangled if passed to to the base::JSONReader, since the reader will
-// interpret it as UTF-16 and convert it to UTF-8.
-//
-// The output of this function takes the *appearance* of JSON but is not in
-// fact valid according to RFC 4627.
-std::string EscapeBytesAsInvalidJSONString(StringPiece str, bool put_in_quotes);
-
-} // namespace base
-
-#endif // BASE_JSON_STRING_ESCAPE_H_
diff --git a/gn/base/logging.cc b/gn/base/logging.cc
deleted file mode 100644
index c2c243f38f0..00000000000
--- a/gn/base/logging.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-// 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 "base/logging.h"
-
-#include <limits.h>
-#include <stdint.h>
-
-#include <thread>
-
-#include "base/macros.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <io.h>
-#include <windows.h>
-// Windows warns on using write(). It prefers _write().
-#define write(fd, buf, count) _write(fd, buf, static_cast<unsigned int>(count))
-// Windows doesn't define STDERR_FILENO. Define it here.
-#define STDERR_FILENO 2
-
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <sys/time.h>
-#include <time.h>
-#endif
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <errno.h>
-#include <paths.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
-
-#include <algorithm>
-#include <cstring>
-#include <ctime>
-#include <iomanip>
-#include <ostream>
-#include <string>
-#include <utility>
-
-#include "base/callback.h"
-#include "base/containers/stack.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-
-#if defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include "base/posix/safe_strerror.h"
-#endif
-
-namespace logging {
-
-namespace {
-
-const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
-static_assert(LOG_NUM_SEVERITIES == arraysize(log_severity_names),
- "Incorrect number of log_severity_names");
-
-const char* log_severity_name(int severity) {
- if (severity >= 0 && severity < LOG_NUM_SEVERITIES)
- return log_severity_names[severity];
- return "UNKNOWN";
-}
-
-int g_min_log_level = 0;
-
-// For LOG_ERROR and above, always print to stderr.
-const int kAlwaysPrintErrorLevel = LOG_ERROR;
-
-} // namespace
-
-#if DCHECK_IS_CONFIGURABLE
-// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be
-// determined at run-time. We default it to INFO, to avoid it triggering
-// crashes before the run-time has explicitly chosen the behaviour.
-logging::LogSeverity LOG_DCHECK = LOG_INFO;
-#endif // DCHECK_IS_CONFIGURABLE
-
-// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
-// an object of the correct type on the LHS of the unused part of the ternary
-// operator.
-std::ostream* g_swallow_stream;
-
-void SetMinLogLevel(int level) {
- g_min_log_level = std::min(LOG_FATAL, level);
-}
-
-int GetMinLogLevel() {
- return g_min_log_level;
-}
-
-bool ShouldCreateLogMessage(int severity) {
- if (severity < g_min_log_level)
- return false;
-
- // Return true here unless we know ~LogMessage won't do anything. Note that
- // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even
- // when g_logging_destination is LOG_NONE.
- return severity >= kAlwaysPrintErrorLevel;
-}
-
-// Explicit instantiations for commonly used comparisons.
-template std::string* MakeCheckOpString<int, int>(const int&,
- const int&,
- const char* names);
-template std::string* MakeCheckOpString<unsigned long, unsigned long>(
- const unsigned long&,
- const unsigned long&,
- const char* names);
-template std::string* MakeCheckOpString<unsigned long, unsigned int>(
- const unsigned long&,
- const unsigned int&,
- const char* names);
-template std::string* MakeCheckOpString<unsigned int, unsigned long>(
- const unsigned int&,
- const unsigned long&,
- const char* names);
-template std::string* MakeCheckOpString<std::string, std::string>(
- const std::string&,
- const std::string&,
- const char* name);
-
-void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) {
- (*os) << "nullptr";
-}
-
-#if defined(OS_WIN)
-LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {}
-
-LogMessage::SaveLastError::~SaveLastError() {
- ::SetLastError(last_error_);
-}
-#endif // defined(OS_WIN)
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
- : severity_(severity), file_(file), line_(line) {
- Init(file, line);
-}
-
-LogMessage::LogMessage(const char* file, int line, const char* condition)
- : severity_(LOG_FATAL), file_(file), line_(line) {
- Init(file, line);
- stream_ << "Check failed: " << condition << ". ";
-}
-
-LogMessage::LogMessage(const char* file, int line, std::string* result)
- : severity_(LOG_FATAL), file_(file), line_(line) {
- Init(file, line);
- stream_ << "Check failed: " << *result;
- delete result;
-}
-
-LogMessage::LogMessage(const char* file,
- int line,
- LogSeverity severity,
- std::string* result)
- : severity_(severity), file_(file), line_(line) {
- Init(file, line);
- stream_ << "Check failed: " << *result;
- delete result;
-}
-
-LogMessage::~LogMessage() {
- if (severity_ == LOG_FATAL) {
- stream_ << std::endl; // Newline to separate from log message.
- }
- stream_ << std::endl;
- std::string str_newline(stream_.str());
-
-#if defined(OS_WIN)
- OutputDebugStringA(str_newline.c_str());
-#endif
- ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr));
- fflush(stderr);
-
- if (severity_ == LOG_FATAL) {
- abort();
- }
-}
-
-// writes the common header info to the stream
-void LogMessage::Init(const char* file, int line) {
- base::StringPiece filename(file);
- size_t last_slash_pos = filename.find_last_of("\\/");
- if (last_slash_pos != base::StringPiece::npos)
- filename.remove_prefix(last_slash_pos + 1);
-
- // TODO(darin): It might be nice if the columns were fixed width.
-
- stream_ << '[';
- stream_ << std::this_thread::get_id() << ':';
-#if defined(OS_WIN)
- SYSTEMTIME local_time;
- GetLocalTime(&local_time);
- stream_ << std::setfill('0') << std::setw(2) << local_time.wMonth
- << std::setw(2) << local_time.wDay << '/' << std::setw(2)
- << local_time.wHour << std::setw(2) << local_time.wMinute
- << std::setw(2) << local_time.wSecond << '.' << std::setw(3)
- << local_time.wMilliseconds << ':';
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- timeval tv;
- gettimeofday(&tv, nullptr);
- time_t t = tv.tv_sec;
- struct tm local_time;
- localtime_r(&t, &local_time);
- struct tm* tm_time = &local_time;
- stream_ << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
- << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
- << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
- << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << ':';
-#else
-#error Unsupported platform
-#endif
- if (severity_ >= 0)
- stream_ << log_severity_name(severity_);
- else
- stream_ << "VERBOSE" << -severity_;
-
- stream_ << ":" << filename << "(" << line << ")] ";
-
- message_start_ = stream_.str().length();
-}
-
-#if defined(OS_WIN)
-// This has already been defined in the header, but defining it again as DWORD
-// ensures that the type used in the header is equivalent to DWORD. If not,
-// the redefinition is a compile error.
-typedef DWORD SystemErrorCode;
-#endif
-
-SystemErrorCode GetLastSystemErrorCode() {
-#if defined(OS_WIN)
- return ::GetLastError();
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- return errno;
-#endif
-}
-
-std::string SystemErrorCodeToString(SystemErrorCode error_code) {
-#if defined(OS_WIN)
- const int kErrorMessageBufferSize = 256;
- char msgbuf[kErrorMessageBufferSize];
- DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
- DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf,
- arraysize(msgbuf), nullptr);
- if (len) {
- // Messages returned by system end with line breaks.
- return base::CollapseWhitespaceASCII(msgbuf, true) +
- base::StringPrintf(" (0x%lX)", error_code);
- }
- return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)",
- GetLastError(), error_code);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- return base::safe_strerror(error_code) +
- base::StringPrintf(" (%d)", error_code);
-#endif // defined(OS_WIN)
-}
-
-#if defined(OS_WIN)
-Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
- int line,
- LogSeverity severity,
- SystemErrorCode err)
- : err_(err), log_message_(file, line, severity) {}
-
-Win32ErrorLogMessage::~Win32ErrorLogMessage() {
- stream() << ": " << SystemErrorCodeToString(err_);
-}
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-ErrnoLogMessage::ErrnoLogMessage(const char* file,
- int line,
- LogSeverity severity,
- SystemErrorCode err)
- : err_(err), log_message_(file, line, severity) {}
-
-ErrnoLogMessage::~ErrnoLogMessage() {
- stream() << ": " << SystemErrorCodeToString(err_);
-}
-#endif // defined(OS_WIN)
-
-void RawLog(int level, const char* message) {
- if (level >= g_min_log_level && message) {
- size_t bytes_written = 0;
- const size_t message_len = strlen(message);
- int rv;
- while (bytes_written < message_len) {
- rv = HANDLE_EINTR(write(STDERR_FILENO, message + bytes_written,
- message_len - bytes_written));
- if (rv < 0) {
- // Give up, nothing we can do now.
- break;
- }
- bytes_written += rv;
- }
-
- if (message_len > 0 && message[message_len - 1] != '\n') {
- do {
- rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1));
- if (rv < 0) {
- // Give up, nothing we can do now.
- break;
- }
- } while (rv != 1);
- }
- }
-
- if (level == LOG_FATAL)
- abort();
-}
-
-// This was defined at the beginning of this file.
-#undef write
-
-void LogErrorNotReached(const char* file, int line) {
- LogMessage(file, line, LOG_ERROR).stream() << "NOTREACHED() hit.";
-}
-
-} // namespace logging
-
-std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr) {
- return out << (wstr ? base::WideToUTF8(wstr) : std::string());
-}
diff --git a/gn/base/logging.h b/gn/base/logging.h
deleted file mode 100644
index 2edad38c4a5..00000000000
--- a/gn/base/logging.h
+++ /dev/null
@@ -1,956 +0,0 @@
-// 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 BASE_LOGGING_H_
-#define BASE_LOGGING_H_
-
-#include <stddef.h>
-
-#include <cassert>
-#include <cstring>
-#include <sstream>
-#include <string>
-#include <type_traits>
-#include <utility>
-
-#include "base/callback_forward.h"
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/strings/string_piece_forward.h"
-#include "base/template_util.h"
-#include "util/build_config.h"
-
-//
-// Optional message capabilities
-// -----------------------------
-// Assertion failed messages and fatal errors are displayed in a dialog box
-// before the application exits. However, running this UI creates a message
-// loop, which causes application messages to be processed and potentially
-// dispatched to existing application windows. Since the application is in a
-// bad state when this assertion dialog is displayed, these messages may not
-// get processed and hang the dialog, or the application might go crazy.
-//
-// Therefore, it can be beneficial to display the error dialog in a separate
-// process from the main application. When the logging system needs to display
-// a fatal error dialog box, it will look for a program called
-// "DebugMessage.exe" in the same directory as the application executable. It
-// will run this application with the message as the command line, and will
-// not include the name of the application as is traditional for easier
-// parsing.
-//
-// The code for DebugMessage.exe is only one line. In WinMain, do:
-// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
-//
-// If DebugMessage.exe is not found, the logging code will use a normal
-// MessageBox, potentially causing the problems discussed above.
-
-// Instructions
-// ------------
-//
-// Make a bunch of macros for logging. The way to log things is to stream
-// things to LOG(<a particular severity level>). E.g.,
-//
-// LOG(INFO) << "Found " << num_cookies << " cookies";
-//
-// You can also do conditional logging:
-//
-// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-// The CHECK(condition) macro is active in both debug and release builds and
-// effectively performs a LOG(FATAL) which terminates the process and
-// generates a crashdump unless a debugger is attached.
-//
-// There are also "debug mode" logging macros like the ones above:
-//
-// DLOG(INFO) << "Found cookies";
-//
-// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
-//
-// All "debug mode" logging is compiled away to nothing for non-debug mode
-// compiles. LOG_IF and development flags also work well together
-// because the code can be compiled away sometimes.
-//
-// We also have
-//
-// LOG_ASSERT(assertion);
-// DLOG_ASSERT(assertion);
-//
-// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
-//
-// We also override the standard 'assert' to use 'DLOG_ASSERT'.
-//
-// Lastly, there is:
-//
-// PLOG(ERROR) << "Couldn't do foo";
-// DPLOG(ERROR) << "Couldn't do foo";
-// PLOG_IF(ERROR, cond) << "Couldn't do foo";
-// DPLOG_IF(ERROR, cond) << "Couldn't do foo";
-// PCHECK(condition) << "Couldn't do foo";
-// DPCHECK(condition) << "Couldn't do foo";
-//
-// which append the last system error to the message in string form (taken from
-// GetLastError() on Windows and errno on POSIX).
-//
-// The supported severity levels for macros that allow you to specify one
-// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
-//
-// Very important: logging a message at the FATAL severity level causes
-// the program to terminate (after the message is logged).
-//
-// There is the special severity of DFATAL, which logs FATAL in debug mode,
-// ERROR in normal mode.
-
-namespace logging {
-
-// Sets the log level. Anything at or above this level will be written to the
-// log file/displayed to the user (if applicable). Anything below this level
-// will be silently ignored. The log level defaults to 0 (everything is logged
-// up to level INFO) if this function is not called.
-void SetMinLogLevel(int level);
-
-// Gets the current log level.
-int GetMinLogLevel();
-
-// Used by LOG_IS_ON to lazy-evaluate stream arguments.
-bool ShouldCreateLogMessage(int severity);
-
-// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints
-// to Clang which control what code paths are statically analyzed,
-// and is meant to be used in conjunction with assert & assert-like functions.
-// The expression is passed straight through if analysis isn't enabled.
-//
-// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current
-// codepath and any other branching codepaths that might follow.
-#if defined(__clang_analyzer__)
-
-inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
- return false;
-}
-
-inline constexpr bool AnalyzerAssumeTrue(bool arg) {
- // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is
- // false.
- return arg || AnalyzerNoReturn();
-}
-
-#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg))
-#define ANALYZER_SKIP_THIS_PATH() \
- static_cast<void>(::logging::AnalyzerNoReturn())
-#define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
-
-#else // !defined(__clang_analyzer__)
-
-#define ANALYZER_ASSUME_TRUE(arg) (arg)
-#define ANALYZER_SKIP_THIS_PATH()
-#define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
-
-#endif // defined(__clang_analyzer__)
-
-typedef int LogSeverity;
-const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
-// Note: the log severities are used to index into the array of names,
-// see log_severity_names.
-const LogSeverity LOG_INFO = 0;
-const LogSeverity LOG_WARNING = 1;
-const LogSeverity LOG_ERROR = 2;
-const LogSeverity LOG_FATAL = 3;
-const LogSeverity LOG_NUM_SEVERITIES = 4;
-
-// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode
-#if defined(NDEBUG)
-const LogSeverity LOG_DFATAL = LOG_ERROR;
-#else
-const LogSeverity LOG_DFATAL = LOG_FATAL;
-#endif
-
-// A few definitions of macros that don't generate much code. These are used
-// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
-// better to have compact code for these operations.
-#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \
- ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
- ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DCHECK, ##__VA_ARGS__)
-
-#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
-#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
-#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
-#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
-#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
-#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage)
-
-#if defined(OS_WIN)
-// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
-// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
-// to keep using this syntax, we define this macro to do the same thing
-// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
-// the Windows SDK does for consistency.
-#define ERROR 0
-#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \
- COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
-// Needed for LOG_IS_ON(ERROR).
-const LogSeverity LOG_0 = LOG_ERROR;
-#endif
-
-// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,
-// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will
-// always fire if they fail.
-#define LOG_IS_ON(severity) \
- (::logging::ShouldCreateLogMessage(::logging::LOG_##severity))
-
-// Helper macro which avoids evaluating the arguments to a stream if
-// the condition doesn't hold. Condition is evaluated once and only once.
-#define LAZY_STREAM(stream, condition) \
- !(condition) ? (void)0 : ::logging::LogMessageVoidify() & (stream)
-
-// We use the preprocessor's merging operator, "##", so that, e.g.,
-// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
-// subtle difference between ostream member streaming functions (e.g.,
-// ostream::operator<<(int) and ostream non-member streaming functions
-// (e.g., ::operator<<(ostream&, string&): it turns out that it's
-// impossible to stream something like a string directly to an unnamed
-// ostream. We employ a neat hack by calling the stream() member
-// function of LogMessage which seems to avoid the problem.
-#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_##severity.stream()
-
-#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
-#define LOG_IF(severity, condition) \
- LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
-
-#define LOG_ASSERT(condition) \
- LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \
- << "Assert failed: " #condition ". "
-
-#if defined(OS_WIN)
-#define PLOG_STREAM(severity) \
- COMPACT_GOOGLE_LOG_EX_##severity(Win32ErrorLogMessage, \
- ::logging::GetLastSystemErrorCode()) \
- .stream()
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#define PLOG_STREAM(severity) \
- COMPACT_GOOGLE_LOG_EX_##severity(ErrnoLogMessage, \
- ::logging::GetLastSystemErrorCode()) \
- .stream()
-#endif
-
-#define PLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
-
-#define PLOG_IF(severity, condition) \
- LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
-
-extern std::ostream* g_swallow_stream;
-
-// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to
-// avoid the creation of an object with a non-trivial destructor (LogMessage).
-// On MSVC x86 (checked on 2015 Update 3), this causes a few additional
-// pointless instructions to be emitted even at full optimization level, even
-// though the : arm of the ternary operator is clearly never executed. Using a
-// simpler object to be &'d with Voidify() avoids these extra instructions.
-// Using a simpler POD object with a templated operator<< also works to avoid
-// these instructions. However, this causes warnings on statically defined
-// implementations of operator<<(std::ostream, ...) in some .cc files, because
-// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an
-// ostream* also is not suitable, because some compilers warn of undefined
-// behavior.
-#define EAT_STREAM_PARAMETERS \
- true ? (void)0 \
- : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream)
-
-// Captures the result of a CHECK_EQ (for example) and facilitates testing as a
-// boolean.
-class CheckOpResult {
- public:
- // |message| must be non-null if and only if the check failed.
- CheckOpResult(std::string* message) : message_(message) {}
- // Returns true if the check succeeded.
- operator bool() const { return !message_; }
- // Returns the message.
- std::string* message() { return message_; }
-
- private:
- std::string* message_;
-};
-
-// Crashes in the fastest possible way with no attempt at logging.
-// There are different constraints to satisfy here, see http://crbug.com/664209
-// for more context:
-// - The trap instructions, and hence the PC value at crash time, have to be
-// distinct and not get folded into the same opcode by the compiler.
-// On Linux/Android this is tricky because GCC still folds identical
-// asm volatile blocks. The workaround is generating distinct opcodes for
-// each CHECK using the __COUNTER__ macro.
-// - The debug info for the trap instruction has to be attributed to the source
-// line that has the CHECK(), to make crash reports actionable. This rules
-// out the ability of using a inline function, at least as long as clang
-// doesn't support attribute(artificial).
-// - Failed CHECKs should produce a signal that is distinguishable from an
-// invalid memory access, to improve the actionability of crash reports.
-// - The compiler should treat the CHECK as no-return instructions, so that the
-// trap code can be efficiently packed in the prologue of the function and
-// doesn't interfere with the main execution flow.
-// - When debugging, developers shouldn't be able to accidentally step over a
-// CHECK. This is achieved by putting opcodes that will cause a non
-// continuable exception after the actual trap instruction.
-// - Don't cause too much binary bloat.
-#if defined(COMPILER_GCC)
-
-#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
-// int 3 will generate a SIGTRAP.
-#define TRAP_SEQUENCE() \
- asm volatile( \
- "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
-
-#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL)
-// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
-// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
-// cause a SIGTRAP from userspace without using a syscall (which would be a
-// problem for sandboxing).
-#define TRAP_SEQUENCE() \
- asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
-
-#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL)
-// This will always generate a SIGTRAP on arm64.
-#define TRAP_SEQUENCE() \
- asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
-
-#else
-// Crash report accuracy will not be guaranteed on other architectures, but at
-// least this will crash as expected.
-#define TRAP_SEQUENCE() __builtin_trap()
-#endif // ARCH_CPU_*
-
-// CHECK() and the trap sequence can be invoked from a constexpr function.
-// This could make compilation fail on GCC, as it forbids directly using inline
-// asm inside a constexpr function. However, it allows calling a lambda
-// expression including the same asm.
-// The side effect is that the top of the stacktrace will not point to the
-// calling function, but to this anonymous lambda. This is still useful as the
-// full name of the lambda will typically include the name of the function that
-// calls CHECK() and the debugger will still break at the right line of code.
-#if !defined(__clang__)
-#define WRAPPED_TRAP_SEQUENCE() \
- do { \
- [] { TRAP_SEQUENCE(); }(); \
- } while (false)
-#else
-#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
-#endif
-
-#define IMMEDIATE_CRASH() \
- ({ \
- WRAPPED_TRAP_SEQUENCE(); \
- __builtin_unreachable(); \
- })
-
-#elif defined(COMPILER_MSVC)
-
-// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
-// instruction following the __debugbreak() to have it emit distinct locations
-// for CHECKs rather than collapsing them all together. It would be nice to use
-// a short intrinsic to do this (and perhaps have only one implementation for
-// both clang and MSVC), however clang-cl currently does not support intrinsics.
-// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
-// two implementations. Normally clang-cl's version will be 5 bytes (1 for
-// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
-// https://crbug.com/694670 clang-cl doesn't currently support %'ing
-// __COUNTER__, so eventually it will emit the dword form of push.
-// TODO(scottmg): Reinvestigate a short sequence that will work on both
-// compilers once clang supports more intrinsics. See https://crbug.com/693713.
-#if defined(__clang__)
-#define IMMEDIATE_CRASH() \
- ({ \
- {__asm int 3 __asm ud2 __asm push __COUNTER__}; \
- __builtin_unreachable(); \
- })
-#else
-#define IMMEDIATE_CRASH() __debugbreak()
-#endif // __clang__
-
-#else
-#error Port
-#endif
-
-// CHECK dies with a fatal error if condition is not true. It is *not*
-// controlled by NDEBUG, so the check will be executed regardless of
-// compilation mode.
-//
-// We make sure CHECK et al. always evaluates their arguments, as
-// doing CHECK(FunctionWithSideEffect()) is a common idiom.
-
-#if defined(OFFICIAL_BUILD) && defined(NDEBUG)
-
-// Make all CHECK functions discard their log strings to reduce code bloat, and
-// improve performance, for official release builds.
-//
-// This is not calling BreakDebugger since this is called frequently, and
-// calling an out-of-line function instead of a noreturn inline macro prevents
-// compiler optimizations.
-#define CHECK(condition) \
- UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS
-
-// PCHECK includes the system error code, which is useful for determining
-// why the condition failed. In official builds, preserve only the error code
-// message so that it is available in crash reports. The stringified
-// condition and any additional stream parameters are dropped.
-#define PCHECK(condition) \
- LAZY_STREAM(PLOG_STREAM(FATAL), UNLIKELY(!(condition))); \
- EAT_STREAM_PARAMETERS
-
-#define CHECK_OP(name, op, val1, val2) CHECK((val1)op(val2))
-
-#else // !(OFFICIAL_BUILD && NDEBUG)
-
-#if defined(_PREFAST_) && defined(OS_WIN)
-// Use __analysis_assume to tell the VC++ static analysis engine that
-// assert conditions are true, to suppress warnings. The LAZY_STREAM
-// parameter doesn't reference 'condition' in /analyze builds because
-// this evaluation confuses /analyze. The !! before condition is because
-// __analysis_assume gets confused on some conditions:
-// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/
-
-#define CHECK(condition) \
- __analysis_assume(!!(condition)), LAZY_STREAM(LOG_STREAM(FATAL), false) \
- << "Check failed: " #condition ". "
-
-#define PCHECK(condition) \
- __analysis_assume(!!(condition)), LAZY_STREAM(PLOG_STREAM(FATAL), false) \
- << "Check failed: " #condition ". "
-
-#else // _PREFAST_
-
-// Do as much work as possible out of line to reduce inline code size.
-#define CHECK(condition) \
- LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
- !ANALYZER_ASSUME_TRUE(condition))
-
-#define PCHECK(condition) \
- LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
- << "Check failed: " #condition ". "
-
-#endif // _PREFAST_
-
-// Helper macro for binary operators.
-// Don't use this macro directly in your code, use CHECK_EQ et al below.
-// The 'switch' is used to prevent the 'else' from being ambiguous when the
-// macro is used in an 'if' clause such as:
-// if (a == 1)
-// CHECK_EQ(2, a);
-#define CHECK_OP(name, op, val1, val2) \
- switch (0) \
- case 0: \
- default: \
- if (::logging::CheckOpResult true_if_passed = \
- ::logging::Check##name##Impl((val1), (val2), \
- #val1 " " #op " " #val2)) \
- ; \
- else \
- ::logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()) \
- .stream()
-
-#endif // !(OFFICIAL_BUILD && NDEBUG)
-
-// This formats a value for a failing CHECK_XX statement. Ordinarily,
-// it uses the definition for operator<<, with a few special cases below.
-template <typename T>
-inline typename std::enable_if<
- base::internal::SupportsOstreamOperator<const T&>::value &&
- !std::is_function<typename std::remove_pointer<T>::type>::value,
- void>::type
-MakeCheckOpValueString(std::ostream* os, const T& v) {
- (*os) << v;
-}
-
-// Provide an overload for functions and function pointers. Function pointers
-// don't implicitly convert to void* but do implicitly convert to bool, so
-// without this function pointers are always printed as 1 or 0. (MSVC isn't
-// standards-conforming here and converts function pointers to regular
-// pointers, so this is a no-op for MSVC.)
-template <typename T>
-inline typename std::enable_if<
- std::is_function<typename std::remove_pointer<T>::type>::value,
- void>::type
-MakeCheckOpValueString(std::ostream* os, const T& v) {
- (*os) << reinterpret_cast<const void*>(v);
-}
-
-// We need overloads for enums that don't support operator<<.
-// (i.e. scoped enums where no operator<< overload was declared).
-template <typename T>
-inline typename std::enable_if<
- !base::internal::SupportsOstreamOperator<const T&>::value &&
- std::is_enum<T>::value,
- void>::type
-MakeCheckOpValueString(std::ostream* os, const T& v) {
- (*os) << static_cast<typename std::underlying_type<T>::type>(v);
-}
-
-// We need an explicit overload for std::nullptr_t.
-void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p);
-
-// Build the error message string. This is separate from the "Impl"
-// function template because it is not performance critical and so can
-// be out of line, while the "Impl" code should be inline. Caller
-// takes ownership of the returned string.
-template <class t1, class t2>
-std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
- std::ostringstream ss;
- ss << names << " (";
- MakeCheckOpValueString(&ss, v1);
- ss << " vs. ";
- MakeCheckOpValueString(&ss, v2);
- ss << ")";
- std::string* msg = new std::string(ss.str());
- return msg;
-}
-
-// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
-// in logging.cc.
-extern template std::string* MakeCheckOpString<int, int>(const int&,
- const int&,
- const char* names);
-extern template std::string* MakeCheckOpString<unsigned long, unsigned long>(
- const unsigned long&,
- const unsigned long&,
- const char* names);
-extern template std::string* MakeCheckOpString<unsigned long, unsigned int>(
- const unsigned long&,
- const unsigned int&,
- const char* names);
-extern template std::string* MakeCheckOpString<unsigned int, unsigned long>(
- const unsigned int&,
- const unsigned long&,
- const char* names);
-extern template std::string* MakeCheckOpString<std::string, std::string>(
- const std::string&,
- const std::string&,
- const char* name);
-
-// Helper functions for CHECK_OP macro.
-// The (int, int) specialization works around the issue that the compiler
-// will not instantiate the template version of the function on values of
-// unnamed enum type - see comment below.
-//
-// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
-// static analysis builds, blocks analysis of the current path if the
-// condition is false.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
- template <class t1, class t2> \
- inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
- const char* names) { \
- if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
- return NULL; \
- else \
- return ::logging::MakeCheckOpString(v1, v2, names); \
- } \
- inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
- if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
- return NULL; \
- else \
- return ::logging::MakeCheckOpString(v1, v2, names); \
- }
-DEFINE_CHECK_OP_IMPL(EQ, ==)
-DEFINE_CHECK_OP_IMPL(NE, !=)
-DEFINE_CHECK_OP_IMPL(LE, <=)
-DEFINE_CHECK_OP_IMPL(LT, <)
-DEFINE_CHECK_OP_IMPL(GE, >=)
-DEFINE_CHECK_OP_IMPL(GT, >)
-#undef DEFINE_CHECK_OP_IMPL
-
-#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
-#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
-#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
-#define CHECK_LT(val1, val2) CHECK_OP(LT, <, val1, val2)
-#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
-#define CHECK_GT(val1, val2) CHECK_OP(GT, >, val1, val2)
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-#define DCHECK_IS_ON() 0
-#else
-#define DCHECK_IS_ON() 1
-#endif
-
-// Definitions for DLOG et al.
-
-#if DCHECK_IS_ON()
-
-#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
-#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
-#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
-#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
-
-#else // DCHECK_IS_ON()
-
-// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition|
-// (which may reference a variable defined only if DCHECK_IS_ON()).
-// Contrast this with DCHECK et al., which has different behavior.
-
-#define DLOG_IS_ON(severity) false
-#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
-#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
-#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
-
-#endif // DCHECK_IS_ON()
-
-#define DLOG(severity) LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
-
-#define DPLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
-
-// Definitions for DCHECK et al.
-
-#if DCHECK_IS_ON()
-
-#if DCHECK_IS_CONFIGURABLE
-extern LogSeverity LOG_DCHECK;
-#else
-const LogSeverity LOG_DCHECK = LOG_FATAL;
-#endif
-
-#else // DCHECK_IS_ON()
-
-// There may be users of LOG_DCHECK that are enabled independently
-// of DCHECK_IS_ON(), so default to FATAL logging for those.
-const LogSeverity LOG_DCHECK = LOG_FATAL;
-
-#endif // DCHECK_IS_ON()
-
-// DCHECK et al. make sure to reference |condition| regardless of
-// whether DCHECKs are enabled; this is so that we don't get unused
-// variable warnings if the only use of a variable is in a DCHECK.
-// This behavior is different from DLOG_IF et al.
-//
-// Note that the definition of the DCHECK macros depends on whether or not
-// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
-// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
-
-#if defined(_PREFAST_) && defined(OS_WIN)
-// See comments on the previous use of __analysis_assume.
-
-#define DCHECK(condition) \
- __analysis_assume(!!(condition)), LAZY_STREAM(LOG_STREAM(DCHECK), false) \
- << "Check failed: " #condition ". "
-
-#define DPCHECK(condition) \
- __analysis_assume(!!(condition)), LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
- << "Check failed: " #condition ". "
-
-#else // !(defined(_PREFAST_) && defined(OS_WIN))
-
-#if DCHECK_IS_ON()
-
-#define DCHECK(condition) \
- LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
- << "Check failed: " #condition ". "
-#define DPCHECK(condition) \
- LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
- << "Check failed: " #condition ". "
-
-#else // DCHECK_IS_ON()
-
-#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
-#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
-
-#endif // DCHECK_IS_ON()
-
-#endif // defined(_PREFAST_) && defined(OS_WIN)
-
-// Helper macro for binary operators.
-// Don't use this macro directly in your code, use DCHECK_EQ et al below.
-// The 'switch' is used to prevent the 'else' from being ambiguous when the
-// macro is used in an 'if' clause such as:
-// if (a == 1)
-// DCHECK_EQ(2, a);
-#if DCHECK_IS_ON()
-
-#define DCHECK_OP(name, op, val1, val2) \
- switch (0) \
- case 0: \
- default: \
- if (::logging::CheckOpResult true_if_passed = \
- DCHECK_IS_ON() ? ::logging::Check##name##Impl( \
- (val1), (val2), #val1 " " #op " " #val2) \
- : nullptr) \
- ; \
- else \
- ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \
- true_if_passed.message()) \
- .stream()
-
-#else // DCHECK_IS_ON()
-
-// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<<
-// overloads for |val1| and |val2| to avoid potential compiler warnings about
-// unused functions. For the same reason, it also compares |val1| and |val2|
-// using |op|.
-//
-// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated
-// once. Even though |val1| and |val2| appear twice in this version of the macro
-// expansion, this is OK, since the expression is never actually evaluated.
-#define DCHECK_OP(name, op, val1, val2) \
- EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \
- ::logging::g_swallow_stream, val1), \
- ::logging::MakeCheckOpValueString( \
- ::logging::g_swallow_stream, val2), \
- (val1)op(val2))
-
-#endif // DCHECK_IS_ON()
-
-// Equality/Inequality checks - compare two values, and log a
-// LOG_DCHECK message including the two values when the result is not
-// as expected. The values must have operator<<(ostream, ...)
-// defined.
-//
-// You may append to the error message like so:
-// DCHECK_NE(1, 2) << "The world must be ending!";
-//
-// We are very careful to ensure that each argument is evaluated exactly
-// once, and that anything which is legal to pass as a function argument is
-// legal here. In particular, the arguments may be temporary expressions
-// which will end up being destroyed at the end of the apparent statement,
-// for example:
-// DCHECK_EQ(string("abc")[1], 'b');
-//
-// WARNING: These don't compile correctly if one of the arguments is a pointer
-// and the other is NULL. In new code, prefer nullptr instead. To
-// work around this for C++98, simply static_cast NULL to the type of the
-// desired pointer.
-
-#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
-#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
-#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
-#define DCHECK_LT(val1, val2) DCHECK_OP(LT, <, val1, val2)
-#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
-#define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2)
-
-#if !DCHECK_IS_ON() && defined(OS_CHROMEOS)
-// Implement logging of NOTREACHED() as a dedicated function to get function
-// call overhead down to a minimum.
-void LogErrorNotReached(const char* file, int line);
-#define NOTREACHED() \
- true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \
- : EAT_STREAM_PARAMETERS
-#else
-#define NOTREACHED() DCHECK(false)
-#endif
-
-// Redefine the standard assert to use our nice log files
-#undef assert
-#define assert(x) DLOG_ASSERT(x)
-
-// This class more or less represents a particular log message. You
-// create an instance of LogMessage and then stream stuff to it.
-// When you finish streaming to it, ~LogMessage is called and the
-// full message gets streamed to the appropriate destination.
-//
-// You shouldn't actually use LogMessage's constructor to log things,
-// though. You should use the LOG() macro (and variants thereof)
-// above.
-class LogMessage {
- public:
- // Used for LOG(severity).
- LogMessage(const char* file, int line, LogSeverity severity);
-
- // Used for CHECK(). Implied severity = LOG_FATAL.
- LogMessage(const char* file, int line, const char* condition);
-
- // Used for CHECK_EQ(), etc. Takes ownership of the given string.
- // Implied severity = LOG_FATAL.
- LogMessage(const char* file, int line, std::string* result);
-
- // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
- LogMessage(const char* file,
- int line,
- LogSeverity severity,
- std::string* result);
-
- ~LogMessage();
-
- std::ostream& stream() { return stream_; }
-
- LogSeverity severity() { return severity_; }
- std::string str() { return stream_.str(); }
-
- private:
- void Init(const char* file, int line);
-
- LogSeverity severity_;
- std::ostringstream stream_;
- size_t message_start_; // Offset of the start of the message (past prefix
- // info).
- // The file and line information passed in to the constructor.
- const char* file_;
- const int line_;
-
-#if defined(OS_WIN)
- // Stores the current value of GetLastError in the constructor and restores
- // it in the destructor by calling SetLastError.
- // This is useful since the LogMessage class uses a lot of Win32 calls
- // that will lose the value of GLE and the code that called the log function
- // will have lost the thread error value when the log call returns.
- class SaveLastError {
- public:
- SaveLastError();
- ~SaveLastError();
-
- unsigned long get_error() const { return last_error_; }
-
- protected:
- unsigned long last_error_;
- };
-
- SaveLastError last_error_;
-#endif
-
- DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
-
-// This class is used to explicitly ignore values in the conditional
-// logging macros. This avoids compiler warnings like "value computed
-// is not used" and "statement has no effect".
-class LogMessageVoidify {
- public:
- LogMessageVoidify() = default;
- // This has to be an operator with a precedence lower than << but
- // higher than ?:
- void operator&(std::ostream&) {}
-};
-
-#if defined(OS_WIN)
-typedef unsigned long SystemErrorCode;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-typedef int SystemErrorCode;
-#endif
-
-// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
-// pull in windows.h just for GetLastError() and DWORD.
-SystemErrorCode GetLastSystemErrorCode();
-std::string SystemErrorCodeToString(SystemErrorCode error_code);
-
-#if defined(OS_WIN)
-// Appends a formatted system message of the GetLastError() type.
-class Win32ErrorLogMessage {
- public:
- Win32ErrorLogMessage(const char* file,
- int line,
- LogSeverity severity,
- SystemErrorCode err);
-
- // Appends the error message before destructing the encapsulated class.
- ~Win32ErrorLogMessage();
-
- std::ostream& stream() { return log_message_.stream(); }
-
- private:
- SystemErrorCode err_;
- LogMessage log_message_;
-
- DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
-};
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-// Appends a formatted system message of the errno type
-class ErrnoLogMessage {
- public:
- ErrnoLogMessage(const char* file,
- int line,
- LogSeverity severity,
- SystemErrorCode err);
-
- // Appends the error message before destructing the encapsulated class.
- ~ErrnoLogMessage();
-
- std::ostream& stream() { return log_message_.stream(); }
-
- private:
- SystemErrorCode err_;
- LogMessage log_message_;
-
- DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
-};
-#endif // OS_WIN
-
-// Closes the log file explicitly if open.
-// NOTE: Since the log file is opened as necessary by the action of logging
-// statements, there's no guarantee that it will stay closed
-// after this call.
-void CloseLogFile();
-
-// Async signal safe logging mechanism.
-void RawLog(int level, const char* message);
-
-#define RAW_LOG(level, message) \
- ::logging::RawLog(::logging::LOG_##level, message)
-
-#define RAW_CHECK(condition) \
- do { \
- if (!(condition)) \
- ::logging::RawLog(::logging::LOG_FATAL, \
- "Check failed: " #condition "\n"); \
- } while (0)
-
-#if defined(OS_WIN)
-// Returns true if logging to file is enabled.
-bool IsLoggingToFileEnabled();
-
-// Returns the default log file path.
-std::wstring GetLogFileFullPath();
-#endif
-
-} // namespace logging
-
-// Note that "The behavior of a C++ program is undefined if it adds declarations
-// or definitions to namespace std or to a namespace within namespace std unless
-// otherwise specified." --C++11[namespace.std]
-//
-// We've checked that this particular definition has the intended behavior on
-// our implementations, but it's prone to breaking in the future, and please
-// don't imitate this in your own definitions without checking with some
-// standard library experts.
-namespace std {
-// These functions are provided as a convenience for logging, which is where we
-// use streams (it is against Google style to use streams in other places). It
-// is designed to allow you to emit non-ASCII Unicode strings to the log file,
-// which is normally ASCII. It is relatively slow, so try not to use it for
-// common cases. Non-ASCII characters will be converted to UTF-8 by these
-// operators.
-std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
-inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
- return out << wstr.c_str();
-}
-} // namespace std
-
-// The NOTIMPLEMENTED() macro annotates codepaths which have not been
-// implemented yet. If output spam is a serious concern,
-// NOTIMPLEMENTED_LOG_ONCE can be used.
-
-#if defined(COMPILER_GCC)
-// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
-// of the current function in the NOTIMPLEMENTED message.
-#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
-#else
-#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
-#endif
-
-#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
-#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
-#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS
-#else
-#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
-#define NOTIMPLEMENTED_LOG_ONCE() \
- do { \
- static bool logged_once = false; \
- LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
- logged_once = true; \
- } while (0); \
- EAT_STREAM_PARAMETERS
-#endif
-
-#endif // BASE_LOGGING_H_
diff --git a/gn/base/macros.h b/gn/base/macros.h
deleted file mode 100644
index 321f65bc513..00000000000
--- a/gn/base/macros.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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 macros and macro-like constructs (e.g., templates) that
-// are commonly used throughout Chromium source. (It may also contain things
-// that are closely related to things that are commonly used that belong in this
-// file.)
-
-#ifndef BASE_MACROS_H_
-#define BASE_MACROS_H_
-
-#include <stddef.h> // For size_t.
-
-// Distinguish mips32.
-#if defined(__mips__) && (_MIPS_SIM == _ABIO32) && !defined(__mips32__)
-#define __mips32__
-#endif
-
-// Distinguish mips64.
-#if defined(__mips__) && (_MIPS_SIM == _ABI64) && !defined(__mips64__)
-#define __mips64__
-#endif
-
-// Put this in the declarations for a class to be uncopyable.
-#define DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete
-
-// Put this in the declarations for a class to be unassignable.
-#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete
-
-// Put this in the declarations for a class to be uncopyable and unassignable.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- DISALLOW_COPY(TypeName); \
- DISALLOW_ASSIGN(TypeName)
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-// This is especially useful for classes containing only static methods.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName() = delete; \
- DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// The arraysize(arr) macro returns the # of elements in an array arr. The
-// expression is a compile-time constant, and therefore can be used in defining
-// new arrays, for example. If you use arraysize on a pointer by mistake, you
-// will get a compile-time error. For the technical details, refer to
-// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-//
-// DEPRECATED, please use base::size(array) instead.
-// TODO(https://crbug.com/837308): Replace existing arraysize usages.
-template <typename T, size_t N>
-char (&ArraySizeHelper(T (&array)[N]))[N];
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-// Used to explicitly mark the return value of a function as unused. If you are
-// really sure you don't want to do anything with the return value of a function
-// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
-//
-// std::unique_ptr<MyType> my_var = ...;
-// if (TakeOwnership(my_var.get()) == SUCCESS)
-// ignore_result(my_var.release());
-//
-template <typename T>
-inline void ignore_result(const T&) {}
-
-namespace base {
-
-// Use these to declare and define a static local variable (static T;) so that
-// it is leaked so that its destructors are not called at exit. This is
-// thread-safe.
-//
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEPRECATED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-// Please don't use this macro. Use a function-local static of type
-// base::NoDestructor<T> instead:
-//
-// Factory& Factory::GetInstance() {
-// static base::NoDestructor<Factory> instance;
-// return *instance;
-// }
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \
- static type& name = *new type arguments
-
-// Workaround for MSVC, which expands __VA_ARGS__ as one macro argument. To
-// work around this bug, wrap the entire expression in this macro...
-#define CR_EXPAND_ARG(arg) arg
-
-} // namespace base
-
-#endif // BASE_MACROS_H_
diff --git a/gn/base/md5.cc b/gn/base/md5.cc
deleted file mode 100644
index c66f7b220c1..00000000000
--- a/gn/base/md5.cc
+++ /dev/null
@@ -1,299 +0,0 @@
-// 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.
-
-// The original file was copied from sqlite, and was in the public domain.
-
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to MD5Init, call MD5Update as
- * needed on buffers full of bytes, and then call MD5Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-#include "base/md5.h"
-
-#include <stddef.h>
-
-namespace {
-
-struct Context {
- uint32_t buf[4];
- uint32_t bits[2];
- uint8_t in[64];
-};
-
-/*
- * Note: this code is harmless on little-endian machines.
- */
-void byteReverse(uint8_t* buf, unsigned longs) {
- do {
- uint32_t temp =
- static_cast<uint32_t>(static_cast<unsigned>(buf[3]) << 8 | buf[2])
- << 16 |
- (static_cast<unsigned>(buf[1]) << 8 | buf[0]);
- *reinterpret_cast<uint32_t*>(buf) = temp;
- buf += 4;
- } while (--longs);
-}
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f, w, x, y, z, data, s) \
- (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data. MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-void MD5Transform(uint32_t buf[4], const uint32_t in[16]) {
- uint32_t a, b, c, d;
-
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
-
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
-
-} // namespace
-
-namespace base {
-
-/*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-void MD5Init(MD5Context* context) {
- struct Context* ctx = reinterpret_cast<struct Context*>(context);
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
- ctx->bits[0] = 0;
- ctx->bits[1] = 0;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-void MD5Update(MD5Context* context, const StringPiece& data) {
- struct Context* ctx = reinterpret_cast<struct Context*>(context);
- const uint8_t* buf = reinterpret_cast<const uint8_t*>(data.data());
- size_t len = data.size();
-
- /* Update bitcount */
-
- uint32_t t = ctx->bits[0];
- if ((ctx->bits[0] = t + (static_cast<uint32_t>(len) << 3)) < t)
- ctx->bits[1]++; /* Carry from low to high */
- ctx->bits[1] += static_cast<uint32_t>(len >> 29);
-
- t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
-
- /* Handle any leading odd-sized chunks */
-
- if (t) {
- uint8_t* p = static_cast<uint8_t*>(ctx->in + t);
-
- t = 64 - t;
- if (len < t) {
- memcpy(p, buf, len);
- return;
- }
- memcpy(p, buf, t);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
- buf += t;
- len -= t;
- }
-
- /* Process data in 64-byte chunks */
-
- while (len >= 64) {
- memcpy(ctx->in, buf, 64);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
- buf += 64;
- len -= 64;
- }
-
- /* Handle any remaining bytes of data. */
-
- memcpy(ctx->in, buf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-void MD5Final(MD5Digest* digest, MD5Context* context) {
- struct Context* ctx = reinterpret_cast<struct Context*>(context);
- unsigned count;
- uint8_t* p;
-
- /* Compute number of bytes mod 64 */
- count = (ctx->bits[0] >> 3) & 0x3F;
-
- /* Set the first char of padding to 0x80. This is safe since there is
- always at least one byte free */
- p = ctx->in + count;
- *p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = 64 - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8) {
- /* Two lots of padding: Pad the first block to 64 bytes */
- memset(p, 0, count);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
-
- /* Now fill the next block with 56 bytes */
- memset(ctx->in, 0, 56);
- } else {
- /* Pad block to 56 bytes */
- memset(p, 0, count - 8);
- }
- byteReverse(ctx->in, 14);
-
- /* Append length in bits and transform */
- memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], &ctx->bits[0],
- sizeof(ctx->bits[0]));
- memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], &ctx->bits[1],
- sizeof(ctx->bits[1]));
-
- MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
- byteReverse(reinterpret_cast<uint8_t*>(ctx->buf), 4);
- memcpy(digest->a, ctx->buf, 16);
- memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
-}
-
-void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) {
- /* MD5Final mutates the MD5Context*. Make a copy for generating the
- intermediate value. */
- MD5Context context_copy;
- memcpy(&context_copy, context, sizeof(context_copy));
- MD5Final(digest, &context_copy);
-}
-
-std::string MD5DigestToBase16(const MD5Digest& digest) {
- static char const zEncode[] = "0123456789abcdef";
-
- std::string ret;
- ret.resize(32);
-
- for (int i = 0, j = 0; i < 16; i++, j += 2) {
- uint8_t a = digest.a[i];
- ret[j] = zEncode[(a >> 4) & 0xf];
- ret[j + 1] = zEncode[a & 0xf];
- }
- return ret;
-}
-
-void MD5Sum(const void* data, size_t length, MD5Digest* digest) {
- MD5Context ctx;
- MD5Init(&ctx);
- MD5Update(&ctx, StringPiece(reinterpret_cast<const char*>(data), length));
- MD5Final(digest, &ctx);
-}
-
-std::string MD5String(const StringPiece& str) {
- MD5Digest digest;
- MD5Sum(str.data(), str.length(), &digest);
- return MD5DigestToBase16(digest);
-}
-
-} // namespace base
diff --git a/gn/base/md5.h b/gn/base/md5.h
deleted file mode 100644
index 16a0a6f90d9..00000000000
--- a/gn/base/md5.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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.
-
-#ifndef BASE_MD5_H_
-#define BASE_MD5_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// MD5 stands for Message Digest algorithm 5.
-// MD5 is a robust hash function, designed for cyptography, but often used
-// for file checksums. The code is complex and slow, but has few
-// collisions.
-// See Also:
-// http://en.wikipedia.org/wiki/MD5
-
-// These functions perform MD5 operations. The simplest call is MD5Sum() to
-// generate the MD5 sum of the given data.
-//
-// You can also compute the MD5 sum of data incrementally by making multiple
-// calls to MD5Update():
-// MD5Context ctx; // intermediate MD5 data: do not use
-// MD5Init(&ctx);
-// MD5Update(&ctx, data1, length1);
-// MD5Update(&ctx, data2, length2);
-// ...
-//
-// MD5Digest digest; // the result of the computation
-// MD5Final(&digest, &ctx);
-//
-// You can call MD5DigestToBase16() to generate a string of the digest.
-
-// The output of an MD5 operation.
-struct MD5Digest {
- uint8_t a[16];
-};
-
-// Used for storing intermediate data during an MD5 computation. Callers
-// should not access the data.
-typedef char MD5Context[88];
-
-// Initializes the given MD5 context structure for subsequent calls to
-// MD5Update().
-void MD5Init(MD5Context* context);
-
-// For the given buffer of |data| as a StringPiece, updates the given MD5
-// context with the sum of the data. You can call this any number of times
-// during the computation, except that MD5Init() must have been called first.
-void MD5Update(MD5Context* context, const StringPiece& data);
-
-// Finalizes the MD5 operation and fills the buffer with the digest.
-void MD5Final(MD5Digest* digest, MD5Context* context);
-
-// MD5IntermediateFinal() generates a digest without finalizing the MD5
-// operation. Can be used to generate digests for the input seen thus far,
-// without affecting the digest generated for the entire input.
-void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context);
-
-// Converts a digest into human-readable hexadecimal.
-std::string MD5DigestToBase16(const MD5Digest& digest);
-
-// Computes the MD5 sum of the given data buffer with the given length.
-// The given 'digest' structure will be filled with the result data.
-void MD5Sum(const void* data, size_t length, MD5Digest* digest);
-
-// Returns the MD5 (in hexadecimal) of a string.
-std::string MD5String(const StringPiece& str);
-
-} // namespace base
-
-#endif // BASE_MD5_H_
diff --git a/gn/base/memory/raw_scoped_refptr_mismatch_checker.h b/gn/base/memory/raw_scoped_refptr_mismatch_checker.h
deleted file mode 100644
index ab8b2abcbb9..00000000000
--- a/gn/base/memory/raw_scoped_refptr_mismatch_checker.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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.
-
-#ifndef BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
-#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
-
-#include <type_traits>
-
-#include "base/template_util.h"
-
-// It is dangerous to post a task with a T* argument where T is a subtype of
-// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the
-// object may already have been deleted since it was not held with a
-// scoped_refptr. Example: http://crbug.com/27191
-// The following set of traits are designed to generate a compile error
-// whenever this antipattern is attempted.
-
-namespace base {
-
-// This is a base internal implementation file used by task.h and callback.h.
-// Not for public consumption, so we wrap it in namespace internal.
-namespace internal {
-
-template <typename T, typename = void>
-struct IsRefCountedType : std::false_type {};
-
-template <typename T>
-struct IsRefCountedType<T,
- void_t<decltype(std::declval<T*>()->AddRef()),
- decltype(std::declval<T*>()->Release())>>
- : std::true_type {};
-
-template <typename T>
-struct NeedsScopedRefptrButGetsRawPtr {
- static_assert(!std::is_reference<T>::value,
- "NeedsScopedRefptrButGetsRawPtr requires non-reference type.");
-
- enum {
- // Human readable translation: you needed to be a scoped_refptr if you are a
- // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase)
- // type.
- value = std::is_pointer<T>::value &&
- IsRefCountedType<std::remove_pointer_t<T>>::value
- };
-};
-
-} // namespace internal
-
-} // namespace base
-
-#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
diff --git a/gn/base/numerics/safe_math_shared_impl.h b/gn/base/numerics/safe_math_shared_impl.h
deleted file mode 100644
index 583c487a42e..00000000000
--- a/gn/base/numerics/safe_math_shared_impl.h
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2017 The Chromium 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 BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
-#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <cassert>
-#include <climits>
-#include <cmath>
-#include <cstdlib>
-#include <limits>
-#include <type_traits>
-
-#include "base/numerics/safe_conversions.h"
-
-// Where available use builtin math overflow support on Clang and GCC.
-#if !defined(__native_client__) && \
- ((defined(__clang__) && \
- ((__clang_major__ > 3) || \
- (__clang_major__ == 3 && __clang_minor__ >= 4))) || \
- (defined(__GNUC__) && __GNUC__ >= 5))
-#include "base/numerics/safe_math_clang_gcc_impl.h"
-#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
-#else
-#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
-#endif
-
-namespace base {
-namespace internal {
-
-// These are the non-functioning boilerplate implementations of the optimized
-// safe math routines.
-#if !BASE_HAS_OPTIMIZED_SAFE_MATH
-template <typename T, typename U>
-struct CheckedAddFastOp {
- static const bool is_supported = false;
- template <typename V>
- static constexpr bool Do(T, U, V*) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<bool>();
- }
-};
-
-template <typename T, typename U>
-struct CheckedSubFastOp {
- static const bool is_supported = false;
- template <typename V>
- static constexpr bool Do(T, U, V*) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<bool>();
- }
-};
-
-template <typename T, typename U>
-struct CheckedMulFastOp {
- static const bool is_supported = false;
- template <typename V>
- static constexpr bool Do(T, U, V*) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<bool>();
- }
-};
-
-template <typename T, typename U>
-struct ClampedAddFastOp {
- static const bool is_supported = false;
- template <typename V>
- static constexpr V Do(T, U) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<V>();
- }
-};
-
-template <typename T, typename U>
-struct ClampedSubFastOp {
- static const bool is_supported = false;
- template <typename V>
- static constexpr V Do(T, U) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<V>();
- }
-};
-
-template <typename T, typename U>
-struct ClampedMulFastOp {
- static const bool is_supported = false;
- template <typename V>
- static constexpr V Do(T, U) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<V>();
- }
-};
-
-template <typename T>
-struct ClampedNegFastOp {
- static const bool is_supported = false;
- static constexpr T Do(T) {
- // Force a compile failure if instantiated.
- return CheckOnFailure::template HandleFailure<T>();
- }
-};
-#endif // BASE_HAS_OPTIMIZED_SAFE_MATH
-#undef BASE_HAS_OPTIMIZED_SAFE_MATH
-
-// This is used for UnsignedAbs, where we need to support floating-point
-// template instantiations even though we don't actually support the operations.
-// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
-// so the float versions will not compile.
-template <typename Numeric,
- bool IsInteger = std::is_integral<Numeric>::value,
- bool IsFloat = std::is_floating_point<Numeric>::value>
-struct UnsignedOrFloatForSize;
-
-template <typename Numeric>
-struct UnsignedOrFloatForSize<Numeric, true, false> {
- using type = typename std::make_unsigned<Numeric>::type;
-};
-
-template <typename Numeric>
-struct UnsignedOrFloatForSize<Numeric, false, true> {
- using type = Numeric;
-};
-
-// Wrap the unary operations to allow SFINAE when instantiating integrals versus
-// floating points. These don't perform any overflow checking. Rather, they
-// exhibit well-defined overflow semantics and rely on the caller to detect
-// if an overflow occured.
-
-template <typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr T NegateWrapper(T value) {
- using UnsignedT = typename std::make_unsigned<T>::type;
- // This will compile to a NEG on Intel, and is normal negation on ARM.
- return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
-}
-
-template <
- typename T,
- typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-constexpr T NegateWrapper(T value) {
- return -value;
-}
-
-template <typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
- return ~value;
-}
-
-template <typename T,
- typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-constexpr T AbsWrapper(T value) {
- return static_cast<T>(SafeUnsignedAbs(value));
-}
-
-template <
- typename T,
- typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
-constexpr T AbsWrapper(T value) {
- return value < 0 ? -value : value;
-}
-
-template <template <typename, typename, typename> class M,
- typename L,
- typename R>
-struct MathWrapper {
- using math = M<typename UnderlyingType<L>::type,
- typename UnderlyingType<R>::type,
- void>;
- using type = typename math::result_type;
-};
-
-// These variadic templates work out the return types.
-// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
-template <template <typename, typename, typename> class M,
- typename L,
- typename R,
- typename... Args>
-struct ResultType;
-
-template <template <typename, typename, typename> class M,
- typename L,
- typename R>
-struct ResultType<M, L, R> {
- using type = typename MathWrapper<M, L, R>::type;
-};
-
-template <template <typename, typename, typename> class M,
- typename L,
- typename R,
- typename... Args>
-struct ResultType {
- using type =
- typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
-};
-
-// The following macros are just boilerplate for the standard arithmetic
-// operator overloads and variadic function templates. A macro isn't the nicest
-// solution, but it beats rewriting these over and over again.
-#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
- template <typename L, typename R, typename... Args> \
- constexpr CLASS##Numeric< \
- typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
- CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
- return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
- args...); \
- }
-
-#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
- /* Binary arithmetic operator for all CLASS##Numeric operations. */ \
- template <typename L, typename R, \
- typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
- nullptr> \
- constexpr CLASS##Numeric< \
- typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
- operator OP(const L lhs, const R rhs) { \
- return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
- rhs); \
- } \
- /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
- template <typename L> \
- template <typename R> \
- constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \
- const R rhs) { \
- return MathOp<CLASS##OP_NAME##Op>(rhs); \
- } \
- /* Variadic arithmetic functions that return CLASS##Numeric. */ \
- BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
diff --git a/gn/base/optional.h b/gn/base/optional.h
deleted file mode 100644
index 931305ccc6b..00000000000
--- a/gn/base/optional.h
+++ /dev/null
@@ -1,922 +0,0 @@
-// Copyright 2016 The Chromium 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 BASE_OPTIONAL_H_
-#define BASE_OPTIONAL_H_
-
-#include <type_traits>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/template_util.h"
-
-namespace base {
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/in_place_t
-struct in_place_t {};
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
-struct nullopt_t {
- constexpr explicit nullopt_t(int) {}
-};
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/in_place
-constexpr in_place_t in_place = {};
-
-// Specification:
-// http://en.cppreference.com/w/cpp/utility/optional/nullopt
-constexpr nullopt_t nullopt(0);
-
-// Forward declaration, which is refered by following helpers.
-template <typename T>
-class Optional;
-
-namespace internal {
-
-template <typename T, bool = std::is_trivially_destructible<T>::value>
-struct OptionalStorageBase {
- // Initializing |empty_| here instead of using default member initializing
- // to avoid errors in g++ 4.8.
- constexpr OptionalStorageBase() : empty_('\0') {}
-
- template <class... Args>
- constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
- : is_populated_(true), value_(std::forward<Args>(args)...) {}
-
- // When T is not trivially destructible we must call its
- // destructor before deallocating its memory.
- // Note that this hides the (implicitly declared) move constructor, which
- // would be used for constexpr move constructor in OptionalStorage<T>.
- // It is needed iff T is trivially move constructible. However, the current
- // is_trivially_{copy,move}_constructible implementation requires
- // is_trivially_destructible (which looks a bug, cf:
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 and
- // http://cplusplus.github.io/LWG/lwg-active.html#2116), so it is not
- // necessary for this case at the moment. Please see also the destructor
- // comment in "is_trivially_destructible = true" specialization below.
- ~OptionalStorageBase() {
- if (is_populated_)
- value_.~T();
- }
-
- template <class... Args>
- void Init(Args&&... args) {
- DCHECK(!is_populated_);
- ::new (&value_) T(std::forward<Args>(args)...);
- is_populated_ = true;
- }
-
- bool is_populated_ = false;
- union {
- // |empty_| exists so that the union will always be initialized, even when
- // it doesn't contain a value. Union members must be initialized for the
- // constructor to be 'constexpr'.
- char empty_;
- T value_;
- };
-};
-
-template <typename T>
-struct OptionalStorageBase<T, true /* trivially destructible */> {
- // Initializing |empty_| here instead of using default member initializing
- // to avoid errors in g++ 4.8.
- constexpr OptionalStorageBase() : empty_('\0') {}
-
- template <class... Args>
- constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
- : is_populated_(true), value_(std::forward<Args>(args)...) {}
-
- // When T is trivially destructible (i.e. its destructor does nothing) there
- // is no need to call it. Implicitly defined destructor is trivial, because
- // both members (bool and union containing only variants which are trivially
- // destructible) are trivially destructible.
- // Explicitly-defaulted destructor is also trivial, but do not use it here,
- // because it hides the implicit move constructor. It is needed to implement
- // constexpr move constructor in OptionalStorage iff T is trivially move
- // constructible. Note that, if T is trivially move constructible, the move
- // constructor of OptionalStorageBase<T> is also implicitly defined and it is
- // trivially move constructor. If T is not trivially move constructible,
- // "not declaring move constructor without destructor declaration" here means
- // "delete move constructor", which works because any move constructor of
- // OptionalStorage will not refer to it in that case.
-
- template <class... Args>
- void Init(Args&&... args) {
- DCHECK(!is_populated_);
- ::new (&value_) T(std::forward<Args>(args)...);
- is_populated_ = true;
- }
-
- bool is_populated_ = false;
- union {
- // |empty_| exists so that the union will always be initialized, even when
- // it doesn't contain a value. Union members must be initialized for the
- // constructor to be 'constexpr'.
- char empty_;
- T value_;
- };
-};
-
-// Implement conditional constexpr copy and move constructors. These are
-// constexpr if is_trivially_{copy,move}_constructible<T>::value is true
-// respectively. If each is true, the corresponding constructor is defined as
-// "= default;", which generates a constexpr constructor (In this case,
-// the condition of constexpr-ness is satisfied because the base class also has
-// compiler generated constexpr {copy,move} constructors). Note that
-// placement-new is prohibited in constexpr.
-template <typename T,
- bool = is_trivially_copy_constructible<T>::value,
- bool = std::is_trivially_move_constructible<T>::value>
-struct OptionalStorage : OptionalStorageBase<T> {
- // This is no trivially {copy,move} constructible case. Other cases are
- // defined below as specializations.
-
- // Accessing the members of template base class requires explicit
- // declaration.
- using OptionalStorageBase<T>::is_populated_;
- using OptionalStorageBase<T>::value_;
- using OptionalStorageBase<T>::Init;
-
- // Inherit constructors (specifically, the in_place constructor).
- using OptionalStorageBase<T>::OptionalStorageBase;
-
- // User defined constructor deletes the default constructor.
- // Define it explicitly.
- OptionalStorage() = default;
-
- OptionalStorage(const OptionalStorage& other) {
- if (other.is_populated_)
- Init(other.value_);
- }
-
- OptionalStorage(OptionalStorage&& other) noexcept(
- std::is_nothrow_move_constructible<T>::value) {
- if (other.is_populated_)
- Init(std::move(other.value_));
- }
-};
-
-template <typename T>
-struct OptionalStorage<T,
- true /* trivially copy constructible */,
- false /* trivially move constructible */>
- : OptionalStorageBase<T> {
- using OptionalStorageBase<T>::is_populated_;
- using OptionalStorageBase<T>::value_;
- using OptionalStorageBase<T>::Init;
- using OptionalStorageBase<T>::OptionalStorageBase;
-
- OptionalStorage() = default;
- OptionalStorage(const OptionalStorage& other) = default;
-
- OptionalStorage(OptionalStorage&& other) noexcept(
- std::is_nothrow_move_constructible<T>::value) {
- if (other.is_populated_)
- Init(std::move(other.value_));
- }
-};
-
-template <typename T>
-struct OptionalStorage<T,
- false /* trivially copy constructible */,
- true /* trivially move constructible */>
- : OptionalStorageBase<T> {
- using OptionalStorageBase<T>::is_populated_;
- using OptionalStorageBase<T>::value_;
- using OptionalStorageBase<T>::Init;
- using OptionalStorageBase<T>::OptionalStorageBase;
-
- OptionalStorage() = default;
- OptionalStorage(OptionalStorage&& other) = default;
-
- OptionalStorage(const OptionalStorage& other) {
- if (other.is_populated_)
- Init(other.value_);
- }
-};
-
-template <typename T>
-struct OptionalStorage<T,
- true /* trivially copy constructible */,
- true /* trivially move constructible */>
- : OptionalStorageBase<T> {
- // If both trivially {copy,move} constructible are true, it is not necessary
- // to use user-defined constructors. So, just inheriting constructors
- // from the base class works.
- using OptionalStorageBase<T>::OptionalStorageBase;
-};
-
-// Base class to support conditionally usable copy-/move- constructors
-// and assign operators.
-template <typename T>
-class OptionalBase {
- // This class provides implementation rather than public API, so everything
- // should be hidden. Often we use composition, but we cannot in this case
- // because of C++ language restriction.
- protected:
- constexpr OptionalBase() = default;
- constexpr OptionalBase(const OptionalBase& other) = default;
- constexpr OptionalBase(OptionalBase&& other) = default;
-
- template <class... Args>
- constexpr explicit OptionalBase(in_place_t, Args&&... args)
- : storage_(in_place, std::forward<Args>(args)...) {}
-
- // Implementation of converting constructors.
- template <typename U>
- explicit OptionalBase(const OptionalBase<U>& other) {
- if (other.storage_.is_populated_)
- storage_.Init(other.storage_.value_);
- }
-
- template <typename U>
- explicit OptionalBase(OptionalBase<U>&& other) {
- if (other.storage_.is_populated_)
- storage_.Init(std::move(other.storage_.value_));
- }
-
- ~OptionalBase() = default;
-
- OptionalBase& operator=(const OptionalBase& other) {
- CopyAssign(other);
- return *this;
- }
-
- OptionalBase& operator=(OptionalBase&& other) noexcept(
- std::is_nothrow_move_assignable<T>::value&&
- std::is_nothrow_move_constructible<T>::value) {
- MoveAssign(std::move(other));
- return *this;
- }
-
- template <typename U>
- void CopyAssign(const OptionalBase<U>& other) {
- if (other.storage_.is_populated_)
- InitOrAssign(other.storage_.value_);
- else
- FreeIfNeeded();
- }
-
- template <typename U>
- void MoveAssign(OptionalBase<U>&& other) {
- if (other.storage_.is_populated_)
- InitOrAssign(std::move(other.storage_.value_));
- else
- FreeIfNeeded();
- }
-
- template <typename U>
- void InitOrAssign(U&& value) {
- if (storage_.is_populated_)
- storage_.value_ = std::forward<U>(value);
- else
- storage_.Init(std::forward<U>(value));
- }
-
- void FreeIfNeeded() {
- if (!storage_.is_populated_)
- return;
- storage_.value_.~T();
- storage_.is_populated_ = false;
- }
-
- // For implementing conversion, allow access to other typed OptionalBase
- // class.
- template <typename U>
- friend class OptionalBase;
-
- OptionalStorage<T> storage_;
-};
-
-// The following {Copy,Move}{Constructible,Assignable} structs are helpers to
-// implement constructor/assign-operator overloading. Specifically, if T is
-// is not movable but copyable, Optional<T>'s move constructor should not
-// participate in overload resolution. This inheritance trick implements that.
-template <bool is_copy_constructible>
-struct CopyConstructible {};
-
-template <>
-struct CopyConstructible<false> {
- constexpr CopyConstructible() = default;
- constexpr CopyConstructible(const CopyConstructible&) = delete;
- constexpr CopyConstructible(CopyConstructible&&) = default;
- CopyConstructible& operator=(const CopyConstructible&) = default;
- CopyConstructible& operator=(CopyConstructible&&) = default;
-};
-
-template <bool is_move_constructible>
-struct MoveConstructible {};
-
-template <>
-struct MoveConstructible<false> {
- constexpr MoveConstructible() = default;
- constexpr MoveConstructible(const MoveConstructible&) = default;
- constexpr MoveConstructible(MoveConstructible&&) = delete;
- MoveConstructible& operator=(const MoveConstructible&) = default;
- MoveConstructible& operator=(MoveConstructible&&) = default;
-};
-
-template <bool is_copy_assignable>
-struct CopyAssignable {};
-
-template <>
-struct CopyAssignable<false> {
- constexpr CopyAssignable() = default;
- constexpr CopyAssignable(const CopyAssignable&) = default;
- constexpr CopyAssignable(CopyAssignable&&) = default;
- CopyAssignable& operator=(const CopyAssignable&) = delete;
- CopyAssignable& operator=(CopyAssignable&&) = default;
-};
-
-template <bool is_move_assignable>
-struct MoveAssignable {};
-
-template <>
-struct MoveAssignable<false> {
- constexpr MoveAssignable() = default;
- constexpr MoveAssignable(const MoveAssignable&) = default;
- constexpr MoveAssignable(MoveAssignable&&) = default;
- MoveAssignable& operator=(const MoveAssignable&) = default;
- MoveAssignable& operator=(MoveAssignable&&) = delete;
-};
-
-// Helper to conditionally enable converting constructors and assign operators.
-template <typename T, typename U>
-struct IsConvertibleFromOptional
- : std::integral_constant<
- bool,
- std::is_constructible<T, Optional<U>&>::value ||
- std::is_constructible<T, const Optional<U>&>::value ||
- std::is_constructible<T, Optional<U>&&>::value ||
- std::is_constructible<T, const Optional<U>&&>::value ||
- std::is_convertible<Optional<U>&, T>::value ||
- std::is_convertible<const Optional<U>&, T>::value ||
- std::is_convertible<Optional<U>&&, T>::value ||
- std::is_convertible<const Optional<U>&&, T>::value> {};
-
-template <typename T, typename U>
-struct IsAssignableFromOptional
- : std::integral_constant<
- bool,
- IsConvertibleFromOptional<T, U>::value ||
- std::is_assignable<T&, Optional<U>&>::value ||
- std::is_assignable<T&, const Optional<U>&>::value ||
- std::is_assignable<T&, Optional<U>&&>::value ||
- std::is_assignable<T&, const Optional<U>&&>::value> {};
-
-// Forward compatibility for C++17.
-// Introduce one more deeper nested namespace to avoid leaking using std::swap.
-namespace swappable_impl {
-using std::swap;
-
-struct IsSwappableImpl {
- // Tests if swap can be called. Check<T&>(0) returns true_type iff swap
- // is available for T. Otherwise, Check's overload resolution falls back
- // to Check(...) declared below thanks to SFINAE, so returns false_type.
- template <typename T>
- static auto Check(int)
- -> decltype(swap(std::declval<T>(), std::declval<T>()), std::true_type());
-
- template <typename T>
- static std::false_type Check(...);
-};
-} // namespace swappable_impl
-
-template <typename T>
-struct IsSwappable : decltype(swappable_impl::IsSwappableImpl::Check<T&>(0)) {};
-
-// Forward compatibility for C++20.
-template <typename T>
-using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>;
-
-} // namespace internal
-
-// On Windows, by default, empty-base class optimization does not work,
-// which means even if the base class is empty struct, it still consumes one
-// byte for its body. __declspec(empty_bases) enables the optimization.
-// cf)
-// https://blogs.msdn.microsoft.com/vcblog/2016/03/30/optimizing-the-layout-of-empty-base-classes-in-vs2015-update-2-3/
-#ifdef OS_WIN
-#define OPTIONAL_DECLSPEC_EMPTY_BASES __declspec(empty_bases)
-#else
-#define OPTIONAL_DECLSPEC_EMPTY_BASES
-#endif
-
-// base::Optional is a Chromium version of the C++17 optional class:
-// std::optional documentation:
-// http://en.cppreference.com/w/cpp/utility/optional
-// Chromium documentation:
-// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
-//
-// These are the differences between the specification and the implementation:
-// - Constructors do not use 'constexpr' as it is a C++14 extension.
-// - 'constexpr' might be missing in some places for reasons specified locally.
-// - No exceptions are thrown, because they are banned from Chromium.
-// Marked noexcept for only move constructor and move assign operators.
-// - All the non-members are in the 'base' namespace instead of 'std'.
-//
-// Note that T cannot have a constructor T(Optional<T>) etc. Optional<T> checks
-// T's constructor (specifically via IsConvertibleFromOptional), and in the
-// check whether T can be constructible from Optional<T>, which is recursive
-// so it does not work. As of Feb 2018, std::optional C++17 implementation in
-// both clang and gcc has same limitation. MSVC SFINAE looks to have different
-// behavior, but anyway it reports an error, too.
-template <typename T>
-class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
- : public internal::OptionalBase<T>,
- public internal::CopyConstructible<std::is_copy_constructible<T>::value>,
- public internal::MoveConstructible<std::is_move_constructible<T>::value>,
- public internal::CopyAssignable<std::is_copy_constructible<T>::value &&
- std::is_copy_assignable<T>::value>,
- public internal::MoveAssignable<std::is_move_constructible<T>::value &&
- std::is_move_assignable<T>::value> {
- public:
-#undef OPTIONAL_DECLSPEC_EMPTY_BASES
- using value_type = T;
-
- // Defer default/copy/move constructor implementation to OptionalBase.
- constexpr Optional() = default;
- constexpr Optional(const Optional& other) = default;
- constexpr Optional(Optional&& other) noexcept(
- std::is_nothrow_move_constructible<T>::value) = default;
-
- constexpr Optional(nullopt_t) {} // NOLINT(runtime/explicit)
-
- // Converting copy constructor. "explicit" only if
- // std::is_convertible<const U&, T>::value is false. It is implemented by
- // declaring two almost same constructors, but that condition in enable_if_t
- // is different, so that either one is chosen, thanks to SFINAE.
- template <
- typename U,
- std::enable_if_t<std::is_constructible<T, const U&>::value &&
- !internal::IsConvertibleFromOptional<T, U>::value &&
- std::is_convertible<const U&, T>::value,
- bool> = false>
- Optional(const Optional<U>& other) : internal::OptionalBase<T>(other) {}
-
- template <
- typename U,
- std::enable_if_t<std::is_constructible<T, const U&>::value &&
- !internal::IsConvertibleFromOptional<T, U>::value &&
- !std::is_convertible<const U&, T>::value,
- bool> = false>
- explicit Optional(const Optional<U>& other)
- : internal::OptionalBase<T>(other) {}
-
- // Converting move constructor. Similar to converting copy constructor,
- // declaring two (explicit and non-explicit) constructors.
- template <
- typename U,
- std::enable_if_t<std::is_constructible<T, U&&>::value &&
- !internal::IsConvertibleFromOptional<T, U>::value &&
- std::is_convertible<U&&, T>::value,
- bool> = false>
- Optional(Optional<U>&& other) : internal::OptionalBase<T>(std::move(other)) {}
-
- template <
- typename U,
- std::enable_if_t<std::is_constructible<T, U&&>::value &&
- !internal::IsConvertibleFromOptional<T, U>::value &&
- !std::is_convertible<U&&, T>::value,
- bool> = false>
- explicit Optional(Optional<U>&& other)
- : internal::OptionalBase<T>(std::move(other)) {}
-
- template <class... Args>
- constexpr explicit Optional(in_place_t, Args&&... args)
- : internal::OptionalBase<T>(in_place, std::forward<Args>(args)...) {}
-
- template <
- class U,
- class... Args,
- class = std::enable_if_t<std::is_constructible<value_type,
- std::initializer_list<U>&,
- Args...>::value>>
- constexpr explicit Optional(in_place_t,
- std::initializer_list<U> il,
- Args&&... args)
- : internal::OptionalBase<T>(in_place, il, std::forward<Args>(args)...) {}
-
- // Forward value constructor. Similar to converting constructors,
- // conditionally explicit.
- template <
- typename U = value_type,
- std::enable_if_t<
- std::is_constructible<T, U&&>::value &&
- !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
- !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
- std::is_convertible<U&&, T>::value,
- bool> = false>
- constexpr Optional(U&& value)
- : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
-
- template <
- typename U = value_type,
- std::enable_if_t<
- std::is_constructible<T, U&&>::value &&
- !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
- !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
- !std::is_convertible<U&&, T>::value,
- bool> = false>
- constexpr explicit Optional(U&& value)
- : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
-
- ~Optional() = default;
-
- // Defer copy-/move- assign operator implementation to OptionalBase.
- Optional& operator=(const Optional& other) = default;
- Optional& operator=(Optional&& other) noexcept(
- std::is_nothrow_move_assignable<T>::value&&
- std::is_nothrow_move_constructible<T>::value) = default;
-
- Optional& operator=(nullopt_t) {
- FreeIfNeeded();
- return *this;
- }
-
- // Perfect-forwarded assignment.
- template <typename U>
- std::enable_if_t<
- !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
- std::is_constructible<T, U>::value &&
- std::is_assignable<T&, U>::value &&
- (!std::is_scalar<T>::value ||
- !std::is_same<std::decay_t<U>, T>::value),
- Optional&>
- operator=(U&& value) {
- InitOrAssign(std::forward<U>(value));
- return *this;
- }
-
- // Copy assign the state of other.
- template <typename U>
- std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
- std::is_constructible<T, const U&>::value &&
- std::is_assignable<T&, const U&>::value,
- Optional&>
- operator=(const Optional<U>& other) {
- CopyAssign(other);
- return *this;
- }
-
- // Move assign the state of other.
- template <typename U>
- std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
- std::is_constructible<T, U>::value &&
- std::is_assignable<T&, U>::value,
- Optional&>
- operator=(Optional<U>&& other) {
- MoveAssign(std::move(other));
- return *this;
- }
-
- constexpr const T* operator->() const {
- CHECK(storage_.is_populated_);
- return &storage_.value_;
- }
-
- constexpr T* operator->() {
- CHECK(storage_.is_populated_);
- return &storage_.value_;
- }
-
- constexpr const T& operator*() const& {
- CHECK(storage_.is_populated_);
- return storage_.value_;
- }
-
- constexpr T& operator*() & {
- CHECK(storage_.is_populated_);
- return storage_.value_;
- }
-
- constexpr const T&& operator*() const&& {
- CHECK(storage_.is_populated_);
- return std::move(storage_.value_);
- }
-
- constexpr T&& operator*() && {
- CHECK(storage_.is_populated_);
- return std::move(storage_.value_);
- }
-
- constexpr explicit operator bool() const { return storage_.is_populated_; }
-
- constexpr bool has_value() const { return storage_.is_populated_; }
-
- constexpr T& value() & {
- CHECK(storage_.is_populated_);
- return storage_.value_;
- }
-
- constexpr const T& value() const& {
- CHECK(storage_.is_populated_);
- return storage_.value_;
- }
-
- constexpr T&& value() && {
- CHECK(storage_.is_populated_);
- return std::move(storage_.value_);
- }
-
- constexpr const T&& value() const&& {
- CHECK(storage_.is_populated_);
- return std::move(storage_.value_);
- }
-
- template <class U>
- constexpr T value_or(U&& default_value) const& {
- // TODO(mlamouri): add the following assert when possible:
- // static_assert(std::is_copy_constructible<T>::value,
- // "T must be copy constructible");
- static_assert(std::is_convertible<U, T>::value,
- "U must be convertible to T");
- return storage_.is_populated_
- ? value()
- : static_cast<T>(std::forward<U>(default_value));
- }
-
- template <class U>
- constexpr T value_or(U&& default_value) && {
- // TODO(mlamouri): add the following assert when possible:
- // static_assert(std::is_move_constructible<T>::value,
- // "T must be move constructible");
- static_assert(std::is_convertible<U, T>::value,
- "U must be convertible to T");
- return storage_.is_populated_
- ? std::move(value())
- : static_cast<T>(std::forward<U>(default_value));
- }
-
- void swap(Optional& other) {
- if (!storage_.is_populated_ && !other.storage_.is_populated_)
- return;
-
- if (storage_.is_populated_ != other.storage_.is_populated_) {
- if (storage_.is_populated_) {
- other.storage_.Init(std::move(storage_.value_));
- FreeIfNeeded();
- } else {
- storage_.Init(std::move(other.storage_.value_));
- other.FreeIfNeeded();
- }
- return;
- }
-
- DCHECK(storage_.is_populated_ && other.storage_.is_populated_);
- using std::swap;
- swap(**this, *other);
- }
-
- void reset() { FreeIfNeeded(); }
-
- template <class... Args>
- T& emplace(Args&&... args) {
- FreeIfNeeded();
- storage_.Init(std::forward<Args>(args)...);
- return storage_.value_;
- }
-
- template <class U, class... Args>
- std::enable_if_t<
- std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
- T&>
- emplace(std::initializer_list<U> il, Args&&... args) {
- FreeIfNeeded();
- storage_.Init(il, std::forward<Args>(args)...);
- return storage_.value_;
- }
-
- private:
- // Accessing template base class's protected member needs explicit
- // declaration to do so.
- using internal::OptionalBase<T>::CopyAssign;
- using internal::OptionalBase<T>::FreeIfNeeded;
- using internal::OptionalBase<T>::InitOrAssign;
- using internal::OptionalBase<T>::MoveAssign;
- using internal::OptionalBase<T>::storage_;
-};
-
-// Here after defines comparation operators. The definition follows
-// http://en.cppreference.com/w/cpp/utility/optional/operator_cmp
-// while bool() casting is replaced by has_value() to meet the chromium
-// style guide.
-template <class T, class U>
-constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
- if (lhs.has_value() != rhs.has_value())
- return false;
- if (!lhs.has_value())
- return true;
- return *lhs == *rhs;
-}
-
-template <class T, class U>
-constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
- if (lhs.has_value() != rhs.has_value())
- return true;
- if (!lhs.has_value())
- return false;
- return *lhs != *rhs;
-}
-
-template <class T, class U>
-constexpr bool operator<(const Optional<T>& lhs, const Optional<U>& rhs) {
- if (!rhs.has_value())
- return false;
- if (!lhs.has_value())
- return true;
- return *lhs < *rhs;
-}
-
-template <class T, class U>
-constexpr bool operator<=(const Optional<T>& lhs, const Optional<U>& rhs) {
- if (!lhs.has_value())
- return true;
- if (!rhs.has_value())
- return false;
- return *lhs <= *rhs;
-}
-
-template <class T, class U>
-constexpr bool operator>(const Optional<T>& lhs, const Optional<U>& rhs) {
- if (!lhs.has_value())
- return false;
- if (!rhs.has_value())
- return true;
- return *lhs > *rhs;
-}
-
-template <class T, class U>
-constexpr bool operator>=(const Optional<T>& lhs, const Optional<U>& rhs) {
- if (!rhs.has_value())
- return true;
- if (!lhs.has_value())
- return false;
- return *lhs >= *rhs;
-}
-
-template <class T>
-constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
- return !opt;
-}
-
-template <class T>
-constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
- return !opt;
-}
-
-template <class T>
-constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
- return opt.has_value();
-}
-
-template <class T>
-constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
- return opt.has_value();
-}
-
-template <class T>
-constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
- return false;
-}
-
-template <class T>
-constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
- return opt.has_value();
-}
-
-template <class T>
-constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
- return !opt;
-}
-
-template <class T>
-constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
- return true;
-}
-
-template <class T>
-constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
- return opt.has_value();
-}
-
-template <class T>
-constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
- return false;
-}
-
-template <class T>
-constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
- return true;
-}
-
-template <class T>
-constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
- return !opt;
-}
-
-template <class T, class U>
-constexpr bool operator==(const Optional<T>& opt, const U& value) {
- return opt.has_value() ? *opt == value : false;
-}
-
-template <class T, class U>
-constexpr bool operator==(const U& value, const Optional<T>& opt) {
- return opt.has_value() ? value == *opt : false;
-}
-
-template <class T, class U>
-constexpr bool operator!=(const Optional<T>& opt, const U& value) {
- return opt.has_value() ? *opt != value : true;
-}
-
-template <class T, class U>
-constexpr bool operator!=(const U& value, const Optional<T>& opt) {
- return opt.has_value() ? value != *opt : true;
-}
-
-template <class T, class U>
-constexpr bool operator<(const Optional<T>& opt, const U& value) {
- return opt.has_value() ? *opt < value : true;
-}
-
-template <class T, class U>
-constexpr bool operator<(const U& value, const Optional<T>& opt) {
- return opt.has_value() ? value < *opt : false;
-}
-
-template <class T, class U>
-constexpr bool operator<=(const Optional<T>& opt, const U& value) {
- return opt.has_value() ? *opt <= value : true;
-}
-
-template <class T, class U>
-constexpr bool operator<=(const U& value, const Optional<T>& opt) {
- return opt.has_value() ? value <= *opt : false;
-}
-
-template <class T, class U>
-constexpr bool operator>(const Optional<T>& opt, const U& value) {
- return opt.has_value() ? *opt > value : false;
-}
-
-template <class T, class U>
-constexpr bool operator>(const U& value, const Optional<T>& opt) {
- return opt.has_value() ? value > *opt : true;
-}
-
-template <class T, class U>
-constexpr bool operator>=(const Optional<T>& opt, const U& value) {
- return opt.has_value() ? *opt >= value : false;
-}
-
-template <class T, class U>
-constexpr bool operator>=(const U& value, const Optional<T>& opt) {
- return opt.has_value() ? value >= *opt : true;
-}
-
-template <class T>
-constexpr Optional<std::decay_t<T>> make_optional(T&& value) {
- return Optional<std::decay_t<T>>(std::forward<T>(value));
-}
-
-template <class T, class... Args>
-constexpr Optional<T> make_optional(Args&&... args) {
- return Optional<T>(in_place, std::forward<Args>(args)...);
-}
-
-template <class T, class U, class... Args>
-constexpr Optional<T> make_optional(std::initializer_list<U> il,
- Args&&... args) {
- return Optional<T>(in_place, il, std::forward<Args>(args)...);
-}
-
-// Partial specialization for a function template is not allowed. Also, it is
-// not allowed to add overload function to std namespace, while it is allowed
-// to specialize the template in std. Thus, swap() (kind of) overloading is
-// defined in base namespace, instead.
-template <class T>
-std::enable_if_t<std::is_move_constructible<T>::value &&
- internal::IsSwappable<T>::value>
-swap(Optional<T>& lhs, Optional<T>& rhs) {
- lhs.swap(rhs);
-}
-
-} // namespace base
-
-namespace std {
-
-template <class T>
-struct hash<base::Optional<T>> {
- size_t operator()(const base::Optional<T>& opt) const {
- return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
- }
-};
-
-} // namespace std
-
-#endif // BASE_OPTIONAL_H_
diff --git a/gn/base/posix/safe_strerror.cc b/gn/base/posix/safe_strerror.cc
deleted file mode 100644
index 3e3bb15810b..00000000000
--- a/gn/base/posix/safe_strerror.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2006-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.
-
-#if defined(__ANDROID__)
-// Post-L versions of bionic define the GNU-specific strerror_r if _GNU_SOURCE
-// is defined, but the symbol is renamed to __gnu_strerror_r which only exists
-// on those later versions. To preserve ABI compatibility with older versions,
-// undefine _GNU_SOURCE and use the POSIX version.
-#undef _GNU_SOURCE
-#endif
-
-#include "base/posix/safe_strerror.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "util/build_config.h"
-
-namespace base {
-
-#if defined(__GLIBC__) || defined(OS_NACL)
-#define USE_HISTORICAL_STRERRO_R 1
-#else
-#define USE_HISTORICAL_STRERRO_R 0
-#endif
-
-#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
-// GCC will complain about the unused second wrap function unless we tell it
-// that we meant for them to be potentially unused, which is exactly what this
-// attribute is for.
-#define POSSIBLY_UNUSED __attribute__((unused))
-#else
-#define POSSIBLY_UNUSED
-#endif
-
-#if USE_HISTORICAL_STRERRO_R
-// glibc has two strerror_r functions: a historical GNU-specific one that
-// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
-// that returns int. This wraps the GNU-specific one.
-static void POSSIBLY_UNUSED
-wrap_posix_strerror_r(char* (*strerror_r_ptr)(int, char*, size_t),
- int err,
- char* buf,
- size_t len) {
- // GNU version.
- char* rc = (*strerror_r_ptr)(err, buf, len);
- if (rc != buf) {
- // glibc did not use buf and returned a static string instead. Copy it
- // into buf.
- buf[0] = '\0';
- strncat(buf, rc, len - 1);
- }
- // The GNU version never fails. Unknown errors get an "unknown error" message.
- // The result is always null terminated.
-}
-#endif // USE_HISTORICAL_STRERRO_R
-
-// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
-// does not define the behaviour for some of the edge cases, so we wrap it to
-// guarantee that they are handled. This is compiled on all POSIX platforms, but
-// it will only be used on Linux if the POSIX strerror_r implementation is
-// being used (see below).
-static void POSSIBLY_UNUSED wrap_posix_strerror_r(int (*strerror_r_ptr)(int,
- char*,
- size_t),
- int err,
- char* buf,
- size_t len) {
- int old_errno = errno;
- // Have to cast since otherwise we get an error if this is the GNU version
- // (but in such a scenario this function is never called). Sadly we can't use
- // C++-style casts because the appropriate one is reinterpret_cast but it's
- // considered illegal to reinterpret_cast a type to itself, so we get an
- // error in the opposite case.
- int result = (*strerror_r_ptr)(err, buf, len);
- if (result == 0) {
- // POSIX is vague about whether the string will be terminated, although
- // it indirectly implies that typically ERANGE will be returned, instead
- // of truncating the string. We play it safe by always terminating the
- // string explicitly.
- buf[len - 1] = '\0';
- } else {
- // Error. POSIX is vague about whether the return value is itself a system
- // error code or something else. On Linux currently it is -1 and errno is
- // set. On BSD-derived systems it is a system error and errno is unchanged.
- // We try and detect which case it is so as to put as much useful info as
- // we can into our message.
- int strerror_error; // The error encountered in strerror
- int new_errno = errno;
- if (new_errno != old_errno) {
- // errno was changed, so probably the return value is just -1 or something
- // else that doesn't provide any info, and errno is the error.
- strerror_error = new_errno;
- } else {
- // Either the error from strerror_r was the same as the previous value, or
- // errno wasn't used. Assume the latter.
- strerror_error = result;
- }
- // snprintf truncates and always null-terminates.
- snprintf(buf, len, "Error %d while retrieving error %d", strerror_error,
- err);
- }
- errno = old_errno;
-}
-
-void safe_strerror_r(int err, char* buf, size_t len) {
- if (buf == nullptr || len <= 0) {
- return;
- }
- // If using glibc (i.e., Linux), the compiler will automatically select the
- // appropriate overloaded function based on the function type of strerror_r.
- // The other one will be elided from the translation unit since both are
- // static.
- wrap_posix_strerror_r(&strerror_r, err, buf, len);
-}
-
-std::string safe_strerror(int err) {
- const int buffer_size = 256;
- char buf[buffer_size];
- safe_strerror_r(err, buf, sizeof(buf));
- return std::string(buf);
-}
-
-} // namespace base
diff --git a/gn/base/stl_util.h b/gn/base/stl_util.h
deleted file mode 100644
index d9e3484163b..00000000000
--- a/gn/base/stl_util.h
+++ /dev/null
@@ -1,406 +0,0 @@
-// 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.
-
-// Derived from google3/util/gtl/stl_util.h
-
-#ifndef BASE_STL_UTIL_H_
-#define BASE_STL_UTIL_H_
-
-#include <algorithm>
-#include <deque>
-#include <forward_list>
-#include <functional>
-#include <initializer_list>
-#include <iterator>
-#include <list>
-#include <map>
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/optional.h"
-
-namespace base {
-
-namespace internal {
-
-// Calls erase on iterators of matching elements.
-template <typename Container, typename Predicate>
-void IterateAndEraseIf(Container& container, Predicate pred) {
- for (auto it = container.begin(); it != container.end();) {
- if (pred(*it))
- it = container.erase(it);
- else
- ++it;
- }
-}
-
-} // namespace internal
-
-// C++14 implementation of C++17's std::size():
-// http://en.cppreference.com/w/cpp/iterator/size
-template <typename Container>
-constexpr auto size(const Container& c) -> decltype(c.size()) {
- return c.size();
-}
-
-template <typename T, size_t N>
-constexpr size_t size(const T (&array)[N]) noexcept {
- return N;
-}
-
-// C++14 implementation of C++17's std::empty():
-// http://en.cppreference.com/w/cpp/iterator/empty
-template <typename Container>
-constexpr auto empty(const Container& c) -> decltype(c.empty()) {
- return c.empty();
-}
-
-template <typename T, size_t N>
-constexpr bool empty(const T (&array)[N]) noexcept {
- return false;
-}
-
-template <typename T>
-constexpr bool empty(std::initializer_list<T> il) noexcept {
- return il.size() == 0;
-}
-
-// C++14 implementation of C++17's std::data():
-// http://en.cppreference.com/w/cpp/iterator/data
-template <typename Container>
-constexpr auto data(Container& c) -> decltype(c.data()) {
- return c.data();
-}
-
-// std::basic_string::data() had no mutable overload prior to C++17 [1].
-// Hence this overload is provided.
-// Note: str[0] is safe even for empty strings, as they are guaranteed to be
-// null-terminated [2].
-//
-// [1] http://en.cppreference.com/w/cpp/string/basic_string/data
-// [2] http://en.cppreference.com/w/cpp/string/basic_string/operator_at
-template <typename CharT, typename Traits, typename Allocator>
-CharT* data(std::basic_string<CharT, Traits, Allocator>& str) {
- return std::addressof(str[0]);
-}
-
-template <typename Container>
-constexpr auto data(const Container& c) -> decltype(c.data()) {
- return c.data();
-}
-
-template <typename T, size_t N>
-constexpr T* data(T (&array)[N]) noexcept {
- return array;
-}
-
-template <typename T>
-constexpr const T* data(std::initializer_list<T> il) noexcept {
- return il.begin();
-}
-
-// Returns a const reference to the underlying container of a container adapter.
-// Works for std::priority_queue, std::queue, and std::stack.
-template <class A>
-const typename A::container_type& GetUnderlyingContainer(const A& adapter) {
- struct ExposedAdapter : A {
- using A::c;
- };
- return adapter.*&ExposedAdapter::c;
-}
-
-// Clears internal memory of an STL object.
-// STL clear()/reserve(0) does not always free internal memory allocated
-// This function uses swap/destructor to ensure the internal memory is freed.
-template <class T>
-void STLClearObject(T* obj) {
- T tmp;
- tmp.swap(*obj);
- // Sometimes "T tmp" allocates objects with memory (arena implementation?).
- // Hence using additional reserve(0) even if it doesn't always work.
- obj->reserve(0);
-}
-
-// Counts the number of instances of val in a container.
-template <typename Container, typename T>
-typename std::iterator_traits<
- typename Container::const_iterator>::difference_type
-STLCount(const Container& container, const T& val) {
- return std::count(container.begin(), container.end(), val);
-}
-
-// Test to see if a set or map contains a particular key.
-// Returns true if the key is in the collection.
-template <typename Collection, typename Key>
-bool ContainsKey(const Collection& collection, const Key& key) {
- return collection.find(key) != collection.end();
-}
-
-namespace internal {
-
-template <typename Collection>
-class HasKeyType {
- template <typename C>
- static std::true_type test(typename C::key_type*);
- template <typename C>
- static std::false_type test(...);
-
- public:
- static constexpr bool value = decltype(test<Collection>(nullptr))::value;
-};
-
-} // namespace internal
-
-// Test to see if a collection like a vector contains a particular value.
-// Returns true if the value is in the collection.
-// Don't use this on collections such as sets or maps. This is enforced by
-// disabling this method if the collection defines a key_type.
-template <typename Collection,
- typename Value,
- typename std::enable_if<!internal::HasKeyType<Collection>::value,
- int>::type = 0>
-bool ContainsValue(const Collection& collection, const Value& value) {
- return std::find(std::begin(collection), std::end(collection), value) !=
- std::end(collection);
-}
-
-// Returns true if the container is sorted.
-template <typename Container>
-bool STLIsSorted(const Container& cont) {
- // Note: Use reverse iterator on container to ensure we only require
- // value_type to implement operator<.
- return std::adjacent_find(cont.rbegin(), cont.rend(),
- std::less<typename Container::value_type>()) ==
- cont.rend();
-}
-
-// Returns a new ResultType containing the difference of two sorted containers.
-template <typename ResultType, typename Arg1, typename Arg2>
-ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
- DCHECK(STLIsSorted(a1));
- DCHECK(STLIsSorted(a2));
- ResultType difference;
- std::set_difference(a1.begin(), a1.end(), a2.begin(), a2.end(),
- std::inserter(difference, difference.end()));
- return difference;
-}
-
-// Returns a new ResultType containing the union of two sorted containers.
-template <typename ResultType, typename Arg1, typename Arg2>
-ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
- DCHECK(STLIsSorted(a1));
- DCHECK(STLIsSorted(a2));
- ResultType result;
- std::set_union(a1.begin(), a1.end(), a2.begin(), a2.end(),
- std::inserter(result, result.end()));
- return result;
-}
-
-// Returns a new ResultType containing the intersection of two sorted
-// containers.
-template <typename ResultType, typename Arg1, typename Arg2>
-ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
- DCHECK(STLIsSorted(a1));
- DCHECK(STLIsSorted(a2));
- ResultType result;
- std::set_intersection(a1.begin(), a1.end(), a2.begin(), a2.end(),
- std::inserter(result, result.end()));
- return result;
-}
-
-// Returns true if the sorted container |a1| contains all elements of the sorted
-// container |a2|.
-template <typename Arg1, typename Arg2>
-bool STLIncludes(const Arg1& a1, const Arg2& a2) {
- DCHECK(STLIsSorted(a1));
- DCHECK(STLIsSorted(a2));
- return std::includes(a1.begin(), a1.end(), a2.begin(), a2.end());
-}
-
-// Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
-// http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
-// They provide a generic way to erase elements from a container.
-// The functions here implement these for the standard containers until those
-// functions are available in the C++ standard.
-// For Chromium containers overloads should be defined in their own headers
-// (like standard containers).
-// Note: there is no std::erase for standard associative containers so we don't
-// have it either.
-
-template <typename CharT, typename Traits, typename Allocator, typename Value>
-void Erase(std::basic_string<CharT, Traits, Allocator>& container,
- const Value& value) {
- container.erase(std::remove(container.begin(), container.end(), value),
- container.end());
-}
-
-template <typename CharT, typename Traits, typename Allocator, class Predicate>
-void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
- Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::deque<T, Allocator>& container, const Value& value) {
- container.erase(std::remove(container.begin(), container.end(), value),
- container.end());
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::vector<T, Allocator>& container, const Value& value) {
- container.erase(std::remove(container.begin(), container.end(), value),
- container.end());
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
- container.erase(std::remove_if(container.begin(), container.end(), pred),
- container.end());
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
- // Unlike std::forward_list::remove, this function template accepts
- // heterogeneous types and does not force a conversion to the container's
- // value type before invoking the == operator.
- container.remove_if([&](const T& cur) { return cur == value; });
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
- container.remove_if(pred);
-}
-
-template <class T, class Allocator, class Value>
-void Erase(std::list<T, Allocator>& container, const Value& value) {
- // Unlike std::list::remove, this function template accepts heterogeneous
- // types and does not force a conversion to the container's value type before
- // invoking the == operator.
- container.remove_if([&](const T& cur) { return cur == value; });
-}
-
-template <class T, class Allocator, class Predicate>
-void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
- container.remove_if(pred);
-}
-
-template <class Key, class T, class Compare, class Allocator, class Predicate>
-void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key, class T, class Compare, class Allocator, class Predicate>
-void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key, class Compare, class Allocator, class Predicate>
-void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key, class Compare, class Allocator, class Predicate>
-void EraseIf(std::multiset<Key, Compare, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class T,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class T,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(
- std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-template <class Key,
- class Hash,
- class KeyEqual,
- class Allocator,
- class Predicate>
-void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
- Predicate pred) {
- internal::IterateAndEraseIf(container, pred);
-}
-
-// A helper class to be used as the predicate with |EraseIf| to implement
-// in-place set intersection. Helps implement the algorithm of going through
-// each container an element at a time, erasing elements from the first
-// container if they aren't in the second container. Requires each container be
-// sorted. Note that the logic below appears inverted since it is returning
-// whether an element should be erased.
-template <class Collection>
-class IsNotIn {
- public:
- explicit IsNotIn(const Collection& collection)
- : i_(collection.begin()), end_(collection.end()) {}
-
- bool operator()(const typename Collection::value_type& x) {
- while (i_ != end_ && *i_ < x)
- ++i_;
- if (i_ == end_)
- return true;
- if (*i_ == x) {
- ++i_;
- return false;
- }
- return true;
- }
-
- private:
- typename Collection::const_iterator i_;
- const typename Collection::const_iterator end_;
-};
-
-// Helper for returning the optional value's address, or nullptr.
-template <class T>
-T* OptionalOrNullptr(base::Optional<T>& optional) {
- return optional.has_value() ? &optional.value() : nullptr;
-}
-
-template <class T>
-const T* OptionalOrNullptr(const base::Optional<T>& optional) {
- return optional.has_value() ? &optional.value() : nullptr;
-}
-
-} // namespace base
-
-#endif // BASE_STL_UTIL_H_
diff --git a/gn/base/strings/char_traits.h b/gn/base/strings/char_traits.h
deleted file mode 100644
index b193e216ccc..00000000000
--- a/gn/base/strings/char_traits.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 The Chromium 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 BASE_STRINGS_CHAR_TRAITS_H_
-#define BASE_STRINGS_CHAR_TRAITS_H_
-
-#include <stddef.h>
-
-#include "base/compiler_specific.h"
-
-namespace base {
-
-// constexpr version of http://en.cppreference.com/w/cpp/string/char_traits.
-// This currently just implements the bits needed to support a (mostly)
-// constexpr StringPiece.
-//
-// TODO(dcheng): Once we switch to C++17, most methods will become constexpr and
-// we can switch over to using the one in the standard library.
-template <typename T>
-struct CharTraits {
- // Performs a lexographical comparison of the first N characters of |s1| and
- // |s2|. Returns 0 if equal, -1 if |s1| is less than |s2|, and 1 if |s1| is
- // greater than |s2|.
- static constexpr int compare(const T* s1, const T* s2, size_t n) noexcept;
-
- // Returns the length of |s|, assuming null termination (and not including the
- // terminating null).
- static constexpr size_t length(const T* s) noexcept;
-};
-
-template <typename T>
-constexpr int CharTraits<T>::compare(const T* s1,
- const T* s2,
- size_t n) noexcept {
- for (; n; --n, ++s1, ++s2) {
- if (*s1 < *s2)
- return -1;
- if (*s1 > *s2)
- return 1;
- }
- return 0;
-}
-
-template <typename T>
-constexpr size_t CharTraits<T>::length(const T* s) noexcept {
- size_t i = 0;
- for (; *s; ++s)
- ++i;
- return i;
-}
-
-// char specialization of CharTraits that can use clang's constexpr instrinsics,
-// where available.
-template <>
-struct CharTraits<char> {
- static constexpr int compare(const char* s1,
- const char* s2,
- size_t n) noexcept;
- static constexpr size_t length(const char* s) noexcept;
-};
-
-constexpr int CharTraits<char>::compare(const char* s1,
- const char* s2,
- size_t n) noexcept {
-#if HAS_FEATURE(cxx_constexpr_string_builtins)
- return __builtin_memcmp(s1, s2, n);
-#else
- for (; n; --n, ++s1, ++s2) {
- if (*s1 < *s2)
- return -1;
- if (*s1 > *s2)
- return 1;
- }
- return 0;
-#endif
-}
-
-constexpr size_t CharTraits<char>::length(const char* s) noexcept {
-#if defined(__clang__)
- return __builtin_strlen(s);
-#else
- size_t i = 0;
- for (; *s; ++s)
- ++i;
- return i;
-#endif
-}
-
-} // namespace base
-
-#endif // BASE_STRINGS_CHAR_TRAITS_H_
diff --git a/gn/base/strings/string16.cc b/gn/base/strings/string16.cc
deleted file mode 100644
index 997ab20efac..00000000000
--- a/gn/base/strings/string16.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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/strings/string16.h"
-
-#if defined(WCHAR_T_IS_UTF16) && !defined(_AIX)
-
-#error This file should not be used on 2-byte wchar_t systems
-// If this winds up being needed on 2-byte wchar_t systems, either the
-// definitions below can be used, or the host system's wide character
-// functions like wmemcmp can be wrapped.
-
-#elif defined(WCHAR_T_IS_UTF32)
-
-#include <ostream>
-
-#include "base/strings/utf_string_conversions.h"
-
-namespace base {
-
-int c16memcmp(const char16* s1, const char16* s2, size_t n) {
- // We cannot call memcmp because that changes the semantics.
- while (n-- > 0) {
- if (*s1 != *s2) {
- // We cannot use (*s1 - *s2) because char16 is unsigned.
- return ((*s1 < *s2) ? -1 : 1);
- }
- ++s1;
- ++s2;
- }
- return 0;
-}
-
-size_t c16len(const char16* s) {
- const char16* s_orig = s;
- while (*s) {
- ++s;
- }
- return s - s_orig;
-}
-
-const char16* c16memchr(const char16* s, char16 c, size_t n) {
- while (n-- > 0) {
- if (*s == c) {
- return s;
- }
- ++s;
- }
- return nullptr;
-}
-
-char16* c16memmove(char16* s1, const char16* s2, size_t n) {
- return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16)));
-}
-
-char16* c16memcpy(char16* s1, const char16* s2, size_t n) {
- return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16)));
-}
-
-char16* c16memset(char16* s, char16 c, size_t n) {
- char16* s_orig = s;
- while (n-- > 0) {
- *s = c;
- ++s;
- }
- return s_orig;
-}
-
-namespace string16_internals {
-
-std::ostream& operator<<(std::ostream& out, const string16& str) {
- return out << UTF16ToUTF8(str);
-}
-
-void PrintTo(const string16& str, std::ostream* out) {
- *out << str;
-}
-
-} // namespace string16_internals
-
-} // namespace base
-
-template class std::
- basic_string<base::char16, base::string16_internals::string16_char_traits>;
-
-#endif // WCHAR_T_IS_UTF32
diff --git a/gn/base/strings/string16.h b/gn/base/strings/string16.h
deleted file mode 100644
index 59bf6d70c5b..00000000000
--- a/gn/base/strings/string16.h
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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 BASE_STRINGS_STRING16_H_
-#define BASE_STRINGS_STRING16_H_
-
-// WHAT:
-// A version of std::basic_string that provides 2-byte characters even when
-// wchar_t is not implemented as a 2-byte type. You can access this class as
-// string16. We also define char16, which string16 is based upon.
-//
-// WHY:
-// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2
-// data. Plenty of existing code operates on strings encoded as UTF-16.
-//
-// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make
-// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails
-// at run time, because it calls some functions (like wcslen) that come from
-// the system's native C library -- which was built with a 4-byte wchar_t!
-// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's
-// entirely improper on those systems where the encoding of wchar_t is defined
-// as UTF-32.
-//
-// Here, we define string16, which is similar to std::wstring but replaces all
-// libc functions with custom, 2-byte-char compatible routines. It is capable
-// of carrying UTF-16-encoded data.
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include <functional>
-#include <string>
-
-#include "util/build_config.h"
-
-#if defined(WCHAR_T_IS_UTF16)
-
-namespace base {
-
-typedef wchar_t char16;
-typedef std::wstring string16;
-
-} // namespace base
-
-#elif defined(WCHAR_T_IS_UTF32)
-
-#include <wchar.h> // for mbstate_t
-
-namespace base {
-
-typedef uint16_t char16;
-
-// char16 versions of the functions required by string16_char_traits; these
-// are based on the wide character functions of similar names ("w" or "wcs"
-// instead of "c16").
-int c16memcmp(const char16* s1, const char16* s2, size_t n);
-size_t c16len(const char16* s);
-const char16* c16memchr(const char16* s, char16 c, size_t n);
-char16* c16memmove(char16* s1, const char16* s2, size_t n);
-char16* c16memcpy(char16* s1, const char16* s2, size_t n);
-char16* c16memset(char16* s, char16 c, size_t n);
-
-// This namespace contains the implementation of base::string16 along with
-// things that need to be found via argument-dependent lookup from a
-// base::string16.
-namespace string16_internals {
-
-struct string16_char_traits {
- typedef char16 char_type;
- typedef int int_type;
-
- // int_type needs to be able to hold each possible value of char_type, and in
- // addition, the distinct value of eof().
- static_assert(sizeof(int_type) > sizeof(char_type),
- "int must be larger than 16 bits wide");
-
- typedef std::streamoff off_type;
- typedef mbstate_t state_type;
- typedef std::fpos<state_type> pos_type;
-
- static void assign(char_type& c1, const char_type& c2) { c1 = c2; }
-
- static bool eq(const char_type& c1, const char_type& c2) { return c1 == c2; }
- static bool lt(const char_type& c1, const char_type& c2) { return c1 < c2; }
-
- static int compare(const char_type* s1, const char_type* s2, size_t n) {
- return c16memcmp(s1, s2, n);
- }
-
- static size_t length(const char_type* s) { return c16len(s); }
-
- static const char_type* find(const char_type* s,
- size_t n,
- const char_type& a) {
- return c16memchr(s, a, n);
- }
-
- static char_type* move(char_type* s1, const char_type* s2, size_t n) {
- return c16memmove(s1, s2, n);
- }
-
- static char_type* copy(char_type* s1, const char_type* s2, size_t n) {
- return c16memcpy(s1, s2, n);
- }
-
- static char_type* assign(char_type* s, size_t n, char_type a) {
- return c16memset(s, a, n);
- }
-
- static int_type not_eof(const int_type& c) {
- return eq_int_type(c, eof()) ? 0 : c;
- }
-
- static char_type to_char_type(const int_type& c) { return char_type(c); }
-
- static int_type to_int_type(const char_type& c) { return int_type(c); }
-
- static bool eq_int_type(const int_type& c1, const int_type& c2) {
- return c1 == c2;
- }
-
- static int_type eof() { return static_cast<int_type>(EOF); }
-};
-
-} // namespace string16_internals
-
-typedef std::basic_string<char16,
- base::string16_internals::string16_char_traits>
- string16;
-
-namespace string16_internals {
-
-extern std::ostream& operator<<(std::ostream& out, const string16& str);
-
-// This is required by googletest to print a readable output on test failures.
-extern void PrintTo(const string16& str, std::ostream* out);
-
-} // namespace string16_internals
-
-} // namespace base
-
-// The string class will be explicitly instantiated only once, in string16.cc.
-//
-// std::basic_string<> in GNU libstdc++ contains a static data member,
-// _S_empty_rep_storage, to represent empty strings. When an operation such
-// as assignment or destruction is performed on a string, causing its existing
-// data member to be invalidated, it must not be freed if this static data
-// member is being used. Otherwise, it counts as an attempt to free static
-// (and not allocated) data, which is a memory error.
-//
-// Generally, due to C++ template magic, _S_empty_rep_storage will be marked
-// as a coalesced symbol, meaning that the linker will combine multiple
-// instances into a single one when generating output.
-//
-// If a string class is used by multiple shared libraries, a problem occurs.
-// Each library will get its own copy of _S_empty_rep_storage. When strings
-// are passed across a library boundary for alteration or destruction, memory
-// errors will result. GNU libstdc++ contains a configuration option,
-// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which
-// disables the static data member optimization, but it's a good optimization
-// and non-STL code is generally at the mercy of the system's STL
-// configuration. Fully-dynamic strings are not the default for GNU libstdc++
-// libstdc++ itself or for the libstdc++ installations on the systems we care
-// about, such as Mac OS X and relevant flavors of Linux.
-//
-// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 .
-//
-// To avoid problems, string classes need to be explicitly instantiated only
-// once, in exactly one library. All other string users see it via an "extern"
-// declaration. This is precisely how GNU libstdc++ handles
-// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring).
-//
-// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2),
-// in which the linker does not fully coalesce symbols when dead code
-// stripping is enabled. This bug causes the memory errors described above
-// to occur even when a std::basic_string<> does not cross shared library
-// boundaries, such as in statically-linked executables.
-//
-// TODO(mark): File this bug with Apple and update this note with a bug number.
-
-extern template class std::
- basic_string<base::char16, base::string16_internals::string16_char_traits>;
-
-// Specialize std::hash for base::string16. Although the style guide forbids
-// this in general, it is necessary for consistency with WCHAR_T_IS_UTF16
-// platforms, where base::string16 is a type alias for std::wstring.
-namespace std {
-template <>
-struct hash<base::string16> {
- std::size_t operator()(const base::string16& s) const {
- std::size_t result = 0;
- for (base::char16 c : s)
- result = (result * 131) + c;
- return result;
- }
-};
-} // namespace std
-
-#endif // WCHAR_T_IS_UTF32
-
-#endif // BASE_STRINGS_STRING16_H_
diff --git a/gn/base/strings/string_number_conversions.cc b/gn/base/strings/string_number_conversions.cc
deleted file mode 100644
index 01375f5de22..00000000000
--- a/gn/base/strings/string_number_conversions.cc
+++ /dev/null
@@ -1,458 +0,0 @@
-// 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 "base/strings/string_number_conversions.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <wctype.h>
-
-#include <limits>
-#include <type_traits>
-
-#include "base/logging.h"
-#include "base/numerics/safe_math.h"
-#include "base/scoped_clear_errno.h"
-#include "base/strings/utf_string_conversions.h"
-
-namespace base {
-
-namespace {
-
-template <typename STR, typename INT>
-struct IntToStringT {
- static STR IntToString(INT value) {
- // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
- // So round up to allocate 3 output characters per byte, plus 1 for '-'.
- const size_t kOutputBufSize =
- 3 * sizeof(INT) + std::numeric_limits<INT>::is_signed;
-
- // Create the string in a temporary buffer, write it back to front, and
- // then return the substr of what we ended up using.
- using CHR = typename STR::value_type;
- CHR outbuf[kOutputBufSize];
-
- // The ValueOrDie call below can never fail, because UnsignedAbs is valid
- // for all valid inputs.
- typename std::make_unsigned<INT>::type res =
- CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
-
- CHR* end = outbuf + kOutputBufSize;
- CHR* i = end;
- do {
- --i;
- DCHECK(i != outbuf);
- *i = static_cast<CHR>((res % 10) + '0');
- res /= 10;
- } while (res != 0);
- if (IsValueNegative(value)) {
- --i;
- DCHECK(i != outbuf);
- *i = static_cast<CHR>('-');
- }
- return STR(i, end);
- }
-};
-
-// Utility to convert a character to a digit in a given base
-template <typename CHAR, int BASE, bool BASE_LTE_10>
-class BaseCharToDigit {};
-
-// Faster specialization for bases <= 10
-template <typename CHAR, int BASE>
-class BaseCharToDigit<CHAR, BASE, true> {
- public:
- static bool Convert(CHAR c, uint8_t* digit) {
- if (c >= '0' && c < '0' + BASE) {
- *digit = static_cast<uint8_t>(c - '0');
- return true;
- }
- return false;
- }
-};
-
-// Specialization for bases where 10 < base <= 36
-template <typename CHAR, int BASE>
-class BaseCharToDigit<CHAR, BASE, false> {
- public:
- static bool Convert(CHAR c, uint8_t* digit) {
- if (c >= '0' && c <= '9') {
- *digit = c - '0';
- } else if (c >= 'a' && c < 'a' + BASE - 10) {
- *digit = c - 'a' + 10;
- } else if (c >= 'A' && c < 'A' + BASE - 10) {
- *digit = c - 'A' + 10;
- } else {
- return false;
- }
- return true;
- }
-};
-
-template <int BASE, typename CHAR>
-bool CharToDigit(CHAR c, uint8_t* digit) {
- return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
-}
-
-// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it
-// is locale independent, whereas the functions we are replacing were
-// locale-dependent. TBD what is desired, but for the moment let's not
-// introduce a change in behaviour.
-template <typename CHAR>
-class WhitespaceHelper {};
-
-template <>
-class WhitespaceHelper<char> {
- public:
- static bool Invoke(char c) {
- return 0 != isspace(static_cast<unsigned char>(c));
- }
-};
-
-template <>
-class WhitespaceHelper<char16> {
- public:
- static bool Invoke(char16 c) { return 0 != iswspace(c); }
-};
-
-template <typename CHAR>
-bool LocalIsWhitespace(CHAR c) {
- return WhitespaceHelper<CHAR>::Invoke(c);
-}
-
-// IteratorRangeToNumberTraits should provide:
-// - a typedef for iterator_type, the iterator type used as input.
-// - a typedef for value_type, the target numeric type.
-// - static functions min, max (returning the minimum and maximum permitted
-// values)
-// - constant kBase, the base in which to interpret the input
-template <typename IteratorRangeToNumberTraits>
-class IteratorRangeToNumber {
- public:
- typedef IteratorRangeToNumberTraits traits;
- typedef typename traits::iterator_type const_iterator;
- typedef typename traits::value_type value_type;
-
- // Generalized iterator-range-to-number conversion.
- //
- static bool Invoke(const_iterator begin,
- const_iterator end,
- value_type* output) {
- bool valid = true;
-
- while (begin != end && LocalIsWhitespace(*begin)) {
- valid = false;
- ++begin;
- }
-
- if (begin != end && *begin == '-') {
- if (!std::numeric_limits<value_type>::is_signed) {
- *output = 0;
- valid = false;
- } else if (!Negative::Invoke(begin + 1, end, output)) {
- valid = false;
- }
- } else {
- if (begin != end && *begin == '+') {
- ++begin;
- }
- if (!Positive::Invoke(begin, end, output)) {
- valid = false;
- }
- }
-
- return valid;
- }
-
- private:
- // Sign provides:
- // - a static function, CheckBounds, that determines whether the next digit
- // causes an overflow/underflow
- // - a static function, Increment, that appends the next digit appropriately
- // according to the sign of the number being parsed.
- template <typename Sign>
- class Base {
- public:
- static bool Invoke(const_iterator begin,
- const_iterator end,
- typename traits::value_type* output) {
- *output = 0;
-
- if (begin == end) {
- return false;
- }
-
- // Note: no performance difference was found when using template
- // specialization to remove this check in bases other than 16
- if (traits::kBase == 16 && end - begin > 2 && *begin == '0' &&
- (*(begin + 1) == 'x' || *(begin + 1) == 'X')) {
- begin += 2;
- }
-
- for (const_iterator current = begin; current != end; ++current) {
- uint8_t new_digit = 0;
-
- if (!CharToDigit<traits::kBase>(*current, &new_digit)) {
- return false;
- }
-
- if (current != begin) {
- if (!Sign::CheckBounds(output, new_digit)) {
- return false;
- }
- *output *= traits::kBase;
- }
-
- Sign::Increment(new_digit, output);
- }
- return true;
- }
- };
-
- class Positive : public Base<Positive> {
- public:
- static bool CheckBounds(value_type* output, uint8_t new_digit) {
- if (*output > static_cast<value_type>(traits::max() / traits::kBase) ||
- (*output == static_cast<value_type>(traits::max() / traits::kBase) &&
- new_digit > traits::max() % traits::kBase)) {
- *output = traits::max();
- return false;
- }
- return true;
- }
- static void Increment(uint8_t increment, value_type* output) {
- *output += increment;
- }
- };
-
- class Negative : public Base<Negative> {
- public:
- static bool CheckBounds(value_type* output, uint8_t new_digit) {
- if (*output < traits::min() / traits::kBase ||
- (*output == traits::min() / traits::kBase &&
- new_digit > 0 - traits::min() % traits::kBase)) {
- *output = traits::min();
- return false;
- }
- return true;
- }
- static void Increment(uint8_t increment, value_type* output) {
- *output -= increment;
- }
- };
-};
-
-template <typename ITERATOR, typename VALUE, int BASE>
-class BaseIteratorRangeToNumberTraits {
- public:
- typedef ITERATOR iterator_type;
- typedef VALUE value_type;
- static value_type min() { return std::numeric_limits<value_type>::min(); }
- static value_type max() { return std::numeric_limits<value_type>::max(); }
- static const int kBase = BASE;
-};
-
-template <typename ITERATOR>
-class BaseHexIteratorRangeToIntTraits
- : public BaseIteratorRangeToNumberTraits<ITERATOR, int, 16> {};
-
-template <typename ITERATOR>
-class BaseHexIteratorRangeToUIntTraits
- : public BaseIteratorRangeToNumberTraits<ITERATOR, uint32_t, 16> {};
-
-template <typename ITERATOR>
-class BaseHexIteratorRangeToInt64Traits
- : public BaseIteratorRangeToNumberTraits<ITERATOR, int64_t, 16> {};
-
-template <typename ITERATOR>
-class BaseHexIteratorRangeToUInt64Traits
- : public BaseIteratorRangeToNumberTraits<ITERATOR, uint64_t, 16> {};
-
-typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
- HexIteratorRangeToIntTraits;
-
-typedef BaseHexIteratorRangeToUIntTraits<StringPiece::const_iterator>
- HexIteratorRangeToUIntTraits;
-
-typedef BaseHexIteratorRangeToInt64Traits<StringPiece::const_iterator>
- HexIteratorRangeToInt64Traits;
-
-typedef BaseHexIteratorRangeToUInt64Traits<StringPiece::const_iterator>
- HexIteratorRangeToUInt64Traits;
-
-template <typename VALUE, int BASE>
-class StringPieceToNumberTraits
- : public BaseIteratorRangeToNumberTraits<StringPiece::const_iterator,
- VALUE,
- BASE> {};
-
-template <typename VALUE>
-bool StringToIntImpl(StringPiece input, VALUE* output) {
- return IteratorRangeToNumber<StringPieceToNumberTraits<VALUE, 10>>::Invoke(
- input.begin(), input.end(), output);
-}
-
-template <typename VALUE, int BASE>
-class StringPiece16ToNumberTraits
- : public BaseIteratorRangeToNumberTraits<StringPiece16::const_iterator,
- VALUE,
- BASE> {};
-
-template <typename VALUE>
-bool String16ToIntImpl(StringPiece16 input, VALUE* output) {
- return IteratorRangeToNumber<StringPiece16ToNumberTraits<VALUE, 10>>::Invoke(
- input.begin(), input.end(), output);
-}
-
-} // namespace
-
-std::string NumberToString(int value) {
- return IntToStringT<std::string, int>::IntToString(value);
-}
-
-string16 NumberToString16(int value) {
- return IntToStringT<string16, int>::IntToString(value);
-}
-
-std::string NumberToString(unsigned value) {
- return IntToStringT<std::string, unsigned>::IntToString(value);
-}
-
-string16 NumberToString16(unsigned value) {
- return IntToStringT<string16, unsigned>::IntToString(value);
-}
-
-std::string NumberToString(long value) {
- return IntToStringT<std::string, long>::IntToString(value);
-}
-
-string16 NumberToString16(long value) {
- return IntToStringT<string16, long>::IntToString(value);
-}
-
-std::string NumberToString(unsigned long value) {
- return IntToStringT<std::string, unsigned long>::IntToString(value);
-}
-
-string16 NumberToString16(unsigned long value) {
- return IntToStringT<string16, unsigned long>::IntToString(value);
-}
-
-std::string NumberToString(long long value) {
- return IntToStringT<std::string, long long>::IntToString(value);
-}
-
-string16 NumberToString16(long long value) {
- return IntToStringT<string16, long long>::IntToString(value);
-}
-
-std::string NumberToString(unsigned long long value) {
- return IntToStringT<std::string, unsigned long long>::IntToString(value);
-}
-
-string16 NumberToString16(unsigned long long value) {
- return IntToStringT<string16, unsigned long long>::IntToString(value);
-}
-
-bool StringToInt(StringPiece input, int* output) {
- return StringToIntImpl(input, output);
-}
-
-bool StringToInt(StringPiece16 input, int* output) {
- return String16ToIntImpl(input, output);
-}
-
-bool StringToUint(StringPiece input, unsigned* output) {
- return StringToIntImpl(input, output);
-}
-
-bool StringToUint(StringPiece16 input, unsigned* output) {
- return String16ToIntImpl(input, output);
-}
-
-bool StringToInt64(StringPiece input, int64_t* output) {
- return StringToIntImpl(input, output);
-}
-
-bool StringToInt64(StringPiece16 input, int64_t* output) {
- return String16ToIntImpl(input, output);
-}
-
-bool StringToUint64(StringPiece input, uint64_t* output) {
- return StringToIntImpl(input, output);
-}
-
-bool StringToUint64(StringPiece16 input, uint64_t* output) {
- return String16ToIntImpl(input, output);
-}
-
-bool StringToSizeT(StringPiece input, size_t* output) {
- return StringToIntImpl(input, output);
-}
-
-bool StringToSizeT(StringPiece16 input, size_t* output) {
- return String16ToIntImpl(input, output);
-}
-
-// Note: if you need to add String16ToDouble, first ask yourself if it's
-// really necessary. If it is, probably the best implementation here is to
-// convert to 8-bit and then use the 8-bit version.
-
-// Note: if you need to add an iterator range version of StringToDouble, first
-// ask yourself if it's really necessary. If it is, probably the best
-// implementation here is to instantiate a string and use the string version.
-
-std::string HexEncode(const void* bytes, size_t size) {
- static const char kHexChars[] = "0123456789ABCDEF";
-
- // Each input byte creates two output hex characters.
- std::string ret(size * 2, '\0');
-
- for (size_t i = 0; i < size; ++i) {
- char b = reinterpret_cast<const char*>(bytes)[i];
- ret[(i * 2)] = kHexChars[(b >> 4) & 0xf];
- ret[(i * 2) + 1] = kHexChars[b & 0xf];
- }
- return ret;
-}
-
-bool HexStringToInt(StringPiece input, int* output) {
- return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
- input.begin(), input.end(), output);
-}
-
-bool HexStringToUInt(StringPiece input, uint32_t* output) {
- return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
- input.begin(), input.end(), output);
-}
-
-bool HexStringToInt64(StringPiece input, int64_t* output) {
- return IteratorRangeToNumber<HexIteratorRangeToInt64Traits>::Invoke(
- input.begin(), input.end(), output);
-}
-
-bool HexStringToUInt64(StringPiece input, uint64_t* output) {
- return IteratorRangeToNumber<HexIteratorRangeToUInt64Traits>::Invoke(
- input.begin(), input.end(), output);
-}
-
-bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
- DCHECK_EQ(output->size(), 0u);
- size_t count = input.size();
- if (count == 0 || (count % 2) != 0)
- return false;
- for (uintptr_t i = 0; i < count / 2; ++i) {
- uint8_t msb = 0; // most significant 4 bits
- uint8_t lsb = 0; // least significant 4 bits
- if (!CharToDigit<16>(input[i * 2], &msb) ||
- !CharToDigit<16>(input[i * 2 + 1], &lsb)) {
- return false;
- }
- output->push_back((msb << 4) | lsb);
- }
- return true;
-}
-
-} // namespace base
diff --git a/gn/base/strings/string_number_conversions.h b/gn/base/strings/string_number_conversions.h
deleted file mode 100644
index ecf950e72eb..00000000000
--- a/gn/base/strings/string_number_conversions.h
+++ /dev/null
@@ -1,152 +0,0 @@
-// 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 BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
-#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "util/build_config.h"
-
-// ----------------------------------------------------------------------------
-// IMPORTANT MESSAGE FROM YOUR SPONSOR
-//
-// This file contains no "wstring" variants. New code should use string16. If
-// you need to make old code work, use the UTF8 version and convert. Please do
-// not add wstring variants.
-//
-// Please do not add "convenience" functions for converting strings to integers
-// that return the value and ignore success/failure. That encourages people to
-// write code that doesn't properly handle the error conditions.
-//
-// DO NOT use these functions in any UI unless it's NOT localized on purpose.
-// Instead, use base::MessageFormatter for a complex message with numbers
-// (integer, float) embedded or base::Format{Number,Percent} to
-// just format a single number/percent. Note that some languages use native
-// digits instead of ASCII digits while others use a group separator or decimal
-// point different from ',' and '.'. Using these functions in the UI would lead
-// numbers to be formatted in a non-native way.
-// ----------------------------------------------------------------------------
-
-namespace base {
-
-// Number -> string conversions ------------------------------------------------
-
-// Ignores locale! see warning above.
-std::string NumberToString(int value);
-string16 NumberToString16(int value);
-std::string NumberToString(unsigned int value);
-string16 NumberToString16(unsigned int value);
-std::string NumberToString(long value);
-string16 NumberToString16(long value);
-std::string NumberToString(unsigned long value);
-string16 NumberToString16(unsigned long value);
-std::string NumberToString(long long value);
-string16 NumberToString16(long long value);
-std::string NumberToString(unsigned long long value);
-string16 NumberToString16(unsigned long long value);
-
-// Type-specific naming for backwards compatibility.
-//
-// TODO(brettw) these should be removed and callers converted to the overloaded
-// "NumberToString" variant.
-inline std::string IntToString(int value) {
- return NumberToString(value);
-}
-inline string16 IntToString16(int value) {
- return NumberToString16(value);
-}
-inline std::string UintToString(unsigned value) {
- return NumberToString(value);
-}
-inline string16 UintToString16(unsigned value) {
- return NumberToString16(value);
-}
-inline std::string Int64ToString(int64_t value) {
- return NumberToString(value);
-}
-inline string16 Int64ToString16(int64_t value) {
- return NumberToString16(value);
-}
-
-// String -> number conversions ------------------------------------------------
-
-// Perform a best-effort conversion of the input string to a numeric type,
-// setting |*output| to the result of the conversion. Returns true for
-// "perfect" conversions; returns false in the following cases:
-// - Overflow. |*output| will be set to the maximum value supported
-// by the data type.
-// - Underflow. |*output| will be set to the minimum value supported
-// by the data type.
-// - Trailing characters in the string after parsing the number. |*output|
-// will be set to the value of the number that was parsed.
-// - Leading whitespace in the string before parsing the number. |*output| will
-// be set to the value of the number that was parsed.
-// - No characters parseable as a number at the beginning of the string.
-// |*output| will be set to 0.
-// - Empty string. |*output| will be set to 0.
-// WARNING: Will write to |output| even when returning false.
-// Read the comments above carefully.
-bool StringToInt(StringPiece input, int* output);
-bool StringToInt(StringPiece16 input, int* output);
-
-bool StringToUint(StringPiece input, unsigned* output);
-bool StringToUint(StringPiece16 input, unsigned* output);
-
-bool StringToInt64(StringPiece input, int64_t* output);
-bool StringToInt64(StringPiece16 input, int64_t* output);
-
-bool StringToUint64(StringPiece input, uint64_t* output);
-bool StringToUint64(StringPiece16 input, uint64_t* output);
-
-bool StringToSizeT(StringPiece input, size_t* output);
-bool StringToSizeT(StringPiece16 input, size_t* output);
-
-// Hex encoding ----------------------------------------------------------------
-
-// Returns a hex string representation of a binary buffer. The returned hex
-// string will be in upper case. This function does not check if |size| is
-// within reasonable limits since it's written with trusted data in mind. If
-// you suspect that the data you want to format might be large, the absolute
-// max size for |size| should be is
-// std::numeric_limits<size_t>::max() / 2
-std::string HexEncode(const void* bytes, size_t size);
-
-// Best effort conversion, see StringToInt above for restrictions.
-// Will only successful parse hex values that will fit into |output|, i.e.
-// -0x80000000 < |input| < 0x7FFFFFFF.
-bool HexStringToInt(StringPiece input, int* output);
-
-// Best effort conversion, see StringToInt above for restrictions.
-// Will only successful parse hex values that will fit into |output|, i.e.
-// 0x00000000 < |input| < 0xFFFFFFFF.
-// The string is not required to start with 0x.
-bool HexStringToUInt(StringPiece input, uint32_t* output);
-
-// Best effort conversion, see StringToInt above for restrictions.
-// Will only successful parse hex values that will fit into |output|, i.e.
-// -0x8000000000000000 < |input| < 0x7FFFFFFFFFFFFFFF.
-bool HexStringToInt64(StringPiece input, int64_t* output);
-
-// Best effort conversion, see StringToInt above for restrictions.
-// Will only successful parse hex values that will fit into |output|, i.e.
-// 0x0000000000000000 < |input| < 0xFFFFFFFFFFFFFFFF.
-// The string is not required to start with 0x.
-bool HexStringToUInt64(StringPiece input, uint64_t* output);
-
-// Similar to the previous functions, except that output is a vector of bytes.
-// |*output| will contain as many bytes as were successfully parsed prior to the
-// error. There is no overflow, but input.size() must be evenly divisible by 2.
-// Leading 0x or +/- are not allowed.
-bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output);
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
diff --git a/gn/base/strings/string_piece.cc b/gn/base/strings/string_piece.cc
deleted file mode 100644
index 612298a060e..00000000000
--- a/gn/base/strings/string_piece.cc
+++ /dev/null
@@ -1,440 +0,0 @@
-// 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.
-// Copied from strings/stringpiece.cc with modifications
-
-#include "base/strings/string_piece.h"
-
-#include <limits.h>
-
-#include <algorithm>
-#include <ostream>
-
-#include "base/logging.h"
-
-namespace base {
-namespace {
-
-// For each character in characters_wanted, sets the index corresponding
-// to the ASCII code of that character to 1 in table. This is used by
-// the find_.*_of methods below to tell whether or not a character is in
-// the lookup table in constant time.
-// The argument `table' must be an array that is large enough to hold all
-// the possible values of an unsigned char. Thus it should be be declared
-// as follows:
-// bool table[UCHAR_MAX + 1]
-inline void BuildLookupTable(const StringPiece& characters_wanted,
- bool* table) {
- const size_t length = characters_wanted.length();
- const char* const data = characters_wanted.data();
- for (size_t i = 0; i < length; ++i) {
- table[static_cast<unsigned char>(data[i])] = true;
- }
-}
-
-} // namespace
-
-// MSVC doesn't like complex extern templates and DLLs.
-#if !defined(COMPILER_MSVC)
-template class BasicStringPiece<std::string>;
-template class BasicStringPiece<string16>;
-#endif
-
-bool operator==(const StringPiece& x, const StringPiece& y) {
- if (x.size() != y.size())
- return false;
-
- return CharTraits<StringPiece::value_type>::compare(x.data(), y.data(),
- x.size()) == 0;
-}
-
-std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
- o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
- return o;
-}
-
-namespace internal {
-
-template <typename STR>
-void CopyToStringT(const BasicStringPiece<STR>& self, STR* target) {
- if (self.empty())
- target->clear();
- else
- target->assign(self.data(), self.size());
-}
-
-void CopyToString(const StringPiece& self, std::string* target) {
- CopyToStringT(self, target);
-}
-
-void CopyToString(const StringPiece16& self, string16* target) {
- CopyToStringT(self, target);
-}
-
-template <typename STR>
-void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
- if (!self.empty())
- target->append(self.data(), self.size());
-}
-
-void AppendToString(const StringPiece& self, std::string* target) {
- AppendToStringT(self, target);
-}
-
-void AppendToString(const StringPiece16& self, string16* target) {
- AppendToStringT(self, target);
-}
-
-template <typename STR>
-size_t copyT(const BasicStringPiece<STR>& self,
- typename STR::value_type* buf,
- size_t n,
- size_t pos) {
- size_t ret = std::min(self.size() - pos, n);
- memcpy(buf, self.data() + pos, ret * sizeof(typename STR::value_type));
- return ret;
-}
-
-size_t copy(const StringPiece& self, char* buf, size_t n, size_t pos) {
- return copyT(self, buf, n, pos);
-}
-
-size_t copy(const StringPiece16& self, char16* buf, size_t n, size_t pos) {
- return copyT(self, buf, n, pos);
-}
-
-template <typename STR>
-size_t findT(const BasicStringPiece<STR>& self,
- const BasicStringPiece<STR>& s,
- size_t pos) {
- if (pos > self.size())
- return BasicStringPiece<STR>::npos;
-
- typename BasicStringPiece<STR>::const_iterator result =
- std::search(self.begin() + pos, self.end(), s.begin(), s.end());
- const size_t xpos = static_cast<size_t>(result - self.begin());
- return xpos + s.size() <= self.size() ? xpos : BasicStringPiece<STR>::npos;
-}
-
-size_t find(const StringPiece& self, const StringPiece& s, size_t pos) {
- return findT(self, s, pos);
-}
-
-size_t find(const StringPiece16& self, const StringPiece16& s, size_t pos) {
- return findT(self, s, pos);
-}
-
-template <typename STR>
-size_t findT(const BasicStringPiece<STR>& self,
- typename STR::value_type c,
- size_t pos) {
- if (pos >= self.size())
- return BasicStringPiece<STR>::npos;
-
- typename BasicStringPiece<STR>::const_iterator result =
- std::find(self.begin() + pos, self.end(), c);
- return result != self.end() ? static_cast<size_t>(result - self.begin())
- : BasicStringPiece<STR>::npos;
-}
-
-size_t find(const StringPiece& self, char c, size_t pos) {
- return findT(self, c, pos);
-}
-
-size_t find(const StringPiece16& self, char16 c, size_t pos) {
- return findT(self, c, pos);
-}
-
-template <typename STR>
-size_t rfindT(const BasicStringPiece<STR>& self,
- const BasicStringPiece<STR>& s,
- size_t pos) {
- if (self.size() < s.size())
- return BasicStringPiece<STR>::npos;
-
- if (s.empty())
- return std::min(self.size(), pos);
-
- typename BasicStringPiece<STR>::const_iterator last =
- self.begin() + std::min(self.size() - s.size(), pos) + s.size();
- typename BasicStringPiece<STR>::const_iterator result =
- std::find_end(self.begin(), last, s.begin(), s.end());
- return result != last ? static_cast<size_t>(result - self.begin())
- : BasicStringPiece<STR>::npos;
-}
-
-size_t rfind(const StringPiece& self, const StringPiece& s, size_t pos) {
- return rfindT(self, s, pos);
-}
-
-size_t rfind(const StringPiece16& self, const StringPiece16& s, size_t pos) {
- return rfindT(self, s, pos);
-}
-
-template <typename STR>
-size_t rfindT(const BasicStringPiece<STR>& self,
- typename STR::value_type c,
- size_t pos) {
- if (self.size() == 0)
- return BasicStringPiece<STR>::npos;
-
- for (size_t i = std::min(pos, self.size() - 1);; --i) {
- if (self.data()[i] == c)
- return i;
- if (i == 0)
- break;
- }
- return BasicStringPiece<STR>::npos;
-}
-
-size_t rfind(const StringPiece& self, char c, size_t pos) {
- return rfindT(self, c, pos);
-}
-
-size_t rfind(const StringPiece16& self, char16 c, size_t pos) {
- return rfindT(self, c, pos);
-}
-
-// 8-bit version using lookup table.
-size_t find_first_of(const StringPiece& self,
- const StringPiece& s,
- size_t pos) {
- if (self.size() == 0 || s.size() == 0)
- return StringPiece::npos;
-
- // Avoid the cost of BuildLookupTable() for a single-character search.
- if (s.size() == 1)
- return find(self, s.data()[0], pos);
-
- bool lookup[UCHAR_MAX + 1] = {false};
- BuildLookupTable(s, lookup);
- for (size_t i = pos; i < self.size(); ++i) {
- if (lookup[static_cast<unsigned char>(self.data()[i])]) {
- return i;
- }
- }
- return StringPiece::npos;
-}
-
-// 16-bit brute force version.
-size_t find_first_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos) {
- StringPiece16::const_iterator found =
- std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end());
- if (found == self.end())
- return StringPiece16::npos;
- return found - self.begin();
-}
-
-// 8-bit version using lookup table.
-size_t find_first_not_of(const StringPiece& self,
- const StringPiece& s,
- size_t pos) {
- if (self.size() == 0)
- return StringPiece::npos;
-
- if (s.size() == 0)
- return 0;
-
- // Avoid the cost of BuildLookupTable() for a single-character search.
- if (s.size() == 1)
- return find_first_not_of(self, s.data()[0], pos);
-
- bool lookup[UCHAR_MAX + 1] = {false};
- BuildLookupTable(s, lookup);
- for (size_t i = pos; i < self.size(); ++i) {
- if (!lookup[static_cast<unsigned char>(self.data()[i])]) {
- return i;
- }
- }
- return StringPiece::npos;
-}
-
-// 16-bit brute-force version.
-size_t find_first_not_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos) {
- if (self.size() == 0)
- return StringPiece16::npos;
-
- for (size_t self_i = pos; self_i < self.size(); ++self_i) {
- bool found = false;
- for (size_t s_i = 0; s_i < s.size(); ++s_i) {
- if (self[self_i] == s[s_i]) {
- found = true;
- break;
- }
- }
- if (!found)
- return self_i;
- }
- return StringPiece16::npos;
-}
-
-template <typename STR>
-size_t find_first_not_ofT(const BasicStringPiece<STR>& self,
- typename STR::value_type c,
- size_t pos) {
- if (self.size() == 0)
- return BasicStringPiece<STR>::npos;
-
- for (; pos < self.size(); ++pos) {
- if (self.data()[pos] != c) {
- return pos;
- }
- }
- return BasicStringPiece<STR>::npos;
-}
-
-size_t find_first_not_of(const StringPiece& self, char c, size_t pos) {
- return find_first_not_ofT(self, c, pos);
-}
-
-size_t find_first_not_of(const StringPiece16& self, char16 c, size_t pos) {
- return find_first_not_ofT(self, c, pos);
-}
-
-// 8-bit version using lookup table.
-size_t find_last_of(const StringPiece& self, const StringPiece& s, size_t pos) {
- if (self.size() == 0 || s.size() == 0)
- return StringPiece::npos;
-
- // Avoid the cost of BuildLookupTable() for a single-character search.
- if (s.size() == 1)
- return rfind(self, s.data()[0], pos);
-
- bool lookup[UCHAR_MAX + 1] = {false};
- BuildLookupTable(s, lookup);
- for (size_t i = std::min(pos, self.size() - 1);; --i) {
- if (lookup[static_cast<unsigned char>(self.data()[i])])
- return i;
- if (i == 0)
- break;
- }
- return StringPiece::npos;
-}
-
-// 16-bit brute-force version.
-size_t find_last_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos) {
- if (self.size() == 0)
- return StringPiece16::npos;
-
- for (size_t self_i = std::min(pos, self.size() - 1);; --self_i) {
- for (size_t s_i = 0; s_i < s.size(); s_i++) {
- if (self.data()[self_i] == s[s_i])
- return self_i;
- }
- if (self_i == 0)
- break;
- }
- return StringPiece16::npos;
-}
-
-// 8-bit version using lookup table.
-size_t find_last_not_of(const StringPiece& self,
- const StringPiece& s,
- size_t pos) {
- if (self.size() == 0)
- return StringPiece::npos;
-
- size_t i = std::min(pos, self.size() - 1);
- if (s.size() == 0)
- return i;
-
- // Avoid the cost of BuildLookupTable() for a single-character search.
- if (s.size() == 1)
- return find_last_not_of(self, s.data()[0], pos);
-
- bool lookup[UCHAR_MAX + 1] = {false};
- BuildLookupTable(s, lookup);
- for (;; --i) {
- if (!lookup[static_cast<unsigned char>(self.data()[i])])
- return i;
- if (i == 0)
- break;
- }
- return StringPiece::npos;
-}
-
-// 16-bit brute-force version.
-size_t find_last_not_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos) {
- if (self.size() == 0)
- return StringPiece::npos;
-
- for (size_t self_i = std::min(pos, self.size() - 1);; --self_i) {
- bool found = false;
- for (size_t s_i = 0; s_i < s.size(); s_i++) {
- if (self.data()[self_i] == s[s_i]) {
- found = true;
- break;
- }
- }
- if (!found)
- return self_i;
- if (self_i == 0)
- break;
- }
- return StringPiece16::npos;
-}
-
-template <typename STR>
-size_t find_last_not_ofT(const BasicStringPiece<STR>& self,
- typename STR::value_type c,
- size_t pos) {
- if (self.size() == 0)
- return BasicStringPiece<STR>::npos;
-
- for (size_t i = std::min(pos, self.size() - 1);; --i) {
- if (self.data()[i] != c)
- return i;
- if (i == 0)
- break;
- }
- return BasicStringPiece<STR>::npos;
-}
-
-size_t find_last_not_of(const StringPiece& self, char c, size_t pos) {
- return find_last_not_ofT(self, c, pos);
-}
-
-size_t find_last_not_of(const StringPiece16& self, char16 c, size_t pos) {
- return find_last_not_ofT(self, c, pos);
-}
-
-template <typename STR>
-BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self,
- size_t pos,
- size_t n) {
- if (pos > self.size())
- pos = self.size();
- if (n > self.size() - pos)
- n = self.size() - pos;
- return BasicStringPiece<STR>(self.data() + pos, n);
-}
-
-StringPiece substr(const StringPiece& self, size_t pos, size_t n) {
- return substrT(self, pos, n);
-}
-
-StringPiece16 substr(const StringPiece16& self, size_t pos, size_t n) {
- return substrT(self, pos, n);
-}
-
-#if DCHECK_IS_ON()
-void AssertIteratorsInOrder(std::string::const_iterator begin,
- std::string::const_iterator end) {
- DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
-}
-void AssertIteratorsInOrder(string16::const_iterator begin,
- string16::const_iterator end) {
- DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
-}
-#endif
-
-} // namespace internal
-} // namespace base
diff --git a/gn/base/strings/string_piece.h b/gn/base/strings/string_piece.h
deleted file mode 100644
index 8e4deaed652..00000000000
--- a/gn/base/strings/string_piece.h
+++ /dev/null
@@ -1,439 +0,0 @@
-// 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.
-// Copied from strings/stringpiece.h with modifications
-//
-// A string-like object that points to a sized piece of memory.
-//
-// You can use StringPiece as a function or method parameter. A StringPiece
-// parameter can receive a double-quoted string literal argument, a "const
-// char*" argument, a string argument, or a StringPiece argument with no data
-// copying. Systematic use of StringPiece for arguments reduces data
-// copies and strlen() calls.
-//
-// Prefer passing StringPieces by value:
-// void MyFunction(StringPiece arg);
-// If circumstances require, you may also pass by const reference:
-// void MyFunction(const StringPiece& arg); // not preferred
-// Both of these have the same lifetime semantics. Passing by value
-// generates slightly smaller code. For more discussion, Googlers can see
-// the thread go/stringpiecebyvalue on c-users.
-
-#ifndef BASE_STRINGS_STRING_PIECE_H_
-#define BASE_STRINGS_STRING_PIECE_H_
-
-#include <stddef.h>
-
-#include <iosfwd>
-#include <string>
-
-#include "base/logging.h"
-#include "base/strings/char_traits.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_piece_forward.h"
-
-namespace base {
-
-// internal --------------------------------------------------------------------
-
-// Many of the StringPiece functions use different implementations for the
-// 8-bit and 16-bit versions, and we don't want lots of template expansions in
-// this (very common) header that will slow down compilation.
-//
-// So here we define overloaded functions called by the StringPiece template.
-// For those that share an implementation, the two versions will expand to a
-// template internal to the .cc file.
-namespace internal {
-
-void CopyToString(const StringPiece& self, std::string* target);
-void CopyToString(const StringPiece16& self, string16* target);
-
-void AppendToString(const StringPiece& self, std::string* target);
-void AppendToString(const StringPiece16& self, string16* target);
-
-size_t copy(const StringPiece& self, char* buf, size_t n, size_t pos);
-size_t copy(const StringPiece16& self, char16* buf, size_t n, size_t pos);
-
-size_t find(const StringPiece& self, const StringPiece& s, size_t pos);
-size_t find(const StringPiece16& self, const StringPiece16& s, size_t pos);
-size_t find(const StringPiece& self, char c, size_t pos);
-size_t find(const StringPiece16& self, char16 c, size_t pos);
-
-size_t rfind(const StringPiece& self, const StringPiece& s, size_t pos);
-size_t rfind(const StringPiece16& self, const StringPiece16& s, size_t pos);
-size_t rfind(const StringPiece& self, char c, size_t pos);
-size_t rfind(const StringPiece16& self, char16 c, size_t pos);
-
-size_t find_first_of(const StringPiece& self, const StringPiece& s, size_t pos);
-size_t find_first_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos);
-
-size_t find_first_not_of(const StringPiece& self,
- const StringPiece& s,
- size_t pos);
-size_t find_first_not_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos);
-size_t find_first_not_of(const StringPiece& self, char c, size_t pos);
-size_t find_first_not_of(const StringPiece16& self, char16 c, size_t pos);
-
-size_t find_last_of(const StringPiece& self, const StringPiece& s, size_t pos);
-size_t find_last_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos);
-size_t find_last_of(const StringPiece& self, char c, size_t pos);
-size_t find_last_of(const StringPiece16& self, char16 c, size_t pos);
-
-size_t find_last_not_of(const StringPiece& self,
- const StringPiece& s,
- size_t pos);
-size_t find_last_not_of(const StringPiece16& self,
- const StringPiece16& s,
- size_t pos);
-size_t find_last_not_of(const StringPiece16& self, char16 c, size_t pos);
-size_t find_last_not_of(const StringPiece& self, char c, size_t pos);
-
-StringPiece substr(const StringPiece& self, size_t pos, size_t n);
-StringPiece16 substr(const StringPiece16& self, size_t pos, size_t n);
-
-#if DCHECK_IS_ON()
-// Asserts that begin <= end to catch some errors with iterator usage.
-void AssertIteratorsInOrder(std::string::const_iterator begin,
- std::string::const_iterator end);
-void AssertIteratorsInOrder(string16::const_iterator begin,
- string16::const_iterator end);
-#endif
-
-} // namespace internal
-
-// BasicStringPiece ------------------------------------------------------------
-
-// Defines the types, methods, operators, and data members common to both
-// StringPiece and StringPiece16. Do not refer to this class directly, but
-// rather to BasicStringPiece, StringPiece, or StringPiece16.
-//
-// This is templatized by string class type rather than character type, so
-// BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
-template <typename STRING_TYPE>
-class BasicStringPiece {
- public:
- // Standard STL container boilerplate.
- typedef size_t size_type;
- typedef typename STRING_TYPE::value_type value_type;
- typedef const value_type* pointer;
- typedef const value_type& reference;
- typedef const value_type& const_reference;
- typedef ptrdiff_t difference_type;
- typedef const value_type* const_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- static const size_type npos;
-
- public:
- // We provide non-explicit singleton constructors so users can pass
- // in a "const char*" or a "string" wherever a "StringPiece" is
- // expected (likewise for char16, string16, StringPiece16).
- constexpr BasicStringPiece() : ptr_(NULL), length_(0) {}
- // TODO(dcheng): Construction from nullptr is not allowed for
- // std::basic_string_view, so remove the special handling for it.
- // Note: This doesn't just use STRING_TYPE::traits_type::length(), since that
- // isn't constexpr until C++17.
- constexpr BasicStringPiece(const value_type* str)
- : ptr_(str), length_(!str ? 0 : CharTraits<value_type>::length(str)) {}
- BasicStringPiece(const STRING_TYPE& str)
- : ptr_(str.data()), length_(str.size()) {}
- constexpr BasicStringPiece(const value_type* offset, size_type len)
- : ptr_(offset), length_(len) {}
- BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
- const typename STRING_TYPE::const_iterator& end) {
-#if DCHECK_IS_ON()
- // This assertion is done out-of-line to avoid bringing in logging.h and
- // instantiating logging macros for every instantiation.
- internal::AssertIteratorsInOrder(begin, end);
-#endif
- length_ = static_cast<size_t>(std::distance(begin, end));
-
- // The length test before assignment is to avoid dereferencing an iterator
- // that may point to the end() of a string.
- ptr_ = length_ > 0 ? &*begin : nullptr;
- }
-
- // data() may return a pointer to a buffer with embedded NULs, and the
- // returned buffer may or may not be null terminated. Therefore it is
- // typically a mistake to pass data() to a routine that expects a NUL
- // terminated string.
- constexpr const value_type* data() const { return ptr_; }
- constexpr size_type size() const { return length_; }
- constexpr size_type length() const { return length_; }
- bool empty() const { return length_ == 0; }
-
- void clear() {
- ptr_ = NULL;
- length_ = 0;
- }
- void set(const value_type* data, size_type len) {
- ptr_ = data;
- length_ = len;
- }
- void set(const value_type* str) {
- ptr_ = str;
- length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
- }
-
- constexpr value_type operator[](size_type i) const {
- CHECK(i < length_);
- return ptr_[i];
- }
-
- value_type front() const {
- CHECK_NE(0UL, length_);
- return ptr_[0];
- }
-
- value_type back() const {
- CHECK_NE(0UL, length_);
- return ptr_[length_ - 1];
- }
-
- constexpr void remove_prefix(size_type n) {
- CHECK(n <= length_);
- ptr_ += n;
- length_ -= n;
- }
-
- constexpr void remove_suffix(size_type n) {
- CHECK(n <= length_);
- length_ -= n;
- }
-
- constexpr int compare(BasicStringPiece x) const noexcept {
- int r = CharTraits<value_type>::compare(
- ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_));
- if (r == 0) {
- if (length_ < x.length_)
- r = -1;
- else if (length_ > x.length_)
- r = +1;
- }
- return r;
- }
-
- // This is the style of conversion preferred by std::string_view in C++17.
- explicit operator STRING_TYPE() const { return as_string(); }
-
- STRING_TYPE as_string() const {
- // std::string doesn't like to take a NULL pointer even with a 0 size.
- return empty() ? STRING_TYPE() : STRING_TYPE(data(), size());
- }
-
- const_iterator begin() const { return ptr_; }
- const_iterator end() const { return ptr_ + length_; }
- const_reverse_iterator rbegin() const {
- return const_reverse_iterator(ptr_ + length_);
- }
- const_reverse_iterator rend() const { return const_reverse_iterator(ptr_); }
-
- size_type max_size() const { return length_; }
- size_type capacity() const { return length_; }
-
- // Sets the value of the given string target type to be the current string.
- // This saves a temporary over doing |a = b.as_string()|
- void CopyToString(STRING_TYPE* target) const {
- internal::CopyToString(*this, target);
- }
-
- void AppendToString(STRING_TYPE* target) const {
- internal::AppendToString(*this, target);
- }
-
- size_type copy(value_type* buf, size_type n, size_type pos = 0) const {
- return internal::copy(*this, buf, n, pos);
- }
-
- // Does "this" start with "x"
- constexpr bool starts_with(BasicStringPiece x) const noexcept {
- return (
- (this->length_ >= x.length_) &&
- (CharTraits<value_type>::compare(this->ptr_, x.ptr_, x.length_) == 0));
- }
-
- // Does "this" end with "x"
- constexpr bool ends_with(BasicStringPiece x) const noexcept {
- return ((this->length_ >= x.length_) &&
- (CharTraits<value_type>::compare(
- this->ptr_ + (this->length_ - x.length_), x.ptr_, x.length_) ==
- 0));
- }
-
- // find: Search for a character or substring at a given offset.
- size_type find(const BasicStringPiece<STRING_TYPE>& s,
- size_type pos = 0) const {
- return internal::find(*this, s, pos);
- }
- size_type find(value_type c, size_type pos = 0) const {
- return internal::find(*this, c, pos);
- }
-
- // rfind: Reverse find.
- size_type rfind(const BasicStringPiece& s,
- size_type pos = BasicStringPiece::npos) const {
- return internal::rfind(*this, s, pos);
- }
- size_type rfind(value_type c, size_type pos = BasicStringPiece::npos) const {
- return internal::rfind(*this, c, pos);
- }
-
- // find_first_of: Find the first occurence of one of a set of characters.
- size_type find_first_of(const BasicStringPiece& s, size_type pos = 0) const {
- return internal::find_first_of(*this, s, pos);
- }
- size_type find_first_of(value_type c, size_type pos = 0) const {
- return find(c, pos);
- }
-
- // find_first_not_of: Find the first occurence not of a set of characters.
- size_type find_first_not_of(const BasicStringPiece& s,
- size_type pos = 0) const {
- return internal::find_first_not_of(*this, s, pos);
- }
- size_type find_first_not_of(value_type c, size_type pos = 0) const {
- return internal::find_first_not_of(*this, c, pos);
- }
-
- // find_last_of: Find the last occurence of one of a set of characters.
- size_type find_last_of(const BasicStringPiece& s,
- size_type pos = BasicStringPiece::npos) const {
- return internal::find_last_of(*this, s, pos);
- }
- size_type find_last_of(value_type c,
- size_type pos = BasicStringPiece::npos) const {
- return rfind(c, pos);
- }
-
- // find_last_not_of: Find the last occurence not of a set of characters.
- size_type find_last_not_of(const BasicStringPiece& s,
- size_type pos = BasicStringPiece::npos) const {
- return internal::find_last_not_of(*this, s, pos);
- }
- size_type find_last_not_of(value_type c,
- size_type pos = BasicStringPiece::npos) const {
- return internal::find_last_not_of(*this, c, pos);
- }
-
- // substr.
- BasicStringPiece substr(size_type pos,
- size_type n = BasicStringPiece::npos) const {
- return internal::substr(*this, pos, n);
- }
-
- protected:
- const value_type* ptr_;
- size_type length_;
-};
-
-template <typename STRING_TYPE>
-const typename BasicStringPiece<STRING_TYPE>::size_type
- BasicStringPiece<STRING_TYPE>::npos =
- typename BasicStringPiece<STRING_TYPE>::size_type(-1);
-
-// MSVC doesn't like complex extern templates and DLLs.
-#if !defined(COMPILER_MSVC)
-extern template class BasicStringPiece<std::string>;
-extern template class BasicStringPiece<string16>;
-#endif
-
-// StingPiece operators --------------------------------------------------------
-
-bool operator==(const StringPiece& x, const StringPiece& y);
-
-inline bool operator!=(const StringPiece& x, const StringPiece& y) {
- return !(x == y);
-}
-
-inline bool operator<(const StringPiece& x, const StringPiece& y) {
- const int r = CharTraits<StringPiece::value_type>::compare(
- x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size()));
- return ((r < 0) || ((r == 0) && (x.size() < y.size())));
-}
-
-inline bool operator>(const StringPiece& x, const StringPiece& y) {
- return y < x;
-}
-
-inline bool operator<=(const StringPiece& x, const StringPiece& y) {
- return !(x > y);
-}
-
-inline bool operator>=(const StringPiece& x, const StringPiece& y) {
- return !(x < y);
-}
-
-// StringPiece16 operators -----------------------------------------------------
-
-inline bool operator==(const StringPiece16& x, const StringPiece16& y) {
- if (x.size() != y.size())
- return false;
-
- return CharTraits<StringPiece16::value_type>::compare(x.data(), y.data(),
- x.size()) == 0;
-}
-
-inline bool operator!=(const StringPiece16& x, const StringPiece16& y) {
- return !(x == y);
-}
-
-inline bool operator<(const StringPiece16& x, const StringPiece16& y) {
- const int r = CharTraits<StringPiece16::value_type>::compare(
- x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size()));
- return ((r < 0) || ((r == 0) && (x.size() < y.size())));
-}
-
-inline bool operator>(const StringPiece16& x, const StringPiece16& y) {
- return y < x;
-}
-
-inline bool operator<=(const StringPiece16& x, const StringPiece16& y) {
- return !(x > y);
-}
-
-inline bool operator>=(const StringPiece16& x, const StringPiece16& y) {
- return !(x < y);
-}
-
-std::ostream& operator<<(std::ostream& o, const StringPiece& piece);
-
-// Hashing ---------------------------------------------------------------------
-
-// We provide appropriate hash functions so StringPiece and StringPiece16 can
-// be used as keys in hash sets and maps.
-
-// This hash function is copied from base/strings/string16.h. We don't use the
-// ones already defined for string and string16 directly because it would
-// require the string constructors to be called, which we don't want.
-#define HASH_STRING_PIECE(StringPieceType, string_piece) \
- std::size_t result = 0; \
- for (StringPieceType::const_iterator i = string_piece.begin(); \
- i != string_piece.end(); ++i) \
- result = (result * 131) + *i; \
- return result;
-
-struct StringPieceHash {
- std::size_t operator()(const StringPiece& sp) const {
- HASH_STRING_PIECE(StringPiece, sp);
- }
-};
-struct StringPiece16Hash {
- std::size_t operator()(const StringPiece16& sp16) const {
- HASH_STRING_PIECE(StringPiece16, sp16);
- }
-};
-struct WStringPieceHash {
- std::size_t operator()(const WStringPiece& wsp) const {
- HASH_STRING_PIECE(WStringPiece, wsp);
- }
-};
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_PIECE_H_
diff --git a/gn/base/strings/string_piece_forward.h b/gn/base/strings/string_piece_forward.h
deleted file mode 100644
index b50b9806c9b..00000000000
--- a/gn/base/strings/string_piece_forward.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Forward declaration of StringPiece types from base/strings/string_piece.h
-
-#ifndef BASE_STRINGS_STRING_PIECE_FORWARD_H_
-#define BASE_STRINGS_STRING_PIECE_FORWARD_H_
-
-#include <string>
-
-#include "base/strings/string16.h"
-
-namespace base {
-
-template <typename STRING_TYPE>
-class BasicStringPiece;
-typedef BasicStringPiece<std::string> StringPiece;
-typedef BasicStringPiece<string16> StringPiece16;
-typedef BasicStringPiece<std::wstring> WStringPiece;
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_PIECE_FORWARD_H_
diff --git a/gn/base/strings/string_split.cc b/gn/base/strings/string_split.cc
deleted file mode 100644
index e9b5c96c9b9..00000000000
--- a/gn/base/strings/string_split.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-// 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 "base/strings/string_split.h"
-
-#include <stddef.h>
-
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/third_party/icu/icu_utf.h"
-
-namespace base {
-
-namespace {
-
-// PieceToOutputType converts a StringPiece as needed to a given output type,
-// which is either the same type of StringPiece (a NOP) or the corresponding
-// non-piece string type.
-//
-// The default converter is a NOP, it works when the OutputType is the
-// correct StringPiece.
-template <typename Str, typename OutputType>
-OutputType PieceToOutputType(BasicStringPiece<Str> piece) {
- return piece;
-}
-template <> // Convert StringPiece to std::string
-std::string PieceToOutputType<std::string, std::string>(StringPiece piece) {
- return piece.as_string();
-}
-template <> // Convert StringPiece16 to string16.
-string16 PieceToOutputType<string16, string16>(StringPiece16 piece) {
- return piece.as_string();
-}
-
-// Returns either the ASCII or UTF-16 whitespace.
-template <typename Str>
-BasicStringPiece<Str> WhitespaceForType();
-template <>
-StringPiece16 WhitespaceForType<string16>() {
- return kWhitespaceUTF16;
-}
-template <>
-StringPiece WhitespaceForType<std::string>() {
- return kWhitespaceASCII;
-}
-
-// Optimize the single-character case to call find() on the string instead,
-// since this is the common case and can be made faster. This could have been
-// done with template specialization too, but would have been less clear.
-//
-// There is no corresponding FindFirstNotOf because StringPiece already
-// implements these different versions that do the optimized searching.
-size_t FindFirstOf(StringPiece piece, char c, size_t pos) {
- return piece.find(c, pos);
-}
-size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) {
- return piece.find(c, pos);
-}
-size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) {
- return piece.find_first_of(one_of, pos);
-}
-size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) {
- return piece.find_first_of(one_of, pos);
-}
-
-// General string splitter template. Can take 8- or 16-bit input, can produce
-// the corresponding string or StringPiece output, and can take single- or
-// multiple-character delimiters.
-//
-// DelimiterType is either a character (Str::value_type) or a string piece of
-// multiple characters (BasicStringPiece<Str>). StringPiece has a version of
-// find for both of these cases, and the single-character version is the most
-// common and can be implemented faster, which is why this is a template.
-template <typename Str, typename OutputStringType, typename DelimiterType>
-static std::vector<OutputStringType> SplitStringT(BasicStringPiece<Str> str,
- DelimiterType delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- std::vector<OutputStringType> result;
- if (str.empty())
- return result;
-
- size_t start = 0;
- while (start != Str::npos) {
- size_t end = FindFirstOf(str, delimiter, start);
-
- BasicStringPiece<Str> piece;
- if (end == Str::npos) {
- piece = str.substr(start);
- start = Str::npos;
- } else {
- piece = str.substr(start, end - start);
- start = end + 1;
- }
-
- if (whitespace == TRIM_WHITESPACE)
- piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
-
- if (result_type == SPLIT_WANT_ALL || !piece.empty())
- result.push_back(PieceToOutputType<Str, OutputStringType>(piece));
- }
- return result;
-}
-
-bool AppendStringKeyValue(StringPiece input,
- char delimiter,
- StringPairs* result) {
- // Always append a new item regardless of success (it might be empty). The
- // below code will copy the strings directly into the result pair.
- result->resize(result->size() + 1);
- auto& result_pair = result->back();
-
- // Find the delimiter.
- size_t end_key_pos = input.find_first_of(delimiter);
- if (end_key_pos == std::string::npos) {
- return false; // No delimiter.
- }
- input.substr(0, end_key_pos).CopyToString(&result_pair.first);
-
- // Find the value string.
- StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos);
- size_t begin_value_pos = remains.find_first_not_of(delimiter);
- if (begin_value_pos == StringPiece::npos) {
- return false; // No value.
- }
- remains.substr(begin_value_pos, remains.size() - begin_value_pos)
- .CopyToString(&result_pair.second);
-
- return true;
-}
-
-template <typename Str, typename OutputStringType>
-void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
- BasicStringPiece<Str> delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type,
- std::vector<OutputStringType>* result) {
- using Piece = BasicStringPiece<Str>;
- using size_type = typename Piece::size_type;
-
- result->clear();
- for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
- begin_index = end_index + delimiter.size()) {
- end_index = input.find(delimiter, begin_index);
- Piece term = end_index == Piece::npos
- ? input.substr(begin_index)
- : input.substr(begin_index, end_index - begin_index);
-
- if (whitespace == TRIM_WHITESPACE)
- term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
-
- if (result_type == SPLIT_WANT_ALL || !term.empty())
- result->push_back(PieceToOutputType<Str, OutputStringType>(term));
- }
-}
-
-} // namespace
-
-std::vector<std::string> SplitString(StringPiece input,
- StringPiece separators,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<std::string, std::string, char>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<std::string, std::string, StringPiece>(
- input, separators, whitespace, result_type);
-}
-
-std::vector<string16> SplitString(StringPiece16 input,
- StringPiece16 separators,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<string16, string16, char16>(input, separators[0],
- whitespace, result_type);
- }
- return SplitStringT<string16, string16, StringPiece16>(
- input, separators, whitespace, result_type);
-}
-
-std::vector<StringPiece> SplitStringPiece(StringPiece input,
- StringPiece separators,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<std::string, StringPiece, char>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<std::string, StringPiece, StringPiece>(
- input, separators, whitespace, result_type);
-}
-
-std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
- StringPiece16 separators,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<string16, StringPiece16, char16>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<string16, StringPiece16, StringPiece16>(
- input, separators, whitespace, result_type);
-}
-
-bool SplitStringIntoKeyValuePairs(StringPiece input,
- char key_value_delimiter,
- char key_value_pair_delimiter,
- StringPairs* key_value_pairs) {
- key_value_pairs->clear();
-
- std::vector<StringPiece> pairs =
- SplitStringPiece(input, std::string(1, key_value_pair_delimiter),
- TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
- key_value_pairs->reserve(pairs.size());
-
- bool success = true;
- for (const StringPiece& pair : pairs) {
- if (!AppendStringKeyValue(pair, key_value_delimiter, key_value_pairs)) {
- // Don't return here, to allow for pairs without associated
- // value or key; just record that the split failed.
- success = false;
- }
- }
- return success;
-}
-
-std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
- StringPiece16 delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- std::vector<string16> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
-}
-
-std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
- StringPiece delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- std::vector<std::string> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
-}
-
-std::vector<StringPiece16> SplitStringPieceUsingSubstr(
- StringPiece16 input,
- StringPiece16 delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- std::vector<StringPiece16> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
-}
-
-std::vector<StringPiece> SplitStringPieceUsingSubstr(
- StringPiece input,
- StringPiece delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type) {
- std::vector<StringPiece> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
-}
-
-} // namespace base
diff --git a/gn/base/strings/string_split.h b/gn/base/strings/string_split.h
deleted file mode 100644
index 6f8fc7ca1a8..00000000000
--- a/gn/base/strings/string_split.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 BASE_STRINGS_STRING_SPLIT_H_
-#define BASE_STRINGS_STRING_SPLIT_H_
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-enum WhitespaceHandling {
- KEEP_WHITESPACE,
- TRIM_WHITESPACE,
-};
-
-enum SplitResult {
- // Strictly return all results.
- //
- // If the input is ",," and the separator is ',' this will return a
- // vector of three empty strings.
- SPLIT_WANT_ALL,
-
- // Only nonempty results will be added to the results. Multiple separators
- // will be coalesced. Separators at the beginning and end of the input will
- // be ignored. With TRIM_WHITESPACE, whitespace-only results will be dropped.
- //
- // If the input is ",," and the separator is ',', this will return an empty
- // vector.
- SPLIT_WANT_NONEMPTY,
-};
-
-// Split the given string on ANY of the given separators, returning copies of
-// the result.
-//
-// To split on either commas or semicolons, keeping all whitespace:
-//
-// std::vector<std::string> tokens = base::SplitString(
-// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-std::vector<std::string> SplitString(StringPiece input,
- StringPiece separators,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-std::vector<string16> SplitString(StringPiece16 input,
- StringPiece16 separators,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-
-// Like SplitString above except it returns a vector of StringPieces which
-// reference the original buffer without copying. Although you have to be
-// careful to keep the original string unmodified, this provides an efficient
-// way to iterate through tokens in a string.
-//
-// To iterate through all whitespace-separated tokens in an input string:
-//
-// for (const auto& cur :
-// base::SplitStringPiece(input, base::kWhitespaceASCII,
-// base::KEEP_WHITESPACE,
-// base::SPLIT_WANT_NONEMPTY)) {
-// ...
-std::vector<StringPiece> SplitStringPiece(StringPiece input,
- StringPiece separators,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
- StringPiece16 separators,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-
-using StringPairs = std::vector<std::pair<std::string, std::string>>;
-
-// Splits |line| into key value pairs according to the given delimiters and
-// removes whitespace leading each key and trailing each value. Returns true
-// only if each pair has a non-empty key and value. |key_value_pairs| will
-// include ("","") pairs for entries without |key_value_delimiter|.
-bool SplitStringIntoKeyValuePairs(StringPiece input,
- char key_value_delimiter,
- char key_value_pair_delimiter,
- StringPairs* key_value_pairs);
-
-// Similar to SplitString, but use a substring delimiter instead of a list of
-// characters that are all possible delimiters.
-std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
- StringPiece16 delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
- StringPiece delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-
-// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
-// which reference the original buffer without copying. Although you have to be
-// careful to keep the original string unmodified, this provides an efficient
-// way to iterate through tokens in a string.
-//
-// To iterate through all newline-separated tokens in an input string:
-//
-// for (const auto& cur :
-// base::SplitStringUsingSubstr(input, "\r\n",
-// base::KEEP_WHITESPACE,
-// base::SPLIT_WANT_NONEMPTY)) {
-// ...
-std::vector<StringPiece16> SplitStringPieceUsingSubstr(
- StringPiece16 input,
- StringPiece16 delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-std::vector<StringPiece> SplitStringPieceUsingSubstr(
- StringPiece input,
- StringPiece delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type);
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_SPLIT_H_
diff --git a/gn/base/strings/string_tokenizer.h b/gn/base/strings/string_tokenizer.h
deleted file mode 100644
index 451a0110523..00000000000
--- a/gn/base/strings/string_tokenizer.h
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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.
-
-#ifndef BASE_STRINGS_STRING_TOKENIZER_H_
-#define BASE_STRINGS_STRING_TOKENIZER_H_
-
-#include <algorithm>
-#include <string>
-
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// StringTokenizerT is a simple string tokenizer class. It works like an
-// iterator that with each step (see the Advance method) updates members that
-// refer to the next token in the input string. The user may optionally
-// configure the tokenizer to return delimiters.
-//
-// EXAMPLE 1:
-//
-// char input[] = "this is a test";
-// CStringTokenizer t(input, input + strlen(input), " ");
-// while (t.GetNext()) {
-// printf("%s\n", t.token().c_str());
-// }
-//
-// Output:
-//
-// this
-// is
-// a
-// test
-//
-//
-// EXAMPLE 2:
-//
-// std::string input = "no-cache=\"foo, bar\", private";
-// StringTokenizer t(input, ", ");
-// t.set_quote_chars("\"");
-// while (t.GetNext()) {
-// printf("%s\n", t.token().c_str());
-// }
-//
-// Output:
-//
-// no-cache="foo, bar"
-// private
-//
-//
-// EXAMPLE 3:
-//
-// bool next_is_option = false, next_is_value = false;
-// std::string input = "text/html; charset=UTF-8; foo=bar";
-// StringTokenizer t(input, "; =");
-// t.set_options(StringTokenizer::RETURN_DELIMS);
-// while (t.GetNext()) {
-// if (t.token_is_delim()) {
-// switch (*t.token_begin()) {
-// case ';':
-// next_is_option = true;
-// break;
-// case '=':
-// next_is_value = true;
-// break;
-// }
-// } else {
-// const char* label;
-// if (next_is_option) {
-// label = "option-name";
-// next_is_option = false;
-// } else if (next_is_value) {
-// label = "option-value";
-// next_is_value = false;
-// } else {
-// label = "mime-type";
-// }
-// printf("%s: %s\n", label, t.token().c_str());
-// }
-// }
-//
-//
-template <class str, class const_iterator>
-class StringTokenizerT {
- public:
- typedef typename str::value_type char_type;
-
- // Options that may be pass to set_options()
- enum {
- // Specifies the delimiters should be returned as tokens
- RETURN_DELIMS = 1 << 0,
- };
-
- // The string object must live longer than the tokenizer. In particular, this
- // should not be constructed with a temporary. The deleted rvalue constructor
- // blocks the most obvious instances of this (e.g. passing a string literal to
- // the constructor), but caution must still be exercised.
- StringTokenizerT(const str& string, const str& delims) {
- Init(string.begin(), string.end(), delims);
- }
-
- // Don't allow temporary strings to be used with string tokenizer, since
- // Init() would otherwise save iterators to a temporary string.
- StringTokenizerT(str&&, const str& delims) = delete;
-
- StringTokenizerT(const_iterator string_begin,
- const_iterator string_end,
- const str& delims) {
- Init(string_begin, string_end, delims);
- }
-
- // Set the options for this tokenizer. By default, this is 0.
- void set_options(int options) { options_ = options; }
-
- // Set the characters to regard as quotes. By default, this is empty. When
- // a quote char is encountered, the tokenizer will switch into a mode where
- // it ignores delimiters that it finds. It switches out of this mode once it
- // finds another instance of the quote char. If a backslash is encountered
- // within a quoted string, then the next character is skipped.
- void set_quote_chars(const str& quotes) { quotes_ = quotes; }
-
- // Call this method to advance the tokenizer to the next delimiter. This
- // returns false if the tokenizer is complete. This method must be called
- // before calling any of the token* methods.
- bool GetNext() {
- if (quotes_.empty() && options_ == 0)
- return QuickGetNext();
- else
- return FullGetNext();
- }
-
- // Start iterating through tokens from the beginning of the string.
- void Reset() { token_end_ = start_pos_; }
-
- // Returns true if token is a delimiter. When the tokenizer is constructed
- // with the RETURN_DELIMS option, this method can be used to check if the
- // returned token is actually a delimiter.
- bool token_is_delim() const { return token_is_delim_; }
-
- // If GetNext() returned true, then these methods may be used to read the
- // value of the token.
- const_iterator token_begin() const { return token_begin_; }
- const_iterator token_end() const { return token_end_; }
- str token() const { return str(token_begin_, token_end_); }
- BasicStringPiece<str> token_piece() const {
- return BasicStringPiece<str>(&*token_begin_,
- std::distance(token_begin_, token_end_));
- }
-
- private:
- void Init(const_iterator string_begin,
- const_iterator string_end,
- const str& delims) {
- start_pos_ = string_begin;
- token_begin_ = string_begin;
- token_end_ = string_begin;
- end_ = string_end;
- delims_ = delims;
- options_ = 0;
- token_is_delim_ = false;
- }
-
- // Implementation of GetNext() for when we have no quote characters. We have
- // two separate implementations because AdvanceOne() is a hot spot in large
- // text files with large tokens.
- bool QuickGetNext() {
- token_is_delim_ = false;
- for (;;) {
- token_begin_ = token_end_;
- if (token_end_ == end_)
- return false;
- ++token_end_;
- if (delims_.find(*token_begin_) == str::npos)
- break;
- // else skip over delimiter.
- }
- while (token_end_ != end_ && delims_.find(*token_end_) == str::npos)
- ++token_end_;
- return true;
- }
-
- // Implementation of GetNext() for when we have to take quotes into account.
- bool FullGetNext() {
- AdvanceState state;
- token_is_delim_ = false;
- for (;;) {
- token_begin_ = token_end_;
- if (token_end_ == end_)
- return false;
- ++token_end_;
- if (AdvanceOne(&state, *token_begin_))
- break;
- if (options_ & RETURN_DELIMS) {
- token_is_delim_ = true;
- return true;
- }
- // else skip over delimiter.
- }
- while (token_end_ != end_ && AdvanceOne(&state, *token_end_))
- ++token_end_;
- return true;
- }
-
- bool IsDelim(char_type c) const { return delims_.find(c) != str::npos; }
-
- bool IsQuote(char_type c) const { return quotes_.find(c) != str::npos; }
-
- struct AdvanceState {
- bool in_quote;
- bool in_escape;
- char_type quote_char;
- AdvanceState() : in_quote(false), in_escape(false), quote_char('\0') {}
- };
-
- // Returns true if a delimiter was not hit.
- bool AdvanceOne(AdvanceState* state, char_type c) {
- if (state->in_quote) {
- if (state->in_escape) {
- state->in_escape = false;
- } else if (c == '\\') {
- state->in_escape = true;
- } else if (c == state->quote_char) {
- state->in_quote = false;
- }
- } else {
- if (IsDelim(c))
- return false;
- state->in_quote = IsQuote(state->quote_char = c);
- }
- return true;
- }
-
- const_iterator start_pos_;
- const_iterator token_begin_;
- const_iterator token_end_;
- const_iterator end_;
- str delims_;
- str quotes_;
- int options_;
- bool token_is_delim_;
-};
-
-typedef StringTokenizerT<std::string, std::string::const_iterator>
- StringTokenizer;
-typedef StringTokenizerT<std::wstring, std::wstring::const_iterator>
- WStringTokenizer;
-typedef StringTokenizerT<std::string, const char*> CStringTokenizer;
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_TOKENIZER_H_
diff --git a/gn/base/strings/string_util.cc b/gn/base/strings/string_util.cc
deleted file mode 100644
index 9b03c265e48..00000000000
--- a/gn/base/strings/string_util.cc
+++ /dev/null
@@ -1,1101 +0,0 @@
-// 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/strings/string_util.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <math.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include <algorithm>
-#include <limits>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/utf_string_conversion_utils.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/third_party/icu/icu_utf.h"
-#include "util/build_config.h"
-
-namespace base {
-
-namespace {
-
-// Used by ReplaceStringPlaceholders to track the position in the string of
-// replaced parameters.
-struct ReplacementOffset {
- ReplacementOffset(uintptr_t parameter, size_t offset)
- : parameter(parameter), offset(offset) {}
-
- // Index of the parameter.
- uintptr_t parameter;
-
- // Starting position in the string.
- size_t offset;
-};
-
-static bool CompareParameter(const ReplacementOffset& elem1,
- const ReplacementOffset& elem2) {
- return elem1.parameter < elem2.parameter;
-}
-
-// Overloaded function to append one string onto the end of another. Having a
-// separate overload for |source| as both string and StringPiece allows for more
-// efficient usage from functions templated to work with either type (avoiding a
-// redundant call to the BasicStringPiece constructor in both cases).
-template <typename string_type>
-inline void AppendToString(string_type* target, const string_type& source) {
- target->append(source);
-}
-
-template <typename string_type>
-inline void AppendToString(string_type* target,
- const BasicStringPiece<string_type>& source) {
- source.AppendToString(target);
-}
-
-// Assuming that a pointer is the size of a "machine word", then
-// uintptr_t is an integer type that is also a machine word.
-typedef uintptr_t MachineWord;
-const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
-
-inline bool IsAlignedToMachineWord(const void* pointer) {
- return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask);
-}
-
-template <typename T>
-inline T* AlignToMachineWord(T* pointer) {
- return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) &
- ~kMachineWordAlignmentMask);
-}
-
-template <size_t size, typename CharacterType>
-struct NonASCIIMask;
-template <>
-struct NonASCIIMask<4, char16> {
- static inline uint32_t value() { return 0xFF80FF80U; }
-};
-template <>
-struct NonASCIIMask<4, char> {
- static inline uint32_t value() { return 0x80808080U; }
-};
-template <>
-struct NonASCIIMask<8, char16> {
- static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
-};
-template <>
-struct NonASCIIMask<8, char> {
- static inline uint64_t value() { return 0x8080808080808080ULL; }
-};
-#if defined(WCHAR_T_IS_UTF32)
-template <>
-struct NonASCIIMask<4, wchar_t> {
- static inline uint32_t value() { return 0xFFFFFF80U; }
-};
-template <>
-struct NonASCIIMask<8, wchar_t> {
- static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; }
-};
-#endif // WCHAR_T_IS_UTF32
-
-} // namespace
-
-bool IsWprintfFormatPortable(const wchar_t* format) {
- for (const wchar_t* position = format; *position != '\0'; ++position) {
- if (*position == '%') {
- bool in_specification = true;
- bool modifier_l = false;
- while (in_specification) {
- // Eat up characters until reaching a known specifier.
- if (*++position == '\0') {
- // The format string ended in the middle of a specification. Call
- // it portable because no unportable specifications were found. The
- // string is equally broken on all platforms.
- return true;
- }
-
- if (*position == 'l') {
- // 'l' is the only thing that can save the 's' and 'c' specifiers.
- modifier_l = true;
- } else if (((*position == 's' || *position == 'c') && !modifier_l) ||
- *position == 'S' || *position == 'C' || *position == 'F' ||
- *position == 'D' || *position == 'O' || *position == 'U') {
- // Not portable.
- return false;
- }
-
- if (wcschr(L"diouxXeEfgGaAcspn%", *position)) {
- // Portable, keep scanning the rest of the format string.
- in_specification = false;
- }
- }
- }
- }
-
- return true;
-}
-
-namespace {
-
-template <typename StringType>
-StringType ToLowerASCIIImpl(BasicStringPiece<StringType> str) {
- StringType ret;
- ret.reserve(str.size());
- for (size_t i = 0; i < str.size(); i++)
- ret.push_back(ToLowerASCII(str[i]));
- return ret;
-}
-
-template <typename StringType>
-StringType ToUpperASCIIImpl(BasicStringPiece<StringType> str) {
- StringType ret;
- ret.reserve(str.size());
- for (size_t i = 0; i < str.size(); i++)
- ret.push_back(ToUpperASCII(str[i]));
- return ret;
-}
-
-} // namespace
-
-std::string ToLowerASCII(StringPiece str) {
- return ToLowerASCIIImpl<std::string>(str);
-}
-
-string16 ToLowerASCII(StringPiece16 str) {
- return ToLowerASCIIImpl<string16>(str);
-}
-
-std::string ToUpperASCII(StringPiece str) {
- return ToUpperASCIIImpl<std::string>(str);
-}
-
-string16 ToUpperASCII(StringPiece16 str) {
- return ToUpperASCIIImpl<string16>(str);
-}
-
-template <class StringType>
-int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
- BasicStringPiece<StringType> b) {
- // Find the first characters that aren't equal and compare them. If the end
- // of one of the strings is found before a nonequal character, the lengths
- // of the strings are compared.
- size_t i = 0;
- while (i < a.length() && i < b.length()) {
- typename StringType::value_type lower_a = ToLowerASCII(a[i]);
- typename StringType::value_type lower_b = ToLowerASCII(b[i]);
- if (lower_a < lower_b)
- return -1;
- if (lower_a > lower_b)
- return 1;
- i++;
- }
-
- // End of one string hit before finding a different character. Expect the
- // common case to be "strings equal" at this point so check that first.
- if (a.length() == b.length())
- return 0;
-
- if (a.length() < b.length())
- return -1;
- return 1;
-}
-
-int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
- return CompareCaseInsensitiveASCIIT<std::string>(a, b);
-}
-
-int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
- return CompareCaseInsensitiveASCIIT<string16>(a, b);
-}
-
-bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
- if (a.length() != b.length())
- return false;
- return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
-}
-
-bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
- if (a.length() != b.length())
- return false;
- return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
-}
-
-template <class StringType>
-bool ReplaceCharsT(const StringType& input,
- BasicStringPiece<StringType> find_any_of_these,
- BasicStringPiece<StringType> replace_with,
- StringType* output);
-
-bool ReplaceChars(const string16& input,
- StringPiece16 replace_chars,
- const string16& replace_with,
- string16* output) {
- return ReplaceCharsT(input, replace_chars, StringPiece16(replace_with),
- output);
-}
-
-bool ReplaceChars(const std::string& input,
- StringPiece replace_chars,
- const std::string& replace_with,
- std::string* output) {
- return ReplaceCharsT(input, replace_chars, StringPiece(replace_with), output);
-}
-
-bool RemoveChars(const string16& input,
- StringPiece16 remove_chars,
- string16* output) {
- return ReplaceCharsT(input, remove_chars, StringPiece16(), output);
-}
-
-bool RemoveChars(const std::string& input,
- StringPiece remove_chars,
- std::string* output) {
- return ReplaceCharsT(input, remove_chars, StringPiece(), output);
-}
-
-template <typename Str>
-TrimPositions TrimStringT(const Str& input,
- BasicStringPiece<Str> trim_chars,
- TrimPositions positions,
- Str* output) {
- // Find the edges of leading/trailing whitespace as desired. Need to use
- // a StringPiece version of input to be able to call find* on it with the
- // StringPiece version of trim_chars (normally the trim_chars will be a
- // constant so avoid making a copy).
- BasicStringPiece<Str> input_piece(input);
- const size_t last_char = input.length() - 1;
- const size_t first_good_char = (positions & TRIM_LEADING)
- ? input_piece.find_first_not_of(trim_chars)
- : 0;
- const size_t last_good_char = (positions & TRIM_TRAILING)
- ? input_piece.find_last_not_of(trim_chars)
- : last_char;
-
- // When the string was all trimmed, report that we stripped off characters
- // from whichever position the caller was interested in. For empty input, we
- // stripped no characters, but we still need to clear |output|.
- if (input.empty() || (first_good_char == Str::npos) ||
- (last_good_char == Str::npos)) {
- bool input_was_empty = input.empty(); // in case output == &input
- output->clear();
- return input_was_empty ? TRIM_NONE : positions;
- }
-
- // Trim.
- *output = input.substr(first_good_char, last_good_char - first_good_char + 1);
-
- // Return where we trimmed from.
- return static_cast<TrimPositions>(
- ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
- ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
-}
-
-bool TrimString(const string16& input,
- StringPiece16 trim_chars,
- string16* output) {
- return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
-}
-
-bool TrimString(const std::string& input,
- StringPiece trim_chars,
- std::string* output) {
- return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
-}
-
-template <typename Str>
-BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
- BasicStringPiece<Str> trim_chars,
- TrimPositions positions) {
- size_t begin =
- (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
- size_t end = (positions & TRIM_TRAILING)
- ? input.find_last_not_of(trim_chars) + 1
- : input.size();
- return input.substr(begin, end - begin);
-}
-
-StringPiece16 TrimString(StringPiece16 input,
- StringPiece16 trim_chars,
- TrimPositions positions) {
- return TrimStringPieceT(input, trim_chars, positions);
-}
-
-StringPiece TrimString(StringPiece input,
- StringPiece trim_chars,
- TrimPositions positions) {
- return TrimStringPieceT(input, trim_chars, positions);
-}
-
-void TruncateUTF8ToByteSize(const std::string& input,
- const size_t byte_size,
- std::string* output) {
- DCHECK(output);
- if (byte_size > input.length()) {
- *output = input;
- return;
- }
- DCHECK_LE(byte_size,
- static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
- // Note: This cast is necessary because CBU8_NEXT uses int32_ts.
- int32_t truncation_length = static_cast<int32_t>(byte_size);
- int32_t char_index = truncation_length - 1;
- const char* data = input.data();
-
- // Using CBU8, we will move backwards from the truncation point
- // to the beginning of the string looking for a valid UTF8
- // character. Once a full UTF8 character is found, we will
- // truncate the string to the end of that character.
- while (char_index >= 0) {
- int32_t prev = char_index;
- base_icu::UChar32 code_point = 0;
- CBU8_NEXT(data, char_index, truncation_length, code_point);
- if (!IsValidCharacter(code_point) || !IsValidCodepoint(code_point)) {
- char_index = prev - 1;
- } else {
- break;
- }
- }
-
- if (char_index >= 0)
- *output = input.substr(0, char_index);
- else
- output->clear();
-}
-
-TrimPositions TrimWhitespace(const string16& input,
- TrimPositions positions,
- string16* output) {
- return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
-}
-
-StringPiece16 TrimWhitespace(StringPiece16 input, TrimPositions positions) {
- return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
-}
-
-TrimPositions TrimWhitespaceASCII(const std::string& input,
- TrimPositions positions,
- std::string* output) {
- return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
-}
-
-StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) {
- return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
-}
-
-template <typename STR>
-STR CollapseWhitespaceT(const STR& text, bool trim_sequences_with_line_breaks) {
- STR result;
- result.resize(text.size());
-
- // Set flags to pretend we're already in a trimmed whitespace sequence, so we
- // will trim any leading whitespace.
- bool in_whitespace = true;
- bool already_trimmed = true;
-
- int chars_written = 0;
- for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
- if (IsUnicodeWhitespace(*i)) {
- if (!in_whitespace) {
- // Reduce all whitespace sequences to a single space.
- in_whitespace = true;
- result[chars_written++] = L' ';
- }
- if (trim_sequences_with_line_breaks && !already_trimmed &&
- ((*i == '\n') || (*i == '\r'))) {
- // Whitespace sequences containing CR or LF are eliminated entirely.
- already_trimmed = true;
- --chars_written;
- }
- } else {
- // Non-whitespace chracters are copied straight across.
- in_whitespace = false;
- already_trimmed = false;
- result[chars_written++] = *i;
- }
- }
-
- if (in_whitespace && !already_trimmed) {
- // Any trailing whitespace is eliminated.
- --chars_written;
- }
-
- result.resize(chars_written);
- return result;
-}
-
-string16 CollapseWhitespace(const string16& text,
- bool trim_sequences_with_line_breaks) {
- return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
-}
-
-std::string CollapseWhitespaceASCII(const std::string& text,
- bool trim_sequences_with_line_breaks) {
- return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
-}
-
-bool ContainsOnlyChars(StringPiece input, StringPiece characters) {
- return input.find_first_not_of(characters) == StringPiece::npos;
-}
-
-bool ContainsOnlyChars(StringPiece16 input, StringPiece16 characters) {
- return input.find_first_not_of(characters) == StringPiece16::npos;
-}
-
-template <class Char>
-inline bool DoIsStringASCII(const Char* characters, size_t length) {
- MachineWord all_char_bits = 0;
- const Char* end = characters + length;
-
- // Prologue: align the input.
- while (!IsAlignedToMachineWord(characters) && characters != end) {
- all_char_bits |= *characters;
- ++characters;
- }
-
- // Compare the values of CPU word size.
- const Char* word_end = AlignToMachineWord(end);
- const size_t loop_increment = sizeof(MachineWord) / sizeof(Char);
- while (characters < word_end) {
- all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
- characters += loop_increment;
- }
-
- // Process the remaining bytes.
- while (characters != end) {
- all_char_bits |= *characters;
- ++characters;
- }
-
- MachineWord non_ascii_bit_mask =
- NonASCIIMask<sizeof(MachineWord), Char>::value();
- return !(all_char_bits & non_ascii_bit_mask);
-}
-
-bool IsStringASCII(StringPiece str) {
- return DoIsStringASCII(str.data(), str.length());
-}
-
-bool IsStringASCII(StringPiece16 str) {
- return DoIsStringASCII(str.data(), str.length());
-}
-
-#if defined(WCHAR_T_IS_UTF32)
-bool IsStringASCII(WStringPiece str) {
- return DoIsStringASCII(str.data(), str.length());
-}
-#endif
-
-bool IsStringUTF8(StringPiece str) {
- const char* src = str.data();
- int32_t src_len = static_cast<int32_t>(str.length());
- int32_t char_index = 0;
-
- while (char_index < src_len) {
- int32_t code_point;
- CBU8_NEXT(src, char_index, src_len, code_point);
- if (!IsValidCharacter(code_point))
- return false;
- }
- return true;
-}
-
-// Implementation note: Normally this function will be called with a hardcoded
-// constant for the lowercase_ascii parameter. Constructing a StringPiece from
-// a C constant requires running strlen, so the result will be two passes
-// through the buffers, one to file the length of lowercase_ascii, and one to
-// compare each letter.
-//
-// This function could have taken a const char* to avoid this and only do one
-// pass through the string. But the strlen is faster than the case-insensitive
-// compares and lets us early-exit in the case that the strings are different
-// lengths (will often be the case for non-matches). So whether one approach or
-// the other will be faster depends on the case.
-//
-// The hardcoded strings are typically very short so it doesn't matter, and the
-// string piece gives additional flexibility for the caller (doesn't have to be
-// null terminated) so we choose the StringPiece route.
-template <typename Str>
-static inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,
- StringPiece lowercase_ascii) {
- if (str.size() != lowercase_ascii.size())
- return false;
- for (size_t i = 0; i < str.size(); i++) {
- if (ToLowerASCII(str[i]) != lowercase_ascii[i])
- return false;
- }
- return true;
-}
-
-bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) {
- return DoLowerCaseEqualsASCII<std::string>(str, lowercase_ascii);
-}
-
-bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowercase_ascii) {
- return DoLowerCaseEqualsASCII<string16>(str, lowercase_ascii);
-}
-
-bool EqualsASCII(StringPiece16 str, StringPiece ascii) {
- if (str.length() != ascii.length())
- return false;
- return std::equal(ascii.begin(), ascii.end(), str.begin());
-}
-
-template <typename Str>
-bool StartsWithT(BasicStringPiece<Str> str,
- BasicStringPiece<Str> search_for,
- CompareCase case_sensitivity) {
- if (search_for.size() > str.size())
- return false;
-
- BasicStringPiece<Str> source = str.substr(0, search_for.size());
-
- switch (case_sensitivity) {
- case CompareCase::SENSITIVE:
- return source == search_for;
-
- case CompareCase::INSENSITIVE_ASCII:
- return std::equal(
- search_for.begin(), search_for.end(), source.begin(),
- CaseInsensitiveCompareASCII<typename Str::value_type>());
-
- default:
- NOTREACHED();
- return false;
- }
-}
-
-bool StartsWith(StringPiece str,
- StringPiece search_for,
- CompareCase case_sensitivity) {
- return StartsWithT<std::string>(str, search_for, case_sensitivity);
-}
-
-bool StartsWith(StringPiece16 str,
- StringPiece16 search_for,
- CompareCase case_sensitivity) {
- return StartsWithT<string16>(str, search_for, case_sensitivity);
-}
-
-template <typename Str>
-bool EndsWithT(BasicStringPiece<Str> str,
- BasicStringPiece<Str> search_for,
- CompareCase case_sensitivity) {
- if (search_for.size() > str.size())
- return false;
-
- BasicStringPiece<Str> source =
- str.substr(str.size() - search_for.size(), search_for.size());
-
- switch (case_sensitivity) {
- case CompareCase::SENSITIVE:
- return source == search_for;
-
- case CompareCase::INSENSITIVE_ASCII:
- return std::equal(
- source.begin(), source.end(), search_for.begin(),
- CaseInsensitiveCompareASCII<typename Str::value_type>());
-
- default:
- NOTREACHED();
- return false;
- }
-}
-
-bool EndsWith(StringPiece str,
- StringPiece search_for,
- CompareCase case_sensitivity) {
- return EndsWithT<std::string>(str, search_for, case_sensitivity);
-}
-
-bool EndsWith(StringPiece16 str,
- StringPiece16 search_for,
- CompareCase case_sensitivity) {
- return EndsWithT<string16>(str, search_for, case_sensitivity);
-}
-
-char HexDigitToInt(wchar_t c) {
- DCHECK(IsHexDigit(c));
- if (c >= '0' && c <= '9')
- return static_cast<char>(c - '0');
- if (c >= 'A' && c <= 'F')
- return static_cast<char>(c - 'A' + 10);
- if (c >= 'a' && c <= 'f')
- return static_cast<char>(c - 'a' + 10);
- return 0;
-}
-
-bool IsUnicodeWhitespace(wchar_t c) {
- // kWhitespaceWide is a NULL-terminated string
- for (const wchar_t* cur = kWhitespaceWide; *cur; ++cur) {
- if (*cur == c)
- return true;
- }
- return false;
-}
-
-static const char* const kByteStringsUnlocalized[] = {" B", " kB", " MB",
- " GB", " TB", " PB"};
-
-string16 FormatBytesUnlocalized(int64_t bytes) {
- double unit_amount = static_cast<double>(bytes);
- size_t dimension = 0;
- const int kKilo = 1024;
- while (unit_amount >= kKilo &&
- dimension < arraysize(kByteStringsUnlocalized) - 1) {
- unit_amount /= kKilo;
- dimension++;
- }
-
- char buf[64];
- if (bytes != 0 && dimension > 0 && unit_amount < 100) {
- base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount,
- kByteStringsUnlocalized[dimension]);
- } else {
- base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount,
- kByteStringsUnlocalized[dimension]);
- }
-
- return ASCIIToUTF16(buf);
-}
-
-// A Matcher for DoReplaceMatchesAfterOffset() that matches substrings.
-template <class StringType>
-struct SubstringMatcher {
- BasicStringPiece<StringType> find_this;
-
- size_t Find(const StringType& input, size_t pos) {
- return input.find(find_this.data(), pos, find_this.length());
- }
- size_t MatchSize() { return find_this.length(); }
-};
-
-// A Matcher for DoReplaceMatchesAfterOffset() that matches single characters.
-template <class StringType>
-struct CharacterMatcher {
- BasicStringPiece<StringType> find_any_of_these;
-
- size_t Find(const StringType& input, size_t pos) {
- return input.find_first_of(find_any_of_these.data(), pos,
- find_any_of_these.length());
- }
- constexpr size_t MatchSize() { return 1; }
-};
-
-enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST };
-
-// Runs in O(n) time in the length of |str|, and transforms the string without
-// reallocating when possible. Returns |true| if any matches were found.
-//
-// This is parameterized on a |Matcher| traits type, so that it can be the
-// implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset().
-template <class StringType, class Matcher>
-bool DoReplaceMatchesAfterOffset(StringType* str,
- size_t initial_offset,
- Matcher matcher,
- BasicStringPiece<StringType> replace_with,
- ReplaceType replace_type) {
- using CharTraits = typename StringType::traits_type;
-
- const size_t find_length = matcher.MatchSize();
- if (!find_length)
- return false;
-
- // If the find string doesn't appear, there's nothing to do.
- size_t first_match = matcher.Find(*str, initial_offset);
- if (first_match == StringType::npos)
- return false;
-
- // If we're only replacing one instance, there's no need to do anything
- // complicated.
- const size_t replace_length = replace_with.length();
- if (replace_type == ReplaceType::REPLACE_FIRST) {
- str->replace(first_match, find_length, replace_with.data(), replace_length);
- return true;
- }
-
- // If the find and replace strings are the same length, we can simply use
- // replace() on each instance, and finish the entire operation in O(n) time.
- if (find_length == replace_length) {
- auto* buffer = &((*str)[0]);
- for (size_t offset = first_match; offset != StringType::npos;
- offset = matcher.Find(*str, offset + replace_length)) {
- CharTraits::copy(buffer + offset, replace_with.data(), replace_length);
- }
- return true;
- }
-
- // Since the find and replace strings aren't the same length, a loop like the
- // one above would be O(n^2) in the worst case, as replace() will shift the
- // entire remaining string each time. We need to be more clever to keep things
- // O(n).
- //
- // When the string is being shortened, it's possible to just shift the matches
- // down in one pass while finding, and truncate the length at the end of the
- // search.
- //
- // If the string is being lengthened, more work is required. The strategy used
- // here is to make two find() passes through the string. The first pass counts
- // the number of matches to determine the new size. The second pass will
- // either construct the new string into a new buffer (if the existing buffer
- // lacked capacity), or else -- if there is room -- create a region of scratch
- // space after |first_match| by shifting the tail of the string to a higher
- // index, and doing in-place moves from the tail to lower indices thereafter.
- size_t str_length = str->length();
- size_t expansion = 0;
- if (replace_length > find_length) {
- // This operation lengthens the string; determine the new length by counting
- // matches.
- const size_t expansion_per_match = (replace_length - find_length);
- size_t num_matches = 0;
- for (size_t match = first_match; match != StringType::npos;
- match = matcher.Find(*str, match + find_length)) {
- expansion += expansion_per_match;
- ++num_matches;
- }
- const size_t final_length = str_length + expansion;
-
- if (str->capacity() < final_length) {
- // If we'd have to allocate a new buffer to grow the string, build the
- // result directly into the new allocation via append().
- StringType src(str->get_allocator());
- str->swap(src);
- str->reserve(final_length);
-
- size_t pos = 0;
- for (size_t match = first_match;; match = matcher.Find(src, pos)) {
- str->append(src, pos, match - pos);
- str->append(replace_with.data(), replace_length);
- pos = match + find_length;
-
- // A mid-loop test/break enables skipping the final Find() call; the
- // number of matches is known, so don't search past the last one.
- if (!--num_matches)
- break;
- }
-
- // Handle substring after the final match.
- str->append(src, pos, str_length - pos);
- return true;
- }
-
- // Prepare for the copy/move loop below -- expand the string to its final
- // size by shifting the data after the first match to the end of the resized
- // string.
- size_t shift_src = first_match + find_length;
- size_t shift_dst = shift_src + expansion;
-
- // Big |expansion| factors (relative to |str_length|) require padding up to
- // |shift_dst|.
- if (shift_dst > str_length)
- str->resize(shift_dst);
-
- str->replace(shift_dst, str_length - shift_src, *str, shift_src,
- str_length - shift_src);
- str_length = final_length;
- }
-
- // We can alternate replacement and move operations. This won't overwrite the
- // unsearched region of the string so long as |write_offset| <= |read_offset|;
- // that condition is always satisfied because:
- //
- // (a) If the string is being shortened, |expansion| is zero and
- // |write_offset| grows slower than |read_offset|.
- //
- // (b) If the string is being lengthened, |write_offset| grows faster than
- // |read_offset|, but |expansion| is big enough so that |write_offset|
- // will only catch up to |read_offset| at the point of the last match.
- auto* buffer = &((*str)[0]);
- size_t write_offset = first_match;
- size_t read_offset = first_match + expansion;
- do {
- if (replace_length) {
- CharTraits::copy(buffer + write_offset, replace_with.data(),
- replace_length);
- write_offset += replace_length;
- }
- read_offset += find_length;
-
- // min() clamps StringType::npos (the largest unsigned value) to str_length.
- size_t match = std::min(matcher.Find(*str, read_offset), str_length);
-
- size_t length = match - read_offset;
- if (length) {
- CharTraits::move(buffer + write_offset, buffer + read_offset, length);
- write_offset += length;
- read_offset += length;
- }
- } while (read_offset < str_length);
-
- // If we're shortening the string, truncate it now.
- str->resize(write_offset);
- return true;
-}
-
-template <class StringType>
-bool ReplaceCharsT(const StringType& input,
- BasicStringPiece<StringType> find_any_of_these,
- BasicStringPiece<StringType> replace_with,
- StringType* output) {
- // Commonly, this is called with output and input being the same string; in
- // that case, this assignment is inexpensive.
- *output = input;
-
- return DoReplaceMatchesAfterOffset(
- output, 0, CharacterMatcher<StringType>{find_any_of_these}, replace_with,
- ReplaceType::REPLACE_ALL);
-}
-
-void ReplaceFirstSubstringAfterOffset(string16* str,
- size_t start_offset,
- StringPiece16 find_this,
- StringPiece16 replace_with) {
- DoReplaceMatchesAfterOffset(str, start_offset,
- SubstringMatcher<string16>{find_this},
- replace_with, ReplaceType::REPLACE_FIRST);
-}
-
-void ReplaceFirstSubstringAfterOffset(std::string* str,
- size_t start_offset,
- StringPiece find_this,
- StringPiece replace_with) {
- DoReplaceMatchesAfterOffset(str, start_offset,
- SubstringMatcher<std::string>{find_this},
- replace_with, ReplaceType::REPLACE_FIRST);
-}
-
-void ReplaceSubstringsAfterOffset(string16* str,
- size_t start_offset,
- StringPiece16 find_this,
- StringPiece16 replace_with) {
- DoReplaceMatchesAfterOffset(str, start_offset,
- SubstringMatcher<string16>{find_this},
- replace_with, ReplaceType::REPLACE_ALL);
-}
-
-void ReplaceSubstringsAfterOffset(std::string* str,
- size_t start_offset,
- StringPiece find_this,
- StringPiece replace_with) {
- DoReplaceMatchesAfterOffset(str, start_offset,
- SubstringMatcher<std::string>{find_this},
- replace_with, ReplaceType::REPLACE_ALL);
-}
-
-template <class string_type>
-inline typename string_type::value_type* WriteIntoT(string_type* str,
- size_t length_with_null) {
- DCHECK_GT(length_with_null, 1u);
- str->reserve(length_with_null);
- str->resize(length_with_null - 1);
- return &((*str)[0]);
-}
-
-char* WriteInto(std::string* str, size_t length_with_null) {
- return WriteIntoT(str, length_with_null);
-}
-
-char16* WriteInto(string16* str, size_t length_with_null) {
- return WriteIntoT(str, length_with_null);
-}
-
-#if defined(_MSC_VER) && !defined(__clang__)
-// Work around VC++ code-gen bug. https://crbug.com/804884
-#pragma optimize("", off)
-#endif
-
-// Generic version for all JoinString overloads. |list_type| must be a sequence
-// (std::vector or std::initializer_list) of strings/StringPieces (std::string,
-// string16, StringPiece or StringPiece16). |string_type| is either std::string
-// or string16.
-template <typename list_type, typename string_type>
-static string_type JoinStringT(const list_type& parts,
- BasicStringPiece<string_type> sep) {
- if (parts.size() == 0)
- return string_type();
-
- // Pre-allocate the eventual size of the string. Start with the size of all of
- // the separators (note that this *assumes* parts.size() > 0).
- size_t total_size = (parts.size() - 1) * sep.size();
- for (const auto& part : parts)
- total_size += part.size();
- string_type result;
- result.reserve(total_size);
-
- auto iter = parts.begin();
- DCHECK(iter != parts.end());
- AppendToString(&result, *iter);
- ++iter;
-
- for (; iter != parts.end(); ++iter) {
- sep.AppendToString(&result);
- // Using the overloaded AppendToString allows this template function to work
- // on both strings and StringPieces without creating an intermediate
- // StringPiece object.
- AppendToString(&result, *iter);
- }
-
- // Sanity-check that we pre-allocated correctly.
- DCHECK_EQ(total_size, result.size());
-
- return result;
-}
-
-std::string JoinString(const std::vector<std::string>& parts,
- StringPiece separator) {
- return JoinStringT(parts, separator);
-}
-
-string16 JoinString(const std::vector<string16>& parts,
- StringPiece16 separator) {
- return JoinStringT(parts, separator);
-}
-
-#if defined(_MSC_VER) && !defined(__clang__)
-// Work around VC++ code-gen bug. https://crbug.com/804884
-#pragma optimize("", on)
-#endif
-
-std::string JoinString(const std::vector<StringPiece>& parts,
- StringPiece separator) {
- return JoinStringT(parts, separator);
-}
-
-string16 JoinString(const std::vector<StringPiece16>& parts,
- StringPiece16 separator) {
- return JoinStringT(parts, separator);
-}
-
-std::string JoinString(std::initializer_list<StringPiece> parts,
- StringPiece separator) {
- return JoinStringT(parts, separator);
-}
-
-string16 JoinString(std::initializer_list<StringPiece16> parts,
- StringPiece16 separator) {
- return JoinStringT(parts, separator);
-}
-
-template <class FormatStringType, class OutStringType>
-OutStringType DoReplaceStringPlaceholders(
- const FormatStringType& format_string,
- const std::vector<OutStringType>& subst,
- std::vector<size_t>* offsets) {
- size_t substitutions = subst.size();
- DCHECK_LT(substitutions, 10U);
-
- size_t sub_length = 0;
- for (const auto& cur : subst)
- sub_length += cur.length();
-
- OutStringType formatted;
- formatted.reserve(format_string.length() + sub_length);
-
- std::vector<ReplacementOffset> r_offsets;
- for (auto i = format_string.begin(); i != format_string.end(); ++i) {
- if ('$' == *i) {
- if (i + 1 != format_string.end()) {
- ++i;
- if ('$' == *i) {
- while (i != format_string.end() && '$' == *i) {
- formatted.push_back('$');
- ++i;
- }
- --i;
- } else {
- if (*i < '1' || *i > '9') {
- DLOG(ERROR) << "Invalid placeholder: $" << *i;
- continue;
- }
- uintptr_t index = *i - '1';
- if (offsets) {
- ReplacementOffset r_offset(index,
- static_cast<int>(formatted.size()));
- r_offsets.insert(
- std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
- &CompareParameter),
- r_offset);
- }
- if (index < substitutions)
- formatted.append(subst.at(index));
- }
- }
- } else {
- formatted.push_back(*i);
- }
- }
- if (offsets) {
- for (const auto& cur : r_offsets)
- offsets->push_back(cur.offset);
- }
- return formatted;
-}
-
-string16 ReplaceStringPlaceholders(const string16& format_string,
- const std::vector<string16>& subst,
- std::vector<size_t>* offsets) {
- return DoReplaceStringPlaceholders(format_string, subst, offsets);
-}
-
-std::string ReplaceStringPlaceholders(StringPiece format_string,
- const std::vector<std::string>& subst,
- std::vector<size_t>* offsets) {
- return DoReplaceStringPlaceholders(format_string, subst, offsets);
-}
-
-string16 ReplaceStringPlaceholders(const string16& format_string,
- const string16& a,
- size_t* offset) {
- std::vector<size_t> offsets;
- std::vector<string16> subst;
- subst.push_back(a);
- string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
-
- DCHECK_EQ(1U, offsets.size());
- if (offset)
- *offset = offsets[0];
- return result;
-}
-
-// The following code is compatible with the OpenBSD lcpy interface. See:
-// http://www.gratisoft.us/todd/papers/strlcpy.html
-// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
-
-namespace {
-
-template <typename CHAR>
-size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
- for (size_t i = 0; i < dst_size; ++i) {
- if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL.
- return i;
- }
-
- // We were left off at dst_size. We over copied 1 byte. Null terminate.
- if (dst_size != 0)
- dst[dst_size - 1] = 0;
-
- // Count the rest of the |src|, and return it's length in characters.
- while (src[dst_size])
- ++dst_size;
- return dst_size;
-}
-
-} // namespace
-
-size_t strlcpy(char* dst, const char* src, size_t dst_size) {
- return lcpyT<char>(dst, src, dst_size);
-}
-size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
- return lcpyT<wchar_t>(dst, src, dst_size);
-}
-
-} // namespace base
diff --git a/gn/base/strings/string_util.h b/gn/base/strings/string_util.h
deleted file mode 100644
index 2d5d01cb727..00000000000
--- a/gn/base/strings/string_util.h
+++ /dev/null
@@ -1,455 +0,0 @@
-// 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 defines utility functions for working with strings.
-
-#ifndef BASE_STRINGS_STRING_UTIL_H_
-#define BASE_STRINGS_STRING_UTIL_H_
-
-#include <ctype.h>
-#include <stdarg.h> // va_list
-#include <stddef.h>
-#include <stdint.h>
-
-#include <initializer_list>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h" // For implicit conversions.
-#include "util/build_config.h"
-
-namespace base {
-
-// C standard-library functions that aren't cross-platform are provided as
-// "base::...", and their prototypes are listed below. These functions are
-// then implemented as inline calls to the platform-specific equivalents in the
-// platform-specific headers.
-
-// Wrapper for vsnprintf that always null-terminates and always returns the
-// number of characters that would be in an untruncated formatted
-// string, even when truncation occurs.
-int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments)
- PRINTF_FORMAT(3, 0);
-
-// Some of these implementations need to be inlined.
-
-// We separate the declaration from the implementation of this inline
-// function just so the PRINTF_FORMAT works.
-inline int snprintf(char* buffer,
- size_t size,
- _Printf_format_string_ const char* format,
- ...) PRINTF_FORMAT(3, 4);
-inline int snprintf(char* buffer,
- size_t size,
- _Printf_format_string_ const char* format,
- ...) {
- va_list arguments;
- va_start(arguments, format);
- int result = vsnprintf(buffer, size, format, arguments);
- va_end(arguments);
- return result;
-}
-
-// BSD-style safe and consistent string copy functions.
-// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
-// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
-// long as |dst_size| is not 0. Returns the length of |src| in characters.
-// If the return value is >= dst_size, then the output was truncated.
-// NOTE: All sizes are in number of characters, NOT in bytes.
-size_t strlcpy(char* dst, const char* src, size_t dst_size);
-size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size);
-
-// Scan a wprintf format string to determine whether it's portable across a
-// variety of systems. This function only checks that the conversion
-// specifiers used by the format string are supported and have the same meaning
-// on a variety of systems. It doesn't check for other errors that might occur
-// within a format string.
-//
-// Nonportable conversion specifiers for wprintf are:
-// - 's' and 'c' without an 'l' length modifier. %s and %c operate on char
-// data on all systems except Windows, which treat them as wchar_t data.
-// Use %ls and %lc for wchar_t data instead.
-// - 'S' and 'C', which operate on wchar_t data on all systems except Windows,
-// which treat them as char data. Use %ls and %lc for wchar_t data
-// instead.
-// - 'F', which is not identified by Windows wprintf documentation.
-// - 'D', 'O', and 'U', which are deprecated and not available on all systems.
-// Use %ld, %lo, and %lu instead.
-//
-// Note that there is no portable conversion specifier for char data when
-// working with wprintf.
-//
-// This function is intended to be called from base::vswprintf.
-bool IsWprintfFormatPortable(const wchar_t* format);
-
-// ASCII-specific tolower. The standard library's tolower is locale sensitive,
-// so we don't want to use it here.
-inline char ToLowerASCII(char c) {
- return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
-}
-inline char16 ToLowerASCII(char16 c) {
- return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
-}
-
-// ASCII-specific toupper. The standard library's toupper is locale sensitive,
-// so we don't want to use it here.
-inline char ToUpperASCII(char c) {
- return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
-}
-inline char16 ToUpperASCII(char16 c) {
- return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
-}
-
-// Converts the given string to it's ASCII-lowercase equivalent.
-std::string ToLowerASCII(StringPiece str);
-string16 ToLowerASCII(StringPiece16 str);
-
-// Converts the given string to it's ASCII-uppercase equivalent.
-std::string ToUpperASCII(StringPiece str);
-string16 ToUpperASCII(StringPiece16 str);
-
-// Functor for case-insensitive ASCII comparisons for STL algorithms like
-// std::search.
-//
-// Note that a full Unicode version of this functor is not possible to write
-// because case mappings might change the number of characters, depend on
-// context (combining accents), and require handling UTF-16. If you need
-// proper Unicode support, use base::i18n::ToLower/FoldCase and then just
-// use a normal operator== on the result.
-template <typename Char>
-struct CaseInsensitiveCompareASCII {
- public:
- bool operator()(Char x, Char y) const {
- return ToLowerASCII(x) == ToLowerASCII(y);
- }
-};
-
-// Like strcasecmp for case-insensitive ASCII characters only. Returns:
-// -1 (a < b)
-// 0 (a == b)
-// 1 (a > b)
-// (unlike strcasecmp which can return values greater or less than 1/-1). For
-// full Unicode support, use base::i18n::ToLower or base::i18h::FoldCase
-// and then just call the normal string operators on the result.
-int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b);
-int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b);
-
-// Equality for ASCII case-insensitive comparisons. For full Unicode support,
-// use base::i18n::ToLower or base::i18h::FoldCase and then compare with either
-// == or !=.
-bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b);
-bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b);
-
-// Contains the set of characters representing whitespace in the corresponding
-// encoding. Null-terminated. The ASCII versions are the whitespaces as defined
-// by HTML5, and don't include control characters.
-extern const wchar_t kWhitespaceWide[]; // Includes Unicode.
-extern const char16 kWhitespaceUTF16[]; // Includes Unicode.
-extern const char kWhitespaceASCII[];
-extern const char16 kWhitespaceASCIIAs16[]; // No unicode.
-
-// Null-terminated string representing the UTF-8 byte order mark.
-extern const char kUtf8ByteOrderMark[];
-
-// Removes characters in |remove_chars| from anywhere in |input|. Returns true
-// if any characters were removed. |remove_chars| must be null-terminated.
-// NOTE: Safe to use the same variable for both |input| and |output|.
-bool RemoveChars(const string16& input,
- StringPiece16 remove_chars,
- string16* output);
-bool RemoveChars(const std::string& input,
- StringPiece remove_chars,
- std::string* output);
-
-// Replaces characters in |replace_chars| from anywhere in |input| with
-// |replace_with|. Each character in |replace_chars| will be replaced with
-// the |replace_with| string. Returns true if any characters were replaced.
-// |replace_chars| must be null-terminated.
-// NOTE: Safe to use the same variable for both |input| and |output|.
-bool ReplaceChars(const string16& input,
- StringPiece16 replace_chars,
- const string16& replace_with,
- string16* output);
-bool ReplaceChars(const std::string& input,
- StringPiece replace_chars,
- const std::string& replace_with,
- std::string* output);
-
-enum TrimPositions {
- TRIM_NONE = 0,
- TRIM_LEADING = 1 << 0,
- TRIM_TRAILING = 1 << 1,
- TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
-};
-
-// Removes characters in |trim_chars| from the beginning and end of |input|.
-// The 8-bit version only works on 8-bit characters, not UTF-8. Returns true if
-// any characters were removed.
-//
-// It is safe to use the same variable for both |input| and |output| (this is
-// the normal usage to trim in-place).
-bool TrimString(const string16& input,
- StringPiece16 trim_chars,
- string16* output);
-bool TrimString(const std::string& input,
- StringPiece trim_chars,
- std::string* output);
-
-// StringPiece versions of the above. The returned pieces refer to the original
-// buffer.
-StringPiece16 TrimString(StringPiece16 input,
- StringPiece16 trim_chars,
- TrimPositions positions);
-StringPiece TrimString(StringPiece input,
- StringPiece trim_chars,
- TrimPositions positions);
-
-// Truncates a string to the nearest UTF-8 character that will leave
-// the string less than or equal to the specified byte size.
-void TruncateUTF8ToByteSize(const std::string& input,
- const size_t byte_size,
- std::string* output);
-
-// Trims any whitespace from either end of the input string.
-//
-// The StringPiece versions return a substring referencing the input buffer.
-// The ASCII versions look only for ASCII whitespace.
-//
-// The std::string versions return where whitespace was found.
-// NOTE: Safe to use the same variable for both input and output.
-TrimPositions TrimWhitespace(const string16& input,
- TrimPositions positions,
- string16* output);
-StringPiece16 TrimWhitespace(StringPiece16 input, TrimPositions positions);
-TrimPositions TrimWhitespaceASCII(const std::string& input,
- TrimPositions positions,
- std::string* output);
-StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions);
-
-// Searches for CR or LF characters. Removes all contiguous whitespace
-// strings that contain them. This is useful when trying to deal with text
-// copied from terminals.
-// Returns |text|, with the following three transformations:
-// (1) Leading and trailing whitespace is trimmed.
-// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
-// sequences containing a CR or LF are trimmed.
-// (3) All other whitespace sequences are converted to single spaces.
-string16 CollapseWhitespace(const string16& text,
- bool trim_sequences_with_line_breaks);
-std::string CollapseWhitespaceASCII(const std::string& text,
- bool trim_sequences_with_line_breaks);
-
-// Returns true if |input| is empty or contains only characters found in
-// |characters|.
-bool ContainsOnlyChars(StringPiece input, StringPiece characters);
-bool ContainsOnlyChars(StringPiece16 input, StringPiece16 characters);
-
-// Returns true if the specified string matches the criteria. How can a wide
-// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
-// first case) or characters that use only 8-bits and whose 8-bit
-// representation looks like a UTF-8 string (the second case).
-//
-// Note that IsStringUTF8 checks not only if the input is structurally
-// valid but also if it doesn't contain any non-character codepoint
-// (e.g. U+FFFE). It's done on purpose because all the existing callers want
-// to have the maximum 'discriminating' power from other encodings. If
-// there's a use case for just checking the structural validity, we have to
-// add a new function for that.
-//
-// IsStringASCII assumes the input is likely all ASCII, and does not leave early
-// if it is not the case.
-bool IsStringUTF8(StringPiece str);
-bool IsStringASCII(StringPiece str);
-bool IsStringASCII(StringPiece16 str);
-#if defined(WCHAR_T_IS_UTF32)
-bool IsStringASCII(WStringPiece str);
-#endif
-
-// Compare the lower-case form of the given string against the given
-// previously-lower-cased ASCII string (typically a constant).
-bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowecase_ascii);
-bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowecase_ascii);
-
-// Performs a case-sensitive string compare of the given 16-bit string against
-// the given 8-bit ASCII string (typically a constant). The behavior is
-// undefined if the |ascii| string is not ASCII.
-bool EqualsASCII(StringPiece16 str, StringPiece ascii);
-
-// Indicates case sensitivity of comparisons. Only ASCII case insensitivity
-// is supported. Full Unicode case-insensitive conversions would need to go in
-// base/i18n so it can use ICU.
-//
-// If you need to do Unicode-aware case-insensitive StartsWith/EndsWith, it's
-// best to call base::i18n::ToLower() or base::i18n::FoldCase() (see
-// base/i18n/case_conversion.h for usage advice) on the arguments, and then use
-// the results to a case-sensitive comparison.
-enum class CompareCase {
- SENSITIVE,
- INSENSITIVE_ASCII,
-};
-
-bool StartsWith(StringPiece str,
- StringPiece search_for,
- CompareCase case_sensitivity);
-bool StartsWith(StringPiece16 str,
- StringPiece16 search_for,
- CompareCase case_sensitivity);
-bool EndsWith(StringPiece str,
- StringPiece search_for,
- CompareCase case_sensitivity);
-bool EndsWith(StringPiece16 str,
- StringPiece16 search_for,
- CompareCase case_sensitivity);
-
-// Determines the type of ASCII character, independent of locale (the C
-// library versions will change based on locale).
-template <typename Char>
-inline bool IsAsciiWhitespace(Char c) {
- return c == ' ' || c == '\r' || c == '\n' || c == '\t';
-}
-template <typename Char>
-inline bool IsAsciiAlpha(Char c) {
- return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
-}
-template <typename Char>
-inline bool IsAsciiUpper(Char c) {
- return c >= 'A' && c <= 'Z';
-}
-template <typename Char>
-inline bool IsAsciiLower(Char c) {
- return c >= 'a' && c <= 'z';
-}
-template <typename Char>
-inline bool IsAsciiDigit(Char c) {
- return c >= '0' && c <= '9';
-}
-
-template <typename Char>
-inline bool IsHexDigit(Char c) {
- return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
- (c >= 'a' && c <= 'f');
-}
-
-// Returns the integer corresponding to the given hex character. For example:
-// '4' -> 4
-// 'a' -> 10
-// 'B' -> 11
-// Assumes the input is a valid hex character. DCHECKs in debug builds if not.
-char HexDigitToInt(wchar_t c);
-
-// Returns true if it's a Unicode whitespace character.
-bool IsUnicodeWhitespace(wchar_t c);
-
-// Return a byte string in human-readable format with a unit suffix. Not
-// appropriate for use in any UI; use of FormatBytes and friends in ui/base is
-// highly recommended instead. TODO(avi): Figure out how to get callers to use
-// FormatBytes instead; remove this.
-string16 FormatBytesUnlocalized(int64_t bytes);
-
-// Starting at |start_offset| (usually 0), replace the first instance of
-// |find_this| with |replace_with|.
-void ReplaceFirstSubstringAfterOffset(base::string16* str,
- size_t start_offset,
- StringPiece16 find_this,
- StringPiece16 replace_with);
-void ReplaceFirstSubstringAfterOffset(std::string* str,
- size_t start_offset,
- StringPiece find_this,
- StringPiece replace_with);
-
-// Starting at |start_offset| (usually 0), look through |str| and replace all
-// instances of |find_this| with |replace_with|.
-//
-// This does entire substrings; use std::replace in <algorithm> for single
-// characters, for example:
-// std::replace(str.begin(), str.end(), 'a', 'b');
-void ReplaceSubstringsAfterOffset(string16* str,
- size_t start_offset,
- StringPiece16 find_this,
- StringPiece16 replace_with);
-void ReplaceSubstringsAfterOffset(std::string* str,
- size_t start_offset,
- StringPiece find_this,
- StringPiece replace_with);
-
-// Reserves enough memory in |str| to accommodate |length_with_null| characters,
-// sets the size of |str| to |length_with_null - 1| characters, and returns a
-// pointer to the underlying contiguous array of characters. This is typically
-// used when calling a function that writes results into a character array, but
-// the caller wants the data to be managed by a string-like object. It is
-// convenient in that is can be used inline in the call, and fast in that it
-// avoids copying the results of the call from a char* into a string.
-//
-// |length_with_null| must be at least 2, since otherwise the underlying string
-// would have size 0, and trying to access &((*str)[0]) in that case can result
-// in a number of problems.
-//
-// Internally, this takes linear time because the resize() call 0-fills the
-// underlying array for potentially all
-// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
-// could avoid this aspect of the resize() call, as we expect the caller to
-// immediately write over this memory, but there is no other way to set the size
-// of the string, and not doing that will mean people who access |str| rather
-// than str.c_str() will get back a string of whatever size |str| had on entry
-// to this function (probably 0).
-char* WriteInto(std::string* str, size_t length_with_null);
-char16* WriteInto(string16* str, size_t length_with_null);
-
-// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list
-// of strings into a single string, inserting |separator| (which may be empty)
-// in between all elements.
-//
-// If possible, callers should build a vector of StringPieces and use the
-// StringPiece variant, so that they do not create unnecessary copies of
-// strings. For example, instead of using SplitString, modifying the vector,
-// then using JoinString, use SplitStringPiece followed by JoinString so that no
-// copies of those strings are created until the final join operation.
-//
-// Use StrCat (in base/strings/strcat.h) if you don't need a separator.
-std::string JoinString(const std::vector<std::string>& parts,
- StringPiece separator);
-string16 JoinString(const std::vector<string16>& parts,
- StringPiece16 separator);
-std::string JoinString(const std::vector<StringPiece>& parts,
- StringPiece separator);
-string16 JoinString(const std::vector<StringPiece16>& parts,
- StringPiece16 separator);
-// Explicit initializer_list overloads are required to break ambiguity when used
-// with a literal initializer list (otherwise the compiler would not be able to
-// decide between the string and StringPiece overloads).
-std::string JoinString(std::initializer_list<StringPiece> parts,
- StringPiece separator);
-string16 JoinString(std::initializer_list<StringPiece16> parts,
- StringPiece16 separator);
-
-// Replace $1-$2-$3..$9 in the format string with values from |subst|.
-// Additionally, any number of consecutive '$' characters is replaced by that
-// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
-// NULL. This only allows you to use up to nine replacements.
-string16 ReplaceStringPlaceholders(const string16& format_string,
- const std::vector<string16>& subst,
- std::vector<size_t>* offsets);
-
-std::string ReplaceStringPlaceholders(StringPiece format_string,
- const std::vector<std::string>& subst,
- std::vector<size_t>* offsets);
-
-// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
-string16 ReplaceStringPlaceholders(const string16& format_string,
- const string16& a,
- size_t* offset);
-
-} // namespace base
-
-#if defined(OS_WIN)
-#include "base/strings/string_util_win.h"
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include "base/strings/string_util_posix.h"
-#else
-#error Define string operations appropriately for your platform
-#endif
-
-#endif // BASE_STRINGS_STRING_UTIL_H_
diff --git a/gn/base/strings/string_util_constants.cc b/gn/base/strings/string_util_constants.cc
deleted file mode 100644
index 37ff95f6472..00000000000
--- a/gn/base/strings/string_util_constants.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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/strings/string_util.h"
-
-namespace base {
-
-#define WHITESPACE_UNICODE \
- 0x0009, /* CHARACTER TABULATION */ \
- 0x000A, /* LINE FEED (LF) */ \
- 0x000B, /* LINE TABULATION */ \
- 0x000C, /* FORM FEED (FF) */ \
- 0x000D, /* CARRIAGE RETURN (CR) */ \
- 0x0020, /* SPACE */ \
- 0x0085, /* NEXT LINE (NEL) */ \
- 0x00A0, /* NO-BREAK SPACE */ \
- 0x1680, /* OGHAM SPACE MARK */ \
- 0x2000, /* EN QUAD */ \
- 0x2001, /* EM QUAD */ \
- 0x2002, /* EN SPACE */ \
- 0x2003, /* EM SPACE */ \
- 0x2004, /* THREE-PER-EM SPACE */ \
- 0x2005, /* FOUR-PER-EM SPACE */ \
- 0x2006, /* SIX-PER-EM SPACE */ \
- 0x2007, /* FIGURE SPACE */ \
- 0x2008, /* PUNCTUATION SPACE */ \
- 0x2009, /* THIN SPACE */ \
- 0x200A, /* HAIR SPACE */ \
- 0x2028, /* LINE SEPARATOR */ \
- 0x2029, /* PARAGRAPH SEPARATOR */ \
- 0x202F, /* NARROW NO-BREAK SPACE */ \
- 0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
- 0x3000, /* IDEOGRAPHIC SPACE */ \
- 0
-
-const wchar_t kWhitespaceWide[] = {WHITESPACE_UNICODE};
-
-const char16 kWhitespaceUTF16[] = {WHITESPACE_UNICODE};
-
-const char kWhitespaceASCII[] = {0x09, // CHARACTER TABULATION
- 0x0A, // LINE FEED (LF)
- 0x0B, // LINE TABULATION
- 0x0C, // FORM FEED (FF)
- 0x0D, // CARRIAGE RETURN (CR)
- 0x20, // SPACE
- 0};
-
-const char16 kWhitespaceASCIIAs16[] = {0x09, // CHARACTER TABULATION
- 0x0A, // LINE FEED (LF)
- 0x0B, // LINE TABULATION
- 0x0C, // FORM FEED (FF)
- 0x0D, // CARRIAGE RETURN (CR)
- 0x20, // SPACE
- 0};
-
-const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
-
-} // namespace base
diff --git a/gn/base/strings/string_util_posix.h b/gn/base/strings/string_util_posix.h
deleted file mode 100644
index 6868a658a08..00000000000
--- a/gn/base/strings/string_util_posix.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 BASE_STRINGS_STRING_UTIL_POSIX_H_
-#define BASE_STRINGS_STRING_UTIL_POSIX_H_
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <wchar.h>
-
-#include "base/logging.h"
-
-namespace base {
-
-// Chromium code style is to not use malloc'd strings; this is only for use
-// for interaction with APIs that require it.
-inline char* strdup(const char* str) {
- return ::strdup(str);
-}
-
-inline int vsnprintf(char* buffer,
- size_t size,
- const char* format,
- va_list arguments) {
- return ::vsnprintf(buffer, size, format, arguments);
-}
-
-inline int vswprintf(wchar_t* buffer,
- size_t size,
- const wchar_t* format,
- va_list arguments) {
- DCHECK(IsWprintfFormatPortable(format));
- return ::vswprintf(buffer, size, format, arguments);
-}
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_UTIL_POSIX_H_
diff --git a/gn/base/strings/string_util_win.h b/gn/base/strings/string_util_win.h
deleted file mode 100644
index 8f9fa81631a..00000000000
--- a/gn/base/strings/string_util_win.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 BASE_STRINGS_STRING_UTIL_WIN_H_
-#define BASE_STRINGS_STRING_UTIL_WIN_H_
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <wchar.h>
-
-#include "base/logging.h"
-
-namespace base {
-
-// Chromium code style is to not use malloc'd strings; this is only for use
-// for interaction with APIs that require it.
-inline char* strdup(const char* str) {
- return _strdup(str);
-}
-
-inline int vsnprintf(char* buffer,
- size_t size,
- const char* format,
- va_list arguments) {
- int length = vsnprintf_s(buffer, size, size - 1, format, arguments);
- if (length < 0)
- return _vscprintf(format, arguments);
- return length;
-}
-
-inline int vswprintf(wchar_t* buffer,
- size_t size,
- const wchar_t* format,
- va_list arguments) {
- DCHECK(IsWprintfFormatPortable(format));
-
- int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments);
- if (length < 0)
- return _vscwprintf(format, arguments);
- return length;
-}
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRING_UTIL_WIN_H_
diff --git a/gn/base/strings/stringprintf.cc b/gn/base/strings/stringprintf.cc
deleted file mode 100644
index c7d141b4366..00000000000
--- a/gn/base/strings/stringprintf.cc
+++ /dev/null
@@ -1,190 +0,0 @@
-// 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/strings/stringprintf.h"
-
-#include <errno.h>
-#include <stddef.h>
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/scoped_clear_errno.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/build_config.h"
-
-namespace base {
-
-namespace {
-
-// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
-// is the size of the buffer. These return the number of characters in the
-// formatted string excluding the NUL terminator. If the buffer is not
-// large enough to accommodate the formatted string without truncation, they
-// return the number of characters that would be in the fully-formatted string
-// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms).
-inline int vsnprintfT(char* buffer,
- size_t buf_size,
- const char* format,
- va_list argptr) {
- return base::vsnprintf(buffer, buf_size, format, argptr);
-}
-
-#if defined(OS_WIN)
-inline int vsnprintfT(wchar_t* buffer,
- size_t buf_size,
- const wchar_t* format,
- va_list argptr) {
- return base::vswprintf(buffer, buf_size, format, argptr);
-}
-#endif
-
-// Templatized backend for StringPrintF/StringAppendF. This does not finalize
-// the va_list, the caller is expected to do that.
-template <class StringType>
-static void StringAppendVT(StringType* dst,
- const typename StringType::value_type* format,
- va_list ap) {
- // First try with a small fixed size buffer.
- // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
- // and StringUtilTest.StringPrintfBounds.
- typename StringType::value_type stack_buf[1024];
-
- va_list ap_copy;
- va_copy(ap_copy, ap);
-
-#if !defined(OS_WIN)
- ScopedClearErrno clear_errno;
-#endif
- int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, ap_copy);
- va_end(ap_copy);
-
- if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) {
- // It fit.
- dst->append(stack_buf, result);
- return;
- }
-
- // Repeatedly increase buffer size until it fits.
- int mem_length = arraysize(stack_buf);
- while (true) {
- if (result < 0) {
-#if defined(OS_WIN)
- // On Windows, vsnprintfT always returns the number of characters in a
- // fully-formatted string, so if we reach this point, something else is
- // wrong and no amount of buffer-doubling is going to fix it.
- return;
-#else
- if (errno != 0 && errno != EOVERFLOW)
- return;
- // Try doubling the buffer size.
- mem_length *= 2;
-#endif
- } else {
- // We need exactly "result + 1" characters.
- mem_length = result + 1;
- }
-
- if (mem_length > 32 * 1024 * 1024) {
- // That should be plenty, don't try anything larger. This protects
- // against huge allocations when using vsnprintfT implementations that
- // return -1 for reasons other than overflow without setting errno.
- DLOG(WARNING) << "Unable to printf the requested string due to size.";
- return;
- }
-
- std::vector<typename StringType::value_type> mem_buf(mem_length);
-
- // NOTE: You can only use a va_list once. Since we're in a while loop, we
- // need to make a new copy each time so we don't use up the original.
- va_copy(ap_copy, ap);
- result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
- va_end(ap_copy);
-
- if ((result >= 0) && (result < mem_length)) {
- // It fit.
- dst->append(&mem_buf[0], result);
- return;
- }
- }
-}
-
-} // namespace
-
-std::string StringPrintf(const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- std::string result;
- StringAppendV(&result, format, ap);
- va_end(ap);
- return result;
-}
-
-#if defined(OS_WIN)
-std::wstring StringPrintf(const wchar_t* format, ...) {
- va_list ap;
- va_start(ap, format);
- std::wstring result;
- StringAppendV(&result, format, ap);
- va_end(ap);
- return result;
-}
-#endif
-
-std::string StringPrintV(const char* format, va_list ap) {
- std::string result;
- StringAppendV(&result, format, ap);
- return result;
-}
-
-const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- dst->clear();
- StringAppendV(dst, format, ap);
- va_end(ap);
- return *dst;
-}
-
-#if defined(OS_WIN)
-const std::wstring& SStringPrintf(std::wstring* dst,
- const wchar_t* format,
- ...) {
- va_list ap;
- va_start(ap, format);
- dst->clear();
- StringAppendV(dst, format, ap);
- va_end(ap);
- return *dst;
-}
-#endif
-
-void StringAppendF(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-#if defined(OS_WIN)
-void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-#endif
-
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
- StringAppendVT(dst, format, ap);
-}
-
-#if defined(OS_WIN)
-void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
- StringAppendVT(dst, format, ap);
-}
-#endif
-
-} // namespace base
diff --git a/gn/base/strings/stringprintf.h b/gn/base/strings/stringprintf.h
deleted file mode 100644
index 8c12acadb81..00000000000
--- a/gn/base/strings/stringprintf.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 BASE_STRINGS_STRINGPRINTF_H_
-#define BASE_STRINGS_STRINGPRINTF_H_
-
-#include <stdarg.h> // va_list
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "util/build_config.h"
-
-namespace base {
-
-// Return a C++ string given printf-like input.
-std::string StringPrintf(_Printf_format_string_ const char* format, ...)
- PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
-#if defined(OS_WIN)
-std::wstring StringPrintf(_Printf_format_string_ const wchar_t* format, ...)
- WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
-#endif
-
-// Return a C++ string given vprintf-like input.
-std::string StringPrintV(const char* format, va_list ap)
- PRINTF_FORMAT(1, 0) WARN_UNUSED_RESULT;
-
-// Store result into a supplied string and return it.
-const std::string& SStringPrintf(std::string* dst,
- _Printf_format_string_ const char* format,
- ...) PRINTF_FORMAT(2, 3);
-#if defined(OS_WIN)
-const std::wstring& SStringPrintf(std::wstring* dst,
- _Printf_format_string_ const wchar_t* format,
- ...) WPRINTF_FORMAT(2, 3);
-#endif
-
-// Append result to a supplied string.
-void StringAppendF(std::string* dst,
- _Printf_format_string_ const char* format,
- ...) PRINTF_FORMAT(2, 3);
-#if defined(OS_WIN)
-void StringAppendF(std::wstring* dst,
- _Printf_format_string_ const wchar_t* format,
- ...) WPRINTF_FORMAT(2, 3);
-#endif
-
-// Lower-level routine that takes a va_list and appends to a specified
-// string. All other routines are just convenience wrappers around it.
-void StringAppendV(std::string* dst, const char* format, va_list ap)
- PRINTF_FORMAT(2, 0);
-#if defined(OS_WIN)
-void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap)
- WPRINTF_FORMAT(2, 0);
-#endif
-
-} // namespace base
-
-#endif // BASE_STRINGS_STRINGPRINTF_H_
diff --git a/gn/base/strings/utf_offset_string_conversions.cc b/gn/base/strings/utf_offset_string_conversions.cc
deleted file mode 100644
index 177839a8e71..00000000000
--- a/gn/base/strings/utf_offset_string_conversions.cc
+++ /dev/null
@@ -1,266 +0,0 @@
-// 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.
-
-#include "base/strings/utf_offset_string_conversions.h"
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <memory>
-
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversion_utils.h"
-
-namespace base {
-
-OffsetAdjuster::Adjustment::Adjustment(size_t original_offset,
- size_t original_length,
- size_t output_length)
- : original_offset(original_offset),
- original_length(original_length),
- output_length(output_length) {}
-
-// static
-void OffsetAdjuster::AdjustOffsets(const Adjustments& adjustments,
- std::vector<size_t>* offsets_for_adjustment,
- size_t limit) {
- DCHECK(offsets_for_adjustment);
- for (std::vector<size_t>::iterator i(offsets_for_adjustment->begin());
- i != offsets_for_adjustment->end(); ++i)
- AdjustOffset(adjustments, &(*i), limit);
-}
-
-// static
-void OffsetAdjuster::AdjustOffset(const Adjustments& adjustments,
- size_t* offset,
- size_t limit) {
- DCHECK(offset);
- if (*offset == string16::npos)
- return;
- int adjustment = 0;
- for (Adjustments::const_iterator i = adjustments.begin();
- i != adjustments.end(); ++i) {
- if (*offset <= i->original_offset)
- break;
- if (*offset < (i->original_offset + i->original_length)) {
- *offset = string16::npos;
- return;
- }
- adjustment += static_cast<int>(i->original_length - i->output_length);
- }
- *offset -= adjustment;
-
- if (*offset > limit)
- *offset = string16::npos;
-}
-
-// static
-void OffsetAdjuster::UnadjustOffsets(
- const Adjustments& adjustments,
- std::vector<size_t>* offsets_for_unadjustment) {
- if (!offsets_for_unadjustment || adjustments.empty())
- return;
- for (std::vector<size_t>::iterator i(offsets_for_unadjustment->begin());
- i != offsets_for_unadjustment->end(); ++i)
- UnadjustOffset(adjustments, &(*i));
-}
-
-// static
-void OffsetAdjuster::UnadjustOffset(const Adjustments& adjustments,
- size_t* offset) {
- if (*offset == string16::npos)
- return;
- int adjustment = 0;
- for (Adjustments::const_iterator i = adjustments.begin();
- i != adjustments.end(); ++i) {
- if (*offset + adjustment <= i->original_offset)
- break;
- adjustment += static_cast<int>(i->original_length - i->output_length);
- if ((*offset + adjustment) < (i->original_offset + i->original_length)) {
- *offset = string16::npos;
- return;
- }
- }
- *offset += adjustment;
-}
-
-// static
-void OffsetAdjuster::MergeSequentialAdjustments(
- const Adjustments& first_adjustments,
- Adjustments* adjustments_on_adjusted_string) {
- Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin();
- Adjustments::const_iterator first_iter = first_adjustments.begin();
- // Simultaneously iterate over all |adjustments_on_adjusted_string| and
- // |first_adjustments|, adding adjustments to or correcting the adjustments
- // in |adjustments_on_adjusted_string| as we go. |shift| keeps track of the
- // current number of characters collapsed by |first_adjustments| up to this
- // point. |currently_collapsing| keeps track of the number of characters
- // collapsed by |first_adjustments| into the current |adjusted_iter|'s
- // length. These are characters that will change |shift| as soon as we're
- // done processing the current |adjusted_iter|; they are not yet reflected in
- // |shift|.
- size_t shift = 0;
- size_t currently_collapsing = 0;
- while (adjusted_iter != adjustments_on_adjusted_string->end()) {
- if ((first_iter == first_adjustments.end()) ||
- ((adjusted_iter->original_offset + shift +
- adjusted_iter->original_length) <= first_iter->original_offset)) {
- // Entire |adjusted_iter| (accounting for its shift and including its
- // whole original length) comes before |first_iter|.
- //
- // Correct the offset at |adjusted_iter| and move onto the next
- // adjustment that needs revising.
- adjusted_iter->original_offset += shift;
- shift += currently_collapsing;
- currently_collapsing = 0;
- ++adjusted_iter;
- } else if ((adjusted_iter->original_offset + shift) >
- first_iter->original_offset) {
- // |first_iter| comes before the |adjusted_iter| (as adjusted by |shift|).
-
- // It's not possible for the adjustments to overlap. (It shouldn't
- // be possible that we have an |adjusted_iter->original_offset| that,
- // when adjusted by the computed |shift|, is in the middle of
- // |first_iter|'s output's length. After all, that would mean the
- // current adjustment_on_adjusted_string somehow points to an offset
- // that was supposed to have been eliminated by the first set of
- // adjustments.)
- DCHECK_LE(first_iter->original_offset + first_iter->output_length,
- adjusted_iter->original_offset + shift);
-
- // Add the |first_adjustment_iter| to the full set of adjustments while
- // making sure |adjusted_iter| continues pointing to the same element.
- // We do this by inserting the |first_adjustment_iter| right before
- // |adjusted_iter|, then incrementing |adjusted_iter| so it points to
- // the following element.
- shift += first_iter->original_length - first_iter->output_length;
- adjusted_iter =
- adjustments_on_adjusted_string->insert(adjusted_iter, *first_iter);
- ++adjusted_iter;
- ++first_iter;
- } else {
- // The first adjustment adjusted something that then got further adjusted
- // by the second set of adjustments. In other words, |first_iter| points
- // to something in the range covered by |adjusted_iter|'s length (after
- // accounting for |shift|). Precisely,
- // adjusted_iter->original_offset + shift
- // <=
- // first_iter->original_offset
- // <=
- // adjusted_iter->original_offset + shift +
- // adjusted_iter->original_length
-
- // Modify the current |adjusted_iter| to include whatever collapsing
- // happened in |first_iter|, then advance to the next |first_adjustments|
- // because we dealt with the current one.
- const int collapse = static_cast<int>(first_iter->original_length) -
- static_cast<int>(first_iter->output_length);
- // This function does not know how to deal with a string that expands and
- // then gets modified, only strings that collapse and then get modified.
- DCHECK_GT(collapse, 0);
- adjusted_iter->original_length += collapse;
- currently_collapsing += collapse;
- ++first_iter;
- }
- }
- DCHECK_EQ(0u, currently_collapsing);
- if (first_iter != first_adjustments.end()) {
- // Only first adjustments are left. These do not need to be modified.
- // (Their offsets are already correct with respect to the original string.)
- // Append them all.
- DCHECK(adjusted_iter == adjustments_on_adjusted_string->end());
- adjustments_on_adjusted_string->insert(
- adjustments_on_adjusted_string->end(), first_iter,
- first_adjustments.end());
- }
-}
-
-// Converts the given source Unicode character type to the given destination
-// Unicode character type as a STL string. The given input buffer and size
-// determine the source, and the given output STL string will be replaced by
-// the result. If non-NULL, |adjustments| is set to reflect the all the
-// alterations to the string that are not one-character-to-one-character.
-// It will always be sorted by increasing offset.
-template <typename SrcChar, typename DestStdString>
-bool ConvertUnicode(const SrcChar* src,
- size_t src_len,
- DestStdString* output,
- OffsetAdjuster::Adjustments* adjustments) {
- if (adjustments)
- adjustments->clear();
- // ICU requires 32-bit numbers.
- bool success = true;
- int32_t src_len32 = static_cast<int32_t>(src_len);
- for (int32_t i = 0; i < src_len32; i++) {
- uint32_t code_point;
- size_t original_i = i;
- size_t chars_written = 0;
- if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
- chars_written = WriteUnicodeCharacter(code_point, output);
- } else {
- chars_written = WriteUnicodeCharacter(0xFFFD, output);
- success = false;
- }
-
- // Only bother writing an adjustment if this modification changed the
- // length of this character.
- // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last
- // character read, not after it (so that incrementing it in the loop
- // increment will place it at the right location), so we need to account
- // for that in determining the amount that was read.
- if (adjustments && ((i - original_i + 1) != chars_written)) {
- adjustments->push_back(OffsetAdjuster::Adjustment(
- original_i, i - original_i + 1, chars_written));
- }
- }
- return success;
-}
-
-bool UTF8ToUTF16WithAdjustments(
- const char* src,
- size_t src_len,
- string16* output,
- base::OffsetAdjuster::Adjustments* adjustments) {
- PrepareForUTF16Or32Output(src, src_len, output);
- return ConvertUnicode(src, src_len, output, adjustments);
-}
-
-string16 UTF8ToUTF16WithAdjustments(
- const base::StringPiece& utf8,
- base::OffsetAdjuster::Adjustments* adjustments) {
- string16 result;
- UTF8ToUTF16WithAdjustments(utf8.data(), utf8.length(), &result, adjustments);
- return result;
-}
-
-string16 UTF8ToUTF16AndAdjustOffsets(
- const base::StringPiece& utf8,
- std::vector<size_t>* offsets_for_adjustment) {
- for (size_t& offset : *offsets_for_adjustment) {
- if (offset > utf8.length())
- offset = string16::npos;
- }
- OffsetAdjuster::Adjustments adjustments;
- string16 result = UTF8ToUTF16WithAdjustments(utf8, &adjustments);
- OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
- return result;
-}
-
-std::string UTF16ToUTF8AndAdjustOffsets(
- const base::StringPiece16& utf16,
- std::vector<size_t>* offsets_for_adjustment) {
- for (size_t& offset : *offsets_for_adjustment) {
- if (offset > utf16.length())
- offset = string16::npos;
- }
- std::string result;
- PrepareForUTF8Output(utf16.data(), utf16.length(), &result);
- OffsetAdjuster::Adjustments adjustments;
- ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments);
- OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
- return result;
-}
-
-} // namespace base
diff --git a/gn/base/strings/utf_offset_string_conversions.h b/gn/base/strings/utf_offset_string_conversions.h
deleted file mode 100644
index b7c4a1b8086..00000000000
--- a/gn/base/strings/utf_offset_string_conversions.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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.
-
-#ifndef BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
-#define BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
-
-#include <stddef.h>
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// A helper class and associated data structures to adjust offsets into a
-// string in response to various adjustments one might do to that string
-// (e.g., eliminating a range). For details on offsets, see the comments by
-// the AdjustOffsets() function below.
-class OffsetAdjuster {
- public:
- struct Adjustment {
- Adjustment(size_t original_offset,
- size_t original_length,
- size_t output_length);
-
- size_t original_offset;
- size_t original_length;
- size_t output_length;
- };
- typedef std::vector<Adjustment> Adjustments;
-
- // Adjusts all offsets in |offsets_for_adjustment| to reflect the adjustments
- // recorded in |adjustments|. Adjusted offsets greater than |limit| will be
- // set to string16::npos.
- //
- // Offsets represents insertion/selection points between characters: if |src|
- // is "abcd", then 0 is before 'a', 2 is between 'b' and 'c', and 4 is at the
- // end of the string. Valid input offsets range from 0 to |src_len|. On
- // exit, each offset will have been modified to point at the same logical
- // position in the output string. If an offset cannot be successfully
- // adjusted (e.g., because it points into the middle of a multibyte sequence),
- // it will be set to string16::npos.
- static void AdjustOffsets(const Adjustments& adjustments,
- std::vector<size_t>* offsets_for_adjustment,
- size_t limit = string16::npos);
-
- // Adjusts the single |offset| to reflect the adjustments recorded in
- // |adjustments|.
- static void AdjustOffset(const Adjustments& adjustments,
- size_t* offset,
- size_t limit = string16::npos);
-
- // Adjusts all offsets in |offsets_for_unadjustment| to reflect the reverse
- // of the adjustments recorded in |adjustments|. In other words, the offsets
- // provided represent offsets into an adjusted string and the caller wants
- // to know the offsets they correspond to in the original string. If an
- // offset cannot be successfully unadjusted (e.g., because it points into
- // the middle of a multibyte sequence), it will be set to string16::npos.
- static void UnadjustOffsets(const Adjustments& adjustments,
- std::vector<size_t>* offsets_for_unadjustment);
-
- // Adjusts the single |offset| to reflect the reverse of the adjustments
- // recorded in |adjustments|.
- static void UnadjustOffset(const Adjustments& adjustments, size_t* offset);
-
- // Combines two sequential sets of adjustments, storing the combined revised
- // adjustments in |adjustments_on_adjusted_string|. That is, suppose a
- // string was altered in some way, with the alterations recorded as
- // adjustments in |first_adjustments|. Then suppose the resulting string is
- // further altered, with the alterations recorded as adjustments scored in
- // |adjustments_on_adjusted_string|, with the offsets recorded in these
- // adjustments being with respect to the intermediate string. This function
- // combines the two sets of adjustments into one, storing the result in
- // |adjustments_on_adjusted_string|, whose offsets are correct with respect
- // to the original string.
- //
- // Assumes both parameters are sorted by increasing offset.
- //
- // WARNING: Only supports |first_adjustments| that involve collapsing ranges
- // of text, not expanding ranges.
- static void MergeSequentialAdjustments(
- const Adjustments& first_adjustments,
- Adjustments* adjustments_on_adjusted_string);
-};
-
-// Like the conversions in utf_string_conversions.h, but also fills in an
-// |adjustments| parameter that reflects the alterations done to the string.
-// It may be NULL.
-bool UTF8ToUTF16WithAdjustments(const char* src,
- size_t src_len,
- string16* output,
- base::OffsetAdjuster::Adjustments* adjustments);
-string16 UTF8ToUTF16WithAdjustments(
- const base::StringPiece& utf8,
- base::OffsetAdjuster::Adjustments* adjustments);
-// As above, but instead internally examines the adjustments and applies them
-// to |offsets_for_adjustment|. Input offsets greater than the length of the
-// input string will be set to string16::npos. See comments by AdjustOffsets().
-string16 UTF8ToUTF16AndAdjustOffsets(
- const base::StringPiece& utf8,
- std::vector<size_t>* offsets_for_adjustment);
-std::string UTF16ToUTF8AndAdjustOffsets(
- const base::StringPiece16& utf16,
- std::vector<size_t>* offsets_for_adjustment);
-
-} // namespace base
-
-#endif // BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
diff --git a/gn/base/strings/utf_string_conversion_utils.cc b/gn/base/strings/utf_string_conversion_utils.cc
deleted file mode 100644
index c962411b81d..00000000000
--- a/gn/base/strings/utf_string_conversion_utils.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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.
-
-#include "base/strings/utf_string_conversion_utils.h"
-
-#include "base/third_party/icu/icu_utf.h"
-#include "util/build_config.h"
-
-namespace base {
-
-// ReadUnicodeCharacter --------------------------------------------------------
-
-bool ReadUnicodeCharacter(const char* src,
- int32_t src_len,
- int32_t* char_index,
- uint32_t* code_point_out) {
- // U8_NEXT expects to be able to use -1 to signal an error, so we must
- // use a signed type for code_point. But this function returns false
- // on error anyway, so code_point_out is unsigned.
- int32_t code_point;
- CBU8_NEXT(src, *char_index, src_len, code_point);
- *code_point_out = static_cast<uint32_t>(code_point);
-
- // The ICU macro above moves to the next char, we want to point to the last
- // char consumed.
- (*char_index)--;
-
- // Validate the decoded value.
- return IsValidCodepoint(code_point);
-}
-
-bool ReadUnicodeCharacter(const char16* src,
- int32_t src_len,
- int32_t* char_index,
- uint32_t* code_point) {
- if (CBU16_IS_SURROGATE(src[*char_index])) {
- if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) ||
- *char_index + 1 >= src_len || !CBU16_IS_TRAIL(src[*char_index + 1])) {
- // Invalid surrogate pair.
- return false;
- }
-
- // Valid surrogate pair.
- *code_point =
- CBU16_GET_SUPPLEMENTARY(src[*char_index], src[*char_index + 1]);
- (*char_index)++;
- } else {
- // Not a surrogate, just one 16-bit word.
- *code_point = src[*char_index];
- }
-
- return IsValidCodepoint(*code_point);
-}
-
-#if defined(WCHAR_T_IS_UTF32)
-bool ReadUnicodeCharacter(const wchar_t* src,
- int32_t src_len,
- int32_t* char_index,
- uint32_t* code_point) {
- // Conversion is easy since the source is 32-bit.
- *code_point = src[*char_index];
-
- // Validate the value.
- return IsValidCodepoint(*code_point);
-}
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// WriteUnicodeCharacter -------------------------------------------------------
-
-size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output) {
- if (code_point <= 0x7f) {
- // Fast path the common case of one byte.
- output->push_back(static_cast<char>(code_point));
- return 1;
- }
-
- // CBU8_APPEND_UNSAFE can append up to 4 bytes.
- size_t char_offset = output->length();
- size_t original_char_offset = char_offset;
- output->resize(char_offset + CBU8_MAX_LENGTH);
-
- CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
-
- // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so
- // it will represent the new length of the string.
- output->resize(char_offset);
- return char_offset - original_char_offset;
-}
-
-size_t WriteUnicodeCharacter(uint32_t code_point, string16* output) {
- if (CBU16_LENGTH(code_point) == 1) {
- // Thie code point is in the Basic Multilingual Plane (BMP).
- output->push_back(static_cast<char16>(code_point));
- return 1;
- }
- // Non-BMP characters use a double-character encoding.
- size_t char_offset = output->length();
- output->resize(char_offset + CBU16_MAX_LENGTH);
- CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
- return CBU16_MAX_LENGTH;
-}
-
-// Generalized Unicode converter -----------------------------------------------
-
-template <typename CHAR>
-void PrepareForUTF8Output(const CHAR* src,
- size_t src_len,
- std::string* output) {
- output->clear();
- if (src_len == 0)
- return;
- if (src[0] < 0x80) {
- // Assume that the entire input will be ASCII.
- output->reserve(src_len);
- } else {
- // Assume that the entire input is non-ASCII and will have 3 bytes per char.
- output->reserve(src_len * 3);
- }
-}
-
-// Instantiate versions we know callers will need.
-#if !defined(OS_WIN)
-// wchar_t and char16 are the same thing on Windows.
-template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*);
-#endif
-template void PrepareForUTF8Output(const char16*, size_t, std::string*);
-
-template <typename STRING>
-void PrepareForUTF16Or32Output(const char* src,
- size_t src_len,
- STRING* output) {
- output->clear();
- if (src_len == 0)
- return;
- if (static_cast<unsigned char>(src[0]) < 0x80) {
- // Assume the input is all ASCII, which means 1:1 correspondence.
- output->reserve(src_len);
- } else {
- // Otherwise assume that the UTF-8 sequences will have 2 bytes for each
- // character.
- output->reserve(src_len / 2);
- }
-}
-
-// Instantiate versions we know callers will need.
-#if !defined(OS_WIN)
-// std::wstring and string16 are the same thing on Windows.
-template void PrepareForUTF16Or32Output(const char*, size_t, std::wstring*);
-#endif
-template void PrepareForUTF16Or32Output(const char*, size_t, string16*);
-
-} // namespace base
diff --git a/gn/base/strings/utf_string_conversion_utils.h b/gn/base/strings/utf_string_conversion_utils.h
deleted file mode 100644
index 6f89652ee87..00000000000
--- a/gn/base/strings/utf_string_conversion_utils.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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.
-
-#ifndef BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
-#define BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
-
-// Low-level UTF handling functions. Most code will want to use the functions
-// in utf_string_conversions.h
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/strings/string16.h"
-
-namespace base {
-
-inline bool IsValidCodepoint(uint32_t code_point) {
- // Excludes the surrogate code points ([0xD800, 0xDFFF]) and
- // codepoints larger than 0x10FFFF (the highest codepoint allowed).
- // Non-characters and unassigned codepoints are allowed.
- return code_point < 0xD800u ||
- (code_point >= 0xE000u && code_point <= 0x10FFFFu);
-}
-
-inline bool IsValidCharacter(uint32_t code_point) {
- // Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in
- // 0xFFFE or 0xFFFF) from the set of valid code points.
- return code_point < 0xD800u ||
- (code_point >= 0xE000u && code_point < 0xFDD0u) ||
- (code_point > 0xFDEFu && code_point <= 0x10FFFFu &&
- (code_point & 0xFFFEu) != 0xFFFEu);
-}
-
-// ReadUnicodeCharacter --------------------------------------------------------
-
-// Reads a UTF-8 stream, placing the next code point into the given output
-// |*code_point|. |src| represents the entire string to read, and |*char_index|
-// is the character offset within the string to start reading at. |*char_index|
-// will be updated to index the last character read, such that incrementing it
-// (as in a for loop) will take the reader to the next character.
-//
-// Returns true on success. On false, |*code_point| will be invalid.
-bool ReadUnicodeCharacter(const char* src,
- int32_t src_len,
- int32_t* char_index,
- uint32_t* code_point_out);
-
-// Reads a UTF-16 character. The usage is the same as the 8-bit version above.
-bool ReadUnicodeCharacter(const char16* src,
- int32_t src_len,
- int32_t* char_index,
- uint32_t* code_point);
-
-#if defined(WCHAR_T_IS_UTF32)
-// Reads UTF-32 character. The usage is the same as the 8-bit version above.
-bool ReadUnicodeCharacter(const wchar_t* src,
- int32_t src_len,
- int32_t* char_index,
- uint32_t* code_point);
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// WriteUnicodeCharacter -------------------------------------------------------
-
-// Appends a UTF-8 character to the given 8-bit string. Returns the number of
-// bytes written.
-size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output);
-
-// Appends the given code point as a UTF-16 character to the given 16-bit
-// string. Returns the number of 16-bit values written.
-size_t WriteUnicodeCharacter(uint32_t code_point, string16* output);
-
-#if defined(WCHAR_T_IS_UTF32)
-// Appends the given UTF-32 character to the given 32-bit string. Returns the
-// number of 32-bit values written.
-inline size_t WriteUnicodeCharacter(uint32_t code_point, std::wstring* output) {
- // This is the easy case, just append the character.
- output->push_back(code_point);
- return 1;
-}
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// Generalized Unicode converter -----------------------------------------------
-
-// Guesses the length of the output in UTF-8 in bytes, clears that output
-// string, and reserves that amount of space. We assume that the input
-// character types are unsigned, which will be true for UTF-16 and -32 on our
-// systems.
-template <typename CHAR>
-void PrepareForUTF8Output(const CHAR* src, size_t src_len, std::string* output);
-
-// Prepares an output buffer (containing either UTF-16 or -32 data) given some
-// UTF-8 input that will be converted to it. See PrepareForUTF8Output().
-template <typename STRING>
-void PrepareForUTF16Or32Output(const char* src, size_t src_len, STRING* output);
-
-} // namespace base
-
-#endif // BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
diff --git a/gn/base/strings/utf_string_conversions.cc b/gn/base/strings/utf_string_conversions.cc
deleted file mode 100644
index b04d54d215e..00000000000
--- a/gn/base/strings/utf_string_conversions.cc
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (c) 2018 The Chromium 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/strings/utf_string_conversions.h"
-
-#include <stdint.h>
-
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversion_utils.h"
-#include "base/third_party/icu/icu_utf.h"
-#include "util/build_config.h"
-
-namespace base {
-
-namespace {
-
-constexpr int32_t kErrorCodePoint = 0xFFFD;
-
-// Size coefficient ----------------------------------------------------------
-// The maximum number of codeunits in the destination encoding corresponding to
-// one codeunit in the source encoding.
-
-template <typename SrcChar, typename DestChar>
-struct SizeCoefficient {
- static_assert(sizeof(SrcChar) < sizeof(DestChar),
- "Default case: from a smaller encoding to the bigger one");
-
- // ASCII symbols are encoded by one codeunit in all encodings.
- static constexpr int value = 1;
-};
-
-template <>
-struct SizeCoefficient<char16, char> {
- // One UTF-16 codeunit corresponds to at most 3 codeunits in UTF-8.
- static constexpr int value = 3;
-};
-
-#if defined(WCHAR_T_IS_UTF32)
-template <>
-struct SizeCoefficient<wchar_t, char> {
- // UTF-8 uses at most 4 codeunits per character.
- static constexpr int value = 4;
-};
-
-template <>
-struct SizeCoefficient<wchar_t, char16> {
- // UTF-16 uses at most 2 codeunits per character.
- static constexpr int value = 2;
-};
-#endif // defined(WCHAR_T_IS_UTF32)
-
-template <typename SrcChar, typename DestChar>
-constexpr int size_coefficient_v =
- SizeCoefficient<std::decay_t<SrcChar>, std::decay_t<DestChar>>::value;
-
-// UnicodeAppendUnsafe --------------------------------------------------------
-// Function overloads that write code_point to the output string. Output string
-// has to have enough space for the codepoint.
-
-void UnicodeAppendUnsafe(char* out, int32_t* size, uint32_t code_point) {
- CBU8_APPEND_UNSAFE(out, *size, code_point);
-}
-
-void UnicodeAppendUnsafe(char16* out, int32_t* size, uint32_t code_point) {
- CBU16_APPEND_UNSAFE(out, *size, code_point);
-}
-
-#if defined(WCHAR_T_IS_UTF32)
-
-void UnicodeAppendUnsafe(wchar_t* out, int32_t* size, uint32_t code_point) {
- out[(*size)++] = code_point;
-}
-
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// DoUTFConversion ------------------------------------------------------------
-// Main driver of UTFConversion specialized for different Src encodings.
-// dest has to have enough room for the converted text.
-
-template <typename DestChar>
-bool DoUTFConversion(const char* src,
- int32_t src_len,
- DestChar* dest,
- int32_t* dest_len) {
- bool success = true;
-
- for (int32_t i = 0; i < src_len;) {
- int32_t code_point;
- CBU8_NEXT(src, i, src_len, code_point);
-
- if (!IsValidCodepoint(code_point)) {
- success = false;
- code_point = kErrorCodePoint;
- }
-
- UnicodeAppendUnsafe(dest, dest_len, code_point);
- }
-
- return success;
-}
-
-template <typename DestChar>
-bool DoUTFConversion(const char16* src,
- int32_t src_len,
- DestChar* dest,
- int32_t* dest_len) {
- bool success = true;
-
- auto ConvertSingleChar = [&success](char16 in) -> int32_t {
- if (!CBU16_IS_SINGLE(in) || !IsValidCodepoint(in)) {
- success = false;
- return kErrorCodePoint;
- }
- return in;
- };
-
- int32_t i = 0;
-
- // Always have another symbol in order to avoid checking boundaries in the
- // middle of the surrogate pair.
- while (i < src_len - 1) {
- int32_t code_point;
-
- if (CBU16_IS_LEAD(src[i]) && CBU16_IS_TRAIL(src[i + 1])) {
- code_point = CBU16_GET_SUPPLEMENTARY(src[i], src[i + 1]);
- if (!IsValidCodepoint(code_point)) {
- code_point = kErrorCodePoint;
- success = false;
- }
- i += 2;
- } else {
- code_point = ConvertSingleChar(src[i]);
- ++i;
- }
-
- UnicodeAppendUnsafe(dest, dest_len, code_point);
- }
-
- if (i < src_len)
- UnicodeAppendUnsafe(dest, dest_len, ConvertSingleChar(src[i]));
-
- return success;
-}
-
-#if defined(WCHAR_T_IS_UTF32)
-
-template <typename DestChar>
-bool DoUTFConversion(const wchar_t* src,
- int32_t src_len,
- DestChar* dest,
- int32_t* dest_len) {
- bool success = true;
-
- for (int32_t i = 0; i < src_len; ++i) {
- int32_t code_point = src[i];
-
- if (!IsValidCodepoint(code_point)) {
- success = false;
- code_point = kErrorCodePoint;
- }
-
- UnicodeAppendUnsafe(dest, dest_len, code_point);
- }
-
- return success;
-}
-
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// UTFConversion --------------------------------------------------------------
-// Function template for generating all UTF conversions.
-
-template <typename InputString, typename DestString>
-bool UTFConversion(const InputString& src_str, DestString* dest_str) {
- if (IsStringASCII(src_str)) {
- dest_str->assign(src_str.begin(), src_str.end());
- return true;
- }
-
- dest_str->resize(src_str.length() *
- size_coefficient_v<typename InputString::value_type,
- typename DestString::value_type>);
-
- // Empty string is ASCII => it OK to call operator[].
- auto* dest = &(*dest_str)[0];
-
- // ICU requires 32 bit numbers.
- int32_t src_len32 = static_cast<int32_t>(src_str.length());
- int32_t dest_len32 = 0;
-
- bool res = DoUTFConversion(src_str.data(), src_len32, dest, &dest_len32);
-
- dest_str->resize(dest_len32);
- dest_str->shrink_to_fit();
-
- return res;
-}
-
-} // namespace
-
-// UTF16 <-> UTF8 --------------------------------------------------------------
-
-bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) {
- return UTFConversion(StringPiece(src, src_len), output);
-}
-
-string16 UTF8ToUTF16(StringPiece utf8) {
- string16 ret;
- // Ignore the success flag of this call, it will do the best it can for
- // invalid input, which is what we want here.
- UTF8ToUTF16(utf8.data(), utf8.size(), &ret);
- return ret;
-}
-
-bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) {
- return UTFConversion(StringPiece16(src, src_len), output);
-}
-
-std::string UTF16ToUTF8(StringPiece16 utf16) {
- std::string ret;
- // Ignore the success flag of this call, it will do the best it can for
- // invalid input, which is what we want here.
- UTF16ToUTF8(utf16.data(), utf16.length(), &ret);
- return ret;
-}
-
-// UTF-16 <-> Wide -------------------------------------------------------------
-
-#if defined(WCHAR_T_IS_UTF16)
-// When wide == UTF-16 the conversions are a NOP.
-
-bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) {
- output->assign(src, src_len);
- return true;
-}
-
-string16 WideToUTF16(WStringPiece wide) {
- return wide.as_string();
-}
-
-bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) {
- output->assign(src, src_len);
- return true;
-}
-
-std::wstring UTF16ToWide(StringPiece16 utf16) {
- return utf16.as_string();
-}
-
-#elif defined(WCHAR_T_IS_UTF32)
-
-bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) {
- return UTFConversion(base::WStringPiece(src, src_len), output);
-}
-
-string16 WideToUTF16(WStringPiece wide) {
- string16 ret;
- // Ignore the success flag of this call, it will do the best it can for
- // invalid input, which is what we want here.
- WideToUTF16(wide.data(), wide.length(), &ret);
- return ret;
-}
-
-bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) {
- return UTFConversion(StringPiece16(src, src_len), output);
-}
-
-std::wstring UTF16ToWide(StringPiece16 utf16) {
- std::wstring ret;
- // Ignore the success flag of this call, it will do the best it can for
- // invalid input, which is what we want here.
- UTF16ToWide(utf16.data(), utf16.length(), &ret);
- return ret;
-}
-
-#endif // defined(WCHAR_T_IS_UTF32)
-
-// UTF-8 <-> Wide --------------------------------------------------------------
-
-// UTF8ToWide is the same code, regardless of whether wide is 16 or 32 bits
-
-bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output) {
- return UTFConversion(StringPiece(src, src_len), output);
-}
-
-std::wstring UTF8ToWide(StringPiece utf8) {
- std::wstring ret;
- // Ignore the success flag of this call, it will do the best it can for
- // invalid input, which is what we want here.
- UTF8ToWide(utf8.data(), utf8.length(), &ret);
- return ret;
-}
-
-#if defined(WCHAR_T_IS_UTF16)
-// Easy case since we can use the "utf" versions we already wrote above.
-
-bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) {
- return UTF16ToUTF8(src, src_len, output);
-}
-
-std::string WideToUTF8(WStringPiece wide) {
- return UTF16ToUTF8(wide);
-}
-
-#elif defined(WCHAR_T_IS_UTF32)
-
-bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) {
- return UTFConversion(WStringPiece(src, src_len), output);
-}
-
-std::string WideToUTF8(WStringPiece wide) {
- std::string ret;
- // Ignore the success flag of this call, it will do the best it can for
- // invalid input, which is what we want here.
- WideToUTF8(wide.data(), wide.length(), &ret);
- return ret;
-}
-
-#endif // defined(WCHAR_T_IS_UTF32)
-
-string16 ASCIIToUTF16(StringPiece ascii) {
- DCHECK(IsStringASCII(ascii)) << ascii;
- return string16(ascii.begin(), ascii.end());
-}
-
-std::string UTF16ToASCII(StringPiece16 utf16) {
- DCHECK(IsStringASCII(utf16)) << UTF16ToUTF8(utf16);
- return std::string(utf16.begin(), utf16.end());
-}
-
-} // namespace base
diff --git a/gn/base/strings/utf_string_conversions.h b/gn/base/strings/utf_string_conversions.h
deleted file mode 100644
index 704943eddaf..00000000000
--- a/gn/base/strings/utf_string_conversions.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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.
-
-#ifndef BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
-#define BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// These convert between UTF-8, -16, and -32 strings. They are potentially slow,
-// so avoid unnecessary conversions. The low-level versions return a boolean
-// indicating whether the conversion was 100% valid. In this case, it will still
-// do the best it can and put the result in the output buffer. The versions that
-// return strings ignore this error and just return the best conversion
-// possible.
-bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output);
-std::string WideToUTF8(WStringPiece wide);
-bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output);
-std::wstring UTF8ToWide(StringPiece utf8);
-
-bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output);
-string16 WideToUTF16(WStringPiece wide);
-bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output);
-std::wstring UTF16ToWide(StringPiece16 utf16);
-
-bool UTF8ToUTF16(const char* src, size_t src_len, string16* output);
-string16 UTF8ToUTF16(StringPiece utf8);
-bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output);
-std::string UTF16ToUTF8(StringPiece16 utf16);
-
-// This converts an ASCII string, typically a hardcoded constant, to a UTF16
-// string.
-string16 ASCIIToUTF16(StringPiece ascii);
-
-// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
-// beforehand.
-std::string UTF16ToASCII(StringPiece16 utf16);
-
-} // namespace base
-
-#endif // BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
diff --git a/gn/base/template_util.h b/gn/base/template_util.h
deleted file mode 100644
index dda9587ff7c..00000000000
--- a/gn/base/template_util.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// 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.
-
-#ifndef BASE_TEMPLATE_UTIL_H_
-#define BASE_TEMPLATE_UTIL_H_
-
-#include <stddef.h>
-#include <iosfwd>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include "util/build_config.h"
-
-// Some versions of libstdc++ have partial support for type_traits, but misses
-// a smaller subset while removing some of the older non-standard stuff. Assume
-// that all versions below 5.0 fall in this category, along with one 5.0
-// experimental release. Test for this by consulting compiler major version,
-// the only reliable option available, so theoretically this could fail should
-// you attempt to mix an earlier version of libstdc++ with >= GCC5. But
-// that's unlikely to work out, especially as GCC5 changed ABI.
-#define CR_GLIBCXX_5_0_0 20150123
-#if (defined(__GNUC__) && __GNUC__ < 5) || \
- (defined(__GLIBCXX__) && __GLIBCXX__ == CR_GLIBCXX_5_0_0)
-#define CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
-#endif
-
-// This hacks around using gcc with libc++ which has some incompatibilies.
-// - is_trivially_* doesn't work: https://llvm.org/bugs/show_bug.cgi?id=27538
-// TODO(danakj): Remove this when android builders are all using a newer version
-// of gcc, or the android ndk is updated to a newer libc++ that works with older
-// gcc versions.
-#if !defined(__clang__) && defined(_LIBCPP_VERSION)
-#define CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
-#endif
-
-namespace base {
-
-template <class T>
-struct is_non_const_reference : std::false_type {};
-template <class T>
-struct is_non_const_reference<T&> : std::true_type {};
-template <class T>
-struct is_non_const_reference<const T&> : std::false_type {};
-
-namespace internal {
-
-// Implementation detail of base::void_t below.
-template <typename...>
-struct make_void {
- using type = void;
-};
-
-} // namespace internal
-
-// base::void_t is an implementation of std::void_t from C++17.
-//
-// We use |base::internal::make_void| as a helper struct to avoid a C++14
-// defect:
-// http://en.cppreference.com/w/cpp/types/void_t
-// http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558
-template <typename... Ts>
-using void_t = typename ::base::internal::make_void<Ts...>::type;
-
-namespace internal {
-
-// Uses expression SFINAE to detect whether using operator<< would work.
-template <typename T, typename = void>
-struct SupportsOstreamOperator : std::false_type {};
-template <typename T>
-struct SupportsOstreamOperator<T,
- decltype(void(std::declval<std::ostream&>()
- << std::declval<T>()))>
- : std::true_type {};
-
-// Used to detech whether the given type is an iterator. This is normally used
-// with std::enable_if to provide disambiguation for functions that take
-// templatzed iterators as input.
-template <typename T, typename = void>
-struct is_iterator : std::false_type {};
-
-template <typename T>
-struct is_iterator<T,
- void_t<typename std::iterator_traits<T>::iterator_category>>
- : std::true_type {};
-
-} // namespace internal
-
-// is_trivially_copyable is especially hard to get right.
-// - Older versions of libstdc++ will fail to have it like they do for other
-// type traits. This has become a subset of the second point, but used to be
-// handled independently.
-// - An experimental release of gcc includes most of type_traits but misses
-// is_trivially_copyable, so we still have to avoid using libstdc++ in this
-// case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX.
-// - When compiling libc++ from before r239653, with a gcc compiler, the
-// std::is_trivially_copyable can fail. So we need to work around that by not
-// using the one in libc++ in this case. This is covered by the
-// CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX define, and is discussed in
-// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 where they point out that
-// in libc++'s commit r239653 this is fixed by libc++ checking for gcc 5.1.
-// - In both of the above cases we are using the gcc compiler. When defining
-// this ourselves on compiler intrinsics, the __is_trivially_copyable()
-// intrinsic is not available on gcc before version 5.1 (see the discussion in
-// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 again), so we must check for
-// that version.
-// - When __is_trivially_copyable() is not available because we are on gcc older
-// than 5.1, we need to fall back to something, so we use __has_trivial_copy()
-// instead based on what was done one-off in bit_cast() previously.
-
-// TODO(crbug.com/554293): Remove this when all platforms have this in the std
-// namespace and it works with gcc as needed.
-#if defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
- defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX)
-template <typename T>
-struct is_trivially_copyable {
-// TODO(danakj): Remove this when android builders are all using a newer version
-// of gcc, or the android ndk is updated to a newer libc++ that does this for
-// us.
-#if _GNUC_VER >= 501
- static constexpr bool value = __is_trivially_copyable(T);
-#else
- static constexpr bool value =
- __has_trivial_copy(T) && __has_trivial_destructor(T);
-#endif
-};
-#else
-template <class T>
-using is_trivially_copyable = std::is_trivially_copyable<T>;
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 7
-// Workaround for g++7 and earlier family.
-// Due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654, without this
-// Optional<std::vector<T>> where T is non-copyable causes a compile error.
-// As we know it is not trivially copy constructible, explicitly declare so.
-template <typename T>
-struct is_trivially_copy_constructible
- : std::is_trivially_copy_constructible<T> {};
-
-template <typename... T>
-struct is_trivially_copy_constructible<std::vector<T...>> : std::false_type {};
-#else
-// Otherwise use std::is_trivially_copy_constructible as is.
-template <typename T>
-using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
-#endif
-
-} // namespace base
-
-#undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
-#undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
-
-#endif // BASE_TEMPLATE_UTIL_H_
diff --git a/gn/base/values.cc b/gn/base/values.cc
deleted file mode 100644
index 0b348b725f9..00000000000
--- a/gn/base/values.cc
+++ /dev/null
@@ -1,1297 +0,0 @@
-// 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 "base/values.h"
-
-#include <string.h>
-
-#include <algorithm>
-#include <cmath>
-#include <new>
-#include <ostream>
-#include <utility>
-
-#include "base/json/json_writer.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-
-namespace base {
-
-namespace {
-
-const char* const kTypeNames[] = {"null", "boolean", "integer", "string",
- "binary", "dictionary", "list"};
-static_assert(arraysize(kTypeNames) ==
- static_cast<size_t>(Value::Type::LIST) + 1,
- "kTypeNames Has Wrong Size");
-
-std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node);
-
-// Make a deep copy of |node|, but don't include empty lists or dictionaries
-// in the copy. It's possible for this function to return NULL and it
-// expects |node| to always be non-NULL.
-std::unique_ptr<Value> CopyListWithoutEmptyChildren(const Value& list) {
- Value copy(Value::Type::LIST);
- for (const auto& entry : list.GetList()) {
- std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(entry);
- if (child_copy)
- copy.GetList().push_back(std::move(*child_copy));
- }
- return copy.GetList().empty() ? nullptr
- : std::make_unique<Value>(std::move(copy));
-}
-
-std::unique_ptr<DictionaryValue> CopyDictionaryWithoutEmptyChildren(
- const DictionaryValue& dict) {
- std::unique_ptr<DictionaryValue> copy;
- for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
- std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(it.value());
- if (child_copy) {
- if (!copy)
- copy = std::make_unique<DictionaryValue>();
- copy->SetWithoutPathExpansion(it.key(), std::move(child_copy));
- }
- }
- return copy;
-}
-
-std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node) {
- switch (node.type()) {
- case Value::Type::LIST:
- return CopyListWithoutEmptyChildren(static_cast<const ListValue&>(node));
-
- case Value::Type::DICTIONARY:
- return CopyDictionaryWithoutEmptyChildren(
- static_cast<const DictionaryValue&>(node));
-
- default:
- return std::make_unique<Value>(node.Clone());
- }
-}
-
-} // namespace
-
-// static
-std::unique_ptr<Value> Value::CreateWithCopiedBuffer(const char* buffer,
- size_t size) {
- return std::make_unique<Value>(BlobStorage(buffer, buffer + size));
-}
-
-// static
-Value Value::FromUniquePtrValue(std::unique_ptr<Value> val) {
- return std::move(*val);
-}
-
-// static
-std::unique_ptr<Value> Value::ToUniquePtrValue(Value val) {
- return std::make_unique<Value>(std::move(val));
-}
-
-Value::Value(Value&& that) noexcept {
- InternalMoveConstructFrom(std::move(that));
-}
-
-Value::Value() noexcept : type_(Type::NONE) {}
-
-Value::Value(Type type) : type_(type) {
- // Initialize with the default value.
- switch (type_) {
- case Type::NONE:
- return;
-
- case Type::BOOLEAN:
- bool_value_ = false;
- return;
- case Type::INTEGER:
- int_value_ = 0;
- return;
- case Type::STRING:
- new (&string_value_) std::string();
- return;
- case Type::BINARY:
- new (&binary_value_) BlobStorage();
- return;
- case Type::DICTIONARY:
- new (&dict_) DictStorage();
- return;
- case Type::LIST:
- new (&list_) ListStorage();
- return;
- }
-}
-
-Value::Value(bool in_bool) : type_(Type::BOOLEAN), bool_value_(in_bool) {}
-
-Value::Value(int in_int) : type_(Type::INTEGER), int_value_(in_int) {}
-
-Value::Value(const char* in_string) : Value(std::string(in_string)) {}
-
-Value::Value(StringPiece in_string) : Value(std::string(in_string)) {}
-
-Value::Value(std::string&& in_string) noexcept
- : type_(Type::STRING), string_value_(std::move(in_string)) {
- DCHECK(IsStringUTF8(string_value_));
-}
-
-Value::Value(const char16* in_string16) : Value(StringPiece16(in_string16)) {}
-
-Value::Value(StringPiece16 in_string16) : Value(UTF16ToUTF8(in_string16)) {}
-
-Value::Value(const BlobStorage& in_blob)
- : type_(Type::BINARY), binary_value_(in_blob) {}
-
-Value::Value(BlobStorage&& in_blob) noexcept
- : type_(Type::BINARY), binary_value_(std::move(in_blob)) {}
-
-Value::Value(const DictStorage& in_dict) : type_(Type::DICTIONARY), dict_() {
- dict_.reserve(in_dict.size());
- for (const auto& it : in_dict) {
- dict_.try_emplace(dict_.end(), it.first,
- std::make_unique<Value>(it.second->Clone()));
- }
-}
-
-Value::Value(DictStorage&& in_dict) noexcept
- : type_(Type::DICTIONARY), dict_(std::move(in_dict)) {}
-
-Value::Value(const ListStorage& in_list) : type_(Type::LIST), list_() {
- list_.reserve(in_list.size());
- for (const auto& val : in_list)
- list_.emplace_back(val.Clone());
-}
-
-Value::Value(ListStorage&& in_list) noexcept
- : type_(Type::LIST), list_(std::move(in_list)) {}
-
-Value& Value::operator=(Value&& that) noexcept {
- InternalCleanup();
- InternalMoveConstructFrom(std::move(that));
-
- return *this;
-}
-
-Value Value::Clone() const {
- switch (type_) {
- case Type::NONE:
- return Value();
- case Type::BOOLEAN:
- return Value(bool_value_);
- case Type::INTEGER:
- return Value(int_value_);
- case Type::STRING:
- return Value(string_value_);
- case Type::BINARY:
- return Value(binary_value_);
- case Type::DICTIONARY:
- return Value(dict_);
- case Type::LIST:
- return Value(list_);
- }
-
- NOTREACHED();
- return Value();
-}
-
-Value::~Value() {
- InternalCleanup();
-}
-
-// static
-const char* Value::GetTypeName(Value::Type type) {
- DCHECK_GE(static_cast<int>(type), 0);
- DCHECK_LT(static_cast<size_t>(type), arraysize(kTypeNames));
- return kTypeNames[static_cast<size_t>(type)];
-}
-
-bool Value::GetBool() const {
- CHECK(is_bool());
- return bool_value_;
-}
-
-int Value::GetInt() const {
- CHECK(is_int());
- return int_value_;
-}
-
-const std::string& Value::GetString() const {
- CHECK(is_string());
- return string_value_;
-}
-
-const Value::BlobStorage& Value::GetBlob() const {
- CHECK(is_blob());
- return binary_value_;
-}
-
-Value::ListStorage& Value::GetList() {
- CHECK(is_list());
- return list_;
-}
-
-const Value::ListStorage& Value::GetList() const {
- CHECK(is_list());
- return list_;
-}
-
-Value* Value::FindKey(StringPiece key) {
- return const_cast<Value*>(static_cast<const Value*>(this)->FindKey(key));
-}
-
-const Value* Value::FindKey(StringPiece key) const {
- CHECK(is_dict());
- auto found = dict_.find(key);
- if (found == dict_.end())
- return nullptr;
- return found->second.get();
-}
-
-Value* Value::FindKeyOfType(StringPiece key, Type type) {
- return const_cast<Value*>(
- static_cast<const Value*>(this)->FindKeyOfType(key, type));
-}
-
-const Value* Value::FindKeyOfType(StringPiece key, Type type) const {
- const Value* result = FindKey(key);
- if (!result || result->type() != type)
- return nullptr;
- return result;
-}
-
-bool Value::RemoveKey(StringPiece key) {
- CHECK(is_dict());
- // NOTE: Can't directly return dict_->erase(key) due to MSVC warning C4800.
- return dict_.erase(key) != 0;
-}
-
-Value* Value::SetKey(StringPiece key, Value value) {
- CHECK(is_dict());
- // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
- // an explicit conversion from StringPiece to std::string if necessary.
- auto val_ptr = std::make_unique<Value>(std::move(value));
- auto result = dict_.try_emplace(key, std::move(val_ptr));
- if (!result.second) {
- // val_ptr is guaranteed to be still intact at this point.
- result.first->second = std::move(val_ptr);
- }
- return result.first->second.get();
-}
-
-Value* Value::SetKey(std::string&& key, Value value) {
- CHECK(is_dict());
- return dict_
- .insert_or_assign(std::move(key),
- std::make_unique<Value>(std::move(value)))
- .first->second.get();
-}
-
-Value* Value::SetKey(const char* key, Value value) {
- return SetKey(StringPiece(key), std::move(value));
-}
-
-Value* Value::FindPath(std::initializer_list<StringPiece> path) {
- return const_cast<Value*>(const_cast<const Value*>(this)->FindPath(path));
-}
-
-Value* Value::FindPath(span<const StringPiece> path) {
- return const_cast<Value*>(const_cast<const Value*>(this)->FindPath(path));
-}
-
-const Value* Value::FindPath(std::initializer_list<StringPiece> path) const {
- DCHECK_GE(path.size(), 2u) << "Use FindKey() for a path of length 1.";
- return FindPath(make_span(path.begin(), path.size()));
-}
-
-const Value* Value::FindPath(span<const StringPiece> path) const {
- const Value* cur = this;
- for (const StringPiece component : path) {
- if (!cur->is_dict() || (cur = cur->FindKey(component)) == nullptr)
- return nullptr;
- }
- return cur;
-}
-
-Value* Value::FindPathOfType(std::initializer_list<StringPiece> path,
- Type type) {
- return const_cast<Value*>(
- const_cast<const Value*>(this)->FindPathOfType(path, type));
-}
-
-Value* Value::FindPathOfType(span<const StringPiece> path, Type type) {
- return const_cast<Value*>(
- const_cast<const Value*>(this)->FindPathOfType(path, type));
-}
-
-const Value* Value::FindPathOfType(std::initializer_list<StringPiece> path,
- Type type) const {
- DCHECK_GE(path.size(), 2u) << "Use FindKeyOfType() for a path of length 1.";
- return FindPathOfType(make_span(path.begin(), path.size()), type);
-}
-
-const Value* Value::FindPathOfType(span<const StringPiece> path,
- Type type) const {
- const Value* result = FindPath(path);
- if (!result || result->type() != type)
- return nullptr;
- return result;
-}
-
-Value* Value::SetPath(std::initializer_list<StringPiece> path, Value value) {
- DCHECK_GE(path.size(), 2u) << "Use SetKey() for a path of length 1.";
- return SetPath(make_span(path.begin(), path.size()), std::move(value));
-}
-
-Value* Value::SetPath(span<const StringPiece> path, Value value) {
- DCHECK_NE(path.begin(), path.end()); // Can't be empty path.
-
- // Walk/construct intermediate dictionaries. The last element requires
- // special handling so skip it in this loop.
- Value* cur = this;
- const StringPiece* cur_path = path.begin();
- for (; (cur_path + 1) < path.end(); ++cur_path) {
- if (!cur->is_dict())
- return nullptr;
-
- // Use lower_bound to avoid doing the search twice for missing keys.
- const StringPiece path_component = *cur_path;
- auto found = cur->dict_.lower_bound(path_component);
- if (found == cur->dict_.end() || found->first != path_component) {
- // No key found, insert one.
- auto inserted = cur->dict_.try_emplace(
- found, path_component, std::make_unique<Value>(Type::DICTIONARY));
- cur = inserted->second.get();
- } else {
- cur = found->second.get();
- }
- }
-
- // "cur" will now contain the last dictionary to insert or replace into.
- if (!cur->is_dict())
- return nullptr;
- return cur->SetKey(*cur_path, std::move(value));
-}
-
-bool Value::RemovePath(std::initializer_list<StringPiece> path) {
- DCHECK_GE(path.size(), 2u) << "Use RemoveKey() for a path of length 1.";
- return RemovePath(make_span(path.begin(), path.size()));
-}
-
-bool Value::RemovePath(span<const StringPiece> path) {
- if (!is_dict() || path.empty())
- return false;
-
- if (path.size() == 1)
- return RemoveKey(path[0]);
-
- auto found = dict_.find(path[0]);
- if (found == dict_.end() || !found->second->is_dict())
- return false;
-
- bool removed = found->second->RemovePath(path.subspan(1));
- if (removed && found->second->dict_.empty())
- dict_.erase(found);
-
- return removed;
-}
-
-Value::dict_iterator_proxy Value::DictItems() {
- CHECK(is_dict());
- return dict_iterator_proxy(&dict_);
-}
-
-Value::const_dict_iterator_proxy Value::DictItems() const {
- CHECK(is_dict());
- return const_dict_iterator_proxy(&dict_);
-}
-
-size_t Value::DictSize() const {
- CHECK(is_dict());
- return dict_.size();
-}
-
-bool Value::DictEmpty() const {
- CHECK(is_dict());
- return dict_.empty();
-}
-
-bool Value::GetAsBoolean(bool* out_value) const {
- if (out_value && is_bool()) {
- *out_value = bool_value_;
- return true;
- }
- return is_bool();
-}
-
-bool Value::GetAsInteger(int* out_value) const {
- if (out_value && is_int()) {
- *out_value = int_value_;
- return true;
- }
- return is_int();
-}
-
-bool Value::GetAsString(std::string* out_value) const {
- if (out_value && is_string()) {
- *out_value = string_value_;
- return true;
- }
- return is_string();
-}
-
-bool Value::GetAsString(string16* out_value) const {
- if (out_value && is_string()) {
- *out_value = UTF8ToUTF16(string_value_);
- return true;
- }
- return is_string();
-}
-
-bool Value::GetAsString(const Value** out_value) const {
- if (out_value && is_string()) {
- *out_value = static_cast<const Value*>(this);
- return true;
- }
- return is_string();
-}
-
-bool Value::GetAsString(StringPiece* out_value) const {
- if (out_value && is_string()) {
- *out_value = string_value_;
- return true;
- }
- return is_string();
-}
-
-bool Value::GetAsList(ListValue** out_value) {
- if (out_value && is_list()) {
- *out_value = static_cast<ListValue*>(this);
- return true;
- }
- return is_list();
-}
-
-bool Value::GetAsList(const ListValue** out_value) const {
- if (out_value && is_list()) {
- *out_value = static_cast<const ListValue*>(this);
- return true;
- }
- return is_list();
-}
-
-bool Value::GetAsDictionary(DictionaryValue** out_value) {
- if (out_value && is_dict()) {
- *out_value = static_cast<DictionaryValue*>(this);
- return true;
- }
- return is_dict();
-}
-
-bool Value::GetAsDictionary(const DictionaryValue** out_value) const {
- if (out_value && is_dict()) {
- *out_value = static_cast<const DictionaryValue*>(this);
- return true;
- }
- return is_dict();
-}
-
-Value* Value::DeepCopy() const {
- return new Value(Clone());
-}
-
-std::unique_ptr<Value> Value::CreateDeepCopy() const {
- return std::make_unique<Value>(Clone());
-}
-
-bool operator==(const Value& lhs, const Value& rhs) {
- if (lhs.type_ != rhs.type_)
- return false;
-
- switch (lhs.type_) {
- case Value::Type::NONE:
- return true;
- case Value::Type::BOOLEAN:
- return lhs.bool_value_ == rhs.bool_value_;
- case Value::Type::INTEGER:
- return lhs.int_value_ == rhs.int_value_;
- case Value::Type::STRING:
- return lhs.string_value_ == rhs.string_value_;
- case Value::Type::BINARY:
- return lhs.binary_value_ == rhs.binary_value_;
- // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
- // are completely inlined.
- case Value::Type::DICTIONARY:
- if (lhs.dict_.size() != rhs.dict_.size())
- return false;
- return std::equal(
- std::begin(lhs.dict_), std::end(lhs.dict_), std::begin(rhs.dict_),
- [](const auto& u, const auto& v) {
- return std::tie(u.first, *u.second) == std::tie(v.first, *v.second);
- });
- case Value::Type::LIST:
- return lhs.list_ == rhs.list_;
- }
-
- NOTREACHED();
- return false;
-}
-
-bool operator!=(const Value& lhs, const Value& rhs) {
- return !(lhs == rhs);
-}
-
-bool operator<(const Value& lhs, const Value& rhs) {
- if (lhs.type_ != rhs.type_)
- return lhs.type_ < rhs.type_;
-
- switch (lhs.type_) {
- case Value::Type::NONE:
- return false;
- case Value::Type::BOOLEAN:
- return lhs.bool_value_ < rhs.bool_value_;
- case Value::Type::INTEGER:
- return lhs.int_value_ < rhs.int_value_;
- case Value::Type::STRING:
- return lhs.string_value_ < rhs.string_value_;
- case Value::Type::BINARY:
- return lhs.binary_value_ < rhs.binary_value_;
- // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
- // are completely inlined.
- case Value::Type::DICTIONARY:
- return std::lexicographical_compare(
- std::begin(lhs.dict_), std::end(lhs.dict_), std::begin(rhs.dict_),
- std::end(rhs.dict_),
- [](const Value::DictStorage::value_type& u,
- const Value::DictStorage::value_type& v) {
- return std::tie(u.first, *u.second) < std::tie(v.first, *v.second);
- });
- case Value::Type::LIST:
- return lhs.list_ < rhs.list_;
- }
-
- NOTREACHED();
- return false;
-}
-
-bool operator>(const Value& lhs, const Value& rhs) {
- return rhs < lhs;
-}
-
-bool operator<=(const Value& lhs, const Value& rhs) {
- return !(rhs < lhs);
-}
-
-bool operator>=(const Value& lhs, const Value& rhs) {
- return !(lhs < rhs);
-}
-
-bool Value::Equals(const Value* other) const {
- DCHECK(other);
- return *this == *other;
-}
-
-void Value::InternalMoveConstructFrom(Value&& that) {
- type_ = that.type_;
-
- switch (type_) {
- case Type::NONE:
- return;
- case Type::BOOLEAN:
- bool_value_ = that.bool_value_;
- return;
- case Type::INTEGER:
- int_value_ = that.int_value_;
- return;
- case Type::STRING:
- new (&string_value_) std::string(std::move(that.string_value_));
- return;
- case Type::BINARY:
- new (&binary_value_) BlobStorage(std::move(that.binary_value_));
- return;
- case Type::DICTIONARY:
- new (&dict_) DictStorage(std::move(that.dict_));
- return;
- case Type::LIST:
- new (&list_) ListStorage(std::move(that.list_));
- return;
- }
-}
-
-void Value::InternalCleanup() {
- switch (type_) {
- case Type::NONE:
- case Type::BOOLEAN:
- case Type::INTEGER:
- // Nothing to do
- return;
-
- case Type::STRING:
- string_value_.~basic_string();
- return;
- case Type::BINARY:
- binary_value_.~BlobStorage();
- return;
- case Type::DICTIONARY:
- dict_.~DictStorage();
- return;
- case Type::LIST:
- list_.~ListStorage();
- return;
- }
-}
-
-///////////////////// DictionaryValue ////////////////////
-
-// static
-std::unique_ptr<DictionaryValue> DictionaryValue::From(
- std::unique_ptr<Value> value) {
- DictionaryValue* out;
- if (value && value->GetAsDictionary(&out)) {
- ignore_result(value.release());
- return WrapUnique(out);
- }
- return nullptr;
-}
-
-DictionaryValue::DictionaryValue() : Value(Type::DICTIONARY) {}
-DictionaryValue::DictionaryValue(const DictStorage& in_dict) : Value(in_dict) {}
-DictionaryValue::DictionaryValue(DictStorage&& in_dict) noexcept
- : Value(std::move(in_dict)) {}
-
-bool DictionaryValue::HasKey(StringPiece key) const {
- DCHECK(IsStringUTF8(key));
- auto current_entry = dict_.find(key);
- DCHECK((current_entry == dict_.end()) || current_entry->second);
- return current_entry != dict_.end();
-}
-
-void DictionaryValue::Clear() {
- dict_.clear();
-}
-
-Value* DictionaryValue::Set(StringPiece path, std::unique_ptr<Value> in_value) {
- DCHECK(IsStringUTF8(path));
- DCHECK(in_value);
-
- StringPiece current_path(path);
- Value* current_dictionary = this;
- for (size_t delimiter_position = current_path.find('.');
- delimiter_position != StringPiece::npos;
- delimiter_position = current_path.find('.')) {
- // Assume that we're indexing into a dictionary.
- StringPiece key = current_path.substr(0, delimiter_position);
- Value* child_dictionary =
- current_dictionary->FindKeyOfType(key, Type::DICTIONARY);
- if (!child_dictionary) {
- child_dictionary =
- current_dictionary->SetKey(key, Value(Type::DICTIONARY));
- }
-
- current_dictionary = child_dictionary;
- current_path = current_path.substr(delimiter_position + 1);
- }
-
- return static_cast<DictionaryValue*>(current_dictionary)
- ->SetWithoutPathExpansion(current_path, std::move(in_value));
-}
-
-Value* DictionaryValue::SetBoolean(StringPiece path, bool in_value) {
- return Set(path, std::make_unique<Value>(in_value));
-}
-
-Value* DictionaryValue::SetInteger(StringPiece path, int in_value) {
- return Set(path, std::make_unique<Value>(in_value));
-}
-
-Value* DictionaryValue::SetString(StringPiece path, StringPiece in_value) {
- return Set(path, std::make_unique<Value>(in_value));
-}
-
-Value* DictionaryValue::SetString(StringPiece path, const string16& in_value) {
- return Set(path, std::make_unique<Value>(in_value));
-}
-
-DictionaryValue* DictionaryValue::SetDictionary(
- StringPiece path,
- std::unique_ptr<DictionaryValue> in_value) {
- return static_cast<DictionaryValue*>(Set(path, std::move(in_value)));
-}
-
-ListValue* DictionaryValue::SetList(StringPiece path,
- std::unique_ptr<ListValue> in_value) {
- return static_cast<ListValue*>(Set(path, std::move(in_value)));
-}
-
-Value* DictionaryValue::SetWithoutPathExpansion(
- StringPiece key,
- std::unique_ptr<Value> in_value) {
- // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
- // an explicit conversion from StringPiece to std::string if necessary.
- auto result = dict_.try_emplace(key, std::move(in_value));
- if (!result.second) {
- // in_value is guaranteed to be still intact at this point.
- result.first->second = std::move(in_value);
- }
- return result.first->second.get();
-}
-
-bool DictionaryValue::Get(StringPiece path, const Value** out_value) const {
- DCHECK(IsStringUTF8(path));
- StringPiece current_path(path);
- const DictionaryValue* current_dictionary = this;
- for (size_t delimiter_position = current_path.find('.');
- delimiter_position != std::string::npos;
- delimiter_position = current_path.find('.')) {
- const DictionaryValue* child_dictionary = nullptr;
- if (!current_dictionary->GetDictionaryWithoutPathExpansion(
- current_path.substr(0, delimiter_position), &child_dictionary)) {
- return false;
- }
-
- current_dictionary = child_dictionary;
- current_path = current_path.substr(delimiter_position + 1);
- }
-
- return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
-}
-
-bool DictionaryValue::Get(StringPiece path, Value** out_value) {
- return static_cast<const DictionaryValue&>(*this).Get(
- path, const_cast<const Value**>(out_value));
-}
-
-bool DictionaryValue::GetBoolean(StringPiece path, bool* bool_value) const {
- const Value* value;
- if (!Get(path, &value))
- return false;
-
- return value->GetAsBoolean(bool_value);
-}
-
-bool DictionaryValue::GetInteger(StringPiece path, int* out_value) const {
- const Value* value;
- if (!Get(path, &value))
- return false;
-
- return value->GetAsInteger(out_value);
-}
-
-bool DictionaryValue::GetString(StringPiece path,
- std::string* out_value) const {
- const Value* value;
- if (!Get(path, &value))
- return false;
-
- return value->GetAsString(out_value);
-}
-
-bool DictionaryValue::GetString(StringPiece path, string16* out_value) const {
- const Value* value;
- if (!Get(path, &value))
- return false;
-
- return value->GetAsString(out_value);
-}
-
-bool DictionaryValue::GetStringASCII(StringPiece path,
- std::string* out_value) const {
- std::string out;
- if (!GetString(path, &out))
- return false;
-
- if (!IsStringASCII(out)) {
- NOTREACHED();
- return false;
- }
-
- out_value->assign(out);
- return true;
-}
-
-bool DictionaryValue::GetBinary(StringPiece path,
- const Value** out_value) const {
- const Value* value;
- bool result = Get(path, &value);
- if (!result || !value->is_blob())
- return false;
-
- if (out_value)
- *out_value = value;
-
- return true;
-}
-
-bool DictionaryValue::GetBinary(StringPiece path, Value** out_value) {
- return static_cast<const DictionaryValue&>(*this).GetBinary(
- path, const_cast<const Value**>(out_value));
-}
-
-bool DictionaryValue::GetDictionary(StringPiece path,
- const DictionaryValue** out_value) const {
- const Value* value;
- bool result = Get(path, &value);
- if (!result || !value->is_dict())
- return false;
-
- if (out_value)
- *out_value = static_cast<const DictionaryValue*>(value);
-
- return true;
-}
-
-bool DictionaryValue::GetDictionary(StringPiece path,
- DictionaryValue** out_value) {
- return static_cast<const DictionaryValue&>(*this).GetDictionary(
- path, const_cast<const DictionaryValue**>(out_value));
-}
-
-bool DictionaryValue::GetList(StringPiece path,
- const ListValue** out_value) const {
- const Value* value;
- bool result = Get(path, &value);
- if (!result || !value->is_list())
- return false;
-
- if (out_value)
- *out_value = static_cast<const ListValue*>(value);
-
- return true;
-}
-
-bool DictionaryValue::GetList(StringPiece path, ListValue** out_value) {
- return static_cast<const DictionaryValue&>(*this).GetList(
- path, const_cast<const ListValue**>(out_value));
-}
-
-bool DictionaryValue::GetWithoutPathExpansion(StringPiece key,
- const Value** out_value) const {
- DCHECK(IsStringUTF8(key));
- auto entry_iterator = dict_.find(key);
- if (entry_iterator == dict_.end())
- return false;
-
- if (out_value)
- *out_value = entry_iterator->second.get();
- return true;
-}
-
-bool DictionaryValue::GetWithoutPathExpansion(StringPiece key,
- Value** out_value) {
- return static_cast<const DictionaryValue&>(*this).GetWithoutPathExpansion(
- key, const_cast<const Value**>(out_value));
-}
-
-bool DictionaryValue::GetBooleanWithoutPathExpansion(StringPiece key,
- bool* out_value) const {
- const Value* value;
- if (!GetWithoutPathExpansion(key, &value))
- return false;
-
- return value->GetAsBoolean(out_value);
-}
-
-bool DictionaryValue::GetIntegerWithoutPathExpansion(StringPiece key,
- int* out_value) const {
- const Value* value;
- if (!GetWithoutPathExpansion(key, &value))
- return false;
-
- return value->GetAsInteger(out_value);
-}
-
-bool DictionaryValue::GetStringWithoutPathExpansion(
- StringPiece key,
- std::string* out_value) const {
- const Value* value;
- if (!GetWithoutPathExpansion(key, &value))
- return false;
-
- return value->GetAsString(out_value);
-}
-
-bool DictionaryValue::GetStringWithoutPathExpansion(StringPiece key,
- string16* out_value) const {
- const Value* value;
- if (!GetWithoutPathExpansion(key, &value))
- return false;
-
- return value->GetAsString(out_value);
-}
-
-bool DictionaryValue::GetDictionaryWithoutPathExpansion(
- StringPiece key,
- const DictionaryValue** out_value) const {
- const Value* value;
- bool result = GetWithoutPathExpansion(key, &value);
- if (!result || !value->is_dict())
- return false;
-
- if (out_value)
- *out_value = static_cast<const DictionaryValue*>(value);
-
- return true;
-}
-
-bool DictionaryValue::GetDictionaryWithoutPathExpansion(
- StringPiece key,
- DictionaryValue** out_value) {
- const DictionaryValue& const_this =
- static_cast<const DictionaryValue&>(*this);
- return const_this.GetDictionaryWithoutPathExpansion(
- key, const_cast<const DictionaryValue**>(out_value));
-}
-
-bool DictionaryValue::GetListWithoutPathExpansion(
- StringPiece key,
- const ListValue** out_value) const {
- const Value* value;
- bool result = GetWithoutPathExpansion(key, &value);
- if (!result || !value->is_list())
- return false;
-
- if (out_value)
- *out_value = static_cast<const ListValue*>(value);
-
- return true;
-}
-
-bool DictionaryValue::GetListWithoutPathExpansion(StringPiece key,
- ListValue** out_value) {
- return static_cast<const DictionaryValue&>(*this).GetListWithoutPathExpansion(
- key, const_cast<const ListValue**>(out_value));
-}
-
-bool DictionaryValue::Remove(StringPiece path,
- std::unique_ptr<Value>* out_value) {
- DCHECK(IsStringUTF8(path));
- StringPiece current_path(path);
- DictionaryValue* current_dictionary = this;
- size_t delimiter_position = current_path.rfind('.');
- if (delimiter_position != StringPiece::npos) {
- if (!GetDictionary(current_path.substr(0, delimiter_position),
- &current_dictionary))
- return false;
- current_path = current_path.substr(delimiter_position + 1);
- }
-
- return current_dictionary->RemoveWithoutPathExpansion(current_path,
- out_value);
-}
-
-bool DictionaryValue::RemoveWithoutPathExpansion(
- StringPiece key,
- std::unique_ptr<Value>* out_value) {
- DCHECK(IsStringUTF8(key));
- auto entry_iterator = dict_.find(key);
- if (entry_iterator == dict_.end())
- return false;
-
- if (out_value)
- *out_value = std::move(entry_iterator->second);
- dict_.erase(entry_iterator);
- return true;
-}
-
-bool DictionaryValue::RemovePath(StringPiece path,
- std::unique_ptr<Value>* out_value) {
- bool result = false;
- size_t delimiter_position = path.find('.');
-
- if (delimiter_position == std::string::npos)
- return RemoveWithoutPathExpansion(path, out_value);
-
- StringPiece subdict_path = path.substr(0, delimiter_position);
- DictionaryValue* subdict = nullptr;
- if (!GetDictionary(subdict_path, &subdict))
- return false;
- result = subdict->RemovePath(path.substr(delimiter_position + 1), out_value);
- if (result && subdict->empty())
- RemoveWithoutPathExpansion(subdict_path, nullptr);
-
- return result;
-}
-
-std::unique_ptr<DictionaryValue> DictionaryValue::DeepCopyWithoutEmptyChildren()
- const {
- std::unique_ptr<DictionaryValue> copy =
- CopyDictionaryWithoutEmptyChildren(*this);
- if (!copy)
- copy = std::make_unique<DictionaryValue>();
- return copy;
-}
-
-void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) {
- CHECK(dictionary->is_dict());
- for (DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd(); it.Advance()) {
- const Value* merge_value = &it.value();
- // Check whether we have to merge dictionaries.
- if (merge_value->is_dict()) {
- DictionaryValue* sub_dict;
- if (GetDictionaryWithoutPathExpansion(it.key(), &sub_dict)) {
- sub_dict->MergeDictionary(
- static_cast<const DictionaryValue*>(merge_value));
- continue;
- }
- }
- // All other cases: Make a copy and hook it up.
- SetKey(it.key(), merge_value->Clone());
- }
-}
-
-void DictionaryValue::Swap(DictionaryValue* other) {
- CHECK(other->is_dict());
- dict_.swap(other->dict_);
-}
-
-DictionaryValue::Iterator::Iterator(const DictionaryValue& target)
- : target_(target), it_(target.dict_.begin()) {}
-
-DictionaryValue::Iterator::Iterator(const Iterator& other) = default;
-
-DictionaryValue::Iterator::~Iterator() = default;
-
-DictionaryValue* DictionaryValue::DeepCopy() const {
- return new DictionaryValue(dict_);
-}
-
-std::unique_ptr<DictionaryValue> DictionaryValue::CreateDeepCopy() const {
- return std::make_unique<DictionaryValue>(dict_);
-}
-
-///////////////////// ListValue ////////////////////
-
-// static
-std::unique_ptr<ListValue> ListValue::From(std::unique_ptr<Value> value) {
- ListValue* out;
- if (value && value->GetAsList(&out)) {
- ignore_result(value.release());
- return WrapUnique(out);
- }
- return nullptr;
-}
-
-ListValue::ListValue() : Value(Type::LIST) {}
-ListValue::ListValue(const ListStorage& in_list) : Value(in_list) {}
-ListValue::ListValue(ListStorage&& in_list) noexcept
- : Value(std::move(in_list)) {}
-
-void ListValue::Clear() {
- list_.clear();
-}
-
-void ListValue::Reserve(size_t n) {
- list_.reserve(n);
-}
-
-bool ListValue::Set(size_t index, std::unique_ptr<Value> in_value) {
- if (!in_value)
- return false;
-
- if (index >= list_.size())
- list_.resize(index + 1);
-
- list_[index] = std::move(*in_value);
- return true;
-}
-
-bool ListValue::Get(size_t index, const Value** out_value) const {
- if (index >= list_.size())
- return false;
-
- if (out_value)
- *out_value = &list_[index];
-
- return true;
-}
-
-bool ListValue::Get(size_t index, Value** out_value) {
- return static_cast<const ListValue&>(*this).Get(
- index, const_cast<const Value**>(out_value));
-}
-
-bool ListValue::GetBoolean(size_t index, bool* bool_value) const {
- const Value* value;
- if (!Get(index, &value))
- return false;
-
- return value->GetAsBoolean(bool_value);
-}
-
-bool ListValue::GetInteger(size_t index, int* out_value) const {
- const Value* value;
- if (!Get(index, &value))
- return false;
-
- return value->GetAsInteger(out_value);
-}
-
-bool ListValue::GetString(size_t index, std::string* out_value) const {
- const Value* value;
- if (!Get(index, &value))
- return false;
-
- return value->GetAsString(out_value);
-}
-
-bool ListValue::GetString(size_t index, string16* out_value) const {
- const Value* value;
- if (!Get(index, &value))
- return false;
-
- return value->GetAsString(out_value);
-}
-
-bool ListValue::GetDictionary(size_t index,
- const DictionaryValue** out_value) const {
- const Value* value;
- bool result = Get(index, &value);
- if (!result || !value->is_dict())
- return false;
-
- if (out_value)
- *out_value = static_cast<const DictionaryValue*>(value);
-
- return true;
-}
-
-bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) {
- return static_cast<const ListValue&>(*this).GetDictionary(
- index, const_cast<const DictionaryValue**>(out_value));
-}
-
-bool ListValue::GetList(size_t index, const ListValue** out_value) const {
- const Value* value;
- bool result = Get(index, &value);
- if (!result || !value->is_list())
- return false;
-
- if (out_value)
- *out_value = static_cast<const ListValue*>(value);
-
- return true;
-}
-
-bool ListValue::GetList(size_t index, ListValue** out_value) {
- return static_cast<const ListValue&>(*this).GetList(
- index, const_cast<const ListValue**>(out_value));
-}
-
-bool ListValue::Remove(size_t index, std::unique_ptr<Value>* out_value) {
- if (index >= list_.size())
- return false;
-
- if (out_value)
- *out_value = std::make_unique<Value>(std::move(list_[index]));
-
- list_.erase(list_.begin() + index);
- return true;
-}
-
-bool ListValue::Remove(const Value& value, size_t* index) {
- auto it = std::find(list_.begin(), list_.end(), value);
-
- if (it == list_.end())
- return false;
-
- if (index)
- *index = std::distance(list_.begin(), it);
-
- list_.erase(it);
- return true;
-}
-
-ListValue::iterator ListValue::Erase(iterator iter,
- std::unique_ptr<Value>* out_value) {
- if (out_value)
- *out_value = std::make_unique<Value>(std::move(*iter));
-
- return list_.erase(iter);
-}
-
-void ListValue::Append(std::unique_ptr<Value> in_value) {
- list_.push_back(std::move(*in_value));
-}
-
-void ListValue::AppendBoolean(bool in_value) {
- list_.emplace_back(in_value);
-}
-
-void ListValue::AppendInteger(int in_value) {
- list_.emplace_back(in_value);
-}
-
-void ListValue::AppendString(StringPiece in_value) {
- list_.emplace_back(in_value);
-}
-
-void ListValue::AppendString(const string16& in_value) {
- list_.emplace_back(in_value);
-}
-
-void ListValue::AppendStrings(const std::vector<std::string>& in_values) {
- list_.reserve(list_.size() + in_values.size());
- for (const auto& in_value : in_values)
- list_.emplace_back(in_value);
-}
-
-void ListValue::AppendStrings(const std::vector<string16>& in_values) {
- list_.reserve(list_.size() + in_values.size());
- for (const auto& in_value : in_values)
- list_.emplace_back(in_value);
-}
-
-bool ListValue::AppendIfNotPresent(std::unique_ptr<Value> in_value) {
- DCHECK(in_value);
- if (ContainsValue(list_, *in_value))
- return false;
-
- list_.push_back(std::move(*in_value));
- return true;
-}
-
-bool ListValue::Insert(size_t index, std::unique_ptr<Value> in_value) {
- DCHECK(in_value);
- if (index > list_.size())
- return false;
-
- list_.insert(list_.begin() + index, std::move(*in_value));
- return true;
-}
-
-ListValue::const_iterator ListValue::Find(const Value& value) const {
- return std::find(list_.begin(), list_.end(), value);
-}
-
-void ListValue::Swap(ListValue* other) {
- CHECK(other->is_list());
- list_.swap(other->list_);
-}
-
-ListValue* ListValue::DeepCopy() const {
- return new ListValue(list_);
-}
-
-std::unique_ptr<ListValue> ListValue::CreateDeepCopy() const {
- return std::make_unique<ListValue>(list_);
-}
-
-ValueSerializer::~ValueSerializer() = default;
-
-ValueDeserializer::~ValueDeserializer() = default;
-
-std::ostream& operator<<(std::ostream& out, const Value& value) {
- std::string json;
- JSONWriter::WriteWithOptions(value, JSONWriter::OPTIONS_PRETTY_PRINT, &json);
- return out << json;
-}
-
-std::ostream& operator<<(std::ostream& out, const Value::Type& type) {
- if (static_cast<int>(type) < 0 ||
- static_cast<size_t>(type) >= arraysize(kTypeNames))
- return out << "Invalid Type (index = " << static_cast<int>(type) << ")";
- return out << Value::GetTypeName(type);
-}
-
-} // namespace base
diff --git a/gn/base/values.h b/gn/base/values.h
deleted file mode 100644
index c3ec875fa4b..00000000000
--- a/gn/base/values.h
+++ /dev/null
@@ -1,757 +0,0 @@
-// 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 specifies a recursive data storage class called Value intended for
-// storing settings and other persistable data.
-//
-// A Value represents something that can be stored in JSON or passed to/from
-// JavaScript. As such, it is NOT a generalized variant type, since only the
-// types supported by JavaScript/JSON are supported.
-//
-// IN PARTICULAR this means that there is no support for int64_t or unsigned
-// numbers. Writing JSON with such types would violate the spec. If you need
-// something like this, make a string value containing the number you want.
-//
-// NOTE: A Value parameter that is always a Value::STRING should just be passed
-// as a std::string. Similarly for Values that are always Value::DICTIONARY
-// (should be flat_map), Value::LIST (should be std::vector), et cetera.
-
-#ifndef BASE_VALUES_H_
-#define BASE_VALUES_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <iosfwd>
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/containers/flat_map.h"
-#include "base/containers/span.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "base/value_iterators.h"
-
-namespace base {
-
-class DictionaryValue;
-class ListValue;
-class Value;
-
-// The Value class is the base class for Values. A Value can be instantiated
-// via passing the appropriate type or backing storage to the constructor.
-//
-// See the file-level comment above for more information.
-//
-// base::Value is currently in the process of being refactored. Design doc:
-// https://docs.google.com/document/d/1uDLu5uTRlCWePxQUEHc8yNQdEoE1BDISYdpggWEABnw
-//
-// Previously (which is how most code that currently exists is written), Value
-// used derived types to implement the individual data types, and base::Value
-// was just a base class to refer to them. This required everything be heap
-// allocated.
-//
-// OLD WAY:
-//
-// std::unique_ptr<base::Value> GetFoo() {
-// std::unique_ptr<DictionaryValue> dict;
-// dict->SetString("mykey", foo);
-// return dict;
-// }
-//
-// The new design makes base::Value a variant type that holds everything in
-// a union. It is now recommended to pass by value with std::move rather than
-// use heap allocated values. The DictionaryValue and ListValue subclasses
-// exist only as a compatibility shim that we're in the process of removing.
-//
-// NEW WAY:
-//
-// base::Value GetFoo() {
-// base::Value dict(base::Value::Type::DICTIONARY);
-// dict.SetKey("mykey", base::Value(foo));
-// return dict;
-// }
-class Value {
- public:
- using BlobStorage = std::vector<char>;
- using DictStorage = flat_map<std::string, std::unique_ptr<Value>>;
- using ListStorage = std::vector<Value>;
-
- enum class Type {
- NONE = 0,
- BOOLEAN,
- INTEGER,
- STRING,
- BINARY,
- DICTIONARY,
- LIST
- // Note: Do not add more types. See the file-level comment above for why.
- };
-
- // For situations where you want to keep ownership of your buffer, this
- // factory method creates a new BinaryValue by copying the contents of the
- // buffer that's passed in.
- // DEPRECATED, use std::make_unique<Value>(const BlobStorage&) instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- static std::unique_ptr<Value> CreateWithCopiedBuffer(const char* buffer,
- size_t size);
-
- // Adaptors for converting from the old way to the new way and vice versa.
- static Value FromUniquePtrValue(std::unique_ptr<Value> val);
- static std::unique_ptr<Value> ToUniquePtrValue(Value val);
-
- Value(Value&& that) noexcept;
- Value() noexcept; // A null value.
-
- // Value's copy constructor and copy assignment operator are deleted. Use this
- // to obtain a deep copy explicitly.
- Value Clone() const;
-
- explicit Value(Type type);
- explicit Value(bool in_bool);
- explicit Value(int in_int);
-
- // Value(const char*) and Value(const char16*) are required despite
- // Value(StringPiece) and Value(StringPiece16) because otherwise the
- // compiler will choose the Value(bool) constructor for these arguments.
- // Value(std::string&&) allow for efficient move construction.
- explicit Value(const char* in_string);
- explicit Value(StringPiece in_string);
- explicit Value(std::string&& in_string) noexcept;
- explicit Value(const char16* in_string16);
- explicit Value(StringPiece16 in_string16);
-
- explicit Value(const BlobStorage& in_blob);
- explicit Value(BlobStorage&& in_blob) noexcept;
-
- explicit Value(const DictStorage& in_dict);
- explicit Value(DictStorage&& in_dict) noexcept;
-
- explicit Value(const ListStorage& in_list);
- explicit Value(ListStorage&& in_list) noexcept;
-
- Value& operator=(Value&& that) noexcept;
-
- ~Value();
-
- // Returns the name for a given |type|.
- static const char* GetTypeName(Type type);
-
- // Returns the type of the value stored by the current Value object.
- Type type() const { return type_; }
-
- // Returns true if the current object represents a given type.
- bool is_none() const { return type() == Type::NONE; }
- bool is_bool() const { return type() == Type::BOOLEAN; }
- bool is_int() const { return type() == Type::INTEGER; }
- bool is_string() const { return type() == Type::STRING; }
- bool is_blob() const { return type() == Type::BINARY; }
- bool is_dict() const { return type() == Type::DICTIONARY; }
- bool is_list() const { return type() == Type::LIST; }
-
- // These will all fatally assert if the type doesn't match.
- bool GetBool() const;
- int GetInt() const;
- const std::string& GetString() const;
- const BlobStorage& GetBlob() const;
-
- ListStorage& GetList();
- const ListStorage& GetList() const;
-
- // |FindKey| looks up |key| in the underlying dictionary. If found, it returns
- // a pointer to the element. Otherwise it returns nullptr.
- // returned. Callers are expected to perform a check against null before using
- // the pointer.
- // Note: This fatally asserts if type() is not Type::DICTIONARY.
- //
- // Example:
- // auto* found = FindKey("foo");
- Value* FindKey(StringPiece key);
- const Value* FindKey(StringPiece key) const;
-
- // |FindKeyOfType| is similar to |FindKey|, but it also requires the found
- // value to have type |type|. If no type is found, or the found value is of a
- // different type nullptr is returned.
- // Callers are expected to perform a check against null before using the
- // pointer.
- // Note: This fatally asserts if type() is not Type::DICTIONARY.
- //
- // Example:
- // auto* found = FindKey("foo", Type::INTEGER);
- Value* FindKeyOfType(StringPiece key, Type type);
- const Value* FindKeyOfType(StringPiece key, Type type) const;
-
- // |SetKey| looks up |key| in the underlying dictionary and sets the mapped
- // value to |value|. If |key| could not be found, a new element is inserted.
- // A pointer to the modified item is returned.
- // Note: This fatally asserts if type() is not Type::DICTIONARY.
- //
- // Example:
- // SetKey("foo", std::move(myvalue));
- Value* SetKey(StringPiece key, Value value);
- // This overload results in a performance improvement for std::string&&.
- Value* SetKey(std::string&& key, Value value);
- // This overload is necessary to avoid ambiguity for const char* arguments.
- Value* SetKey(const char* key, Value value);
-
- // This attemps to remove the value associated with |key|. In case of failure,
- // e.g. the key does not exist, |false| is returned and the underlying
- // dictionary is not changed. In case of success, |key| is deleted from the
- // dictionary and the method returns |true|.
- // Note: This fatally asserts if type() is not Type::DICTIONARY.
- //
- // Example:
- // bool success = RemoveKey("foo");
- bool RemoveKey(StringPiece key);
-
- // Searches a hierarchy of dictionary values for a given value. If a path
- // of dictionaries exist, returns the item at that path. If any of the path
- // components do not exist or if any but the last path components are not
- // dictionaries, returns nullptr.
- //
- // The type of the leaf Value is not checked.
- //
- // Implementation note: This can't return an iterator because the iterator
- // will actually be into another Value, so it can't be compared to iterators
- // from this one (in particular, the DictItems().end() iterator).
- //
- // Example:
- // auto* found = FindPath({"foo", "bar"});
- //
- // std::vector<StringPiece> components = ...
- // auto* found = FindPath(components);
- //
- // Note: If there is only one component in the path, use FindKey() instead.
- Value* FindPath(std::initializer_list<StringPiece> path);
- Value* FindPath(span<const StringPiece> path);
- const Value* FindPath(std::initializer_list<StringPiece> path) const;
- const Value* FindPath(span<const StringPiece> path) const;
-
- // Like FindPath() but will only return the value if the leaf Value type
- // matches the given type. Will return nullptr otherwise.
- //
- // Note: If there is only one component in the path, use FindKeyOfType()
- // instead.
- Value* FindPathOfType(std::initializer_list<StringPiece> path, Type type);
- Value* FindPathOfType(span<const StringPiece> path, Type type);
- const Value* FindPathOfType(std::initializer_list<StringPiece> path,
- Type type) const;
- const Value* FindPathOfType(span<const StringPiece> path, Type type) const;
-
- // Sets the given path, expanding and creating dictionary keys as necessary.
- //
- // If the current value is not a dictionary, the function returns nullptr. If
- // path components do not exist, they will be created. If any but the last
- // components matches a value that is not a dictionary, the function will fail
- // (it will not overwrite the value) and return nullptr. The last path
- // component will be unconditionally overwritten if it exists, and created if
- // it doesn't.
- //
- // Example:
- // value.SetPath({"foo", "bar"}, std::move(myvalue));
- //
- // std::vector<StringPiece> components = ...
- // value.SetPath(components, std::move(myvalue));
- //
- // Note: If there is only one component in the path, use SetKey() instead.
- Value* SetPath(std::initializer_list<StringPiece> path, Value value);
- Value* SetPath(span<const StringPiece> path, Value value);
-
- // Tries to remove a Value at the given path.
- //
- // If the current value is not a dictionary or any path components does not
- // exist, this operation fails, leaves underlying Values untouched and returns
- // |false|. In case intermediate dictionaries become empty as a result of this
- // path removal, they will be removed as well.
- //
- // Example:
- // bool success = value.RemovePath({"foo", "bar"});
- //
- // std::vector<StringPiece> components = ...
- // bool success = value.RemovePath(components);
- //
- // Note: If there is only one component in the path, use RemoveKey() instead.
- bool RemovePath(std::initializer_list<StringPiece> path);
- bool RemovePath(span<const StringPiece> path);
-
- using dict_iterator_proxy = detail::dict_iterator_proxy;
- using const_dict_iterator_proxy = detail::const_dict_iterator_proxy;
-
- // |DictItems| returns a proxy object that exposes iterators to the underlying
- // dictionary. These are intended for iteration over all items in the
- // dictionary and are compatible with for-each loops and standard library
- // algorithms.
- // Note: This fatally asserts if type() is not Type::DICTIONARY.
- dict_iterator_proxy DictItems();
- const_dict_iterator_proxy DictItems() const;
-
- // Returns the size of the dictionary, and if the dictionary is empty.
- // Note: This fatally asserts if type() is not Type::DICTIONARY.
- size_t DictSize() const;
- bool DictEmpty() const;
-
- // These methods allow the convenient retrieval of the contents of the Value.
- // If the current object can be converted into the given type, the value is
- // returned through the |out_value| parameter and true is returned;
- // otherwise, false is returned and |out_value| is unchanged.
- // DEPRECATED, use GetBool() instead.
- bool GetAsBoolean(bool* out_value) const;
- // DEPRECATED, use GetInt() instead.
- bool GetAsInteger(int* out_value) const;
- // DEPRECATED, use GetString() instead.
- bool GetAsString(std::string* out_value) const;
- bool GetAsString(string16* out_value) const;
- bool GetAsString(const Value** out_value) const;
- bool GetAsString(StringPiece* out_value) const;
- // ListValue::From is the equivalent for std::unique_ptr conversions.
- // DEPRECATED, use GetList() instead.
- bool GetAsList(ListValue** out_value);
- bool GetAsList(const ListValue** out_value) const;
- // DictionaryValue::From is the equivalent for std::unique_ptr conversions.
- bool GetAsDictionary(DictionaryValue** out_value);
- bool GetAsDictionary(const DictionaryValue** out_value) const;
- // Note: Do not add more types. See the file-level comment above for why.
-
- // This creates a deep copy of the entire Value tree, and returns a pointer
- // to the copy. The caller gets ownership of the copy, of course.
- // Subclasses return their own type directly in their overrides;
- // this works because C++ supports covariant return types.
- // DEPRECATED, use Value::Clone() instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- Value* DeepCopy() const;
- // DEPRECATED, use Value::Clone() instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- std::unique_ptr<Value> CreateDeepCopy() const;
-
- // Comparison operators so that Values can easily be used with standard
- // library algorithms and associative containers.
- friend bool operator==(const Value& lhs, const Value& rhs);
- friend bool operator!=(const Value& lhs, const Value& rhs);
- friend bool operator<(const Value& lhs, const Value& rhs);
- friend bool operator>(const Value& lhs, const Value& rhs);
- friend bool operator<=(const Value& lhs, const Value& rhs);
- friend bool operator>=(const Value& lhs, const Value& rhs);
-
- // Compares if two Value objects have equal contents.
- // DEPRECATED, use operator==(const Value& lhs, const Value& rhs) instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- bool Equals(const Value* other) const;
-
- // Estimates dynamic memory usage.
- // See base/trace_event/memory_usage_estimator.h for more info.
- size_t EstimateMemoryUsage() const;
-
- protected:
- // TODO(crbug.com/646113): Make these private once DictionaryValue and
- // ListValue are properly inlined.
- Type type_;
-
- union {
- bool bool_value_;
- int int_value_;
- std::string string_value_;
- BlobStorage binary_value_;
- DictStorage dict_;
- ListStorage list_;
- };
-
- private:
- void InternalMoveConstructFrom(Value&& that);
- void InternalCleanup();
-
- DISALLOW_COPY_AND_ASSIGN(Value);
-};
-
-// DictionaryValue provides a key-value dictionary with (optional) "path"
-// parsing for recursive access; see the comment at the top of the file. Keys
-// are |std::string|s and should be UTF-8 encoded.
-class DictionaryValue : public Value {
- public:
- using const_iterator = DictStorage::const_iterator;
- using iterator = DictStorage::iterator;
-
- // Returns |value| if it is a dictionary, nullptr otherwise.
- static std::unique_ptr<DictionaryValue> From(std::unique_ptr<Value> value);
-
- DictionaryValue();
- explicit DictionaryValue(const DictStorage& in_dict);
- explicit DictionaryValue(DictStorage&& in_dict) noexcept;
-
- // Returns true if the current dictionary has a value for the given key.
- // DEPRECATED, use Value::FindKey(key) instead.
- bool HasKey(StringPiece key) const;
-
- // Returns the number of Values in this dictionary.
- size_t size() const { return dict_.size(); }
-
- // Returns whether the dictionary is empty.
- bool empty() const { return dict_.empty(); }
-
- // Clears any current contents of this dictionary.
- void Clear();
-
- // Sets the Value associated with the given path starting from this object.
- // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
- // into the next DictionaryValue down. Obviously, "." can't be used
- // within a key, but there are no other restrictions on keys.
- // If the key at any step of the way doesn't exist, or exists but isn't
- // a DictionaryValue, a new DictionaryValue will be created and attached
- // to the path in that location. |in_value| must be non-null.
- // Returns a pointer to the inserted value.
- // DEPRECATED, use Value::SetPath(path, value) instead.
- Value* Set(StringPiece path, std::unique_ptr<Value> in_value);
-
- // Convenience forms of Set(). These methods will replace any existing
- // value at that path, even if it has a different type.
- // DEPRECATED, use Value::SetPath(path, Value(bool)) instead.
- Value* SetBoolean(StringPiece path, bool in_value);
- // DEPRECATED, use Value::SetPath(path, Value(int)) instead.
- Value* SetInteger(StringPiece path, int in_value);
- // DEPRECATED, use Value::SetPath(path, Value(StringPiece)) instead.
- Value* SetString(StringPiece path, StringPiece in_value);
- // DEPRECATED, use Value::SetPath(path, Value(const string& 16)) instead.
- Value* SetString(StringPiece path, const string16& in_value);
- // DEPRECATED, use Value::SetPath(path, Value(Type::DICTIONARY)) instead.
- DictionaryValue* SetDictionary(StringPiece path,
- std::unique_ptr<DictionaryValue> in_value);
- // DEPRECATED, use Value::SetPath(path, Value(Type::LIST)) instead.
- ListValue* SetList(StringPiece path, std::unique_ptr<ListValue> in_value);
-
- // Like Set(), but without special treatment of '.'. This allows e.g. URLs to
- // be used as paths.
- // DEPRECATED, use Value::SetKey(key, value) instead.
- Value* SetWithoutPathExpansion(StringPiece key,
- std::unique_ptr<Value> in_value);
-
- // Gets the Value associated with the given path starting from this object.
- // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
- // into the next DictionaryValue down. If the path can be resolved
- // successfully, the value for the last key in the path will be returned
- // through the |out_value| parameter, and the function will return true.
- // Otherwise, it will return false and |out_value| will be untouched.
- // Note that the dictionary always owns the value that's returned.
- // |out_value| is optional and will only be set if non-NULL.
- // DEPRECATED, use Value::FindPath(path) instead.
- bool Get(StringPiece path, const Value** out_value) const;
- // DEPRECATED, use Value::FindPath(path) instead.
- bool Get(StringPiece path, Value** out_value);
-
- // These are convenience forms of Get(). The value will be retrieved
- // and the return value will be true if the path is valid and the value at
- // the end of the path can be returned in the form specified.
- // |out_value| is optional and will only be set if non-NULL.
- // DEPRECATED, use Value::FindPath(path) and Value::GetBool() instead.
- bool GetBoolean(StringPiece path, bool* out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetInt() instead.
- bool GetInteger(StringPiece path, int* out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetString() instead.
- bool GetString(StringPiece path, std::string* out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetString() instead.
- bool GetString(StringPiece path, string16* out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetString() instead.
- bool GetStringASCII(StringPiece path, std::string* out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetBlob() instead.
- bool GetBinary(StringPiece path, const Value** out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetBlob() instead.
- bool GetBinary(StringPiece path, Value** out_value);
- // DEPRECATED, use Value::FindPath(path) and Value's Dictionary API instead.
- bool GetDictionary(StringPiece path, const DictionaryValue** out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value's Dictionary API instead.
- bool GetDictionary(StringPiece path, DictionaryValue** out_value);
- // DEPRECATED, use Value::FindPath(path) and Value::GetList() instead.
- bool GetList(StringPiece path, const ListValue** out_value) const;
- // DEPRECATED, use Value::FindPath(path) and Value::GetList() instead.
- bool GetList(StringPiece path, ListValue** out_value);
-
- // Like Get(), but without special treatment of '.'. This allows e.g. URLs to
- // be used as paths.
- // DEPRECATED, use Value::FindKey(key) instead.
- bool GetWithoutPathExpansion(StringPiece key, const Value** out_value) const;
- // DEPRECATED, use Value::FindKey(key) instead.
- bool GetWithoutPathExpansion(StringPiece key, Value** out_value);
- // DEPRECATED, use Value::FindKey(key) and Value::GetBool() instead.
- bool GetBooleanWithoutPathExpansion(StringPiece key, bool* out_value) const;
- // DEPRECATED, use Value::FindKey(key) and Value::GetInt() instead.
- bool GetIntegerWithoutPathExpansion(StringPiece key, int* out_value) const;
- // DEPRECATED, use Value::FindKey(key) and Value::GetString() instead.
- bool GetStringWithoutPathExpansion(StringPiece key,
- std::string* out_value) const;
- // DEPRECATED, use Value::FindKey(key) and Value::GetString() instead.
- bool GetStringWithoutPathExpansion(StringPiece key,
- string16* out_value) const;
- // DEPRECATED, use Value::FindKey(key) and Value's Dictionary API instead.
- bool GetDictionaryWithoutPathExpansion(
- StringPiece key,
- const DictionaryValue** out_value) const;
- // DEPRECATED, use Value::FindKey(key) and Value's Dictionary API instead.
- bool GetDictionaryWithoutPathExpansion(StringPiece key,
- DictionaryValue** out_value);
- // DEPRECATED, use Value::FindKey(key) and Value::GetList() instead.
- bool GetListWithoutPathExpansion(StringPiece key,
- const ListValue** out_value) const;
- // DEPRECATED, use Value::FindKey(key) and Value::GetList() instead.
- bool GetListWithoutPathExpansion(StringPiece key, ListValue** out_value);
-
- // Removes the Value with the specified path from this dictionary (or one
- // of its child dictionaries, if the path is more than just a local key).
- // If |out_value| is non-NULL, the removed Value will be passed out via
- // |out_value|. If |out_value| is NULL, the removed value will be deleted.
- // This method returns true if |path| is a valid path; otherwise it will
- // return false and the DictionaryValue object will be unchanged.
- // DEPRECATED, use Value::RemovePath(path) instead.
- bool Remove(StringPiece path, std::unique_ptr<Value>* out_value);
-
- // Like Remove(), but without special treatment of '.'. This allows e.g. URLs
- // to be used as paths.
- // DEPRECATED, use Value::RemoveKey(key) instead.
- bool RemoveWithoutPathExpansion(StringPiece key,
- std::unique_ptr<Value>* out_value);
-
- // Removes a path, clearing out all dictionaries on |path| that remain empty
- // after removing the value at |path|.
- // DEPRECATED, use Value::RemovePath(path) instead.
- bool RemovePath(StringPiece path, std::unique_ptr<Value>* out_value);
-
- using Value::RemovePath; // DictionaryValue::RemovePath shadows otherwise.
-
- // Makes a copy of |this| but doesn't include empty dictionaries and lists in
- // the copy. This never returns NULL, even if |this| itself is empty.
- std::unique_ptr<DictionaryValue> DeepCopyWithoutEmptyChildren() const;
-
- // Merge |dictionary| into this dictionary. This is done recursively, i.e. any
- // sub-dictionaries will be merged as well. In case of key collisions, the
- // passed in dictionary takes precedence and data already present will be
- // replaced. Values within |dictionary| are deep-copied, so |dictionary| may
- // be freed any time after this call.
- void MergeDictionary(const DictionaryValue* dictionary);
-
- // Swaps contents with the |other| dictionary.
- void Swap(DictionaryValue* other);
-
- // This class provides an iterator over both keys and values in the
- // dictionary. It can't be used to modify the dictionary.
- // DEPRECATED, use Value::DictItems() instead.
- class Iterator {
- public:
- explicit Iterator(const DictionaryValue& target);
- Iterator(const Iterator& other);
- ~Iterator();
-
- bool IsAtEnd() const { return it_ == target_.dict_.end(); }
- void Advance() { ++it_; }
-
- const std::string& key() const { return it_->first; }
- const Value& value() const { return *it_->second; }
-
- private:
- const DictionaryValue& target_;
- DictStorage::const_iterator it_;
- };
-
- // Iteration.
- // DEPRECATED, use Value::DictItems() instead.
- iterator begin() { return dict_.begin(); }
- iterator end() { return dict_.end(); }
-
- // DEPRECATED, use Value::DictItems() instead.
- const_iterator begin() const { return dict_.begin(); }
- const_iterator end() const { return dict_.end(); }
-
- // DEPRECATED, use Value::Clone() instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- DictionaryValue* DeepCopy() const;
- // DEPRECATED, use Value::Clone() instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- std::unique_ptr<DictionaryValue> CreateDeepCopy() const;
-};
-
-// This type of Value represents a list of other Value values.
-class ListValue : public Value {
- public:
- using const_iterator = ListStorage::const_iterator;
- using iterator = ListStorage::iterator;
-
- // Returns |value| if it is a list, nullptr otherwise.
- static std::unique_ptr<ListValue> From(std::unique_ptr<Value> value);
-
- ListValue();
- explicit ListValue(const ListStorage& in_list);
- explicit ListValue(ListStorage&& in_list) noexcept;
-
- // Clears the contents of this ListValue
- // DEPRECATED, use GetList()::clear() instead.
- void Clear();
-
- // Returns the number of Values in this list.
- // DEPRECATED, use GetList()::size() instead.
- size_t GetSize() const { return list_.size(); }
-
- // Returns whether the list is empty.
- // DEPRECATED, use GetList()::empty() instead.
- bool empty() const { return list_.empty(); }
-
- // Reserves storage for at least |n| values.
- // DEPRECATED, use GetList()::reserve() instead.
- void Reserve(size_t n);
-
- // Sets the list item at the given index to be the Value specified by
- // the value given. If the index beyond the current end of the list, null
- // Values will be used to pad out the list.
- // Returns true if successful, or false if the index was negative or
- // the value is a null pointer.
- // DEPRECATED, use GetList()::operator[] instead.
- bool Set(size_t index, std::unique_ptr<Value> in_value);
-
- // Gets the Value at the given index. Modifies |out_value| (and returns true)
- // only if the index falls within the current list range.
- // Note that the list always owns the Value passed out via |out_value|.
- // |out_value| is optional and will only be set if non-NULL.
- // DEPRECATED, use GetList()::operator[] instead.
- bool Get(size_t index, const Value** out_value) const;
- bool Get(size_t index, Value** out_value);
-
- // Convenience forms of Get(). Modifies |out_value| (and returns true)
- // only if the index is valid and the Value at that index can be returned
- // in the specified form.
- // |out_value| is optional and will only be set if non-NULL.
- // DEPRECATED, use GetList()::operator[]::GetBool() instead.
- bool GetBoolean(size_t index, bool* out_value) const;
- // DEPRECATED, use GetList()::operator[]::GetInt() instead.
- bool GetInteger(size_t index, int* out_value) const;
- // DEPRECATED, use GetList()::operator[]::GetString() instead.
- bool GetString(size_t index, std::string* out_value) const;
- bool GetString(size_t index, string16* out_value) const;
-
- bool GetDictionary(size_t index, const DictionaryValue** out_value) const;
- bool GetDictionary(size_t index, DictionaryValue** out_value);
-
- using Value::GetList;
- // DEPRECATED, use GetList()::operator[]::GetList() instead.
- bool GetList(size_t index, const ListValue** out_value) const;
- bool GetList(size_t index, ListValue** out_value);
-
- // Removes the Value with the specified index from this list.
- // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
- // passed out via |out_value|. If |out_value| is NULL, the removed value will
- // be deleted. This method returns true if |index| is valid; otherwise
- // it will return false and the ListValue object will be unchanged.
- // DEPRECATED, use GetList()::erase() instead.
- bool Remove(size_t index, std::unique_ptr<Value>* out_value);
-
- // Removes the first instance of |value| found in the list, if any, and
- // deletes it. |index| is the location where |value| was found. Returns false
- // if not found.
- // DEPRECATED, use GetList()::erase() instead.
- bool Remove(const Value& value, size_t* index);
-
- // Removes the element at |iter|. If |out_value| is NULL, the value will be
- // deleted, otherwise ownership of the value is passed back to the caller.
- // Returns an iterator pointing to the location of the element that
- // followed the erased element.
- // DEPRECATED, use GetList()::erase() instead.
- iterator Erase(iterator iter, std::unique_ptr<Value>* out_value);
-
- // Appends a Value to the end of the list.
- // DEPRECATED, use GetList()::push_back() instead.
- void Append(std::unique_ptr<Value> in_value);
-
- // Convenience forms of Append.
- // DEPRECATED, use GetList()::emplace_back() instead.
- void AppendBoolean(bool in_value);
- void AppendInteger(int in_value);
- void AppendString(StringPiece in_value);
- void AppendString(const string16& in_value);
- // DEPRECATED, use GetList()::emplace_back() in a loop instead.
- void AppendStrings(const std::vector<std::string>& in_values);
- void AppendStrings(const std::vector<string16>& in_values);
-
- // Appends a Value if it's not already present. Returns true if successful,
- // or false if the value was already
- // DEPRECATED, use std::find() with GetList()::push_back() instead.
- bool AppendIfNotPresent(std::unique_ptr<Value> in_value);
-
- // Insert a Value at index.
- // Returns true if successful, or false if the index was out of range.
- // DEPRECATED, use GetList()::insert() instead.
- bool Insert(size_t index, std::unique_ptr<Value> in_value);
-
- // Searches for the first instance of |value| in the list using the Equals
- // method of the Value type.
- // Returns a const_iterator to the found item or to end() if none exists.
- // DEPRECATED, use std::find() instead.
- const_iterator Find(const Value& value) const;
-
- // Swaps contents with the |other| list.
- // DEPRECATED, use GetList()::swap() instead.
- void Swap(ListValue* other);
-
- // Iteration.
- // DEPRECATED, use GetList()::begin() instead.
- iterator begin() { return list_.begin(); }
- // DEPRECATED, use GetList()::end() instead.
- iterator end() { return list_.end(); }
-
- // DEPRECATED, use GetList()::begin() instead.
- const_iterator begin() const { return list_.begin(); }
- // DEPRECATED, use GetList()::end() instead.
- const_iterator end() const { return list_.end(); }
-
- // DEPRECATED, use Value::Clone() instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- ListValue* DeepCopy() const;
- // DEPRECATED, use Value::Clone() instead.
- // TODO(crbug.com/646113): Delete this and migrate callsites.
- std::unique_ptr<ListValue> CreateDeepCopy() const;
-};
-
-// This interface is implemented by classes that know how to serialize
-// Value objects.
-class ValueSerializer {
- public:
- virtual ~ValueSerializer();
-
- virtual bool Serialize(const Value& root) = 0;
-};
-
-// This interface is implemented by classes that know how to deserialize Value
-// objects.
-class ValueDeserializer {
- public:
- virtual ~ValueDeserializer();
-
- // This method deserializes the subclass-specific format into a Value object.
- // If the return value is non-NULL, the caller takes ownership of returned
- // Value. If the return value is NULL, and if error_code is non-NULL,
- // error_code will be set with the underlying error.
- // If |error_message| is non-null, it will be filled in with a formatted
- // error message including the location of the error if appropriate.
- virtual std::unique_ptr<Value> Deserialize(int* error_code,
- std::string* error_str) = 0;
-};
-
-// Stream operator so Values can be used in assertion statements. In order that
-// gtest uses this operator to print readable output on test failures, we must
-// override each specific type. Otherwise, the default template implementation
-// is preferred over an upcast.
-std::ostream& operator<<(std::ostream& out, const Value& value);
-
-inline std::ostream& operator<<(std::ostream& out,
- const DictionaryValue& value) {
- return out << static_cast<const Value&>(value);
-}
-
-inline std::ostream& operator<<(std::ostream& out, const ListValue& value) {
- return out << static_cast<const Value&>(value);
-}
-
-// Stream operator so that enum class Types can be used in log statements.
-std::ostream& operator<<(std::ostream& out, const Value::Type& type);
-
-} // namespace base
-
-#endif // BASE_VALUES_H_
diff --git a/gn/base/win/registry.cc b/gn/base/win/registry.cc
deleted file mode 100644
index e07820baaab..00000000000
--- a/gn/base/win/registry.cc
+++ /dev/null
@@ -1,613 +0,0 @@
-// 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 "base/win/registry.h"
-
-#include <shlwapi.h>
-#include <stddef.h>
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_util.h"
-
-namespace base {
-namespace win {
-
-namespace {
-
-// RegEnumValue() reports the number of characters from the name that were
-// written to the buffer, not how many there are. This constant is the maximum
-// name size, such that a buffer with this size should read any name.
-const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
-
-// Registry values are read as BYTE* but can have wchar_t* data whose last
-// wchar_t is truncated. This function converts the reported |byte_size| to
-// a size in wchar_t that can store a truncated wchar_t if necessary.
-inline DWORD to_wchar_size(DWORD byte_size) {
- return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
-}
-
-// Mask to pull WOW64 access flags out of REGSAM access.
-const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
-
-} // namespace
-
-// RegKey ----------------------------------------------------------------------
-
-RegKey::RegKey() : key_(NULL), wow64access_(0) {}
-
-RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) {}
-
-RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
- : key_(NULL), wow64access_(0) {
- if (rootkey) {
- if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
- Create(rootkey, subkey, access);
- else
- Open(rootkey, subkey, access);
- } else {
- DCHECK(!subkey);
- wow64access_ = access & kWow64AccessMask;
- }
-}
-
-RegKey::~RegKey() {
- Close();
-}
-
-LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
- DWORD disposition_value;
- return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
-}
-
-LONG RegKey::CreateWithDisposition(HKEY rootkey,
- const wchar_t* subkey,
- DWORD* disposition,
- REGSAM access) {
- DCHECK(rootkey && subkey && access && disposition);
- HKEY subhkey = NULL;
- LONG result =
- RegCreateKeyEx(rootkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, access,
- NULL, &subhkey, disposition);
- if (result == ERROR_SUCCESS) {
- Close();
- key_ = subhkey;
- wow64access_ = access & kWow64AccessMask;
- }
-
- return result;
-}
-
-LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
- DCHECK(name && access);
- // After the application has accessed an alternate registry view using one of
- // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
- // (create, delete, or open) on child registry keys must explicitly use the
- // same flag. Otherwise, there can be unexpected behavior.
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
- if ((access & kWow64AccessMask) != wow64access_) {
- NOTREACHED();
- return ERROR_INVALID_PARAMETER;
- }
- HKEY subkey = NULL;
- LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
- access, NULL, &subkey, NULL);
- if (result == ERROR_SUCCESS) {
- Close();
- key_ = subkey;
- wow64access_ = access & kWow64AccessMask;
- }
-
- return result;
-}
-
-LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
- DCHECK(rootkey && subkey && access);
- HKEY subhkey = NULL;
-
- LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
- if (result == ERROR_SUCCESS) {
- Close();
- key_ = subhkey;
- wow64access_ = access & kWow64AccessMask;
- }
-
- return result;
-}
-
-LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
- DCHECK(relative_key_name && access);
- // After the application has accessed an alternate registry view using one of
- // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
- // (create, delete, or open) on child registry keys must explicitly use the
- // same flag. Otherwise, there can be unexpected behavior.
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
- if ((access & kWow64AccessMask) != wow64access_) {
- NOTREACHED();
- return ERROR_INVALID_PARAMETER;
- }
- HKEY subkey = NULL;
- LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
-
- // We have to close the current opened key before replacing it with the new
- // one.
- if (result == ERROR_SUCCESS) {
- Close();
- key_ = subkey;
- wow64access_ = access & kWow64AccessMask;
- }
- return result;
-}
-
-void RegKey::Close() {
- if (key_) {
- ::RegCloseKey(key_);
- key_ = NULL;
- wow64access_ = 0;
- }
-}
-
-// TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
-void RegKey::Set(HKEY key) {
- if (key_ != key) {
- Close();
- key_ = key;
- }
-}
-
-HKEY RegKey::Take() {
- DCHECK_EQ(wow64access_, 0u);
- HKEY key = key_;
- key_ = NULL;
- return key;
-}
-
-bool RegKey::HasValue(const wchar_t* name) const {
- return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
-}
-
-DWORD RegKey::GetValueCount() const {
- DWORD count = 0;
- LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
- NULL, NULL, NULL, NULL);
- return (result == ERROR_SUCCESS) ? count : 0;
-}
-
-LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
- wchar_t buf[256];
- DWORD bufsize = arraysize(buf);
- LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
- if (r == ERROR_SUCCESS)
- *name = buf;
-
- return r;
-}
-
-LONG RegKey::DeleteKey(const wchar_t* name) {
- DCHECK(key_);
- DCHECK(name);
- HKEY subkey = NULL;
-
- // Verify the key exists before attempting delete to replicate previous
- // behavior.
- LONG result =
- RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
- if (result != ERROR_SUCCESS)
- return result;
- RegCloseKey(subkey);
-
- return RegDelRecurse(key_, std::wstring(name), wow64access_);
-}
-
-LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
- DCHECK(key_);
- DCHECK(name);
-
- HKEY target_key = NULL;
- LONG result =
- RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_, &target_key);
-
- if (result != ERROR_SUCCESS)
- return result;
-
- DWORD count = 0;
- result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
- NULL, NULL, NULL, NULL);
-
- RegCloseKey(target_key);
-
- if (result != ERROR_SUCCESS)
- return result;
-
- if (count == 0)
- return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
-
- return ERROR_DIR_NOT_EMPTY;
-}
-
-LONG RegKey::DeleteValue(const wchar_t* value_name) {
- DCHECK(key_);
- LONG result = RegDeleteValue(key_, value_name);
- return result;
-}
-
-LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
- DCHECK(out_value);
- DWORD type = REG_DWORD;
- DWORD size = sizeof(DWORD);
- DWORD local_value = 0;
- LONG result = ReadValue(name, &local_value, &size, &type);
- if (result == ERROR_SUCCESS) {
- if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
- *out_value = local_value;
- else
- result = ERROR_CANTREAD;
- }
-
- return result;
-}
-
-LONG RegKey::ReadInt64(const wchar_t* name, int64_t* out_value) const {
- DCHECK(out_value);
- DWORD type = REG_QWORD;
- int64_t local_value = 0;
- DWORD size = sizeof(local_value);
- LONG result = ReadValue(name, &local_value, &size, &type);
- if (result == ERROR_SUCCESS) {
- if ((type == REG_QWORD || type == REG_BINARY) &&
- size == sizeof(local_value))
- *out_value = local_value;
- else
- result = ERROR_CANTREAD;
- }
-
- return result;
-}
-
-LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
- DCHECK(out_value);
- const size_t kMaxStringLength = 1024; // This is after expansion.
- // Use the one of the other forms of ReadValue if 1024 is too small for you.
- wchar_t raw_value[kMaxStringLength];
- DWORD type = REG_SZ, size = sizeof(raw_value);
- LONG result = ReadValue(name, raw_value, &size, &type);
- if (result == ERROR_SUCCESS) {
- if (type == REG_SZ) {
- *out_value = raw_value;
- } else if (type == REG_EXPAND_SZ) {
- wchar_t expanded[kMaxStringLength];
- size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
- // Success: returns the number of wchar_t's copied
- // Fail: buffer too small, returns the size required
- // Fail: other, returns 0
- if (size == 0 || size > kMaxStringLength) {
- result = ERROR_MORE_DATA;
- } else {
- *out_value = expanded;
- }
- } else {
- // Not a string. Oops.
- result = ERROR_CANTREAD;
- }
- }
-
- return result;
-}
-
-LONG RegKey::ReadValue(const wchar_t* name,
- void* data,
- DWORD* dsize,
- DWORD* dtype) const {
- LONG result = RegQueryValueEx(key_, name, 0, dtype,
- reinterpret_cast<LPBYTE>(data), dsize);
- return result;
-}
-
-LONG RegKey::ReadValues(const wchar_t* name,
- std::vector<std::wstring>* values) {
- values->clear();
-
- DWORD type = REG_MULTI_SZ;
- DWORD size = 0;
- LONG result = ReadValue(name, NULL, &size, &type);
- if (result != ERROR_SUCCESS || size == 0)
- return result;
-
- if (type != REG_MULTI_SZ)
- return ERROR_CANTREAD;
-
- std::vector<wchar_t> buffer(size / sizeof(wchar_t));
- result = ReadValue(name, &buffer[0], &size, NULL);
- if (result != ERROR_SUCCESS || size == 0)
- return result;
-
- // Parse the double-null-terminated list of strings.
- // Note: This code is paranoid to not read outside of |buf|, in the case where
- // it may not be properly terminated.
- const wchar_t* entry = &buffer[0];
- const wchar_t* buffer_end = entry + (size / sizeof(wchar_t));
- while (entry < buffer_end && entry[0] != '\0') {
- const wchar_t* entry_end = std::find(entry, buffer_end, L'\0');
- values->push_back(std::wstring(entry, entry_end));
- entry = entry_end + 1;
- }
- return 0;
-}
-
-LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
- return WriteValue(name, &in_value, static_cast<DWORD>(sizeof(in_value)),
- REG_DWORD);
-}
-
-LONG RegKey::WriteValue(const wchar_t* name, const wchar_t* in_value) {
- return WriteValue(
- name, in_value,
- static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ);
-}
-
-LONG RegKey::WriteValue(const wchar_t* name,
- const void* data,
- DWORD dsize,
- DWORD dtype) {
- DCHECK(data || !dsize);
-
- LONG result =
- RegSetValueEx(key_, name, 0, dtype,
- reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
- return result;
-}
-
-// static
-LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
- const wchar_t* lpSubKey,
- REGSAM samDesired,
- DWORD Reserved) {
- typedef LSTATUS(WINAPI * RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
-
- RegDeleteKeyExPtr reg_delete_key_ex_func =
- reinterpret_cast<RegDeleteKeyExPtr>(
- GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
-
- if (reg_delete_key_ex_func)
- return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
-
- // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
- return RegDeleteKey(hKey, lpSubKey);
-}
-
-// static
-LONG RegKey::RegDelRecurse(HKEY root_key,
- const std::wstring& name,
- REGSAM access) {
- // First, see if the key can be deleted without having to recurse.
- LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
- if (result == ERROR_SUCCESS)
- return result;
-
- HKEY target_key = NULL;
- result = RegOpenKeyEx(root_key, name.c_str(), 0,
- KEY_ENUMERATE_SUB_KEYS | access, &target_key);
-
- if (result == ERROR_FILE_NOT_FOUND)
- return ERROR_SUCCESS;
- if (result != ERROR_SUCCESS)
- return result;
-
- std::wstring subkey_name(name);
-
- // Check for an ending slash and add one if it is missing.
- if (!name.empty() && subkey_name[name.length() - 1] != L'\\')
- subkey_name += L"\\";
-
- // Enumerate the keys
- result = ERROR_SUCCESS;
- const DWORD kMaxKeyNameLength = MAX_PATH;
- const size_t base_key_length = subkey_name.length();
- std::wstring key_name;
- while (result == ERROR_SUCCESS) {
- DWORD key_size = kMaxKeyNameLength;
- result =
- RegEnumKeyEx(target_key, 0, WriteInto(&key_name, kMaxKeyNameLength),
- &key_size, NULL, NULL, NULL, NULL);
-
- if (result != ERROR_SUCCESS)
- break;
-
- key_name.resize(key_size);
- subkey_name.resize(base_key_length);
- subkey_name += key_name;
-
- if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
- break;
- }
-
- RegCloseKey(target_key);
-
- // Try again to delete the key.
- result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
-
- return result;
-}
-
-// RegistryValueIterator ------------------------------------------------------
-
-RegistryValueIterator::RegistryValueIterator(HKEY root_key,
- const wchar_t* folder_key,
- REGSAM wow64access)
- : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') {
- Initialize(root_key, folder_key, wow64access);
-}
-
-RegistryValueIterator::RegistryValueIterator(HKEY root_key,
- const wchar_t* folder_key)
- : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') {
- Initialize(root_key, folder_key, 0);
-}
-
-void RegistryValueIterator::Initialize(HKEY root_key,
- const wchar_t* folder_key,
- REGSAM wow64access) {
- DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
- LONG result =
- RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
- if (result != ERROR_SUCCESS) {
- key_ = NULL;
- } else {
- DWORD count = 0;
- result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
- NULL, NULL, NULL, NULL);
-
- if (result != ERROR_SUCCESS) {
- ::RegCloseKey(key_);
- key_ = NULL;
- } else {
- index_ = count - 1;
- }
- }
-
- Read();
-}
-
-RegistryValueIterator::~RegistryValueIterator() {
- if (key_)
- ::RegCloseKey(key_);
-}
-
-DWORD RegistryValueIterator::ValueCount() const {
- DWORD count = 0;
- LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
- NULL, NULL, NULL, NULL);
- if (result != ERROR_SUCCESS)
- return 0;
-
- return count;
-}
-
-bool RegistryValueIterator::Valid() const {
- return key_ != NULL && index_ >= 0;
-}
-
-void RegistryValueIterator::operator++() {
- --index_;
- Read();
-}
-
-bool RegistryValueIterator::Read() {
- if (Valid()) {
- DWORD capacity = static_cast<DWORD>(name_.capacity());
- DWORD name_size = capacity;
- // |value_size_| is in bytes. Reserve the last character for a NUL.
- value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
- LONG result = ::RegEnumValue(
- key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
- reinterpret_cast<BYTE*>(value_.data()), &value_size_);
-
- if (result == ERROR_MORE_DATA) {
- // Registry key names are limited to 255 characters and fit within
- // MAX_PATH (which is 260) but registry value names can use up to 16,383
- // characters and the value itself is not limited
- // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
- // ms724872(v=vs.85).aspx).
- // Resize the buffers and retry if their size caused the failure.
- DWORD value_size_in_wchars = to_wchar_size(value_size_);
- if (value_size_in_wchars + 1 > value_.size())
- value_.resize(value_size_in_wchars + 1, L'\0');
- value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
- name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
- result = ::RegEnumValue(
- key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
- reinterpret_cast<BYTE*>(value_.data()), &value_size_);
- }
-
- if (result == ERROR_SUCCESS) {
- DCHECK_LT(to_wchar_size(value_size_), value_.size());
- value_[to_wchar_size(value_size_)] = L'\0';
- return true;
- }
- }
-
- name_[0] = L'\0';
- value_[0] = L'\0';
- value_size_ = 0;
- return false;
-}
-
-// RegistryKeyIterator --------------------------------------------------------
-
-RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
- const wchar_t* folder_key) {
- Initialize(root_key, folder_key, 0);
-}
-
-RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
- const wchar_t* folder_key,
- REGSAM wow64access) {
- Initialize(root_key, folder_key, wow64access);
-}
-
-RegistryKeyIterator::~RegistryKeyIterator() {
- if (key_)
- ::RegCloseKey(key_);
-}
-
-DWORD RegistryKeyIterator::SubkeyCount() const {
- DWORD count = 0;
- LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL);
- if (result != ERROR_SUCCESS)
- return 0;
-
- return count;
-}
-
-bool RegistryKeyIterator::Valid() const {
- return key_ != NULL && index_ >= 0;
-}
-
-void RegistryKeyIterator::operator++() {
- --index_;
- Read();
-}
-
-bool RegistryKeyIterator::Read() {
- if (Valid()) {
- DWORD ncount = arraysize(name_);
- FILETIME written;
- LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, NULL,
- &written);
- if (ERROR_SUCCESS == r)
- return true;
- }
-
- name_[0] = '\0';
- return false;
-}
-
-void RegistryKeyIterator::Initialize(HKEY root_key,
- const wchar_t* folder_key,
- REGSAM wow64access) {
- DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
- LONG result =
- RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
- if (result != ERROR_SUCCESS) {
- key_ = NULL;
- } else {
- DWORD count = 0;
- result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL);
-
- if (result != ERROR_SUCCESS) {
- ::RegCloseKey(key_);
- key_ = NULL;
- } else {
- index_ = count - 1;
- }
- }
-
- Read();
-}
-
-} // namespace win
-} // namespace base
diff --git a/gn/base/win/registry.h b/gn/base/win/registry.h
deleted file mode 100644
index e2e0d305cec..00000000000
--- a/gn/base/win/registry.h
+++ /dev/null
@@ -1,248 +0,0 @@
-// 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 BASE_WIN_REGISTRY_H_
-#define BASE_WIN_REGISTRY_H_
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-#include "base/win/windows_types.h"
-
-#include "base/macros.h"
-#include "base/win/scoped_handle.h"
-
-namespace base {
-namespace win {
-
-// Utility class to read, write and manipulate the Windows Registry.
-// Registry vocabulary primer: a "key" is like a folder, in which there
-// are "values", which are <name, data> pairs, with an associated data type.
-//
-// Note:
-// * ReadValue family of functions guarantee that the out-parameter
-// is not touched in case of failure.
-// * Functions returning LONG indicate success as ERROR_SUCCESS or an
-// error as a (non-zero) win32 error code.
-class RegKey {
- public:
- RegKey();
- explicit RegKey(HKEY key);
- RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access);
- ~RegKey();
-
- LONG Create(HKEY rootkey, const wchar_t* subkey, REGSAM access);
-
- LONG CreateWithDisposition(HKEY rootkey,
- const wchar_t* subkey,
- DWORD* disposition,
- REGSAM access);
-
- // Creates a subkey or open it if it already exists.
- LONG CreateKey(const wchar_t* name, REGSAM access);
-
- // Opens an existing reg key.
- LONG Open(HKEY rootkey, const wchar_t* subkey, REGSAM access);
-
- // Opens an existing reg key, given the relative key name.
- LONG OpenKey(const wchar_t* relative_key_name, REGSAM access);
-
- // Closes this reg key.
- void Close();
-
- // Replaces the handle of the registry key and takes ownership of the handle.
- void Set(HKEY key);
-
- // Transfers ownership away from this object.
- HKEY Take();
-
- // Returns false if this key does not have the specified value, or if an error
- // occurrs while attempting to access it.
- bool HasValue(const wchar_t* value_name) const;
-
- // Returns the number of values for this key, or 0 if the number cannot be
- // determined.
- DWORD GetValueCount() const;
-
- // Determines the nth value's name.
- LONG GetValueNameAt(int index, std::wstring* name) const;
-
- // True while the key is valid.
- bool Valid() const { return key_ != NULL; }
-
- // Kills a key and everything that lives below it; please be careful when
- // using it.
- LONG DeleteKey(const wchar_t* name);
-
- // Deletes an empty subkey. If the subkey has subkeys or values then this
- // will fail.
- LONG DeleteEmptyKey(const wchar_t* name);
-
- // Deletes a single value within the key.
- LONG DeleteValue(const wchar_t* name);
-
- // Getters:
-
- // Reads a REG_DWORD (uint32_t) into |out_value|. If |name| is null or empty,
- // reads the key's default value, if any.
- LONG ReadValueDW(const wchar_t* name, DWORD* out_value) const;
-
- // Reads a REG_QWORD (int64_t) into |out_value|. If |name| is null or empty,
- // reads the key's default value, if any.
- LONG ReadInt64(const wchar_t* name, int64_t* out_value) const;
-
- // Reads a string into |out_value|. If |name| is null or empty, reads
- // the key's default value, if any.
- LONG ReadValue(const wchar_t* name, std::wstring* out_value) const;
-
- // Reads a REG_MULTI_SZ registry field into a vector of strings. Clears
- // |values| initially and adds further strings to the list. Returns
- // ERROR_CANTREAD if type is not REG_MULTI_SZ.
- LONG ReadValues(const wchar_t* name, std::vector<std::wstring>* values);
-
- // Reads raw data into |data|. If |name| is null or empty, reads the key's
- // default value, if any.
- LONG ReadValue(const wchar_t* name,
- void* data,
- DWORD* dsize,
- DWORD* dtype) const;
-
- // Setters:
-
- // Sets an int32_t value.
- LONG WriteValue(const wchar_t* name, DWORD in_value);
-
- // Sets a string value.
- LONG WriteValue(const wchar_t* name, const wchar_t* in_value);
-
- // Sets raw data, including type.
- LONG WriteValue(const wchar_t* name,
- const void* data,
- DWORD dsize,
- DWORD dtype);
-
- HKEY Handle() const { return key_; }
-
- private:
- // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to
- // RegDeleteKey.
- static LONG RegDeleteKeyExWrapper(HKEY hKey,
- const wchar_t* lpSubKey,
- REGSAM samDesired,
- DWORD Reserved);
-
- // Recursively deletes a key and all of its subkeys.
- static LONG RegDelRecurse(HKEY root_key,
- const std::wstring& name,
- REGSAM access);
-
- HKEY key_; // The registry key being iterated.
- REGSAM wow64access_;
-
- DISALLOW_COPY_AND_ASSIGN(RegKey);
-};
-
-// Iterates the entries found in a particular folder on the registry.
-class RegistryValueIterator {
- public:
- // Constructs a Registry Value Iterator with default WOW64 access.
- RegistryValueIterator(HKEY root_key, const wchar_t* folder_key);
-
- // Constructs a Registry Key Iterator with specific WOW64 access, one of
- // KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0.
- // Note: |wow64access| should be the same access used to open |root_key|
- // previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE).
- // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
- RegistryValueIterator(HKEY root_key,
- const wchar_t* folder_key,
- REGSAM wow64access);
-
- ~RegistryValueIterator();
-
- DWORD ValueCount() const;
-
- // True while the iterator is valid.
- bool Valid() const;
-
- // Advances to the next registry entry.
- void operator++();
-
- const wchar_t* Name() const { return name_.c_str(); }
- const wchar_t* Value() const { return value_.data(); }
- // ValueSize() is in bytes.
- DWORD ValueSize() const { return value_size_; }
- DWORD Type() const { return type_; }
-
- int Index() const { return index_; }
-
- private:
- // Reads in the current values.
- bool Read();
-
- void Initialize(HKEY root_key, const wchar_t* folder_key, REGSAM wow64access);
-
- // The registry key being iterated.
- HKEY key_;
-
- // Current index of the iteration.
- int index_;
-
- // Current values.
- std::wstring name_;
- std::vector<wchar_t> value_;
- DWORD value_size_;
- DWORD type_;
-
- DISALLOW_COPY_AND_ASSIGN(RegistryValueIterator);
-};
-
-class RegistryKeyIterator {
- public:
- // Constructs a Registry Key Iterator with default WOW64 access.
- RegistryKeyIterator(HKEY root_key, const wchar_t* folder_key);
-
- // Constructs a Registry Value Iterator with specific WOW64 access, one of
- // KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0.
- // Note: |wow64access| should be the same access used to open |root_key|
- // previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE).
- // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
- RegistryKeyIterator(HKEY root_key,
- const wchar_t* folder_key,
- REGSAM wow64access);
-
- ~RegistryKeyIterator();
-
- DWORD SubkeyCount() const;
-
- // True while the iterator is valid.
- bool Valid() const;
-
- // Advances to the next entry in the folder.
- void operator++();
-
- const wchar_t* Name() const { return name_; }
-
- int Index() const { return index_; }
-
- private:
- // Reads in the current values.
- bool Read();
-
- void Initialize(HKEY root_key, const wchar_t* folder_key, REGSAM wow64access);
-
- // The registry key being iterated.
- HKEY key_;
-
- // Current index of the iteration.
- int index_;
-
- wchar_t name_[MAX_PATH];
-
- DISALLOW_COPY_AND_ASSIGN(RegistryKeyIterator);
-};
-
-} // namespace win
-} // namespace base
-
-#endif // BASE_WIN_REGISTRY_H_
diff --git a/gn/base/win/scoped_handle.cc b/gn/base/win/scoped_handle.cc
deleted file mode 100644
index ddc744fbc05..00000000000
--- a/gn/base/win/scoped_handle.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 "base/win/scoped_handle.h"
-#include "base/win/windows_types.h"
-
-#include <windows.h>
-
-namespace base {
-namespace win {
-
-// Static.
-bool HandleTraits::CloseHandle(HANDLE handle) {
- return ::CloseHandle(handle);
-}
-
-// Static.
-void VerifierTraits::StartTracking(HANDLE handle,
- const void* owner,
- const void* pc1,
- const void* pc2) {}
-
-// Static.
-void VerifierTraits::StopTracking(HANDLE handle,
- const void* owner,
- const void* pc1,
- const void* pc2) {}
-
-void DisableHandleVerifier() {}
-
-void OnHandleBeingClosed(HANDLE handle) {}
-
-} // namespace win
-} // namespace base
diff --git a/gn/base/win/scoped_handle.h b/gn/base/win/scoped_handle.h
deleted file mode 100644
index e67e44717fd..00000000000
--- a/gn/base/win/scoped_handle.h
+++ /dev/null
@@ -1,172 +0,0 @@
-// 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 BASE_WIN_SCOPED_HANDLE_H_
-#define BASE_WIN_SCOPED_HANDLE_H_
-
-#include "base/win/windows_types.h"
-
-#include "base/gtest_prod_util.h"
-#include "base/logging.h"
-#include "base/macros.h"
-
-// TODO(rvargas): remove this with the rest of the verifier.
-#if defined(COMPILER_MSVC)
-#include <intrin.h>
-#define BASE_WIN_GET_CALLER _ReturnAddress()
-#elif defined(COMPILER_GCC)
-#define BASE_WIN_GET_CALLER \
- __builtin_extract_return_addr(\ __builtin_return_address(0))
-#endif
-
-namespace base {
-namespace win {
-
-// Generic wrapper for raw handles that takes care of closing handles
-// automatically. The class interface follows the style of
-// the ScopedFILE class with two additions:
-// - IsValid() method can tolerate multiple invalid handle values such as NULL
-// and INVALID_HANDLE_VALUE (-1) for Win32 handles.
-// - Set() (and the constructors and assignment operators that call it)
-// preserve the Windows LastError code. This ensures that GetLastError() can
-// be called after stashing a handle in a GenericScopedHandle object. Doing
-// this explicitly is necessary because of bug 528394 and VC++ 2015.
-template <class Traits, class Verifier>
-class GenericScopedHandle {
- public:
- typedef typename Traits::Handle Handle;
-
- GenericScopedHandle() : handle_(Traits::NullHandle()) {}
-
- explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) {
- Set(handle);
- }
-
- GenericScopedHandle(GenericScopedHandle&& other)
- : handle_(Traits::NullHandle()) {
- Set(other.Take());
- }
-
- ~GenericScopedHandle() { Close(); }
-
- bool IsValid() const { return Traits::IsHandleValid(handle_); }
-
- GenericScopedHandle& operator=(GenericScopedHandle&& other) {
- DCHECK_NE(this, &other);
- Set(other.Take());
- return *this;
- }
-
- void Set(Handle handle) {
- if (handle_ != handle) {
- // Preserve old LastError to avoid bug 528394.
- auto last_error = ::GetLastError();
- Close();
-
- if (Traits::IsHandleValid(handle)) {
- handle_ = handle;
- }
- ::SetLastError(last_error);
- }
- }
-
- Handle Get() const { return handle_; }
-
- // Transfers ownership away from this object.
- Handle Take() {
- Handle temp = handle_;
- handle_ = Traits::NullHandle();
- return temp;
- }
-
- // Explicitly closes the owned handle.
- void Close() {
- if (Traits::IsHandleValid(handle_)) {
- Traits::CloseHandle(handle_);
- handle_ = Traits::NullHandle();
- }
- }
-
- private:
- FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierWrongOwner);
- FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierUntrackedHandle);
- Handle handle_;
-
- DISALLOW_COPY_AND_ASSIGN(GenericScopedHandle);
-};
-
-#undef BASE_WIN_GET_CALLER
-
-// The traits class for Win32 handles that can be closed via CloseHandle() API.
-class HandleTraits {
- public:
- typedef HANDLE Handle;
-
- // Closes the handle.
- static bool CloseHandle(HANDLE handle);
-
- // Returns true if the handle value is valid.
- static bool IsHandleValid(HANDLE handle) {
- return handle != NULL && handle != INVALID_HANDLE_VALUE;
- }
-
- // Returns NULL handle value.
- static HANDLE NullHandle() { return NULL; }
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(HandleTraits);
-};
-
-// Do-nothing verifier.
-class DummyVerifierTraits {
- public:
- typedef HANDLE Handle;
-
- static void StartTracking(HANDLE handle,
- const void* owner,
- const void* pc1,
- const void* pc2) {}
- static void StopTracking(HANDLE handle,
- const void* owner,
- const void* pc1,
- const void* pc2) {}
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(DummyVerifierTraits);
-};
-
-// Performs actual run-time tracking.
-class VerifierTraits {
- public:
- typedef HANDLE Handle;
-
- static void StartTracking(HANDLE handle,
- const void* owner,
- const void* pc1,
- const void* pc2);
- static void StopTracking(HANDLE handle,
- const void* owner,
- const void* pc1,
- const void* pc2);
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits);
-};
-
-typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle;
-
-// This function may be called by the embedder to disable the use of
-// VerifierTraits at runtime. It has no effect if DummyVerifierTraits is used
-// for ScopedHandle.
-void DisableHandleVerifier();
-
-// This should be called whenever the OS is closing a handle, if extended
-// verification of improper handle closing is desired. If |handle| is being
-// tracked by the handle verifier and ScopedHandle is not the one closing it,
-// a CHECK is generated.
-void OnHandleBeingClosed(HANDLE handle);
-} // namespace win
-} // namespace base
-
-#endif // BASE_WIN_SCOPED_HANDLE_H_
diff --git a/gn/base/win/windows_types.h b/gn/base/win/windows_types.h
deleted file mode 100644
index d076c446b87..00000000000
--- a/gn/base/win/windows_types.h
+++ /dev/null
@@ -1,254 +0,0 @@
-// 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.
-
-// This file contains defines and typedefs that allow popular Windows types to
-// be used without the overhead of including windows.h.
-
-#ifndef BASE_WIN_WINDOWS_TYPES_H
-#define BASE_WIN_WINDOWS_TYPES_H
-
-// Needed for function prototypes.
-#include <concurrencysal.h>
-#include <sal.h>
-#include <specstrings.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// typedef and define the most commonly used Windows integer types.
-
-typedef unsigned long DWORD;
-typedef long LONG;
-typedef __int64 LONGLONG;
-typedef unsigned __int64 ULONGLONG;
-
-#define VOID void
-typedef char CHAR;
-typedef short SHORT;
-typedef long LONG;
-typedef int INT;
-typedef unsigned int UINT;
-typedef unsigned int* PUINT;
-typedef void* LPVOID;
-typedef void* PVOID;
-typedef void* HANDLE;
-typedef int BOOL;
-typedef unsigned char BYTE;
-typedef BYTE BOOLEAN;
-typedef DWORD ULONG;
-typedef unsigned short WORD;
-typedef WORD UWORD;
-typedef WORD ATOM;
-
-#if defined(_WIN64)
-typedef __int64 INT_PTR, *PINT_PTR;
-typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
-
-typedef __int64 LONG_PTR, *PLONG_PTR;
-typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
-#else
-typedef __w64 int INT_PTR, *PINT_PTR;
-typedef __w64 unsigned int UINT_PTR, *PUINT_PTR;
-
-typedef __w64 long LONG_PTR, *PLONG_PTR;
-typedef __w64 unsigned long ULONG_PTR, *PULONG_PTR;
-#endif
-
-typedef UINT_PTR WPARAM;
-typedef LONG_PTR LPARAM;
-typedef LONG_PTR LRESULT;
-#define LRESULT LONG_PTR
-typedef _Return_type_success_(return >= 0) long HRESULT;
-
-typedef ULONG_PTR SIZE_T, *PSIZE_T;
-typedef LONG_PTR SSIZE_T, *PSSIZE_T;
-
-typedef DWORD ACCESS_MASK;
-typedef ACCESS_MASK REGSAM;
-
-// Forward declare Windows compatible handles.
-
-#define CHROME_DECLARE_HANDLE(name) \
- struct name##__; \
- typedef struct name##__* name
-CHROME_DECLARE_HANDLE(HGLRC);
-CHROME_DECLARE_HANDLE(HICON);
-CHROME_DECLARE_HANDLE(HINSTANCE);
-CHROME_DECLARE_HANDLE(HKEY);
-CHROME_DECLARE_HANDLE(HKL);
-CHROME_DECLARE_HANDLE(HMENU);
-CHROME_DECLARE_HANDLE(HWND);
-typedef HINSTANCE HMODULE;
-#undef CHROME_DECLARE_HANDLE
-
-// Forward declare some Windows struct/typedef sets.
-
-typedef struct _OVERLAPPED OVERLAPPED;
-typedef struct tagMSG MSG, *PMSG, *NPMSG, *LPMSG;
-
-typedef struct _RTL_SRWLOCK RTL_SRWLOCK;
-typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
-
-typedef struct _GUID GUID;
-typedef GUID CLSID;
-
-typedef struct tagLOGFONTW LOGFONTW, *PLOGFONTW, *NPLOGFONTW, *LPLOGFONTW;
-typedef LOGFONTW LOGFONT;
-
-typedef struct _FILETIME FILETIME;
-
-typedef struct tagMENUITEMINFOW MENUITEMINFOW, MENUITEMINFO;
-
-typedef struct tagNMHDR NMHDR;
-
-// Declare Chrome versions of some Windows structures. These are needed for
-// when we need a concrete type but don't want to pull in Windows.h. We can't
-// declare the Windows types so we declare our types and cast to the Windows
-// types in a few places.
-
-struct CHROME_SRWLOCK {
- PVOID Ptr;
-};
-
-struct CHROME_CONDITION_VARIABLE {
- PVOID Ptr;
-};
-
-// Define some commonly used Windows constants. Note that the layout of these
-// macros - including internal spacing - must be 100% consistent with windows.h.
-
-// clang-format off
-
-#ifndef INVALID_HANDLE_VALUE
-// Work around there being two slightly different definitions in the SDK.
-#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
-#endif
-#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
-#define HTNOWHERE 0
-#define MAX_PATH 260
-#define CS_GLOBALCLASS 0x4000
-
-#define ERROR_SUCCESS 0L
-#define ERROR_FILE_NOT_FOUND 2L
-#define ERROR_ACCESS_DENIED 5L
-#define ERROR_INVALID_HANDLE 6L
-#define ERROR_SHARING_VIOLATION 32L
-#define ERROR_LOCK_VIOLATION 33L
-#define REG_BINARY ( 3ul )
-
-#define STATUS_PENDING ((DWORD )0x00000103L)
-#define STILL_ACTIVE STATUS_PENDING
-#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
-#define FAILED(hr) (((HRESULT)(hr)) < 0)
-
-#define HKEY_CLASSES_ROOT (( HKEY ) (ULONG_PTR)((LONG)0x80000000) )
-#define HKEY_LOCAL_MACHINE (( HKEY ) (ULONG_PTR)((LONG)0x80000002) )
-#define HKEY_CURRENT_USER (( HKEY ) (ULONG_PTR)((LONG)0x80000001) )
-#define KEY_QUERY_VALUE (0x0001)
-#define KEY_SET_VALUE (0x0002)
-#define KEY_CREATE_SUB_KEY (0x0004)
-#define KEY_ENUMERATE_SUB_KEYS (0x0008)
-#define KEY_NOTIFY (0x0010)
-#define KEY_CREATE_LINK (0x0020)
-#define KEY_WOW64_32KEY (0x0200)
-#define KEY_WOW64_64KEY (0x0100)
-#define KEY_WOW64_RES (0x0300)
-
-#define READ_CONTROL (0x00020000L)
-#define SYNCHRONIZE (0x00100000L)
-
-#define STANDARD_RIGHTS_READ (READ_CONTROL)
-#define STANDARD_RIGHTS_WRITE (READ_CONTROL)
-#define STANDARD_RIGHTS_ALL (0x001F0000L)
-
-#define KEY_READ ((STANDARD_RIGHTS_READ |\
- KEY_QUERY_VALUE |\
- KEY_ENUMERATE_SUB_KEYS |\
- KEY_NOTIFY) \
- & \
- (~SYNCHRONIZE))
-
-
-#define KEY_WRITE ((STANDARD_RIGHTS_WRITE |\
- KEY_SET_VALUE |\
- KEY_CREATE_SUB_KEY) \
- & \
- (~SYNCHRONIZE))
-
-#define KEY_ALL_ACCESS ((STANDARD_RIGHTS_ALL |\
- KEY_QUERY_VALUE |\
- KEY_SET_VALUE |\
- KEY_CREATE_SUB_KEY |\
- KEY_ENUMERATE_SUB_KEYS |\
- KEY_NOTIFY |\
- KEY_CREATE_LINK) \
- & \
- (~SYNCHRONIZE))
-
-// Define some macros needed when prototyping Windows functions.
-
-#define DECLSPEC_IMPORT __declspec(dllimport)
-#define WINBASEAPI DECLSPEC_IMPORT
-#define WINUSERAPI DECLSPEC_IMPORT
-#define WINAPI __stdcall
-#define CALLBACK __stdcall
-
-// clang-format on
-
-// Needed for optimal lock performance.
-WINBASEAPI _Releases_exclusive_lock_(*SRWLock) VOID WINAPI
- ReleaseSRWLockExclusive(_Inout_ PSRWLOCK SRWLock);
-
-// Needed to support protobuf's GetMessage macro magic.
-WINUSERAPI BOOL WINAPI GetMessageW(_Out_ LPMSG lpMsg,
- _In_opt_ HWND hWnd,
- _In_ UINT wMsgFilterMin,
- _In_ UINT wMsgFilterMax);
-
-// Needed for thread_local_storage.h
-WINBASEAPI LPVOID WINAPI TlsGetValue(_In_ DWORD dwTlsIndex);
-
-// Needed for scoped_handle.h
-WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI
- GetLastError(VOID);
-
-WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode);
-
-#ifdef __cplusplus
-}
-#endif
-
-// These macros are all defined by windows.h and are also used as the names of
-// functions in the Chromium code base. Add to this list as needed whenever
-// there is a Windows macro which causes a function call to be renamed. This
-// ensures that the same renaming will happen everywhere. Includes of this file
-// can be added wherever needed to ensure this consistent renaming.
-
-#define CopyFile CopyFileW
-#define CreateDirectory CreateDirectoryW
-#define CreateEvent CreateEventW
-#define CreateFile CreateFileW
-#define CreateService CreateServiceW
-#define DeleteFile DeleteFileW
-#define DispatchMessage DispatchMessageW
-#define DrawText DrawTextW
-#define GetComputerName GetComputerNameW
-#define GetCurrentDirectory GetCurrentDirectoryW
-#define GetCurrentTime() GetTickCount()
-#define GetFileAttributes GetFileAttributesW
-#define GetMessage GetMessageW
-#define GetUserName GetUserNameW
-#define LoadIcon LoadIconW
-#define LoadImage LoadImageW
-#define PostMessage PostMessageW
-#define ReplaceFile ReplaceFileW
-#define ReportEvent ReportEventW
-#define SendMessage SendMessageW
-#define SendMessageCallback SendMessageCallbackW
-#define SetCurrentDirectory SetCurrentDirectoryW
-#define StartService StartServiceW
-#define StrCat StrCatW
-
-#endif // BASE_WIN_WINDOWS_TYPES_H
diff --git a/gn/build/build_aix.ninja.template b/gn/build/build_aix.ninja.template
index f393e18be38..7d696b0e610 100644
--- a/gn/build/build_aix.ninja.template
+++ b/gn/build/build_aix.ninja.template
@@ -1,16 +1,13 @@
-rule cc
- command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c -c $in -o $out
- description = CC $out
- depfile = $out.d
- deps = gcc
rule cxx
- command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc -c $in -o $out
+ command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
+
rule alink_thin
command = rm -f $out && $ar rcsT $out $in
description = AR $out
+
rule link
command = $ld $ldflags -o $out $in $libs $solibs
description = LINK $out
diff --git a/gn/build/build_haiku.ninja.template b/gn/build/build_haiku.ninja.template
new file mode 100644
index 00000000000..ab117fb42c7
--- /dev/null
+++ b/gn/build/build_haiku.ninja.template
@@ -0,0 +1,13 @@
+rule cxx
+ command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
+ description = CXX $out
+ depfile = $out.d
+ deps = gcc
+
+rule alink_thin
+ command = rm -f $out && $ar rcsT $out $in
+ description = AR $out
+
+rule link
+ command = $ld $ldflags -o $out -Wl,--start-group $in $libs -Wl,--end-group $solibs
+ description = LINK $out
diff --git a/gn/build/build_linux.ninja.template b/gn/build/build_linux.ninja.template
index e59854b189e..ab117fb42c7 100644
--- a/gn/build/build_linux.ninja.template
+++ b/gn/build/build_linux.ninja.template
@@ -1,11 +1,5 @@
-rule cc
- command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c -c $in -o $out
- description = CC $out
- depfile = $out.d
- deps = gcc
-
rule cxx
- command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc -c $in -o $out
+ command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
diff --git a/gn/build/build_mac.ninja.template b/gn/build/build_mac.ninja.template
index e7dedac2f14..8d75a3cd0dc 100644
--- a/gn/build/build_mac.ninja.template
+++ b/gn/build/build_mac.ninja.template
@@ -1,11 +1,5 @@
-rule cc
- command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c -c $in -o $out
- description = CC $out
- depfile = $out.d
- deps = gcc
-
rule cxx
- command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc -c $in -o $out
+ command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
diff --git a/gn/build/build_openbsd.ninja.template b/gn/build/build_openbsd.ninja.template
index f22ccb327a8..8466900692f 100644
--- a/gn/build/build_openbsd.ninja.template
+++ b/gn/build/build_openbsd.ninja.template
@@ -1,11 +1,5 @@
-rule cc
- command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c -c $in -o $out
- description = CC $out
- depfile = $out.d
- deps = gcc
-
rule cxx
- command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc -c $in -o $out
+ command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
diff --git a/gn/build/build_win.ninja.template b/gn/build/build_win.ninja.template
index 3edfb6a4611..e2ca1861042 100644
--- a/gn/build/build_win.ninja.template
+++ b/gn/build/build_win.ninja.template
@@ -1,25 +1,12 @@
-rule cc
- command = ninja -t msvc -- $cc /nologo /showIncludes /FC @${out}.rsp /c ${in} /Fo${out}
- description = CC ${out}
- rspfile = ${out}.rsp
- rspfile_content = ${defines} ${includes} ${cflags} ${cflags_c}
- deps = msvc
-
rule cxx
- command = ninja -t msvc -- $cxx /nologo /showIncludes /FC @${out}.rsp /c ${in} /Fo${out}
- description = CXX ${out}
- rspfile = ${out}.rsp
- rspfile_content = ${defines} ${includes} ${cflags} ${cflags_cc}
+ command = ninja -t msvc -- $cxx /nologo /showIncludes /FC $includes $cflags /c $in /Fo$out
+ description = CXX $out
deps = msvc
rule alink_thin
- command = ninja -t msvc -- $ar /nologo /ignore:4221 /OUT:${out} @${out}.rsp
- description = LIB ${out}
- rspfile = ${out}.rsp
- rspfile_content = ${in_newline} ${libflags}
+ command = ninja -t msvc -- $ar /nologo /ignore:4221 $libflags /OUT:$out $in
+ description = LIB $out
rule link
- command = ninja -t msvc -- $ld /nologo /OUT:${out} /PDB:${out}.pdb @${out}.rsp
- description = LINK ${out}
- rspfile = ${out}.rsp
- rspfile_content = ${in_newline} ${libs} ${solibs} ${ldflags}
+ command = ninja -t msvc -- $ld /nologo $ldflags /OUT:$out /PDB:$out.pdb $in $solibs $libs
+ description = LINK $out
diff --git a/gn/build/gen.py b/gn/build/gen.py
index 38085b4d9db..922bf46fa57 100755
--- a/gn/build/gen.py
+++ b/gn/build/gen.py
@@ -15,9 +15,13 @@ import subprocess
import sys
import tempfile
+try: # py3
+ from shlex import quote as shell_quote
+except ImportError: # py2
+ from pipes import quote as shell_quote
+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT = os.path.dirname(SCRIPT_DIR)
-GN_ROOT = os.path.join(REPO_ROOT, 'tools', 'gn')
class Platform(object):
@@ -33,6 +37,8 @@ class Platform(object):
self._platform = 'darwin'
elif self._platform.startswith('mingw'):
self._platform = 'mingw'
+ elif self._platform.startswith('msys'):
+ self._platform = 'msys'
elif self._platform.startswith('win'):
self._platform = 'msvc'
elif self._platform.startswith('aix'):
@@ -41,12 +47,18 @@ class Platform(object):
self._platform = 'fuchsia'
elif self._platform.startswith('freebsd'):
self._platform = 'freebsd'
+ elif self._platform.startswith('netbsd'):
+ self._platform = 'netbsd'
elif self._platform.startswith('openbsd'):
self._platform = 'openbsd'
+ elif self._platform.startswith('haiku'):
+ self._platform = 'haiku'
+ elif self._platform.startswith('sunos'):
+ self._platform = 'solaris'
@staticmethod
def known_platforms():
- return ['linux', 'darwin', 'msvc', 'aix', 'fuchsia', 'freebsd', 'openbsd']
+ return ['linux', 'darwin', 'mingw', 'msys', 'msvc', 'aix', 'fuchsia', 'freebsd', 'netbsd', 'openbsd', 'haiku', 'solaris']
def platform(self):
return self._platform
@@ -57,6 +69,9 @@ class Platform(object):
def is_mingw(self):
return self._platform == 'mingw'
+ def is_msys(self):
+ return self._platform == 'msys'
+
def is_msvc(self):
return self._platform == 'msvc'
@@ -69,8 +84,14 @@ class Platform(object):
def is_aix(self):
return self._platform == 'aix'
+ def is_haiku(self):
+ return self._platform == 'haiku'
+
+ def is_solaris(self):
+ return self._platform == 'solaris'
+
def is_posix(self):
- return self._platform in ['linux', 'freebsd', 'darwin', 'aix', 'openbsd']
+ return self._platform in ['linux', 'freebsd', 'darwin', 'aix', 'openbsd', 'haiku', 'solaris', 'msys', 'netbsd']
def main(argv):
@@ -98,6 +119,16 @@ def main(argv):
parser.add_option('--no-static-libstdc++', action='store_true',
default=False, dest='no_static_libstdcpp',
help='Don\'t link libstdc++ statically')
+ parser.add_option('--link-lib',
+ action='append',
+ metavar='LINK_LIB',
+ default=[],
+ dest='link_libs',
+ help=('Add a library to the final executable link. ' +
+ 'LINK_LIB must be the path to a static or shared ' +
+ 'library, or \'-l<name>\' on POSIX systems. Can be ' +
+ 'used multiple times. Useful to link custom malloc ' +
+ 'or cpu profiling libraries.'))
options, args = parser.parse_args(argv)
if args:
@@ -134,10 +165,11 @@ def GenerateLastCommitPosition(host, header):
#ifndef OUT_LAST_COMMIT_POSITION_H_
#define OUT_LAST_COMMIT_POSITION_H_
+#define LAST_COMMIT_POSITION_NUM %s
#define LAST_COMMIT_POSITION "%s (%s)"
#endif // OUT_LAST_COMMIT_POSITION_H_
-''' % (mo.group(1), mo.group(2))
+''' % (mo.group(1), mo.group(1), mo.group(2))
# Only write/touch this file if the commit position has changed.
old_contents = ''
@@ -151,18 +183,20 @@ def GenerateLastCommitPosition(host, header):
def WriteGenericNinja(path, static_libraries, executables,
- cc, cxx, ar, ld, platform, host, options,
- cflags=[], cflags_cc=[], ldflags=[], libflags=[],
+ cxx, ar, ld, platform, host, options,
+ cflags=[], ldflags=[], libflags=[],
include_dirs=[], solibs=[]):
+ args = ' -d' if options.debug else ''
+ for link_lib in options.link_libs:
+ args += ' --link-lib=' + shell_quote(link_lib)
+
ninja_header_lines = [
- 'cc = ' + cc,
'cxx = ' + cxx,
'ar = ' + ar,
'ld = ' + ld,
'',
'rule regen',
- ' command = %s ../build/gen.py%s' % (
- sys.executable, ' -d' if options.debug else ''),
+ ' command = %s ../build/gen.py%s' % (sys.executable, args),
' description = Regenerating ninja files',
'',
'build build.ninja: regen',
@@ -174,11 +208,16 @@ def WriteGenericNinja(path, static_libraries, executables,
template_filename = os.path.join(SCRIPT_DIR, {
'msvc': 'build_win.ninja.template',
+ 'mingw': 'build_linux.ninja.template',
+ 'msys': 'build_linux.ninja.template',
'darwin': 'build_mac.ninja.template',
'linux': 'build_linux.ninja.template',
'freebsd': 'build_linux.ninja.template',
'aix': 'build_aix.ninja.template',
'openbsd': 'build_openbsd.ninja.template',
+ 'haiku': 'build_haiku.ninja.template',
+ 'solaris': 'build_linux.ninja.template',
+ 'netbsd': 'build_linux.ninja.template',
}[platform.platform()])
with open(template_filename) as f:
@@ -205,18 +244,14 @@ def WriteGenericNinja(path, static_libraries, executables,
ninja_lines = []
def build_source(src_file, settings):
ninja_lines.extend([
- 'build %s: %s %s' % (src_to_obj(src_file),
- settings['tool'],
- escape_path_ninja(
- os.path.relpath(
- os.path.join(REPO_ROOT, src_file),
- os.path.dirname(path)))),
+ 'build %s: cxx %s' % (src_to_obj(src_file),
+ escape_path_ninja(
+ os.path.relpath(
+ os.path.join(REPO_ROOT, src_file),
+ os.path.dirname(path)))),
' includes = %s' % ' '.join(
- ['-I' + escape_path_ninja(dirname) for dirname in
- include_dirs + settings.get('include_dirs', [])]),
- ' cflags = %s' % ' '.join(cflags + settings.get('cflags', [])),
- ' cflags_cc = %s' %
- ' '.join(cflags_cc + settings.get('cflags_cc', [])),
+ ['-I' + escape_path_ninja(dirname) for dirname in include_dirs]),
+ ' cflags = %s' % ' '.join(cflags),
])
for library, settings in static_libraries.items():
@@ -260,26 +295,30 @@ def WriteGenericNinja(path, static_libraries, executables,
def WriteGNNinja(path, platform, host, options):
if platform.is_msvc():
- cc = os.environ.get('CC', 'cl.exe')
cxx = os.environ.get('CXX', 'cl.exe')
ld = os.environ.get('LD', 'link.exe')
ar = os.environ.get('AR', 'lib.exe')
elif platform.is_aix():
- cc = os.environ.get('CC', 'gcc')
cxx = os.environ.get('CXX', 'g++')
ld = os.environ.get('LD', 'g++')
ar = os.environ.get('AR', 'ar -X64')
+ elif platform.is_msys() or platform.is_mingw():
+ cxx = os.environ.get('CXX', 'g++')
+ ld = os.environ.get('LD', 'g++')
+ ar = os.environ.get('AR', 'ar')
else:
- cc = os.environ.get('CC', 'clang')
cxx = os.environ.get('CXX', 'clang++')
ld = cxx
ar = os.environ.get('AR', 'ar')
cflags = os.environ.get('CFLAGS', '').split()
- cflags_cc = os.environ.get('CXXFLAGS', '').split()
+ cflags += os.environ.get('CXXFLAGS', '').split()
ldflags = os.environ.get('LDFLAGS', '').split()
libflags = os.environ.get('LIBFLAGS', '').split()
- include_dirs = [os.path.relpath(REPO_ROOT, os.path.dirname(path)), '.']
+ include_dirs = [
+ os.path.relpath(os.path.join(REPO_ROOT, 'src'), os.path.dirname(path)),
+ '.',
+ ]
libs = []
if not platform.is_msvc():
@@ -298,7 +337,7 @@ def WriteGNNinja(path, platform, host, options):
ldflags.extend(['-fdata-sections', '-ffunction-sections'])
if platform.is_darwin():
ldflags.append('-Wl,-dead_strip')
- elif not platform.is_aix():
+ elif not platform.is_aix() and not platform.is_solaris():
# Garbage collection is done by default on aix.
ldflags.append('-Wl,--gc-sections')
@@ -308,6 +347,8 @@ def WriteGNNinja(path, platform, host, options):
ldflags.append('-Wl,-S')
elif platform.is_aix():
ldflags.append('-Wl,-s')
+ elif platform.is_solaris():
+ ldflags.append('-Wl,--strip-all')
else:
ldflags.append('-Wl,-strip-all')
@@ -315,6 +356,10 @@ def WriteGNNinja(path, platform, host, options):
if options.use_icf and not platform.is_darwin():
ldflags.append('-Wl,--icf=all')
+ if options.use_lto:
+ cflags.extend(['-flto', '-fwhole-program-vtables'])
+ ldflags.extend(['-flto', '-fwhole-program-vtables'])
+
cflags.extend([
'-D_FILE_OFFSET_BITS=64',
'-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS',
@@ -323,37 +368,69 @@ def WriteGNNinja(path, platform, host, options):
'-fno-exceptions',
'-fno-rtti',
'-fdiagnostics-color',
+ '-Wall',
+ '-Wextra',
+ '-Wno-unused-parameter',
+ '-std=c++17'
])
- cflags_cc.extend(['-std=c++14', '-Wno-c++11-narrowing'])
- if platform.is_linux():
+ if platform.is_linux() or platform.is_mingw() or platform.is_msys():
ldflags.append('-Wl,--as-needed')
if not options.no_static_libstdcpp:
ldflags.append('-static-libstdc++')
- # This is needed by libc++.
- libs.append('-ldl')
+ if platform.is_mingw() or platform.is_msys():
+ cflags.remove('-std=c++17')
+ cflags.extend([
+ '-Wno-deprecated-copy',
+ '-Wno-implicit-fallthrough',
+ '-Wno-redundant-move',
+ '-Wno-unused-variable',
+ '-Wno-format', # Use of %llx, which is supported by _UCRT, false positive
+ '-Wno-strict-aliasing', # Dereferencing punned pointer
+ '-Wno-cast-function-type', # Casting FARPROC to RegDeleteKeyExPtr
+ '-std=gnu++17',
+ ])
+ else:
+ # This is needed by libc++.
+ libs.append('-ldl')
elif platform.is_darwin():
min_mac_version_flag = '-mmacosx-version-min=10.9'
cflags.append(min_mac_version_flag)
ldflags.append(min_mac_version_flag)
elif platform.is_aix():
- cflags_cc.append('-maix64')
+ cflags.append('-maix64')
ldflags.append('-maix64')
+ elif platform.is_haiku():
+ cflags.append('-fPIC')
+ cflags.extend(['-D_BSD_SOURCE'])
- if platform.is_posix():
+ if platform.is_posix() and not platform.is_haiku():
ldflags.append('-pthread')
- if options.use_lto:
- cflags.extend(['-flto', '-fwhole-program-vtables'])
- ldflags.extend(['-flto', '-fwhole-program-vtables'])
-
+ if platform.is_mingw() or platform.is_msys():
+ cflags.extend(['-DUNICODE',
+ '-DNOMINMAX',
+ '-DWIN32_LEAN_AND_MEAN',
+ '-DWINVER=0x0A00',
+ '-D_CRT_SECURE_NO_DEPRECATE',
+ '-D_SCL_SECURE_NO_DEPRECATE',
+ '-D_UNICODE',
+ '-D_WIN32_WINNT=0x0A00',
+ '-D_HAS_EXCEPTIONS=0'
+ ])
elif platform.is_msvc():
if not options.debug:
- cflags.extend(['/O2', '/DNDEBUG', '/GL'])
- libflags.extend(['/LTCG'])
- ldflags.extend(['/LTCG', '/OPT:REF', '/OPT:ICF'])
+ cflags.extend(['/O2', '/DNDEBUG', '/Zc:inline'])
+ ldflags.extend(['/OPT:REF'])
+
+ if options.use_icf:
+ libflags.extend(['/OPT:ICF'])
+ if options.use_lto:
+ cflags.extend(['/GL'])
+ libflags.extend(['/LTCG'])
+ ldflags.extend(['/LTCG'])
cflags.extend([
'/DNOMINMAX',
@@ -376,8 +453,7 @@ def WriteGNNinja(path, platform, host, options):
'/wd4505',
'/wd4838',
'/wd4996',
- ])
- cflags_cc.extend([
+ '/std:c++17',
'/GR-',
'/D_HAS_EXCEPTIONS=0',
])
@@ -386,307 +462,346 @@ def WriteGNNinja(path, platform, host, options):
static_libraries = {
'base': {'sources': [
- 'base/callback_internal.cc',
- 'base/command_line.cc',
- 'base/environment.cc',
- 'base/files/file.cc',
- 'base/files/file_enumerator.cc',
- 'base/files/file_path.cc',
- 'base/files/file_path_constants.cc',
- 'base/files/file_util.cc',
- 'base/files/scoped_file.cc',
- 'base/files/scoped_temp_dir.cc',
- 'base/json/json_parser.cc',
- 'base/json/json_reader.cc',
- 'base/json/json_writer.cc',
- 'base/json/string_escape.cc',
- 'base/logging.cc',
- 'base/md5.cc',
- 'base/memory/ref_counted.cc',
- 'base/memory/weak_ptr.cc',
- 'base/sha1.cc',
- 'base/strings/string_number_conversions.cc',
- 'base/strings/string_piece.cc',
- 'base/strings/string_split.cc',
- 'base/strings/string_util.cc',
- 'base/strings/string_util_constants.cc',
- 'base/strings/stringprintf.cc',
- 'base/strings/utf_string_conversion_utils.cc',
- 'base/strings/utf_string_conversions.cc',
- 'base/third_party/icu/icu_utf.cc',
- 'base/timer/elapsed_timer.cc',
- 'base/value_iterators.cc',
- 'base/values.cc',
- ], 'tool': 'cxx', 'include_dirs': []},
+ 'src/base/command_line.cc',
+ 'src/base/environment.cc',
+ 'src/base/files/file.cc',
+ 'src/base/files/file_enumerator.cc',
+ 'src/base/files/file_path.cc',
+ 'src/base/files/file_path_constants.cc',
+ 'src/base/files/file_util.cc',
+ 'src/base/files/scoped_file.cc',
+ 'src/base/files/scoped_temp_dir.cc',
+ 'src/base/json/json_parser.cc',
+ 'src/base/json/json_reader.cc',
+ 'src/base/json/json_writer.cc',
+ 'src/base/json/string_escape.cc',
+ 'src/base/logging.cc',
+ 'src/base/md5.cc',
+ 'src/base/memory/ref_counted.cc',
+ 'src/base/memory/weak_ptr.cc',
+ 'src/base/sha1.cc',
+ 'src/base/strings/string_number_conversions.cc',
+ 'src/base/strings/string_split.cc',
+ 'src/base/strings/string_util.cc',
+ 'src/base/strings/string_util_constants.cc',
+ 'src/base/strings/stringprintf.cc',
+ 'src/base/strings/utf_string_conversion_utils.cc',
+ 'src/base/strings/utf_string_conversions.cc',
+ 'src/base/third_party/icu/icu_utf.cc',
+ 'src/base/timer/elapsed_timer.cc',
+ 'src/base/value_iterators.cc',
+ 'src/base/values.cc',
+ ]},
'gn_lib': {'sources': [
- 'tools/gn/action_target_generator.cc',
- 'tools/gn/action_values.cc',
- 'tools/gn/analyzer.cc',
- 'tools/gn/args.cc',
- 'tools/gn/binary_target_generator.cc',
- 'tools/gn/builder.cc',
- 'tools/gn/builder_record.cc',
- 'tools/gn/build_settings.cc',
- 'tools/gn/bundle_data.cc',
- 'tools/gn/bundle_data_target_generator.cc',
- 'tools/gn/bundle_file_rule.cc',
- 'tools/gn/c_include_iterator.cc',
- 'tools/gn/c_substitution_type.cc',
- 'tools/gn/c_tool.cc',
- 'tools/gn/command_analyze.cc',
- 'tools/gn/command_args.cc',
- 'tools/gn/command_check.cc',
- 'tools/gn/command_clean.cc',
- 'tools/gn/command_desc.cc',
- 'tools/gn/command_format.cc',
- 'tools/gn/command_gen.cc',
- 'tools/gn/command_help.cc',
- 'tools/gn/command_meta.cc',
- 'tools/gn/command_ls.cc',
- 'tools/gn/command_path.cc',
- 'tools/gn/command_refs.cc',
- 'tools/gn/commands.cc',
- 'tools/gn/compile_commands_writer.cc',
- 'tools/gn/config.cc',
- 'tools/gn/config_values.cc',
- 'tools/gn/config_values_extractors.cc',
- 'tools/gn/config_values_generator.cc',
- 'tools/gn/copy_target_generator.cc',
- 'tools/gn/create_bundle_target_generator.cc',
- 'tools/gn/deps_iterator.cc',
- 'tools/gn/desc_builder.cc',
- 'tools/gn/eclipse_writer.cc',
- 'tools/gn/err.cc',
- 'tools/gn/escape.cc',
- 'tools/gn/exec_process.cc',
- 'tools/gn/filesystem_utils.cc',
- 'tools/gn/function_exec_script.cc',
- 'tools/gn/function_foreach.cc',
- 'tools/gn/function_forward_variables_from.cc',
- 'tools/gn/function_get_label_info.cc',
- 'tools/gn/function_get_path_info.cc',
- 'tools/gn/function_get_target_outputs.cc',
- 'tools/gn/function_process_file_template.cc',
- 'tools/gn/function_read_file.cc',
- 'tools/gn/function_rebase_path.cc',
- 'tools/gn/functions.cc',
- 'tools/gn/function_set_defaults.cc',
- 'tools/gn/function_set_default_toolchain.cc',
- 'tools/gn/functions_target.cc',
- 'tools/gn/function_template.cc',
- 'tools/gn/function_toolchain.cc',
- 'tools/gn/function_write_file.cc',
- 'tools/gn/general_tool.cc',
- 'tools/gn/generated_file_target_generator.cc',
- 'tools/gn/group_target_generator.cc',
- 'tools/gn/header_checker.cc',
- 'tools/gn/import_manager.cc',
- 'tools/gn/inherited_libraries.cc',
- 'tools/gn/input_conversion.cc',
- 'tools/gn/input_file.cc',
- 'tools/gn/input_file_manager.cc',
- 'tools/gn/item.cc',
- 'tools/gn/json_project_writer.cc',
- 'tools/gn/label.cc',
- 'tools/gn/label_pattern.cc',
- 'tools/gn/lib_file.cc',
- 'tools/gn/loader.cc',
- 'tools/gn/location.cc',
- 'tools/gn/metadata.cc',
- 'tools/gn/metadata_walk.cc',
- 'tools/gn/ninja_action_target_writer.cc',
- 'tools/gn/ninja_binary_target_writer.cc',
- 'tools/gn/ninja_build_writer.cc',
- 'tools/gn/ninja_bundle_data_target_writer.cc',
- 'tools/gn/ninja_c_binary_target_writer.cc',
- 'tools/gn/ninja_copy_target_writer.cc',
- 'tools/gn/ninja_create_bundle_target_writer.cc',
- 'tools/gn/ninja_generated_file_target_writer.cc',
- 'tools/gn/ninja_group_target_writer.cc',
- 'tools/gn/ninja_rust_binary_target_writer.cc',
- 'tools/gn/ninja_target_command_util.cc',
- 'tools/gn/ninja_target_writer.cc',
- 'tools/gn/ninja_toolchain_writer.cc',
- 'tools/gn/ninja_utils.cc',
- 'tools/gn/ninja_writer.cc',
- 'tools/gn/operators.cc',
- 'tools/gn/output_conversion.cc',
- 'tools/gn/output_file.cc',
- 'tools/gn/parse_node_value_adapter.cc',
- 'tools/gn/parser.cc',
- 'tools/gn/parse_tree.cc',
- 'tools/gn/path_output.cc',
- 'tools/gn/pattern.cc',
- 'tools/gn/pool.cc',
- 'tools/gn/qt_creator_writer.cc',
- 'tools/gn/runtime_deps.cc',
- 'tools/gn/rust_substitution_type.cc',
- 'tools/gn/rust_values_generator.cc',
- 'tools/gn/rust_tool.cc',
- 'tools/gn/rust_values.cc',
- 'tools/gn/rust_variables.cc',
- 'tools/gn/scheduler.cc',
- 'tools/gn/scope.cc',
- 'tools/gn/scope_per_file_provider.cc',
- 'tools/gn/settings.cc',
- 'tools/gn/setup.cc',
- 'tools/gn/source_dir.cc',
- 'tools/gn/source_file.cc',
- 'tools/gn/standard_out.cc',
- 'tools/gn/string_utils.cc',
- 'tools/gn/substitution_list.cc',
- 'tools/gn/substitution_pattern.cc',
- 'tools/gn/substitution_type.cc',
- 'tools/gn/substitution_writer.cc',
- 'tools/gn/switches.cc',
- 'tools/gn/target.cc',
- 'tools/gn/target_generator.cc',
- 'tools/gn/template.cc',
- 'tools/gn/token.cc',
- 'tools/gn/tokenizer.cc',
- 'tools/gn/tool.cc',
- 'tools/gn/toolchain.cc',
- 'tools/gn/trace.cc',
- 'tools/gn/value.cc',
- 'tools/gn/value_extractors.cc',
- 'tools/gn/variables.cc',
- 'tools/gn/visibility.cc',
- 'tools/gn/visual_studio_utils.cc',
- 'tools/gn/visual_studio_writer.cc',
- 'tools/gn/xcode_object.cc',
- 'tools/gn/xcode_writer.cc',
- 'tools/gn/xml_element_writer.cc',
- 'util/exe_path.cc',
- 'util/msg_loop.cc',
- 'util/semaphore.cc',
- 'util/sys_info.cc',
- 'util/ticks.cc',
- 'util/worker_pool.cc',
- ], 'tool': 'cxx', 'include_dirs': []},
+ 'src/gn/action_target_generator.cc',
+ 'src/gn/action_values.cc',
+ 'src/gn/analyzer.cc',
+ 'src/gn/args.cc',
+ 'src/gn/binary_target_generator.cc',
+ 'src/gn/build_settings.cc',
+ 'src/gn/builder.cc',
+ 'src/gn/builder_record.cc',
+ 'src/gn/bundle_data.cc',
+ 'src/gn/bundle_data_target_generator.cc',
+ 'src/gn/bundle_file_rule.cc',
+ 'src/gn/c_include_iterator.cc',
+ 'src/gn/c_substitution_type.cc',
+ 'src/gn/c_tool.cc',
+ 'src/gn/command_analyze.cc',
+ 'src/gn/command_args.cc',
+ 'src/gn/command_check.cc',
+ 'src/gn/command_clean.cc',
+ 'src/gn/command_clean_stale.cc',
+ 'src/gn/command_desc.cc',
+ 'src/gn/command_format.cc',
+ 'src/gn/command_gen.cc',
+ 'src/gn/command_help.cc',
+ 'src/gn/command_ls.cc',
+ 'src/gn/command_meta.cc',
+ 'src/gn/command_outputs.cc',
+ 'src/gn/command_path.cc',
+ 'src/gn/command_refs.cc',
+ 'src/gn/commands.cc',
+ 'src/gn/compile_commands_writer.cc',
+ 'src/gn/rust_project_writer.cc',
+ 'src/gn/config.cc',
+ 'src/gn/config_values.cc',
+ 'src/gn/config_values_extractors.cc',
+ 'src/gn/config_values_generator.cc',
+ 'src/gn/copy_target_generator.cc',
+ 'src/gn/create_bundle_target_generator.cc',
+ 'src/gn/deps_iterator.cc',
+ 'src/gn/desc_builder.cc',
+ 'src/gn/eclipse_writer.cc',
+ 'src/gn/err.cc',
+ 'src/gn/escape.cc',
+ 'src/gn/exec_process.cc',
+ 'src/gn/filesystem_utils.cc',
+ 'src/gn/file_writer.cc',
+ 'src/gn/frameworks_utils.cc',
+ 'src/gn/function_exec_script.cc',
+ 'src/gn/function_filter.cc',
+ 'src/gn/function_foreach.cc',
+ 'src/gn/function_forward_variables_from.cc',
+ 'src/gn/function_get_label_info.cc',
+ 'src/gn/function_get_path_info.cc',
+ 'src/gn/function_get_target_outputs.cc',
+ 'src/gn/function_process_file_template.cc',
+ 'src/gn/function_read_file.cc',
+ 'src/gn/function_rebase_path.cc',
+ 'src/gn/function_set_default_toolchain.cc',
+ 'src/gn/function_set_defaults.cc',
+ 'src/gn/function_template.cc',
+ 'src/gn/function_toolchain.cc',
+ 'src/gn/function_write_file.cc',
+ 'src/gn/functions.cc',
+ 'src/gn/functions_target.cc',
+ 'src/gn/general_tool.cc',
+ 'src/gn/generated_file_target_generator.cc',
+ 'src/gn/group_target_generator.cc',
+ 'src/gn/header_checker.cc',
+ 'src/gn/import_manager.cc',
+ 'src/gn/inherited_libraries.cc',
+ 'src/gn/input_conversion.cc',
+ 'src/gn/input_file.cc',
+ 'src/gn/input_file_manager.cc',
+ 'src/gn/item.cc',
+ 'src/gn/json_project_writer.cc',
+ 'src/gn/label.cc',
+ 'src/gn/label_pattern.cc',
+ 'src/gn/lib_file.cc',
+ 'src/gn/loader.cc',
+ 'src/gn/location.cc',
+ 'src/gn/metadata.cc',
+ 'src/gn/metadata_walk.cc',
+ 'src/gn/ninja_action_target_writer.cc',
+ 'src/gn/ninja_binary_target_writer.cc',
+ 'src/gn/ninja_build_writer.cc',
+ 'src/gn/ninja_bundle_data_target_writer.cc',
+ 'src/gn/ninja_c_binary_target_writer.cc',
+ 'src/gn/ninja_copy_target_writer.cc',
+ 'src/gn/ninja_create_bundle_target_writer.cc',
+ 'src/gn/ninja_generated_file_target_writer.cc',
+ 'src/gn/ninja_group_target_writer.cc',
+ 'src/gn/ninja_rust_binary_target_writer.cc',
+ 'src/gn/ninja_target_command_util.cc',
+ 'src/gn/ninja_target_writer.cc',
+ 'src/gn/ninja_toolchain_writer.cc',
+ 'src/gn/ninja_tools.cc',
+ 'src/gn/ninja_utils.cc',
+ 'src/gn/ninja_writer.cc',
+ 'src/gn/operators.cc',
+ 'src/gn/output_conversion.cc',
+ 'src/gn/output_file.cc',
+ 'src/gn/parse_node_value_adapter.cc',
+ 'src/gn/parse_tree.cc',
+ 'src/gn/parser.cc',
+ 'src/gn/path_output.cc',
+ 'src/gn/pattern.cc',
+ 'src/gn/pool.cc',
+ 'src/gn/qt_creator_writer.cc',
+ 'src/gn/runtime_deps.cc',
+ 'src/gn/rust_substitution_type.cc',
+ 'src/gn/rust_tool.cc',
+ 'src/gn/rust_values.cc',
+ 'src/gn/rust_values_generator.cc',
+ 'src/gn/rust_variables.cc',
+ 'src/gn/scheduler.cc',
+ 'src/gn/scope.cc',
+ 'src/gn/scope_per_file_provider.cc',
+ 'src/gn/settings.cc',
+ 'src/gn/setup.cc',
+ 'src/gn/source_dir.cc',
+ 'src/gn/source_file.cc',
+ 'src/gn/standard_out.cc',
+ 'src/gn/string_atom.cc',
+ 'src/gn/string_output_buffer.cc',
+ 'src/gn/string_utils.cc',
+ 'src/gn/substitution_list.cc',
+ 'src/gn/substitution_pattern.cc',
+ 'src/gn/substitution_type.cc',
+ 'src/gn/substitution_writer.cc',
+ 'src/gn/swift_values.cc',
+ 'src/gn/swift_values_generator.cc',
+ 'src/gn/swift_variables.cc',
+ 'src/gn/switches.cc',
+ 'src/gn/target.cc',
+ 'src/gn/target_generator.cc',
+ 'src/gn/template.cc',
+ 'src/gn/token.cc',
+ 'src/gn/tokenizer.cc',
+ 'src/gn/tool.cc',
+ 'src/gn/toolchain.cc',
+ 'src/gn/trace.cc',
+ 'src/gn/value.cc',
+ 'src/gn/value_extractors.cc',
+ 'src/gn/variables.cc',
+ 'src/gn/version.cc',
+ 'src/gn/visibility.cc',
+ 'src/gn/visual_studio_utils.cc',
+ 'src/gn/visual_studio_writer.cc',
+ 'src/gn/xcode_object.cc',
+ 'src/gn/xcode_writer.cc',
+ 'src/gn/xml_element_writer.cc',
+ 'src/util/exe_path.cc',
+ 'src/util/msg_loop.cc',
+ 'src/util/semaphore.cc',
+ 'src/util/sys_info.cc',
+ 'src/util/ticks.cc',
+ 'src/util/worker_pool.cc',
+ ]},
}
executables = {
- 'gn': {'sources': [ 'tools/gn/gn_main.cc' ],
- 'tool': 'cxx', 'include_dirs': [], 'libs': []},
+ 'gn': {'sources': [ 'src/gn/gn_main.cc' ], 'libs': []},
'gn_unittests': { 'sources': [
- 'tools/gn/action_target_generator_unittest.cc',
- 'tools/gn/analyzer_unittest.cc',
- 'tools/gn/args_unittest.cc',
- 'tools/gn/builder_unittest.cc',
- 'tools/gn/c_include_iterator_unittest.cc',
- 'tools/gn/command_format_unittest.cc',
- 'tools/gn/compile_commands_writer_unittest.cc',
- 'tools/gn/config_unittest.cc',
- 'tools/gn/config_values_extractors_unittest.cc',
- 'tools/gn/escape_unittest.cc',
- 'tools/gn/exec_process_unittest.cc',
- 'tools/gn/filesystem_utils_unittest.cc',
- 'tools/gn/function_foreach_unittest.cc',
- 'tools/gn/function_forward_variables_from_unittest.cc',
- 'tools/gn/function_get_label_info_unittest.cc',
- 'tools/gn/function_get_path_info_unittest.cc',
- 'tools/gn/function_get_target_outputs_unittest.cc',
- 'tools/gn/function_process_file_template_unittest.cc',
- 'tools/gn/function_rebase_path_unittest.cc',
- 'tools/gn/function_template_unittest.cc',
- 'tools/gn/function_toolchain_unittest.cc',
- 'tools/gn/function_write_file_unittest.cc',
- 'tools/gn/functions_target_unittest.cc',
- 'tools/gn/functions_target_rust_unittest.cc',
- 'tools/gn/functions_unittest.cc',
- 'tools/gn/header_checker_unittest.cc',
- 'tools/gn/inherited_libraries_unittest.cc',
- 'tools/gn/input_conversion_unittest.cc',
- 'tools/gn/json_project_writer_unittest.cc',
- 'tools/gn/label_pattern_unittest.cc',
- 'tools/gn/label_unittest.cc',
- 'tools/gn/loader_unittest.cc',
- 'tools/gn/metadata_unittest.cc',
- 'tools/gn/metadata_walk_unittest.cc',
- 'tools/gn/ninja_action_target_writer_unittest.cc',
- 'tools/gn/ninja_binary_target_writer_unittest.cc',
- 'tools/gn/ninja_c_binary_target_writer_unittest.cc',
- 'tools/gn/ninja_build_writer_unittest.cc',
- 'tools/gn/ninja_bundle_data_target_writer_unittest.cc',
- 'tools/gn/ninja_copy_target_writer_unittest.cc',
- 'tools/gn/ninja_create_bundle_target_writer_unittest.cc',
- 'tools/gn/ninja_rust_binary_target_writer_unittest.cc',
- 'tools/gn/ninja_generated_file_target_writer_unittest.cc',
- 'tools/gn/ninja_group_target_writer_unittest.cc',
- 'tools/gn/ninja_target_writer_unittest.cc',
- 'tools/gn/ninja_toolchain_writer_unittest.cc',
- 'tools/gn/operators_unittest.cc',
- 'tools/gn/output_conversion_unittest.cc',
- 'tools/gn/parse_tree_unittest.cc',
- 'tools/gn/parser_unittest.cc',
- 'tools/gn/path_output_unittest.cc',
- 'tools/gn/pattern_unittest.cc',
- 'tools/gn/runtime_deps_unittest.cc',
- 'tools/gn/scope_per_file_provider_unittest.cc',
- 'tools/gn/scope_unittest.cc',
- 'tools/gn/setup_unittest.cc',
- 'tools/gn/source_dir_unittest.cc',
- 'tools/gn/source_file_unittest.cc',
- 'tools/gn/string_utils_unittest.cc',
- 'tools/gn/substitution_pattern_unittest.cc',
- 'tools/gn/substitution_writer_unittest.cc',
- 'tools/gn/target_unittest.cc',
- 'tools/gn/template_unittest.cc',
- 'tools/gn/test_with_scheduler.cc',
- 'tools/gn/test_with_scope.cc',
- 'tools/gn/tokenizer_unittest.cc',
- 'tools/gn/unique_vector_unittest.cc',
- 'tools/gn/value_unittest.cc',
- 'tools/gn/visibility_unittest.cc',
- 'tools/gn/visual_studio_utils_unittest.cc',
- 'tools/gn/visual_studio_writer_unittest.cc',
- 'tools/gn/xcode_object_unittest.cc',
- 'tools/gn/xml_element_writer_unittest.cc',
- 'util/test/gn_test.cc',
- ], 'tool': 'cxx', 'include_dirs': [], 'libs': []},
+ 'src/gn/action_target_generator_unittest.cc',
+ 'src/gn/analyzer_unittest.cc',
+ 'src/gn/args_unittest.cc',
+ 'src/gn/builder_unittest.cc',
+ 'src/gn/c_include_iterator_unittest.cc',
+ 'src/gn/command_format_unittest.cc',
+ 'src/gn/commands_unittest.cc',
+ 'src/gn/compile_commands_writer_unittest.cc',
+ 'src/gn/config_unittest.cc',
+ 'src/gn/config_values_extractors_unittest.cc',
+ 'src/gn/escape_unittest.cc',
+ 'src/gn/exec_process_unittest.cc',
+ 'src/gn/filesystem_utils_unittest.cc',
+ 'src/gn/file_writer_unittest.cc',
+ 'src/gn/frameworks_utils_unittest.cc',
+ 'src/gn/function_filter_unittest.cc',
+ 'src/gn/function_foreach_unittest.cc',
+ 'src/gn/function_forward_variables_from_unittest.cc',
+ 'src/gn/function_get_label_info_unittest.cc',
+ 'src/gn/function_get_path_info_unittest.cc',
+ 'src/gn/function_get_target_outputs_unittest.cc',
+ 'src/gn/function_process_file_template_unittest.cc',
+ 'src/gn/function_rebase_path_unittest.cc',
+ 'src/gn/function_template_unittest.cc',
+ 'src/gn/function_toolchain_unittest.cc',
+ 'src/gn/function_write_file_unittest.cc',
+ 'src/gn/functions_target_rust_unittest.cc',
+ 'src/gn/functions_target_unittest.cc',
+ 'src/gn/functions_unittest.cc',
+ 'src/gn/hash_table_base_unittest.cc',
+ 'src/gn/header_checker_unittest.cc',
+ 'src/gn/inherited_libraries_unittest.cc',
+ 'src/gn/input_conversion_unittest.cc',
+ 'src/gn/json_project_writer_unittest.cc',
+ 'src/gn/rust_project_writer_unittest.cc',
+ 'src/gn/rust_project_writer_helpers_unittest.cc',
+ 'src/gn/label_pattern_unittest.cc',
+ 'src/gn/label_unittest.cc',
+ 'src/gn/loader_unittest.cc',
+ 'src/gn/metadata_unittest.cc',
+ 'src/gn/metadata_walk_unittest.cc',
+ 'src/gn/ninja_action_target_writer_unittest.cc',
+ 'src/gn/ninja_binary_target_writer_unittest.cc',
+ 'src/gn/ninja_build_writer_unittest.cc',
+ 'src/gn/ninja_bundle_data_target_writer_unittest.cc',
+ 'src/gn/ninja_c_binary_target_writer_unittest.cc',
+ 'src/gn/ninja_copy_target_writer_unittest.cc',
+ 'src/gn/ninja_create_bundle_target_writer_unittest.cc',
+ 'src/gn/ninja_generated_file_target_writer_unittest.cc',
+ 'src/gn/ninja_group_target_writer_unittest.cc',
+ 'src/gn/ninja_rust_binary_target_writer_unittest.cc',
+ 'src/gn/ninja_target_command_util_unittest.cc',
+ 'src/gn/ninja_target_writer_unittest.cc',
+ 'src/gn/ninja_toolchain_writer_unittest.cc',
+ 'src/gn/operators_unittest.cc',
+ 'src/gn/output_conversion_unittest.cc',
+ 'src/gn/parse_tree_unittest.cc',
+ 'src/gn/parser_unittest.cc',
+ 'src/gn/path_output_unittest.cc',
+ 'src/gn/pattern_unittest.cc',
+ 'src/gn/runtime_deps_unittest.cc',
+ 'src/gn/scope_per_file_provider_unittest.cc',
+ 'src/gn/scope_unittest.cc',
+ 'src/gn/setup_unittest.cc',
+ 'src/gn/source_dir_unittest.cc',
+ 'src/gn/source_file_unittest.cc',
+ 'src/gn/string_atom_unittest.cc',
+ 'src/gn/string_output_buffer_unittest.cc',
+ 'src/gn/string_utils_unittest.cc',
+ 'src/gn/substitution_pattern_unittest.cc',
+ 'src/gn/substitution_writer_unittest.cc',
+ 'src/gn/target_unittest.cc',
+ 'src/gn/template_unittest.cc',
+ 'src/gn/test_with_scheduler.cc',
+ 'src/gn/test_with_scope.cc',
+ 'src/gn/tokenizer_unittest.cc',
+ 'src/gn/unique_vector_unittest.cc',
+ 'src/gn/value_unittest.cc',
+ 'src/gn/vector_utils_unittest.cc',
+ 'src/gn/version_unittest.cc',
+ 'src/gn/visibility_unittest.cc',
+ 'src/gn/visual_studio_utils_unittest.cc',
+ 'src/gn/visual_studio_writer_unittest.cc',
+ 'src/gn/xcode_object_unittest.cc',
+ 'src/gn/xml_element_writer_unittest.cc',
+ 'src/util/test/gn_test.cc',
+ ], 'libs': []},
}
if platform.is_posix():
static_libraries['base']['sources'].extend([
- 'base/files/file_enumerator_posix.cc',
- 'base/files/file_posix.cc',
- 'base/files/file_util_posix.cc',
- 'base/posix/file_descriptor_shuffle.cc',
- 'base/posix/safe_strerror.cc',
- 'base/strings/string16.cc',
+ 'src/base/files/file_enumerator_posix.cc',
+ 'src/base/files/file_posix.cc',
+ 'src/base/files/file_util_posix.cc',
+ 'src/base/posix/file_descriptor_shuffle.cc',
+ 'src/base/posix/safe_strerror.cc',
])
if platform.is_windows():
static_libraries['base']['sources'].extend([
- 'base/files/file_enumerator_win.cc',
- 'base/files/file_util_win.cc',
- 'base/files/file_win.cc',
- 'base/win/registry.cc',
- 'base/win/scoped_handle.cc',
- 'base/win/scoped_process_information.cc',
+ 'src/base/files/file_enumerator_win.cc',
+ 'src/base/files/file_util_win.cc',
+ 'src/base/files/file_win.cc',
+ 'src/base/win/registry.cc',
+ 'src/base/win/scoped_handle.cc',
+ 'src/base/win/scoped_process_information.cc',
])
- libs.extend([
- 'advapi32.lib',
- 'dbghelp.lib',
- 'kernel32.lib',
- 'ole32.lib',
- 'shell32.lib',
- 'user32.lib',
- 'userenv.lib',
- 'version.lib',
- 'winmm.lib',
- 'ws2_32.lib',
- 'Shlwapi.lib',
- ])
+ if platform.is_msvc():
+ libs.extend([
+ 'advapi32.lib',
+ 'dbghelp.lib',
+ 'kernel32.lib',
+ 'ole32.lib',
+ 'shell32.lib',
+ 'user32.lib',
+ 'userenv.lib',
+ 'version.lib',
+ 'winmm.lib',
+ 'ws2_32.lib',
+ 'Shlwapi.lib',
+ ])
+ else:
+ libs.extend([
+ '-ladvapi32',
+ '-ldbghelp',
+ '-lkernel32',
+ '-lole32',
+ '-lshell32',
+ '-luser32',
+ '-luserenv',
+ '-lversion',
+ '-lwinmm',
+ '-lws2_32',
+ '-lshlwapi',
+ ])
+
+
+ libs.extend(options.link_libs)
# we just build static libraries that GN needs
executables['gn']['libs'].extend(static_libraries.keys())
executables['gn_unittests']['libs'].extend(static_libraries.keys())
- WriteGenericNinja(path, static_libraries, executables, cc, cxx, ar, ld,
- platform, host, options, cflags, cflags_cc, ldflags,
+ WriteGenericNinja(path, static_libraries, executables, cxx, ar, ld,
+ platform, host, options, cflags, ldflags,
libflags, include_dirs, libs)
diff --git a/gn/docs/cross_compiles.md b/gn/docs/cross_compiles.md
index 01de4567188..ad71b945458 100644
--- a/gn/docs/cross_compiles.md
+++ b/gn/docs/cross_compiles.md
@@ -111,10 +111,11 @@ config("my_config") {
## As a //build/config or //build/toolchain author
-The `default_toolchain` is declared in the `//build/config/BUILDCONFIG.gn`
-file. Usually the `default_toolchain` should be the toolchain for the
-`target_os` and `target_cpu`. The `current_toolchain` reflects the
-toolchain that is currently in effect for a rule.
+The `default_toolchain` is declared in the `BUILDCONFIG.gn` file (in Google
+projects this normally is in the `//build/config` directory). Usually the
+`default_toolchain` should be the toolchain for the `target_os` and
+`target_cpu`. The `current_toolchain` reflects the toolchain that is currently
+in effect for a rule.
Be sure you understand the differences between `host_cpu`, `target_cpu`,
`current_cpu`, and `toolchain_cpu` (and the os equivalents). The first
diff --git a/gn/docs/language.md b/gn/docs/language.md
index 16cb2a47ec7..137300adb1e 100644
--- a/gn/docs/language.md
+++ b/gn/docs/language.md
@@ -58,11 +58,6 @@ user-defined function calls, for example (templates are the closest thing). As
per the above design philosophy, if you need this kind of thing you're probably
doing it wrong.
-The variable `sources` has a special rule: when assigning to it, a list
-of exclusion patterns is applied to it. This is designed to
-automatically filter out some types of files. See `gn help
-set_sources_assignment_filter` and `gn help label_pattern` for more.
-
The full grammar for language nerds is available in `gn help grammar`.
### Strings
@@ -116,7 +111,7 @@ You can remove items from a list:
```
a = [ "first", "second", "third", "first" ]
b = a - [ "first" ] # [ "second", "third" ]
-a -= [ "second" ] # [ "first", "third", "fourth" ]
+a -= [ "second" ] # [ "first", "third", "first" ]
```
The - operator on a list searches for matches and removes all matching
@@ -471,7 +466,7 @@ root_build_dir) ```
Patterns are used to generate the output file names for a given set of
inputs for custom target types, and to automatically remove files from
-the `sources` variable (see `gn help set_sources_assignment_filter`).
+the list values (see `gn help filter_include` and `gn help filter_exclude`).
They are like simple regular expressions. See `gn help label_pattern`
for more.
diff --git a/gn/docs/quick_start.md b/gn/docs/quick_start.md
index d3941c54bf9..fc99e861ba9 100644
--- a/gn/docs/quick_start.md
+++ b/gn/docs/quick_start.md
@@ -2,24 +2,30 @@
[TOC]
-Note: the gn repo has been ported from chromium, and the tutorial within this
-document has not yet been updated to reflect this. Building the tutorial files
-will require the chromium tree, even though gn is now independent of chromium.
-
## Running GN
-You just run `gn` from the command line. There is a script in
-`depot_tools`, which is presumably in your PATH, with this name. The
-script will find the binary in the source tree containing the current
-directory and run it.
+You just run `gn` from the command line. For large projects, GN is versioned
+and distributed with the source checkout.
+
+ * For Chromium and Chromium-based projects, there is a script in
+ `depot_tools`, which is presumably in your PATH, with this name. The script
+ will find the binary in the source tree containing the current directory and
+ run it.
+
+ * For Fuchsia in-tree development, run `fx gn ...` which will find the right
+ GN binary and run it with the given arguments.
+
+ * For other projects, see your project's documentation.
## Setting up a build
-In GYP, the system would generate `Debug` and `Release` build
-directories for you and configure them accordingly. GN doesn't do this.
-Instead, you set up whatever build directory you want with whatever
-configuration you want. The Ninja files will be automatically
-regenerated if they're out of date when you build in that directory.
+Unlike some other build systems, with GN you set up your own build directories
+with the settings you want. This lets you maintain as many different builds in
+parallel as you need.
+
+Once you set up a build directory, the Ninja files will be automatically
+regenerated if they're out of date when you build in that directory so you
+should not have to re-run GN.
To make a build directory:
@@ -42,8 +48,9 @@ is_component_build = true
is_debug = false
```
-You can see the list of available arguments and their default values by
-typing
+The available variables will depend on your build (this example is from
+Chromium). You can see the list of available arguments and their default values
+by typing
```
gn args --list out/my_build
@@ -51,7 +58,7 @@ gn args --list out/my_build
on the command line. Note that you have to specify the build directory
for this command because the available arguments can change according
-to what's set.
+to the build.
Chrome developers can also read the [Chrome-specific build
configuration](http://www.chromium.org/developers/gn-build-configuration)
@@ -71,72 +78,56 @@ target_cpu = "x86"
target_cpu = "x64"
```
-See [GNCrossCompiles](cross_compiles.md) for more info.
-
-## Configuring goma
-
-Run `gn args out/Default` (substituting your build directory as needed).
-Add:
-
-```
-use_goma = true
-goma_dir = "~/foo/bar/goma"
-```
-
-If your goma is in the default location (`~/goma`) then you can omit the
-`goma_dir` line.
-
-## Configuring component mode
-
-This is a build arg like the goma flags. run `gn args out/Default` and add:
-
-```
-is_component_build = true
-```
+See [GN cross compiles](cross_compiles.md) for more info.
## Step-by-step
### Adding a build file
-Create a `tools/gn/tutorial/BUILD.gn` file [in the chromium repo](https://cs.chromium.org/chromium/src/tools/gn/tutorial/) and enter
-the following:
+Go to the directory `examples/simple_build`. This is the root of a minimal GN
+repository.
+
+In that directory there is a `tutorial` directory. There is already a
+`tutorial.cc` file that's not hooked up to the build. Create a new `BUILD.gn`
+file in that directory for our new target.
```
-executable("hello_world") {
+executable("tutorial") {
sources = [
- "hello_world.cc",
+ "tutorial.cc",
]
}
```
-There should already be a `hello_world.cc` file in that directory,
-containing what you expect. That's it! Now we just need to tell the
-build about this file. Open the `BUILD.gn` file in the root directory
-and add the label of this target to the dependencies of one of the root
-groups (a "group" target is a meta-target that is just a collection of
-other targets):
+Now we just need to tell the build about this new target. Open the `BUILD.gn`
+file in the parent (`simple_build`) directory. GN starts by loading this root
+file, and then loads all dependencies ourward from here, so we just need to add
+a reference to our new target from this file.
+
+You could add our new target as a dependency from one of the existing targets in
+the `simple_build/BUILD.gn` file, but it usually doesn't make a lot of sense to
+have an executable as a depdency of another executable (they can't be linked).
+So let's make a "tools" group. In GN, a "group" is just a collection of
+dependencies that's not complied or linked:
```
-group("root") {
+group("tools") {
deps = [
- ...
- "//url",
- "//tools/gn/tutorial:hello_world",
+ # This will expand to the name "//tutorial:tutorial" which is the full name
+ # of our new target. Run "gn help labels" for more.
+ "//tutorial",
]
}
```
-You can see the label of your target is "//" (indicating the source
-root), followed by the directory name, a colon, and the target name.
-
### Testing your addition
-From the command line in the source root directory:
+From the command line in the `simple_build` directory:
```
-gn gen out/Default
-ninja -C out/Default hello_world
-out/Default/hello_world
+gn gen out
+ninja -C out tutorial
+out/tutorial
```
You should see "Hello, world." output to the console.
@@ -146,50 +137,73 @@ unique. To build one of these, you can pass the label with its path (but no lead
"//") to ninja:
```
-ninja -C out/Default tools/gn/tutorial:hello_world
+ninja -C out some/path/to/target:my_target
```
### Declaring dependencies
-Let's make a static library that has a function to say hello to random
-people. There is a source file `hello.cc` in that directory which has a
-function to do this. Open the `tools/gn/tutorial/BUILD.gn` file and add
-the static library to the bottom of the existing file:
+Let's look at the targets defined in
+[examples/simple_build/BUILD.gn](../examples/simple_build/BUILD.gn). There is a
+static library that defines one function, `GetStaticText()`:
```
-static_library("hello") {
+static_library("hello_static") {
sources = [
- "hello.cc",
+ "hello_static.cc",
+ "hello_static.h",
]
}
```
-Now let's add an executable that depends on this library:
+There is also a shared library that defines one function `GetSharedText()`:
```
-executable("say_hello") {
+shared_library("hello_shared") {
sources = [
- "say_hello.cc",
+ "hello_shared.cc",
+ "hello_shared.h",
]
+
+ defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
+}
+```
+
+This also illustrates how to set preprocessor defines for a target. To set more
+than one or to assign values, use this form:
+
+```
+defines = [
+ "HELLO_SHARED_IMPLEMENTATION",
+ "ENABLE_DOOM_MELON=0",
+]
+```
+
+Now let's look at the executable that depends on these two libraries:
+
+```
+executable("hello") {
+ sources = [
+ "hello.cc",
+ ]
+
deps = [
- ":hello",
+ ":hello_shared",
+ ":hello_static",
]
}
```
This executable includes one source file and depends on the previous
-static library. The static library is referenced by its label in the
-`deps`. You could have used the full label `//tools/gn/tutorial:hello`
-but if you're referencing a target in the same build file, you can use
-the shortcut `:hello`.
+two libraries. Labels starting with a colon refer to a target with that name in
+the current BUILD.gn file.
-### Test the static library version
+### Test the binary
-From the command line in the source root directory:
+From the command line in the `simple_build` directory:
```
-ninja -C out/Default say_hello
-out/Default/say_hello
+ninja -C out hello
+out/hello
```
Note that you **didn't** need to re-run GN. GN will automatically rebuild
@@ -197,94 +211,84 @@ the ninja files when any build file has changed. You know this happens
when ninja prints `[1/1] Regenerating ninja files` at the beginning of
execution.
-### Compiler settings
+### Putting settings in a config
-Our hello library has a new feature, the ability to say hello to two
-people at once. This feature is controlled by defining `TWO_PEOPLE`. We
-can add defines like so:
+Users of a library often need compiler flags, defines, and include directories
+applied to them. To do this, put all such settings into a "config" which is a
+named collection of settings (but not sources or dependencies):
```
-static_library("hello") {
- sources = [
- "hello.cc",
- ]
- defines = [
- "TWO_PEOPLE",
- ]
+config("my_lib_config") {
+ defines = [ "ENABLE_DOOM_MELON" ]
+ include_dirs = [ "//third_party/something" ]
}
```
-### Putting settings in a config
-
-However, users of the library also need to know about this define, and
-putting it in the static library target defines it only for the files
-there. If somebody else includes `hello.h`, they won't see the new
-definition. To see the new definition, everybody will have to define
-`TWO_PEOPLE`.
-
-GN has a concept called a "config" which encapsulates settings. Let's
-create one that defines our preprocessor define:
+To apply a config's settings to a target, add it to the `configs` list:
```
-config("hello_config") {
- defines = [
- "TWO_PEOPLE",
+static_library("hello_shared") {
+ ...
+ # Note "+=" here is usually required, see "default configs" below.
+ configs += [
+ ":my_lib_config",
]
}
```
-To apply these settings to your target, you only need to add the
-config's label to the list of configs in the target:
+A config can be applied to all targets that depend on the current one by putting
+its label in the `public_configs` list:
```
-static_library("hello") {
+static_library("hello_shared") {
...
- configs += [
- ":hello_config",
+ public_configs = [
+ ":my_lib_config",
]
}
```
-Note that you need "+=" here instead of "=" since the build
-configuration has a default set of configs applied to each target that
-set up the default build stuff. You want to add to this list rather than
-overwrite it. To see the default configs, you can use the `print`
-function in the build file or the `desc` command-line subcommand (see
-below for examples of both).
+The `public_configs` also applies to the current target, so there's no need to
+list a config in both places.
-### Dependent configs
+### Default configs
-This nicely encapsulates our settings, but still requires everybody that
-uses our library to set the config on themselves. It would be nice if
-everybody that depends on our `hello` library can get this
-automatically. Change your library definition to:
+The build configuration will set up some settings that apply to every target by
+default. These will normally be set as a default list of configs. You can see
+this using the "print" command which is useful for debugging:
```
-static_library("hello") {
- sources = [
- "hello.cc",
- ]
- all_dependent_configs = [
- ":hello_config"
- ]
+executable("hello") {
+ print(configs)
}
```
-This applies the `hello_config` to the `hello` target itself, plus all
-targets that transitively depend on the current one. Now everybody that
-depends on us will get our settings. You can also set `public_configs`
-which applies only to targets that directly depend on your target (not
-transitively).
+Running GN will print something like:
+
+```
+$ gn gen out
+["//build:compiler_defaults", "//build:executable_ldconfig"]
+Done. Made 5 targets from 5 files in 9ms
+```
+
+Targets can modify this list to change their defaults. For example, the build
+setup might turn off exceptions by default by adding a `no_exceptions` config,
+but a target might re-enable them by replacing it with a different one:
+
+```
+executable("hello") {
+ ...
+ configs -= [ "//build:no_exceptions" ] # Remove global default.
+ configs += [ "//build:exceptions" ] # Replace with a different one.
+}
+```
-Now if you compile and run, you'll see the new version with two people:
+Our print command from above could also be expressed using string interpolation.
+This is a way to convert values to strings. It uses the symbol "$" to refer to a
+variable:
```
-> ninja -C out/Default say_hello
-ninja: Entering directory 'out/Default'
-[1/1] Regenerating ninja files
-[4/4] LINK say_hello
-> out/Default/say_hello
-Hello, Bill and Joy.
+print("The configs for the target $target_name are $configs")
```
## Add a new build argument
@@ -310,27 +314,13 @@ care should be used in scoping and naming arguments.
You can run GN in verbose mode to see lots of messages about what it's
doing. Use `-v` for this.
-### Print debugging
-
-There is a `print` command which just writes to stdout:
-
-```
-static_library("hello") {
- ...
- print(configs)
-}
-```
-
-This will print all of the configs applying to your target (including
-the default ones).
-
### The "desc" command
You can run `gn desc <build_dir> <targetname>` to get information about
a given target:
```
-gn desc out/Default //tools/gn/tutorial:say_hello
+gn desc out/Default //foo/bar:say_hello
```
will print out lots of exciting information. You can also print just one
@@ -338,17 +328,13 @@ section. Lets say you wanted to know where your `TWO_PEOPLE` define
came from on the `say_hello` target:
```
-> gn desc out/Default //tools/gn/tutorial:say_hello defines --blame
+> gn desc out/Default //foo/bar:say_hello defines --blame
...lots of other stuff omitted...
- From //tools/gn/tutorial:hello_config
- (Added by //tools/gn/tutorial/BUILD.gn:12)
+ From //foo/bar:hello_config
+ (Added by //foo/bar/BUILD.gn:12)
TWO_PEOPLE
```
-You can see that `TWO_PEOPLE` was defined by a config, and you can also
-see the which line caused that config to be applied to your target (in
-this case, the `all_dependent_configs` line).
-
Another particularly interesting variation:
```
@@ -356,17 +342,3 @@ gn desc out/Default //base:base_i18n deps --tree
```
See `gn help desc` for more.
-
-### Performance
-
-You can see what took a long time by running it with the `--time` command
-line flag. This will output a summary of timings for various things.
-
-You can also make a trace of how the build files were executed:
-
-```
-gn --tracelog=mylog.trace
-```
-
-and you can load the resulting file in Chrome's `about:tracing` page to
-look at everything.
diff --git a/gn/docs/reference.md b/gn/docs/reference.md
index 1601d01a5c2..33ee391e62a 100644
--- a/gn/docs/reference.md
+++ b/gn/docs/reference.md
@@ -9,12 +9,14 @@
* [args: Display or configure arguments declared by the build.](#cmd_args)
* [check: Check header dependencies.](#cmd_check)
* [clean: Cleans the output directory.](#cmd_clean)
+ * [clean_stale: Cleans the stale output files from the output directory.](#cmd_clean_stale)
* [desc: Show lots of insightful information about a target or config.](#cmd_desc)
* [format: Format .gn files.](#cmd_format)
* [gen: Generate ninja files.](#cmd_gen)
* [help: Does what you think.](#cmd_help)
* [ls: List matching targets.](#cmd_ls)
* [meta: List target metadata collection results.](#cmd_meta)
+ * [outputs: Which files a source/target make.](#cmd_outputs)
* [path: Find paths between two targets.](#cmd_path)
* [refs: Find stuff referencing a target or file.](#cmd_refs)
* [Target declarations](#targets)
@@ -28,6 +30,7 @@
* [group: Declare a named group of targets.](#func_group)
* [loadable_module: Declare a loadable module target.](#func_loadable_module)
* [rust_library: Declare a Rust library target.](#func_rust_library)
+ * [rust_proc_macro: Declare a Rust procedural macro target.](#func_rust_proc_macro)
* [shared_library: Declare a shared library target.](#func_shared_library)
* [source_set: Declare a source set target.](#func_source_set)
* [static_library: Declare a static library target.](#func_static_library)
@@ -38,6 +41,8 @@
* [declare_args: Declare build arguments.](#func_declare_args)
* [defined: Returns whether an identifier is defined.](#func_defined)
* [exec_script: Synchronously run a script and return the output.](#func_exec_script)
+ * [filter_exclude: Remove values that match a set of patterns.](#func_filter_exclude)
+ * [filter_include: Remove values that do not match a set of patterns.](#func_filter_include)
* [foreach: Iterate over a list.](#func_foreach)
* [forward_variables_from: Copies variables from a different scope.](#func_forward_variables_from)
* [get_label_info: Get an attribute from a target's label.](#func_get_label_info)
@@ -53,9 +58,10 @@
* [rebase_path: Rebase a file or directory to another location.](#func_rebase_path)
* [set_default_toolchain: Sets the default toolchain name.](#func_set_default_toolchain)
* [set_defaults: Set default values for a target type.](#func_set_defaults)
- * [set_sources_assignment_filter: Set a pattern to filter source files.](#func_set_sources_assignment_filter)
* [split_list: Splits a list into N different sub-lists.](#func_split_list)
+ * [string_join: Concatenates a list of strings with a separator.](#func_string_join)
* [string_replace: Replaces substring in the given string.](#func_string_replace)
+ * [string_split: Split string into a list of strings.](#func_string_split)
* [template: Define a template rule.](#func_template)
* [tool: Specify arguments to a toolchain tool.](#func_tool)
* [toolchain: Defines a toolchain.](#func_toolchain)
@@ -65,6 +71,7 @@
* [current_os: [string] The operating system of the current toolchain.](#var_current_os)
* [current_toolchain: [string] Label of the current toolchain.](#var_current_toolchain)
* [default_toolchain: [string] Label of the default toolchain.](#var_default_toolchain)
+ * [gn_version: [number] The version of gn.](#var_gn_version)
* [host_cpu: [string] The processor architecture that GN is running on.](#var_host_cpu)
* [host_os: [string] The operating system that GN is running on.](#var_host_os)
* [invoker: [string] The invoking scope inside a template.](#var_invoker)
@@ -85,6 +92,7 @@
* [args: [string list] Arguments passed to an action.](#var_args)
* [asmflags: [string list] Flags passed to the assembler.](#var_asmflags)
* [assert_no_deps: [label pattern list] Ensure no deps on these targets.](#var_assert_no_deps)
+ * [bridge_header: [string] Path to C/Objective-C compatibility header.](#var_bridge_header)
* [bundle_contents_dir: Expansion of {{bundle_contents_dir}} in create_bundle.](#var_bundle_contents_dir)
* [bundle_deps_filter: [label list] A list of labels that are filtered out.](#var_bundle_deps_filter)
* [bundle_executable_dir: Expansion of {{bundle_executable_dir}} in create_bundle](#var_bundle_executable_dir)
@@ -112,7 +120,9 @@
* [defines: [string list] C preprocessor defines.](#var_defines)
* [depfile: [string] File name for input dependencies for actions.](#var_depfile)
* [deps: [label list] Private linked dependencies.](#var_deps)
- * [edition: [string] The rustc edition to use in compiliation.](#var_edition)
+ * [externs: [scope] Set of Rust crate-dependency pairs.](#var_externs)
+ * [framework_dirs: [directory list] Additional framework search directories.](#var_framework_dirs)
+ * [frameworks: [name list] Name of frameworks that must be linked.](#var_frameworks)
* [friend: [label pattern list] Allow targets to include private headers.](#var_friend)
* [include_dirs: [directory list] Additional include directories.](#var_include_dirs)
* [inputs: [file list] Additional compile-time dependencies.](#var_inputs)
@@ -120,6 +130,7 @@
* [lib_dirs: [directory list] Additional library directories.](#var_lib_dirs)
* [libs: [string list] Additional libraries to link.](#var_libs)
* [metadata: [scope] Metadata of this target.](#var_metadata)
+ * [module_name: [string] The name for the compiled module.](#var_module_name)
* [output_conversion: Data format for generated_file targets.](#var_output_conversion)
* [output_dir: [directory] Directory to put output file in.](#var_output_dir)
* [output_extension: [string] Value to use for the output's file extension.](#var_output_extension)
@@ -139,10 +150,13 @@
* [response_file_contents: [string list] Contents of .rsp file for actions.](#var_response_file_contents)
* [script: [file name] Script file for actions.](#var_script)
* [sources: [file list] Source files for a target.](#var_sources)
+ * [swiftflags: [string list] Flags passed to the swift compiler.](#var_swiftflags)
* [testonly: [boolean] Declares a target must only be used for testing.](#var_testonly)
* [visibility: [label list] A list of labels that can depend on a target.](#var_visibility)
* [walk_keys: [string list] Key(s) for managing the metadata collection walk.](#var_walk_keys)
+ * [weak_frameworks: [name list] Name of frameworks that must be weak linked.](#var_weak_frameworks)
* [write_runtime_deps: Writes the target's runtime_deps to the given path.](#var_write_runtime_deps)
+ * [xcasset_compiler_flags: [string list] Flags passed to xcassets compiler](#var_xcasset_compiler_flags)
* [xcode_extra_attributes: [scope] Extra attributes for Xcode projects.](#var_xcode_extra_attributes)
* [xcode_test_application_name: [string] Name for Xcode test target.](#var_xcode_test_application_name)
* [Other help topics](#other)
@@ -152,6 +166,7 @@
* [execution: Build graph and execution overview.](#execution)
* [grammar: Language and grammar for GN build files.](#grammar)
* [input_conversion: Processing input from exec_script and read_file.](#io_conversion)
+ * [file_pattern: Matching more than one file.](#file_pattern)
* [label_pattern: Matching more than one label.](#label_pattern)
* [labels: About labels.](#labels)
* [metadata_collection: About metadata and its collection.](#metadata_collection)
@@ -194,6 +209,8 @@
This filtering behavior is also known as "pruning" the list of compile
targets.
+ If input_path is -, input is read from stdin.
+
output_path is a path indicating where the results of the command are to be
written. The results will be a file containing a JSON object with one or more
of following fields:
@@ -215,7 +232,7 @@
- "Found dependency"
- "No dependency"
- - "Found dependency (all) "
+ - "Found dependency (all)"
In the first case, the lists returned in compile_targets and test_targets
should be passed to ninja to build. In the second case, nothing was
@@ -227,6 +244,8 @@
a string describing the error. This includes cases where the input file is
not in the right format, or contains invalid targets.
+ If output_path is -, output is written to stdout.
+
The command returns 1 if it is unable to read the input file or write the
output file, or if there is something wrong with the build such that gen
would also fail, and 0 otherwise. In particular, it returns 0 even if the
@@ -341,22 +360,36 @@
#### **Command-specific switches**
```
- --force
- Ignores specifications of "check_includes = false" and checks all
- target's files that match the target label.
-
--check-generated
Generated files are normally not checked since they do not exist
until after a build. With this flag, those generated files that
can be found on disk are also checked.
+
+ --check-system
+ Check system style includes (using <angle brackets>) in addition to
+ "double quote" includes.
+
+ --default-toolchain
+ Normally wildcard targets are matched in all toolchains. This
+ switch makes wildcard labels with no explicit toolchain reference
+ only match targets in the default toolchain.
+
+ Non-wildcard inputs with no explicit toolchain specification will
+ always match only a target in the default toolchain if one exists.
+
+ --force
+ Ignores specifications of "check_includes = false" and checks all
+ target's files that match the target label.
```
#### **What gets checked**
```
The .gn file may specify a list of targets to be checked in the list
- check_targets (see "gn help dotfile"). If a label pattern is specified
- on the command line, check_targets is not used.
+ check_targets (see "gn help dotfile"). Alternatively, the .gn file may
+ specify a list of targets not to be checked in no_check_targets. If a label
+ pattern is specified on the command line, neither check_targets or
+ no_check_targets is used.
Targets can opt-out from checking with "check_includes = false" (see
"gn help check_includes").
@@ -372,8 +405,9 @@
- Includes with a "nogncheck" annotation are skipped (see
"gn help nogncheck").
- - Only includes using "quotes" are checked. <brackets> are assumed to be
- system includes.
+ - Includes using "quotes" are always checked.
+ If system style checking is enabled, includes using <angle brackets>
+ are also checked.
- Include paths are assumed to be relative to any of the "include_dirs" for
the target (including the implicit current dir).
@@ -446,12 +480,30 @@
gn check out/Default "//foo/*
Check only the files in targets in the //foo directory tree.
```
-### <a name="cmd_clean"></a>**gn clean &lt;out_dir&gt;**
+### <a name="cmd_clean"></a>**gn clean &lt;out_dir&gt;...**
```
Deletes the contents of the output directory except for args.gn and
creates a Ninja build environment sufficient to regenerate the build.
```
+### <a name="cmd_clean_stale"></a>**gn clean_stale [\--ninja-executable=...] &lt;out_dir&gt;...**
+
+```
+ Removes the no longer needed output files from the build directory and prunes
+ their records from the ninja build log and dependency database. These are
+ output files that were generated from previous builds, but the current build
+ graph no longer references them.
+
+ This command requires a ninja executable of at least version 1.10.0. The
+ executable must be provided by the --ninja-executable switch.
+```
+
+#### **Options**
+
+```
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use.
+```
### <a name="cmd_desc"></a>**gn desc**
```
@@ -484,6 +536,8 @@
defines [--blame]
depfile
deps [--all] [--tree] (see below)
+ framework_dirs
+ frameworks
include_dirs [--blame]
inputs
ldflags [--blame]
@@ -500,6 +554,7 @@
testonly
visibility
walk_keys
+ weak_frameworks
runtime_deps
Compute all runtime deps for the given target. This is a computed list
@@ -512,15 +567,15 @@
```
#### **Shared flags**
+
```
- --all-toolchains
- Normally only inputs in the default toolchain will be included.
- This switch will turn on matching all toolchains.
+ --default-toolchain
+ Normally wildcard targets are matched in all toolchains. This
+ switch makes wildcard labels with no explicit toolchain reference
+ only match targets in the default toolchain.
- For example, a file is in a target might be compiled twice:
- once in the default toolchain and once in a secondary one. Without
- this flag, only the default toolchain one will be matched by
- wildcards. With this flag, both will be matched.
+ Non-wildcard inputs with no explicit toolchain specification will
+ always match only a target in the default toolchain if one exists.
--format=json
Format the output as JSON instead of text.
@@ -531,9 +586,10 @@
```
--blame
Used with any value specified on a config, this will name the config that
- causes that target to get the flag. This doesn't currently work for libs
- and lib_dirs because those are inherited and are more complicated to
- figure out the blame (patches welcome).
+ causes that target to get the flag. This doesn't currently work for libs,
+ lib_dirs, frameworks, weak_frameworks and framework_dirs because those are
+ inherited and are more complicated to figure out the blame (patches
+ welcome).
```
#### **Configs**
@@ -563,6 +619,7 @@
--all
Collects all recursive dependencies and prints a sorted flat list. Also
usable with --tree (see below).
+
--as=(buildfile|label|output)
How to print targets.
@@ -589,6 +646,7 @@
Tree output can not be used with the filtering or output flags: --as,
--type, --testonly.
+
--type=(action|copy|executable|group|loadable_module|shared_library|
source_set|static_library)
Restrict outputs to targets matching the given type. If
@@ -638,9 +696,8 @@
```
--dry-run
- Does not change or output anything, but sets the process exit code based
- on whether output would be different than what's on disk. This is useful
- for presubmit/lint-type checks.
+ Prints the list of files that would be reformatted but does not write
+ anything to disk. This is useful for presubmit/lint-type checks.
- Exit code 0: successful format, matches on disk.
- Exit code 1: general failure (parse error, etc.)
- Exit code 2: successful format, but differs from on disk.
@@ -652,6 +709,12 @@
--stdin
Read input from stdin and write to stdout rather than update a file
in-place.
+
+ --read-tree=json
+ Reads an AST from stdin in the format output by --dump-tree=json and
+ uses that as the parse tree. (The only read-tree format currently
+ supported is json.) The given .gn file will be overwritten. This can be
+ used to programmatically transform .gn files.
```
#### **Examples**
@@ -660,6 +723,7 @@
gn format some\\BUILD.gn
gn format /abspath/some/BUILD.gn
gn format --stdin
+ gn format --read-tree=json //rewritten/BUILD.gn
```
### <a name="cmd_gen"></a>**gn gen [\--check] [&lt;ide options&gt;] &lt;out_dir&gt;**
@@ -672,22 +736,41 @@
Or it can be a directory relative to the current directory such as:
out/foo
- "gn gen --check" is the same as running "gn check". See "gn help check"
- for documentation on that mode.
+ "gn gen --check" is the same as running "gn check". "gn gen --check=system" is
+ the same as running "gn check --check-system". See "gn help check" for
+ documentation on that mode.
See "gn help switches" for the common command-line switches.
```
+#### **General options**
+
+```
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use. This executable will
+ be used as an IDE option to indicate which ninja to use for building. This
+ executable will also be used as part of the gen process for triggering a
+ restat on generated ninja files and for use with --clean-stale.
+
+ --clean-stale
+ This option will cause no longer needed output files to be removed from
+ the build directory, and their records pruned from the ninja build log and
+ dependency database after the ninja build graph has been generated. This
+ option requires a ninja executable of at least version 1.10.0. It can be
+ provided by the --ninja-executable switch. Also see "gn help clean_stale".
+```
+
#### **IDE options**
```
- GN optionally generates files for IDE. Possibilities for <ide options>
+ GN optionally generates files for IDE. Files won't be overwritten if their
+ contents don't change. Possibilities for <ide options>
--ide=<ide_name>
Generate files for an IDE. Currently supported values:
"eclipse" - Eclipse CDT settings file.
"vs" - Visual Studio project/solution files.
- (default Visual Studio version: 2017)
+ (default Visual Studio version: 2019)
"vs2013" - Visual Studio 2013 project/solution files.
"vs2015" - Visual Studio 2015 project/solution files.
"vs2017" - Visual Studio 2017 project/solution files.
@@ -727,15 +810,24 @@
#### **Xcode Flags**
```
- --workspace=<file_name>
- Override defaut workspace file name ("all"). The workspace file is
+ --xcode-project=<file_name>
+ Override default Xcode project file name ("all"). The project file is
written to the root build directory.
+ --xcode-build-system=<value>
+ Configure the build system to use for the Xcode project. Supported
+ values are (default to "legacy"):
+ "legacy" - Legacy Build system
+ "new" - New Build System
+
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use when building.
+
--ninja-extra-args=<string>
This string is passed without any quoting to the ninja invocation
command-line. Can be used to configure ninja flags, like "-j".
- --root-target=<target_name>
+ --ide-root-target=<target_name>
Name of the target corresponding to "All" target in Xcode. If unset,
"All" invokes ninja without any target and builds everything.
```
@@ -743,7 +835,7 @@
#### **QtCreator Flags**
```
- --root-target=<target_name>
+ --ide-root-target=<target_name>
Name of the root target for which the QtCreator project will be generated
to contain files of it and its dependencies. If unset, the whole build
graph will be emitted.
@@ -773,10 +865,10 @@
Overrides default file name (project.json) of generated JSON file.
--json-ide-script=<path_to_python_script>
- Executes python script after the JSON file is generated. Path can be
- project absolute (//), system absolute (/) or relative, in which case the
- output directory will be base. Path to generated JSON file will be first
- argument when invoking script.
+ Executes python script after the JSON file is generated or updated with
+ new content. Path can be project absolute (//), system absolute (/) or
+ relative, in which case the output directory will be base. Path to
+ generated JSON file will be first argument when invoking script.
--json-ide-script-args=<argument>
Optional second argument that will passed to executed script.
@@ -785,15 +877,27 @@
#### **Compilation Database**
```
+ --export-rust-project
+ Produces a rust-project.json file in the root of the build directory
+ This is used for various tools in the Rust ecosystem allowing for the
+ replay of individual compilations independent of the build system.
+ This is an unstable format and likely to change without warning.
+
--export-compile-commands[=<target_name1,target_name2...>]
Produces a compile_commands.json file in the root of the build directory
containing an array of “command objects”, where each command object
specifies one way a translation unit is compiled in the project. If a list
- of target_name is supplied, only targets that are reachable from the list
- of target_name will be used for “command objects” generation, otherwise
- all available targets will be used. This is used for various Clang-based
- tooling, allowing for the replay of individual compilations independent
- of the build system.
+ of target_name is supplied, only targets that are reachable from any
+ target in any build file whose name is target_name will be used for
+ “command objects” generation, otherwise all available targets will be used.
+ This is used for various Clang-based tooling, allowing for the replay of
+ individual compilations independent of the build system.
+ e.g. "foo" will match:
+ - "//path/to/src:foo"
+ - "//other/path:foo"
+ - "//foo:foo"
+ and not match:
+ - "//foo:bar"
```
### <a name="cmd_help"></a>**gn help &lt;anything&gt;**
@@ -817,7 +921,7 @@
gn help --markdown all
Dump all help to stdout in markdown format.
```
-### <a name="cmd_ls"></a>**gn ls &lt;out_dir&gt; [&lt;label_pattern&gt;] [\--all-toolchains] [\--as=...]**
+### <a name="cmd_ls"></a>**gn ls &lt;out_dir&gt; [&lt;label_pattern&gt;] [\--default-toolchain] [\--as=...]**
```
[--type=...] [--testonly=...]
@@ -845,14 +949,13 @@
Prints the first output file for the target relative to the
root build directory.
- --all-toolchains
- Normally only inputs in the default toolchain will be included.
- This switch will turn on matching all toolchains.
+ --default-toolchain
+ Normally wildcard targets are matched in all toolchains. This
+ switch makes wildcard labels with no explicit toolchain reference
+ only match targets in the default toolchain.
- For example, a file is in a target might be compiled twice:
- once in the default toolchain and once in a secondary one. Without
- this flag, only the default toolchain one will be matched by
- wildcards. With this flag, both will be matched.
+ Non-wildcard inputs with no explicit toolchain specification will
+ always match only a target in the default toolchain if one exists.
--testonly=(true|false)
Restrict outputs to targets with the testonly flag set
@@ -885,10 +988,6 @@
gn ls out/Debug "//base/*" --as=output | xargs ninja -C out/Debug
Builds all targets in //base and all subdirectories.
-
- gn ls out/Debug //base --all-toolchains
- Lists all variants of the target //base:base (it may be referenced
- in multiple toolchains).
```
### <a name="cmd_meta"></a>**gn meta**
@@ -947,6 +1046,60 @@
target and all of its dependency tree, rebasing the strings in the `files`
key onto the source directory of the target's declaration relative to "/".
```
+### <a name="cmd_outputs"></a>**gn outputs &lt;out_dir&gt; &lt;list of target or file names...&gt;**
+
+```
+ Lists the output files corresponding to the given target(s) or file name(s).
+ There can be multiple outputs because there can be more than one output
+ generated by a build step, and there can be more than one toolchain matched.
+ You can also list multiple inputs which will generate a union of all the
+ outputs from those inputs.
+
+ - The input target/file names are relative to the current directory.
+
+ - The output file names are relative to the root build directory.
+
+ This command is useful for finding a ninja command that will build only a
+ portion of the build.
+```
+
+#### **Target outputs**
+
+```
+ If the parameter is a target name that includes a toolchain, it will match
+ only that target in that toolchain. If no toolchain is specified, it will
+ match all targets with that name in any toolchain.
+
+ The result will be the outputs specified by that target which could be a
+ library, executable, output of an action, a stamp file, etc.
+```
+
+#### **File outputs**
+
+```
+ If the parameter is a file name it will compute the output for that compile
+ step for all targets in all toolchains that contain that file as a source
+ file.
+
+ If the source is not compiled (e.g. a header or text file), the command will
+ produce no output.
+
+ If the source is listed as an "input" to a binary target or action will
+ resolve to that target's outputs.
+```
+
+#### **Example**
+
+```
+ gn outputs out/debug some/directory:some_target
+ Find the outputs of a given target.
+
+ gn outputs out/debug src/project/my_file.cc | xargs ninja -C out/debug
+ Compiles just the given source file in all toolchains it's referenced in.
+
+ git diff --name-only | xargs gn outputs out/x64 | xargs ninja -C out/x64
+ Compiles all files changed in git.
+```
### <a name="cmd_path"></a>**gn path &lt;out_dir&gt; &lt;target_one&gt; &lt;target_two&gt;**
```
@@ -990,13 +1143,13 @@
#### **Example**
```
- gn path out/Default //base //tools/gn
+ gn path out/Default //base //gn
```
### <a name="cmd_refs"></a>**gn refs**
```
- gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)*
- [--all] [--all-toolchains] [--as=...] [--testonly=...] [--type=...]
+ gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all]
+ [--default-toolchain] [--as=...] [--testonly=...] [--type=...]
Finds reverse dependencies (which targets reference something). The input is
a list containing:
@@ -1032,14 +1185,6 @@
directly or indirectly on that file.
When used with --tree, turns off eliding to show a complete tree.
- --all-toolchains
- Normally only inputs in the default toolchain will be included.
- This switch will turn on matching all toolchains.
-
- For example, a file is in a target might be compiled twice:
- once in the default toolchain and once in a secondary one. Without
- this flag, only the default toolchain one will be matched by
- wildcards. With this flag, both will be matched.
--as=(buildfile|label|output)
How to print targets.
@@ -1053,10 +1198,19 @@
Prints the first output file for the target relative to the
root build directory.
+ --default-toolchain
+ Normally wildcard targets are matched in all toolchains. This
+ switch makes wildcard labels with no explicit toolchain reference
+ only match targets in the default toolchain.
+
+ Non-wildcard inputs with no explicit toolchain specification will
+ always match only a target in the default toolchain if one exists.
+
-q
Quiet. If nothing matches, don't print any output. Without this option, if
there are no matches there will be an informational message printed which
might interfere with scripts processing the output.
+
--testonly=(true|false)
Restrict outputs to targets with the testonly flag set
accordingly. When unspecified, the target's testonly flags are
@@ -1068,6 +1222,7 @@
Tree output can not be used with the filtering or output flags: --as,
--type, --testonly.
+
--type=(action|copy|executable|group|loadable_module|shared_library|
source_set|static_library)
Restrict outputs to targets matching the given type. If
@@ -1077,7 +1232,7 @@
#### **Examples (target input)**
```
- gn refs out/Debug //tools/gn:gn
+ gn refs out/Debug //gn:gn
Find all targets depending on the given exact target name.
gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim
@@ -1169,6 +1324,14 @@
file names to be relative to the build directory (file names in the
sources, outputs, and inputs will be all treated as relative to the
current build file and converted as needed automatically).
+
+ GN sets Ninja's flag 'restat = 1` for all action commands. This means
+ that Ninja will check the timestamp of the output after the action
+ completes. If output timestamp is unchanged, the step will be treated
+ as if it never needed to be rebuilt, potentially eliminating some
+ downstream steps for incremental builds. Scripts can improve build
+ performance by taking care not to change the timstamp of the output
+ file(s) if the contents have not changed.
```
#### **File name handling**
@@ -1250,6 +1413,14 @@
file names to be relative to the build directory (file names in the
sources, outputs, and inputs will be all treated as relative to the
current build file and converted as needed automatically).
+
+ GN sets Ninja's flag 'restat = 1` for all action commands. This means
+ that Ninja will check the timestamp of the output after the action
+ completes. If output timestamp is unchanged, the step will be treated
+ as if it never needed to be rebuilt, potentially eliminating some
+ downstream steps for incremental builds. Scripts can improve build
+ performance by taking care not to change the timstamp of the output
+ file(s) if the contents have not changed.
```
#### **File name handling**
@@ -1292,14 +1463,14 @@
args = [
"{{source}}",
"-o",
- rebase_path(relative_target_gen_dir, root_build_dir) +
+ rebase_path(target_gen_dir, root_build_dir) +
"/{{source_name_part}}.h" ]
}
```
### <a name="func_bundle_data"></a>**bundle_data**: [iOS/macOS] Declare a target without output.
```
- This target type allows to declare data that is required at runtime. It is
+ This target type allows one to declare data that is required at runtime. It is
used to inform "create_bundle" targets of the files to copy into generated
bundle, see "gn help create_bundle" for help.
@@ -1496,7 +1667,7 @@
bundle_resources_dir = bundle_contents_dir
bundle_executable_dir = bundle_contents_dir
- extra_attributes = {
+ xcode_extra_attributes = {
ONLY_ACTIVE_ARCH = "YES"
DEBUG_INFORMATION_FORMAT = "dwarf"
}
@@ -1555,13 +1726,13 @@
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
- Rust variables: aliased_deps, crate_root, crate_name, edition
+ Rust variables: aliased_deps, crate_root, crate_name
```
### <a name="func_generated_file"></a>**generated_file**: Declare a generated_file target.
@@ -1569,7 +1740,7 @@
Writes data value(s) to disk on resolution. This target type mirrors some
functionality of the write_file() function, but also provides the ability to
collect metadata from its dependencies on resolution rather than writing out
- parse time.
+ at parse time.
The `outputs` variable is required to be a list with a single element,
specifying the intended location of the output file.
@@ -1598,8 +1769,8 @@
doom_melon = [ "enable" ]
my_files = [ "foo.cpp" ]
- // Note: this is functionally equivalent to not defining `my_barrier`
- // at all in this target's metadata.
+ # Note: this is functionally equivalent to not defining `my_barrier`
+ # at all in this target's metadata.
my_barrier = [ "" ]
}
@@ -1749,13 +1920,13 @@
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
- Rust variables: aliased_deps, crate_root, crate_name, crate_type, edition
+ Rust variables: aliased_deps, crate_root, crate_name, crate_type
```
### <a name="func_rust_library"></a>**rust_library**: Declare a Rust library target.
@@ -1781,13 +1952,48 @@
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
- Rust variables: aliased_deps, crate_root, crate_name, edition
+ Rust variables: aliased_deps, crate_root, crate_name
+```
+### <a name="func_rust_proc_macro"></a>**rust_proc_macro**: Declare a Rust procedural macro target.
+
+```
+ A Rust procedural macro allows creating syntax extensions as execution of a
+ function. They are compiled as dynamic libraries and used by the compiler at
+ runtime.
+
+ Their use is the same as of other Rust libraries, but their build has some
+ additional restrictions in terms of supported flags.
+```
+
+#### **Language and compilation**
+
+```
+ The tools and commands used to create this target type will be
+ determined by the source files in its sources. Targets containing
+ multiple compiler-incompatible languages are not allowed (e.g. a
+ target containing both C and C++ sources is acceptable, but a
+ target containing C and Rust sources is not).
+```
+
+#### **Variables**
+
+```
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv, swiftflags
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
+ Rust variables: aliased_deps, crate_root, crate_name
```
### <a name="func_shared_library"></a>**shared_library**: Declare a shared library target.
@@ -1815,19 +2021,18 @@
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
- Rust variables: aliased_deps, crate_root, crate_name, crate_type, edition
+ Rust variables: aliased_deps, crate_root, crate_name, crate_type
```
### <a name="func_source_set"></a>**source_set**: Declare a source set target.
```
- The language of a source_set target is determined by the extensions present
- in its sources.
+ Only C-language source sets are supported at the moment.
```
#### **C-language source_sets**
@@ -1855,22 +2060,13 @@
when linking multiple static libraries into a shared library.
```
-#### **Rust-language source_sets**
-
-```
- A Rust source set is a collection of sources that get passed along to the
- final target that depends on it. No compilation is performed, and the source
- files are simply added as dependencies on the eventual rustc invocation that
- would produce a binary.
-```
-
#### **Variables**
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
@@ -1894,13 +2090,13 @@
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
- Rust variables: aliased_deps, crate_root, crate_name, edition
+ Rust variables: aliased_deps, crate_root, crate_name
The tools and commands used to create this target type will be
determined by the source files in its sources. Targets containing
@@ -2003,7 +2199,7 @@
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
libs, precompiled_header, precompiled_source, rustflags,
- rustenv
+ rustenv, swiftflags
Nested configs: configs
```
@@ -2042,8 +2238,9 @@
the current scope is not (since the overrides haven't been applied yet).
2. At the end of executing the block, any variables set within that scope
- are saved globally as build arguments, with their current values being
- saved as the "default value" for that argument.
+ are saved, with the values specified in the block used as the "default value"
+ for that argument. Once saved, these variables are available for override
+ via args.gn.
3. User-defined overrides are applied. Anything set in "gn args" now
overrides any default values. The resulting set of variables is promoted
@@ -2179,6 +2376,42 @@
# result.
exec_script("//foo/bar/myscript.py")
```
+### <a name="func_filter_exclude"></a>**filter_exclude**: Remove values that match a set of patterns.
+
+```
+ filter_exclude(values, exclude_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument exclude_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Any elements in values matching at least one
+ of those patterns will be excluded.
+```
+
+#### **Examples**
+```
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_exclude(values, [ "*.proto" ])
+ # result will be [ "foo.cc", "foo.h" ]
+```
+### <a name="func_filter_include"></a>**filter_include**: Remove values that do not match a set of patterns.
+
+```
+ filter_include(values, include_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument include_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Only elements from values matching at least
+ one of the pattern will be included.
+```
+
+#### **Examples**
+```
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_include(values, [ "*.proto" ])
+ # result will be [ "foo.proto" ]
+```
### <a name="func_foreach"></a>**foreach**: Iterate over a list.
```
@@ -2238,10 +2471,6 @@
important because most targets have an implicit configs list, which means it
wouldn't work at all if it didn't clobber).
- The sources assignment filter (see "gn help set_sources_assignment_filter")
- is never applied by this function. It's assumed than any desired filtering
- was already done when sources was set on the from_scope.
-
If variables_to_not_forward_list is non-empty, then it must contains a list
of variable names that will not be forwarded. This is mostly useful when
variable_list_or_star has a value of "*".
@@ -2471,12 +2700,6 @@
process_file_template will return for those inputs (see "gn help
process_file_template").
- binary targets (executables, libraries): this will return a list of the
- resulting binary file(s). The "main output" (the actual binary or library)
- will always be the 0th element in the result. Depending on the platform and
- output type, there may be other output files as well (like import libraries)
- which will follow.
-
source sets and groups: this will return a list containing the path of the
"stamp" file that Ninja will produce once all outputs are generated. This
probably isn't very useful.
@@ -2612,7 +2835,7 @@
toolchain("toolchain") {
tool("link") {
command = "..."
- pool = ":link_pool($default_toolchain)")
+ pool = ":link_pool($default_toolchain)"
}
}
```
@@ -2729,14 +2952,14 @@
with a double slash like "//foo/bar"), you should use the get_path_info()
function. This function won't work because it will always make relative
paths, and it needs to support making paths relative to the source root, so
- can't also generate source-absolute paths without more special-cases.
+ it can't also generate source-absolute paths without more special-cases.
```
#### **Arguments**
```
input
- A string or list of strings representing file or directory names These
+ A string or list of strings representing file or directory names. These
can be relative paths ("foo/bar.txt"), system absolute paths
("/foo/bar.txt"), or source absolute paths ("//foo/bar.txt").
@@ -2875,70 +3098,6 @@
configs -= [ "//tools/mything:settings" ]
}
```
-### <a name="func_set_sources_assignment_filter"></a>**set_sources_assignment_filter**: Set a pattern to filter source files.
-
-```
- The sources assignment filter is a list of patterns that remove files from
- the list implicitly whenever the "sources" variable is assigned to. This will
- do nothing for non-lists.
-
- This is intended to be used to globally filter out files with
- platform-specific naming schemes when they don't apply, for example you may
- want to filter out all "*_win.cc" files on non-Windows platforms.
-
- Typically this will be called once in the master build config script to set
- up the filter for the current platform. Subsequent calls will overwrite the
- previous values.
-
- If you want to bypass the filter and add a file even if it might be filtered
- out, call set_sources_assignment_filter([]) to clear the list of filters.
- This will apply until the current scope exits
-```
-
-#### **How to use patterns**
-
-```
- File patterns are VERY limited regular expressions. They must match the
- entire input string to be counted as a match. In regular expression parlance,
- there is an implicit "^...$" surrounding your input. If you want to match a
- substring, you need to use wildcards at the beginning and end.
-
- There are only two special tokens understood by the pattern matcher.
- Everything else is a literal.
-
- - "*" Matches zero or more of any character. It does not depend on the
- preceding character (in regular expression parlance it is equivalent to
- ".*").
-
- - "\b" Matches a path boundary. This will match the beginning or end of a
- string, or a slash.
-```
-
-#### **Pattern examples**
-
-```
- "*asdf*"
- Matches a string containing "asdf" anywhere.
-
- "asdf"
- Matches only the exact string "asdf".
-
- "*.cc"
- Matches strings ending in the literal ".cc".
-
- "\bwin/*"
- Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
-```
-
-#### **Sources assignment example**
-
-```
- # Filter out all _win files.
- set_sources_assignment_filter([ "*_win.cc", "*_win.h" ])
- sources = [ "a.cc", "b_win.cc" ]
- print(sources)
- # Will print [ "a.cc" ]. b_win one was filtered out.
-```
### <a name="func_split_list"></a>**split_list**: Splits a list into N different sub-lists.
```
@@ -2962,6 +3121,22 @@
Will print:
[[1, 2], [3, 4], [5, 6]
```
+### <a name="func_string_join"></a>**string_join**: Concatenates a list of strings with a separator.
+
+```
+ result = string_join(separator, strings)
+
+ Concatenate a list of strings with intervening occurrences of separator.
+```
+
+#### **Examples**
+
+```
+ string_join("", ["a", "b", "c"]) --> "abc"
+ string_join("|", ["a", "b", "c"]) --> "a|b|c"
+ string_join(", ", ["a", "b", "c"]) --> "a, b, c"
+ string_join("s", ["", ""]) --> "s"
+```
### <a name="func_string_replace"></a>**string_replace**: Replaces substring in the given string.
```
@@ -2983,6 +3158,33 @@
Will print:
Hello, GN!
```
+### <a name="func_string_split"></a>**string_split**: Split string into a list of strings.
+
+```
+ result = string_split(str[, sep])
+
+ Split string into all substrings separated by separator and returns a list
+ of the substrings between those separators.
+
+ If the separator argument is omitted, the split is by any whitespace, and
+ any leading/trailing whitespace is ignored; similar to Python's str.split().
+```
+
+#### **Examples without a separator (split on whitespace)**:
+
+```
+ string_split("") --> []
+ string_split("a") --> ["a"]
+ string_split(" aa bb") --> ["aa", "bb"]
+```
+
+#### **Examples with a separator (split on separators)**:
+
+```
+ string_split("", "|") --> [""]
+ string_split(" a b ", " ") --> ["", "", "a", "b", "", ""]
+ string_split("aa+-bb+-c", "+-") --> ["aa", "bb", "c"]
+```
### <a name="func_template"></a>**template**: Define a template rule.
```
@@ -3162,10 +3364,12 @@
Compiler tools:
"cc": C compiler
"cxx": C++ compiler
+ "cxx_module": C++ compiler used for Clang .modulemap files
"objc": Objective C compiler
"objcxx": Objective C++ compiler
"rc": Resource compiler (Windows .rc files)
"asm": Assembler
+ "swift": Swift compiler driver
Linker tools:
"alink": Linker for static libraries (archives)
@@ -3182,7 +3386,12 @@
"compile_xcassets": [iOS, macOS] Tool to compile asset catalogs.
Rust tools:
- "rustc": Rust compiler and linker
+ "rust_bin": Tool for compiling Rust binaries
+ "rust_cdylib": Tool for compiling C-compatible dynamic libraries.
+ "rust_dylib": Tool for compiling Rust dynamic libraries.
+ "rust_macro": Tool for compiling Rust procedural macros.
+ "rust_rlib": Tool for compiling Rust libraries.
+ "rust_staticlib": Tool for compiling Rust static libraries.
```
#### **Tool variables**
@@ -3259,7 +3468,7 @@
rlib_output_extension [string, optional, rust tools only]
dylib_output_extension [string, optional, rust tools only]
cdylib_output_extension [string, optional, rust tools only]
- proc_macro_output_extension [string, optional, rust tools only]
+ rust_proc_macro_output_extension [string, optional, rust tools only]
Valid for: Rust tools
These specify the default tool output for each of the crate types.
@@ -3274,12 +3483,49 @@
Valid for: Linker tools except "alink"
These strings will be prepended to the libraries and library search
- directories, respectively, because linkers differ on how specify them.
+ directories, respectively, because linkers differ on how to specify
+ them.
+
If you specified:
lib_switch = "-l"
lib_dir_switch = "-L"
- then the "{{libs}}" expansion for [ "freetype", "expat"] would be
- "-lfreetype -lexpat".
+ then the "{{libs}}" expansion for
+ [ "freetype", "expat" ]
+ would be
+ "-lfreetype -lexpat".
+
+ framework_switch [string, optional, link tools only]
+ weak_framework_switch [string, optional, link tools only]
+ framework_dir_switch [string, optional, link tools only]
+ Valid for: Linker tools
+
+ These strings will be prepended to the frameworks and framework search
+ path directories, respectively, because linkers differ on how to specify
+ them.
+
+ If you specified:
+ framework_switch = "-framework "
+ weak_framework_switch = "-weak_framework "
+ framework_dir_switch = "-F"
+ and:
+ framework_dirs = [ "$root_out_dir" ]
+ frameworks = [ "UIKit.framework", "Foo.framework" ]
+ weak_frameworks = [ "MediaPlayer.framework" ]
+ would be:
+ "-F. -framework UIKit -framework Foo -weak_framework MediaPlayer"
+
+ swiftmodule_switch [string, optional, link tools only]
+ Valid for: Linker tools except "alink"
+
+ The string will be prependend to the path to the .swiftmodule files
+ that are embedded in the linker output.
+
+ If you specified:
+ swiftmodule_swift = "-Wl,-add_ast_path,"
+ then the "{{swiftmodules}}" expansion for
+ [ "obj/foo/Foo.swiftmodule" ]
+ would be
+ "-Wl,-add_ast_path,obj/foo/Foo.swiftmodule"
outputs [list of strings with substitutions]
Valid for: Linker and compiler tools (required)
@@ -3306,11 +3552,21 @@
{{target_output_name}}, {{output_extension}} and {{output_dir}} allows
the target to override these values.
outputs = [
- "{{output_dir}}/{{target_output_name}}"
- "{{output_extension}}",
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}",
"{{output_dir}}/{{target_output_name}}.lib",
]
+ partial_outputs [list of strings with substitutions]
+ Valid for: "swift" only
+
+ An array of names for the partial outputs the tool produces. These
+ are relative to the build output directory. The expansion will be
+ evaluated for each file listed in the "sources" of the target.
+
+ This is used to deal with whole module optimization, allowing to
+ list one object file per source file when whole module optimization
+ is disabled.
+
pool [label, optional]
Valid for: all tools (optional)
@@ -3400,7 +3656,7 @@
tool("link") {
command = "link -o {{output}} {{ldflags}} @{{output}}.rsp"
rspfile = "{{output}}.rsp"
- rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
+ rspfile_content = "{{inputs}} {{solibs}} {{libs}} {{rlibs}}"
}
runtime_outputs [string list with substitutions]
@@ -3429,6 +3685,11 @@
{{target_output_name}}, this is not affected by the "output_prefix" in
the tool or the "output_name" set on the target.
+ {{label_no_toolchain}}
+ The label of the current target, never including the toolchain
+ (otherwise, this is identical to {{label}}). This is used as the module
+ name when using .modulemap files.
+
{{output}}
The relative path and name of the output(s) of the current build step.
If there is more than one output, this will expand to a list of all of
@@ -3468,6 +3729,13 @@
prefixed by "-I" (these work with Posix tools as well as Microsoft
ones).
+ {{module_deps}}
+ {{module_deps_no_self}}
+ Strings that correspond to the flags necessary to depend upon the Clang
+ modules referenced by the current target. The "_no_self" version doesn't
+ include the module for the current target, and can be used to compile
+ the pcm itself.
+
{{source}}
The relative path and name of the current input file.
Example: "../../base/my_file.cc"
@@ -3513,10 +3781,6 @@
Expands to the list of system libraries to link to. Each will be
prefixed by the "lib_switch".
- As a special case to support Mac, libraries with names ending in
- ".framework" will be added to the {{libs}} with "-framework" preceding
- it, and the lib prefix will be ignored.
-
Example: "-lfoo -lbar"
{{output_dir}}
@@ -3550,6 +3814,27 @@
Example: "libfoo.so libbar.so"
+ {{rlibs}}
+ Any Rust .rlibs which need to be linked into a final C++ target.
+ These should be treated as {{inputs}} except that sometimes
+ they might have different linker directives applied.
+
+ Example: "obj/foo/libfoo.rlib"
+
+ {{frameworks}}
+ Shared libraries packaged as framework bundle. This is principally
+ used on Apple's platforms (macOS and iOS). All name must be ending
+ with ".framework" suffix; the suffix will be stripped when expanding
+ {{frameworks}} and each item will be preceded by "-framework" or
+ "-weak_framework".
+
+ {{swiftmodules}}
+ Swift .swiftmodule files that needs to be embedded into the binary.
+ This is necessary to correctly link with object generated by the
+ Swift compiler (the .swiftmodule file cannot be embedded in object
+ files directly). Those will be prefixed with "swiftmodule_switch"
+ value.
+
The static library ("alink") tool allows {{arflags}} plus the common tool
substitutions.
@@ -3583,6 +3868,25 @@
assets catalog compiler. Usually based on the target_name of
the create_bundle target.
+ {{xcasset_compiler_flags}}
+ Expands to the list of flags specified in corresponding
+ create_bundle target.
+
+ The Swift tool has multiple input and outputs. It must have exactly one
+ output of .swiftmodule type, but can have one or more object file outputs,
+ in addition to other type of outputs. The following expansions are available:
+
+ {{module_name}}
+ Expands to the string representing the module name of target under
+ compilation (see "module_name" variable).
+
+ {{module_dirs}}
+ Expands to the list of -I<path> for the target Swift module search
+ path computed from target dependencies.
+
+ {{swiftflags}}
+ Expands to the list of strings representing Swift compiler flags.
+
Rust tools have the notion of a single input and a single output, along
with a set of compiler-specific flags. The following expansions are
available:
@@ -3599,13 +3903,6 @@
Expands to the list of --extern flags needed to include addition Rust
libraries in this target. Includes any specified renamed dependencies.
- {{rustc_output_extension}}
- Expands to the output extension for this target's crate type.
-
- {{rustc_output_prefix}}
- Expands to the prefix for shared and static libraries. This should
- generally be "lib". Empty for executable targets.
-
{{rustdeps}}
Expands to the list of -Ldependency=<path> strings needed to compile
this target.
@@ -3640,14 +3937,12 @@
command = "..."
outputs = [
"{{output_dir}}/{{target_output_name}}{{output_extension}}",
- "{{output_dir}}/{{target_output_name}}"
- "{{output_extension}}.TOC",
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}.TOC",
]
link_output =
"{{output_dir}}/{{target_output_name}}{{output_extension}}"
depend_output =
- "{{output_dir}}/{{target_output_name}}"
- "{{output_extension}}.TOC"
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}.TOC"
restat = true
}
```
@@ -3906,13 +4201,24 @@
A fully-qualified label representing the default toolchain, which may not
necessarily be the current one (see "current_toolchain").
```
+### <a name="var_gn_version"></a>**gn_version**: [number] The version of gn.
+
+```
+ Corresponds to the number printed by `gn --version`.
+```
+
+#### **Example**
+
+```
+ assert(gn_version >= 1700, "need GN version 1700 for the frobulate feature")
+```
### <a name="var_host_cpu"></a>**host_cpu**: The processor architecture that GN is running on.
```
This is value is exposed so that cross-compile toolchains can access the host
architecture when needed.
- The value should generally be considered read-only, but it can be overriden
+ The value should generally be considered read-only, but it can be overridden
in order to handle unusual cases where there might be multiple plausible
values for the host architecture (e.g., if you can do either 32-bit or 64-bit
builds). The value is not used internally by GN for any purpose.
@@ -4079,7 +4385,7 @@
```
action("myscript") {
# Pass the generated output dir to the script.
- args = [ "-o", rebase_path(target_gen_dir, root_build_dir) ]"
+ args = [ "-o", rebase_path(target_gen_dir, root_build_dir) ]
}
```
### <a name="var_target_name"></a>**target_name**: [string] The name of the current target.
@@ -4182,7 +4488,7 @@
```
action("myscript") {
# Pass the output dir to the script.
- args = [ "-o", rebase_path(target_out_dir, root_build_dir) ]"
+ args = [ "-o", rebase_path(target_out_dir, root_build_dir) ]
}
```
## <a name="target_variables"></a>Variables you set in targets
@@ -4248,7 +4554,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4354,7 +4660,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4389,7 +4695,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4434,6 +4740,14 @@
]
}
```
+### <a name="var_bridge_header"></a>**bridge_header**: [string] Path to C/Objective-C compatibility header.
+
+```
+ Valid for binary targets that contain Swift sources.
+
+ Path to an header that includes C/Objective-C functions and types that
+ needs to be made available to the Swift module.
+```
### <a name="var_bundle_contents_dir"></a>**bundle_contents_dir**: Expansion of {{bundle_contents_dir}} in
```
create_bundle.
@@ -4544,7 +4858,8 @@
versions of cflags* will be appended on the compiler command line after
"cflags".
- See also "asmflags" for flags for assembly-language files.
+ See also "asmflags" for flags for assembly-language files and "swiftflags"
+ for swift files.
```
#### **Ordering of flags and values**
@@ -4559,7 +4874,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4577,7 +4892,8 @@
versions of cflags* will be appended on the compiler command line after
"cflags".
- See also "asmflags" for flags for assembly-language files.
+ See also "asmflags" for flags for assembly-language files and "swiftflags"
+ for swift files.
```
#### **Ordering of flags and values**
@@ -4592,7 +4908,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4610,7 +4926,8 @@
versions of cflags* will be appended on the compiler command line after
"cflags".
- See also "asmflags" for flags for assembly-language files.
+ See also "asmflags" for flags for assembly-language files and "swiftflags"
+ for swift files.
```
#### **Ordering of flags and values**
@@ -4625,7 +4942,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4643,7 +4960,8 @@
versions of cflags* will be appended on the compiler command line after
"cflags".
- See also "asmflags" for flags for assembly-language files.
+ See also "asmflags" for flags for assembly-language files and "swiftflags"
+ for swift files.
```
#### **Ordering of flags and values**
@@ -4658,7 +4976,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4676,7 +4994,8 @@
versions of cflags* will be appended on the compiler command line after
"cflags".
- See also "asmflags" for flags for assembly-language files.
+ See also "asmflags" for flags for assembly-language files and "swiftflags"
+ for swift files.
```
#### **Ordering of flags and values**
@@ -4691,7 +5010,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4853,7 +5172,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -4921,7 +5240,7 @@
Options for this field are "cdylib", "staticlib", "proc-macro", and "dylib".
This field sets the `crate-type` attribute for the `rustc` tool on static
- libraries, as well as the appropiate output extension in the
+ libraries, as well as the appropriate output extension in the
`rust_output_extension` attribute. Since outputs must be explicit, the `lib`
crate type (where the Rust compiler produces what it thinks is the
appropriate library type) is not supported.
@@ -5018,7 +5337,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -5043,7 +5362,7 @@
The .d file should go in the target output directory. If you have more than
one source file that the script is being run over, you can use the output
file expansions described in "gn help action_foreach" to name the .d file
- according to the input."
+ according to the input.
The format is that of a Makefile and all paths must be relative to the root
build directory. Only one output may be listed and it must match the first
@@ -5101,15 +5420,93 @@
See also "public_deps".
```
-### <a name="var_edition"></a>**edition**: [string] The rustc edition to use in compiliation.
+### <a name="var_externs"></a>**externs**: [scope] Set of Rust crate-dependency pairs.
```
- Valid for `rust_library` targets and `executable`, `static_library`,
- `shared_library`, and `source_set` targets that contain Rust sources.
+ A list, each value being a scope indicating a pair of crate name and the path
+ to the Rust library.
+
+ These libraries will be passed as `--extern crate_name=path` to compiler
+ invocation containing the current target.
+```
+
+#### **Examples**
+
+```
+ executable("foo") {
+ sources = [ "main.rs" ]
+ externs = [{
+ crate_name = "bar",
+ path = "path/to/bar.rlib"
+ }]
+ }
+
+ This target would compile the `foo` crate with the following `extern` flag:
+ `--extern bar=path/to/bar.rlib`.
+```
+### <a name="var_framework_dirs"></a>**framework_dirs**: [directory list] Additional framework search directories.
+
+```
+ A list of source directories.
+
+ The directories in this list will be added to the framework search path for
+ the files in the affected target.
+```
+
+#### **Ordering of flags and values**
+
+```
+ 1. Those set on the current target (not in a config).
+ 2. Those set on the "configs" on the target in order that the
+ configs appear in the list.
+ 3. Those set on the "all_dependent_configs" on the target in order
+ that the configs appear in the list.
+ 4. Those set on the "public_configs" on the target in order that
+ those configs appear in the list.
+ 5. all_dependent_configs pulled from dependencies, in the order of
+ the "deps" list. This is done recursively. If a config appears
+ more than once, only the first occurrence will be used.
+ 6. public_configs pulled from dependencies, in the order of the
+ "deps" list. If a dependency is public, they will be applied
+ recursively.
+```
+
+#### **Example**
+
+```
+ framework_dirs = [ "src/include", "//third_party/foo" ]
+```
+### <a name="var_frameworks"></a>**frameworks**: [name list] Name of frameworks that must be linked.
+
+```
+ A list of framework names.
+
+ The frameworks named in that list will be linked with any dynamic link
+ type target.
+```
+
+#### **Ordering of flags and values**
- This indicates the compiler edition to use in compilition. Should be a value
- like "2015" or "2018", indiicating the appropriate value to pass to the
- `--edition=<>` flag in rustc.
+```
+ 1. Those set on the current target (not in a config).
+ 2. Those set on the "configs" on the target in order that the
+ configs appear in the list.
+ 3. Those set on the "all_dependent_configs" on the target in order
+ that the configs appear in the list.
+ 4. Those set on the "public_configs" on the target in order that
+ those configs appear in the list.
+ 5. all_dependent_configs pulled from dependencies, in the order of
+ the "deps" list. This is done recursively. If a config appears
+ more than once, only the first occurrence will be used.
+ 6. public_configs pulled from dependencies, in the order of the
+ "deps" list. If a dependency is public, they will be applied
+ recursively.
+```
+
+#### **Example**
+
+```
+ frameworks = [ "Foundation.framework", "Foo.framework" ]
```
### <a name="var_friend"></a>**friend**: Allow targets to include private headers.
@@ -5193,7 +5590,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -5301,7 +5698,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -5334,7 +5731,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -5383,12 +5780,6 @@
"lib_dirs" so the given library is found. Your BUILD.gn file should not
specify the switch (like "-l"): this will be encoded in the "lib_switch"
of the tool.
-
- Apple frameworks
- System libraries ending in ".framework" will be special-cased: the switch
- "-framework" will be prepended instead of the lib_switch, and the
- ".framework" suffix will be trimmed. This is to support the way Mac links
- framework dependencies.
```
#### **Ordering of flags and values**
@@ -5403,7 +5794,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -5430,7 +5821,7 @@
Generally, these keys will include three types of lists: lists of ordinary
strings, lists of filenames intended to be rebased according to their
particular source directory, and lists of target labels intended to be used
- as barriers to the walk. Verfication of these categories occurs at walk time,
+ as barriers to the walk. Verification of these categories occurs at walk time,
not creation time (since it is not clear until the walk which values are
intended for which purpose).
```
@@ -5447,7 +5838,14 @@
}
}
```
-### <a name="var_output_conversion"></a>**"output_conversion**: Data format for generated_file targets.
+### <a name="var_module_name"></a>**module_name**: [string] The name for the compiled module.
+
+```
+ Valid for binary targets that contain Swift sources.
+
+ If module_name is not set, then this rule will use the target name.
+```
+### <a name="var_output_conversion"></a>**output_conversion**: Data format for generated_file targets.
```
Controls how the "contents" of a generated_file target is formatted.
@@ -5773,7 +6171,7 @@
```
Public configs are applied to all targets that depend directly on this one.
- These dependant targets can further push this target's public configs
+ These dependent targets can further push this target's public configs
higher in the dependency tree by depending on it via public_deps (see "gn
help public_deps").
@@ -5842,7 +6240,7 @@
those configs appear in the list.
5. all_dependent_configs pulled from dependencies, in the order of
the "deps" list. This is done recursively. If a config appears
- more than once, only the first occurence will be used.
+ more than once, only the first occurrence will be used.
6. public_configs pulled from dependencies, in the order of the
"deps" list. If a dependency is public, they will be applied
recursively.
@@ -6001,6 +6399,32 @@
copy
The source are the source files to copy.
```
+### <a name="var_swiftflags"></a>**swiftflags**: Flags passed to the swift compiler.
+
+```
+ A list of strings.
+
+ "swiftflags" are passed to any invocation of a tool that takes an .swift
+ file as input.
+```
+
+#### **Ordering of flags and values**
+
+```
+ 1. Those set on the current target (not in a config).
+ 2. Those set on the "configs" on the target in order that the
+ configs appear in the list.
+ 3. Those set on the "all_dependent_configs" on the target in order
+ that the configs appear in the list.
+ 4. Those set on the "public_configs" on the target in order that
+ those configs appear in the list.
+ 5. all_dependent_configs pulled from dependencies, in the order of
+ the "deps" list. This is done recursively. If a config appears
+ more than once, only the first occurrence will be used.
+ 6. public_configs pulled from dependencies, in the order of the
+ "deps" list. If a dependency is public, they will be applied
+ recursively.
+```
### <a name="var_testonly"></a>**testonly**: Declares a target must only be used for testing.
```
@@ -6091,6 +6515,42 @@
See "gn help generated_file".
```
+### <a name="var_weak_frameworks"></a>**weak_frameworks**: [name list] Name of frameworks that must be weak linked.
+
+```
+ A list of framework names.
+
+ The frameworks named in that list will be weak linked with any dynamic link
+ type target. Weak linking instructs the dynamic loader to attempt to load
+ the framework, but if it is not able to do so, it leaves any imported symbols
+ unresolved. This is typically used when a framework is present in a new
+ version of an SDK but not on older versions of the OS that the software runs
+ on.
+```
+
+#### **Ordering of flags and values**
+
+```
+ 1. Those set on the current target (not in a config).
+ 2. Those set on the "configs" on the target in order that the
+ configs appear in the list.
+ 3. Those set on the "all_dependent_configs" on the target in order
+ that the configs appear in the list.
+ 4. Those set on the "public_configs" on the target in order that
+ those configs appear in the list.
+ 5. all_dependent_configs pulled from dependencies, in the order of
+ the "deps" list. This is done recursively. If a config appears
+ more than once, only the first occurrence will be used.
+ 6. public_configs pulled from dependencies, in the order of the
+ "deps" list. If a dependency is public, they will be applied
+ recursively.
+```
+
+#### **Example**
+
+```
+ weak_frameworks = [ "OnlyOnNewerOSes.framework" ]
+```
### <a name="var_write_runtime_deps"></a>**write_runtime_deps**: Writes the target's runtime_deps to the given path.
```
@@ -6111,6 +6571,15 @@
same as requesting the runtime deps be written on the command line (see "gn
help --runtime-deps-list-file").
```
+### <a name="var_xcasset_compiler_flags"></a>**xcasset_compiler_flags**: Flags passed to xcassets compiler.
+
+```
+ A list of strings.
+
+ Valid for create_bundle target. Those flags are directly passed to
+ xcassets compiler, corresponding to {{xcasset_compiler_flags}} substitution
+ in compile_xcassets tool.
+```
### <a name="var_xcode_extra_attributes"></a>**xcode_extra_attributes**: [scope] Extra attributes for Xcode projects.
```
@@ -6234,13 +6703,33 @@
check_targets [optional]
A list of labels and label patterns that should be checked when running
- "gn check" or "gn gen --check". If unspecified, all targets will be
- checked. If it is the empty list, no targets will be checked. To
- bypass this list, request an explicit check of targets, like "//*".
+ "gn check" or "gn gen --check". If neither check_targets or
+ no_check_targets (see below) is specified, all targets will be checked.
+ It is an error to specify both check_targets and no_check_targets. If it
+ is the empty list, no targets will be checked. To bypass this list,
+ request an explicit check of targets, like "//*".
+
+ The format of this list is identical to that of "visibility" so see "gn
+ help visibility" for examples.
+
+ no_check_targets [optional]
+ A list of labels and label patterns that should *not* be checked when
+ running "gn check" or "gn gen --check". All other targets will be checked.
+ If neither check_targets (see above) or no_check_targets is specified, all
+ targets will be checked. It is an error to specify both check_targets and
+ no_check_targets.
The format of this list is identical to that of "visibility" so see "gn
help visibility" for examples.
+ check_system_includes [optional]
+ Boolean to control whether system style includes are checked by default
+ when running "gn check" or "gn gen --check". System style includes are
+ includes that use angle brackets <> instead of double quotes "". If this
+ setting is omitted or set to false, these includes will be ignored by
+ default. They can be checked explicitly by running
+ "gn check --check-system" or "gn gen --check=system"
+
exec_script_whitelist [optional]
A list of .gn/.gni files (not labels) that have permission to call the
exec_script function. If this list is defined, calls to exec_script will
@@ -6263,12 +6752,19 @@
root [optional]
Label of the root build target. The GN build will start by loading the
build file containing this target name. This defaults to "//:" which will
- cause the file //BUILD.gn to be loaded.
+ cause the file //BUILD.gn to be loaded. Note that build_file_extension
+ applies to the default case as well.
+
+ The command-line switch --root-target will override this value (see "gn
+ help --root-target").
script_executable [optional]
- Path to specific Python executable or potentially a different language
- interpreter that is used to execute scripts in action targets and
- exec_script calls.
+ Path to specific Python executable or other interpreter to use in
+ action targets and exec_script calls. By default GN searches the
+ PATH for Python to execute these scripts.
+
+ If set to the empty string, the path specified in action targets
+ and exec_script calls will be executed directly.
secondary_source [optional]
Label of an alternate directory tree to find input files. When searching
@@ -6285,11 +6781,23 @@
default_args [optional]
Scope containing the default overrides for declared arguments. These
overrides take precedence over the default values specified in the
- declare_args() block, but can be overriden using --args or the
+ declare_args() block, but can be overridden using --args or the
args.gn file.
This is intended to be used when subprojects declare arguments with
default values that need to be changed for whatever reason.
+
+ build_file_extension [optional]
+ If set to a non-empty string, this is added to the name of all build files
+ to load.
+ GN will look for build files named "BUILD.$build_file_extension.gn".
+ This is intended to be used during migrations or other situations where
+ there are two independent GN builds in the same directories.
+
+ ninja_required_version [optional]
+ When set specifies the minimum required version of Ninja. The default
+ required version is 1.7.2. Specifying a higher version might enable the
+ use of some of newer features that can make the build more efficient.
```
#### **Example .gn file contents**
@@ -6337,6 +6845,9 @@
file to disk.
6. When all targets are resolved, write out the root build.ninja file.
+
+ Note that the BUILD.gn file name may be modulated by .gn arguments such as
+ build_file_extension.
```
#### **Executing target definitions and templates**
@@ -6453,12 +6964,10 @@
string = `"` { char | escape | expansion } `"` .
escape = `\` ( "$" | `"` | char ) .
- BracketExpansion = "{" ( identifier | ArrayAccess | ScopeAccess "
- ") "}" .
+ BracketExpansion = "{" ( identifier | ArrayAccess | ScopeAccess ) "}" .
Hex = "0x" [0-9A-Fa-f][0-9A-Fa-f]
expansion = "$" ( identifier | BracketExpansion | Hex ) .
- char = /* any character except "$", `"`, or newline "
- "*/ .
+ char = /* any character except "$", `"`, or newline */ .
After a backslash, certain sequences represent special characters:
@@ -6579,10 +7088,6 @@
mylist = []
mylist = otherlist
-
- When assigning to a list named 'sources' using '=' or '+=', list items may be
- automatically filtered out. See "gn help set_sources_assignment_filter" for
- more.
```
#### **Scopes**
@@ -6727,6 +7232,40 @@
Note that "trim value" is useless because the value parser skips
whitespace anyway.
```
+### <a name="file_pattern"></a>**File patterns**
+
+```
+ File patterns are VERY limited regular expressions. They must match the
+ entire input string to be counted as a match. In regular expression parlance,
+ there is an implicit "^...$" surrounding your input. If you want to match a
+ substring, you need to use wildcards at the beginning and end.
+
+ There are only two special tokens understood by the pattern matcher.
+ Everything else is a literal.
+
+ - "*" Matches zero or more of any character. It does not depend on the
+ preceding character (in regular expression parlance it is equivalent to
+ ".*").
+
+ - "\b" Matches a path boundary. This will match the beginning or end of a
+ string, or a slash.
+```
+
+#### **Pattern examples**
+
+```
+ "*asdf*"
+ Matches a string containing "asdf" anywhere.
+
+ "asdf"
+ Matches only the exact string "asdf".
+
+ "*.cc"
+ Matches strings ending in the literal ".cc".
+
+ "\bwin/*"
+ Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+```
### <a name="label_pattern"></a>**Label patterns**
```
@@ -6828,7 +7367,7 @@
```
Metadata is information attached to targets throughout the dependency tree. GN
allows for the collection of this data into files written during the generation
- step, enabing users to expose and aggregate this data based on the dependency
+ step, enabling users to expose and aggregate this data based on the dependency
tree.
```
@@ -6837,17 +7376,17 @@
```
Similar to the write_file() function, the generated_file target type
creates a file in the specified location with the specified content. The
- primary difference between the function and the target type is that the
+ primary difference between write_file() and this target type is that the
write_file function does the file write at parse time, while the
generated_file target type writes at target resolution time. See
"gn help generated_file" for more detail.
- When written at target resolution time, the generated_file enables GN to
+ When written at target resolution time, generated_file enables GN to
collect and write aggregated metadata from dependents.
- A generated_file target can declare either 'contents' (to write statically
- known contents to a file) or 'data_keys'(to aggregate metadata and write the
- result to a file). It can also specify 'walk_keys' (to restrict the metadata
+ A generated_file target can declare either 'contents' to write statically
+ known contents to a file or 'data_keys' to aggregate metadata and write the
+ result to a file. It can also specify 'walk_keys' (to restrict the metadata
collection), 'output_conversion', and 'rebase'.
```
@@ -6855,22 +7394,23 @@
```
Targets can declare a 'metadata' variable containing a scope, and this
- metadata is collected and written to file by generated_file aggregation
- targets. The 'metadata' scope must contain only list values, since the
- aggregation step collects a list of these values.
+ metadata may be collected and written out to a file specified by
+ generated_file aggregation targets. The 'metadata' scope must contain
+ only list values since the aggregation step collects a list of these values.
During the target resolution, generated_file targets will walk their
dependencies recursively, collecting metadata based on the specified
'data_keys'. 'data_keys' is specified as a list of strings, used by the walk
to identify which variables in dependencies' 'metadata' scopes to collect.
- The walk begins with the listed dependencies of the 'generated_file' target,
- for each checking the "metadata" scope for any of the "data_keys". If
- present, the data in those variables is appended to the aggregate list. Note
- that this means that if more than one walk key is specified, the data in all
- of them will be aggregated into one list. From there, the walk will then
- recurse into the dependencies of each target it encounters, collecting the
- specified metadata for each.
+ The walk begins with the listed dependencies of the 'generated_file' target.
+ The 'metadata' scope for each dependency is inspected for matching elements
+ of the 'generated_file' target's 'data_keys' list. If a match is found, the
+ data from the dependent's matching key list is appended to the aggregate walk
+ list. Note that this means that if more than one walk key is specified, the
+ data in all of them will be aggregated into one list. From there, the walk
+ will then recurse into the dependencies of each target it encounters,
+ collecting the specified metadata for each.
For example:
@@ -6903,16 +7443,16 @@
bar.cpp
baz.cpp
- The dependency walk can be limited by using the "walk_keys". This is a list of
+ The dependency walk can be limited by using the 'walk_keys'. This is a list of
labels that should be included in the walk. All labels specified here should
also be in one of the deps lists. These keys act as barriers, where the walk
- will only recurse into targets listed here. An empty list in all specified
+ will only recurse into the targets listed. An empty list in all specified
barriers will end that portion of the walk.
group("a") {
metadata = {
my_files = [ "foo.cpp" ]
- my_files_barrier [ ":b" ]
+ my_files_barrier = [ ":b" ]
}
deps = [ ":b", ":c" ]
@@ -6932,7 +7472,8 @@
generated_file("metadata") {
outputs = [ "$root_build_dir/my_files.json" ]
- data_keys = [ "my_files", "my_extra_files" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_files_barrier" ]
deps = [ ":a" ]
}
@@ -7254,9 +7795,11 @@
* --dotfile: Override the name of the ".gn" file.
* --fail-on-unused-args: Treat unused build args as fatal errors.
* --markdown: Write help output in the Markdown format.
+ * --ninja-executable: Set the Ninja executable.
* --nocolor: Force non-colored output.
* -q: Quiet mode. Don't print output on success.
* --root: Explicitly specify source root.
+ * --root-target: Override the root target.
* --runtime-deps-list-file: Save runtime dependencies for targets in file.
* --script-executable: Set the executable used to execute scripts.
* --threads: Specify number of worker threads.
diff --git a/gn/examples/ios/.gn b/gn/examples/ios/.gn
new file mode 100644
index 00000000000..e5b6d4a3db7
--- /dev/null
+++ b/gn/examples/ios/.gn
@@ -0,0 +1,2 @@
+# The location of the build configuration file.
+buildconfig = "//build/BUILDCONFIG.gn"
diff --git a/gn/examples/ios/BUILD.gn b/gn/examples/ios/BUILD.gn
new file mode 100644
index 00000000000..bd71b4fb380
--- /dev/null
+++ b/gn/examples/ios/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("all") {
+ deps = [
+ "//app:hello",
+ "//host:username",
+ ]
+}
diff --git a/gn/examples/ios/app/AppDelegate.h b/gn/examples/ios/app/AppDelegate.h
new file mode 100644
index 00000000000..54d31eb38d6
--- /dev/null
+++ b/gn/examples/ios/app/AppDelegate.h
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@end
diff --git a/gn/examples/ios/app/AppDelegate.m b/gn/examples/ios/app/AppDelegate.m
new file mode 100644
index 00000000000..b6f94fb0d14
--- /dev/null
+++ b/gn/examples/ios/app/AppDelegate.m
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium 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 "app/AppDelegate.h"
+
+#import "app/Foo.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication*)application
+ didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+ NSLog(@"%@", [[[FooWrapper alloc] init] helloWithName:@"World"]);
+ return YES;
+}
+
+#pragma mark - UISceneSession lifecycle
+
+- (UISceneConfiguration*)application:(UIApplication*)application
+ configurationForConnectingSceneSession:
+ (UISceneSession*)connectingSceneSession
+ options:(UISceneConnectionOptions*)options {
+ return
+ [[UISceneConfiguration alloc] initWithName:@"Default Configuration"
+ sessionRole:connectingSceneSession.role];
+}
+
+- (void)application:(UIApplication*)application
+ didDiscardSceneSessions:(NSSet<UISceneSession*>*)sceneSessions {
+}
+
+@end
diff --git a/gn/examples/ios/app/BUILD.gn b/gn/examples/ios/app/BUILD.gn
new file mode 100644
index 00000000000..fdb3d0348f1
--- /dev/null
+++ b/gn/examples/ios/app/BUILD.gn
@@ -0,0 +1,67 @@
+# Copyright 2019 The Chromium 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/ios/templates/ios_app_bundle.gni")
+import("//build/config/ios/templates/storyboards.gni")
+
+ios_app_bundle("hello") {
+ output_name = "Hello"
+
+ info_plist = "resources/Info.plist"
+
+ sources = [
+ "AppDelegate.h",
+ "AppDelegate.m",
+ "SceneDelegate.h",
+ "SceneDelegate.m",
+ "ViewController.h",
+ "ViewController.m",
+ "main.m",
+ ]
+
+ frameworks = [
+ "CoreGraphics.framework",
+ "Foundation.framework",
+ "UIKit.framework",
+ ]
+
+ deps = [
+ ":foo",
+ ":storyboards",
+ "//shared:hello_framework",
+ "//shared:hello_framework+bundle",
+ ]
+}
+
+storyboards("storyboards") {
+ sources = [
+ "resources/LaunchScreen.storyboard",
+ "resources/Main.storyboard",
+ ]
+}
+
+source_set("baz") {
+ module_name = "Baz"
+ sources = [ "Baz.swift" ]
+}
+
+source_set("bar") {
+ module_name = "Bar"
+ sources = [ "Bar.swift" ]
+ deps = [ ":baz" ]
+}
+
+group("bar_indirect") {
+ public_deps = [ ":bar" ]
+}
+
+source_set("foo") {
+ module_name = "Foo"
+ bridge_header = "Foo-Bridging-Header.h"
+ sources = [
+ "Foo.swift",
+ "FooWrapper.swift",
+ ]
+ deps = [ ":bar_indirect" ]
+}
diff --git a/gn/examples/ios/app/Bar.swift b/gn/examples/ios/app/Bar.swift
new file mode 100644
index 00000000000..9e35c9d9747
--- /dev/null
+++ b/gn/examples/ios/app/Bar.swift
@@ -0,0 +1,8 @@
+
+import Baz;
+
+public class Greeter {
+ public static func greet(greeting: String, name: String, from: String) -> String {
+ return greeting + ", " + name + " (from " + from + ")";
+ }
+}
diff --git a/gn/examples/ios/app/Baz.swift b/gn/examples/ios/app/Baz.swift
new file mode 100644
index 00000000000..f38957862c4
--- /dev/null
+++ b/gn/examples/ios/app/Baz.swift
@@ -0,0 +1,2 @@
+
+class Baz {}
diff --git a/gn/examples/ios/app/Foo-Bridging-Header.h b/gn/examples/ios/app/Foo-Bridging-Header.h
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gn/examples/ios/app/Foo-Bridging-Header.h
diff --git a/gn/examples/ios/app/Foo.swift b/gn/examples/ios/app/Foo.swift
new file mode 100644
index 00000000000..5e9faa15eda
--- /dev/null
+++ b/gn/examples/ios/app/Foo.swift
@@ -0,0 +1,12 @@
+
+import Bar
+
+class Foo {
+ var name: String;
+ public init(name: String) {
+ self.name = name;
+ }
+ public func hello(name: String) -> String {
+ return Greeter.greet(greeting: "Hello", name: name, from: self.name);
+ }
+}
diff --git a/gn/examples/ios/app/FooWrapper.swift b/gn/examples/ios/app/FooWrapper.swift
new file mode 100644
index 00000000000..a82c8a8b511
--- /dev/null
+++ b/gn/examples/ios/app/FooWrapper.swift
@@ -0,0 +1,10 @@
+
+import Foundation;
+
+@objc
+public class FooWrapper : NSObject {
+ @objc
+ public func hello(name: String) -> String {
+ return Foo(name: "Foo").hello(name: name);
+ }
+}
diff --git a/gn/examples/ios/app/SceneDelegate.h b/gn/examples/ios/app/SceneDelegate.h
new file mode 100644
index 00000000000..ef1888fbc60
--- /dev/null
+++ b/gn/examples/ios/app/SceneDelegate.h
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
+
+@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
+
+@property(strong, nonatomic) UIWindow* window;
+
+@end
diff --git a/gn/examples/ios/app/SceneDelegate.m b/gn/examples/ios/app/SceneDelegate.m
new file mode 100644
index 00000000000..5e20b9e0b6e
--- /dev/null
+++ b/gn/examples/ios/app/SceneDelegate.m
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium 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 "app/SceneDelegate.h"
+
+@implementation SceneDelegate
+
+- (void)scene:(UIScene*)scene
+ willConnectToSession:(UISceneSession*)session
+ options:(UISceneConnectionOptions*)connectionOptions {
+}
+
+- (void)sceneDidDisconnect:(UIScene*)scene {
+}
+
+- (void)sceneDidBecomeActive:(UIScene*)scene {
+}
+
+- (void)sceneWillResignActive:(UIScene*)scene {
+}
+
+- (void)sceneWillEnterForeground:(UIScene*)scene {
+}
+
+- (void)sceneDidEnterBackground:(UIScene*)scene {
+}
+
+@end
diff --git a/gn/examples/ios/app/ViewController.h b/gn/examples/ios/app/ViewController.h
new file mode 100644
index 00000000000..4076e406e2f
--- /dev/null
+++ b/gn/examples/ios/app/ViewController.h
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+
+@end
diff --git a/gn/examples/ios/app/ViewController.m b/gn/examples/ios/app/ViewController.m
new file mode 100644
index 00000000000..e0a80cb728e
--- /dev/null
+++ b/gn/examples/ios/app/ViewController.m
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium 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 <HelloShared/HelloShared.h>
+
+#import "app/ViewController.h"
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ UILabel* label = [self labelWithText:[Greetings greet]];
+ [self addCenteredView:label toParentView:self.view];
+}
+
+- (UILabel*)labelWithText:(NSString*)text {
+ UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
+ label.text = text;
+ [label sizeToFit];
+ return label;
+}
+
+- (void)addCenteredView:(UIView*)view toParentView:(UIView*)parentView {
+ view.center = [parentView convertPoint:parentView.center
+ fromView:parentView.superview];
+ [parentView addSubview:view];
+}
+
+@end
diff --git a/gn/examples/ios/app/main.m b/gn/examples/ios/app/main.m
new file mode 100644
index 00000000000..b01cb7af899
--- /dev/null
+++ b/gn/examples/ios/app/main.m
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
+#import "app/AppDelegate.h"
+
+int main(int argc, char** argv) {
+ NSString* appDelegateClassName;
+ @autoreleasepool {
+ appDelegateClassName = NSStringFromClass([AppDelegate class]);
+ }
+
+ return UIApplicationMain(argc, argv, nil, appDelegateClassName);
+}
diff --git a/gn/examples/ios/app/resources/Info.plist b/gn/examples/ios/app/resources/Info.plist
new file mode 100644
index 00000000000..7b6037c25e5
--- /dev/null
+++ b/gn/examples/ios/app/resources/Info.plist
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIApplicationSceneManifest</key>
+ <dict>
+ <key>UIApplicationSupportsMultipleScenes</key>
+ <false/>
+ <key>UISceneConfigurations</key>
+ <dict>
+ <key>UIWindowSceneSessionRoleApplication</key>
+ <array>
+ <dict>
+ <key>UISceneConfigurationName</key>
+ <string>Default Configuration</string>
+ <key>UISceneDelegateClassName</key>
+ <string>SceneDelegate</string>
+ <key>UISceneStoryboardFile</key>
+ <string>Main</string>
+ </dict>
+ </array>
+ </dict>
+ </dict>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/gn/examples/ios/app/resources/LaunchScreen.storyboard b/gn/examples/ios/app/resources/LaunchScreen.storyboard
new file mode 100644
index 00000000000..865e9329f37
--- /dev/null
+++ b/gn/examples/ios/app/resources/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/gn/examples/ios/app/resources/Main.storyboard b/gn/examples/ios/app/resources/Main.storyboard
new file mode 100644
index 00000000000..808a21ce779
--- /dev/null
+++ b/gn/examples/ios/app/resources/Main.storyboard
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/gn/examples/ios/build/BUILD.gn b/gn/examples/ios/build/BUILD.gn
new file mode 100644
index 00000000000..2831117cea2
--- /dev/null
+++ b/gn/examples/ios/build/BUILD.gn
@@ -0,0 +1,88 @@
+# Copyright 2019 The Chromium 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/ios/deployment_target.gni")
+
+config("compiler") {
+ configs = [
+ ":include_dirs",
+ ":cpp_standard",
+ ":objc_use_arc",
+ ":objc_abi_version",
+ ]
+ cflags = [ "-g" ]
+ swiftflags = [ "-g" ]
+}
+
+config("shared_binary") {
+ if (current_os == "ios" || current_os == "mac") {
+ configs = [
+ ":rpath_config",
+ ":swift_libdir",
+ ]
+ }
+}
+
+config("objc_abi_version") {
+ cflags_objc = [ "-fobjc-abi-version=2" ]
+ cflags_objcc = cflags_objc
+ ldflags = [
+ "-Xlinker",
+ "-objc_abi_version",
+ "-Xlinker",
+ "2",
+ ]
+}
+
+config("include_dirs") {
+ include_dirs = [
+ "//",
+ root_gen_dir,
+ ]
+}
+
+config("objc_use_arc") {
+ cflags_objc = [
+ "-fobjc-arc",
+ "-fobjc-weak",
+ ]
+ cflags_objcc = cflags_objc
+}
+
+config("cpp_standard") {
+ cflags_c = [ "--std=c11" ]
+ cflags_cc = [
+ "--std=c++17",
+ "--stdlib=libc++",
+ ]
+ ldflags = [ "--stdlib=libc++" ]
+}
+
+if (current_os == "ios" || current_os == "mac") {
+ config("rpath_config") {
+ ldflags = [
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@executable_path/Frameworks",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@loader_path/Frameworks",
+ ]
+ }
+
+ _sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
+ [
+ "--target-cpu",
+ current_cpu,
+ "--deployment-target",
+ ios_deployment_target,
+ ],
+ "json")
+
+ config("swift_libdir") {
+ lib_dirs = [ "${_sdk_info.sdk_path}/usr/lib/swift" ]
+ }
+}
diff --git a/gn/examples/ios/build/BUILDCONFIG.gn b/gn/examples/ios/build/BUILDCONFIG.gn
new file mode 100644
index 00000000000..53ee3d9ab69
--- /dev/null
+++ b/gn/examples/ios/build/BUILDCONFIG.gn
@@ -0,0 +1,43 @@
+# Copyright 2019 The Chromium 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 (target_os == "") {
+ target_os = "ios"
+}
+if (target_cpu == "") {
+ target_cpu = host_cpu
+}
+if (current_cpu == "") {
+ current_cpu = target_cpu
+}
+if (current_os == "") {
+ current_os = target_os
+}
+
+# All binary targets will get this list of configs by default.
+_shared_binary_target_configs = [ "//build:compiler" ]
+
+# Apply that default list to the binary target types.
+set_defaults("executable") {
+ configs = _shared_binary_target_configs
+ configs += [ "//build:shared_binary" ]
+}
+set_defaults("static_library") {
+ configs = _shared_binary_target_configs
+}
+set_defaults("shared_library") {
+ configs = _shared_binary_target_configs
+ configs += [ "//build:shared_binary" ]
+}
+set_defaults("source_set") {
+ configs = _shared_binary_target_configs
+}
+
+set_default_toolchain("//build/toolchain/$target_os:clang_$target_cpu")
+
+if (target_os == "ios") {
+ host_toolchain = "//build/toolchain/$host_os:clang_$host_cpu"
+} else {
+ host_toolchain = default_toolchain
+}
diff --git a/gn/examples/ios/build/config/ios/BUILD.gn b/gn/examples/ios/build/config/ios/BUILD.gn
new file mode 100644
index 00000000000..47902c9770f
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2019 The Chromium 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/ios/sdk_info.gni")
+import("//build/config/ios/templates/merge_plist.gni")
+
+merge_plist("compiler_plist") {
+ substitutions = {
+ COMPILER_NAME = sdk_info.compiler
+ MACOS_BUILD = sdk_info.macos_build
+ PLATFORM_BUILD = sdk_info.sdk_build
+ PLATFORM_DISPLAY_NAME = sdk_info.platform_name
+ PLATFORM_NAME = sdk_info.platform
+ PLATFORM_VERSION = sdk_info.sdk_version
+ SDK_BUILD = sdk_info.sdk_build
+ SDK_NAME = sdk_info.sdk
+ XCODE_BUILD = sdk_info.xcode_build
+ XCODE_VERSION = sdk_info.xcode_version
+ }
+
+ output = "$target_out_dir/compiler_plist/Info.plist"
+ plists = [ "//build/config/ios/resources/compiler-Info.plist" ]
+}
diff --git a/gn/examples/ios/build/config/ios/bundle_identifier_prefix.gni b/gn/examples/ios/build/config/ios/bundle_identifier_prefix.gni
new file mode 100644
index 00000000000..97c47cc72fa
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/bundle_identifier_prefix.gni
@@ -0,0 +1,8 @@
+# Copyright 2019 The Chromium 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() {
+ # Default bundle identifier prefix.
+ default_bundle_identifier_prefix = "com.google"
+}
diff --git a/gn/examples/ios/build/config/ios/deployment_target.gni b/gn/examples/ios/build/config/ios/deployment_target.gni
new file mode 100644
index 00000000000..718048835aa
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/deployment_target.gni
@@ -0,0 +1,9 @@
+# Copyright 2019 The Chromium 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() {
+ # Maximum deployment target. Automatically detected by sdk_info.py but
+ # needs to be specified to a version < 11.0 if targetting 32-bit archs.
+ ios_deployment_target = "13.0"
+}
diff --git a/gn/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist b/gn/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist
new file mode 100644
index 00000000000..0a4badfb3c8
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/resources/Entitlements-Simulated.plist
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>application-identifier</key>
+ <string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
+ <key>keychain-access-groups</key>
+ <array>
+ <string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
+ </array>
+</dict>
+</plist>
diff --git a/gn/examples/ios/build/config/ios/resources/Info.plist b/gn/examples/ios/build/config/ios/resources/Info.plist
new file mode 100644
index 00000000000..9bcb244429e
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/resources/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
+</dict>
+</plist>
diff --git a/gn/examples/ios/build/config/ios/resources/compiler-Info.plist b/gn/examples/ios/build/config/ios/resources/compiler-Info.plist
new file mode 100644
index 00000000000..0a025174f6e
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/resources/compiler-Info.plist
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>DTSDKName</key>
+ <string>$(SDK_NAME)</string>
+ <key>DTXcode</key>
+ <string>$(XCODE_VERSION)</string>
+ <key>DTSDKBuild</key>
+ <string>$(SDK_BUILD)</string>
+ <key>BuildMachineOSBuild</key>
+ <string>$(MACOS_BUILD)</string>
+ <key>DTPlatformName</key>
+ <string>$(PLATFORM_NAME)</string>
+ <key>DTCompiler</key>
+ <string>$(COMPILER_NAME)</string>
+ <key>DTPlatformVersion</key>
+ <string>$(PLATFORM_VERSION)</string>
+ <key>DTXcodeBuild</key>
+ <string>$(XCODE_BUILD)</string>
+ <key>DTPlatformBuild</key>
+ <string>$(PLATFORM_BUILD)</string>
+ <key>CFBundleSupportedPlatforms</key>
+ <array>
+ <string>$(PLATFORM_DISPLAY_NAME)</string>
+ </array>
+ <key>UIDeviceFamily</key>
+ <array>
+ <string>1</string>
+ <string>2</string>
+ </array>
+</dict>
+</plist>
diff --git a/gn/examples/ios/build/config/ios/scripts/compile_storyboard.py b/gn/examples/ios/build/config/ios/scripts/compile_storyboard.py
new file mode 100644
index 00000000000..2fa1d20edb0
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/scripts/compile_storyboard.py
@@ -0,0 +1,53 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Compiles a .storyboard file.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+def CompileStoryboard(storyboard, out, ios_deployment_target):
+ """Compiles |storyboard| storyboard to |out| for |ios_deployment_target|."""
+ subprocess.check_call([
+ 'ibtool', '--target-device', 'iphone', '--target-device', 'ipad',
+ '--auto-activate-custom-fonts', '--minimum-deployment-target',
+ ios_deployment_target, '--compilation-directory', out,
+ storyboard,
+ ])
+
+
+def ParseArgs(argv):
+ """Parses command line arguments."""
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument(
+ 'input',
+ help='path to the .storyboard file to compile')
+ parser.add_argument(
+ '-o', '--output', required=True,
+ help='path to the result')
+ parser.add_argument(
+ '-t', '--minimum-deployment-target', required=True,
+ help='iOS deployment target')
+
+ return parser.parse_args(argv)
+
+
+def main(argv):
+ args = ParseArgs(argv)
+
+ CompileStoryboard(
+ os.path.abspath(args.input),
+ os.path.dirname(os.path.abspath(args.output)),
+ args.minimum_deployment_target)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py b/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py
new file mode 100644
index 00000000000..cf38b10d198
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py
@@ -0,0 +1,119 @@
+# Copyright 2019 The Chromium 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 the team identifier to use for code signing bundle given its
+bundle identifier.
+"""
+
+import argparse
+import fnmatch
+import glob
+import json
+import os
+import plistlib
+import subprocess
+import sys
+
+
+class ProvisioningProfile(object):
+
+ def __init__(self, mobileprovision_path):
+ self._path = mobileprovision_path
+ self._data = plistlib.readPlistFromString(
+ subprocess.check_output(
+ ['security', 'cms', '-D', '-i', mobileprovision_path]))
+
+ @property
+ def application_identifier_pattern(self):
+ return self._data.get('Entitlements', {}).get('application-identifier', '')
+
+ @property
+ def app_identifier_prefix(self):
+ return self._data.get('ApplicationIdentifierPrefix', [''])[0]
+
+ def ValidToSignBundle(self, bundle_identifier):
+ """Returns whether the provisioning profile can sign |bundle_identifier|."""
+ return fnmatch.fnmatch(
+ self.app_identifier_prefix + '.' + bundle_identifier,
+ self.application_identifier_pattern)
+
+
+def GetProvisioningProfilesDir():
+ """Returns the location of the locally installed provisioning profiles."""
+ return os.path.join(
+ os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
+
+
+def ListProvisioningProfiles():
+ """Returns a list of all installed provisioning profiles."""
+ return glob.glob(
+ os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision'))
+
+
+def LoadProvisioningProfile(mobileprovision_path):
+ """Loads the Apple Property List embedded in |mobileprovision_path|."""
+ return ProvisioningProfile(mobileprovision_path)
+
+
+def ListValidProvisioningProfiles(bundle_identifier):
+ """Returns a list of provisioning profile valid for |bundle_identifier|."""
+ result = []
+ for mobileprovision_path in ListProvisioningProfiles():
+ mobileprovision = LoadProvisioningProfile(mobileprovision_path)
+ if mobileprovision.ValidToSignBundle(bundle_identifier):
+ result.append(mobileprovision)
+ return result
+
+
+def FindProvisioningProfile(bundle_identifier):
+ """Returns the path to the provisioning profile for |bundle_identifier|."""
+ return max(
+ ListValidProvisioningProfiles(bundle_identifier),
+ key=lambda p: len(p.application_identifier_pattern))
+
+
+def GenerateSubsitutions(bundle_identifier, mobileprovision):
+ if mobileprovision:
+ app_identifier_prefix = mobileprovision.app_identifier_prefix + '.'
+ else:
+ app_identifier_prefix = '*.'
+
+ return {
+ 'CFBundleIdentifier': bundle_identifier,
+ 'AppIdentifierPrefix': app_identifier_prefix
+ }
+
+
+def ParseArgs(argv):
+ """Parses command line arguments."""
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument(
+ '-b', '--bundle-identifier', required=True,
+ help='bundle identifier for the application')
+ parser.add_argument(
+ '-o', '--output', default='-',
+ help='path to the result; - means stdout')
+
+ return parser.parse_args(argv)
+
+
+def main(argv):
+ args = ParseArgs(argv)
+
+ mobileprovision = FindProvisioningProfile(args.bundle_identifier)
+ substitutions = GenerateSubsitutions(args.bundle_identifier, mobileprovision)
+
+ if args.output == '-':
+ sys.stdout.write(json.dumps(substitutions))
+ else:
+ with open(args.output, 'w') as output:
+ output.write(json.dumps(substitutions))
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/examples/ios/build/config/ios/scripts/generate_umbrella_header.py b/gn/examples/ios/build/config/ios/scripts/generate_umbrella_header.py
new file mode 100644
index 00000000000..11373760ab9
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/scripts/generate_umbrella_header.py
@@ -0,0 +1,54 @@
+# Copyright 2019 The Chromium 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 an umbrella header file that #import all public header of a
+binary framework.
+"""
+
+import argparse
+import os
+import sys
+
+
+def GenerateImport(header):
+ """Returns a string for importing |header|."""
+ return '#import "%s"\n' % os.path.basename(header)
+
+
+def GenerateUmbrellaHeader(headers):
+ """Returns a string with the content of the umbrella header."""
+ return ''.join([ GenerateImport(header) for header in headers ])
+
+
+def ParseArgs(argv):
+ """Parses command line arguments."""
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument(
+ 'headers', nargs='+',
+ help='path to the public heeaders')
+ parser.add_argument(
+ '-o', '--output', default='-',
+ help='path of the output file to create; - means stdout')
+
+ return parser.parse_args(argv)
+
+
+def main(argv):
+ args = ParseArgs(argv)
+
+ content = GenerateUmbrellaHeader(args.headers)
+
+ if args.output == '-':
+ sys.stdout.write(content)
+ else:
+ with open(args.output, 'w') as output:
+ output.write(content)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/examples/ios/build/config/ios/scripts/merge_plist.py b/gn/examples/ios/build/config/ios/scripts/merge_plist.py
new file mode 100644
index 00000000000..452cf534f9f
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/scripts/merge_plist.py
@@ -0,0 +1,134 @@
+# Copyright 2019 The Chromium 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 multiple Apple Property List files (.plist) and perform variables
+substitutions $(VARIABLE) in the Property List string values.
+"""
+
+import argparse
+import json
+import re
+import subprocess
+import sys
+
+
+# Pattern representing a variable to substitue in a string value.
+VARIABLE_PATTERN = re.compile(r'\$\(([^)]*)\)')
+
+
+def LoadPlist(plist_path):
+ """Loads Apple Property List file at |plist_path|."""
+ return json.loads(
+ subprocess.check_output(
+ ['plutil', '-convert', 'json', '-o', '-', plist_path]))
+
+
+def SavePlist(plist_path, content, format):
+ """Saves |content| as Apple Property List in |format| at |plist_path|."""
+ proc = subprocess.Popen(
+ ['plutil', '-convert', format, '-o', plist_path, '-'],
+ stdin=subprocess.PIPE)
+ output, _ = proc.communicate(json.dumps(content))
+ if proc.returncode:
+ raise subprocess.CalledProcessError(
+ proc.returncode,
+ ['plutil', '-convert', format, '-o', plist_path, '-'],
+ output)
+
+
+def MergeObjects(obj1, obj2):
+ """Merges two objects (either dictionary, list, string or numbers)."""
+ if type(obj1) != type(obj2):
+ return obj2
+
+ if isinstance(obj2, dict):
+ result = dict(obj1)
+ for key in obj2:
+ value1 = obj1.get(key, None)
+ value2 = obj2.get(key, None)
+ result[key] = MergeObjects(value1, value2)
+ return result
+
+ if isinstance(obj2, list):
+ return obj1 + obj2
+
+ return obj2
+
+
+def MergePlists(plist_paths):
+ """Loads and merges all Apple Property List files at |plist_paths|."""
+ plist = {}
+ for plist_path in plist_paths:
+ plist = MergeObjects(plist, LoadPlist(plist_path))
+ return plist
+
+
+def PerformSubstitutions(plist, substitutions):
+ """Performs variables substitutions in |plist| given by |substitutions|."""
+ if isinstance(plist, dict):
+ result = dict(plist)
+ for key in plist:
+ result[key] = PerformSubstitutions(plist[key], substitutions)
+ return result
+
+ if isinstance(plist, list):
+ return [ PerformSubstitutions(item, substitutions) for item in plist ]
+
+ if isinstance(plist, (str, unicode)):
+ result = plist
+ while True:
+ match = VARIABLE_PATTERN.search(result)
+ if not match:
+ break
+
+ extent = match.span()
+ expand = substitutions[match.group(1)]
+ result = result[:extent[0]] + expand + result[extent[1]:]
+ return result
+
+ return plist
+
+
+def PerformSubstitutionsFrom(plist, substitutions_path):
+ """Performs variable substitutions in |plist| from |substitutions_path|."""
+ with open(substitutions_path) as substitutions_file:
+ return PerformSubstitutions(plist, json.load(substitutions_file))
+
+
+def ParseArgs(argv):
+ """Parses command line arguments."""
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument(
+ '-s', '--substitutions',
+ help='path to a JSON file containing variable substitutions')
+ parser.add_argument(
+ '-f', '--format', default='json', choices=('json', 'binary1', 'xml1'),
+ help='format of the generated file')
+ parser.add_argument(
+ '-o', '--output', default='-',
+ help='path to the result; - means stdout')
+ parser.add_argument(
+ 'inputs', nargs='+',
+ help='path of the input files to merge')
+
+ return parser.parse_args(argv)
+
+
+def main(argv):
+ args = ParseArgs(argv)
+
+ data = MergePlists(args.inputs)
+ if args.substitutions:
+ data = PerformSubstitutionsFrom(
+ data, args.substitutions)
+
+ SavePlist(args.output, data, args.format)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/examples/ios/build/config/ios/scripts/sdk_info.py b/gn/examples/ios/build/config/ios/scripts/sdk_info.py
new file mode 100644
index 00000000000..827aee09f4a
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/scripts/sdk_info.py
@@ -0,0 +1,147 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Collects information about the SDK and return them as JSON file."""
+
+import argparse
+import json
+import re
+import subprocess
+import sys
+
+# Patterns used to extract the Xcode version and build version.
+XCODE_VERSION_PATTERN = re.compile(r'Xcode (\d+)\.(\d+)')
+XCODE_BUILD_PATTERN = re.compile(r'Build version (.*)')
+
+
+def GetAppleCpuName(target_cpu):
+ """Returns the name of the |target_cpu| using Apple's convention."""
+ return {
+ 'x64': 'x86_64',
+ 'arm': 'armv7',
+ 'x86': 'i386'
+ }.get(target_cpu, target_cpu)
+
+
+def IsSimulator(target_cpu):
+ """Returns whether the |target_cpu| corresponds to a simulator build."""
+ return not target_cpu.startswith('arm')
+
+
+def GetPlatform(target_cpu):
+ """Returns the platform for the |target_cpu|."""
+ if IsSimulator(target_cpu):
+ return 'iphonesimulator'
+ else:
+ return 'iphoneos'
+
+
+def GetPlaformDisplayName(target_cpu):
+ """Returns the platform display name for the |target_cpu|."""
+ if IsSimulator(target_cpu):
+ return 'iPhoneSimulator'
+ else:
+ return 'iPhoneOS'
+
+
+def ExtractOSVersion():
+ """Extract the version of macOS of the current machine."""
+ return subprocess.check_output(['sw_vers', '-buildVersion']).strip()
+
+
+def ExtractXcodeInfo():
+ """Extract Xcode version and build version."""
+ version, build = None, None
+ for line in subprocess.check_output(['xcodebuild', '-version']).splitlines():
+ match = XCODE_VERSION_PATTERN.search(line)
+ if match:
+ major, minor = match.group(1), match.group(2)
+ version = major.rjust(2, '0') + minor.ljust(2, '0')
+ continue
+
+ match = XCODE_BUILD_PATTERN.search(line)
+ if match:
+ build = match.group(1)
+ continue
+
+ assert version is not None and build is not None
+ return version, build
+
+
+def ExtractSDKInfo(info, sdk):
+ """Extract information about the SDK."""
+ return subprocess.check_output(
+ ['xcrun', '--sdk', sdk, '--show-sdk-' + info]).strip()
+
+
+def GetSDKInfoForCpu(target_cpu, sdk_version, deployment_target):
+ """Returns a dictionary with information about the SDK."""
+ platform = GetPlatform(target_cpu)
+ sdk_version = sdk_version or ExtractSDKInfo('version', platform)
+ deployment_target = deployment_target or sdk_version
+
+ target = target_cpu + '-apple-ios' + deployment_target
+ if IsSimulator(target_cpu):
+ target = target + '-simulator'
+
+ xcode_version, xcode_build = ExtractXcodeInfo()
+ effective_sdk = platform + sdk_version
+
+ sdk_info = {}
+ sdk_info['compiler'] = 'com.apple.compilers.llvm.clang.1_0'
+ sdk_info['is_simulator'] = IsSimulator(target_cpu)
+ sdk_info['macos_build'] = ExtractOSVersion()
+ sdk_info['platform'] = platform
+ sdk_info['platform_name'] = GetPlaformDisplayName(target_cpu)
+ sdk_info['sdk'] = effective_sdk
+ sdk_info['sdk_build'] = ExtractSDKInfo('build-version', effective_sdk)
+ sdk_info['sdk_path'] = ExtractSDKInfo('path', effective_sdk)
+ sdk_info['sdk_version'] = sdk_version
+ sdk_info['target'] = target
+ sdk_info['xcode_build'] = xcode_build
+ sdk_info['xcode_version'] = xcode_version
+
+ return sdk_info
+
+
+def ParseArgs(argv):
+ """Parses command line arguments."""
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument(
+ '-t', '--target-cpu', default='x64',
+ choices=('x86', 'x64', 'arm', 'arm64'),
+ help='target cpu')
+ parser.add_argument(
+ '-s', '--sdk-version',
+ help='version of the sdk')
+ parser.add_argument(
+ '-d', '--deployment-target',
+ help='iOS deployment target')
+ parser.add_argument(
+ '-o', '--output', default='-',
+ help='path of the output file to create; - means stdout')
+
+ return parser.parse_args(argv)
+
+
+def main(argv):
+ args = ParseArgs(argv)
+
+ sdk_info = GetSDKInfoForCpu(
+ GetAppleCpuName(args.target_cpu),
+ args.sdk_version,
+ args.deployment_target)
+
+ if args.output == '-':
+ sys.stdout.write(json.dumps(sdk_info))
+ else:
+ with open(args.output, 'w') as output:
+ output.write(json.dumps(sdk_info))
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/examples/ios/build/config/ios/sdk_info.gni b/gn/examples/ios/build/config/ios/sdk_info.gni
new file mode 100644
index 00000000000..90b86310870
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/sdk_info.gni
@@ -0,0 +1,14 @@
+# Copyright 2019 The Chromium 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/ios/deployment_target.gni")
+
+sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
+ [
+ "--target-cpu",
+ current_cpu,
+ "--deployment-target",
+ ios_deployment_target,
+ ],
+ "json")
diff --git a/gn/examples/ios/build/config/ios/templates/ios_app_bundle.gni b/gn/examples/ios/build/config/ios/templates/ios_app_bundle.gni
new file mode 100644
index 00000000000..a82a5fa6c39
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/templates/ios_app_bundle.gni
@@ -0,0 +1,147 @@
+# Copyright 2019 The Chromium 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/ios/bundle_identifier_prefix.gni")
+import("//build/config/ios/sdk_info.gni")
+import("//build/config/ios/templates/ios_binary_bundle.gni")
+import("//build/config/ios/templates/merge_plist.gni")
+
+# Template to generate an app bundle.
+#
+# All the other parameters are forwarded to a shared_library target that will
+# generate the bundle binary. In general, you want to pass at least "sources"
+# or "deps" to have some binary objects included in your shared library.
+#
+# Arguments
+#
+# - info_plist (optional)
+#
+# path to additional Info.plist to merge into the final bundle Info.plist
+#
+# - bundle_identifier_prefix (optional)
+#
+# prefix for the bundle identifier (the full identifier will be defined
+# to $bundle_identifier_prefix.$output_name); if unset will defaults to
+# default_bundle_identifier_prefix
+#
+# - output_name (optional)
+#
+# name of the bundle without the extension; defaults to $target_name
+#
+template("ios_app_bundle") {
+ _output_name = target_name
+ if (defined(invoker.output_name)) {
+ _output_name = invoker.output_name
+ }
+
+ _bundle_identifier_prefix = default_bundle_identifier_prefix
+ if (defined(invoker.bundle_identifier_prefix)) {
+ _bundle_identifier_prefix = invoker.bundle_identifier_prefix
+ }
+
+ _bundle_identifier = "$_bundle_identifier_prefix.$_output_name"
+
+ _app_prefix_target = target_name + "_app_prefix"
+ _app_prefix_output = "$target_out_dir/$_app_prefix_target/app_prefix.json"
+
+ action(_app_prefix_target) {
+ script = "//build/config/ios/scripts/find_app_identifier_prefix.py"
+ sources = []
+ outputs = [ _app_prefix_output ]
+ args = [
+ "-b=" + _bundle_identifier,
+ "-o=" + rebase_path(_app_prefix_output, root_build_dir),
+ ]
+ }
+
+ if (sdk_info.is_simulator) {
+ _simu_xcent_target = target_name + "_simu_xcent"
+ _simu_xcent_output =
+ "$target_out_dir/$_simu_xcent_target/" + "Entitlements-Simulated.plist"
+
+ merge_plist(_simu_xcent_target) {
+ format = "xml1"
+ output = _simu_xcent_output
+ plists = [ "//build/config/ios/resources/Entitlements-Simulated.plist" ]
+ substitutions_json = _app_prefix_output
+ deps = [ ":$_app_prefix_target" ]
+ }
+ }
+
+ _executable_target = target_name + "_executable"
+ _executable_bundle = target_name + "_executable_bundle"
+
+ executable(_executable_target) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "bundle_extension",
+ "bundle_identifier_prefix",
+ "bundle_type",
+ "display_name",
+ "info_plist",
+ "output_name",
+ "public_headers",
+ ])
+
+ output_extension = ""
+ output_name = _output_name
+ output_prefix_override = true
+ output_dir = "$target_out_dir/$_executable_target"
+
+ if (sdk_info.is_simulator) {
+ if (!defined(deps)) {
+ deps = []
+ }
+ if (!defined(inputs)) {
+ inputs = []
+ }
+ if (!defined(ldflags)) {
+ ldflags = []
+ }
+
+ deps += [ ":$_simu_xcent_target" ]
+ inputs += [ _simu_xcent_output ]
+ ldflags += [
+ "-Xlinker",
+ "-sectcreate",
+ "-Xlinker",
+ "__TEXT",
+ "-Xlinker",
+ "__entitlements",
+ "-Xlinker",
+ rebase_path(_simu_xcent_output, root_build_dir),
+ ]
+ }
+ }
+
+ bundle_data(_executable_bundle) {
+ public_deps = [ ":$_executable_target" ]
+ sources = [ "$target_out_dir/$_executable_target/$_output_name" ]
+ outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ]
+ }
+
+ ios_binary_bundle(target_name) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "bundle_extension",
+ "bundle_identifier_prefix",
+ "bundle_type",
+ "deps",
+ "output_name",
+ "public_deps",
+ "public_headers",
+ ])
+
+ output_name = _output_name
+ product_type = "com.apple.product-type.application"
+
+ bundle_identifier = _bundle_identifier
+ bundle_extension = "app"
+ bundle_type = "AAPL"
+
+ public_deps = [ ":$_executable_bundle" ]
+ }
+}
diff --git a/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni b/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni
new file mode 100644
index 00000000000..1a3d6296321
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni
@@ -0,0 +1,121 @@
+# Copyright 2019 The Chromium 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/ios/templates/merge_plist.gni")
+
+# Template to create an Apple bundle containing a binary file (e.g. .app or
+# .framework bundle).
+#
+# Arguments
+#
+# - bundle_extension
+#
+# extension of the bundle (e.g. "app", "framework", ...); must not
+# include the dot preceding the extension
+#
+# - bundle_type
+#
+# four letter code corresponding to the bundle type ("FMWK", "AAPL",
+# ...); used to fill the "Bundle OS Type code" value in the generated
+# Info.plist for the bundle
+#
+# - bundle_identitier
+#
+# bundle identitifier
+#
+# - product_type
+#
+# type of the generated bundle (used for Xcode project)
+#
+# - output_name (optional)
+#
+# name of the bundle without the extension; the bundle binary (i.e.
+# the application or the library) must have the same name; defaults
+# to $target_name
+#
+# - display_name (optional)
+#
+# display name of the bundle (e.g. the name that is displayed to the
+# user); defaults to $output_name
+#
+# - info_plist (optional)
+#
+# path to additional Info.plist to merge into the final bundle Info.plist
+#
+template("ios_binary_bundle") {
+ assert(
+ defined(invoker.bundle_extension),
+ "bundle_extension must be defined for ios_binary_bundle ($target_name)")
+ assert(
+ defined(invoker.bundle_identifier),
+ "bundle_identifier must be defined for ios_binary_bundle ($target_name)")
+ assert(defined(invoker.bundle_type),
+ "bundle_type must be defined for ios_binary_bundle ($target_name)")
+ assert(defined(invoker.product_type),
+ "product_type must be defined for ios_binary_bundle ($target_name)")
+
+ _output_name = target_name
+ if (defined(invoker.output_name)) {
+ _output_name = invoker.output_name
+ }
+
+ _display_name = _output_name
+ if (defined(invoker.display_name)) {
+ _display_name = invoker.display_name
+ }
+
+ _plist_target = target_name + "_plist"
+ _plist_bundle = target_name + "_plist_bundle"
+
+ merge_plist(_plist_target) {
+ substitutions = {
+ CURRENT_PROJECT_VERSION = "1"
+ DEVELOPMENT_LANGUAGE = "en"
+ EXECUTABLE_NAME = "$_output_name"
+ PRODUCT_BUNDLE_IDENTIFIER = invoker.bundle_identifier
+ PRODUCT_BUNDLE_PACKAGE_TYPE = invoker.bundle_type
+ PRODUCT_NAME = "$_display_name"
+ }
+
+ format = "binary1"
+ output = "$target_out_dir/$_plist_target/Info.plist"
+ plists = [
+ get_label_info("//build/config/ios:compiler_plist", "target_out_dir") +
+ "/compiler_plist/Info.plist",
+ "//build/config/ios/resources/Info.plist",
+ ]
+
+ if (defined(invoker.info_plist)) {
+ plists += [ invoker.info_plist ]
+ }
+
+ deps = [ "//build/config/ios:compiler_plist" ]
+ }
+
+ bundle_data(_plist_bundle) {
+ public_deps = [ ":$_plist_target" ]
+ sources = [ "$target_out_dir/$_plist_target/Info.plist" ]
+ outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
+ }
+
+ create_bundle(target_name) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "display_name",
+ "output_name",
+ "bundle_extension",
+ "bundle_type",
+ ])
+
+ if (!defined(public_deps)) {
+ public_deps = []
+ }
+ public_deps += [ ":$_plist_bundle" ]
+ bundle_root_dir = "$root_out_dir/$_output_name.${invoker.bundle_extension}"
+ bundle_contents_dir = bundle_root_dir
+ bundle_executable_dir = bundle_contents_dir
+ bundle_resources_dir = bundle_contents_dir
+ }
+}
diff --git a/gn/examples/ios/build/config/ios/templates/ios_framework_bundle.gni b/gn/examples/ios/build/config/ios/templates/ios_framework_bundle.gni
new file mode 100644
index 00000000000..13db26656f0
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/templates/ios_framework_bundle.gni
@@ -0,0 +1,152 @@
+# Copyright 2019 The Chromium 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/ios/bundle_identifier_prefix.gni")
+import("//build/config/ios/templates/ios_binary_bundle.gni")
+
+# Template to generate a framework bundle.
+#
+# All the other parameters are forwarded to a shared_library target that will
+# generate the bundle binary. In general, you want to pass at least "sources"
+# or "deps" to have some binary objects included in your shared library.
+#
+# Arguments
+#
+# - info_plist (optional)
+#
+# path to additional Info.plist to merge into the final bundle Info.plist
+#
+# - bundle_identifier_prefix (optional)
+#
+# prefix for the bundle identifier (the full identifier will be defined
+# to $bundle_identifier_prefix.$output_name); if unset will defaults to
+# default_bundle_identifier_prefix
+#
+# - output_name (optional)
+#
+# name of the bundle without the extension; defaults to $target_name
+#
+# - public_headers (optional)
+#
+# list of public headers files to copy into the framework bundle; this
+# does not generate an umbrella header; an umbrella header named after
+# the framework bundle will be created
+#
+template("ios_framework_bundle") {
+ _output_name = target_name
+ if (defined(invoker.output_name)) {
+ _output_name = invoker.output_name
+ }
+
+ _dylib_target = target_name + "_dylib"
+ _dylib_bundle = target_name + "_dylib_bundle"
+
+ _bundle_identifier_prefix = default_bundle_identifier_prefix
+ if (defined(invoker.bundle_identifier_prefix)) {
+ _bundle_identifier_prefix = invoker.bundle_identifier_prefix
+ }
+
+ _bundle_identifier = "$_bundle_identifier_prefix.$_output_name"
+
+ shared_library(_dylib_target) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "bundle_extension",
+ "bundle_identifier_prefix",
+ "bundle_type",
+ "display_name",
+ "info_plist",
+ "output_name",
+ "public_headers",
+ ])
+
+ output_extension = ""
+ output_name = _output_name
+ output_prefix_override = true
+ output_dir = "$target_out_dir/$_dylib_target"
+
+ if (!defined(ldflags)) {
+ ldflags = []
+ }
+ ldflags += [
+ "-Xlinker",
+ "-install_name",
+ "-Xlinker",
+ "@rpath/$_output_name.framework/$_output_name",
+ ]
+ }
+
+ bundle_data(_dylib_bundle) {
+ public_deps = [ ":$_dylib_target" ]
+ sources = [ "$target_out_dir/$_dylib_target/$_output_name" ]
+ outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ]
+ }
+
+ if (defined(invoker.public_headers)) {
+ _umbrella_target = target_name + "_umbrella"
+ _umbrella_output = "$target_out_dir/$_umbrella_target/$_output_name.h"
+
+ action(_umbrella_target) {
+ script = "//build/config/ios/scripts/generate_umbrella_header.py"
+ sources = []
+ outputs = [ _umbrella_output ]
+ args = [ "-o=" + rebase_path(_umbrella_output, root_build_dir) ] +
+ rebase_path(invoker.public_headers, root_build_dir)
+ }
+
+ _headers_bundle = target_name + "_headers_bundle"
+
+ bundle_data(_headers_bundle) {
+ sources = invoker.public_headers + [ _umbrella_output ]
+ outputs = [ "{{bundle_resources_dir}}/Headers/{{source_file_part}}" ]
+ public_deps = [ ":$_umbrella_target" ]
+ }
+ }
+
+ _config_name = target_name + "_config"
+
+ config(_config_name) {
+ framework_dirs = [ root_out_dir ]
+ frameworks = [ "$_output_name.framework" ]
+ }
+
+ ios_binary_bundle(target_name) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "bundle_extension",
+ "bundle_type",
+ "configs",
+ "deps",
+ "output_name",
+ "public_configs",
+ "public_deps",
+ "public_headers",
+ ])
+
+ output_name = _output_name
+ product_type = "com.apple.product-type.framework"
+
+ bundle_identifier = _bundle_identifier
+ bundle_extension = "framework"
+ bundle_type = "FMWK"
+
+ public_deps = [ ":$_dylib_bundle" ]
+
+ if (defined(invoker.public_headers)) {
+ public_deps += [ ":$_headers_bundle" ]
+ }
+
+ public_configs = [ ":$_config_name" ]
+ }
+
+ _target_name = target_name
+
+ bundle_data("$target_name+bundle") {
+ public_deps = [ ":$_target_name" ]
+ sources = [ "$root_out_dir/$_output_name.framework" ]
+ outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
+ }
+}
diff --git a/gn/examples/ios/build/config/ios/templates/merge_plist.gni b/gn/examples/ios/build/config/ios/templates/merge_plist.gni
new file mode 100644
index 00000000000..f48432bbe66
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/templates/merge_plist.gni
@@ -0,0 +1,85 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Template to merge multiple Apple Property List file into a single file.
+#
+# Arguments
+#
+# - output
+#
+# path of the file that will be generated (must be in a sub-directory
+# of root_build_dir)
+#
+# - plists
+#
+# list of path to Apple Property List file to merge (the file may be
+# in either "json", "binary1" or "xml1" format)
+#
+# - format (optional)
+#
+# format in which the file must be saved; must be one of "json",
+# "binary1" or "xml1" (default to "json" if omitted)
+#
+# - substitutions (optional)
+#
+# a scope defining variable substitutions to perform when merging the
+# Property List files (i.e. if scope define foo = "bar", occurences
+# of $(foo) in any string in a property list will be replaced by
+# bar)
+#
+template("merge_plist") {
+ assert(defined(invoker.output) && invoker.output != "",
+ "output must be defined for merge_plist ($target_name)")
+ assert(defined(invoker.plists) && invoker.plists != [],
+ "plists must be defined for merge_plist ($target_name)")
+
+ if (defined(invoker.substitutions)) {
+ assert(!defined(invoker.substitutions_json),
+ "cannot define both substitutions and substitutions_json")
+
+ _substitutions_json = "$target_out_dir/$target_name/substitutions.json"
+ write_file(_substitutions_json, invoker.substitutions, "json")
+ }
+
+ if (defined(invoker.substitutions_json)) {
+ _substitutions_json = invoker.substitutions_json
+ }
+
+ action(target_name) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "args",
+ "format",
+ "inputs",
+ "output",
+ "plists",
+ "script",
+ "sources",
+ "substitutions",
+ "substitutions_json",
+ ])
+
+ script = "//build/config/ios/scripts/merge_plist.py"
+ sources = invoker.plists
+ outputs = [ invoker.output ]
+
+ _format = "json"
+ if (defined(invoker.format)) {
+ _format = invoker.format
+ }
+
+ args = [
+ "-f=" + _format,
+ "-o=" + rebase_path(invoker.output, root_build_dir),
+ ]
+
+ if (defined(_substitutions_json)) {
+ inputs = [ _substitutions_json ]
+ args += [ "-s=" + rebase_path(_substitutions_json) ]
+ }
+
+ args += rebase_path(sources, root_build_dir)
+ }
+}
diff --git a/gn/examples/ios/build/config/ios/templates/storyboards.gni b/gn/examples/ios/build/config/ios/templates/storyboards.gni
new file mode 100644
index 00000000000..de36ef37c66
--- /dev/null
+++ b/gn/examples/ios/build/config/ios/templates/storyboards.gni
@@ -0,0 +1,31 @@
+# Copyright 2019 The Chromium 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/ios/deployment_target.gni")
+
+template("storyboards") {
+ assert(defined(invoker.sources),
+ "sources must be defined for storyboard ($target_name)")
+
+ _compile_target = target_name + "_compile"
+ _compile_output =
+ "$target_out_dir/$_compile_target/{{source_name_part}}.storyboardc"
+
+ action_foreach(_compile_target) {
+ script = "//build/config/ios/scripts/compile_storyboard.py"
+ sources = invoker.sources
+ outputs = [ _compile_output ]
+ args = [
+ "{{source}}",
+ "-o=" + rebase_path(_compile_output, root_build_dir),
+ "--minimum-deployment-target=$ios_deployment_target",
+ ]
+ }
+
+ bundle_data(target_name) {
+ public_deps = [ ":$_compile_target" ]
+ sources = get_target_outputs(":$_compile_target")
+ outputs = [ "{{bundle_root_dir}}/Base.lproj/{{source_file_part}}" ]
+ }
+}
diff --git a/gn/examples/ios/build/toolchain/apple/swiftc.py b/gn/examples/ios/build/toolchain/apple/swiftc.py
new file mode 100755
index 00000000000..88ae6e5ce4e
--- /dev/null
+++ b/gn/examples/ios/build/toolchain/apple/swiftc.py
@@ -0,0 +1,183 @@
+#!/usr/bin/python3
+
+
+import argparse
+import collections
+import json
+import os
+import subprocess
+import sys
+import tempfile
+
+
+class OrderedSet(collections.OrderedDict):
+
+ def add(self, value):
+ self[value] = True
+
+
+def compile_module(module, sources, settings, extras, tmpdir):
+ output_file_map = {}
+ if settings.whole_module_optimization:
+ output_file_map[''] = {
+ 'object': os.path.join(settings.object_dir, module + '.o'),
+ 'dependencies': os.path.join(tmpdir, module + '.d'),
+ }
+ else:
+ for source in sources:
+ name, _ = os.path.splitext(os.path.basename(source))
+ output_file_map[source] = {
+ 'object': os.path.join(settings.object_dir, name + '.o'),
+ 'dependencies': os.path.join(tmpdir, name + '.d'),
+ }
+
+ for key in ('module_path', 'header_path', 'depfile'):
+ path = getattr(settings, key)
+ if os.path.exists(path):
+ os.unlink(path)
+ if key == 'module_path':
+ for ext in '.swiftdoc', '.swiftsourceinfo':
+ path = os.path.splitext(getattr(settings, key))[0] + ext
+ if os.path.exists(path):
+ os.unlink(path)
+ directory = os.path.dirname(path)
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ if not os.path.exists(settings.object_dir):
+ os.makedirs(settings.object_dir)
+
+ for key in output_file_map:
+ path = output_file_map[key]['object']
+ if os.path.exists(path):
+ os.unlink(path)
+
+ output_file_map_path = os.path.join(tmpdir, module + '.json')
+ with open(output_file_map_path, 'w') as output_file_map_file:
+ output_file_map_file.write(json.dumps(output_file_map))
+ output_file_map_file.flush()
+
+ extra_args = []
+ if settings.bridge_header:
+ extra_args.extend([
+ '-import-objc-header',
+ os.path.abspath(settings.bridge_header),
+ ])
+
+ if settings.whole_module_optimization:
+ extra_args.append('-whole-module-optimization')
+
+ if settings.target:
+ extra_args.extend([
+ '-target',
+ settings.target,
+ ])
+
+ if settings.sdk:
+ extra_args.extend([
+ '-sdk',
+ os.path.abspath(settings.sdk),
+ ])
+
+ if settings.include_dirs:
+ for include_dir in settings.include_dirs:
+ extra_args.append('-I' + include_dir)
+
+ process = subprocess.Popen(
+ ['swiftc',
+ '-parse-as-library',
+ '-module-name',
+ module,
+ '-emit-object',
+ '-emit-dependencies',
+ '-emit-module',
+ '-emit-module-path',
+ settings.module_path,
+ '-emit-objc-header',
+ '-emit-objc-header-path',
+ settings.header_path,
+ '-output-file-map',
+ output_file_map_path,
+ ] + extra_args + extras + sources,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ universal_newlines=True)
+
+ stdout, stderr = process.communicate()
+ if process.returncode:
+ sys.stdout.write(stdout)
+ sys.stderr.write(stderr)
+ sys.exit(process.returncode)
+
+
+ depfile_content = collections.OrderedDict()
+ for key in output_file_map:
+ for line in open(output_file_map[key]['dependencies']):
+ output, inputs = line.split(' : ', 2)
+ _, ext = os.path.splitext(output)
+ if ext == '.o':
+ key = output
+ else:
+ key = os.path.splitext(settings.module_path)[0] + ext
+ if key not in depfile_content:
+ depfile_content[key] = OrderedSet()
+ for path in inputs.split():
+ depfile_content[key].add(path)
+
+ with open(settings.depfile, 'w') as depfile:
+ for key in depfile_content:
+ if not settings.depfile_filter or key in settings.depfile_filter:
+ inputs = depfile_content[key]
+ depfile.write('%s : %s\n' % (key, ' '.join(inputs)))
+
+
+def main(args):
+ parser = argparse.ArgumentParser(add_help=False)
+ parser.add_argument(
+ '--module-name',
+ help='name of the Swift module')
+ parser.add_argument(
+ '--include', '-I', action='append', dest='include_dirs',
+ help='add directory to header search path')
+ parser.add_argument(
+ 'sources', nargs='+',
+ help='Swift source file to compile')
+ parser.add_argument(
+ '--whole-module-optimization', action='store_true',
+ help='enable whole module optimization')
+ parser.add_argument(
+ '--object-dir', '-o',
+ help='path to the generated object files directory')
+ parser.add_argument(
+ '--module-path', '-m',
+ help='path to the generated module file')
+ parser.add_argument(
+ '--header-path', '-h',
+ help='path to the generated header file')
+ parser.add_argument(
+ '--bridge-header', '-b',
+ help='path to the Objective-C bridge header')
+ parser.add_argument(
+ '--depfile', '-d',
+ help='path to the generated depfile')
+ parser.add_argument(
+ '--depfile-filter', action='append',
+ help='limit depfile to those files')
+ parser.add_argument(
+ '--target', action='store',
+ help='generate code for the given target <triple>')
+ parser.add_argument(
+ '--sdk', action='store',
+ help='compile against sdk')
+
+ parsed, extras = parser.parse_known_args(args)
+ with tempfile.TemporaryDirectory() as tmpdir:
+ compile_module(
+ parsed.module_name,
+ parsed.sources,
+ parsed,
+ extras,
+ tmpdir)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/examples/ios/build/toolchain/ios/BUILD.gn b/gn/examples/ios/build/toolchain/ios/BUILD.gn
new file mode 100644
index 00000000000..12d246b5c03
--- /dev/null
+++ b/gn/examples/ios/build/toolchain/ios/BUILD.gn
@@ -0,0 +1,167 @@
+# Copyright 2019 The Chromium 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/ios/deployment_target.gni")
+
+declare_args() {
+ # Configure whether whole module optimization is enabled when compiling
+ # swift modules.
+ swift_whole_module_optimization = true
+}
+
+template("ios_toolchain") {
+ toolchain(target_name) {
+ assert(defined(invoker.toolchain_args),
+ "Toolchains must declare toolchain_args")
+
+ toolchain_args = {
+ forward_variables_from(invoker.toolchain_args, "*")
+ }
+
+ _sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
+ [
+ "--target-cpu",
+ current_cpu,
+ "--deployment-target",
+ ios_deployment_target,
+ ],
+ "json")
+
+ cc = "clang -target ${_sdk_info.target} -isysroot ${_sdk_info.sdk_path}"
+ cxx = "clang++ -target ${_sdk_info.target} -isysroot ${_sdk_info.sdk_path}"
+
+ swiftmodule_switch = "-Wl,-add_ast_path,"
+
+ tool("link") {
+ output = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ rspfile = output + ".rsp"
+ rspfile_content = "{{inputs_newline}}"
+
+ outputs = [ output ]
+ command = "$cxx {{ldflags}} -o $output -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}} {{swiftmodules}}"
+ description = "LINK {{output}}"
+
+ default_output_dir = "{{root_out_dir}}"
+ default_output_extension = ""
+ output_prefix = ""
+ }
+
+ tool("solink") {
+ dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ rspfile = dylib + ".rsp"
+ rspfile_content = "{{inputs_newline}}"
+
+ outputs = [ dylib ]
+ command = "$cxx -dynamiclib {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}} {{swiftmodules}}"
+ description = "SOLINK {{output}}"
+
+ default_output_dir = "{{root_out_dir}}"
+ default_output_extension = ".dylib"
+ output_prefix = "lib"
+ }
+
+ tool("cc") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CC {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("cxx") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CXX {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("objc") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "OBJC {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("objcxx") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "OBJCXX {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("stamp") {
+ command = "touch {{output}}"
+ description = "STAMP {{output}}"
+ }
+
+ tool("copy_bundle_data") {
+ command = "rm -rf {{output}} && cp -PR {{source}} {{output}}"
+ description = "COPY_BUNDLE_DATA {{output}}"
+ }
+
+ tool("swift") {
+ depfile = "{{target_out_dir}}/{{module_name}}.d"
+ depsformat = "gcc"
+
+ outputs = [
+ # The module needs to be the first output to ensure the
+ # depfile generate works correctly with ninja < 1.9.0.
+ "{{target_gen_dir}}/{{module_name}}.swiftmodule",
+
+ "{{target_gen_dir}}/{{module_name}}.h",
+ "{{target_gen_dir}}/{{module_name}}.swiftdoc",
+ "{{target_gen_dir}}/{{module_name}}.swiftsourceinfo",
+ ]
+
+ if (swift_whole_module_optimization) {
+ _extra_flag = "--whole-module-optimization"
+ _object_dir = "{{target_out_dir}}"
+ outputs += [ "{{target_out_dir}}/{{module_name}}.o" ]
+ } else {
+ _extra_flag = ""
+ _object_dir = "{{target_out_dir}}/{{label_name}}"
+ partial_outputs =
+ [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ _swiftc = rebase_path("//build/toolchain/apple/swiftc.py", root_build_dir)
+ command = "$_swiftc --target ${_sdk_info.target} --sdk ${_sdk_info.sdk_path} --module-name {{module_name}} --object-dir $_object_dir --module-path {{target_gen_dir}}/{{module_name}}.swiftmodule --header-path {{target_gen_dir}}/{{module_name}}.h --depfile {{target_out_dir}}/{{module_name}}.d --depfile-filter {{target_gen_dir}}/{{module_name}}.swiftmodule --bridge-header {{bridge_header}} $_extra_flag {{defines}} {{swiftflags}} {{include_dirs}} {{module_dirs}} {{inputs}}"
+ }
+ }
+}
+
+ios_toolchain("clang_x86") {
+ toolchain_args = {
+ current_cpu = "x86"
+ current_os = "ios"
+ }
+}
+
+ios_toolchain("clang_x64") {
+ toolchain_args = {
+ current_cpu = "x64"
+ current_os = "ios"
+ }
+}
+
+ios_toolchain("clang_arm") {
+ toolchain_args = {
+ current_cpu = "arm"
+ current_os = "ios"
+ }
+}
+
+ios_toolchain("clang_arm64") {
+ toolchain_args = {
+ current_cpu = "arm64"
+ current_os = "ios"
+ }
+}
diff --git a/gn/examples/ios/build/toolchain/mac/BUILD.gn b/gn/examples/ios/build/toolchain/mac/BUILD.gn
new file mode 100644
index 00000000000..045a90fe331
--- /dev/null
+++ b/gn/examples/ios/build/toolchain/mac/BUILD.gn
@@ -0,0 +1,119 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+template("mac_toolchain") {
+ toolchain(target_name) {
+ assert(defined(invoker.toolchain_args),
+ "Toolchains must declare toolchain_args")
+
+ toolchain_args = {
+ forward_variables_from(invoker.toolchain_args, "*")
+ }
+
+ cc = "clang"
+ cxx = "clang++"
+
+ tool("link") {
+ output = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ rspfile = output + ".rsp"
+ rspfile_content = "{{inputs_newline}}"
+
+ outputs = [ output ]
+ command = "$cxx {{ldflags}} -o $output -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}"
+ description = "LINK {{output}}"
+
+ default_output_dir = "{{root_out_dir}}"
+ default_output_extension = ""
+ output_prefix = ""
+ }
+
+ tool("solink") {
+ dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ rspfile = dylib + ".rsp"
+ rspfile_content = "{{inputs_newline}}"
+
+ outputs = [ dylib ]
+ command = "$cxx -dynamiclib {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}"
+ description = "SOLINK {{output}}"
+
+ default_output_dir = "{{root_out_dir}}"
+ default_output_extension = ".dylib"
+ output_prefix = "lib"
+ }
+
+ tool("cc") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CC {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("cxx") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "CXX {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("objc") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "OBJC {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("objcxx") {
+ depfile = "{{output}}.d"
+ precompiled_header_type = "gcc"
+ command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}"
+ depsformat = "gcc"
+ description = "OBJCXX {{output}}"
+ outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
+ }
+
+ tool("stamp") {
+ command = "touch {{output}}"
+ description = "STAMP {{output}}"
+ }
+
+ tool("copy_bundle_data") {
+ command = "rm -rf {{output}} && cp -a {{source}} {{output}}"
+ description = "COPY_BUNDLE_DATA {{output}}"
+ }
+ }
+}
+
+mac_toolchain("clang_x86") {
+ toolchain_args = {
+ current_cpu = "x86"
+ current_os = "mac"
+ }
+}
+
+mac_toolchain("clang_x64") {
+ toolchain_args = {
+ current_cpu = "x64"
+ current_os = "mac"
+ }
+}
+
+mac_toolchain("clang_arm") {
+ toolchain_args = {
+ current_cpu = "arm"
+ current_os = "mac"
+ }
+}
+
+mac_toolchain("clang_arm64") {
+ toolchain_args = {
+ current_cpu = "arm64"
+ current_os = "mac"
+ }
+}
diff --git a/gn/examples/ios/host/BUILD.gn b/gn/examples/ios/host/BUILD.gn
new file mode 100644
index 00000000000..90a789f7a66
--- /dev/null
+++ b/gn/examples/ios/host/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2019 The Chromium 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 (current_toolchain == host_toolchain) {
+ executable("username") {
+ sources = [ "main.cc" ]
+ }
+} else {
+ group("username") {
+ deps = [ ":username($host_toolchain)" ]
+ }
+}
diff --git a/gn/examples/ios/host/main.cc b/gn/examples/ios/host/main.cc
new file mode 100644
index 00000000000..1f244efaf4f
--- /dev/null
+++ b/gn/examples/ios/host/main.cc
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium 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 <stdlib.h>
+#include <iostream>
+#include <string>
+
+namespace {
+
+// Returns the current user username.
+std::string Username() {
+ const char* username = getenv("USER");
+ return username ? std::string(username) : std::string();
+}
+
+// Writes |string| to |stream| while escaping all C escape sequences.
+void EscapeString(std::ostream* stream, const std::string& string) {
+ for (char c : string) {
+ switch (c) {
+ case 0:
+ *stream << "\\0";
+ break;
+ case '\a':
+ *stream << "\\a";
+ break;
+ case '\b':
+ *stream << "\\b";
+ break;
+ case '\e':
+ *stream << "\\e";
+ break;
+ case '\f':
+ *stream << "\\f";
+ break;
+ case '\n':
+ *stream << "\\n";
+ break;
+ case '\r':
+ *stream << "\\r";
+ break;
+ case '\t':
+ *stream << "\\t";
+ break;
+ case '\v':
+ *stream << "\\v";
+ break;
+ case '\\':
+ *stream << "\\\\";
+ break;
+ case '\"':
+ *stream << "\\\"";
+ break;
+ default:
+ *stream << c;
+ break;
+ }
+ }
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ std::string username = Username();
+
+ std::cout << "{\"username\": \"";
+ EscapeString(&std::cout, username);
+ std::cout << "\"}" << std::endl;
+
+ return 0;
+}
diff --git a/gn/examples/ios/shared/BUILD.gn b/gn/examples/ios/shared/BUILD.gn
new file mode 100644
index 00000000000..f4641c3b568
--- /dev/null
+++ b/gn/examples/ios/shared/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2019 The Chromium 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/ios/templates/ios_framework_bundle.gni")
+
+ios_framework_bundle("hello_framework") {
+ output_name = "HelloShared"
+
+ sources = [
+ "hello_shared.h",
+ "hello_shared.m",
+ ]
+ public_headers = [ "hello_shared.h" ]
+
+ defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
+ frameworks = [ "Foundation.framework" ]
+}
diff --git a/gn/examples/ios/shared/hello_shared.h b/gn/examples/ios/shared/hello_shared.h
new file mode 100644
index 00000000000..b351a50a4c4
--- /dev/null
+++ b/gn/examples/ios/shared/hello_shared.h
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium 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 <Foundation/Foundation.h>
+
+@interface Greetings : NSObject
+
++ (NSString*)greet;
+
++ (NSString*)greetWithName:(NSString*)name;
+
+@end
diff --git a/gn/examples/ios/shared/hello_shared.m b/gn/examples/ios/shared/hello_shared.m
new file mode 100644
index 00000000000..5e811145524
--- /dev/null
+++ b/gn/examples/ios/shared/hello_shared.m
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium 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 "hello_shared.h"
+
+@implementation Greetings
+
++ (NSString*)greet {
+ return [NSString stringWithFormat:@"Hello from %@!", [Greetings displayName]];
+}
+
++ (NSString*)greetWithName:(NSString*)name {
+ return [NSString stringWithFormat:@"Hello, %@!", name];
+}
+
++ (NSString*)displayName {
+ NSBundle* bundle = [NSBundle bundleForClass:[Greetings class]];
+ return [[bundle infoDictionary] objectForKey:@"CFBundleName"];
+}
+
+@end
diff --git a/gn/examples/rust_example/BUILD.gn b/gn/examples/rust_example/BUILD.gn
index e28f02366e4..964b331f96c 100644
--- a/gn/examples/rust_example/BUILD.gn
+++ b/gn/examples/rust_example/BUILD.gn
@@ -1,5 +1,3 @@
group("default") {
- deps = [
- "//hello_world/src:hello_world",
- ]
+ deps = [ "//hello_world/src:hello_world" ]
}
diff --git a/gn/examples/rust_example/README.txt b/gn/examples/rust_example/README.txt
index 8bfbdd0570c..064e869aea2 100644
--- a/gn/examples/rust_example/README.txt
+++ b/gn/examples/rust_example/README.txt
@@ -1,4 +1,4 @@
-This is an example directory structure that compiles some simple targets using
-gcc. It is intended to show how to set up a simple Rust GN build.
+This is an example directory structure that compiles a Rust hello world
+example. It is intended to show how to set up a simple Rust GN build.
Don't miss the ".gn" file in this directory!
diff --git a/gn/examples/rust_example/build/BUILD.gn b/gn/examples/rust_example/build/BUILD.gn
index e4aecfde1ef..ebfaa64f889 100644
--- a/gn/examples/rust_example/build/BUILD.gn
+++ b/gn/examples/rust_example/build/BUILD.gn
@@ -1,14 +1,34 @@
toolchain("rust") {
- outfile = "{{target_out_dir}}/{{rustc_output_prefix}}{{crate_name}}{{rustc_output_extension}}"
- tool("rustc") {
+ tool("rust_bin") {
depfile = "{{target_out_dir}}/{{crate_name}}.d"
- command = "rustc --edition={{edition}} --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+ outfile = "{{target_out_dir}}/{{crate_name}}"
+ command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
description = "RUST $outfile"
- cdylib_output_extension = ".so"
- proc_macro_output_extension = ".so"
- outputs = [
- outfile,
- ]
+ outputs = [ outfile ]
+ }
+
+ tool("rust_staticlib") {
+ depfile = "{{target_out_dir}}/{{crate_name}}.d"
+ outfile = "{{target_out_dir}}/{{crate_name}}.a"
+ command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+ description = "RUST $outfile"
+ outputs = [ outfile ]
+ }
+
+ tool("rust_rlib") {
+ depfile = "{{target_out_dir}}/{{crate_name}}.d"
+ outfile = "{{target_out_dir}}/lib{{crate_name}}.rlib"
+ command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+ description = "RUST $outfile"
+ outputs = [ outfile ]
+ }
+
+ tool("rust_cdylib") {
+ depfile = "{{target_out_dir}}/{{crate_name}}.d"
+ outfile = "{{target_out_dir}}/lib{{crate_name}}.so"
+ command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+ description = "RUST $outfile"
+ outputs = [ outfile ]
}
tool("stamp") {
diff --git a/gn/examples/rust_example/hello_world/bar/src/BUILD.gn b/gn/examples/rust_example/hello_world/bar/src/BUILD.gn
index 49c6eeef37a..b4eec33fa25 100644
--- a/gn/examples/rust_example/hello_world/bar/src/BUILD.gn
+++ b/gn/examples/rust_example/hello_world/bar/src/BUILD.gn
@@ -1,4 +1,3 @@
rust_library("bar") {
crate_root = "lib.rs"
- edition = "2018"
}
diff --git a/gn/examples/rust_example/hello_world/foo/src/BUILD.gn b/gn/examples/rust_example/hello_world/foo/src/BUILD.gn
index 61ee1f3bae3..7525454cade 100644
--- a/gn/examples/rust_example/hello_world/foo/src/BUILD.gn
+++ b/gn/examples/rust_example/hello_world/foo/src/BUILD.gn
@@ -1,9 +1,4 @@
rust_library("foo") {
- sources = [
- "lib.rs",
- ]
- edition = "2018"
- deps = [
- "//hello_world/bar/src:bar",
- ]
+ sources = [ "lib.rs" ]
+ deps = [ "//hello_world/bar/src:bar" ]
}
diff --git a/gn/examples/rust_example/hello_world/src/BUILD.gn b/gn/examples/rust_example/hello_world/src/BUILD.gn
index d6a0a664968..d50b876d6fe 100644
--- a/gn/examples/rust_example/hello_world/src/BUILD.gn
+++ b/gn/examples/rust_example/hello_world/src/BUILD.gn
@@ -1,11 +1,6 @@
executable("hello_world") {
- sources = [
- "main.rs",
- ]
- deps = [
- "//hello_world/foo/src:foo",
- ]
- edition = "2018"
+ sources = [ "main.rs" ]
+ deps = [ "//hello_world/foo/src:foo" ]
aliased_deps = {
baz = "//hello_world/foo/src:foo"
}
diff --git a/gn/examples/simple_build/BUILD.gn b/gn/examples/simple_build/BUILD.gn
index a18390e6489..965fc42dc1d 100644
--- a/gn/examples/simple_build/BUILD.gn
+++ b/gn/examples/simple_build/BUILD.gn
@@ -3,9 +3,7 @@
# found in the LICENSE file.
executable("hello") {
- sources = [
- "hello.cc",
- ]
+ sources = [ "hello.cc" ]
deps = [
":hello_shared",
diff --git a/gn/examples/simple_build/build/BUILD.gn b/gn/examples/simple_build/build/BUILD.gn
index 8eae46a1aad..cc3f9482bd7 100644
--- a/gn/examples/simple_build/build/BUILD.gn
+++ b/gn/examples/simple_build/build/BUILD.gn
@@ -12,8 +12,10 @@ config("compiler_defaults") {
}
config("executable_ldconfig") {
- ldflags = [
- "-Wl,-rpath=\$ORIGIN/",
- "-Wl,-rpath-link=",
- ]
+ if (!is_mac) {
+ ldflags = [
+ "-Wl,-rpath=\$ORIGIN/",
+ "-Wl,-rpath-link=",
+ ]
+ }
}
diff --git a/gn/examples/simple_build/build/BUILDCONFIG.gn b/gn/examples/simple_build/build/BUILDCONFIG.gn
index e419fd96a8a..619fa979c8f 100644
--- a/gn/examples/simple_build/build/BUILDCONFIG.gn
+++ b/gn/examples/simple_build/build/BUILDCONFIG.gn
@@ -15,6 +15,9 @@ if (current_os == "") {
current_os = target_os
}
+is_linux = host_os == "linux" && current_os == "linux" && target_os == "linux"
+is_mac = host_os == "mac" && current_os == "mac" && target_os == "mac"
+
# All binary targets will get this list of configs by default.
_shared_binary_target_configs = [ "//build:compiler_defaults" ]
diff --git a/gn/examples/simple_build/build/toolchain/BUILD.gn b/gn/examples/simple_build/build/toolchain/BUILD.gn
index d9457d7eae4..c04ee1ead50 100644
--- a/gn/examples/simple_build/build/toolchain/BUILD.gn
+++ b/gn/examples/simple_build/build/toolchain/BUILD.gn
@@ -8,9 +8,8 @@ toolchain("gcc") {
command = "gcc -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",
- ]
+ outputs =
+ [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
}
tool("cxx") {
@@ -18,19 +17,16 @@ toolchain("gcc") {
command = "g++ -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",
- ]
+ outputs =
+ [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
}
tool("alink") {
- rspfile = "{{output}}.rsp"
- command = "rm -f {{output}} && ar rcs {{output}} @$rspfile"
+ command = "rm -f {{output}} && ar rcs {{output}} {{inputs}}"
description = "AR {{target_output_name}}{{output_extension}}"
- rspfile_content = "{{inputs}}"
- outputs = [
- "{{target_out_dir}}/{{target_output_name}}{{output_extension}}",
- ]
+
+ outputs =
+ [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
default_output_extension = ".a"
output_prefix = "lib"
}
@@ -39,9 +35,15 @@ toolchain("gcc") {
soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so".
sofile = "{{output_dir}}/$soname"
rspfile = soname + ".rsp"
+ if (is_mac) {
+ os_specific_option = "-install_name @executable_path/$sofile"
+ rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
+ } else {
+ os_specific_option = "-Wl,-soname=$soname"
+ rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
+ }
- command = "g++ -shared {{ldflags}} -o $sofile -Wl,-soname=$soname @$rspfile"
- rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
+ command = "g++ -shared {{ldflags}} -o $sofile $os_specific_option @$rspfile"
description = "SOLINK $soname"
@@ -54,9 +56,7 @@ toolchain("gcc") {
# it (in which case {{output_dir}} will be what the target specifies).
default_output_dir = "{{root_out_dir}}"
- outputs = [
- sofile,
- ]
+ outputs = [ sofile ]
link_output = sofile
depend_output = sofile
output_prefix = "lib"
@@ -65,13 +65,15 @@ toolchain("gcc") {
tool("link") {
outfile = "{{target_output_name}}{{output_extension}}"
rspfile = "$outfile.rsp"
- command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
+ if (is_mac) {
+ command = "g++ {{ldflags}} -o $outfile @$rspfile {{solibs}} {{libs}}"
+ } else {
+ command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
+ }
description = "LINK $outfile"
default_output_dir = "{{root_out_dir}}"
rspfile_content = "{{inputs}}"
- outputs = [
- outfile,
- ]
+ outputs = [ outfile ]
}
tool("stamp") {
diff --git a/gn/examples/simple_build/tutorial/README.md b/gn/examples/simple_build/tutorial/README.md
new file mode 100644
index 00000000000..3dd11ef45ca
--- /dev/null
+++ b/gn/examples/simple_build/tutorial/README.md
@@ -0,0 +1,4 @@
+# Tutorial directory
+
+This directory isn't used by the simple build example by default. It's here to
+be used for the example in the [quick start guide](../../../docs/quick_start.md).
diff --git a/gn/examples/simple_build/tutorial/tutorial.cc b/gn/examples/simple_build/tutorial/tutorial.cc
new file mode 100644
index 00000000000..b72ffa83def
--- /dev/null
+++ b/gn/examples/simple_build/tutorial/tutorial.cc
@@ -0,0 +1,10 @@
+// Copyright 2020 The Chromium 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 <stdio.h>
+
+int main(int argc, char* argv[]) {
+ printf("Hello from the tutorial.\n");
+ return 0;
+}
diff --git a/gn/infra/README.recipes.md b/gn/infra/README.recipes.md
index 128ecb71b19..02e40986a5e 100644
--- a/gn/infra/README.recipes.md
+++ b/gn/infra/README.recipes.md
@@ -4,11 +4,13 @@
**[Recipe Modules](#Recipe-Modules)**
* [macos_sdk](#recipe_modules-macos_sdk) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
+ * [target](#recipe_modules-target)
* [windows_sdk](#recipe_modules-windows_sdk)
**[Recipes](#Recipes)**
* [gn](#recipes-gn) &mdash; Recipe for building GN.
* [macos_sdk:examples/full](#recipes-macos_sdk_examples_full)
+ * [target:examples/full](#recipes-target_examples_full)
* [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
## Recipe Modules
@@ -25,7 +27,7 @@ Available only to Google-run bots.
API for using OS X SDK distributed via CIPD.
-&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/macos_sdk/api.py#24)(self):**
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/macos_sdk/api.py#30)(self):**
Sets up the XCode SDK environment.
@@ -58,6 +60,15 @@ Usage:
Raises:
StepFailure or InfraFailure.
+
+&emsp; **@property**<br>&mdash; **def [sdk\_dir](/infra/recipe_modules/macos_sdk/api.py#25)(self):**
+### *recipe_modules* / [target](/infra/recipe_modules/target)
+
+[DEPS](/infra/recipe_modules/target/__init__.py#5): [recipe\_engine/platform][recipe_engine/recipe_modules/platform]
+
+#### **class [TargetApi](/infra/recipe_modules/target/api.py#82)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+
+&emsp; **@property**<br>&mdash; **def [host](/infra/recipe_modules/target/api.py#87)(self):**
### *recipe_modules* / [windows\_sdk](/infra/recipe_modules/windows_sdk)
[DEPS](/infra/recipe_modules/windows_sdk/__init__.py#5): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
@@ -78,16 +89,21 @@ Raises:
### *recipes* / [gn](/infra/recipes/gn.py)
-[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [target](#recipe_modules-target), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
Recipe for building GN.
-&mdash; **def [RunSteps](/infra/recipes/gn.py#29)(api, repository):**
+&mdash; **def [RunSteps](/infra/recipes/gn.py#102)(api, repository):**
### *recipes* / [macos\_sdk:examples/full](/infra/recipe_modules/macos_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/macos_sdk/examples/full.py#5): [macos\_sdk](#recipe_modules-macos_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
&mdash; **def [RunSteps](/infra/recipe_modules/macos_sdk/examples/full.py#13)(api):**
+### *recipes* / [target:examples/full](/infra/recipe_modules/target/examples/full.py)
+
+[DEPS](/infra/recipe_modules/target/examples/full.py#5): [target](#recipe_modules-target), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+&mdash; **def [RunSteps](/infra/recipe_modules/target/examples/full.py#13)(api):**
### *recipes* / [windows\_sdk:examples/full](/infra/recipe_modules/windows_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/windows_sdk/examples/full.py#5): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
diff --git a/gn/infra/config/recipes.cfg b/gn/infra/config/recipes.cfg
index 6b7363bd1c4..77f7e994752 100644
--- a/gn/infra/config/recipes.cfg
+++ b/gn/infra/config/recipes.cfg
@@ -3,7 +3,7 @@
"deps": {
"recipe_engine": {
"branch": "master",
- "revision": "0589a429cf3c164004dae4ced4c75784a50afd81",
+ "revision": "d31ba13ede8c21e60116ae61e4490d53ba77fcbd",
"url": "https://chromium.googlesource.com/infra/luci/recipes-py"
}
},
diff --git a/gn/infra/recipe_modules/macos_sdk/__init__.py b/gn/infra/recipe_modules/macos_sdk/__init__.py
index f749bfd5af6..56b6a9450ed 100644
--- a/gn/infra/recipe_modules/macos_sdk/__init__.py
+++ b/gn/infra/recipe_modules/macos_sdk/__init__.py
@@ -34,11 +34,11 @@ PROPERTIES = {
),
default={
'sdk_version':
- '10b61',
+ '12B5025f',
'tool_package':
'infra/tools/mac_toolchain/${platform}',
'tool_version':
- 'git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e',
+ 'git_revision:e9b1fe29fe21a1cd36428c43ea2aba244bd31280',
},
)
}
diff --git a/gn/infra/recipe_modules/macos_sdk/api.py b/gn/infra/recipe_modules/macos_sdk/api.py
index 23d0066aca3..cde4a8bff2d 100644
--- a/gn/infra/recipe_modules/macos_sdk/api.py
+++ b/gn/infra/recipe_modules/macos_sdk/api.py
@@ -17,10 +17,16 @@ class MacOSSDKApi(recipe_api.RecipeApi):
def __init__(self, sdk_properties, *args, **kwargs):
super(MacOSSDKApi, self).__init__(*args, **kwargs)
+ self._sdk_dir = None
self._sdk_version = sdk_properties['sdk_version'].lower()
self._tool_package = sdk_properties['tool_package']
self._tool_version = sdk_properties['tool_version']
+ @property
+ def sdk_dir(self):
+ assert self._sdk_dir
+ return self._sdk_dir
+
@contextmanager
def __call__(self):
"""Sets up the XCode SDK environment.
@@ -61,9 +67,9 @@ class MacOSSDKApi(recipe_api.RecipeApi):
try:
with self.m.context(infra_steps=True):
- sdk_dir = self._ensure_sdk()
+ self._sdk_dir = self._ensure_sdk()
self.m.step('select XCode',
- ['sudo', 'xcode-select', '--switch', sdk_dir])
+ ['sudo', 'xcode-select', '--switch', self._sdk_dir])
yield
finally:
with self.m.context(infra_steps=True):
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
index e2acecdfa75..1fb8aba6432 100644
--- a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
@@ -6,7 +6,7 @@
"-root",
"[CACHE]/macos_sdk",
"-ensure-file",
- "infra/tools/mac_toolchain/${platform} git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e",
+ "infra/tools/mac_toolchain/${platform} git_revision:e9b1fe29fe21a1cd36428c43ea2aba244bd31280",
"-json-output",
"/path/to/tmp/json"
],
@@ -17,7 +17,7 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:434\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:e9b\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
@@ -33,7 +33,7 @@
"-kind",
"mac",
"-xcode-version",
- "10b61",
+ "12b5025f",
"-output-dir",
"[CACHE]/macos_sdk/XCode.app"
],
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.py b/gn/infra/recipe_modules/macos_sdk/examples/full.py
index 5546ad5058e..a3348ab9402 100644
--- a/gn/infra/recipe_modules/macos_sdk/examples/full.py
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.py
@@ -12,6 +12,7 @@ DEPS = [
def RunSteps(api):
with api.macos_sdk():
+ sdk_dir = api.macos_sdk.sdk_dir if api.platform.is_mac else None
api.step('gn', ['gn', 'gen', 'out/Release'])
api.step('ninja', ['ninja', '-C', 'out/Release'])
diff --git a/gn/infra/recipe_modules/target/__init__.py b/gn/infra/recipe_modules/target/__init__.py
new file mode 100644
index 00000000000..1239fd28642
--- /dev/null
+++ b/gn/infra/recipe_modules/target/__init__.py
@@ -0,0 +1,7 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+ 'recipe_engine/platform',
+]
diff --git a/gn/infra/recipe_modules/target/api.py b/gn/infra/recipe_modules/target/api.py
new file mode 100644
index 00000000000..850c104e9ba
--- /dev/null
+++ b/gn/infra/recipe_modules/target/api.py
@@ -0,0 +1,98 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from recipe_engine import recipe_api
+
+
+PLATFORM_TO_TRIPLE = {
+ 'fuchsia-amd64': 'x86_64-fuchsia',
+ 'fuchsia-arm64': 'aarch64-fuchsia',
+ 'linux-amd64': 'x86_64-linux-gnu',
+ 'linux-arm64': 'aarch64-linux-gnu',
+ 'mac-amd64': 'x86_64-apple-darwin',
+ 'mac-arm64': 'arm64-apple-darwin',
+}
+PLATFORMS = PLATFORM_TO_TRIPLE.keys()
+
+
+class Target(object):
+
+ def __init__(self, api, os, arch):
+ self.m = api
+ self._os = os
+ self._arch = arch
+
+ @property
+ def is_win(self):
+ """Returns True iff the target platform is Windows."""
+ return self.os == 'windows'
+
+ @property
+ def is_mac(self):
+ """Returns True iff the target platform is macOS."""
+ return self.os == 'mac'
+
+ @property
+ def is_linux(self):
+ """Returns True iff the target platform is Linux."""
+ return self.os == 'linux'
+
+ @property
+ def is_host(self):
+ """Returns True iff the target platform is host."""
+ return self == self.m.host
+
+ @property
+ def os(self):
+ """Returns the target os name which will be in:
+ * windows
+ * mac
+ * linux
+ """
+ return self._os
+
+ @property
+ def arch(self):
+ """Returns the current CPU architecture."""
+ return self._arch
+
+ @property
+ def platform(self):
+ """Returns the target platform in the <os>-<arch> format."""
+ return '%s-%s' % (self.os, self.arch)
+
+ @property
+ def triple(self):
+ """Returns the target triple."""
+ return PLATFORM_TO_TRIPLE[self.platform]
+
+ def __str__(self):
+ return self.platform
+
+ def __eq__(self, other):
+ if isinstance(other, Target):
+ return self._os == other._os and self._arch == other._arch
+ return False
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+class TargetApi(recipe_api.RecipeApi):
+
+ def __call__(self, platform):
+ return Target(self, *platform.split('-', 2))
+
+ @property
+ def host(self):
+ return Target(self, self.m.platform.name.replace('win', 'windows'), {
+ 'intel': {
+ 32: '386',
+ 64: 'amd64',
+ },
+ 'arm': {
+ 32: 'armv6',
+ 64: 'arm64',
+ },
+ }[self.m.platform.arch][self.m.platform.bits])
diff --git a/gn/infra/recipe_modules/target/examples/full.expected/linux.json b/gn/infra/recipe_modules/target/examples/full.expected/linux.json
new file mode 100644
index 00000000000..947f5d2aee9
--- /dev/null
+++ b/gn/infra/recipe_modules/target/examples/full.expected/linux.json
@@ -0,0 +1,22 @@
+[
+ {
+ "cmd": [],
+ "name": "platform things",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@name@fuchsia@@@",
+ "@@@STEP_LOG_END@name@@@",
+ "@@@STEP_LOG_LINE@arch@arm64@@@",
+ "@@@STEP_LOG_END@arch@@@",
+ "@@@STEP_LOG_LINE@platform@fuchsia-arm64@@@",
+ "@@@STEP_LOG_END@platform@@@",
+ "@@@STEP_LOG_LINE@triple@aarch64-fuchsia@@@",
+ "@@@STEP_LOG_END@triple@@@",
+ "@@@STEP_LOG_LINE@string@fuchsia-arm64@@@",
+ "@@@STEP_LOG_END@string@@@"
+ ]
+ },
+ {
+ "jsonResult": null,
+ "name": "$result"
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/target/examples/full.expected/mac.json b/gn/infra/recipe_modules/target/examples/full.expected/mac.json
new file mode 100644
index 00000000000..947f5d2aee9
--- /dev/null
+++ b/gn/infra/recipe_modules/target/examples/full.expected/mac.json
@@ -0,0 +1,22 @@
+[
+ {
+ "cmd": [],
+ "name": "platform things",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@name@fuchsia@@@",
+ "@@@STEP_LOG_END@name@@@",
+ "@@@STEP_LOG_LINE@arch@arm64@@@",
+ "@@@STEP_LOG_END@arch@@@",
+ "@@@STEP_LOG_LINE@platform@fuchsia-arm64@@@",
+ "@@@STEP_LOG_END@platform@@@",
+ "@@@STEP_LOG_LINE@triple@aarch64-fuchsia@@@",
+ "@@@STEP_LOG_END@triple@@@",
+ "@@@STEP_LOG_LINE@string@fuchsia-arm64@@@",
+ "@@@STEP_LOG_END@string@@@"
+ ]
+ },
+ {
+ "jsonResult": null,
+ "name": "$result"
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/target/examples/full.expected/win.json b/gn/infra/recipe_modules/target/examples/full.expected/win.json
new file mode 100644
index 00000000000..947f5d2aee9
--- /dev/null
+++ b/gn/infra/recipe_modules/target/examples/full.expected/win.json
@@ -0,0 +1,22 @@
+[
+ {
+ "cmd": [],
+ "name": "platform things",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@name@fuchsia@@@",
+ "@@@STEP_LOG_END@name@@@",
+ "@@@STEP_LOG_LINE@arch@arm64@@@",
+ "@@@STEP_LOG_END@arch@@@",
+ "@@@STEP_LOG_LINE@platform@fuchsia-arm64@@@",
+ "@@@STEP_LOG_END@platform@@@",
+ "@@@STEP_LOG_LINE@triple@aarch64-fuchsia@@@",
+ "@@@STEP_LOG_END@triple@@@",
+ "@@@STEP_LOG_LINE@string@fuchsia-arm64@@@",
+ "@@@STEP_LOG_END@string@@@"
+ ]
+ },
+ {
+ "jsonResult": null,
+ "name": "$result"
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/target/examples/full.py b/gn/infra/recipe_modules/target/examples/full.py
new file mode 100644
index 00000000000..c47c86a3f6b
--- /dev/null
+++ b/gn/infra/recipe_modules/target/examples/full.py
@@ -0,0 +1,31 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+ 'target',
+ 'recipe_engine/platform',
+ 'recipe_engine/properties',
+ 'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+ target = api.target('fuchsia-arm64')
+ assert not target.is_win
+ assert not target.is_linux
+ assert not target.is_mac
+ assert api.target.host.is_host
+ assert target != api.target.host
+ assert target != 'foo'
+ step_result = api.step('platform things', cmd=None)
+ step_result.presentation.logs['name'] = [target.os]
+ step_result.presentation.logs['arch'] = [target.arch]
+ step_result.presentation.logs['platform'] = [target.platform]
+ step_result.presentation.logs['triple'] = [target.triple]
+ step_result.presentation.logs['string'] = [str(target)]
+
+
+def GenTests(api):
+ for platform in ('linux', 'mac', 'win'):
+ yield api.test(platform) + api.platform.name(platform)
diff --git a/gn/infra/recipe_modules/windows_sdk/__init__.py b/gn/infra/recipe_modules/windows_sdk/__init__.py
index 3c790ded06b..83323a8e13e 100644
--- a/gn/infra/recipe_modules/windows_sdk/__init__.py
+++ b/gn/infra/recipe_modules/windows_sdk/__init__.py
@@ -25,7 +25,7 @@ PROPERTIES = {
sdk_version=Single(str)),
default={
'sdk_package': 'chrome_internal/third_party/sdk/windows',
- 'sdk_version': 'uploaded:2018-06-13'
+ 'sdk_version': 'uploaded:2019-09-06'
},
)
}
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
index def8e7a2b6d..f8b2b7172d8 100644
--- a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -6,7 +6,7 @@
"-root",
"[CACHE]\\windows_sdk",
"-ensure-file",
- "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "chrome_internal/third_party/sdk/windows uploaded:2019-09-06",
"-json-output",
"/path/to/tmp/json"
],
@@ -17,7 +17,7 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-uploaded:2019-09\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
diff --git a/gn/infra/recipes/gn.expected/ci_linux.json b/gn/infra/recipes/gn.expected/ci_linux.json
index 620f4559f75..61b1fa7f27d 100644
--- a/gn/infra/recipes/gn.expected/ci_linux.json
+++ b/gn/infra/recipes/gn.expected/ci_linux.json
@@ -45,12 +45,26 @@
},
{
"cmd": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
"cipd",
"ensure",
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/third_party/clang/${platform} integration\n@Subdir sysroot\nfuchsia/third_party/sysroot/linux git_revision:c912d089c3d46d8982fdef76a50514cca79b6132",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,8 +75,8 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-integration-----\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
@@ -71,8 +85,8 @@
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:c91\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/sysroot/linux\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -82,11 +96,152 @@
},
{
"cmd": [],
+ "name": "rpmalloc"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]/rpmalloc"
+ ],
+ "name": "rpmalloc.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
+ "6bb6ca97a8d6a72d626153fd8431ef8477a21145"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.checkout",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-amd64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "x86-64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/x86-64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "arm64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
"name": "debug"
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -102,11 +257,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.generate",
+ "name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -121,11 +276,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.ninja",
+ "name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -134,9 +289,17 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "debug.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "debug.linux-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -145,7 +308,7 @@
},
{
"cmd": [],
- "name": "release.build",
+ "name": "release.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -156,17 +319,18 @@
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
- "--use-icf"
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/x86-64/librpmallocwrap.a"
],
"cwd": "[START_DIR]/gn",
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.generate",
+ "name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -181,11 +345,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.ninja",
+ "name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -194,17 +358,120 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "release.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "pkg-build",
+ "-pkg-def",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-amd64\", \"root\": \"[START_DIR]/gn/out\"}",
+ "-out",
+ "[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.upload.build gn/gn/linux-amd64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-amd64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
"cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "-C",
+ "[START_DIR]/gn/out"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
"cipd",
"pkg-build",
"-pkg-def",
- "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-arm64\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
"-hash-algo",
@@ -212,12 +479,21 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/${platform}",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.upload.build gn/gn/linux-arm64",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-arm64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
diff --git a/gn/infra/recipes/gn.expected/ci_mac.json b/gn/infra/recipes/gn.expected/ci_mac.json
index 6ff9fe92e39..5816d9c76b4 100644
--- a/gn/infra/recipes/gn.expected/ci_mac.json
+++ b/gn/infra/recipes/gn.expected/ci_mac.json
@@ -45,12 +45,26 @@
},
{
"cmd": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
"cipd",
"ensure",
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/third_party/clang/${platform} integration",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,8 +75,8 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-integration-----\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
@@ -81,7 +95,7 @@
"-root",
"[CACHE]/macos_sdk",
"-ensure-file",
- "infra/tools/mac_toolchain/${platform} git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e",
+ "infra/tools/mac_toolchain/${platform} git_revision:e9b1fe29fe21a1cd36428c43ea2aba244bd31280",
"-json-output",
"/path/to/tmp/json"
],
@@ -92,7 +106,7 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:434\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:e9b\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
@@ -108,7 +122,7 @@
"-kind",
"mac",
"-xcode-version",
- "10b61",
+ "12b5025f",
"-output-dir",
"[CACHE]/macos_sdk/XCode.app"
],
@@ -126,24 +140,44 @@
"name": "select XCode"
},
{
+ "cmd": [],
+ "name": "debug"
+ },
+ {
"cmd": [
"xcrun",
"--show-sdk-path"
],
- "name": "xcrun",
+ "name": "debug.xcrun sdk-path",
"stdout": "/path/to/tmp/",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
"@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
"@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
]
},
{
- "cmd": [],
- "name": "debug"
+ "cmd": [
+ "xcrun",
+ "--toolchain",
+ "clang",
+ "clang++",
+ "-xc++",
+ "-fsyntax-only",
+ "-Wp,-v",
+ "-"
+ ],
+ "name": "debug.xcrun toolchain",
+ "stderr": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ ]
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.mac-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -159,11 +193,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "debug.build.generate",
+ "name": "debug.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -178,11 +212,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "debug.build.ninja",
+ "name": "debug.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -191,9 +225,17 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "debug.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "debug.mac-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -201,8 +243,40 @@
"name": "release"
},
{
+ "cmd": [
+ "xcrun",
+ "--show-sdk-path"
+ ],
+ "name": "release.xcrun sdk-path",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--toolchain",
+ "clang",
+ "clang++",
+ "-xc++",
+ "-fsyntax-only",
+ "-Wp,-v",
+ "-"
+ ],
+ "name": "release.xcrun toolchain",
+ "stderr": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ ]
+ },
+ {
"cmd": [],
- "name": "release.build",
+ "name": "release.mac-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -219,11 +293,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "release.build.generate",
+ "name": "release.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -238,11 +312,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "release.build.ninja",
+ "name": "release.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -251,26 +325,151 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "release.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-amd64.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.mac-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "pkg-build",
+ "-pkg-def",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/mac-amd64\", \"root\": \"[START_DIR]/gn/out\"}",
+ "-out",
+ "[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-amd64.upload.build gn/gn/mac-amd64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/mac-amd64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--show-sdk-path"
+ ],
+ "name": "release.xcrun sdk-path (2)",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--toolchain",
+ "clang",
+ "clang++",
+ "-xc++",
+ "-fsyntax-only",
+ "-Wp,-v",
+ "-"
+ ],
+ "name": "release.xcrun toolchain (2)",
+ "stderr": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.mac-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
"cmd": [
- "sudo",
- "xcode-select",
- "--reset"
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf"
],
- "infra_step": true,
- "name": "reset XCode"
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-arm64.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "-C",
+ "[START_DIR]/gn/out"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-arm64.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.mac-arm64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
},
{
"cmd": [
"cipd",
"pkg-build",
"-pkg-def",
- "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/mac-arm64\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
"-hash-algo",
@@ -278,18 +477,36 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/${platform}",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-arm64.upload.build gn/gn/mac-arm64",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/mac-arm64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
+ "cmd": [
+ "sudo",
+ "xcode-select",
+ "--reset"
+ ],
+ "infra_step": true,
+ "name": "reset XCode"
+ },
+ {
"jsonResult": null,
"name": "$result"
}
diff --git a/gn/infra/recipes/gn.expected/ci_win.json b/gn/infra/recipes/gn.expected/ci_win.json
index 6df7ef53496..842f0153af8 100644
--- a/gn/infra/recipes/gn.expected/ci_win.json
+++ b/gn/infra/recipes/gn.expected/ci_win.json
@@ -45,6 +45,20 @@
},
{
"cmd": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
"cipd.bat",
"ensure",
"-root",
@@ -77,7 +91,7 @@
"-root",
"[CACHE]\\windows_sdk",
"-ensure-file",
- "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "chrome_internal/third_party/sdk/windows uploaded:2019-09-06",
"-json-output",
"/path/to/tmp/json"
],
@@ -88,7 +102,7 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-uploaded:2019-09\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
@@ -135,7 +149,7 @@
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.windows-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -156,7 +170,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "debug.build.generate",
+ "name": "debug.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -176,7 +190,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "debug.build.ninja",
+ "name": "debug.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -185,6 +199,7 @@
"cmd": [
"[START_DIR]\\gn\\out\\gn_unittests"
],
+ "cwd": "[START_DIR]\\gn",
"env": {
"VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
@@ -193,9 +208,9 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "debug.test",
+ "name": "debug.windows-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -204,7 +219,7 @@
},
{
"cmd": [],
- "name": "release.build",
+ "name": "release.windows-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -226,7 +241,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "release.build.generate",
+ "name": "release.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -246,7 +261,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "release.build.ninja",
+ "name": "release.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -255,6 +270,7 @@
"cmd": [
"[START_DIR]\\gn\\out\\gn_unittests"
],
+ "cwd": "[START_DIR]\\gn",
"env": {
"VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
@@ -263,27 +279,24 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "release.test",
+ "name": "release.windows-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
- "cmd": [
- "taskkill.exe",
- "/f",
- "/t",
- "/im",
- "mspdbsrv.exe"
- ],
- "name": "taskkill mspdbsrv"
+ "cmd": [],
+ "name": "release.windows-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
},
{
"cmd": [
"cipd.bat",
"pkg-build",
"-pkg-def",
- "{\"data\": [{\"file\": \"gn.exe\"}, {\"version_file\": \".versions/gn.exe.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]\\\\gn\\\\out\"}",
+ "{\"data\": [{\"file\": \"gn.exe\"}, {\"version_file\": \".versions/gn.exe.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/windows-amd64\", \"root\": \"[START_DIR]\\\\gn\\\\out\"}",
"-out",
"[CLEANUP]\\gn.cipd",
"-hash-algo",
@@ -291,18 +304,38 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/${platform}",
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.windows-amd64.upload.build gn/gn/windows-amd64",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/windows-amd64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
+ "cmd": [
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
+ ],
+ "name": "taskkill mspdbsrv"
+ },
+ {
"jsonResult": null,
"name": "$result"
}
diff --git a/gn/infra/recipes/gn.expected/cipd_exists.json b/gn/infra/recipes/gn.expected/cipd_exists.json
index 337f548c6f6..e7d6a03bcf5 100644
--- a/gn/infra/recipes/gn.expected/cipd_exists.json
+++ b/gn/infra/recipes/gn.expected/cipd_exists.json
@@ -45,12 +45,26 @@
},
{
"cmd": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
"cipd",
"ensure",
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/third_party/clang/${platform} integration\n@Subdir sysroot\nfuchsia/third_party/sysroot/linux git_revision:c912d089c3d46d8982fdef76a50514cca79b6132",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,8 +75,8 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-integration-----\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
@@ -71,8 +85,8 @@
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:c91\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/sysroot/linux\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -82,11 +96,152 @@
},
{
"cmd": [],
+ "name": "rpmalloc"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]/rpmalloc"
+ ],
+ "name": "rpmalloc.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
+ "6bb6ca97a8d6a72d626153fd8431ef8477a21145"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.checkout",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-amd64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "x86-64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/x86-64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "arm64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
"name": "debug"
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -102,11 +257,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.generate",
+ "name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -121,11 +276,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.ninja",
+ "name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -134,9 +289,17 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "debug.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "debug.linux-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -145,7 +308,7 @@
},
{
"cmd": [],
- "name": "release.build",
+ "name": "release.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -156,17 +319,18 @@
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
- "--use-icf"
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/x86-64/librpmallocwrap.a"
],
"cwd": "[START_DIR]/gn",
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.generate",
+ "name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -181,11 +345,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.ninja",
+ "name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -194,9 +358,24 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "release.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -204,7 +383,7 @@
"cipd",
"pkg-build",
"-pkg-def",
- "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-amd64\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
"-hash-algo",
@@ -212,12 +391,21 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/${platform}",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.upload.build gn/gn/linux-amd64",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-amd64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
@@ -225,31 +413,157 @@
},
{
"cmd": [
- "git",
- "rev-parse",
- "HEAD"
+ "cipd",
+ "search",
+ "gn/gn/linux-amd64",
+ "-tag",
+ "git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:aaa\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-amd64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-amd64.upload.Package is up-to-date",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "-C",
+ "[START_DIR]/gn/out"
],
"cwd": "[START_DIR]/gn",
- "name": "rev-parse",
- "stdout": "/path/to/tmp/"
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "pkg-build",
+ "-pkg-def",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-arm64\", \"root\": \"[START_DIR]/gn/out\"}",
+ "-out",
+ "[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.upload.build gn/gn/linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-arm64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
},
{
"cmd": [
"cipd",
"search",
- "gn/gn/${platform}",
+ "gn/gn/linux-arm64",
"-tag",
"git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"-json-output",
"/path/to/tmp/json"
],
- "name": "cipd search gn/gn/${platform} git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.upload.cipd search gn/gn/linux-arm64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:aaa\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-amd64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-arm64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
@@ -258,7 +572,10 @@
},
{
"cmd": [],
- "name": "Package is up-to-date"
+ "name": "release.linux-arm64.upload.Package is up-to-date",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@"
+ ]
},
{
"jsonResult": null,
diff --git a/gn/infra/recipes/gn.expected/cipd_register.json b/gn/infra/recipes/gn.expected/cipd_register.json
index ce6a2213c1f..6eebe17046e 100644
--- a/gn/infra/recipes/gn.expected/cipd_register.json
+++ b/gn/infra/recipes/gn.expected/cipd_register.json
@@ -45,12 +45,26 @@
},
{
"cmd": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
"cipd",
"ensure",
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/third_party/clang/${platform} integration\n@Subdir sysroot\nfuchsia/third_party/sysroot/linux git_revision:c912d089c3d46d8982fdef76a50514cca79b6132",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,8 +75,8 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-integration-----\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
@@ -71,8 +85,8 @@
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:c91\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/sysroot/linux\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -82,11 +96,152 @@
},
{
"cmd": [],
+ "name": "rpmalloc"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]/rpmalloc"
+ ],
+ "name": "rpmalloc.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
+ "6bb6ca97a8d6a72d626153fd8431ef8477a21145"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.checkout",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-amd64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "x86-64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/x86-64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "arm64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
"name": "debug"
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -102,11 +257,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.generate",
+ "name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -121,11 +276,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.ninja",
+ "name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -134,9 +289,17 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "debug.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "debug.linux-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -145,7 +308,7 @@
},
{
"cmd": [],
- "name": "release.build",
+ "name": "release.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -156,17 +319,18 @@
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
- "--use-icf"
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/x86-64/librpmallocwrap.a"
],
"cwd": "[START_DIR]/gn",
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.generate",
+ "name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -181,11 +345,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.ninja",
+ "name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -194,9 +358,24 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "release.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -204,7 +383,7 @@
"cipd",
"pkg-build",
"-pkg-def",
- "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-amd64\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
"-hash-algo",
@@ -212,12 +391,21 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/${platform}",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.upload.build gn/gn/linux-amd64",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-amd64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
@@ -225,26 +413,25 @@
},
{
"cmd": [
- "git",
- "rev-parse",
- "HEAD"
- ],
- "cwd": "[START_DIR]/gn",
- "name": "rev-parse",
- "stdout": "/path/to/tmp/"
- },
- {
- "cmd": [
"cipd",
"search",
- "gn/gn/${platform}",
+ "gn/gn/linux-amd64",
"-tag",
"git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"-json-output",
"/path/to/tmp/json"
],
- "name": "cipd search gn/gn/${platform} git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": []@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
@@ -265,18 +452,154 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "register gn/gn/${platform}",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.upload.register gn/gn/linux-amd64",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-amd64\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
+ "cmd": [],
+ "name": "release.linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "-C",
+ "[START_DIR]/gn/out"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "pkg-build",
+ "-pkg-def",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/linux-arm64\", \"root\": \"[START_DIR]/gn/out\"}",
+ "-out",
+ "[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.upload.build gn/gn/linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-arm64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "search",
+ "gn/gn/linux-arm64",
+ "-tag",
+ "git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.upload.cipd search gn/gn/linux-arm64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/linux-arm64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64.upload.Package is up-to-date",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@"
+ ]
+ },
+ {
"jsonResult": null,
"name": "$result"
}
diff --git a/gn/infra/recipes/gn.expected/cq_linux.json b/gn/infra/recipes/gn.expected/cq_linux.json
index 329ac6e904d..7ac4cfc1e68 100644
--- a/gn/infra/recipes/gn.expected/cq_linux.json
+++ b/gn/infra/recipes/gn.expected/cq_linux.json
@@ -46,6 +46,20 @@
{
"cmd": [
"git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
"fetch",
"https://gn.googlesource.com/gn",
"refs/changes/56/123456/7"
@@ -60,12 +74,12 @@
{
"cmd": [
"git",
- "cherry-pick",
+ "checkout",
"FETCH_HEAD"
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
- "name": "git.cherry-pick 123456/7",
+ "name": "git.checkout 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -77,7 +91,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/third_party/clang/${platform} integration\n@Subdir sysroot\nfuchsia/third_party/sysroot/linux git_revision:c912d089c3d46d8982fdef76a50514cca79b6132",
"-json-output",
"/path/to/tmp/json"
],
@@ -88,8 +102,8 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-integration-----\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
@@ -98,8 +112,8 @@
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:c91\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/sysroot/linux\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -109,11 +123,152 @@
},
{
"cmd": [],
+ "name": "rpmalloc"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]/rpmalloc"
+ ],
+ "name": "rpmalloc.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
+ "6bb6ca97a8d6a72d626153fd8431ef8477a21145"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "infra_step": true,
+ "name": "rpmalloc.checkout",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-amd64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "x86-64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/x86-64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "rpmalloc.build rpmalloc-linux-arm64",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/rpmalloc/configure.py",
+ "-c",
+ "release",
+ "-a",
+ "arm64",
+ "--lto"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.configure",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/rpmalloc",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
"name": "debug"
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -129,11 +284,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.generate",
+ "name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -148,11 +303,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "debug.build.ninja",
+ "name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -161,9 +316,17 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "debug.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "debug.linux-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -172,7 +335,7 @@
},
{
"cmd": [],
- "name": "release.build",
+ "name": "release.linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -183,17 +346,18 @@
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
- "--use-icf"
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/x86-64/librpmallocwrap.a"
],
"cwd": "[START_DIR]/gn",
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.generate",
+ "name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -208,11 +372,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
- "name": "release.build.ninja",
+ "name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -221,12 +385,68 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "release.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-amd64.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf",
+ "--link-lib=[START_DIR]/rpmalloc/lib/linux/release/arm64/librpmallocwrap.a"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "-C",
+ "[START_DIR]/gn/out"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "name": "release.linux-arm64.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
"jsonResult": null,
"name": "$result"
}
diff --git a/gn/infra/recipes/gn.expected/cq_mac.json b/gn/infra/recipes/gn.expected/cq_mac.json
index b84aab80eff..396543802ae 100644
--- a/gn/infra/recipes/gn.expected/cq_mac.json
+++ b/gn/infra/recipes/gn.expected/cq_mac.json
@@ -46,6 +46,20 @@
{
"cmd": [
"git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
"fetch",
"https://gn.googlesource.com/gn",
"refs/changes/56/123456/7"
@@ -60,12 +74,12 @@
{
"cmd": [
"git",
- "cherry-pick",
+ "checkout",
"FETCH_HEAD"
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
- "name": "git.cherry-pick 123456/7",
+ "name": "git.checkout 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -77,7 +91,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/third_party/clang/${platform} integration",
"-json-output",
"/path/to/tmp/json"
],
@@ -88,8 +102,8 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-integration-----\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/third_party/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
@@ -108,7 +122,7 @@
"-root",
"[CACHE]/macos_sdk",
"-ensure-file",
- "infra/tools/mac_toolchain/${platform} git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e",
+ "infra/tools/mac_toolchain/${platform} git_revision:e9b1fe29fe21a1cd36428c43ea2aba244bd31280",
"-json-output",
"/path/to/tmp/json"
],
@@ -119,7 +133,7 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:434\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:e9b\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
@@ -135,7 +149,7 @@
"-kind",
"mac",
"-xcode-version",
- "10b61",
+ "12b5025f",
"-output-dir",
"[CACHE]/macos_sdk/XCode.app"
],
@@ -153,24 +167,44 @@
"name": "select XCode"
},
{
+ "cmd": [],
+ "name": "debug"
+ },
+ {
"cmd": [
"xcrun",
"--show-sdk-path"
],
- "name": "xcrun",
+ "name": "debug.xcrun sdk-path",
"stdout": "/path/to/tmp/",
"~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
"@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
"@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
]
},
{
- "cmd": [],
- "name": "debug"
+ "cmd": [
+ "xcrun",
+ "--toolchain",
+ "clang",
+ "clang++",
+ "-xc++",
+ "-fsyntax-only",
+ "-Wp,-v",
+ "-"
+ ],
+ "name": "debug.xcrun toolchain",
+ "stderr": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ ]
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.mac-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -186,11 +220,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "debug.build.generate",
+ "name": "debug.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -205,11 +239,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "debug.build.ninja",
+ "name": "debug.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -218,9 +252,17 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "debug.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "debug.mac-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -228,8 +270,40 @@
"name": "release"
},
{
+ "cmd": [
+ "xcrun",
+ "--show-sdk-path"
+ ],
+ "name": "release.xcrun sdk-path",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--toolchain",
+ "clang",
+ "clang++",
+ "-xc++",
+ "-fsyntax-only",
+ "-Wp,-v",
+ "-"
+ ],
+ "name": "release.xcrun toolchain",
+ "stderr": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ ]
+ },
+ {
"cmd": [],
- "name": "release.build",
+ "name": "release.mac-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -246,11 +320,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "release.build.generate",
+ "name": "release.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -265,11 +339,11 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
- "CFLAGS": "--sysroot=/some/xcode/path",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
- "name": "release.build.ninja",
+ "name": "release.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -278,13 +352,100 @@
"cmd": [
"[START_DIR]/gn/out/gn_unittests"
],
- "name": "release.test",
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-amd64.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--show-sdk-path"
+ ],
+ "name": "release.xcrun sdk-path (2)",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--toolchain",
+ "clang",
+ "clang++",
+ "-xc++",
+ "-fsyntax-only",
+ "-Wp,-v",
+ "-"
+ ],
+ "name": "release.xcrun toolchain (2)",
+ "stderr": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.mac-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
"cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-arm64.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd/ninja",
+ "-C",
+ "[START_DIR]/gn/out"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "name": "release.mac-arm64.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
"sudo",
"xcode-select",
"--reset"
diff --git a/gn/infra/recipes/gn.expected/cq_win.json b/gn/infra/recipes/gn.expected/cq_win.json
index 979c3b9eb39..7e4424b4b56 100644
--- a/gn/infra/recipes/gn.expected/cq_win.json
+++ b/gn/infra/recipes/gn.expected/cq_win.json
@@ -46,6 +46,20 @@
{
"cmd": [
"git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.rev-parse",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
"fetch",
"https://gn.googlesource.com/gn",
"refs/changes/56/123456/7"
@@ -60,12 +74,12 @@
{
"cmd": [
"git",
- "cherry-pick",
+ "checkout",
"FETCH_HEAD"
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
- "name": "git.cherry-pick 123456/7",
+ "name": "git.checkout 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -104,7 +118,7 @@
"-root",
"[CACHE]\\windows_sdk",
"-ensure-file",
- "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "chrome_internal/third_party/sdk/windows uploaded:2019-09-06",
"-json-output",
"/path/to/tmp/json"
],
@@ -115,7 +129,7 @@
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
- "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-uploaded:2018-06\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-uploaded:2019-09\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
@@ -162,7 +176,7 @@
},
{
"cmd": [],
- "name": "debug.build",
+ "name": "debug.windows-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -183,7 +197,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "debug.build.generate",
+ "name": "debug.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -203,7 +217,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "debug.build.ninja",
+ "name": "debug.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -212,6 +226,7 @@
"cmd": [
"[START_DIR]\\gn\\out\\gn_unittests"
],
+ "cwd": "[START_DIR]\\gn",
"env": {
"VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
@@ -220,9 +235,9 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "debug.test",
+ "name": "debug.windows-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
@@ -231,7 +246,7 @@
},
{
"cmd": [],
- "name": "release.build",
+ "name": "release.windows-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -253,7 +268,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "release.build.generate",
+ "name": "release.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -273,7 +288,7 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "release.build.ninja",
+ "name": "release.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
@@ -282,6 +297,7 @@
"cmd": [
"[START_DIR]\\gn\\out\\gn_unittests"
],
+ "cwd": "[START_DIR]\\gn",
"env": {
"VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
@@ -290,9 +306,9 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
- "name": "release.test",
+ "name": "release.windows-amd64.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
+ "@@@STEP_NEST_LEVEL@2@@@"
]
},
{
diff --git a/gn/infra/recipes/gn.py b/gn/infra/recipes/gn.py
index f219b810d9d..0cb426965b9 100644
--- a/gn/infra/recipes/gn.py
+++ b/gn/infra/recipes/gn.py
@@ -17,6 +17,7 @@ DEPS = [
'recipe_engine/python',
'recipe_engine/raw_io',
'recipe_engine/step',
+ 'target',
'macos_sdk',
'windows_sdk',
]
@@ -25,10 +26,84 @@ PROPERTIES = {
'repository': Property(kind=str, default='https://gn.googlesource.com/gn'),
}
+# On select platforms, link the GN executable against rpmalloc for a small 10% speed boost.
+RPMALLOC_GIT_URL = 'https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc'
+RPMALLOC_REVISION = '6bb6ca97a8d6a72d626153fd8431ef8477a21145'
+
+# Used to convert os and arch strings to rpmalloc format
+RPMALLOC_MAP = {
+ 'amd64': 'x86-64',
+ 'mac': 'macos',
+}
+
+
+def _get_libcxx_include_path(api):
+ # Run the preprocessor with an empty input and print all include paths.
+ lines = api.step(
+ 'xcrun toolchain', [
+ 'xcrun', '--toolchain', 'clang', 'clang++', '-xc++', '-fsyntax-only',
+ '-Wp,-v', '-'
+ ],
+ stderr=api.raw_io.output(name='toolchain', add_output_log=True),
+ step_test_data=lambda: api.raw_io.test_api.stream_output(
+ str(api.macos_sdk.sdk_dir.join('include', 'c++', 'v1')),
+ stream='stderr')).stderr.splitlines()
+ # Iterate over all include paths and look for the SDK libc++ one.
+ sdk_dir = str(api.macos_sdk.sdk_dir)
+ for line in lines:
+ line = line.strip()
+ if line.startswith(sdk_dir) and 'include/c++/v1' in line:
+ return line
+ return None # pragma: no cover
+
+
+def _get_compilation_environment(api, target, cipd_dir):
+ if target.is_linux:
+ triple = '--target=%s' % target.triple
+ sysroot = '--sysroot=%s' % cipd_dir.join('sysroot')
+ env = {
+ 'CC': cipd_dir.join('bin', 'clang'),
+ 'CXX': cipd_dir.join('bin', 'clang++'),
+ 'AR': cipd_dir.join('bin', 'llvm-ar'),
+ 'CFLAGS': '%s %s' % (triple, sysroot),
+ 'LDFLAGS': '%s %s -static-libstdc++' % (triple, sysroot),
+ }
+ elif target.is_mac:
+ triple = '--target=%s' % target.triple
+ sysroot = '--sysroot=%s' % api.step(
+ 'xcrun sdk-path', ['xcrun', '--show-sdk-path'],
+ stdout=api.raw_io.output(name='sdk-path', add_output_log=True),
+ step_test_data=lambda: api.raw_io.test_api.stream_output(
+ '/some/xcode/path')).stdout.strip()
+ stdlib = cipd_dir.join('lib', 'libc++.a')
+ cxx_include = _get_libcxx_include_path(api)
+ env = {
+ 'CC':
+ cipd_dir.join('bin', 'clang'),
+ 'CXX':
+ cipd_dir.join('bin', 'clang++'),
+ 'AR':
+ cipd_dir.join('bin', 'llvm-ar'),
+ 'CFLAGS':
+ '%s %s -nostdinc++ -cxx-isystem %s' %
+ (triple, sysroot, cxx_include),
+ # TODO(phosek): Use the system libc++ temporarily until we
+ # have universal libc++.a for macOS that supports both x86_64
+ # and arm64.
+ 'LDFLAGS':
+ '%s %s' % (triple, sysroot),
+ }
+ else:
+ env = {}
+
+ return env
+
def RunSteps(api, repository):
src_dir = api.path['start_dir'].join('gn')
+ # TODO: Verify that building and linking rpmalloc works on OS X and Windows as
+ # well.
with api.step.nest('git'), api.context(infra_steps=True):
api.step('init', ['git', 'init', src_dir])
@@ -40,119 +115,176 @@ def RunSteps(api, repository):
# Fetch tags so `git describe` works.
api.step('fetch', ['git', 'fetch', '--tags', repository, ref])
api.step('checkout', ['git', 'checkout', 'FETCH_HEAD'])
+ revision = api.step(
+ 'rev-parse', ['git', 'rev-parse', 'HEAD'],
+ stdout=api.raw_io.output()).stdout.strip()
for change in build_input.gerrit_changes:
api.step('fetch %s/%s' % (change.change, change.patchset), [
'git', 'fetch', repository,
'refs/changes/%s/%s/%s' %
(str(change.change)[-2:], change.change, change.patchset)
])
- api.step('cherry-pick %s/%s' % (change.change, change.patchset),
- ['git', 'cherry-pick', 'FETCH_HEAD'])
+ api.step('checkout %s/%s' % (change.change, change.patchset),
+ ['git', 'checkout', 'FETCH_HEAD'])
with api.context(infra_steps=True):
cipd_dir = api.path['start_dir'].join('cipd')
pkgs = api.cipd.EnsureFile()
pkgs.add_package('infra/ninja/${platform}', 'version:1.8.2')
if api.platform.is_linux or api.platform.is_mac:
- pkgs.add_package('fuchsia/clang/${platform}',
- 'git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a')
+ pkgs.add_package('fuchsia/third_party/clang/${platform}', 'integration')
if api.platform.is_linux:
- pkgs.add_package('fuchsia/sysroot/${platform}',
- 'git_revision:a28dfa20af063e5ca00634024c85732e20220419',
+ pkgs.add_package('fuchsia/third_party/sysroot/linux',
+ 'git_revision:c912d089c3d46d8982fdef76a50514cca79b6132',
'sysroot')
api.cipd.ensure(cipd_dir, pkgs)
+ def release_targets():
+ if api.platform.is_linux:
+ return [api.target('linux-amd64'), api.target('linux-arm64')]
+ elif api.platform.is_mac:
+ return [api.target('mac-amd64'), api.target('mac-arm64')]
+ else:
+ return [api.target.host]
+
# The order is important since release build will get uploaded to CIPD.
configs = [
{
'name': 'debug',
- 'args': ['-d']
+ 'args': ['-d'],
+ 'targets': [api.target.host],
},
{
'name': 'release',
- 'args': ['--use-lto', '--use-icf']
+ 'args': ['--use-lto', '--use-icf'],
+ 'targets': release_targets(),
+ # TODO: Enable this for OS X and Windows.
+ 'use_rpmalloc': api.platform.is_linux
},
]
+ # True if any config uses rpmalloc.
+ use_rpmalloc = any(c.get('use_rpmalloc', False) for c in configs)
+
with api.macos_sdk(), api.windows_sdk():
- if api.platform.is_linux:
- sysroot = '--sysroot=%s' % cipd_dir.join('sysroot')
- env = {
- 'CC': cipd_dir.join('bin', 'clang'),
- 'CXX': cipd_dir.join('bin', 'clang++'),
- 'AR': cipd_dir.join('bin', 'llvm-ar'),
- 'CFLAGS': sysroot,
- 'LDFLAGS': sysroot,
- }
- elif api.platform.is_mac:
- sysroot = '--sysroot=%s' % api.step(
- 'xcrun', ['xcrun', '--show-sdk-path'],
- stdout=api.raw_io.output(name='sdk-path', add_output_log=True),
- step_test_data=
- lambda: api.raw_io.test_api.stream_output('/some/xcode/path')
- ).stdout.strip()
- stdlib = '-nostdlib++ %s' % cipd_dir.join('lib', 'libc++.a')
- env = {
- 'CC': cipd_dir.join('bin', 'clang'),
- 'CXX': cipd_dir.join('bin', 'clang++'),
- 'AR': cipd_dir.join('bin', 'llvm-ar'),
- 'CFLAGS': sysroot,
- 'LDFLAGS': '%s %s' % (sysroot, stdlib),
- }
- else:
- env = {}
+ # Build the rpmalloc static libraries if needed.
+ if use_rpmalloc:
+ # Maps a target.platform string to the location of the corresponding
+ # rpmalloc static library.
+ rpmalloc_static_libs = {}
+
+ # Get the list of all target platforms that are listed in `configs`
+ # above. Note that this is a list of Target instances, some of them
+ # may refer to the same platform string (e.g. linux-amd64).
+ #
+ # For each platform, a version of rpmalloc will be built if necessary,
+ # but doing this properly requires having a valid target instance to
+ # call _get_compilation_environment. So create a { platform -> Target }
+ # map to do that later.
+ all_config_platforms = {}
+ for c in configs:
+ if not c.get('use_rpmalloc', False):
+ continue
+ for t in c['targets']:
+ if t.platform not in all_config_platforms:
+ all_config_platforms[t.platform] = t
+
+ rpmalloc_src_dir = api.path['start_dir'].join('rpmalloc')
+ with api.step.nest('rpmalloc'):
+ api.step('init', ['git', 'init', rpmalloc_src_dir])
+ with api.context(cwd=rpmalloc_src_dir, infra_steps=True):
+ api.step(
+ 'fetch',
+ ['git', 'fetch', '--tags', RPMALLOC_GIT_URL, RPMALLOC_REVISION])
+ api.step('checkout', ['git', 'checkout', 'FETCH_HEAD'])
+
+ for platform in all_config_platforms:
+ # Convert target architecture and os to rpmalloc format.
+ rpmalloc_os, rpmalloc_arch = platform.split('-')
+ rpmalloc_os = RPMALLOC_MAP.get(rpmalloc_os, rpmalloc_os)
+ rpmalloc_arch = RPMALLOC_MAP.get(rpmalloc_arch, rpmalloc_arch)
+
+ env = _get_compilation_environment(api,
+ all_config_platforms[platform],
+ cipd_dir)
+ with api.step.nest('build rpmalloc-' + platform), api.context(
+ env=env, cwd=rpmalloc_src_dir):
+ api.python(
+ 'configure',
+ rpmalloc_src_dir.join('configure.py'),
+ args=['-c', 'release', '-a', rpmalloc_arch, '--lto'])
+
+ # NOTE: Only build the static library.
+ rpmalloc_static_lib = api.path.join('lib', rpmalloc_os, 'release',
+ rpmalloc_arch,
+ 'librpmallocwrap.a')
+ api.step('ninja', [cipd_dir.join('ninja'), rpmalloc_static_lib])
+
+ rpmalloc_static_libs[platform] = rpmalloc_src_dir.join(
+ rpmalloc_static_lib)
for config in configs:
with api.step.nest(config['name']):
- with api.step.nest('build'), api.context(env=env, cwd=src_dir):
- api.python(
- 'generate', src_dir.join('build', 'gen.py'), args=config['args'])
+ for target in config['targets']:
+ env = _get_compilation_environment(api, target, cipd_dir)
+ with api.step.nest(target.platform), api.context(
+ env=env, cwd=src_dir):
+ args = config['args']
+ if config.get('use_rpmalloc', False):
+ args = args[:] + [
+ '--link-lib=%s' % rpmalloc_static_libs[target.platform]
+ ]
- # Windows requires the environment modifications when building too.
- api.step('ninja', [cipd_dir.join('ninja'), '-C', src_dir.join('out')])
+ api.python('generate', src_dir.join('build', 'gen.py'), args=args)
- api.step('test', [src_dir.join('out', 'gn_unittests')])
+ # Windows requires the environment modifications when building too.
+ api.step('build',
+ [cipd_dir.join('ninja'), '-C',
+ src_dir.join('out')])
- if build_input.gerrit_changes:
- return
+ if target.is_host:
+ api.step('test', [src_dir.join('out', 'gn_unittests')])
- cipd_pkg_name = 'gn/gn/${platform}'
- gn = 'gn' + ('.exe' if api.platform.is_win else '')
+ if build_input.gerrit_changes:
+ continue
- pkg_def = api.cipd.PackageDefinition(
- package_name=cipd_pkg_name,
- package_root=src_dir.join('out'),
- install_mode='copy')
- pkg_def.add_file(src_dir.join('out', gn))
- pkg_def.add_version_file('.versions/%s.cipd_version' % gn)
+ if config['name'] != 'release':
+ continue
- cipd_pkg_file = api.path['cleanup'].join('gn.cipd')
+ with api.step.nest('upload'):
+ cipd_pkg_name = 'gn/gn/%s' % target.platform
+ gn = 'gn' + ('.exe' if target.is_win else '')
- api.cipd.build_from_pkg(
- pkg_def=pkg_def,
- output_package=cipd_pkg_file,
- )
+ pkg_def = api.cipd.PackageDefinition(
+ package_name=cipd_pkg_name,
+ package_root=src_dir.join('out'),
+ install_mode='copy')
+ pkg_def.add_file(src_dir.join('out', gn))
+ pkg_def.add_version_file('.versions/%s.cipd_version' % gn)
- if api.buildbucket.builder_id.project == 'infra-internal':
- with api.context(cwd=src_dir):
- revision = api.step(
- 'rev-parse', ['git', 'rev-parse', 'HEAD'],
- stdout=api.raw_io.output()).stdout.strip()
+ cipd_pkg_file = api.path['cleanup'].join('gn.cipd')
+
+ api.cipd.build_from_pkg(
+ pkg_def=pkg_def,
+ output_package=cipd_pkg_file,
+ )
- cipd_pin = api.cipd.search(cipd_pkg_name, 'git_revision:' + revision)
- if cipd_pin:
- api.step('Package is up-to-date', cmd=None)
- return
+ if api.buildbucket.builder_id.project == 'infra-internal':
+ cipd_pin = api.cipd.search(cipd_pkg_name,
+ 'git_revision:' + revision)
+ if cipd_pin:
+ api.step('Package is up-to-date', cmd=None)
+ continue
- api.cipd.register(
- package_name=cipd_pkg_name,
- package_path=cipd_pkg_file,
- refs=['latest'],
- tags={
- 'git_repository': repository,
- 'git_revision': revision,
- },
- )
+ api.cipd.register(
+ package_name=cipd_pkg_name,
+ package_path=cipd_pkg_file,
+ refs=['latest'],
+ tags={
+ 'git_repository': repository,
+ 'git_revision': revision,
+ },
+ )
def GenTests(api):
@@ -173,15 +305,20 @@ def GenTests(api):
project='infra-internal',
git_repo='gn.googlesource.com/gn',
revision='a' * 40,
- ) + api.step_data('rev-parse', api.raw_io.stream_output('a' * 40)) +
- api.step_data('cipd search gn/gn/${platform} git_revision:' + 'a' * 40,
- api.cipd.example_search('gn/gn/linux-amd64',
- ['git_revision:' + 'a' * 40])))
+ ) + api.step_data(
+ 'git.rev-parse', api.raw_io.stream_output('a' * 40)
+ ) + api.step_data(
+ 'release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:' +
+ 'a' * 40,
+ api.cipd.example_search('gn/gn/linux-amd64',
+ ['git_revision:' + 'a' * 40])))
yield (api.test('cipd_register') + api.buildbucket.ci_build(
project='infra-internal',
git_repo='gn.googlesource.com/gn',
revision='a' * 40,
- ) + api.step_data('rev-parse', api.raw_io.stream_output('a' * 40)) +
- api.step_data('cipd search gn/gn/${platform} git_revision:' + 'a' * 40,
- api.cipd.example_search('gn/gn/linux-amd64', [])))
+ ) + api.step_data(
+ 'git.rev-parse', api.raw_io.stream_output('a' * 40)
+ ) + api.step_data(
+ 'release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:' +
+ 'a' * 40, api.cipd.example_search('gn/gn/linux-amd64', [])))
diff --git a/gn/misc/emacs/gn-mode.el b/gn/misc/emacs/gn-mode.el
new file mode 100644
index 00000000000..dc92c9b61c7
--- /dev/null
+++ b/gn/misc/emacs/gn-mode.el
@@ -0,0 +1,192 @@
+;;; gn-mode.el - A major mode for editing gn files.
+
+;; 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.
+
+;; Author: Elliot Glaysher <erg@chromium.org>
+;; Created: April 03, 2015
+;; Keywords: tools, gn, ninja, chromium
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; A major mode for editing GN files. GN stands for Generate Ninja. GN is the
+;; meta build system used in Chromium. For more information on GN, see the GN
+;; manual: <https://gn.googlesource.com/gn/+/refs/heads/master/README.md>
+
+;;; To Do:
+
+;; - We syntax highlight builtin actions, but don't highlight instantiations of
+;; templates. Should we?
+
+
+
+(require 'smie)
+
+(defgroup gn nil
+ "Major mode for editing Generate Ninja files."
+ :prefix "gn-"
+ :group 'languages)
+
+(defcustom gn-indent-basic 2
+ "The number of spaces to indent a new scope."
+ :group 'gn
+ :type 'integer)
+
+(defcustom gn-format-command "gn format --stdin"
+ "The command to run to format gn files in place."
+ :group 'gn
+ :type 'string)
+
+(defgroup gn-faces nil
+ "Faces used in Generate Ninja mode."
+ :group 'gn
+ :group 'faces)
+
+(defface gn-embedded-variable
+ '((t :inherit font-lock-variable-name-face))
+ "Font lock face used to highlight variable names in strings."
+ :group 'gn-faces)
+
+(defface gn-embedded-variable-boundary
+ '((t :bold t
+ :inherit gn-embedded-variable))
+ "Font lock face used to highlight the '$' that starts a
+variable name or the '{{' and '}}' which surround it."
+ :group 'gn-faces)
+
+(defvar gn-font-lock-reserved-keywords
+ '("true" "false" "if" "else"))
+
+(defvar gn-font-lock-target-declaration-keywords
+ '("action" "action_foreach" "bundle_data" "copy" "create_bundle" "executable"
+ "group" "loadable_module" "shared_library" "source_set" "static_library"
+ "generated_file" "target" "rust_library" "rust_proc_macro"))
+
+;; pool() is handled specially since it's also a variable name
+(defvar gn-font-lock-buildfile-fun-keywords
+ '("assert" "config" "declare_args" "defined" "exec_script" "foreach"
+ "forward_variables_from" "get_label_info" "get_path_info"
+ "get_target_outputs" "getenv" "import" "not_needed" "print"
+ "process_file_template" "read_file" "rebase_path" "set_default_toolchain"
+ "set_defaults" "split_list" "string_join" "string_split" "template" "tool"
+ "toolchain" "propagates_configs" "write_file"))
+
+(defvar gn-font-lock-predefined-var-keywords
+ '("current_cpu" "current_os" "current_toolchain" "default_toolchain"
+ "host_cpu" "host_os" "invoker" "python_path" "root_build_dir" "root_gen_dir"
+ "root_out_dir" "target_cpu" "target_gen_dir" "target_name" "target_os"
+ "target_out_dir"))
+
+(defvar gn-font-lock-var-keywords
+ '("all_dependent_configs" "allow_circular_includes_from" "arflags" "args"
+ "asmflags" "assert_no_deps" "bundle_deps_filter" "bundle_executable_dir"
+ "bundle_resources_dir" "bundle_root_dir" "cflags" "cflags_c" "cflags_cc"
+ "cflags_objc" "cflags_objcc" "check_includes" "code_signing_args"
+ "code_signing_outputs" "code_signing_script" "code_signing_sources"
+ "complete_static_lib" "configs" "data" "data_deps" "defines" "depfile"
+ "deps" "framework_dir" "frameworks" "include_dirs" "inputs" "ldflags"
+ "lib_dirs" "libs" "output_dir" "output_extension" "output_name"
+ "output_prefix_override" "outputs" "pool" "precompiled_header"
+ "precompiled_header_type" "precompiled_source" "product_type" "public"
+ "public_configs" "public_deps" "response_file_contents" "script" "sources"
+ "testonly" "visibility" "write_runtime_deps" "bundle_contents_dir"
+ "contents" "output_conversion" "rebase" "data_keys" "walk_keys"))
+
+(defconst gn-font-lock-keywords
+ `((,(regexp-opt gn-font-lock-reserved-keywords 'words) .
+ font-lock-keyword-face)
+ (,(regexp-opt gn-font-lock-target-declaration-keywords 'words) .
+ font-lock-type-face)
+ (,(regexp-opt gn-font-lock-buildfile-fun-keywords 'words) .
+ font-lock-function-name-face)
+ ;; pool() as a function
+ ("\\<\\(pool\\)\\s-*("
+ (1 font-lock-function-name-face))
+ (,(regexp-opt gn-font-lock-predefined-var-keywords 'words) .
+ font-lock-constant-face)
+ (,(regexp-opt gn-font-lock-var-keywords 'words) .
+ font-lock-variable-name-face)
+ ;; $variables_like_this
+ ("\\(\\$\\)\\([a-zA-Z0-9_]+\\)"
+ (1 'gn-embedded-variable-boundary t)
+ (2 'gn-embedded-variable t))
+ ;; ${variables_like_this}
+ ("\\(\\${\\)\\([^\n }]+\\)\\(}\\)"
+ (1 'gn-embedded-variable-boundary t)
+ (2 'gn-embedded-variable t)
+ (3 'gn-embedded-variable-boundary t))
+ ;; {{placeholders}} (see substitute_type.h)
+ ("\\({{\\)\\([^\n }]+\\)\\(}}\\)"
+ (1 'gn-embedded-variable-boundary t)
+ (2 'gn-embedded-variable t)
+ (3 'gn-embedded-variable-boundary t))))
+
+(defun gn-smie-rules (kind token)
+ "These are slightly modified indentation rules from the SMIE
+ Indentation Example info page. This changes the :before rule
+ and adds a :list-intro to handle our x = [ ] syntax."
+ (pcase (cons kind token)
+ (`(:elem . basic) gn-indent-basic)
+ (`(,_ . ",") (smie-rule-separator kind))
+ (`(:list-intro . "") gn-indent-basic)
+ (`(:before . ,(or `"[" `"(" `"{"))
+ (if (smie-rule-hanging-p) (smie-rule-parent)))
+ (`(:before . "if")
+ (and (not (smie-rule-bolp)) (smie-rule-prev-p "else")
+ (smie-rule-parent)))))
+
+(defun gn-fill-paragraph (&optional justify)
+ "We only fill inside of comments in GN mode."
+ (interactive "P")
+ (or (fill-comment-paragraph justify)
+ ;; Never return nil; `fill-paragraph' will perform its default behavior
+ ;; if we do.
+ t))
+
+(defun gn-run-format ()
+ "Run 'gn format' on the buffer in place."
+ (interactive)
+ ;; We can't `save-excursion' here; that will put us at the beginning of the
+ ;; shell output, aka the beginning of the document.
+ (let ((my-start-line (line-number-at-pos)))
+ (shell-command-on-region (point-min) (point-max) gn-format-command nil t)
+ (goto-line my-start-line)))
+
+(defvar gn-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\C-c\C-f" 'gn-run-format)
+ map))
+
+;;;###autoload
+(define-derived-mode gn-mode prog-mode "GN"
+ "Major mode for editing gn (Generate Ninja)."
+ :group 'gn
+
+ (setq-local comment-use-syntax t)
+ (setq-local comment-start "#")
+ (setq-local comment-end "")
+ (setq-local indent-tabs-mode nil)
+
+ (setq-local fill-paragraph-function 'gn-fill-paragraph)
+
+ (setq-local font-lock-defaults '(gn-font-lock-keywords))
+
+ ;; For every 'rule("name") {', adds "name" to the imenu for quick navigation.
+ (setq-local imenu-generic-expression
+ '((nil "^\s*[a-zA-Z0-9_]+(\"\\([a-zA-Z0-9_]+\\)\")\s*{" 1)))
+
+ (smie-setup nil #'gn-smie-rules)
+ (setq-local smie-indent-basic gn-indent-basic)
+
+ ;; python style comment: “# …”
+ (modify-syntax-entry ?# "< b" gn-mode-syntax-table)
+ (modify-syntax-entry ?\n "> b" gn-mode-syntax-table)
+ (modify-syntax-entry ?_ "w" gn-mode-syntax-table))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.gni?\\'" . gn-mode))
+
+(provide 'gn-mode)
diff --git a/gn/tools/gn/misc/help_as_html.py b/gn/misc/help_as_html.py
index f8f1c1bc271..f8f1c1bc271 100755
--- a/gn/tools/gn/misc/help_as_html.py
+++ b/gn/misc/help_as_html.py
diff --git a/gn/misc/tm/GN.tmLanguage b/gn/misc/tm/GN.tmLanguage
new file mode 100644
index 00000000000..7ce3a5cbcbe
--- /dev/null
+++ b/gn/misc/tm/GN.tmLanguage
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>fileTypes</key>
+ <array>
+ <string>gn</string>
+ <string>gni</string>
+ </array>
+ <key>name</key>
+ <string>GN</string>
+ <key>patterns</key>
+ <array>
+ <dict>
+ <key>comment</key>
+ <string>keywords</string>
+ <key>match</key>
+ <string>\b(?:if|else)\b</string>
+ <key>name</key>
+ <string>keyword.control.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>constants</string>
+ <key>match</key>
+ <string>\b(?:true|false)\b</string>
+ <key>name</key>
+ <string>constant.language.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>numbers</string>
+ <key>match</key>
+ <string>\b\d+\.?(?:\d+)?\b</string>
+ <key>name</key>
+ <string>constant.numeric.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>double quoted string</string>
+ <key>match</key>
+ <string>\"[^\"]*\"</string>
+ <key>name</key>
+ <string>string.quoted.double.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>comment</string>
+ <key>begin</key>
+ <string>#</string>
+ <key>end</key>
+ <string>$</string>
+ <key>name</key>
+ <string>comment.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>operators</string>
+ <key>match</key>
+ <string>(?:=|==|\+=|-=|\+|-)</string>
+ <key>name</key>
+ <string>keyword.operator.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>targets</string>
+ <key>match</key>
+ <string>\b(?:action|action_foreach|copy|executable|group|loadable_module|shared_library|source_set|static_library|generated_file|rust_library|rust_proc_macro)\b</string>
+ <key>name</key>
+ <string>entity.name.tag.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>functions</string>
+ <key>match</key>
+ <string>\b(?:assert|config|declare_args|defined|exec_script|foreach|get_label_info|get_path_info|get_target_outputs|getenv|import|print|process_file_template|read_file|rebase_path|set_default_toolchain|set_defaults|split_list|string_join|string_split|template|tool|toolchain|toolchain_args|propagates_configs|write_file)\b</string>
+ <key>name</key>
+ <string>entity.name.function.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>predefined variables</string>
+ <key>match</key>
+ <string>\b(?:current_cpu|current_os|current_toolchain|default_toolchain|host_cpu|host_os|python_path|root_build_dir|root_gen_dir|root_out_dir|target_cpu|target_gen_dir|target_os|target_out_dir)\b</string>
+ <key>name</key>
+ <string>variable.parameter.gn</string>
+ </dict>
+ <dict>
+ <key>comment</key>
+ <string>target variables</string>
+ <key>match</key>
+ <string>\b(?:all_dependent_configs|allow_circular_includes_from|args|asmflags|cflags|cflags_c|cflags_cc|cflags_objc|cflags_objcc|check_includes|complete_static_lib|configs|data|data_deps|defines|depfile|deps|framework_dirs|frameworks|include_dirs|inputs|ldflags|lib_dirs|libs|output_extension|output_name|outputs|public|public_configs|public_deps|script|sources|testonly|visibility|contents|output_conversion|rebase|data_keys|walk_keys)\b</string>
+ <key>name</key>
+ <string>entity.other.attribute-name.gn</string>
+ </dict>
+ </array>
+ <key>scopeName</key>
+ <string>source.gn</string>
+ <key>uuid</key>
+ <string>DE419F8C-EC46-4824-87F3-732BD08694DC</string>
+</dict>
+</plist>
diff --git a/gn/tools/gn/misc/tm/GN.tmPreferences b/gn/misc/tm/GN.tmPreferences
index 2706d51344a..2706d51344a 100644
--- a/gn/tools/gn/misc/tm/GN.tmPreferences
+++ b/gn/misc/tm/GN.tmPreferences
diff --git a/gn/misc/vim/README.md b/gn/misc/vim/README.md
new file mode 100644
index 00000000000..26433f65e81
--- /dev/null
+++ b/gn/misc/vim/README.md
@@ -0,0 +1,65 @@
+# GN vim syntax plugin
+
+## Installation with a plugin manager
+
+You can use modern plugin managers to download the GN repo and manage the vim
+plugin:
+
+Example config for [vim-plug](https://github.com/junegunn/vim-plug):
+
+```
+Plug 'https://gn.googlesource.com/gn', { 'rtp': 'misc/vim' }
+```
+
+Or, for [Vundle](https://github.com/VundleVim/Vundle.vim) users:
+
+```
+Plugin 'https://gn.googlesource.com/gn', { 'rtp': 'misc/vim' }
+```
+
+## Manual installation
+
+If you don't use a plugin manager or would prefer to manage the GN repo
+yourself, you can add this explicitly to `rtp` in your `.vimrc`:
+
+```
+set runtimepath+=/path/to/gn/misc/vim
+" ...
+filetype plugin indent on " or a similar command to turn on filetypes in vim
+```
+
+## Formatting GN files
+
+### vim-codefmt (recommended)
+
+[vim-codefmt](https://github.com/google/vim-codefmt) supports the GN filetype
+natively. Add the following to your `.vimrc`:
+
+```vim
+" Install vim-codefmt and its dependencies
+Plug 'google/vim-maktaba'
+Plug 'google/vim-codefmt'
+
+" Install this plugin:
+Plug 'https://gn.googlesource.com/gn', { 'rtp': 'misc/vim' }
+
+" Optional: configure vim-codefmt to autoformat upon saving the buffer.
+augroup CodeFmt
+ autocmd!
+ autocmd FileType gn AutoFormatBuffer gn
+ " Other file types...
+augroup END
+```
+
+This will autoformat your files every time you save. If you prefer not to format
+files upon saving, vim-codefmt can format the buffer by calling `:FormatCode`.
+
+### Included format integration
+
+If you cannot include vim-codefmt, you can use the limited `gn format`
+integration included in this plugin. Add the following to your `.vimrc`:
+
+```vim
+" Replace <F1> with whichever hotkey you prefer:
+nnoremap <silent> <F1> :pyxf <path-to-this-plugin>/gn-format.py<CR>
+```
diff --git a/gn/tools/gn/misc/vim/autoload/gn.vim b/gn/misc/vim/autoload/gn.vim
index 5573efc0af3..5573efc0af3 100644
--- a/gn/tools/gn/misc/vim/autoload/gn.vim
+++ b/gn/misc/vim/autoload/gn.vim
diff --git a/gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim b/gn/misc/vim/ftdetect/gnfiletype.vim
index 20448c15b11..20448c15b11 100644
--- a/gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim
+++ b/gn/misc/vim/ftdetect/gnfiletype.vim
diff --git a/gn/tools/gn/misc/vim/ftplugin/gn.vim b/gn/misc/vim/ftplugin/gn.vim
index ede251dfe99..ede251dfe99 100644
--- a/gn/tools/gn/misc/vim/ftplugin/gn.vim
+++ b/gn/misc/vim/ftplugin/gn.vim
diff --git a/gn/misc/vim/gn-format.py b/gn/misc/vim/gn-format.py
new file mode 100644
index 00000000000..7d07d4341b1
--- /dev/null
+++ b/gn/misc/vim/gn-format.py
@@ -0,0 +1,62 @@
+# 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.
+#
+# Based on clang-format.py.
+#
+# This file is a minimal gn format vim-integration. To install:
+# - Change 'binary' if gn is not on the path (see below).
+# - Add to your .vimrc:
+#
+# map <F1> :pyxf <path-to-this-file>/gn-format.py<CR>
+#
+# gn format currently formats only a complete file so visual ranges, etc. won't
+# be used. It operates on the current, potentially unsaved buffer and does not
+# create or save any files. To revert a formatting, just undo.
+
+from __future__ import print_function
+import difflib
+import subprocess
+import sys
+import vim
+
+# Change this to the full path if gn is not on the path.
+binary = 'gn'
+if vim.eval('exists("g:gn_path")') == "1":
+ binary = vim.eval('g:gn_path')
+
+def main():
+ # Get the current text.
+ buf = vim.current.buffer
+ text = '\n'.join(buf)
+
+ is_win = sys.platform.startswith('win32')
+ # Avoid flashing an ugly cmd prompt on Windows when invoking gn.
+ startupinfo = None
+ if is_win:
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ startupinfo.wShowWindow = subprocess.SW_HIDE
+
+ # Call formatter. Needs shell=True on Windows due to gn.bat in depot_tools.
+ p = subprocess.Popen([binary, 'format', '--stdin'],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE, startupinfo=startupinfo,
+ shell=is_win, universal_newlines=True)
+ stdout, stderr = p.communicate(input=text)
+ if p.returncode != 0:
+ print('Formatting failed, please report to gn-dev@chromium.org.')
+ print(stdout, stderr)
+ else:
+ # Otherwise, replace current buffer.
+ lines = stdout.split('\n')
+ # Last line should have trailing \n, but we don't want to insert a blank
+ # line at the end of the buffer, so remove that.
+ if lines[-1] == '':
+ lines = lines[:-1]
+ sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
+ for op in reversed(sequence.get_opcodes()):
+ if op[0] != 'equal':
+ vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
+
+main()
diff --git a/gn/misc/vim/syntax/gn.vim b/gn/misc/vim/syntax/gn.vim
new file mode 100644
index 00000000000..454aacc51c8
--- /dev/null
+++ b/gn/misc/vim/syntax/gn.vim
@@ -0,0 +1,86 @@
+" 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.
+"
+" gn.vim: Vim syntax file for GN.
+"
+" Quit when a (custom) syntax file was already loaded
+"if exists("b:current_syntax")
+ "finish
+"endif
+
+syn case match
+
+" Keywords within functions
+syn keyword gnConditional if else
+hi def link gnConditional Conditional
+
+" Predefined variables
+syn keyword gnPredefVar current_cpu current_os current_toolchain
+syn keyword gnPredefVar default_toolchain host_cpu host_os
+syn keyword gnPredefVar root_build_dir root_gen_dir root_out_dir
+syn keyword gnPredefVar target_cpu target_gen_dir target_out_dir
+syn keyword gnPredefVar target_os
+syn keyword gnPredefVar true false
+hi def link gnPredefVar Constant
+
+" Target declarations
+syn keyword gnTarget action action_foreach copy executable group
+syn keyword gnTarget shared_library source_set static_library
+syn keyword gnTarget loadable_module generated_file
+syn keyword gnTarget rust_library rust_proc_macro
+hi def link gnTarget Type
+
+" Buildfile functions
+syn keyword gnFunctions assert config declare_args defined exec_script
+syn keyword gnFunctions foreach get_label_info get_path_info
+syn keyword gnFunctions get_target_outputs getenv import print
+syn keyword gnFunctions process_file_template propagates_configs read_file
+syn keyword gnFunctions rebase_path set_default_toolchain set_defaults
+syn keyword gnFunctions split_list string_join string_split template tool
+syn keyword gnFunctions toolchain toolchain_args write_file
+hi def link gnFunctions Macro
+
+" Variables
+syn keyword gnVariable all_dependent_configs allow_circular_includes_from
+syn keyword gnVariable args asmflags cflags cflags_c cflags_cc cflags_objc
+syn keyword gnVariable cflags_objcc check_includes complete_static_lib
+syn keyword gnVariable configs data data_deps defines depfile deps
+syn keyword gnVariable framework_dirs frameworks include_dirs inputs ldflags
+syn keyword gnVariable lib_dirs libs output_extension output_name outputs
+syn keyword gnVariable public public_configs public_deps scripte sources
+syn keyword gnVariable testonly visibility contents output_conversion rebase
+syn keyword gnVariable data_keys walk_keys
+hi def link gnVariable Keyword
+
+" Strings
+syn region gnString start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=@Spell,gnTargetName
+syn match gnTargetName '\v:[^"]+' contained
+hi def link gnString String
+hi def link gnTargetName Special
+
+" Comments
+syn keyword gnTodo contained TODO FIXME XXX BUG NOTE
+syn cluster gnCommentGroup contains=gnTodo
+syn region gnComment start="#" end="$" contains=@gnCommentGroup,@Spell
+
+hi def link gnComment Comment
+hi def link gnTodo Todo
+
+" Operators; I think this is a bit too colourful.
+"syn match gnOperator /=/
+"syn match gnOperator /!=/
+"syn match gnOperator />=/
+"syn match gnOperator /<=/
+"syn match gnOperator /==/
+"syn match gnOperator /+=/
+"syn match gnOperator /-=/
+"syn match gnOperator /\s>\s/
+"syn match gnOperator /\s<\s/
+"syn match gnOperator /\s+\s/
+"syn match gnOperator /\s-\s/
+"hi def link gnOperator Operator
+
+syn sync minlines=500
+
+let b:current_syntax = "gn"
diff --git a/gn/base/atomic_ref_count.h b/gn/src/base/atomic_ref_count.h
index 3ffa017acd3..3ffa017acd3 100644
--- a/gn/base/atomic_ref_count.h
+++ b/gn/src/base/atomic_ref_count.h
diff --git a/gn/src/base/command_line.cc b/gn/src/base/command_line.cc
new file mode 100644
index 00000000000..93e48cc19e1
--- /dev/null
+++ b/gn/src/base/command_line.cc
@@ -0,0 +1,491 @@
+// 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 "base/command_line.h"
+
+#include <algorithm>
+#include <iterator>
+#include <ostream>
+#include <string_view>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include <shellapi.h>
+#endif
+
+namespace base {
+
+CommandLine* CommandLine::current_process_commandline_ = nullptr;
+
+namespace {
+
+const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
+const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
+
+// Since we use a lazy match, make sure that longer versions (like "--") are
+// listed before shorter versions (like "-") of similar prefixes.
+#if defined(OS_WIN)
+// By putting slash last, we can control whether it is treaded as a switch
+// value by changing the value of switch_prefix_count to be one less than
+// the array size.
+const CommandLine::CharType* const kSwitchPrefixes[] = {u"--", u"-", u"/"};
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Unixes don't use slash as a switch.
+const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
+#endif
+size_t switch_prefix_count = std::size(kSwitchPrefixes);
+
+size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
+ for (size_t i = 0; i < switch_prefix_count; ++i) {
+ CommandLine::StringType prefix(kSwitchPrefixes[i]);
+ if (string.compare(0, prefix.length(), prefix) == 0)
+ return prefix.length();
+ }
+ return 0;
+}
+
+// Fills in |switch_string| and |switch_value| if |string| is a switch.
+// This will preserve the input switch prefix in the output |switch_string|.
+bool IsSwitch(const CommandLine::StringType& string,
+ CommandLine::StringType* switch_string,
+ CommandLine::StringType* switch_value) {
+ switch_string->clear();
+ switch_value->clear();
+ size_t prefix_length = GetSwitchPrefixLength(string);
+ if (prefix_length == 0 || prefix_length == string.length())
+ return false;
+
+ const size_t equals_position = string.find(kSwitchValueSeparator);
+ *switch_string = string.substr(0, equals_position);
+ if (equals_position != CommandLine::StringType::npos)
+ *switch_value = string.substr(equals_position + 1);
+ return true;
+}
+
+// Append switches and arguments, keeping switches before arguments
+// if handle_switches is true.
+void AppendSwitchesAndArguments(CommandLine* command_line,
+ const CommandLine::StringVector& argv,
+ bool handle_switches) {
+ bool parse_switches = handle_switches;
+ for (size_t i = 1; i < argv.size(); ++i) {
+ CommandLine::StringType arg = argv[i];
+#if defined(OS_WIN)
+ TrimWhitespace(arg, TRIM_ALL, &arg);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ TrimWhitespaceASCII(arg, TRIM_ALL, &arg);
+#endif
+
+ CommandLine::StringType switch_string;
+ CommandLine::StringType switch_value;
+ parse_switches &= (arg != kSwitchTerminator);
+ if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
+#if defined(OS_WIN)
+ command_line->AppendSwitchNative(UTF16ToASCII(switch_string),
+ switch_value);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ command_line->AppendSwitchNative(switch_string, switch_value);
+#else
+#error Unsupported platform
+#endif
+ } else {
+ command_line->AppendArgNative(arg);
+ }
+ }
+}
+
+#if defined(OS_WIN)
+// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*.
+std::u16string QuoteForCommandLineToArgvW(const std::u16string& arg,
+ bool quote_placeholders) {
+ // We follow the quoting rules of CommandLineToArgvW.
+ // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+ std::u16string quotable_chars(u" \\\"");
+ // We may also be required to quote '%', which is commonly used in a command
+ // line as a placeholder. (It may be substituted for a string with spaces.)
+ if (quote_placeholders)
+ quotable_chars.push_back('%');
+ if (arg.find_first_of(quotable_chars) == std::u16string::npos) {
+ // No quoting necessary.
+ return arg;
+ }
+
+ std::u16string out;
+ out.push_back('"');
+ for (size_t i = 0; i < arg.size(); ++i) {
+ if (arg[i] == '\\') {
+ // Find the extent of this run of backslashes.
+ size_t start = i, end = start + 1;
+ for (; end < arg.size() && arg[end] == '\\'; ++end) {
+ }
+ size_t backslash_count = end - start;
+
+ // Backslashes are escapes only if the run is followed by a double quote.
+ // Since we also will end the string with a double quote, we escape for
+ // either a double quote or the end of the string.
+ if (end == arg.size() || arg[end] == '"') {
+ // To quote, we need to output 2x as many backslashes.
+ backslash_count *= 2;
+ }
+ for (size_t j = 0; j < backslash_count; ++j)
+ out.push_back('\\');
+
+ // Advance i to one before the end to balance i++ in loop.
+ i = end - 1;
+ } else if (arg[i] == '"') {
+ out.push_back('\\');
+ out.push_back('"');
+ } else {
+ out.push_back(arg[i]);
+ }
+ }
+ out.push_back('"');
+
+ return out;
+}
+#endif
+
+} // namespace
+
+CommandLine::CommandLine(NoProgram no_program)
+ : argv_(1), begin_args_(1), parse_switches_(true) {}
+
+CommandLine::CommandLine(const FilePath& program)
+ : argv_(1), begin_args_(1), parse_switches_(true) {
+ SetProgram(program);
+}
+
+CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
+ : argv_(1), begin_args_(1), parse_switches_(true) {
+ InitFromArgv(argc, argv);
+}
+
+CommandLine::CommandLine(const StringVector& argv)
+ : argv_(1), begin_args_(1), parse_switches_(true) {
+ InitFromArgv(argv);
+}
+
+CommandLine::CommandLine(const CommandLine& other) = default;
+
+CommandLine& CommandLine::operator=(const CommandLine& other) = default;
+
+CommandLine::~CommandLine() = default;
+
+#if defined(OS_WIN)
+// static
+void CommandLine::set_slash_is_not_a_switch() {
+ // The last switch prefix should be slash, so adjust the size to skip it.
+ DCHECK(std::u16string_view(kSwitchPrefixes[std::size(kSwitchPrefixes) - 1]) ==
+ std::u16string_view(u"/"));
+ switch_prefix_count = std::size(kSwitchPrefixes) - 1;
+}
+
+// static
+void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
+ DCHECK(!current_process_commandline_);
+ current_process_commandline_ = new CommandLine(NO_PROGRAM);
+ // On Windows we need to convert the command line arguments to std::u16string.
+ base::CommandLine::StringVector argv_vector;
+ for (int i = 0; i < argc; ++i)
+ argv_vector.push_back(UTF8ToUTF16(argv[i]));
+ current_process_commandline_->InitFromArgv(argv_vector);
+}
+#endif
+
+// static
+bool CommandLine::Init(int argc, const char* const* argv) {
+ if (current_process_commandline_) {
+ // If this is intentional, Reset() must be called first. If we are using
+ // the shared build mode, we have to share a single object across multiple
+ // shared libraries.
+ return false;
+ }
+
+ current_process_commandline_ = new CommandLine(NO_PROGRAM);
+#if defined(OS_WIN)
+ current_process_commandline_->ParseFromString(
+ reinterpret_cast<const char16_t*>(::GetCommandLineW()));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ current_process_commandline_->InitFromArgv(argc, argv);
+#else
+#error Unsupported platform
+#endif
+
+ return true;
+}
+
+// static
+void CommandLine::Reset() {
+ DCHECK(current_process_commandline_);
+ delete current_process_commandline_;
+ current_process_commandline_ = nullptr;
+}
+
+// static
+CommandLine* CommandLine::ForCurrentProcess() {
+ DCHECK(current_process_commandline_);
+ return current_process_commandline_;
+}
+
+// static
+bool CommandLine::InitializedForCurrentProcess() {
+ return !!current_process_commandline_;
+}
+
+#if defined(OS_WIN)
+// static
+CommandLine CommandLine::FromString(const std::u16string& command_line) {
+ CommandLine cmd(NO_PROGRAM);
+ cmd.ParseFromString(command_line);
+ return cmd;
+}
+#endif
+
+void CommandLine::InitFromArgv(int argc,
+ const CommandLine::CharType* const* argv) {
+ StringVector new_argv;
+ for (int i = 0; i < argc; ++i)
+ new_argv.push_back(argv[i]);
+ InitFromArgv(new_argv);
+}
+
+void CommandLine::InitFromArgv(const StringVector& argv) {
+ argv_ = StringVector(1);
+ switches_.clear();
+ begin_args_ = 1;
+ SetProgram(argv.empty() ? FilePath() : FilePath(argv[0]));
+ AppendSwitchesAndArguments(this, argv, parse_switches_);
+}
+
+FilePath CommandLine::GetProgram() const {
+ return FilePath(argv_[0]);
+}
+
+void CommandLine::SetProgram(const FilePath& program) {
+#if defined(OS_WIN)
+ TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]);
+#else
+#error Unsupported platform
+#endif
+}
+
+bool CommandLine::HasSwitch(const std::string_view& switch_string) const {
+ DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
+ return ContainsKey(switches_, switch_string);
+}
+
+bool CommandLine::HasSwitch(const char switch_constant[]) const {
+ return HasSwitch(std::string_view(switch_constant));
+}
+
+std::string CommandLine::GetSwitchValueASCII(
+ const std::string_view& switch_string) const {
+ StringType value = GetSwitchValueNative(switch_string);
+ if (!IsStringASCII(value)) {
+ DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
+ return std::string();
+ }
+#if defined(OS_WIN)
+ return UTF16ToASCII(value);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return value;
+#endif
+}
+
+FilePath CommandLine::GetSwitchValuePath(
+ const std::string_view& switch_string) const {
+ return FilePath(GetSwitchValueNative(switch_string));
+}
+
+CommandLine::StringType CommandLine::GetSwitchValueNative(
+ const std::string_view& switch_string) const {
+ DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
+ auto result = switches_.find(switch_string);
+ return result == switches_.end() ? StringType() : result->second;
+}
+
+void CommandLine::AppendSwitch(const std::string& switch_string) {
+ AppendSwitchNative(switch_string, StringType());
+}
+
+void CommandLine::AppendSwitchPath(const std::string& switch_string,
+ const FilePath& path) {
+ AppendSwitchNative(switch_string, path.value());
+}
+
+void CommandLine::AppendSwitchNative(const std::string& switch_string,
+ const CommandLine::StringType& value) {
+#if defined(OS_WIN)
+ const std::string switch_key = ToLowerASCII(switch_string);
+ StringType combined_switch_string(ASCIIToUTF16(switch_key));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ const std::string& switch_key = switch_string;
+ StringType combined_switch_string(switch_key);
+#endif
+ size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
+ auto insertion =
+ switches_.insert(make_pair(switch_key.substr(prefix_length), value));
+ if (!insertion.second)
+ insertion.first->second = value;
+ // Preserve existing switch prefixes in |argv_|; only append one if necessary.
+ if (prefix_length == 0)
+ combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
+ if (!value.empty())
+ combined_switch_string += kSwitchValueSeparator + value;
+ // Append the switch and update the switches/arguments divider |begin_args_|.
+ argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
+}
+
+void CommandLine::AppendSwitchASCII(const std::string& switch_string,
+ const std::string& value_string) {
+#if defined(OS_WIN)
+ AppendSwitchNative(switch_string, ASCIIToUTF16(value_string));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ AppendSwitchNative(switch_string, value_string);
+#else
+#error Unsupported platform
+#endif
+}
+
+void CommandLine::CopySwitchesFrom(const CommandLine& source,
+ const char* const switches[],
+ size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ if (source.HasSwitch(switches[i]))
+ AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i]));
+ }
+}
+
+CommandLine::StringVector CommandLine::GetArgs() const {
+ // Gather all arguments after the last switch (may include kSwitchTerminator).
+ StringVector args(argv_.begin() + begin_args_, argv_.end());
+ // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
+ StringVector::iterator switch_terminator =
+ std::find(args.begin(), args.end(), kSwitchTerminator);
+ if (switch_terminator != args.end())
+ args.erase(switch_terminator);
+ return args;
+}
+
+void CommandLine::AppendArg(const std::string& value) {
+#if defined(OS_WIN)
+ DCHECK(IsStringUTF8(value));
+ AppendArgNative(UTF8ToUTF16(value));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ AppendArgNative(value);
+#else
+#error Unsupported platform
+#endif
+}
+
+void CommandLine::AppendArgPath(const FilePath& path) {
+ AppendArgNative(path.value());
+}
+
+void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
+ argv_.push_back(value);
+}
+
+void CommandLine::AppendArguments(const CommandLine& other,
+ bool include_program) {
+ if (include_program)
+ SetProgram(other.GetProgram());
+ AppendSwitchesAndArguments(this, other.argv(), parse_switches_);
+}
+
+void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
+ if (wrapper.empty())
+ return;
+ // Split the wrapper command based on whitespace (with quoting).
+ using CommandLineTokenizer =
+ StringTokenizerT<StringType, StringType::const_iterator>;
+ CommandLineTokenizer tokenizer(wrapper, FILE_PATH_LITERAL(" "));
+ tokenizer.set_quote_chars(FILE_PATH_LITERAL("'\""));
+ std::vector<StringType> wrapper_argv;
+ while (tokenizer.GetNext())
+ wrapper_argv.emplace_back(tokenizer.token());
+
+ // Prepend the wrapper and update the switches/arguments |begin_args_|.
+ argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end());
+ begin_args_ += wrapper_argv.size();
+}
+
+#if defined(OS_WIN)
+void CommandLine::ParseFromString(const std::u16string& command_line) {
+ std::u16string command_line_string;
+ TrimWhitespace(command_line, TRIM_ALL, &command_line_string);
+ if (command_line_string.empty())
+ return;
+
+ int num_args = 0;
+ char16_t** args = NULL;
+ args = reinterpret_cast<char16_t**>(::CommandLineToArgvW(
+ reinterpret_cast<LPCWSTR>(command_line_string.c_str()), &num_args));
+
+ DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
+ << UTF16ToUTF8(command_line);
+ InitFromArgv(num_args, args);
+ LocalFree(args);
+}
+#endif
+
+CommandLine::StringType CommandLine::GetCommandLineStringInternal(
+ bool quote_placeholders) const {
+ StringType string(argv_[0]);
+#if defined(OS_WIN)
+ string = QuoteForCommandLineToArgvW(string, quote_placeholders);
+#endif
+ StringType params(GetArgumentsStringInternal(quote_placeholders));
+ if (!params.empty()) {
+ string.append(StringType(FILE_PATH_LITERAL(" ")));
+ string.append(params);
+ }
+ return string;
+}
+
+CommandLine::StringType CommandLine::GetArgumentsStringInternal(
+ bool quote_placeholders) const {
+ StringType params;
+ // Append switches and arguments.
+ bool parse_switches = parse_switches_;
+ for (size_t i = 1; i < argv_.size(); ++i) {
+ StringType arg = argv_[i];
+ StringType switch_string;
+ StringType switch_value;
+ parse_switches &= arg != kSwitchTerminator;
+ if (i > 1)
+ params.append(StringType(FILE_PATH_LITERAL(" ")));
+ if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
+ params.append(switch_string);
+ if (!switch_value.empty()) {
+#if defined(OS_WIN)
+ switch_value =
+ QuoteForCommandLineToArgvW(switch_value, quote_placeholders);
+#endif
+ params.append(kSwitchValueSeparator + switch_value);
+ }
+ } else {
+#if defined(OS_WIN)
+ arg = QuoteForCommandLineToArgvW(arg, quote_placeholders);
+#endif
+ params.append(arg);
+ }
+ }
+ return params;
+}
+
+} // namespace base
diff --git a/gn/src/base/command_line.h b/gn/src/base/command_line.h
new file mode 100644
index 00000000000..46c1fcc588b
--- /dev/null
+++ b/gn/src/base/command_line.h
@@ -0,0 +1,254 @@
+// 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 works with command lines: building and parsing.
+// Arguments with prefixes ('--', '-', and on Windows, '/') are switches.
+// Switches will precede all other arguments without switch prefixes.
+// Switches can optionally have values, delimited by '=', e.g., "-switch=value".
+// An argument of "--" will terminate switch parsing during initialization,
+// interpreting subsequent tokens as non-switch arguments, regardless of prefix.
+
+// There is a singleton read-only CommandLine that represents the command line
+// that the current process was started with. It must be initialized in main().
+
+#ifndef BASE_COMMAND_LINE_H_
+#define BASE_COMMAND_LINE_H_
+
+#include <stddef.h>
+#include <map>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "util/build_config.h"
+
+namespace base {
+
+class FilePath;
+
+class CommandLine {
+ public:
+#if defined(OS_WIN)
+ // The native command line string type.
+ using StringType = std::u16string;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ using StringType = std::string;
+#endif
+
+ using CharType = StringType::value_type;
+ using StringVector = std::vector<StringType>;
+ using SwitchMap = std::map<std::string, StringType, std::less<>>;
+
+ // A constructor for CommandLines that only carry switches and arguments.
+ enum NoProgram { NO_PROGRAM };
+ explicit CommandLine(NoProgram no_program);
+
+ // Construct a new command line with |program| as argv[0].
+ explicit CommandLine(const FilePath& program);
+
+ // Construct a new command line from an argument list.
+ CommandLine(int argc, const CharType* const* argv);
+ explicit CommandLine(const StringVector& argv);
+
+ // Override copy and assign to ensure |switches_by_stringpiece_| is valid.
+ CommandLine(const CommandLine& other);
+ CommandLine& operator=(const CommandLine& other);
+
+ ~CommandLine();
+
+#if defined(OS_WIN)
+ // By default this class will treat command-line arguments beginning with
+ // slashes as switches on Windows, but not other platforms.
+ //
+ // If this behavior is inappropriate for your application, you can call this
+ // function BEFORE initializing the current process' global command line
+ // object and the behavior will be the same as Posix systems (only hyphens
+ // begin switches, everything else will be an arg).
+ static void set_slash_is_not_a_switch();
+
+ // Normally when the CommandLine singleton is initialized it gets the command
+ // line via the GetCommandLineW API and then uses the shell32 API
+ // CommandLineToArgvW to parse the command line and convert it back to
+ // argc and argv. Tests who don't want this dependency on shell32 and need
+ // to honor the arguments passed in should use this function.
+ static void InitUsingArgvForTesting(int argc, const char* const* argv);
+#endif
+
+ // Initialize the current process CommandLine singleton. On Windows, ignores
+ // its arguments (we instead parse GetCommandLineW() directly) because we
+ // don't trust the CRT's parsing of the command line, but it still must be
+ // called to set up the command line. Returns false if initialization has
+ // already occurred, and true otherwise. Only the caller receiving a 'true'
+ // return value should take responsibility for calling Reset.
+ static bool Init(int argc, const char* const* argv);
+
+ // Destroys the current process CommandLine singleton. This is necessary if
+ // you want to reset the base library to its initial state (for example, in an
+ // outer library that needs to be able to terminate, and be re-initialized).
+ // If Init is called only once, as in main(), Reset() is not necessary.
+ // Do not call this in tests. Use base::test::ScopedCommandLine instead.
+ static void Reset();
+
+ // Get the singleton CommandLine representing the current process's
+ // command line. Note: returned value is mutable, but not thread safe;
+ // only mutate if you know what you're doing!
+ static CommandLine* ForCurrentProcess();
+
+ // Returns true if the CommandLine has been initialized for the given process.
+ static bool InitializedForCurrentProcess();
+
+#if defined(OS_WIN)
+ static CommandLine FromString(const std::u16string& command_line);
+#endif
+
+ // Initialize from an argv vector.
+ void InitFromArgv(int argc, const CharType* const* argv);
+ void InitFromArgv(const StringVector& argv);
+
+ // Constructs and returns the represented command line string.
+ // CAUTION! This should be avoided on POSIX because quoting behavior is
+ // unclear.
+ StringType GetCommandLineString() const {
+ return GetCommandLineStringInternal(false);
+ }
+
+#if defined(OS_WIN)
+ // Constructs and returns the represented command line string. Assumes the
+ // command line contains placeholders (eg, %1) and quotes any program or
+ // argument with a '%' in it. This should be avoided unless the placeholder is
+ // required by an external interface (eg, the Windows registry), because it is
+ // not generally safe to replace it with an arbitrary string. If possible,
+ // placeholders should be replaced *before* converting the command line to a
+ // string.
+ StringType GetCommandLineStringWithPlaceholders() const {
+ return GetCommandLineStringInternal(true);
+ }
+#endif
+
+ // Constructs and returns the represented arguments string.
+ // CAUTION! This should be avoided on POSIX because quoting behavior is
+ // unclear.
+ StringType GetArgumentsString() const {
+ return GetArgumentsStringInternal(false);
+ }
+
+#if defined(OS_WIN)
+ // Constructs and returns the represented arguments string. Assumes the
+ // command line contains placeholders (eg, %1) and quotes any argument with a
+ // '%' in it. This should be avoided unless the placeholder is required by an
+ // external interface (eg, the Windows registry), because it is not generally
+ // safe to replace it with an arbitrary string. If possible, placeholders
+ // should be replaced *before* converting the arguments to a string.
+ StringType GetArgumentsStringWithPlaceholders() const {
+ return GetArgumentsStringInternal(true);
+ }
+#endif
+
+ // Returns the original command line string as a vector of strings.
+ const StringVector& argv() const { return argv_; }
+
+ // Get and Set the program part of the command line string (the first item).
+ FilePath GetProgram() const;
+ void SetProgram(const FilePath& program);
+
+ // Enables/disables the parsing of switches for future argument appending.
+ // True by default, but can be set to false to ensure that no re-ordering
+ // is done.
+ void SetParseSwitches(bool parse_switches) {
+ parse_switches_ = parse_switches;
+ }
+
+ // Returns true if this command line contains the given switch.
+ // Switch names must be lowercase.
+ // The second override provides an optimized version to avoid inlining codegen
+ // at every callsite to find the length of the constant and construct a
+ // std::string_view.
+ bool HasSwitch(const std::string_view& switch_string) const;
+ bool HasSwitch(const char switch_constant[]) const;
+
+ // Returns the value associated with the given switch. If the switch has no
+ // value or isn't present, this method returns the empty string.
+ // Switch names must be lowercase.
+ std::string GetSwitchValueASCII(const std::string_view& switch_string) const;
+ FilePath GetSwitchValuePath(const std::string_view& switch_string) const;
+ StringType GetSwitchValueNative(const std::string_view& switch_string) const;
+
+ // Get a copy of all switches, along with their values.
+ const SwitchMap& GetSwitches() const { return switches_; }
+
+ // Append a switch [with optional value] to the command line.
+ // Note: Switches will precede arguments regardless of appending order.
+ void AppendSwitch(const std::string& switch_string);
+ void AppendSwitchPath(const std::string& switch_string, const FilePath& path);
+ void AppendSwitchNative(const std::string& switch_string,
+ const StringType& value);
+ void AppendSwitchASCII(const std::string& switch_string,
+ const std::string& value);
+
+ // Copy a set of switches (and any values) from another command line.
+ // Commonly used when launching a subprocess.
+ void CopySwitchesFrom(const CommandLine& source,
+ const char* const switches[],
+ size_t count);
+
+ // Get the remaining arguments to the command.
+ StringVector GetArgs() const;
+
+ // Append an argument to the command line. Note that the argument is quoted
+ // properly such that it is interpreted as one argument to the target command.
+ // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8.
+ // Note: Switches will precede arguments regardless of appending order.
+ void AppendArg(const std::string& value);
+ void AppendArgPath(const FilePath& value);
+ void AppendArgNative(const StringType& value);
+
+ // Append the switches and arguments from another command line to this one.
+ // If |include_program| is true, include |other|'s program as well.
+ void AppendArguments(const CommandLine& other, bool include_program);
+
+ // Insert a command before the current command.
+ // Common for debuggers, like "gdb --args".
+ void PrependWrapper(const StringType& wrapper);
+
+#if defined(OS_WIN)
+ // Initialize by parsing the given command line string.
+ // The program name is assumed to be the first item in the string.
+ void ParseFromString(const std::u16string& command_line);
+#endif
+
+ private:
+ // Disallow default constructor; a program name must be explicitly specified.
+ CommandLine() = delete;
+ // Allow the copy constructor. A common pattern is to copy of the current
+ // process's command line and then add some flags to it. For example:
+ // CommandLine cl(*CommandLine::ForCurrentProcess());
+ // cl.AppendSwitch(...);
+
+ // Internal version of GetCommandLineString. If |quote_placeholders| is true,
+ // also quotes parts with '%' in them.
+ StringType GetCommandLineStringInternal(bool quote_placeholders) const;
+
+ // Internal version of GetArgumentsString. If |quote_placeholders| is true,
+ // also quotes parts with '%' in them.
+ StringType GetArgumentsStringInternal(bool quote_placeholders) const;
+
+ // The singleton CommandLine representing the current process's command line.
+ static CommandLine* current_process_commandline_;
+
+ // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
+ StringVector argv_;
+
+ // Parsed-out switch keys and values.
+ SwitchMap switches_;
+
+ // The index after the program and switches, any arguments start here.
+ size_t begin_args_;
+
+ // Whether or not to parse arguments that look like switches as switches.
+ bool parse_switches_;
+};
+
+} // namespace base
+
+#endif // BASE_COMMAND_LINE_H_
diff --git a/gn/src/base/compiler_specific.h b/gn/src/base/compiler_specific.h
new file mode 100644
index 00000000000..75f21e8613d
--- /dev/null
+++ b/gn/src/base/compiler_specific.h
@@ -0,0 +1,76 @@
+// 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 BASE_COMPILER_SPECIFIC_H_
+#define BASE_COMPILER_SPECIFIC_H_
+
+#include "util/build_config.h"
+
+#if defined(COMPILER_MSVC)
+
+// For _Printf_format_string_.
+#include <sal.h>
+
+#else // Not MSVC
+
+#define _Printf_format_string_
+
+#endif // COMPILER_MSVC
+
+#if COMPILER_GCC && defined(NDEBUG)
+#define ALWAYS_INLINE inline __attribute__((__always_inline__))
+#elif COMPILER_MSVC && defined(NDEBUG)
+#define ALWAYS_INLINE __forceinline
+#else
+#define ALWAYS_INLINE inline
+#endif
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+// int foo() WARN_UNUSED_RESULT;
+// To explicitly ignore a result, see |ignore_result()| in base/macros.h.
+#undef WARN_UNUSED_RESULT
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+
+// Tell the compiler a function is using a printf-style format string.
+// |format_param| is the one-based index of the format string parameter;
+// |dots_param| is the one-based index of the "..." parameter.
+// For v*printf functions (which take a va_list), pass 0 for dots_param.
+// (This is undocumented but matches what the system C headers do.)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define PRINTF_FORMAT(format_param, dots_param) \
+ __attribute__((format(printf, format_param, dots_param)))
+#else
+#define PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+// Macro for hinting that an expression is likely to be false.
+#if !defined(UNLIKELY)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif // defined(COMPILER_GCC)
+#endif // !defined(UNLIKELY)
+
+#if !defined(LIKELY)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define LIKELY(x) (x)
+#endif // defined(COMPILER_GCC)
+#endif // !defined(LIKELY)
+
+// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
+#if defined(__clang__)
+#define FALLTHROUGH [[clang::fallthrough]]
+#else
+#define FALLTHROUGH
+#endif
+
+#endif // BASE_COMPILER_SPECIFIC_H_
diff --git a/gn/base/containers/circular_deque.h b/gn/src/base/containers/circular_deque.h
index bf42a958448..bf42a958448 100644
--- a/gn/base/containers/circular_deque.h
+++ b/gn/src/base/containers/circular_deque.h
diff --git a/gn/base/containers/flat_map.h b/gn/src/base/containers/flat_map.h
index b4fe5196ec8..b4fe5196ec8 100644
--- a/gn/base/containers/flat_map.h
+++ b/gn/src/base/containers/flat_map.h
diff --git a/gn/base/containers/flat_set.h b/gn/src/base/containers/flat_set.h
index 700617f2825..700617f2825 100644
--- a/gn/base/containers/flat_set.h
+++ b/gn/src/base/containers/flat_set.h
diff --git a/gn/src/base/containers/flat_tree.h b/gn/src/base/containers/flat_tree.h
new file mode 100644
index 00000000000..65afe5856fd
--- /dev/null
+++ b/gn/src/base/containers/flat_tree.h
@@ -0,0 +1,1004 @@
+// Copyright 2017 The Chromium 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 BASE_CONTAINERS_FLAT_TREE_H_
+#define BASE_CONTAINERS_FLAT_TREE_H_
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <vector>
+
+#include "base/template_util.h"
+
+namespace base {
+
+enum FlatContainerDupes {
+ KEEP_FIRST_OF_DUPES,
+ KEEP_LAST_OF_DUPES,
+};
+
+namespace internal {
+
+// This is a convenience method returning true if Iterator is at least a
+// ForwardIterator and thus supports multiple passes over a range.
+template <class Iterator>
+constexpr bool is_multipass() {
+ return std::is_base_of<
+ std::forward_iterator_tag,
+ typename std::iterator_traits<Iterator>::iterator_category>::value;
+}
+
+// This algorithm is like unique() from the standard library except it
+// selects only the last of consecutive values instead of the first.
+template <class Iterator, class BinaryPredicate>
+Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) {
+ Iterator replacable = std::adjacent_find(first, last, compare);
+
+ // No duplicate elements found.
+ if (replacable == last)
+ return last;
+
+ first = std::next(replacable);
+
+ // Last element is a duplicate but all others are unique.
+ if (first == last)
+ return replacable;
+
+ // This loop is based on std::adjacent_find but std::adjacent_find doesn't
+ // quite cut it.
+ for (Iterator next = std::next(first); next != last; ++next, ++first) {
+ if (!compare(*first, *next))
+ *replacable++ = std::move(*first);
+ }
+
+ // Last element should be copied unconditionally.
+ *replacable++ = std::move(*first);
+ return replacable;
+}
+
+// Uses SFINAE to detect whether type has is_transparent member.
+template <typename T, typename = void>
+struct IsTransparentCompare : std::false_type {};
+template <typename T>
+struct IsTransparentCompare<T, std::void_t<typename T::is_transparent>>
+ : std::true_type {};
+
+// Implementation -------------------------------------------------------------
+
+// Implementation of a sorted vector for backing flat_set and flat_map. Do not
+// use directly.
+//
+// The use of "value" in this is like std::map uses, meaning it's the thing
+// contained (in the case of map it's a <Kay, Mapped> pair). The Key is how
+// things are looked up. In the case of a set, Key == Value. In the case of
+// a map, the Key is a component of a Value.
+//
+// The helper class GetKeyFromValue provides the means to extract a key from a
+// value for comparison purposes. It should implement:
+// const Key& operator()(const Value&).
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+class flat_tree {
+ private:
+ using underlying_type = std::vector<Value>;
+
+ public:
+ // --------------------------------------------------------------------------
+ // Types.
+ //
+ using key_type = Key;
+ using key_compare = KeyCompare;
+ using value_type = Value;
+
+ // Wraps the templated key comparison to compare values.
+ class value_compare : public key_compare {
+ public:
+ value_compare() = default;
+
+ template <class Cmp>
+ explicit value_compare(Cmp&& compare_arg)
+ : KeyCompare(std::forward<Cmp>(compare_arg)) {}
+
+ bool operator()(const value_type& left, const value_type& right) const {
+ GetKeyFromValue extractor;
+ return key_compare::operator()(extractor(left), extractor(right));
+ }
+ };
+
+ using pointer = typename underlying_type::pointer;
+ using const_pointer = typename underlying_type::const_pointer;
+ using reference = typename underlying_type::reference;
+ using const_reference = typename underlying_type::const_reference;
+ using size_type = typename underlying_type::size_type;
+ using difference_type = typename underlying_type::difference_type;
+ using iterator = typename underlying_type::iterator;
+ using const_iterator = typename underlying_type::const_iterator;
+ using reverse_iterator = typename underlying_type::reverse_iterator;
+ using const_reverse_iterator =
+ typename underlying_type::const_reverse_iterator;
+
+ // --------------------------------------------------------------------------
+ // Lifetime.
+ //
+ // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity
+ // and take O(N * log(N)) + O(N) if extra memory is available (N is a range
+ // length).
+ //
+ // Assume that move constructors invalidate iterators and references.
+ //
+ // The constructors that take ranges, lists, and vectors do not require that
+ // the input be sorted.
+
+ flat_tree();
+ explicit flat_tree(const key_compare& comp);
+
+ template <class InputIterator>
+ flat_tree(InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
+ const key_compare& comp = key_compare());
+
+ flat_tree(const flat_tree&);
+ flat_tree(flat_tree&&) noexcept = default;
+
+ flat_tree(std::vector<value_type> items,
+ FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
+ const key_compare& comp = key_compare());
+
+ flat_tree(std::initializer_list<value_type> ilist,
+ FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
+ const key_compare& comp = key_compare());
+
+ ~flat_tree();
+
+ // --------------------------------------------------------------------------
+ // Assignments.
+ //
+ // Assume that move assignment invalidates iterators and references.
+
+ flat_tree& operator=(const flat_tree&);
+ flat_tree& operator=(flat_tree&&);
+ // Takes the first if there are duplicates in the initializer list.
+ flat_tree& operator=(std::initializer_list<value_type> ilist);
+
+ // --------------------------------------------------------------------------
+ // Memory management.
+ //
+ // Beware that shrink_to_fit() simply forwards the request to the
+ // underlying_type and its implementation is free to optimize otherwise and
+ // leave capacity() to be greater that its size.
+ //
+ // reserve() and shrink_to_fit() invalidate iterators and references.
+
+ void reserve(size_type new_capacity);
+ size_type capacity() const;
+ void shrink_to_fit();
+
+ // --------------------------------------------------------------------------
+ // Size management.
+ //
+ // clear() leaves the capacity() of the flat_tree unchanged.
+
+ void clear();
+
+ size_type size() const;
+ size_type max_size() const;
+ bool empty() const;
+
+ // --------------------------------------------------------------------------
+ // Iterators.
+
+ iterator begin();
+ const_iterator begin() const;
+ const_iterator cbegin() const;
+
+ iterator end();
+ const_iterator end() const;
+ const_iterator cend() const;
+
+ reverse_iterator rbegin();
+ const_reverse_iterator rbegin() const;
+ const_reverse_iterator crbegin() const;
+
+ reverse_iterator rend();
+ const_reverse_iterator rend() const;
+ const_reverse_iterator crend() const;
+
+ // --------------------------------------------------------------------------
+ // Insert operations.
+ //
+ // Assume that every operation invalidates iterators and references.
+ // Insertion of one element can take O(size). Capacity of flat_tree grows in
+ // an implementation-defined manner.
+ //
+ // NOTE: Prefer to build a new flat_tree from a std::vector (or similar)
+ // instead of calling insert() repeatedly.
+
+ std::pair<iterator, bool> insert(const value_type& val);
+ std::pair<iterator, bool> insert(value_type&& val);
+
+ iterator insert(const_iterator position_hint, const value_type& x);
+ iterator insert(const_iterator position_hint, value_type&& x);
+
+ // This method inserts the values from the range [first, last) into the
+ // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can
+ // overwrite existing values.
+ template <class InputIterator>
+ void insert(InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupes = KEEP_FIRST_OF_DUPES);
+
+ template <class... Args>
+ std::pair<iterator, bool> emplace(Args&&... args);
+
+ template <class... Args>
+ iterator emplace_hint(const_iterator position_hint, Args&&... args);
+
+ // --------------------------------------------------------------------------
+ // Erase operations.
+ //
+ // Assume that every operation invalidates iterators and references.
+ //
+ // erase(position), erase(first, last) can take O(size).
+ // erase(key) may take O(size) + O(log(size)).
+ //
+ // Prefer base::EraseIf() or some other variation on erase(remove(), end())
+ // idiom when deleting multiple non-consecutive elements.
+
+ iterator erase(iterator position);
+ iterator erase(const_iterator position);
+ iterator erase(const_iterator first, const_iterator last);
+ template <typename K>
+ size_type erase(const K& key);
+
+ // --------------------------------------------------------------------------
+ // Comparators.
+
+ key_compare key_comp() const;
+ value_compare value_comp() const;
+
+ // --------------------------------------------------------------------------
+ // Search operations.
+ //
+ // Search operations have O(log(size)) complexity.
+
+ template <typename K>
+ size_type count(const K& key) const;
+
+ template <typename K>
+ iterator find(const K& key);
+
+ template <typename K>
+ const_iterator find(const K& key) const;
+
+ template <typename K>
+ std::pair<iterator, iterator> equal_range(const K& key);
+
+ template <typename K>
+ std::pair<const_iterator, const_iterator> equal_range(const K& key) const;
+
+ template <typename K>
+ iterator lower_bound(const K& key);
+
+ template <typename K>
+ const_iterator lower_bound(const K& key) const;
+
+ template <typename K>
+ iterator upper_bound(const K& key);
+
+ template <typename K>
+ const_iterator upper_bound(const K& key) const;
+
+ // --------------------------------------------------------------------------
+ // General operations.
+ //
+ // Assume that swap invalidates iterators and references.
+ //
+ // Implementation note: currently we use operator==() and operator<() on
+ // std::vector, because they have the same contract we need, so we use them
+ // directly for brevity and in case it is more optimal than calling equal()
+ // and lexicograhpical_compare(). If the underlying container type is changed,
+ // this code may need to be modified.
+
+ void swap(flat_tree& other) noexcept;
+
+ friend bool operator==(const flat_tree& lhs, const flat_tree& rhs) {
+ return lhs.impl_.body_ == rhs.impl_.body_;
+ }
+
+ friend bool operator!=(const flat_tree& lhs, const flat_tree& rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend bool operator<(const flat_tree& lhs, const flat_tree& rhs) {
+ return lhs.impl_.body_ < rhs.impl_.body_;
+ }
+
+ friend bool operator>(const flat_tree& lhs, const flat_tree& rhs) {
+ return rhs < lhs;
+ }
+
+ friend bool operator>=(const flat_tree& lhs, const flat_tree& rhs) {
+ return !(lhs < rhs);
+ }
+
+ friend bool operator<=(const flat_tree& lhs, const flat_tree& rhs) {
+ return !(lhs > rhs);
+ }
+
+ friend void swap(flat_tree& lhs, flat_tree& rhs) noexcept { lhs.swap(rhs); }
+
+ protected:
+ // Emplaces a new item into the tree that is known not to be in it. This
+ // is for implementing map operator[].
+ template <class... Args>
+ iterator unsafe_emplace(const_iterator position, Args&&... args);
+
+ // Attempts to emplace a new element with key |key|. Only if |key| is not yet
+ // present, construct value_type from |args| and insert it. Returns an
+ // iterator to the element with key |key| and a bool indicating whether an
+ // insertion happened.
+ template <class K, class... Args>
+ std::pair<iterator, bool> emplace_key_args(const K& key, Args&&... args);
+
+ // Similar to |emplace_key_args|, but checks |hint| first as a possible
+ // insertion position.
+ template <class K, class... Args>
+ std::pair<iterator, bool> emplace_hint_key_args(const_iterator hint,
+ const K& key,
+ Args&&... args);
+
+ private:
+ // Helper class for e.g. lower_bound that can compare a value on the left
+ // to a key on the right.
+ struct KeyValueCompare {
+ // The key comparison object must outlive this class.
+ explicit KeyValueCompare(const key_compare& key_comp)
+ : key_comp_(key_comp) {}
+
+ template <typename T, typename U>
+ bool operator()(const T& lhs, const U& rhs) const {
+ return key_comp_(extract_if_value_type(lhs), extract_if_value_type(rhs));
+ }
+
+ private:
+ const key_type& extract_if_value_type(const value_type& v) const {
+ GetKeyFromValue extractor;
+ return extractor(v);
+ }
+
+ template <typename K>
+ const K& extract_if_value_type(const K& k) const {
+ return k;
+ }
+
+ const key_compare& key_comp_;
+ };
+
+ const flat_tree& as_const() { return *this; }
+
+ iterator const_cast_it(const_iterator c_it) {
+ auto distance = std::distance(cbegin(), c_it);
+ return std::next(begin(), distance);
+ }
+
+ // This method is inspired by both std::map::insert(P&&) and
+ // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent
+ // element is not present yet, otherwise it overwrites. It returns an iterator
+ // to the modified element and a flag indicating whether insertion or
+ // assignment happened.
+ template <class V>
+ std::pair<iterator, bool> insert_or_assign(V&& val) {
+ auto position = lower_bound(GetKeyFromValue()(val));
+
+ if (position == end() || value_comp()(val, *position))
+ return {impl_.body_.emplace(position, std::forward<V>(val)), true};
+
+ *position = std::forward<V>(val);
+ return {position, false};
+ }
+
+ // This method is similar to insert_or_assign, with the following differences:
+ // - Instead of searching [begin(), end()) it only searches [first, last).
+ // - In case no equivalent element is found, val is appended to the end of the
+ // underlying body and an iterator to the next bigger element in [first,
+ // last) is returned.
+ template <class V>
+ std::pair<iterator, bool> append_or_assign(iterator first,
+ iterator last,
+ V&& val) {
+ auto position = std::lower_bound(first, last, val, value_comp());
+
+ if (position == last || value_comp()(val, *position)) {
+ // emplace_back might invalidate position, which is why distance needs to
+ // be cached.
+ const difference_type distance = std::distance(begin(), position);
+ impl_.body_.emplace_back(std::forward<V>(val));
+ return {std::next(begin(), distance), true};
+ }
+
+ *position = std::forward<V>(val);
+ return {position, false};
+ }
+
+ // This method is similar to insert, with the following differences:
+ // - Instead of searching [begin(), end()) it only searches [first, last).
+ // - In case no equivalent element is found, val is appended to the end of the
+ // underlying body and an iterator to the next bigger element in [first,
+ // last) is returned.
+ template <class V>
+ std::pair<iterator, bool> append_unique(iterator first,
+ iterator last,
+ V&& val) {
+ auto position = std::lower_bound(first, last, val, value_comp());
+
+ if (position == last || value_comp()(val, *position)) {
+ // emplace_back might invalidate position, which is why distance needs to
+ // be cached.
+ const difference_type distance = std::distance(begin(), position);
+ impl_.body_.emplace_back(std::forward<V>(val));
+ return {std::next(begin(), distance), true};
+ }
+
+ return {position, false};
+ }
+
+ void sort_and_unique(iterator first,
+ iterator last,
+ FlatContainerDupes dupes) {
+ // Preserve stability for the unique code below.
+ std::stable_sort(first, last, impl_.get_value_comp());
+
+ auto comparator = [this](const value_type& lhs, const value_type& rhs) {
+ // lhs is already <= rhs due to sort, therefore
+ // !(lhs < rhs) <=> lhs == rhs.
+ return !impl_.get_value_comp()(lhs, rhs);
+ };
+
+ iterator erase_after;
+ switch (dupes) {
+ case KEEP_FIRST_OF_DUPES:
+ erase_after = std::unique(first, last, comparator);
+ break;
+ case KEEP_LAST_OF_DUPES:
+ erase_after = LastUnique(first, last, comparator);
+ break;
+ }
+ erase(erase_after, last);
+ }
+
+ // To support comparators that may not be possible to default-construct, we
+ // have to store an instance of Compare. Using this to store all internal
+ // state of flat_tree and using private inheritance to store compare lets us
+ // take advantage of an empty base class optimization to avoid extra space in
+ // the common case when Compare has no state.
+ struct Impl : private value_compare {
+ Impl() = default;
+
+ template <class Cmp, class... Body>
+ explicit Impl(Cmp&& compare_arg, Body&&... underlying_type_args)
+ : value_compare(std::forward<Cmp>(compare_arg)),
+ body_(std::forward<Body>(underlying_type_args)...) {}
+
+ const value_compare& get_value_comp() const { return *this; }
+ const key_compare& get_key_comp() const { return *this; }
+
+ underlying_type body_;
+ } impl_;
+
+ // If the compare is not transparent we want to construct key_type once.
+ template <typename K>
+ using KeyTypeOrK = typename std::
+ conditional<IsTransparentCompare<key_compare>::value, K, key_type>::type;
+};
+
+// ----------------------------------------------------------------------------
+// Lifetime.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree() = default;
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
+ const KeyCompare& comp)
+ : impl_(comp) {}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class InputIterator>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
+ InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupe_handling,
+ const KeyCompare& comp)
+ : impl_(comp, first, last) {
+ sort_and_unique(begin(), end(), dupe_handling);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
+ const flat_tree&) = default;
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
+ std::vector<value_type> items,
+ FlatContainerDupes dupe_handling,
+ const KeyCompare& comp)
+ : impl_(comp, std::move(items)) {
+ sort_and_unique(begin(), end(), dupe_handling);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
+ std::initializer_list<value_type> ilist,
+ FlatContainerDupes dupe_handling,
+ const KeyCompare& comp)
+ : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default;
+
+// ----------------------------------------------------------------------------
+// Assignments.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
+ const flat_tree&) -> flat_tree& = default;
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(flat_tree &&)
+ -> flat_tree& = default;
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
+ std::initializer_list<value_type> ilist) -> flat_tree& {
+ impl_.body_ = ilist;
+ sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES);
+ return *this;
+}
+
+// ----------------------------------------------------------------------------
+// Memory management.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::reserve(
+ size_type new_capacity) {
+ impl_.body_.reserve(new_capacity);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::capacity() const
+ -> size_type {
+ return impl_.body_.capacity();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::shrink_to_fit() {
+ impl_.body_.shrink_to_fit();
+}
+
+// ----------------------------------------------------------------------------
+// Size management.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::clear() {
+ impl_.body_.clear();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::size() const
+ -> size_type {
+ return impl_.body_.size();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::max_size() const
+ -> size_type {
+ return impl_.body_.max_size();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+bool flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::empty() const {
+ return impl_.body_.empty();
+}
+
+// ----------------------------------------------------------------------------
+// Iterators.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::begin() -> iterator {
+ return impl_.body_.begin();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::begin() const
+ -> const_iterator {
+ return impl_.body_.begin();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::cbegin() const
+ -> const_iterator {
+ return impl_.body_.cbegin();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::end() -> iterator {
+ return impl_.body_.end();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::end() const
+ -> const_iterator {
+ return impl_.body_.end();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::cend() const
+ -> const_iterator {
+ return impl_.body_.cend();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rbegin()
+ -> reverse_iterator {
+ return impl_.body_.rbegin();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rbegin() const
+ -> const_reverse_iterator {
+ return impl_.body_.rbegin();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::crbegin() const
+ -> const_reverse_iterator {
+ return impl_.body_.crbegin();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rend()
+ -> reverse_iterator {
+ return impl_.body_.rend();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::rend() const
+ -> const_reverse_iterator {
+ return impl_.body_.rend();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::crend() const
+ -> const_reverse_iterator {
+ return impl_.body_.crend();
+}
+
+// ----------------------------------------------------------------------------
+// Insert operations.
+//
+// Currently we use position_hint the same way as eastl or boost:
+// https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set.h#L493
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
+ const value_type& val) -> std::pair<iterator, bool> {
+ return emplace_key_args(GetKeyFromValue()(val), val);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
+ value_type&& val) -> std::pair<iterator, bool> {
+ return emplace_key_args(GetKeyFromValue()(val), std::move(val));
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
+ const_iterator position_hint,
+ const value_type& val) -> iterator {
+ return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), val)
+ .first;
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
+ const_iterator position_hint,
+ value_type&& val) -> iterator {
+ return emplace_hint_key_args(position_hint, GetKeyFromValue()(val),
+ std::move(val))
+ .first;
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class InputIterator>
+void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
+ InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupes) {
+ if (first == last)
+ return;
+
+ // Cache results whether existing elements should be overwritten and whether
+ // inserting new elements happens immediately or will be done in a batch.
+ const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES;
+ const bool insert_inplace =
+ is_multipass<InputIterator>() && std::next(first) == last;
+
+ if (insert_inplace) {
+ if (overwrite_existing) {
+ for (; first != last; ++first)
+ insert_or_assign(*first);
+ } else
+ std::copy(first, last, std::inserter(*this, end()));
+ return;
+ }
+
+ // Provide a convenience lambda to obtain an iterator pointing past the last
+ // old element. This needs to be dymanic due to possible re-allocations.
+ const size_type original_size = size();
+ auto middle = [this, original_size]() {
+ return std::next(begin(), original_size);
+ };
+
+ // For batch updates initialize the first insertion point.
+ difference_type pos_first_new = original_size;
+
+ // Loop over the input range while appending new values and overwriting
+ // existing ones, if applicable. Keep track of the first insertion point.
+ if (overwrite_existing) {
+ for (; first != last; ++first) {
+ std::pair<iterator, bool> result =
+ append_or_assign(begin(), middle(), *first);
+ if (result.second) {
+ pos_first_new =
+ std::min(pos_first_new, std::distance(begin(), result.first));
+ }
+ }
+ } else {
+ for (; first != last; ++first) {
+ std::pair<iterator, bool> result =
+ append_unique(begin(), middle(), *first);
+ if (result.second) {
+ pos_first_new =
+ std::min(pos_first_new, std::distance(begin(), result.first));
+ }
+ }
+ }
+
+ // The new elements might be unordered and contain duplicates, so post-process
+ // the just inserted elements and merge them with the rest, inserting them at
+ // the previously found spot.
+ sort_and_unique(middle(), end(), dupes);
+ std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(),
+ value_comp());
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class... Args>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace(Args&&... args)
+ -> std::pair<iterator, bool> {
+ return insert(value_type(std::forward<Args>(args)...));
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class... Args>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_hint(
+ const_iterator position_hint,
+ Args&&... args) -> iterator {
+ return insert(position_hint, value_type(std::forward<Args>(args)...));
+}
+
+// ----------------------------------------------------------------------------
+// Erase operations.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(
+ iterator position) -> iterator {
+ return impl_.body_.erase(position);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(
+ const_iterator position) -> iterator {
+ return impl_.body_.erase(position);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(const K& val)
+ -> size_type {
+ auto eq_range = equal_range(val);
+ auto res = std::distance(eq_range.first, eq_range.second);
+ erase(eq_range.first, eq_range.second);
+ return res;
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::erase(
+ const_iterator first,
+ const_iterator last) -> iterator {
+ return impl_.body_.erase(first, last);
+}
+
+// ----------------------------------------------------------------------------
+// Comparators.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::key_comp() const
+ -> key_compare {
+ return impl_.get_key_comp();
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::value_comp() const
+ -> value_compare {
+ return impl_.get_value_comp();
+}
+
+// ----------------------------------------------------------------------------
+// Search operations.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::count(
+ const K& key) const -> size_type {
+ auto eq_range = equal_range(key);
+ return std::distance(eq_range.first, eq_range.second);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::find(const K& key)
+ -> iterator {
+ return const_cast_it(as_const().find(key));
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::find(
+ const K& key) const -> const_iterator {
+ auto eq_range = equal_range(key);
+ return (eq_range.first == eq_range.second) ? end() : eq_range.first;
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::equal_range(
+ const K& key) -> std::pair<iterator, iterator> {
+ auto res = as_const().equal_range(key);
+ return {const_cast_it(res.first), const_cast_it(res.second)};
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::equal_range(
+ const K& key) const -> std::pair<const_iterator, const_iterator> {
+ auto lower = lower_bound(key);
+
+ GetKeyFromValue extractor;
+ if (lower == end() || impl_.get_key_comp()(key, extractor(*lower)))
+ return {lower, lower};
+
+ return {lower, std::next(lower)};
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::lower_bound(
+ const K& key) -> iterator {
+ return const_cast_it(as_const().lower_bound(key));
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::lower_bound(
+ const K& key) const -> const_iterator {
+ static_assert(std::is_convertible<const KeyTypeOrK<K>&, const K&>::value,
+ "Requested type cannot be bound to the container's key_type "
+ "which is required for a non-transparent compare.");
+
+ const KeyTypeOrK<K>& key_ref = key;
+
+ KeyValueCompare key_value(impl_.get_key_comp());
+ return std::lower_bound(begin(), end(), key_ref, key_value);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::upper_bound(
+ const K& key) -> iterator {
+ return const_cast_it(as_const().upper_bound(key));
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <typename K>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::upper_bound(
+ const K& key) const -> const_iterator {
+ static_assert(std::is_convertible<const KeyTypeOrK<K>&, const K&>::value,
+ "Requested type cannot be bound to the container's key_type "
+ "which is required for a non-transparent compare.");
+
+ const KeyTypeOrK<K>& key_ref = key;
+
+ KeyValueCompare key_value(impl_.get_key_comp());
+ return std::upper_bound(begin(), end(), key_ref, key_value);
+}
+
+// ----------------------------------------------------------------------------
+// General operations.
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::swap(
+ flat_tree& other) noexcept {
+ std::swap(impl_, other.impl_);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class... Args>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::unsafe_emplace(
+ const_iterator position,
+ Args&&... args) -> iterator {
+ return impl_.body_.emplace(position, std::forward<Args>(args)...);
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class K, class... Args>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_key_args(
+ const K& key,
+ Args&&... args) -> std::pair<iterator, bool> {
+ auto lower = lower_bound(key);
+ if (lower == end() || key_comp()(key, GetKeyFromValue()(*lower)))
+ return {unsafe_emplace(lower, std::forward<Args>(args)...), true};
+ return {lower, false};
+}
+
+template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
+template <class K, class... Args>
+auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::emplace_hint_key_args(
+ const_iterator hint,
+ const K& key,
+ Args&&... args) -> std::pair<iterator, bool> {
+ GetKeyFromValue extractor;
+ if ((hint == begin() || key_comp()(extractor(*std::prev(hint)), key))) {
+ if (hint == end() || key_comp()(key, extractor(*hint))) {
+ // *(hint - 1) < key < *hint => key did not exist and hint is correct.
+ return {unsafe_emplace(hint, std::forward<Args>(args)...), true};
+ }
+ if (!key_comp()(extractor(*hint), key)) {
+ // key == *hint => no-op, return correct hint.
+ return {const_cast_it(hint), false};
+ }
+ }
+ // hint was not helpful, dispatch to hintless version.
+ return emplace_key_args(key, std::forward<Args>(args)...);
+}
+
+// For containers like sets, the key is the same as the value. This implements
+// the GetKeyFromValue template parameter to flat_tree for this case.
+template <class Key>
+struct GetKeyFromValueIdentity {
+ const Key& operator()(const Key& k) const { return k; }
+};
+
+} // namespace internal
+
+// ----------------------------------------------------------------------------
+// Free functions.
+
+// Erases all elements that match predicate. It has O(size) complexity.
+template <class Key,
+ class Value,
+ class GetKeyFromValue,
+ class KeyCompare,
+ typename Predicate>
+void EraseIf(base::internal::flat_tree<Key, Value, GetKeyFromValue, KeyCompare>&
+ container,
+ Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_FLAT_TREE_H_
diff --git a/gn/base/containers/queue.h b/gn/src/base/containers/queue.h
index 2d3b480089f..2d3b480089f 100644
--- a/gn/base/containers/queue.h
+++ b/gn/src/base/containers/queue.h
diff --git a/gn/src/base/containers/span.h b/gn/src/base/containers/span.h
new file mode 100644
index 00000000000..29c9a97fc5d
--- /dev/null
+++ b/gn/src/base/containers/span.h
@@ -0,0 +1,453 @@
+// Copyright 2017 The Chromium 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 BASE_CONTAINERS_SPAN_H_
+#define BASE_CONTAINERS_SPAN_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// [views.constants]
+constexpr size_t dynamic_extent = static_cast<size_t>(-1);
+
+template <typename T, size_t Extent = dynamic_extent>
+class span;
+
+namespace internal {
+
+template <typename T>
+struct IsSpanImpl : std::false_type {};
+
+template <typename T, size_t Extent>
+struct IsSpanImpl<span<T, Extent>> : std::true_type {};
+
+template <typename T>
+using IsSpan = IsSpanImpl<std::decay_t<T>>;
+
+template <typename T>
+struct IsStdArrayImpl : std::false_type {};
+
+template <typename T, size_t N>
+struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
+
+template <typename T>
+using IsCArray = std::is_array<std::remove_reference_t<T>>;
+
+template <typename From, typename To>
+using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
+
+template <typename Container, typename T>
+using ContainerHasConvertibleData = IsLegalDataConversion<
+ std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
+ T>;
+
+template <typename Container>
+using ContainerHasIntegralSize =
+ std::is_integral<decltype(std::size(std::declval<Container>()))>;
+
+template <typename From, size_t FromExtent, typename To, size_t ToExtent>
+using EnableIfLegalSpanConversion =
+ std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
+ IsLegalDataConversion<From, To>::value>;
+
+// SFINAE check if Array can be converted to a span<T>.
+template <typename Array, size_t N, typename T, size_t Extent>
+using EnableIfSpanCompatibleArray =
+ std::enable_if_t<(Extent == dynamic_extent || Extent == N) &&
+ ContainerHasConvertibleData<Array, T>::value>;
+
+// SFINAE check if Container can be converted to a span<T>.
+template <typename Container, typename T>
+using EnableIfSpanCompatibleContainer =
+ std::enable_if_t<!internal::IsSpan<Container>::value &&
+ !internal::IsStdArray<Container>::value &&
+ !internal::IsCArray<Container>::value &&
+ ContainerHasConvertibleData<Container, T>::value &&
+ ContainerHasIntegralSize<Container>::value>;
+
+} // namespace internal
+
+// A span is a value type that represents an array of elements of type T. Since
+// it only consists of a pointer to memory with an associated size, it is very
+// light-weight. It is cheap to construct, copy, move and use spans, so that
+// users are encouraged to use it as a pass-by-value parameter. A span does not
+// own the underlying memory, so care must be taken to ensure that a span does
+// not outlive the backing store.
+//
+// span is somewhat analogous to std::string_view, but with arbitrary element
+// types, allowing mutation if T is non-const.
+//
+// span is implicitly convertible from C++ arrays, as well as most [1]
+// container-like types that provide a data() and size() method (such as
+// std::vector<T>). A mutable span<T> can also be implicitly converted to an
+// immutable span<const T>.
+//
+// Consider using a span for functions that take a data pointer and size
+// parameter: it allows the function to still act on an array-like type, while
+// allowing the caller code to be a bit more concise.
+//
+// For read-only data access pass a span<const T>: the caller can supply either
+// a span<const T> or a span<T>, while the callee will have a read-only view.
+// For read-write access a mutable span<T> is required.
+//
+// Without span:
+// Read-Only:
+// // std::string HexEncode(const uint8_t* data, size_t size);
+// std::vector<uint8_t> data_buffer = GenerateData();
+// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
+//
+// Mutable:
+// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
+// char str_buffer[100];
+// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
+//
+// With span:
+// Read-Only:
+// // std::string HexEncode(base::span<const uint8_t> data);
+// std::vector<uint8_t> data_buffer = GenerateData();
+// std::string r = HexEncode(data_buffer);
+//
+// Mutable:
+// // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
+// char str_buffer[100];
+// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
+//
+// Spans with "const" and pointers
+// -------------------------------
+//
+// Const and pointers can get confusing. Here are vectors of pointers and their
+// corresponding spans:
+//
+// const std::vector<int*> => base::span<int* const>
+// std::vector<const int*> => base::span<const int*>
+// const std::vector<const int*> => base::span<const int* const>
+//
+// Differences from the working group proposal
+// -------------------------------------------
+//
+// https://wg21.link/P0122 is the latest working group proposal, Chromium
+// currently implements R7. Differences between the proposal and the
+// implementation are documented in subsections below.
+//
+// Differences from [span.objectrep]:
+// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
+// std::byte
+//
+// Differences in constants and types:
+// - index_type is aliased to size_t
+//
+// Differences from [span.sub]:
+// - using size_t instead of ptrdiff_t for indexing
+//
+// Differences from [span.obs]:
+// - using size_t instead of ptrdiff_t to represent size()
+//
+// Differences from [span.elem]:
+// - using size_t instead of ptrdiff_t for indexing
+//
+// Furthermore, all constructors and methods are marked noexcept due to the lack
+// of exceptions in Chromium.
+//
+// Due to the lack of class template argument deduction guides in C++14
+// appropriate make_span() utility functions are provided.
+
+// [span], class template span
+template <typename T, size_t Extent>
+class span {
+ public:
+ using element_type = T;
+ using value_type = std::remove_cv_t<T>;
+ using index_type = size_t;
+ using difference_type = ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+ using iterator = T*;
+ using const_iterator = const T*;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ static constexpr index_type extent = Extent;
+
+ // [span.cons], span constructors, copy, assignment, and destructor
+ constexpr span() noexcept : data_(nullptr), size_(0) {
+ static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
+ }
+
+ constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) {
+ CHECK(Extent == dynamic_extent || Extent == size);
+ }
+
+ // Artificially templatized to break ambiguity for span(ptr, 0).
+ template <typename = void>
+ constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(begin <= end);
+ }
+
+ template <
+ size_t N,
+ typename = internal::EnableIfSpanCompatibleArray<T (&)[N], N, T, Extent>>
+ constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
+
+ template <
+ size_t N,
+ typename = internal::
+ EnableIfSpanCompatibleArray<std::array<value_type, N>&, N, T, Extent>>
+ constexpr span(std::array<value_type, N>& array) noexcept
+ : span(std::data(array), N) {}
+
+ template <size_t N,
+ typename = internal::EnableIfSpanCompatibleArray<
+ const std::array<value_type, N>&,
+ N,
+ T,
+ Extent>>
+ constexpr span(const std::array<value_type, N>& array) noexcept
+ : span(std::data(array), N) {}
+
+ // Conversion from a container that has compatible std::data() and integral
+ // std::size().
+ template <typename Container,
+ typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
+ constexpr span(Container& container) noexcept
+ : span(std::data(container), std::size(container)) {}
+
+ template <
+ typename Container,
+ typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
+ span(const Container& container) noexcept
+ : span(std::data(container), std::size(container)) {}
+
+ constexpr span(const span& other) noexcept = default;
+
+ // Conversions from spans of compatible types and extents: this allows a
+ // span<T> to be seamlessly used as a span<const T>, but not the other way
+ // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
+ template <
+ typename U,
+ size_t OtherExtent,
+ typename =
+ internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
+ constexpr span(const span<U, OtherExtent>& other)
+ : span(other.data(), other.size()) {}
+
+ constexpr span& operator=(const span& other) noexcept = default;
+ ~span() noexcept = default;
+
+ // [span.sub], span subviews
+ template <size_t Count>
+ constexpr span<T, Count> first() const noexcept {
+ static_assert(Extent == dynamic_extent || Count <= Extent,
+ "Count must not exceed Extent");
+ CHECK(Extent != dynamic_extent || Count <= size());
+ return {data(), Count};
+ }
+
+ template <size_t Count>
+ constexpr span<T, Count> last() const noexcept {
+ static_assert(Extent == dynamic_extent || Count <= Extent,
+ "Count must not exceed Extent");
+ CHECK(Extent != dynamic_extent || Count <= size());
+ return {data() + (size() - Count), Count};
+ }
+
+ template <size_t Offset, size_t Count = dynamic_extent>
+ constexpr span<T,
+ (Count != dynamic_extent
+ ? Count
+ : (Extent != dynamic_extent ? Extent - Offset
+ : dynamic_extent))>
+ subspan() const noexcept {
+ static_assert(Extent == dynamic_extent || Offset <= Extent,
+ "Offset must not exceed Extent");
+ static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
+ Count <= Extent - Offset,
+ "Count must not exceed Extent - Offset");
+ CHECK(Extent != dynamic_extent || Offset <= size());
+ CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
+ Count <= size() - Offset);
+ return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
+ }
+
+ constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(count <= size());
+ return {data(), count};
+ }
+
+ constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(count <= size());
+ return {data() + (size() - count), count};
+ }
+
+ constexpr span<T, dynamic_extent> subspan(size_t offset,
+ size_t count = dynamic_extent) const
+ noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(offset <= size());
+ CHECK(count == dynamic_extent || count <= size() - offset);
+ return {data() + offset, count != dynamic_extent ? count : size() - offset};
+ }
+
+ // [span.obs], span observers
+ constexpr size_t size() const noexcept { return size_; }
+ constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
+ constexpr bool empty() const noexcept { return size() == 0; }
+
+ // [span.elem], span element access
+ constexpr T& operator[](size_t idx) const noexcept {
+ // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
+ CHECK(idx < size());
+ return *(data() + idx);
+ }
+
+ constexpr T& operator()(size_t idx) const noexcept {
+ // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
+ CHECK(idx < size());
+ return *(data() + idx);
+ }
+
+ constexpr T* data() const noexcept { return data_; }
+
+ // [span.iter], span iterator support
+ constexpr iterator begin() const noexcept { return data(); }
+ constexpr iterator end() const noexcept { return data() + size(); }
+
+ constexpr const_iterator cbegin() const noexcept { return begin(); }
+ constexpr const_iterator cend() const noexcept { return end(); }
+
+ constexpr reverse_iterator rbegin() const noexcept {
+ return reverse_iterator(end());
+ }
+ constexpr reverse_iterator rend() const noexcept {
+ return reverse_iterator(begin());
+ }
+
+ constexpr const_reverse_iterator crbegin() const noexcept {
+ return const_reverse_iterator(cend());
+ }
+ constexpr const_reverse_iterator crend() const noexcept {
+ return const_reverse_iterator(cbegin());
+ }
+
+ private:
+ T* data_;
+ size_t size_;
+};
+
+// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
+// definition is required.
+template <class T, size_t Extent>
+constexpr size_t span<T, Extent>::extent;
+
+// [span.comparison], span comparison operators
+// Relational operators. Equality is a element-wise comparison.
+template <typename T, size_t X, typename U, size_t Y>
+constexpr bool operator==(span<T, X> lhs, span<U, Y> rhs) noexcept {
+ return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend());
+}
+
+template <typename T, size_t X, typename U, size_t Y>
+constexpr bool operator!=(span<T, X> lhs, span<U, Y> rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+template <typename T, size_t X, typename U, size_t Y>
+constexpr bool operator<(span<T, X> lhs, span<U, Y> rhs) noexcept {
+ return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(),
+ rhs.cend());
+}
+
+template <typename T, size_t X, typename U, size_t Y>
+constexpr bool operator<=(span<T, X> lhs, span<U, Y> rhs) noexcept {
+ return !(rhs < lhs);
+}
+
+template <typename T, size_t X, typename U, size_t Y>
+constexpr bool operator>(span<T, X> lhs, span<U, Y> rhs) noexcept {
+ return rhs < lhs;
+}
+
+template <typename T, size_t X, typename U, size_t Y>
+constexpr bool operator>=(span<T, X> lhs, span<U, Y> rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+// [span.objectrep], views of object representation
+template <typename T, size_t X>
+span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+as_bytes(span<T, X> s) noexcept {
+ return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
+}
+
+template <typename T,
+ size_t X,
+ typename = std::enable_if_t<!std::is_const<T>::value>>
+span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+as_writable_bytes(span<T, X> s) noexcept {
+ return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
+}
+
+// Type-deducing helpers for constructing a span.
+template <typename T>
+constexpr span<T> make_span(T* data, size_t size) noexcept {
+ return {data, size};
+}
+
+template <typename T>
+constexpr span<T> make_span(T* begin, T* end) noexcept {
+ return {begin, end};
+}
+
+template <typename T, size_t N>
+constexpr span<T, N> make_span(T (&array)[N]) noexcept {
+ return array;
+}
+
+template <typename T, size_t N>
+constexpr span<T, N> make_span(std::array<T, N>& array) noexcept {
+ return array;
+}
+
+template <typename T, size_t N>
+constexpr span<const T, N> make_span(const std::array<T, N>& array) noexcept {
+ return array;
+}
+
+template <typename Container,
+ typename T = typename Container::value_type,
+ typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
+constexpr span<T> make_span(Container& container) noexcept {
+ return container;
+}
+
+template <
+ typename Container,
+ typename T = const typename Container::value_type,
+ typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
+constexpr span<T> make_span(const Container& container) noexcept {
+ return container;
+}
+
+template <typename T, size_t X>
+constexpr span<T, X> make_span(const span<T, X>& span) noexcept {
+ return span;
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_SPAN_H_
diff --git a/gn/base/containers/stack.h b/gn/src/base/containers/stack.h
index 1aaa8793c7c..1aaa8793c7c 100644
--- a/gn/base/containers/stack.h
+++ b/gn/src/base/containers/stack.h
diff --git a/gn/src/base/containers/vector_buffer.h b/gn/src/base/containers/vector_buffer.h
new file mode 100644
index 00000000000..7447126ca99
--- /dev/null
+++ b/gn/src/base/containers/vector_buffer.h
@@ -0,0 +1,163 @@
+// Copyright 2017 The Chromium 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 BASE_CONTAINERS_VECTOR_BUFFERS_H_
+#define BASE_CONTAINERS_VECTOR_BUFFERS_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <type_traits>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace base {
+namespace internal {
+
+// Internal implementation detail of base/containers.
+//
+// Implements a vector-like buffer that holds a certain capacity of T. Unlike
+// std::vector, VectorBuffer never constructs or destructs its arguments, and
+// can't change sizes. But it does implement templates to assist in efficient
+// moving and destruction of those items manually.
+//
+// In particular, the destructor function does not iterate over the items if
+// there is no destructor. Moves should be implemented as a memcpy/memmove for
+// trivially copyable objects (POD) otherwise, it should be a std::move if
+// possible, and as a last resort it falls back to a copy. This behavior is
+// similar to std::vector.
+//
+// No special consideration is done for noexcept move constructors since
+// we compile without exceptions.
+//
+// The current API does not support moving overlapping ranges.
+template <typename T>
+class VectorBuffer {
+ public:
+ constexpr VectorBuffer() = default;
+
+#if defined(__clang__)
+ // This constructor converts an uninitialized void* to a T* which triggers
+ // clang Control Flow Integrity. Since this is as-designed, disable.
+ __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
+#endif
+ VectorBuffer(size_t count)
+ : buffer_(reinterpret_cast<T*>(malloc(sizeof(T) * count))),
+ capacity_(count) {
+ }
+ VectorBuffer(VectorBuffer&& other) noexcept
+ : buffer_(other.buffer_), capacity_(other.capacity_) {
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ }
+
+ ~VectorBuffer() { free(buffer_); }
+
+ VectorBuffer& operator=(VectorBuffer&& other) {
+ free(buffer_);
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ return *this;
+ }
+
+ size_t capacity() const { return capacity_; }
+
+ T& operator[](size_t i) { return buffer_[i]; }
+ const T& operator[](size_t i) const { return buffer_[i]; }
+ T* begin() { return buffer_; }
+ T* end() { return &buffer_[capacity_]; }
+
+ // DestructRange ------------------------------------------------------------
+
+ // Trivially destructible objects need not have their destructors called.
+ template <typename T2 = T,
+ typename std::enable_if<std::is_trivially_destructible<T2>::value,
+ int>::type = 0>
+ void DestructRange(T* begin, T* end) {}
+
+ // Non-trivially destructible objects must have their destructors called
+ // individually.
+ template <typename T2 = T,
+ typename std::enable_if<!std::is_trivially_destructible<T2>::value,
+ int>::type = 0>
+ void DestructRange(T* begin, T* end) {
+ while (begin != end) {
+ begin->~T();
+ begin++;
+ }
+ }
+
+ // MoveRange ----------------------------------------------------------------
+ //
+ // The destructor will be called (as necessary) for all moved types. The
+ // ranges must not overlap.
+ //
+ // The parameters and begin and end (one past the last) of the input buffer,
+ // and the address of the first element to copy to. There must be sufficient
+ // room in the destination for all items in the range [begin, end).
+
+ // Trivially copyable types can use memcpy. trivially copyable implies
+ // that there is a trivial destructor as we don't have to call it.
+ template <typename T2 = T,
+ typename std::enable_if<std::is_trivially_copyable<T2>::value,
+ int>::type = 0>
+ static void MoveRange(T* from_begin, T* from_end, T* to) {
+ DCHECK(!RangesOverlap(from_begin, from_end, to));
+ memcpy(to, from_begin, (from_end - from_begin) * sizeof(T));
+ }
+
+ // Not trivially copyable, but movable: call the move constructor and
+ // destruct the original.
+ template <typename T2 = T,
+ typename std::enable_if<std::is_move_constructible<T2>::value &&
+ !std::is_trivially_copyable<T2>::value,
+ int>::type = 0>
+ static void MoveRange(T* from_begin, T* from_end, T* to) {
+ DCHECK(!RangesOverlap(from_begin, from_end, to));
+ while (from_begin != from_end) {
+ new (to) T(std::move(*from_begin));
+ from_begin->~T();
+ from_begin++;
+ to++;
+ }
+ }
+
+ // Not movable, not trivially copyable: call the copy constructor and
+ // destruct the original.
+ template <typename T2 = T,
+ typename std::enable_if<!std::is_move_constructible<T2>::value &&
+ !std::is_trivially_copyable<T2>::value,
+ int>::type = 0>
+ static void MoveRange(T* from_begin, T* from_end, T* to) {
+ DCHECK(!RangesOverlap(from_begin, from_end, to));
+ while (from_begin != from_end) {
+ new (to) T(*from_begin);
+ from_begin->~T();
+ from_begin++;
+ to++;
+ }
+ }
+
+ private:
+ static bool RangesOverlap(const T* from_begin,
+ const T* from_end,
+ const T* to) {
+ return !(to >= from_end || to + (from_end - from_begin) <= from_begin);
+ }
+
+ T* buffer_ = nullptr;
+ size_t capacity_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorBuffer);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_
diff --git a/gn/src/base/environment.cc b/gn/src/base/environment.cc
new file mode 100644
index 00000000000..427e3e53adb
--- /dev/null
+++ b/gn/src/base/environment.cc
@@ -0,0 +1,243 @@
+// 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 "base/environment.h"
+
+#include <stddef.h>
+
+#include <string_view>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <stdlib.h>
+#endif
+
+namespace base {
+
+namespace {
+
+class EnvironmentImpl : public Environment {
+ public:
+ bool GetVar(std::string_view variable_name, std::string* result) override {
+ if (GetVarImpl(variable_name, result))
+ return true;
+
+ // Some commonly used variable names are uppercase while others
+ // are lowercase, which is inconsistent. Let's try to be helpful
+ // and look for a variable name with the reverse case.
+ // I.e. HTTP_PROXY may be http_proxy for some users/systems.
+ char first_char = variable_name[0];
+ std::string alternate_case_var;
+ if (IsAsciiLower(first_char))
+ alternate_case_var = ToUpperASCII(variable_name);
+ else if (IsAsciiUpper(first_char))
+ alternate_case_var = ToLowerASCII(variable_name);
+ else
+ return false;
+ return GetVarImpl(alternate_case_var, result);
+ }
+
+ bool SetVar(std::string_view variable_name,
+ const std::string& new_value) override {
+ return SetVarImpl(variable_name, new_value);
+ }
+
+ bool UnSetVar(std::string_view variable_name) override {
+ return UnSetVarImpl(variable_name);
+ }
+
+ private:
+ bool GetVarImpl(std::string_view variable_name, std::string* result) {
+#if defined(OS_WIN)
+ DWORD value_length = ::GetEnvironmentVariable(
+ reinterpret_cast<LPCWSTR>(UTF8ToUTF16(variable_name).c_str()), nullptr,
+ 0);
+ if (value_length == 0)
+ return false;
+ if (result) {
+ std::unique_ptr<char16_t[]> value(new char16_t[value_length]);
+ ::GetEnvironmentVariable(
+ reinterpret_cast<LPCWSTR>(UTF8ToUTF16(variable_name).c_str()),
+ reinterpret_cast<LPWSTR>(value.get()), value_length);
+ *result = UTF16ToUTF8(value.get());
+ }
+ return true;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ const char* env_value = getenv(variable_name.data());
+ if (!env_value)
+ return false;
+ // Note that the variable may be defined but empty.
+ if (result)
+ *result = env_value;
+ return true;
+#endif
+ }
+
+ bool SetVarImpl(std::string_view variable_name,
+ const std::string& new_value) {
+#if defined(OS_WIN)
+ // On success, a nonzero value is returned.
+ return !!SetEnvironmentVariable(
+ reinterpret_cast<LPCWSTR>(UTF8ToUTF16(variable_name).c_str()),
+ reinterpret_cast<LPCWSTR>(UTF8ToUTF16(new_value).c_str()));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On success, zero is returned.
+ return !setenv(variable_name.data(), new_value.c_str(), 1);
+#endif
+ }
+
+ bool UnSetVarImpl(std::string_view variable_name) {
+#if defined(OS_WIN)
+ // On success, a nonzero value is returned.
+ return !!SetEnvironmentVariable(
+ reinterpret_cast<LPCWSTR>(UTF8ToUTF16(variable_name).c_str()), nullptr);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On success, zero is returned.
+ return !unsetenv(variable_name.data());
+#endif
+ }
+};
+
+// Parses a null-terminated input string of an environment block. The key is
+// placed into the given string, and the total length of the line, including
+// the terminating null, is returned.
+size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
+ NativeEnvironmentString* key) {
+ // Skip to the equals or end of the string, this is the key.
+ size_t cur = 0;
+ while (input[cur] && input[cur] != '=')
+ cur++;
+ *key = NativeEnvironmentString(&input[0], cur);
+
+ // Now just skip to the end of the string.
+ while (input[cur])
+ cur++;
+ return cur + 1;
+}
+
+} // namespace
+
+namespace env_vars {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// On Posix systems, this variable contains the location of the user's home
+// directory. (e.g, /home/username/).
+const char kHome[] = "HOME";
+#endif
+
+} // namespace env_vars
+
+Environment::~Environment() = default;
+
+// static
+std::unique_ptr<Environment> Environment::Create() {
+ return std::make_unique<EnvironmentImpl>();
+}
+
+bool Environment::HasVar(std::string_view variable_name) {
+ return GetVar(variable_name, nullptr);
+}
+
+#if defined(OS_WIN)
+
+std::u16string AlterEnvironment(const char16_t* env,
+ const EnvironmentMap& changes) {
+ std::u16string result;
+
+ // First copy all unmodified values to the output.
+ size_t cur_env = 0;
+ std::u16string key;
+ while (env[cur_env]) {
+ const char16_t* line = &env[cur_env];
+ size_t line_length = ParseEnvLine(line, &key);
+
+ // Keep only values not specified in the change vector.
+ EnvironmentMap::const_iterator found_change = changes.find(key);
+ if (found_change == changes.end())
+ result.append(line, line_length);
+
+ cur_env += line_length;
+ }
+
+ // Now append all modified and new values.
+ for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end();
+ ++i) {
+ if (!i->second.empty()) {
+ result.append(i->first);
+ result.push_back('=');
+ result.append(i->second);
+ result.push_back(0);
+ }
+ }
+
+ // An additional null marks the end of the list. We always need a double-null
+ // in case nothing was added above.
+ if (result.empty())
+ result.push_back(0);
+ result.push_back(0);
+ return result;
+}
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
+ const EnvironmentMap& changes) {
+ std::string value_storage; // Holds concatenated null-terminated strings.
+ std::vector<size_t> result_indices; // Line indices into value_storage.
+
+ // First build up all of the unchanged environment strings. These are
+ // null-terminated of the form "key=value".
+ std::string key;
+ for (size_t i = 0; env[i]; i++) {
+ size_t line_length = ParseEnvLine(env[i], &key);
+
+ // Keep only values not specified in the change vector.
+ EnvironmentMap::const_iterator found_change = changes.find(key);
+ if (found_change == changes.end()) {
+ result_indices.push_back(value_storage.size());
+ value_storage.append(env[i], line_length);
+ }
+ }
+
+ // Now append all modified and new values.
+ for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end();
+ ++i) {
+ if (!i->second.empty()) {
+ result_indices.push_back(value_storage.size());
+ value_storage.append(i->first);
+ value_storage.push_back('=');
+ value_storage.append(i->second);
+ value_storage.push_back(0);
+ }
+ }
+
+ size_t pointer_count_required =
+ result_indices.size() + 1 + // Null-terminated array of pointers.
+ (value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
+ std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
+
+ // The string storage goes after the array of pointers.
+ char* storage_data =
+ reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
+ if (!value_storage.empty())
+ memcpy(storage_data, value_storage.data(), value_storage.size());
+
+ // Fill array of pointers at the beginning of the result.
+ for (size_t i = 0; i < result_indices.size(); i++)
+ result[i] = &storage_data[result_indices[i]];
+ result[result_indices.size()] = 0; // Null terminator.
+
+ return result;
+}
+
+#endif // OS_WIN
+
+} // namespace base
diff --git a/gn/src/base/environment.h b/gn/src/base/environment.h
new file mode 100644
index 00000000000..f9a2f558d3e
--- /dev/null
+++ b/gn/src/base/environment.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef BASE_ENVIRONMENT_H_
+#define BASE_ENVIRONMENT_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "util/build_config.h"
+
+namespace base {
+
+namespace env_vars {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+extern const char kHome[];
+#endif
+
+} // namespace env_vars
+
+class Environment {
+ public:
+ virtual ~Environment();
+
+ // Returns the appropriate platform-specific instance.
+ static std::unique_ptr<Environment> Create();
+
+ // Gets an environment variable's value and stores it in |result|.
+ // Returns false if the key is unset.
+ virtual bool GetVar(std::string_view variable_name, std::string* result) = 0;
+
+ // Syntactic sugar for GetVar(variable_name, nullptr);
+ virtual bool HasVar(std::string_view variable_name);
+
+ // Returns true on success, otherwise returns false.
+ virtual bool SetVar(std::string_view variable_name,
+ const std::string& new_value) = 0;
+
+ // Returns true on success, otherwise returns false.
+ virtual bool UnSetVar(std::string_view variable_name) = 0;
+};
+
+#if defined(OS_WIN)
+
+typedef std::u16string NativeEnvironmentString;
+typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
+ EnvironmentMap;
+
+// Returns a modified environment vector constructed from the given environment
+// and the list of changes given in |changes|. Each key in the environment is
+// matched against the first element of the pairs. In the event of a match, the
+// value is replaced by the second of the pair, unless the second is empty, in
+// which case the key-value is removed.
+//
+// This Windows version takes and returns a Windows-style environment block
+// which is a concatenated list of null-terminated 16-bit strings. The end is
+// marked by a double-null terminator. The size of the returned string will
+// include the terminators.
+std::u16string AlterEnvironment(const char16_t* env,
+ const EnvironmentMap& changes);
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+typedef std::string NativeEnvironmentString;
+typedef std::map<NativeEnvironmentString, NativeEnvironmentString>
+ EnvironmentMap;
+
+// See general comments for the Windows version above.
+//
+// This Posix version takes and returns a Posix-style environment block, which
+// is a null-terminated list of pointers to null-terminated strings. The
+// returned array will have appended to it the storage for the array itself so
+// there is only one pointer to manage, but this means that you can't copy the
+// array without keeping the original around.
+std::unique_ptr<char*[]> AlterEnvironment(const char* const* env,
+ const EnvironmentMap& changes);
+
+#endif
+
+} // namespace base
+
+#endif // BASE_ENVIRONMENT_H_
diff --git a/gn/src/base/files/file.cc b/gn/src/base/files/file.cc
new file mode 100644
index 00000000000..d82911a895d
--- /dev/null
+++ b/gn/src/base/files/file.cc
@@ -0,0 +1,116 @@
+// 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.
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "util/build_config.h"
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <errno.h>
+#endif
+
+namespace base {
+
+File::Info::Info() : size(0), is_directory(false), is_symbolic_link(false) {}
+
+File::Info::~Info() = default;
+
+File::File()
+ : error_details_(FILE_ERROR_FAILED), created_(false) {}
+
+File::File(const FilePath& path, uint32_t flags)
+ : error_details_(FILE_OK), created_(false) {
+ Initialize(path, flags);
+}
+
+File::File(PlatformFile platform_file)
+ : file_(platform_file),
+ error_details_(FILE_OK),
+ created_(false) {
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ DCHECK_GE(platform_file, -1);
+#endif
+}
+
+File::File(Error error_details)
+ : error_details_(error_details), created_(false) {}
+
+File::File(File&& other)
+ : file_(other.TakePlatformFile()),
+ error_details_(other.error_details()),
+ created_(other.created()) {}
+
+File::~File() {
+ // Go through the AssertIOAllowed logic.
+ Close();
+}
+
+File& File::operator=(File&& other) {
+ Close();
+ SetPlatformFile(other.TakePlatformFile());
+ error_details_ = other.error_details();
+ created_ = other.created();
+ return *this;
+}
+
+void File::Initialize(const FilePath& path, uint32_t flags) {
+ if (path.ReferencesParent()) {
+#if defined(OS_WIN)
+ ::SetLastError(ERROR_ACCESS_DENIED);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ errno = EACCES;
+#else
+#error Unsupported platform
+#endif
+ error_details_ = FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+ DoInitialize(path, flags);
+}
+
+std::string File::ErrorToString(Error error) {
+ switch (error) {
+ case FILE_OK:
+ return "FILE_OK";
+ case FILE_ERROR_FAILED:
+ return "FILE_ERROR_FAILED";
+ case FILE_ERROR_IN_USE:
+ return "FILE_ERROR_IN_USE";
+ case FILE_ERROR_EXISTS:
+ return "FILE_ERROR_EXISTS";
+ case FILE_ERROR_NOT_FOUND:
+ return "FILE_ERROR_NOT_FOUND";
+ case FILE_ERROR_ACCESS_DENIED:
+ return "FILE_ERROR_ACCESS_DENIED";
+ case FILE_ERROR_TOO_MANY_OPENED:
+ return "FILE_ERROR_TOO_MANY_OPENED";
+ case FILE_ERROR_NO_MEMORY:
+ return "FILE_ERROR_NO_MEMORY";
+ case FILE_ERROR_NO_SPACE:
+ return "FILE_ERROR_NO_SPACE";
+ case FILE_ERROR_NOT_A_DIRECTORY:
+ return "FILE_ERROR_NOT_A_DIRECTORY";
+ case FILE_ERROR_INVALID_OPERATION:
+ return "FILE_ERROR_INVALID_OPERATION";
+ case FILE_ERROR_SECURITY:
+ return "FILE_ERROR_SECURITY";
+ case FILE_ERROR_ABORT:
+ return "FILE_ERROR_ABORT";
+ case FILE_ERROR_NOT_A_FILE:
+ return "FILE_ERROR_NOT_A_FILE";
+ case FILE_ERROR_NOT_EMPTY:
+ return "FILE_ERROR_NOT_EMPTY";
+ case FILE_ERROR_INVALID_URL:
+ return "FILE_ERROR_INVALID_URL";
+ case FILE_ERROR_IO:
+ return "FILE_ERROR_IO";
+ case FILE_ERROR_MAX:
+ break;
+ }
+
+ NOTREACHED();
+ return "";
+}
+
+} // namespace base
diff --git a/gn/src/base/files/file.h b/gn/src/base/files/file.h
new file mode 100644
index 00000000000..dead6bd2ec4
--- /dev/null
+++ b/gn/src/base/files/file.h
@@ -0,0 +1,289 @@
+// 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 BASE_FILES_FILE_H_
+#define BASE_FILES_FILE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/platform_file.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "util/build_config.h"
+#include "util/ticks.h"
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/stat.h>
+#endif
+
+namespace base {
+
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
+ defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21
+typedef struct stat stat_wrapper_t;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+typedef struct stat64 stat_wrapper_t;
+#endif
+
+// Thin wrapper around an OS-level file.
+// Note that this class does not provide any support for asynchronous IO.
+//
+// Note about const: this class does not attempt to determine if the underlying
+// file system object is affected by a particular method in order to consider
+// that method const or not. Only methods that deal with member variables in an
+// obvious non-modifying way are marked as const. Any method that forward calls
+// to the OS is not considered const, even if there is no apparent change to
+// member variables.
+class File {
+ public:
+ // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one
+ // of the five (possibly combining with other flags) when opening or creating
+ // a file.
+ enum Flags {
+ FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
+ FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
+ FLAG_READ = 1 << 4,
+ FLAG_WRITE = 1 << 5,
+ };
+
+ // This enum has been recorded in multiple histograms using PlatformFileError
+ // enum. If the order of the fields needs to change, please ensure that those
+ // histograms are obsolete or have been moved to a different enum.
+ //
+ // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a
+ // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser
+ // policy doesn't allow the operation to be executed.
+ enum Error {
+ FILE_OK = 0,
+ FILE_ERROR_FAILED = -1,
+ FILE_ERROR_IN_USE = -2,
+ FILE_ERROR_EXISTS = -3,
+ FILE_ERROR_NOT_FOUND = -4,
+ FILE_ERROR_ACCESS_DENIED = -5,
+ FILE_ERROR_TOO_MANY_OPENED = -6,
+ FILE_ERROR_NO_MEMORY = -7,
+ FILE_ERROR_NO_SPACE = -8,
+ FILE_ERROR_NOT_A_DIRECTORY = -9,
+ FILE_ERROR_INVALID_OPERATION = -10,
+ FILE_ERROR_SECURITY = -11,
+ FILE_ERROR_ABORT = -12,
+ FILE_ERROR_NOT_A_FILE = -13,
+ FILE_ERROR_NOT_EMPTY = -14,
+ FILE_ERROR_INVALID_URL = -15,
+ FILE_ERROR_IO = -16,
+ // Put new entries here and increment FILE_ERROR_MAX.
+ FILE_ERROR_MAX = -17
+ };
+
+ // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux.
+ enum Whence { FROM_BEGIN = 0, FROM_CURRENT = 1, FROM_END = 2 };
+
+ // Used to hold information about a given file.
+ // If you add more fields to this structure (platform-specific fields are OK),
+ // make sure to update all functions that use it in file_util_{win|posix}.cc,
+ // too, and the ParamTraits<base::File::Info> implementation in
+ // ipc/ipc_message_utils.cc.
+ struct Info {
+ Info();
+ ~Info();
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // Fills this struct with values from |stat_info|.
+ void FromStat(const stat_wrapper_t& stat_info);
+#endif
+
+ // The size of the file in bytes. Undefined when is_directory is true.
+ int64_t size;
+
+ // True if the file corresponds to a directory.
+ bool is_directory;
+
+ // True if the file corresponds to a symbolic link. For Windows currently
+ // not supported and thus always false.
+ bool is_symbolic_link;
+
+ // The last modified time of a file.
+ Ticks last_modified;
+
+ // The last accessed time of a file.
+ Ticks last_accessed;
+
+ // The creation time of a file.
+ Ticks creation_time;
+ };
+
+ File();
+
+ // Creates or opens the given file. This will fail with 'access denied' if the
+ // |path| contains path traversal ('..') components.
+ File(const FilePath& path, uint32_t flags);
+
+ // Takes ownership of |platform_file|.
+ explicit File(PlatformFile platform_file);
+
+ // Creates an object with a specific error_details code.
+ explicit File(Error error_details);
+
+ File(File&& other);
+
+ ~File();
+
+ File& operator=(File&& other);
+
+ // Creates or opens the given file.
+ void Initialize(const FilePath& path, uint32_t flags);
+
+ // Returns |true| if the handle / fd wrapped by this object is valid. This
+ // method doesn't interact with the file system (and is safe to be called from
+ // ThreadRestrictions::SetIOAllowed(false) threads).
+ bool IsValid() const;
+
+ // Returns true if a new file was created (or an old one truncated to zero
+ // length to simulate a new file, which can happen with
+ // FLAG_CREATE_ALWAYS), and false otherwise.
+ bool created() const { return created_; }
+
+ // Returns the OS result of opening this file. Note that the way to verify
+ // the success of the operation is to use IsValid(), not this method:
+ // File file(path, flags);
+ // if (!file.IsValid())
+ // return;
+ Error error_details() const { return error_details_; }
+
+ PlatformFile GetPlatformFile() const;
+ PlatformFile TakePlatformFile();
+
+ // Destroying this object closes the file automatically.
+ void Close();
+
+ // Changes current position in the file to an |offset| relative to an origin
+ // defined by |whence|. Returns the resultant current position in the file
+ // (relative to the start) or -1 in case of error.
+ int64_t Seek(Whence whence, int64_t offset);
+
+ // Reads the given number of bytes (or until EOF is reached) starting with the
+ // given offset. Returns the number of bytes read, or -1 on error. Note that
+ // this function makes a best effort to read all data on all platforms, so it
+ // is not intended for stream oriented files but instead for cases when the
+ // normal expectation is that actually |size| bytes are read unless there is
+ // an error.
+ int Read(int64_t offset, char* data, int size);
+
+ // Same as above but without seek.
+ int ReadAtCurrentPos(char* data, int size);
+
+ // Reads the given number of bytes (or until EOF is reached) starting with the
+ // given offset, but does not make any effort to read all data on all
+ // platforms. Returns the number of bytes read, or -1 on error.
+ int ReadNoBestEffort(int64_t offset, char* data, int size);
+
+ // Same as above but without seek.
+ int ReadAtCurrentPosNoBestEffort(char* data, int size);
+
+ // Writes the given buffer into the file at the given offset, overwritting any
+ // data that was previously there. Returns the number of bytes written, or -1
+ // on error. Note that this function makes a best effort to write all data on
+ // all platforms. |data| can be nullptr when |size| is 0.
+ int Write(int64_t offset, const char* data, int size);
+
+ // Save as above but without seek.
+ int WriteAtCurrentPos(const char* data, int size);
+
+ // Save as above but does not make any effort to write all data on all
+ // platforms. Returns the number of bytes written, or -1 on error.
+ int WriteAtCurrentPosNoBestEffort(const char* data, int size);
+
+ // Returns the current size of this file, or a negative number on failure.
+ int64_t GetLength();
+
+ // Truncates the file to the given length. If |length| is greater than the
+ // current size of the file, the file is extended with zeros. If the file
+ // doesn't exist, |false| is returned.
+ bool SetLength(int64_t length);
+
+ // Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows:
+ // FlushFileBuffers).
+ // Calling Flush() does not guarantee file integrity and thus is not a valid
+ // substitute for file integrity checks and recovery codepaths for malformed
+ // files. It can also be *really* slow, so avoid blocking on Flush(),
+ // especially please don't block shutdown on Flush().
+ // Latency percentiles of Flush() across all platforms as of July 2016:
+ // 50 % > 5 ms
+ // 10 % > 58 ms
+ // 1 % > 357 ms
+ // 0.1 % > 1.8 seconds
+ // 0.01 % > 7.6 seconds
+ bool Flush();
+
+ // Returns some basic information for the given file.
+ bool GetInfo(Info* info);
+
+#if !defined(OS_FUCHSIA) // Fuchsia's POSIX API does not support file locking.
+
+ // Attempts to take an exclusive write lock on the file. Returns immediately
+ // (i.e. does not wait for another process to unlock the file). If the lock
+ // was obtained, the result will be FILE_OK. A lock only guarantees
+ // that other processes may not also take a lock on the same file with the
+ // same API - it may still be opened, renamed, unlinked, etc.
+ //
+ // Common semantics:
+ // * Locks are held by processes, but not inherited by child processes.
+ // * Locks are released by the OS on file close or process termination.
+ // * Locks are reliable only on local filesystems.
+ // * Duplicated file handles may also write to locked files.
+ // Windows-specific semantics:
+ // * Locks are mandatory for read/write APIs, advisory for mapping APIs.
+ // * Within a process, locking the same file (by the same or new handle)
+ // will fail.
+ // POSIX-specific semantics:
+ // * Locks are advisory only.
+ // * Within a process, locking the same file (by the same or new handle)
+ // will succeed.
+ // * Closing any descriptor on a given file releases the lock.
+ Error Lock();
+
+ // Unlock a file previously locked.
+ Error Unlock();
+
+#endif // !defined(OS_FUCHSIA)
+
+ // Returns a new object referencing this file for use within the current
+ // process.
+ File Duplicate() const;
+
+#if defined(OS_WIN)
+ static Error OSErrorToFileError(DWORD last_error);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ static Error OSErrorToFileError(int saved_errno);
+#endif
+
+ // Gets the last global error (errno or GetLastError()) and converts it to the
+ // closest base::File::Error equivalent via OSErrorToFileError(). The returned
+ // value is only trustworthy immediately after another base::File method
+ // fails. base::File never resets the global error to zero.
+ static Error GetLastFileError();
+
+ // Converts an error value to a human-readable form. Used for logging.
+ static std::string ErrorToString(Error error);
+
+ private:
+ // Creates or opens the given file. Only called if |path| has no
+ // traversal ('..') components.
+ void DoInitialize(const FilePath& path, uint32_t flags);
+
+ void SetPlatformFile(PlatformFile file);
+
+ ScopedPlatformFile file_;
+
+ Error error_details_;
+ bool created_;
+
+ DISALLOW_COPY_AND_ASSIGN(File);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_H_
diff --git a/gn/base/files/file_enumerator.cc b/gn/src/base/files/file_enumerator.cc
index 9dfb2ba04b5..9dfb2ba04b5 100644
--- a/gn/base/files/file_enumerator.cc
+++ b/gn/src/base/files/file_enumerator.cc
diff --git a/gn/base/files/file_enumerator.h b/gn/src/base/files/file_enumerator.h
index 81f757b445a..81f757b445a 100644
--- a/gn/base/files/file_enumerator.h
+++ b/gn/src/base/files/file_enumerator.h
diff --git a/gn/base/files/file_enumerator_posix.cc b/gn/src/base/files/file_enumerator_posix.cc
index 41d52b8afdd..41d52b8afdd 100644
--- a/gn/base/files/file_enumerator_posix.cc
+++ b/gn/src/base/files/file_enumerator_posix.cc
diff --git a/gn/src/base/files/file_enumerator_win.cc b/gn/src/base/files/file_enumerator_win.cc
new file mode 100644
index 00000000000..aa884ea8aed
--- /dev/null
+++ b/gn/src/base/files/file_enumerator_win.cc
@@ -0,0 +1,193 @@
+// 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 "base/files/file_enumerator.h"
+
+#include <shlwapi.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/win/win_util.h"
+
+namespace base {
+
+namespace {
+
+FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
+ const FilePath& root_path,
+ const FilePath::StringType& pattern) {
+ // MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy
+ // collects all files and filters them manually.
+ switch (policy) {
+ case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
+ return root_path.Append(pattern);
+ case FileEnumerator::FolderSearchPolicy::ALL:
+ return root_path.Append(u"*");
+ }
+ NOTREACHED();
+ return {};
+}
+
+} // namespace
+
+// FileEnumerator::FileInfo ----------------------------------------------------
+
+FileEnumerator::FileInfo::FileInfo() {
+ memset(&find_data_, 0, sizeof(find_data_));
+}
+
+bool FileEnumerator::FileInfo::IsDirectory() const {
+ return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+FilePath FileEnumerator::FileInfo::GetName() const {
+ return FilePath(reinterpret_cast<const char16_t*>(find_data_.cFileName));
+}
+
+int64_t FileEnumerator::FileInfo::GetSize() const {
+ ULARGE_INTEGER size;
+ size.HighPart = find_data_.nFileSizeHigh;
+ size.LowPart = find_data_.nFileSizeLow;
+ DCHECK_LE(size.QuadPart,
+ static_cast<ULONGLONG>(std::numeric_limits<int64_t>::max()));
+ return static_cast<int64_t>(size.QuadPart);
+}
+
+Ticks FileEnumerator::FileInfo::GetLastModifiedTime() const {
+ return *reinterpret_cast<const uint64_t*>(&find_data_.ftLastWriteTime);
+}
+
+// FileEnumerator --------------------------------------------------------------
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type)
+ : FileEnumerator(root_path,
+ recursive,
+ file_type,
+ FilePath::StringType(),
+ FolderSearchPolicy::MATCH_ONLY) {}
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern)
+ : FileEnumerator(root_path,
+ recursive,
+ file_type,
+ pattern,
+ FolderSearchPolicy::MATCH_ONLY) {}
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern,
+ FolderSearchPolicy folder_search_policy)
+ : recursive_(recursive),
+ file_type_(file_type),
+ pattern_(!pattern.empty() ? pattern : u"*"),
+ folder_search_policy_(folder_search_policy) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ memset(&find_data_, 0, sizeof(find_data_));
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() {
+ if (find_handle_ != INVALID_HANDLE_VALUE)
+ FindClose(find_handle_);
+}
+
+FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
+ if (!has_find_data_) {
+ NOTREACHED();
+ return FileInfo();
+ }
+ FileInfo ret;
+ memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
+ return ret;
+}
+
+FilePath FileEnumerator::Next() {
+ while (has_find_data_ || !pending_paths_.empty()) {
+ if (!has_find_data_) {
+ // The last find FindFirstFile operation is done, prepare a new one.
+ root_path_ = pending_paths_.top();
+ pending_paths_.pop();
+
+ // Start a new find operation.
+ const FilePath src =
+ BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
+ find_handle_ = FindFirstFileEx(ToWCharT(&src.value()),
+ FindExInfoBasic, // Omit short name.
+ &find_data_, FindExSearchNameMatch,
+ nullptr, FIND_FIRST_EX_LARGE_FETCH);
+ has_find_data_ = true;
+ } else {
+ // Search for the next file/directory.
+ if (!FindNextFile(find_handle_, &find_data_)) {
+ FindClose(find_handle_);
+ find_handle_ = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ if (INVALID_HANDLE_VALUE == find_handle_) {
+ has_find_data_ = false;
+
+ // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
+ // applies pattern for all subfolders.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
+ // This is reached when we have finished a directory and are advancing
+ // to the next one in the queue. We applied the pattern (if any) to the
+ // files in the root search directory, but for those directories which
+ // were matched, we want to enumerate all files inside them. This will
+ // happen when the handle is empty.
+ pattern_ = u"*";
+ }
+
+ continue;
+ }
+
+ const FilePath filename(reinterpret_cast<char16_t*>(find_data_.cFileName));
+ if (ShouldSkip(filename))
+ continue;
+
+ const bool is_dir =
+ (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ const FilePath abs_path = root_path_.Append(filename);
+
+ // Check if directory should be processed recursive.
+ if (is_dir && recursive_) {
+ // If |cur_file| is a directory, and we are doing recursive searching,
+ // add it to pending_paths_ so we scan it after we finish scanning this
+ // directory. However, don't do recursion through reparse points or we
+ // may end up with an infinite cycle.
+ DWORD attributes = GetFileAttributes(ToWCharT(&abs_path.value()));
+ if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ pending_paths_.push(abs_path);
+ }
+
+ if (IsTypeMatched(is_dir) && IsPatternMatched(filename))
+ return abs_path;
+ }
+ return FilePath();
+}
+
+bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
+ switch (folder_search_policy_) {
+ case FolderSearchPolicy::MATCH_ONLY:
+ // MATCH_ONLY policy filters by pattern on search request, so all found
+ // files already fits to pattern.
+ return true;
+ case FolderSearchPolicy::ALL:
+ // ALL policy enumerates all files, we need to check pattern match
+ // manually.
+ return PathMatchSpec(ToWCharT(&src.value()), ToWCharT(&pattern_)) == TRUE;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace base
diff --git a/gn/src/base/files/file_path.cc b/gn/src/base/files/file_path.cc
new file mode 100644
index 00000000000..4e986531b98
--- /dev/null
+++ b/gn/src/base/files/file_path.cc
@@ -0,0 +1,639 @@
+// 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 "base/files/file_path.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string_view>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_cftyperef.h"
+#include "base/third_party/icu/icu_utf.h"
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+namespace base {
+
+using StringType = FilePath::StringType;
+using StringViewType = FilePath::StringViewType;
+
+namespace {
+
+const char* const kCommonDoubleExtensionSuffixes[] = {"gz", "z", "bz2", "bz"};
+const char* const kCommonDoubleExtensions[] = {"user.js"};
+
+const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
+
+// If this FilePath contains a drive letter specification, returns the
+// position of the last character of the drive letter specification,
+// otherwise returns npos. This can only be true on Windows, when a pathname
+// begins with a letter followed by a colon. On other platforms, this always
+// returns npos.
+StringViewType::size_type FindDriveLetter(StringViewType path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ // This is dependent on an ASCII-based character set, but that's a
+ // reasonable assumption. iswalpha can be too inclusive here.
+ if (path.length() >= 2 && path[1] == L':' &&
+ ((path[0] >= L'A' && path[0] <= L'Z') ||
+ (path[0] >= L'a' && path[0] <= L'z'))) {
+ return 1;
+ }
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+ return StringType::npos;
+}
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+bool EqualDriveLetterCaseInsensitive(StringViewType a, StringViewType b) {
+ size_t a_letter_pos = FindDriveLetter(a);
+ size_t b_letter_pos = FindDriveLetter(b);
+
+ if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos)
+ return a == b;
+
+ StringViewType a_letter(a.substr(0, a_letter_pos + 1));
+ StringViewType b_letter(b.substr(0, b_letter_pos + 1));
+ if (!StartsWith(a_letter, b_letter, CompareCase::INSENSITIVE_ASCII))
+ return false;
+
+ StringViewType a_rest(a.substr(a_letter_pos + 1));
+ StringViewType b_rest(b.substr(b_letter_pos + 1));
+ return a_rest == b_rest;
+}
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+
+bool IsPathAbsolute(StringViewType path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ StringType::size_type letter = FindDriveLetter(path);
+ if (letter != StringType::npos) {
+ // Look for a separator right after the drive specification.
+ return path.length() > letter + 1 &&
+ FilePath::IsSeparator(path[letter + 1]);
+ }
+ // Look for a pair of leading separators.
+ return path.length() > 1 && FilePath::IsSeparator(path[0]) &&
+ FilePath::IsSeparator(path[1]);
+#else // FILE_PATH_USES_DRIVE_LETTERS
+ // Look for a separator in the first position.
+ return path.length() > 0 && FilePath::IsSeparator(path[0]);
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+}
+
+bool AreAllSeparators(const StringType& input) {
+ for (StringType::const_iterator it = input.begin(); it != input.end(); ++it) {
+ if (!FilePath::IsSeparator(*it))
+ return false;
+ }
+
+ return true;
+}
+
+// Find the position of the '.' that separates the extension from the rest
+// of the file name. The position is relative to BaseName(), not value().
+// Returns npos if it can't find an extension.
+StringType::size_type FinalExtensionSeparatorPosition(const StringType& path) {
+ // Special case "." and ".."
+ if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory)
+ return StringType::npos;
+
+ return path.rfind(FilePath::kExtensionSeparator);
+}
+
+// Same as above, but allow a second extension component of up to 4
+// characters when the rightmost extension component is a common double
+// extension (gz, bz2, Z). For example, foo.tar.gz or foo.tar.Z would have
+// extension components of '.tar.gz' and '.tar.Z' respectively.
+StringType::size_type ExtensionSeparatorPosition(const StringType& path) {
+ const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path);
+
+ // No extension, or the extension is the whole filename.
+ if (last_dot == StringType::npos || last_dot == 0U)
+ return last_dot;
+
+ const StringType::size_type penultimate_dot =
+ path.rfind(FilePath::kExtensionSeparator, last_dot - 1);
+ const StringType::size_type last_separator = path.find_last_of(
+ FilePath::kSeparators, last_dot - 1, FilePath::kSeparatorsLength - 1);
+
+ if (penultimate_dot == StringType::npos ||
+ (last_separator != StringType::npos &&
+ penultimate_dot < last_separator)) {
+ return last_dot;
+ }
+
+ for (size_t i = 0; i < std::size(kCommonDoubleExtensions); ++i) {
+ StringType extension(path, penultimate_dot + 1);
+ if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i]))
+ return penultimate_dot;
+ }
+
+ StringType extension(path, last_dot + 1);
+ for (size_t i = 0; i < std::size(kCommonDoubleExtensionSuffixes); ++i) {
+ if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) {
+ if ((last_dot - penultimate_dot) <= 5U &&
+ (last_dot - penultimate_dot) > 1U) {
+ return penultimate_dot;
+ }
+ }
+ }
+
+ return last_dot;
+}
+
+// Returns true if path is "", ".", or "..".
+bool IsEmptyOrSpecialCase(const StringType& path) {
+ // Special cases "", ".", and ".."
+ if (path.empty() || path == FilePath::kCurrentDirectory ||
+ path == FilePath::kParentDirectory) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+FilePath::FilePath() = default;
+
+FilePath::FilePath(const FilePath& that) = default;
+FilePath::FilePath(FilePath&& that) noexcept = default;
+
+FilePath::FilePath(StringViewType path) {
+ path_.assign(path);
+ StringType::size_type nul_pos = path_.find(kStringTerminator);
+ if (nul_pos != StringType::npos)
+ path_.erase(nul_pos, StringType::npos);
+}
+
+FilePath::~FilePath() = default;
+
+FilePath& FilePath::operator=(const FilePath& that) = default;
+
+FilePath& FilePath::operator=(FilePath&& that) = default;
+
+bool FilePath::operator==(const FilePath& that) const {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return EqualDriveLetterCaseInsensitive(this->path_, that.path_);
+#else // defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return path_ == that.path_;
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+}
+
+bool FilePath::operator!=(const FilePath& that) const {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return !EqualDriveLetterCaseInsensitive(this->path_, that.path_);
+#else // defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return path_ != that.path_;
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+}
+
+// static
+bool FilePath::IsSeparator(CharType character) {
+ for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
+ if (character == kSeparators[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void FilePath::GetComponents(std::vector<StringType>* components) const {
+ DCHECK(components);
+ if (!components)
+ return;
+ components->clear();
+ if (value().empty())
+ return;
+
+ std::vector<StringType> ret_val;
+ FilePath current = *this;
+ FilePath base;
+
+ // Capture path components.
+ while (current != current.DirName()) {
+ base = current.BaseName();
+ if (!AreAllSeparators(base.value()))
+ ret_val.push_back(base.value());
+ current = current.DirName();
+ }
+
+ // Capture root, if any.
+ base = current.BaseName();
+ if (!base.value().empty() && base.value() != kCurrentDirectory)
+ ret_val.push_back(current.BaseName().value());
+
+ // Capture drive letter, if any.
+ FilePath dir = current.DirName();
+ StringType::size_type letter = FindDriveLetter(dir.value());
+ if (letter != StringType::npos) {
+ ret_val.push_back(StringType(dir.value(), 0, letter + 1));
+ }
+
+ *components = std::vector<StringType>(ret_val.rbegin(), ret_val.rend());
+}
+
+bool FilePath::IsParent(const FilePath& child) const {
+ return AppendRelativePath(child, nullptr);
+}
+
+bool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const {
+ std::vector<StringType> parent_components;
+ std::vector<StringType> child_components;
+ GetComponents(&parent_components);
+ child.GetComponents(&child_components);
+
+ if (parent_components.empty() ||
+ parent_components.size() >= child_components.size())
+ return false;
+
+ std::vector<StringType>::const_iterator parent_comp =
+ parent_components.begin();
+ std::vector<StringType>::const_iterator child_comp = child_components.begin();
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ // Windows can access case sensitive filesystems, so component
+ // comparisions must be case sensitive, but drive letters are
+ // never case sensitive.
+ if ((FindDriveLetter(*parent_comp) != StringType::npos) &&
+ (FindDriveLetter(*child_comp) != StringType::npos)) {
+ if (!StartsWith(*parent_comp, *child_comp, CompareCase::INSENSITIVE_ASCII))
+ return false;
+ ++parent_comp;
+ ++child_comp;
+ }
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+
+ while (parent_comp != parent_components.end()) {
+ if (*parent_comp != *child_comp)
+ return false;
+ ++parent_comp;
+ ++child_comp;
+ }
+
+ if (path != nullptr) {
+ for (; child_comp != child_components.end(); ++child_comp) {
+ *path = path->Append(*child_comp);
+ }
+ }
+ return true;
+}
+
+// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
+// guaranteed to not modify their input strings, and in fact are implemented
+// differently in this regard on different platforms. Don't use them, but
+// adhere to their behavior.
+FilePath FilePath::DirName() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // The drive letter, if any, always needs to remain in the output. If there
+ // is no drive letter, as will always be the case on platforms which do not
+ // support drive letters, letter will be npos, or -1, so the comparisons and
+ // resizes below using letter will still be valid.
+ StringType::size_type letter = FindDriveLetter(new_path.path_);
+
+ StringType::size_type last_separator = new_path.path_.find_last_of(
+ kSeparators, StringType::npos, kSeparatorsLength - 1);
+ if (last_separator == StringType::npos) {
+ // path_ is in the current directory.
+ new_path.path_.resize(letter + 1);
+ } else if (last_separator == letter + 1) {
+ // path_ is in the root directory.
+ new_path.path_.resize(letter + 2);
+ } else if (last_separator == letter + 2 &&
+ IsSeparator(new_path.path_[letter + 1])) {
+ // path_ is in "//" (possibly with a drive letter); leave the double
+ // separator intact indicating alternate root.
+ new_path.path_.resize(letter + 3);
+ } else if (last_separator != 0) {
+ // path_ is somewhere else, trim the basename.
+ new_path.path_.resize(last_separator);
+ }
+
+ new_path.StripTrailingSeparatorsInternal();
+ if (!new_path.path_.length())
+ new_path.path_ = kCurrentDirectory;
+
+ return new_path;
+}
+
+FilePath FilePath::BaseName() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // The drive letter, if any, is always stripped.
+ StringType::size_type letter = FindDriveLetter(new_path.path_);
+ if (letter != StringType::npos) {
+ new_path.path_.erase(0, letter + 1);
+ }
+
+ // Keep everything after the final separator, but if the pathname is only
+ // one character and it's a separator, leave it alone.
+ StringType::size_type last_separator = new_path.path_.find_last_of(
+ kSeparators, StringType::npos, kSeparatorsLength - 1);
+ if (last_separator != StringType::npos &&
+ last_separator < new_path.path_.length() - 1) {
+ new_path.path_.erase(0, last_separator + 1);
+ }
+
+ return new_path;
+}
+
+StringType FilePath::Extension() const {
+ FilePath base(BaseName());
+ const StringType::size_type dot = ExtensionSeparatorPosition(base.path_);
+ if (dot == StringType::npos)
+ return StringType();
+
+ return base.path_.substr(dot, StringType::npos);
+}
+
+StringType FilePath::FinalExtension() const {
+ FilePath base(BaseName());
+ const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_);
+ if (dot == StringType::npos)
+ return StringType();
+
+ return base.path_.substr(dot, StringType::npos);
+}
+
+FilePath FilePath::RemoveExtension() const {
+ if (Extension().empty())
+ return *this;
+
+ const StringType::size_type dot = ExtensionSeparatorPosition(path_);
+ if (dot == StringType::npos)
+ return *this;
+
+ return FilePath(path_.substr(0, dot));
+}
+
+FilePath FilePath::RemoveFinalExtension() const {
+ if (FinalExtension().empty())
+ return *this;
+
+ const StringType::size_type dot = FinalExtensionSeparatorPosition(path_);
+ if (dot == StringType::npos)
+ return *this;
+
+ return FilePath(path_.substr(0, dot));
+}
+
+FilePath FilePath::InsertBeforeExtension(StringViewType suffix) const {
+ if (suffix.empty())
+ return FilePath(path_);
+
+ if (IsEmptyOrSpecialCase(BaseName().value()))
+ return FilePath();
+
+ StringType ext = Extension();
+ StringType ret = RemoveExtension().value();
+ ret.append(suffix);
+ ret.append(ext);
+ return FilePath(ret);
+}
+
+FilePath FilePath::InsertBeforeExtensionASCII(std::string_view suffix) const {
+ DCHECK(IsStringASCII(suffix));
+#if defined(OS_WIN)
+ return InsertBeforeExtension(ASCIIToUTF16(suffix));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return InsertBeforeExtension(suffix);
+#endif
+}
+
+FilePath FilePath::AddExtension(StringViewType extension) const {
+ if (IsEmptyOrSpecialCase(BaseName().value()))
+ return FilePath();
+
+ // If the new extension is "" or ".", then just return the current FilePath.
+ if (extension.empty() ||
+ (extension.size() == 1 && extension[0] == kExtensionSeparator))
+ return *this;
+
+ StringType str = path_;
+ if (extension[0] != kExtensionSeparator &&
+ *(str.end() - 1) != kExtensionSeparator) {
+ str.append(1, kExtensionSeparator);
+ }
+ str.append(extension);
+ return FilePath(str);
+}
+
+FilePath FilePath::ReplaceExtension(StringViewType extension) const {
+ if (IsEmptyOrSpecialCase(BaseName().value()))
+ return FilePath();
+
+ FilePath no_ext = RemoveExtension();
+ // If the new extension is "" or ".", then just remove the current extension.
+ if (extension.empty() ||
+ (extension.size() == 1 && extension[0] == kExtensionSeparator))
+ return no_ext;
+
+ StringType str = no_ext.value();
+ if (extension[0] != kExtensionSeparator)
+ str.append(1, kExtensionSeparator);
+ str.append(extension);
+ return FilePath(str);
+}
+
+FilePath FilePath::Append(StringViewType component) const {
+ StringViewType appended = component;
+ StringType without_nuls;
+
+ StringType::size_type nul_pos = component.find(kStringTerminator);
+ if (nul_pos != StringViewType::npos) {
+ without_nuls.assign(component.substr(0, nul_pos));
+ appended = StringViewType(without_nuls);
+ }
+
+ DCHECK(!IsPathAbsolute(appended));
+
+ if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
+ // Append normally doesn't do any normalization, but as a special case,
+ // when appending to kCurrentDirectory, just return a new path for the
+ // component argument. Appending component to kCurrentDirectory would
+ // serve no purpose other than needlessly lengthening the path, and
+ // it's likely in practice to wind up with FilePath objects containing
+ // only kCurrentDirectory when calling DirName on a single relative path
+ // component.
+ return FilePath(appended);
+ }
+
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // Don't append a separator if the path is empty (indicating the current
+ // directory) or if the path component is empty (indicating nothing to
+ // append).
+ if (!appended.empty() && !new_path.path_.empty()) {
+ // Don't append a separator if the path still ends with a trailing
+ // separator after stripping (indicating the root directory).
+ if (!IsSeparator(new_path.path_.back())) {
+ // Don't append a separator if the path is just a drive letter.
+ if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
+ new_path.path_.append(1, kSeparators[0]);
+ }
+ }
+ }
+
+ new_path.path_.append(appended);
+ return new_path;
+}
+
+FilePath FilePath::Append(const FilePath& component) const {
+ return Append(component.value());
+}
+
+FilePath FilePath::AppendASCII(std::string_view component) const {
+ DCHECK(base::IsStringASCII(component));
+#if defined(OS_WIN)
+ return Append(ASCIIToUTF16(component));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return Append(component);
+#endif
+}
+
+bool FilePath::IsAbsolute() const {
+ return IsPathAbsolute(path_);
+}
+
+bool FilePath::EndsWithSeparator() const {
+ if (empty())
+ return false;
+ return IsSeparator(path_.back());
+}
+
+FilePath FilePath::AsEndingWithSeparator() const {
+ if (EndsWithSeparator() || path_.empty())
+ return *this;
+
+ StringType path_str;
+ path_str.reserve(path_.length() + 1); // Only allocate string once.
+
+ path_str = path_;
+ path_str.append(&kSeparators[0], 1);
+ return FilePath(path_str);
+}
+
+FilePath FilePath::StripTrailingSeparators() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ return new_path;
+}
+
+bool FilePath::ReferencesParent() const {
+ if (path_.find(kParentDirectory) == StringType::npos) {
+ // GetComponents is quite expensive, so avoid calling it in the majority
+ // of cases where there isn't a kParentDirectory anywhere in the path.
+ return false;
+ }
+
+ std::vector<StringType> components;
+ GetComponents(&components);
+
+ std::vector<StringType>::const_iterator it = components.begin();
+ for (; it != components.end(); ++it) {
+ const StringType& component = *it;
+ // Windows has odd, undocumented behavior with path components containing
+ // only whitespace and . characters. So, if all we see is . and
+ // whitespace, then we treat any .. sequence as referencing parent.
+ // For simplicity we enforce this on all platforms.
+ if (component.find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) ==
+ std::string::npos &&
+ component.find(kParentDirectory) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#if defined(OS_WIN)
+
+std::u16string FilePath::LossyDisplayName() const {
+ return path_;
+}
+
+std::string FilePath::MaybeAsASCII() const {
+ if (base::IsStringASCII(path_))
+ return UTF16ToASCII(path_);
+ return std::string();
+}
+
+std::string FilePath::As8Bit() const {
+ return UTF16ToUTF8(value());
+}
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+// See file_path.h for a discussion of the encoding of paths on POSIX
+// platforms. These encoding conversion functions are not quite correct.
+
+std::string FilePath::MaybeAsASCII() const {
+ if (base::IsStringASCII(path_))
+ return path_;
+ return std::string();
+}
+
+std::string FilePath::As8Bit() const {
+ return value();
+}
+
+#endif // defined(OS_WIN)
+
+void FilePath::StripTrailingSeparatorsInternal() {
+ // If there is no drive letter, start will be 1, which will prevent stripping
+ // the leading separator if there is only one separator. If there is a drive
+ // letter, start will be set appropriately to prevent stripping the first
+ // separator following the drive letter, if a separator immediately follows
+ // the drive letter.
+ StringType::size_type start = FindDriveLetter(path_) + 2;
+
+ StringType::size_type last_stripped = StringType::npos;
+ for (StringType::size_type pos = path_.length();
+ pos > start && IsSeparator(path_[pos - 1]); --pos) {
+ // If the string only has two separators and they're at the beginning,
+ // don't strip them, unless the string began with more than two separators.
+ if (pos != start + 1 || last_stripped == start + 2 ||
+ !IsSeparator(path_[start - 1])) {
+ path_.resize(pos - 1);
+ last_stripped = pos;
+ }
+ }
+}
+
+FilePath FilePath::NormalizePathSeparators() const {
+ return NormalizePathSeparatorsTo(kSeparators[0]);
+}
+
+FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const {
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ DCHECK_NE(kSeparators + kSeparatorsLength,
+ std::find(kSeparators, kSeparators + kSeparatorsLength, separator));
+ StringType copy = path_;
+ for (size_t i = 0; i < kSeparatorsLength; ++i) {
+ std::replace(copy.begin(), copy.end(), kSeparators[i], separator);
+ }
+ return FilePath(copy);
+#else
+ return *this;
+#endif
+}
+
+} // namespace base
diff --git a/gn/src/base/files/file_path.h b/gn/src/base/files/file_path.h
new file mode 100644
index 00000000000..2359bb69a35
--- /dev/null
+++ b/gn/src/base/files/file_path.h
@@ -0,0 +1,394 @@
+// 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.
+
+// FilePath is a container for pathnames stored in a platform's native string
+// type, providing containers for manipulation in according with the
+// platform's conventions for pathnames. It supports the following path
+// types:
+//
+// POSIX Windows
+// --------------- ----------------------------------
+// Fundamental type char[] char16_t[]
+// Encoding unspecified* UTF-16
+// Separator / \, tolerant of /
+// Drive letters no case-insensitive A-Z followed by :
+// Alternate root // (surprise!) \\, for UNC paths
+//
+// * The encoding need not be specified on POSIX systems, although some
+// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8.
+// Chrome OS also uses UTF-8.
+// Linux does not specify an encoding, but in practice, the locale's
+// character set may be used.
+//
+// For more arcane bits of path trivia, see below.
+//
+// FilePath objects are intended to be used anywhere paths are. An
+// application may pass FilePath objects around internally, masking the
+// underlying differences between systems, only differing in implementation
+// where interfacing directly with the system. For example, a single
+// OpenFile(const FilePath &) function may be made available, allowing all
+// callers to operate without regard to the underlying implementation. On
+// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might
+// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This
+// allows each platform to pass pathnames around without requiring conversions
+// between encodings, which has an impact on performance, but more imporantly,
+// has an impact on correctness on platforms that do not have well-defined
+// encodings for pathnames.
+//
+// Several methods are available to perform common operations on a FilePath
+// object, such as determining the parent directory (DirName), isolating the
+// final path component (BaseName), and appending a relative pathname string
+// to an existing FilePath object (Append). These methods are highly
+// recommended over attempting to split and concatenate strings directly.
+// These methods are based purely on string manipulation and knowledge of
+// platform-specific pathname conventions, and do not consult the filesystem
+// at all, making them safe to use without fear of blocking on I/O operations.
+// These methods do not function as mutators but instead return distinct
+// instances of FilePath objects, and are therefore safe to use on const
+// objects. The objects themselves are safe to share between threads.
+//
+// To aid in initialization of FilePath objects from string literals, a
+// FILE_PATH_LITERAL macro is provided, which accounts for the difference
+// between char[]-based pathnames on POSIX systems and char16_t[]-based
+// pathnames on Windows.
+//
+// As a precaution against premature truncation, paths can't contain NULs.
+//
+// Because a FilePath object should not be instantiated at the global scope,
+// instead, use a FilePath::CharType[] and initialize it with
+// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the
+// character array. Example:
+//
+// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt");
+// |
+// | void Function() {
+// | FilePath log_file_path(kLogFileName);
+// | [...]
+// | }
+//
+// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even
+// when the UI language is RTL. This means you always need to pass filepaths
+// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the
+// RTL UI.
+//
+// This is a very common source of bugs, please try to keep this in mind.
+//
+// ARCANE BITS OF PATH TRIVIA
+//
+// - A double leading slash is actually part of the POSIX standard. Systems
+// are allowed to treat // as an alternate root, as Windows does for UNC
+// (network share) paths. Most POSIX systems don't do anything special
+// with two leading slashes, but FilePath handles this case properly
+// in case it ever comes across such a system. FilePath needs this support
+// for Windows UNC paths, anyway.
+// References:
+// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname")
+// and 4.12 ("Pathname Resolution"), available at:
+// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267
+// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12
+//
+// - Windows treats c:\\ the same way it treats \\. This was intended to
+// allow older applications that require drive letters to support UNC paths
+// like \\server\share\path, by permitting c:\\server\share\path as an
+// equivalent. Since the OS treats these paths specially, FilePath needs
+// to do the same. Since Windows can use either / or \ as the separator,
+// FilePath treats c://, c:\\, //, and \\ all equivalently.
+// Reference:
+// The Old New Thing, "Why is a drive letter permitted in front of UNC
+// paths (sometimes)?", available at:
+// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx
+
+#ifndef BASE_FILES_FILE_PATH_H_
+#define BASE_FILES_FILE_PATH_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "util/build_config.h"
+
+// Windows-style drive letter support and pathname separator characters can be
+// enabled and disabled independently, to aid testing. These #defines are
+// here so that the same setting can be used in both the implementation and
+// in the unit test.
+#if defined(OS_WIN)
+#define FILE_PATH_USES_DRIVE_LETTERS
+#define FILE_PATH_USES_WIN_SEPARATORS
+#endif // OS_WIN
+
+// To print path names portably use PRIsFP (based on PRIuS and friends from
+// C99 and format_macros.h) like this:
+// base::StringPrintf("Path is %" PRIsFP ".\n", PATH_CSTR(path);
+#if defined(OS_WIN)
+#define PRIsFP "ls"
+#define PATH_CSTR(x) reinterpret_cast<const wchar_t*>(x.value().c_str())
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define PRIsFP "s"
+#define PATH_CSTR(x) (x.value().c_str())
+#endif // OS_WIN
+
+namespace base {
+
+class Pickle;
+class PickleIterator;
+
+// An abstraction to isolate users from the differences between native
+// pathnames on different platforms.
+class FilePath {
+ public:
+#if defined(OS_WIN)
+ // On Windows, for Unicode-aware applications, native pathnames are char16_t
+ // arrays encoded in UTF-16.
+ typedef std::u16string StringType;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On most platforms, native pathnames are char arrays, and the encoding
+ // may or may not be specified. On Mac OS X, native pathnames are encoded
+ // in UTF-8.
+ typedef std::string StringType;
+#endif // OS_WIN
+
+ using CharType = StringType::value_type;
+ using StringViewType = std::basic_string_view<CharType>;
+
+ // Null-terminated array of separators used to separate components in
+ // hierarchical paths. Each character in this array is a valid separator,
+ // but kSeparators[0] is treated as the canonical separator and will be used
+ // when composing pathnames.
+ static const CharType kSeparators[];
+
+ // std::size(kSeparators).
+ static const size_t kSeparatorsLength;
+
+ // A special path component meaning "this directory."
+ static const CharType kCurrentDirectory[];
+
+ // A special path component meaning "the parent directory."
+ static const CharType kParentDirectory[];
+
+ // The character used to identify a file extension.
+ static const CharType kExtensionSeparator;
+
+ FilePath();
+ FilePath(const FilePath& that);
+ explicit FilePath(StringViewType path);
+ ~FilePath();
+ FilePath& operator=(const FilePath& that);
+
+ // Constructs FilePath with the contents of |that|, which is left in valid but
+ // unspecified state.
+ FilePath(FilePath&& that) noexcept;
+ // Replaces the contents with those of |that|, which is left in valid but
+ // unspecified state.
+ FilePath& operator=(FilePath&& that);
+
+ bool operator==(const FilePath& that) const;
+
+ bool operator!=(const FilePath& that) const;
+
+ // Required for some STL containers and operations
+ bool operator<(const FilePath& that) const { return path_ < that.path_; }
+
+ const StringType& value() const { return path_; }
+
+ bool empty() const { return path_.empty(); }
+
+ void clear() { path_.clear(); }
+
+ // Returns true if |character| is in kSeparators.
+ static bool IsSeparator(CharType character);
+
+ // Returns a vector of all of the components of the provided path. It is
+ // equivalent to calling DirName().value() on the path's root component,
+ // and BaseName().value() on each child component.
+ //
+ // To make sure this is lossless so we can differentiate absolute and
+ // relative paths, the root slash will be included even though no other
+ // slashes will be. The precise behavior is:
+ //
+ // Posix: "/foo/bar" -> [ "/", "foo", "bar" ]
+ // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ]
+ void GetComponents(std::vector<FilePath::StringType>* components) const;
+
+ // Returns true if this FilePath is a strict parent of the |child|. Absolute
+ // and relative paths are accepted i.e. is /foo parent to /foo/bar and
+ // is foo parent to foo/bar. Does not convert paths to absolute, follow
+ // symlinks or directory navigation (e.g. ".."). A path is *NOT* its own
+ // parent.
+ bool IsParent(const FilePath& child) const;
+
+ // If IsParent(child) holds, appends to path (if non-NULL) the
+ // relative path to child and returns true. For example, if parent
+ // holds "/Users/johndoe/Library/Application Support", child holds
+ // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and
+ // *path holds "/Users/johndoe/Library/Caches", then after
+ // parent.AppendRelativePath(child, path) is called *path will hold
+ // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise,
+ // returns false.
+ bool AppendRelativePath(const FilePath& child, FilePath* path) const;
+
+ // Returns a FilePath corresponding to the directory containing the path
+ // named by this object, stripping away the file component. If this object
+ // only contains one component, returns a FilePath identifying
+ // kCurrentDirectory. If this object already refers to the root directory,
+ // returns a FilePath identifying the root directory. Please note that this
+ // doesn't resolve directory navigation, e.g. the result for "../a" is "..".
+ FilePath DirName() const WARN_UNUSED_RESULT;
+
+ // Returns a FilePath corresponding to the last path component of this
+ // object, either a file or a directory. If this object already refers to
+ // the root directory, returns a FilePath identifying the root directory;
+ // this is the only situation in which BaseName will return an absolute path.
+ FilePath BaseName() const WARN_UNUSED_RESULT;
+
+ // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
+ // the file has no extension. If non-empty, Extension() will always start
+ // with precisely one ".". The following code should always work regardless
+ // of the value of path. For common double-extensions like .tar.gz and
+ // .user.js, this method returns the combined extension. For a single
+ // component, use FinalExtension().
+ // new_path = path.RemoveExtension().value().append(path.Extension());
+ // ASSERT(new_path == path.value());
+ // NOTE: this is different from the original file_util implementation which
+ // returned the extension without a leading "." ("jpg" instead of ".jpg")
+ StringType Extension() const WARN_UNUSED_RESULT;
+
+ // Returns the path's file extension, as in Extension(), but will
+ // never return a double extension.
+ //
+ // TODO(davidben): Check all our extension-sensitive code to see if
+ // we can rename this to Extension() and the other to something like
+ // LongExtension(), defaulting to short extensions and leaving the
+ // long "extensions" to logic like base::GetUniquePathNumber().
+ StringType FinalExtension() const WARN_UNUSED_RESULT;
+
+ // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
+ // NOTE: this is slightly different from the similar file_util implementation
+ // which returned simply 'jojo'.
+ FilePath RemoveExtension() const WARN_UNUSED_RESULT;
+
+ // Removes the path's file extension, as in RemoveExtension(), but
+ // ignores double extensions.
+ FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT;
+
+ // Inserts |suffix| after the file name portion of |path| but before the
+ // extension. Returns "" if BaseName() == "." or "..".
+ // Examples:
+ // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
+ // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
+ // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
+ // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
+ FilePath InsertBeforeExtension(StringViewType suffix) const
+ WARN_UNUSED_RESULT;
+ FilePath InsertBeforeExtensionASCII(std::string_view suffix) const
+ WARN_UNUSED_RESULT;
+
+ // Adds |extension| to |file_name|. Returns the current FilePath if
+ // |extension| is empty. Returns "" if BaseName() == "." or "..".
+ FilePath AddExtension(StringViewType extension) const WARN_UNUSED_RESULT;
+
+ // Replaces the extension of |file_name| with |extension|. If |file_name|
+ // does not have an extension, then |extension| is added. If |extension| is
+ // empty, then the extension is removed from |file_name|.
+ // Returns "" if BaseName() == "." or "..".
+ FilePath ReplaceExtension(StringViewType extension) const WARN_UNUSED_RESULT;
+
+ // Returns a FilePath by appending a separator and the supplied path
+ // component to this object's path. Append takes care to avoid adding
+ // excessive separators if this object's path already ends with a separator.
+ // If this object's path is kCurrentDirectory, a new FilePath corresponding
+ // only to |component| is returned. |component| must be a relative path;
+ // it is an error to pass an absolute path.
+ FilePath Append(StringViewType component) const WARN_UNUSED_RESULT;
+ FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT;
+
+ // Although Windows StringType is std::u16string, since the encoding it uses
+ // for paths is well defined, it can handle ASCII path components as well. Mac
+ // uses UTF8, and since ASCII is a subset of that, it works there as well. On
+ // Linux, although it can use any 8-bit encoding for paths, we assume that
+ // ASCII is a valid subset, regardless of the encoding, since many operating
+ // system paths will always be ASCII.
+ FilePath AppendASCII(std::string_view component) const WARN_UNUSED_RESULT;
+
+ // Returns true if this FilePath contains an absolute path. On Windows, an
+ // absolute path begins with either a drive letter specification followed by
+ // a separator character, or with two separator characters. On POSIX
+ // platforms, an absolute path begins with a separator character.
+ bool IsAbsolute() const;
+
+ // Returns true if the patch ends with a path separator character.
+ bool EndsWithSeparator() const WARN_UNUSED_RESULT;
+
+ // Returns a copy of this FilePath that ends with a trailing separator. If
+ // the input path is empty, an empty FilePath will be returned.
+ FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT;
+
+ // Returns a copy of this FilePath that does not end with a trailing
+ // separator.
+ FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT;
+
+ // Returns true if this FilePath contains an attempt to reference a parent
+ // directory (e.g. has a path component that is "..").
+ bool ReferencesParent() const;
+
+ // Return a Unicode human-readable version of this path.
+ // Warning: you can *not*, in general, go from a display name back to a real
+ // path. Only use this when displaying paths to users, not just when you
+ // want to stuff a std::u16string into some other API.
+ std::u16string LossyDisplayName() const;
+
+ // Return the path as ASCII, or the empty string if the path is not ASCII.
+ // This should only be used for cases where the FilePath is representing a
+ // known-ASCII filename.
+ std::string MaybeAsASCII() const;
+
+ // Return the path as 8-bit. On Linux this isn't guaranteed to be UTF-8.
+ std::string As8Bit() const;
+
+ // Normalize all path separators to backslash on Windows
+ // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
+ FilePath NormalizePathSeparators() const;
+
+ // Normalize all path separattors to given type on Windows
+ // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
+ FilePath NormalizePathSeparatorsTo(CharType separator) const;
+
+ private:
+ // Remove trailing separators from this object. If the path is absolute, it
+ // will never be stripped any more than to refer to the absolute root
+ // directory, so "////" will become "/", not "". A leading pair of
+ // separators is never stripped, to support alternate roots. This is used to
+ // support UNC paths on Windows.
+ void StripTrailingSeparatorsInternal();
+
+ StringType path_;
+};
+
+} // namespace base
+
+// Macros for string literal initialization of FilePath::CharType[].
+#if defined(OS_WIN)
+#define FILE_PATH_LITERAL(x) u##x
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define FILE_PATH_LITERAL(x) x
+#endif // OS_WIN
+
+namespace std {
+
+template <>
+struct hash<base::FilePath> {
+ typedef base::FilePath argument_type;
+ typedef std::size_t result_type;
+ result_type operator()(argument_type const& f) const {
+ return hash<base::FilePath::StringType>()(f.value());
+ }
+};
+
+} // namespace std
+
+#endif // BASE_FILES_FILE_PATH_H_
diff --git a/gn/src/base/files/file_path_constants.cc b/gn/src/base/files/file_path_constants.cc
new file mode 100644
index 00000000000..9ae0388644e
--- /dev/null
+++ b/gn/src/base/files/file_path_constants.cc
@@ -0,0 +1,27 @@
+// 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 <stddef.h>
+
+#include <iterator>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace base {
+
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
+#else // FILE_PATH_USES_WIN_SEPARATORS
+const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+
+const size_t FilePath::kSeparatorsLength = std::size(kSeparators);
+
+const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
+const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
+
+const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
+
+} // namespace base
diff --git a/gn/src/base/files/file_posix.cc b/gn/src/base/files/file_posix.cc
new file mode 100644
index 00000000000..349cbfe5f49
--- /dev/null
+++ b/gn/src/base/files/file_posix.cc
@@ -0,0 +1,392 @@
+// 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 "base/files/file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+namespace base {
+
+// Make sure our Whence mappings match the system headers.
+static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
+ File::FROM_END == SEEK_END,
+ "whence mapping must match the system headers");
+
+namespace {
+
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
+ defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21
+int CallFstat(int fd, stat_wrapper_t* sb) {
+ return fstat(fd, sb);
+}
+#else
+int CallFstat(int fd, stat_wrapper_t* sb) {
+ return fstat64(fd, sb);
+}
+#endif
+
+// Some systems don't provide the following system calls, so either simulate
+// them or wrap them in order to minimize the number of #ifdef's in this file.
+#if !defined(OS_AIX)
+bool IsOpenAppend(PlatformFile file) {
+ return (fcntl(file, F_GETFL) & O_APPEND) != 0;
+}
+
+int CallFtruncate(PlatformFile file, int64_t length) {
+ return HANDLE_EINTR(ftruncate(file, length));
+}
+
+#if !defined(OS_FUCHSIA)
+File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
+ struct flock lock;
+ lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0; // Lock entire file.
+ if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
+ return File::GetLastFileError();
+ return File::FILE_OK;
+}
+#endif
+
+#else // !defined(OS_AIX)
+
+bool IsOpenAppend(PlatformFile file) {
+ // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
+ // standard and always appends if the file is opened with O_APPEND, just
+ // return false here.
+ return false;
+}
+
+int CallFtruncate(PlatformFile file, int64_t length) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
+ return 0;
+}
+
+File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
+ return File::FILE_ERROR_INVALID_OPERATION;
+}
+#endif // defined(OS_AIX)
+
+} // namespace
+
+void File::Info::FromStat(const stat_wrapper_t& stat_info) {
+ is_directory = S_ISDIR(stat_info.st_mode);
+ is_symbolic_link = S_ISLNK(stat_info.st_mode);
+ size = stat_info.st_size;
+
+#if defined(OS_MACOSX)
+ time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
+ int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
+ time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
+ int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
+ time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
+ int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
+#elif defined(OS_AIX)
+ time_t last_modified_sec = stat_info.st_mtime;
+ int64_t last_modified_nsec = 0;
+ time_t last_accessed_sec = stat_info.st_atime;
+ int64_t last_accessed_nsec = 0;
+ time_t creation_time_sec = stat_info.st_ctime;
+ int64_t creation_time_nsec = 0;
+#elif defined(OS_POSIX)
+ time_t last_modified_sec = stat_info.st_mtim.tv_sec;
+ int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
+ time_t last_accessed_sec = stat_info.st_atim.tv_sec;
+ int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
+ time_t creation_time_sec = stat_info.st_ctim.tv_sec;
+ int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
+#else
+#error
+#endif
+
+ constexpr uint64_t kNano = 1'000'000'000;
+ last_modified = last_modified_sec * kNano + last_modified_nsec;
+ last_accessed = last_accessed_sec * kNano + last_accessed_nsec;
+ creation_time = creation_time_sec * kNano + creation_time_nsec;
+}
+
+bool File::IsValid() const {
+ return file_.is_valid();
+}
+
+PlatformFile File::GetPlatformFile() const {
+ return file_.get();
+}
+
+PlatformFile File::TakePlatformFile() {
+ return file_.release();
+}
+
+void File::Close() {
+ if (!IsValid())
+ return;
+
+ file_.reset();
+}
+
+int64_t File::Seek(Whence whence, int64_t offset) {
+ DCHECK(IsValid());
+
+ static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
+ return lseek(file_.get(), static_cast<off_t>(offset),
+ static_cast<int>(whence));
+}
+
+int File::Read(int64_t offset, char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read,
+ offset + bytes_read));
+ if (rv <= 0)
+ break;
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+
+int File::ReadAtCurrentPos(char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
+ if (rv <= 0)
+ break;
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+
+int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
+ DCHECK(IsValid());
+ return HANDLE_EINTR(pread(file_.get(), data, size, offset));
+}
+
+int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ return HANDLE_EINTR(read(file_.get(), data, size));
+}
+
+int File::Write(int64_t offset, const char* data, int size) {
+ if (IsOpenAppend(file_.get()))
+ return WriteAtCurrentPos(data, size);
+
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_written = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
+ size - bytes_written, offset + bytes_written));
+ if (rv <= 0)
+ break;
+
+ bytes_written += rv;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : rv;
+}
+
+int File::WriteAtCurrentPos(const char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_written = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(
+ write(file_.get(), data + bytes_written, size - bytes_written));
+ if (rv <= 0)
+ break;
+
+ bytes_written += rv;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : rv;
+}
+
+int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ return HANDLE_EINTR(write(file_.get(), data, size));
+}
+
+int64_t File::GetLength() {
+ DCHECK(IsValid());
+
+ stat_wrapper_t file_info;
+ if (CallFstat(file_.get(), &file_info))
+ return -1;
+
+ return file_info.st_size;
+}
+
+bool File::SetLength(int64_t length) {
+ DCHECK(IsValid());
+
+ return !CallFtruncate(file_.get(), length);
+}
+
+bool File::GetInfo(Info* info) {
+ DCHECK(IsValid());
+
+ stat_wrapper_t file_info;
+ if (CallFstat(file_.get(), &file_info))
+ return false;
+
+ info->FromStat(file_info);
+ return true;
+}
+
+#if !defined(OS_FUCHSIA)
+File::Error File::Lock() {
+ return CallFcntlFlock(file_.get(), true);
+}
+
+File::Error File::Unlock() {
+ return CallFcntlFlock(file_.get(), false);
+}
+#endif
+
+File File::Duplicate() const {
+ if (!IsValid())
+ return File();
+
+ PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
+ if (other_fd == -1)
+ return File(File::GetLastFileError());
+
+ File other(other_fd);
+ return other;
+}
+
+// Static.
+File::Error File::OSErrorToFileError(int saved_errno) {
+ switch (saved_errno) {
+ case EACCES:
+ case EISDIR:
+ case EROFS:
+ case EPERM:
+ return FILE_ERROR_ACCESS_DENIED;
+ case EBUSY:
+ case ETXTBSY:
+ return FILE_ERROR_IN_USE;
+ case EEXIST:
+ return FILE_ERROR_EXISTS;
+ case EIO:
+ return FILE_ERROR_IO;
+ case ENOENT:
+ return FILE_ERROR_NOT_FOUND;
+ case ENFILE: // fallthrough
+ case EMFILE:
+ return FILE_ERROR_TOO_MANY_OPENED;
+ case ENOMEM:
+ return FILE_ERROR_NO_MEMORY;
+ case ENOSPC:
+ return FILE_ERROR_NO_SPACE;
+ case ENOTDIR:
+ return FILE_ERROR_NOT_A_DIRECTORY;
+ default:
+ // This function should only be called for errors.
+ DCHECK_NE(0, saved_errno);
+ return FILE_ERROR_FAILED;
+ }
+}
+
+void File::DoInitialize(const FilePath& path, uint32_t flags) {
+ DCHECK(!IsValid());
+
+ int open_flags = 0;
+ created_ = false;
+
+ if (flags & FLAG_CREATE_ALWAYS) {
+ DCHECK(!open_flags);
+ DCHECK(flags & FLAG_WRITE);
+ open_flags = O_CREAT | O_TRUNC;
+ }
+
+ if (!open_flags && !(flags & FLAG_OPEN)) {
+ NOTREACHED();
+ errno = EOPNOTSUPP;
+ error_details_ = FILE_ERROR_FAILED;
+ return;
+ }
+
+ if (flags & FLAG_WRITE && flags & FLAG_READ) {
+ open_flags |= O_RDWR;
+ } else if (flags & FLAG_WRITE) {
+ open_flags |= O_WRONLY;
+ } else if (!(flags & FLAG_READ)) {
+ NOTREACHED();
+ }
+
+ static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
+
+ int mode = S_IRUSR | S_IWUSR;
+ int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
+
+ if (descriptor < 0) {
+ error_details_ = File::GetLastFileError();
+ return;
+ }
+
+ if (flags & FLAG_CREATE_ALWAYS)
+ created_ = true;
+
+ error_details_ = FILE_OK;
+ file_.reset(descriptor);
+}
+
+bool File::Flush() {
+ DCHECK(IsValid());
+
+#if defined(OS_LINUX)
+ return !HANDLE_EINTR(fdatasync(file_.get()));
+#else
+ return !HANDLE_EINTR(fsync(file_.get()));
+#endif
+}
+
+void File::SetPlatformFile(PlatformFile file) {
+ DCHECK(!file_.is_valid());
+ file_.reset(file);
+}
+
+// static
+File::Error File::GetLastFileError() {
+ return base::File::OSErrorToFileError(errno);
+}
+
+} // namespace base
diff --git a/gn/src/base/files/file_util.cc b/gn/src/base/files/file_util.cc
new file mode 100644
index 00000000000..c68fe02b5d1
--- /dev/null
+++ b/gn/src/base/files/file_util.cc
@@ -0,0 +1,244 @@
+// 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 "base/files/file_util.h"
+
+#if defined(OS_WIN)
+#include <io.h>
+#endif
+#include <stdio.h>
+
+#include <fstream>
+#include <limits>
+#include <string_view>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+namespace base {
+
+namespace {
+
+// The maximum number of 'uniquified' files we will try to create.
+// This is used when the filename we're trying to download is already in use,
+// so we create a new unique filename by appending " (nnn)" before the
+// extension, where 1 <= nnn <= kMaxUniqueFiles.
+// Also used by code that cleans up said files.
+static const int kMaxUniqueFiles = 100;
+
+} // namespace
+
+int64_t ComputeDirectorySize(const FilePath& root_path) {
+ int64_t running_size = 0;
+ FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
+ while (!file_iter.Next().empty())
+ running_size += file_iter.GetInfo().GetSize();
+ return running_size;
+}
+
+bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
+ // We open the file in binary format even if they are text files because
+ // we are just comparing that bytes are exactly same in both files and not
+ // doing anything smart with text formatting.
+ std::ifstream file1(filename1.As8Bit().c_str(),
+ std::ios::in | std::ios::binary);
+ std::ifstream file2(filename2.As8Bit().c_str(),
+ std::ios::in | std::ios::binary);
+
+ // Even if both files aren't openable (and thus, in some sense, "equal"),
+ // any unusable file yields a result of "false".
+ if (!file1.is_open() || !file2.is_open())
+ return false;
+
+ const int BUFFER_SIZE = 2056;
+ char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
+ do {
+ file1.read(buffer1, BUFFER_SIZE);
+ file2.read(buffer2, BUFFER_SIZE);
+
+ if ((file1.eof() != file2.eof()) || (file1.gcount() != file2.gcount()) ||
+ (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
+ file1.close();
+ file2.close();
+ return false;
+ }
+ } while (!file1.eof() || !file2.eof());
+
+ file1.close();
+ file2.close();
+ return true;
+}
+
+bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
+ std::ifstream file1(filename1.As8Bit().c_str(), std::ios::in);
+ std::ifstream file2(filename2.As8Bit().c_str(), std::ios::in);
+
+ // Even if both files aren't openable (and thus, in some sense, "equal"),
+ // any unusable file yields a result of "false".
+ if (!file1.is_open() || !file2.is_open())
+ return false;
+
+ do {
+ std::string line1, line2;
+ getline(file1, line1);
+ getline(file2, line2);
+
+ // Check for mismatched EOF states, or any error state.
+ if ((file1.eof() != file2.eof()) || file1.bad() || file2.bad()) {
+ return false;
+ }
+
+ // Trim all '\r' and '\n' characters from the end of the line.
+ std::string::size_type end1 = line1.find_last_not_of("\r\n");
+ if (end1 == std::string::npos)
+ line1.clear();
+ else if (end1 + 1 < line1.length())
+ line1.erase(end1 + 1);
+
+ std::string::size_type end2 = line2.find_last_not_of("\r\n");
+ if (end2 == std::string::npos)
+ line2.clear();
+ else if (end2 + 1 < line2.length())
+ line2.erase(end2 + 1);
+
+ if (line1 != line2)
+ return false;
+ } while (!file1.eof() || !file2.eof());
+
+ return true;
+}
+
+bool ReadFileToStringWithMaxSize(const FilePath& path,
+ std::string* contents,
+ size_t max_size) {
+ if (contents)
+ contents->clear();
+ if (path.ReferencesParent())
+ return false;
+ FILE* file = OpenFile(path, "rb");
+ if (!file) {
+ return false;
+ }
+
+ // Many files supplied in |path| have incorrect size (proc files etc).
+ // Hence, the file is read sequentially as opposed to a one-shot read, using
+ // file size as a hint for chunk size if available.
+ constexpr int64_t kDefaultChunkSize = 1 << 16;
+ int64_t chunk_size;
+ if (!GetFileSize(path, &chunk_size) || chunk_size <= 0)
+ chunk_size = kDefaultChunkSize - 1;
+ // We need to attempt to read at EOF for feof flag to be set so here we
+ // use |chunk_size| + 1.
+ chunk_size = std::min<uint64_t>(chunk_size, max_size) + 1;
+ size_t bytes_read_this_pass;
+ size_t bytes_read_so_far = 0;
+ bool read_status = true;
+ std::string local_contents;
+ local_contents.resize(chunk_size);
+
+ while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1,
+ chunk_size, file)) > 0) {
+ if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
+ // Read more than max_size bytes, bail out.
+ bytes_read_so_far = max_size;
+ read_status = false;
+ break;
+ }
+ // In case EOF was not reached, iterate again but revert to the default
+ // chunk size.
+ if (bytes_read_so_far == 0)
+ chunk_size = kDefaultChunkSize;
+
+ bytes_read_so_far += bytes_read_this_pass;
+ // Last fread syscall (after EOF) can be avoided via feof, which is just a
+ // flag check.
+ if (feof(file))
+ break;
+ local_contents.resize(bytes_read_so_far + chunk_size);
+ }
+ read_status = read_status && !ferror(file);
+ CloseFile(file);
+ if (contents) {
+ contents->swap(local_contents);
+ contents->resize(bytes_read_so_far);
+ }
+
+ return read_status;
+}
+
+bool ReadFileToString(const FilePath& path, std::string* contents) {
+ return ReadFileToStringWithMaxSize(path, contents,
+ std::numeric_limits<size_t>::max());
+}
+
+bool IsDirectoryEmpty(const FilePath& dir_path) {
+ FileEnumerator files(dir_path, false,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
+ if (files.Next().empty())
+ return true;
+ return false;
+}
+
+bool CreateDirectory(const FilePath& full_path) {
+ return CreateDirectoryAndGetError(full_path, nullptr);
+}
+
+bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
+ File::Info info;
+ if (!GetFileInfo(file_path, &info))
+ return false;
+ *file_size = info.size;
+ return true;
+}
+
+bool CloseFile(FILE* file) {
+ if (file == nullptr)
+ return true;
+ return fclose(file) == 0;
+}
+
+bool TruncateFile(FILE* file) {
+ if (file == nullptr)
+ return false;
+ long current_offset = ftell(file);
+ if (current_offset == -1)
+ return false;
+#if defined(OS_WIN)
+ int fd = _fileno(file);
+ if (_chsize(fd, current_offset) != 0)
+ return false;
+#else
+ int fd = fileno(file);
+ if (ftruncate(fd, current_offset) != 0)
+ return false;
+#endif
+ return true;
+}
+
+int GetUniquePathNumber(const FilePath& path,
+ const FilePath::StringType& suffix) {
+ bool have_suffix = !suffix.empty();
+ if (!PathExists(path) &&
+ (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
+ return 0;
+ }
+
+ FilePath new_path;
+ for (int count = 1; count <= kMaxUniqueFiles; ++count) {
+ new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
+ if (!PathExists(new_path) &&
+ (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
+ return count;
+ }
+ }
+
+ return -1;
+}
+
+} // namespace base
diff --git a/gn/src/base/files/file_util.h b/gn/src/base/files/file_util.h
new file mode 100644
index 00000000000..b44129d8fee
--- /dev/null
+++ b/gn/src/base/files/file_util.h
@@ -0,0 +1,350 @@
+// 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 contains utility functions for dealing with the local
+// filesystem.
+
+#ifndef BASE_FILES_FILE_UTIL_H_
+#define BASE_FILES_FILE_UTIL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+namespace base {
+
+class Environment;
+
+//-----------------------------------------------------------------------------
+// Functions that involve filesystem access or modification:
+
+// Returns an absolute version of a relative path. Returns an empty path on
+// error. On POSIX, this function fails if the path does not exist. This
+// function can result in I/O so it can be slow.
+FilePath MakeAbsoluteFilePath(const FilePath& input);
+
+// Returns the total number of bytes used by all the files under |root_path|.
+// If the path does not exist the function returns 0.
+//
+// This function is implemented using the FileEnumerator class so it is not
+// particularly speedy in any platform.
+int64_t ComputeDirectorySize(const FilePath& root_path);
+
+// Deletes the given path, whether it's a file or a directory.
+// If it's a directory, it's perfectly happy to delete all of the
+// directory's contents. Passing true to recursive deletes
+// subdirectories and their contents as well.
+// Returns true if successful, false otherwise. It is considered successful
+// to attempt to delete a file that does not exist.
+//
+// In posix environment and if |path| is a symbolic link, this deletes only
+// the symlink. (even if the symlink points to a non-existent file)
+//
+// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
+// TO "rm -rf", SO USE WITH CAUTION.
+bool DeleteFile(const FilePath& path, bool recursive);
+
+#if defined(OS_WIN)
+// Schedules to delete the given path, whether it's a file or a directory, until
+// the operating system is restarted.
+// Note:
+// 1) The file/directory to be deleted should exist in a temp folder.
+// 2) The directory to be deleted must be empty.
+bool DeleteFileAfterReboot(const FilePath& path);
+#endif
+
+// Renames file |from_path| to |to_path|. Both paths must be on the same
+// volume, or the function will fail. Destination file will be created
+// if it doesn't exist. Prefer this function over Move when dealing with
+// temporary files. On Windows it preserves attributes of the target file.
+// Returns true on success, leaving *error unchanged.
+// Returns false on failure and sets *error appropriately, if it is non-NULL.
+bool ReplaceFile(const FilePath& from_path,
+ const FilePath& to_path,
+ File::Error* error);
+
+// Returns true if the given path exists on the local filesystem,
+// false otherwise.
+bool PathExists(const FilePath& path);
+
+// Returns true if the given path is writable by the user, false otherwise.
+bool PathIsWritable(const FilePath& path);
+
+// Returns true if the given path exists and is a directory, false otherwise.
+bool DirectoryExists(const FilePath& path);
+
+// Returns true if the contents of the two files given are equal, false
+// otherwise. If either file can't be read, returns false.
+bool ContentsEqual(const FilePath& filename1, const FilePath& filename2);
+
+// Returns true if the contents of the two text files given are equal, false
+// otherwise. This routine treats "\r\n" and "\n" as equivalent.
+bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2);
+
+// Reads the file at |path| into |contents| and returns true on success and
+// false on error. For security reasons, a |path| containing path traversal
+// components ('..') is treated as a read error and |contents| is set to empty.
+// In case of I/O error, |contents| holds the data that could be read from the
+// file before the error occurred.
+// |contents| may be NULL, in which case this function is useful for its side
+// effect of priming the disk cache (could be used for unit tests).
+bool ReadFileToString(const FilePath& path, std::string* contents);
+
+// Reads the file at |path| into |contents| and returns true on success and
+// false on error. For security reasons, a |path| containing path traversal
+// components ('..') is treated as a read error and |contents| is set to empty.
+// In case of I/O error, |contents| holds the data that could be read from the
+// file before the error occurred. When the file size exceeds |max_size|, the
+// function returns false with |contents| holding the file truncated to
+// |max_size|.
+// |contents| may be NULL, in which case this function is useful for its side
+// effect of priming the disk cache (could be used for unit tests).
+bool ReadFileToStringWithMaxSize(const FilePath& path,
+ std::string* contents,
+ size_t max_size);
+
+#if defined(OS_POSIX)
+
+// Creates a symbolic link at |symlink| pointing to |target|. Returns
+// false on failure.
+bool CreateSymbolicLink(const FilePath& target, const FilePath& symlink);
+
+// Reads the given |symlink| and returns where it points to in |target|.
+// Returns false upon failure.
+bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);
+
+// Bits and masks of the file permission.
+enum FilePermissionBits {
+ FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO,
+ FILE_PERMISSION_USER_MASK = S_IRWXU,
+ FILE_PERMISSION_GROUP_MASK = S_IRWXG,
+ FILE_PERMISSION_OTHERS_MASK = S_IRWXO,
+
+ FILE_PERMISSION_READ_BY_USER = S_IRUSR,
+ FILE_PERMISSION_WRITE_BY_USER = S_IWUSR,
+ FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR,
+ FILE_PERMISSION_READ_BY_GROUP = S_IRGRP,
+ FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP,
+ FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP,
+ FILE_PERMISSION_READ_BY_OTHERS = S_IROTH,
+ FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH,
+ FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH,
+};
+
+// Reads the permission of the given |path|, storing the file permission
+// bits in |mode|. If |path| is symbolic link, |mode| is the permission of
+// a file which the symlink points to.
+bool GetPosixFilePermissions(const FilePath& path, int* mode);
+// Sets the permission of the given |path|. If |path| is symbolic link, sets
+// the permission of a file which the symlink points to.
+bool SetPosixFilePermissions(const FilePath& path, int mode);
+
+// Returns true iff |executable| can be found in any directory specified by the
+// environment variable in |env|.
+bool ExecutableExistsInPath(Environment* env,
+ const FilePath::StringType& executable);
+
+#endif // OS_POSIX
+
+// Returns true if the given directory is empty
+bool IsDirectoryEmpty(const FilePath& dir_path);
+
+// Get the temporary directory provided by the system.
+bool GetTempDir(FilePath* path);
+
+// Create a new directory. If prefix is provided, the new directory name is in
+// the format of prefixyyyy.
+// NOTE: prefix is ignored in the POSIX implementation.
+// If success, return true and output the full path of the directory created.
+bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path);
+
+// Create a directory within another directory.
+// Extra characters will be appended to |prefix| to ensure that the
+// new directory does not have the same name as an existing directory.
+bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir);
+
+// Creates a directory, as well as creating any parent directories, if they
+// don't exist. Returns 'true' on successful creation, or if the directory
+// already exists. The directory is only readable by the current user.
+// Returns true on success, leaving *error unchanged.
+// Returns false on failure and sets *error appropriately, if it is non-NULL.
+bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error);
+
+// Backward-compatible convenience method for the above.
+bool CreateDirectory(const FilePath& full_path);
+
+// Returns the file size. Returns true on success.
+bool GetFileSize(const FilePath& file_path, int64_t* file_size);
+
+// Sets |real_path| to |path| with symbolic links and junctions expanded.
+// On windows, make sure the path starts with a lettered drive.
+// |path| must reference a file. Function will fail if |path| points to
+// a directory or to a nonexistent path. On windows, this function will
+// fail if |path| is a junction or symlink that points to an empty file,
+// or if |real_path| would be longer than MAX_PATH characters.
+bool NormalizeFilePath(const FilePath& path, FilePath* real_path);
+
+#if defined(OS_WIN)
+
+// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."),
+// return in |drive_letter_path| the equivalent path that starts with
+// a drive letter ("C:\..."). Return false if no such path exists.
+bool DevicePathToDriveLetterPath(const FilePath& device_path,
+ FilePath* drive_letter_path);
+
+// Given an existing file in |path|, set |real_path| to the path
+// in native NT format, of the form "\Device\HarddiskVolumeXX\..".
+// Returns false if the path can not be found. Empty files cannot
+// be resolved with this function.
+bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path);
+#endif
+
+// This function will return if the given file is a symlink or not.
+bool IsLink(const FilePath& file_path);
+
+// Returns information about the given file path.
+bool GetFileInfo(const FilePath& file_path, File::Info* info);
+
+// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The
+// underlying file descriptor (POSIX) or handle (Windows) is unconditionally
+// configured to not be propagated to child processes.
+FILE* OpenFile(const FilePath& filename, const char* mode);
+
+// Closes file opened by OpenFile. Returns true on success.
+bool CloseFile(FILE* file);
+
+// Associates a standard FILE stream with an existing File. Note that this
+// functions take ownership of the existing File.
+FILE* FileToFILE(File file, const char* mode);
+
+// Truncates an open file to end at the location of the current file pointer.
+// This is a cross-platform analog to Windows' SetEndOfFile() function.
+bool TruncateFile(FILE* file);
+
+// Reads at most the given number of bytes from the file into the buffer.
+// Returns the number of read bytes, or -1 on error.
+int ReadFile(const FilePath& filename, char* data, int max_size);
+
+// Writes the given buffer into the file, overwriting any data that was
+// previously there. Returns the number of bytes written, or -1 on error.
+int WriteFile(const FilePath& filename, const char* data, int size);
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Appends |data| to |fd|. Does not close |fd| when done. Returns true iff
+// |size| bytes of |data| were written to |fd|.
+bool WriteFileDescriptor(const int fd, const char* data, int size);
+#endif
+
+// Appends |data| to |filename|. Returns true iff |size| bytes of |data| were
+// written to |filename|.
+bool AppendToFile(const FilePath& filename, const char* data, int size);
+
+// Gets the current working directory for the process.
+bool GetCurrentDirectory(FilePath* path);
+
+// Sets the current working directory for the process.
+bool SetCurrentDirectory(const FilePath& path);
+
+// Attempts to find a number that can be appended to the |path| to make it
+// unique. If |path| does not exist, 0 is returned. If it fails to find such
+// a number, -1 is returned. If |suffix| is not empty, also checks the
+// existence of it with the given suffix.
+int GetUniquePathNumber(const FilePath& path,
+ const FilePath::StringType& suffix);
+
+// Sets the given |fd| to non-blocking mode.
+// Returns true if it was able to set it in the non-blocking mode, otherwise
+// false.
+bool SetNonBlocking(int fd);
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Creates a non-blocking, close-on-exec pipe.
+// This creates a non-blocking pipe that is not intended to be shared with any
+// child process. This will be done atomically if the operating system supports
+// it. Returns true if it was able to create the pipe, otherwise false.
+bool CreateLocalNonBlockingPipe(int fds[2]);
+
+// Sets the given |fd| to close-on-exec mode.
+// Returns true if it was able to set it in the close-on-exec mode, otherwise
+// false.
+bool SetCloseOnExec(int fd);
+
+// Test that |path| can only be changed by a given user and members of
+// a given set of groups.
+// Specifically, test that all parts of |path| under (and including) |base|:
+// * Exist.
+// * Are owned by a specific user.
+// * Are not writable by all users.
+// * Are owned by a member of a given set of groups, or are not writable by
+// their group.
+// * Are not symbolic links.
+// This is useful for checking that a config file is administrator-controlled.
+// |base| must contain |path|.
+bool VerifyPathControlledByUser(const base::FilePath& base,
+ const base::FilePath& path,
+ uid_t owner_uid,
+ const std::set<gid_t>& group_gids);
+#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Is |path| writable only by a user with administrator privileges?
+// This function uses Mac OS conventions. The super user is assumed to have
+// uid 0, and the administrator group is assumed to be named "admin".
+// Testing that |path|, and every parent directory including the root of
+// the filesystem, are owned by the superuser, controlled by the group
+// "admin", are not writable by all users, and contain no symbolic links.
+// Will return false if |path| does not exist.
+bool VerifyPathControlledByAdmin(const base::FilePath& path);
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+// Returns the maximum length of path component on the volume containing
+// the directory |path|, in the number of FilePath::CharType, or -1 on failure.
+int GetMaximumPathComponentLength(const base::FilePath& path);
+
+#if defined(OS_LINUX) || defined(OS_AIX) || defined(OS_BSD)
+// Broad categories of file systems as returned by statfs() on Linux.
+enum FileSystemType {
+ FILE_SYSTEM_UNKNOWN, // statfs failed.
+ FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS.
+ FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2
+ FILE_SYSTEM_NFS,
+ FILE_SYSTEM_SMB,
+ FILE_SYSTEM_CODA,
+ FILE_SYSTEM_MEMORY, // in-memory file system
+ FILE_SYSTEM_CGROUP, // cgroup control.
+ FILE_SYSTEM_OTHER, // any other value.
+ FILE_SYSTEM_TYPE_COUNT
+};
+
+// Attempts determine the FileSystemType for |path|.
+// Returns false if |path| doesn't exist.
+bool GetFileSystemType(const FilePath& path, FileSystemType* type);
+#endif
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_UTIL_H_
diff --git a/gn/base/files/file_util_linux.cc b/gn/src/base/files/file_util_linux.cc
index b230fd96484..b230fd96484 100644
--- a/gn/base/files/file_util_linux.cc
+++ b/gn/src/base/files/file_util_linux.cc
diff --git a/gn/src/base/files/file_util_posix.cc b/gn/src/base/files/file_util_posix.cc
new file mode 100644
index 00000000000..1eef8e89aaa
--- /dev/null
+++ b/gn/src/base/files/file_util_posix.cc
@@ -0,0 +1,664 @@
+// 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 "base/files/file_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <iterator>
+
+#include "base/command_line.h"
+#include "base/containers/stack.h"
+#include "base/environment.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+#if !defined(OS_IOS)
+#include <grp.h>
+#endif
+
+// We need to do this on AIX due to some inconsistencies in how AIX
+// handles XOPEN_SOURCE and ALL_SOURCE.
+#if defined(OS_AIX)
+extern "C" char* mkdtemp(char* path);
+#endif
+
+namespace base {
+
+namespace {
+
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
+ defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21
+int CallStat(const char* path, stat_wrapper_t* sb) {
+ return stat(path, sb);
+}
+int CallLstat(const char* path, stat_wrapper_t* sb) {
+ return lstat(path, sb);
+}
+#else
+int CallStat(const char* path, stat_wrapper_t* sb) {
+ return stat64(path, sb);
+}
+int CallLstat(const char* path, stat_wrapper_t* sb) {
+ return lstat64(path, sb);
+}
+#endif
+
+// Helper for VerifyPathControlledByUser.
+bool VerifySpecificPathControlledByUser(const FilePath& path,
+ uid_t owner_uid,
+ const std::set<gid_t>& group_gids) {
+ stat_wrapper_t stat_info;
+ if (CallLstat(path.value().c_str(), &stat_info) != 0) {
+ DPLOG(ERROR) << "Failed to get information on path " << path.value();
+ return false;
+ }
+
+ if (S_ISLNK(stat_info.st_mode)) {
+ DLOG(ERROR) << "Path " << path.value() << " is a symbolic link.";
+ return false;
+ }
+
+ if (stat_info.st_uid != owner_uid) {
+ DLOG(ERROR) << "Path " << path.value() << " is owned by the wrong user.";
+ return false;
+ }
+
+ if ((stat_info.st_mode & S_IWGRP) &&
+ !ContainsKey(group_gids, stat_info.st_gid)) {
+ DLOG(ERROR) << "Path " << path.value()
+ << " is writable by an unprivileged group.";
+ return false;
+ }
+
+ if (stat_info.st_mode & S_IWOTH) {
+ DLOG(ERROR) << "Path " << path.value() << " is writable by any user.";
+ return false;
+ }
+
+ return true;
+}
+
+std::string TempFileName() {
+ return std::string(".org.chromium.Chromium.XXXXXX");
+}
+
+#if !defined(OS_MACOSX)
+bool CopyFileContents(File* infile, File* outfile) {
+ static constexpr size_t kBufferSize = 32768;
+ std::vector<char> buffer(kBufferSize);
+
+ for (;;) {
+ ssize_t bytes_read = infile->ReadAtCurrentPos(buffer.data(), buffer.size());
+ if (bytes_read < 0)
+ return false;
+ if (bytes_read == 0)
+ return true;
+ // Allow for partial writes
+ ssize_t bytes_written_per_read = 0;
+ do {
+ ssize_t bytes_written_partial = outfile->WriteAtCurrentPos(
+ &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read);
+ if (bytes_written_partial < 0)
+ return false;
+
+ bytes_written_per_read += bytes_written_partial;
+ } while (bytes_written_per_read < bytes_read);
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+// Appends |mode_char| to |mode| before the optional character set encoding; see
+// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for
+// details.
+std::string AppendModeCharacter(std::string_view mode, char mode_char) {
+ std::string result(mode);
+ size_t comma_pos = result.find(',');
+ result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1,
+ mode_char);
+ return result;
+}
+#endif
+
+} // namespace
+
+FilePath MakeAbsoluteFilePath(const FilePath& input) {
+ char full_path[PATH_MAX];
+ if (realpath(input.value().c_str(), full_path) == nullptr)
+ return FilePath();
+ return FilePath(full_path);
+}
+
+// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
+// which works both with and without the recursive flag. I'm not sure we need
+// that functionality. If not, remove from file_util_win.cc, otherwise add it
+// here.
+bool DeleteFile(const FilePath& path, bool recursive) {
+ const char* path_str = path.value().c_str();
+ stat_wrapper_t file_info;
+ if (CallLstat(path_str, &file_info) != 0) {
+ // The Windows version defines this condition as success.
+ return (errno == ENOENT || errno == ENOTDIR);
+ }
+ if (!S_ISDIR(file_info.st_mode))
+ return (unlink(path_str) == 0);
+ if (!recursive)
+ return (rmdir(path_str) == 0);
+
+ bool success = true;
+ stack<std::string> directories;
+ directories.push(path.value());
+ FileEnumerator traversal(path, true,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
+ FileEnumerator::SHOW_SYM_LINKS);
+ for (FilePath current = traversal.Next(); !current.empty();
+ current = traversal.Next()) {
+ if (traversal.GetInfo().IsDirectory())
+ directories.push(current.value());
+ else
+ success &= (unlink(current.value().c_str()) == 0);
+ }
+
+ while (!directories.empty()) {
+ FilePath dir = FilePath(directories.top());
+ directories.pop();
+ success &= (rmdir(dir.value().c_str()) == 0);
+ }
+ return success;
+}
+
+bool ReplaceFile(const FilePath& from_path,
+ const FilePath& to_path,
+ File::Error* error) {
+ if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
+ return true;
+ if (error)
+ *error = File::GetLastFileError();
+ return false;
+}
+
+bool CreateLocalNonBlockingPipe(int fds[2]) {
+#if defined(OS_LINUX) || defined(OS_BSD)
+ return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0;
+#else
+ int raw_fds[2];
+ if (pipe(raw_fds) != 0)
+ return false;
+ ScopedFD fd_out(raw_fds[0]);
+ ScopedFD fd_in(raw_fds[1]);
+ if (!SetCloseOnExec(fd_out.get()))
+ return false;
+ if (!SetCloseOnExec(fd_in.get()))
+ return false;
+ if (!SetNonBlocking(fd_out.get()))
+ return false;
+ if (!SetNonBlocking(fd_in.get()))
+ return false;
+ fds[0] = fd_out.release();
+ fds[1] = fd_in.release();
+ return true;
+#endif
+}
+
+bool SetNonBlocking(int fd) {
+ const int flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ return false;
+ if (flags & O_NONBLOCK)
+ return true;
+ if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1)
+ return false;
+ return true;
+}
+
+bool SetCloseOnExec(int fd) {
+ const int flags = fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return false;
+ if (flags & FD_CLOEXEC)
+ return true;
+ if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1)
+ return false;
+ return true;
+}
+
+bool PathExists(const FilePath& path) {
+ return access(path.value().c_str(), F_OK) == 0;
+}
+
+bool PathIsWritable(const FilePath& path) {
+ return access(path.value().c_str(), W_OK) == 0;
+}
+
+bool DirectoryExists(const FilePath& path) {
+ stat_wrapper_t file_info;
+ if (CallStat(path.value().c_str(), &file_info) != 0)
+ return false;
+ return S_ISDIR(file_info.st_mode);
+}
+
+#if !defined(OS_FUCHSIA)
+bool CreateSymbolicLink(const FilePath& target_path,
+ const FilePath& symlink_path) {
+ DCHECK(!symlink_path.empty());
+ DCHECK(!target_path.empty());
+ return ::symlink(target_path.value().c_str(), symlink_path.value().c_str()) !=
+ -1;
+}
+
+bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) {
+ DCHECK(!symlink_path.empty());
+ DCHECK(target_path);
+ char buf[PATH_MAX];
+ ssize_t count = ::readlink(symlink_path.value().c_str(), buf, std::size(buf));
+
+ if (count <= 0) {
+ target_path->clear();
+ return false;
+ }
+
+ *target_path = FilePath(FilePath::StringType(buf, count));
+ return true;
+}
+
+bool GetPosixFilePermissions(const FilePath& path, int* mode) {
+ DCHECK(mode);
+
+ stat_wrapper_t file_info;
+ // Uses stat(), because on symbolic link, lstat() does not return valid
+ // permission bits in st_mode
+ if (CallStat(path.value().c_str(), &file_info) != 0)
+ return false;
+
+ *mode = file_info.st_mode & FILE_PERMISSION_MASK;
+ return true;
+}
+
+bool SetPosixFilePermissions(const FilePath& path, int mode) {
+ DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0);
+
+ // Calls stat() so that we can preserve the higher bits like S_ISGID.
+ stat_wrapper_t stat_buf;
+ if (CallStat(path.value().c_str(), &stat_buf) != 0)
+ return false;
+
+ // Clears the existing permission bits, and adds the new ones.
+ mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK;
+ updated_mode_bits |= mode & FILE_PERMISSION_MASK;
+
+ if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0)
+ return false;
+
+ return true;
+}
+
+bool ExecutableExistsInPath(Environment* env,
+ const FilePath::StringType& executable) {
+ std::string path;
+ if (!env->GetVar("PATH", &path)) {
+ LOG(ERROR) << "No $PATH variable. Assuming no " << executable << ".";
+ return false;
+ }
+
+ for (const std::string_view& cur_path :
+ SplitStringPiece(path, ":", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
+ FilePath file(cur_path);
+ int permissions;
+ if (GetPosixFilePermissions(file.Append(executable), &permissions) &&
+ (permissions & FILE_PERMISSION_EXECUTE_BY_USER))
+ return true;
+ }
+ return false;
+}
+
+#endif // !OS_FUCHSIA
+
+bool GetTempDir(FilePath* path) {
+ const char* tmp = getenv("TMPDIR");
+ if (tmp) {
+ *path = FilePath(tmp);
+ return true;
+ }
+
+ *path = FilePath("/tmp");
+ return true;
+}
+
+#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm.
+FilePath GetHomeDir() {
+ const char* home_dir = getenv("HOME");
+ if (home_dir && home_dir[0])
+ return FilePath(home_dir);
+
+ FilePath rv;
+ if (GetTempDir(&rv))
+ return rv;
+
+ // Last resort.
+ return FilePath("/tmp");
+}
+#endif // !defined(OS_MACOSX)
+
+static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
+ const FilePath::StringType& name_tmpl,
+ FilePath* new_dir) {
+ DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
+ << "Directory name template must contain \"XXXXXX\".";
+
+ FilePath sub_dir = base_dir.Append(name_tmpl);
+ std::string sub_dir_string = sub_dir.value();
+
+ // this should be OK since mkdtemp just replaces characters in place
+ char* buffer = const_cast<char*>(sub_dir_string.c_str());
+ char* dtemp = mkdtemp(buffer);
+ if (!dtemp) {
+ DPLOG(ERROR) << "mkdtemp";
+ return false;
+ }
+ *new_dir = FilePath(dtemp);
+ return true;
+}
+
+bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir) {
+ FilePath::StringType mkdtemp_template = prefix;
+ mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
+ return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
+}
+
+bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path) {
+ FilePath tmpdir;
+ if (!GetTempDir(&tmpdir))
+ return false;
+
+ return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path);
+}
+
+bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
+ std::vector<FilePath> subpaths;
+
+ // Collect a list of all parent directories.
+ FilePath last_path = full_path;
+ subpaths.push_back(full_path);
+ for (FilePath path = full_path.DirName(); path.value() != last_path.value();
+ path = path.DirName()) {
+ subpaths.push_back(path);
+ last_path = path;
+ }
+
+ // Iterate through the parents and create the missing ones.
+ for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
+ i != subpaths.rend(); ++i) {
+ if (DirectoryExists(*i))
+ continue;
+ if (mkdir(i->value().c_str(), 0700) == 0)
+ continue;
+ // Mkdir failed, but it might have failed with EEXIST, or some other error
+ // due to the the directory appearing out of thin air. This can occur if
+ // two processes are trying to create the same file system tree at the same
+ // time. Check to see if it exists and make sure it is a directory.
+ int saved_errno = errno;
+ if (!DirectoryExists(*i)) {
+ if (error)
+ *error = File::OSErrorToFileError(saved_errno);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
+ FilePath real_path_result = MakeAbsoluteFilePath(path);
+ if (real_path_result.empty())
+ return false;
+
+ // To be consistant with windows, fail if |real_path_result| is a
+ // directory.
+ if (DirectoryExists(real_path_result))
+ return false;
+
+ *normalized_path = real_path_result;
+ return true;
+}
+
+// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
+// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948
+bool IsLink(const FilePath& file_path) {
+ stat_wrapper_t st;
+ // If we can't lstat the file, it's safe to assume that the file won't at
+ // least be a 'followable' link.
+ if (CallLstat(file_path.value().c_str(), &st) != 0)
+ return false;
+ return S_ISLNK(st.st_mode);
+}
+
+bool GetFileInfo(const FilePath& file_path, File::Info* results) {
+ stat_wrapper_t file_info;
+ if (CallStat(file_path.value().c_str(), &file_info) != 0)
+ return false;
+
+ results->FromStat(file_info);
+ return true;
+}
+
+FILE* OpenFile(const FilePath& filename, const char* mode) {
+ // 'e' is unconditionally added below, so be sure there is not one already
+ // present before a comma in |mode|.
+ DCHECK(
+ strchr(mode, 'e') == nullptr ||
+ (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ',')));
+ FILE* result = nullptr;
+#if defined(OS_MACOSX)
+ // macOS does not provide a mode character to set O_CLOEXEC; see
+ // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html.
+ const char* the_mode = mode;
+#else
+ std::string mode_with_e(AppendModeCharacter(mode, 'e'));
+ const char* the_mode = mode_with_e.c_str();
+#endif
+ do {
+ result = fopen(filename.value().c_str(), the_mode);
+ } while (!result && errno == EINTR);
+#if defined(OS_MACOSX)
+ // Mark the descriptor as close-on-exec.
+ if (result)
+ SetCloseOnExec(fileno(result));
+#endif
+ return result;
+}
+
+FILE* FileToFILE(File file, const char* mode) {
+ FILE* stream = fdopen(file.GetPlatformFile(), mode);
+ if (stream)
+ file.TakePlatformFile();
+ return stream;
+}
+
+int ReadFile(const FilePath& filename, char* data, int max_size) {
+ int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
+ if (fd < 0)
+ return -1;
+
+ ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size));
+ if (IGNORE_EINTR(close(fd)) < 0)
+ return -1;
+ return bytes_read;
+}
+
+int WriteFile(const FilePath& filename, const char* data, int size) {
+ int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
+ if (fd < 0)
+ return -1;
+
+ int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1;
+ if (IGNORE_EINTR(close(fd)) < 0)
+ return -1;
+ return bytes_written;
+}
+
+bool WriteFileDescriptor(const int fd, const char* data, int size) {
+ // Allow for partial writes.
+ ssize_t bytes_written_total = 0;
+ for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
+ bytes_written_total += bytes_written_partial) {
+ bytes_written_partial = HANDLE_EINTR(
+ write(fd, data + bytes_written_total, size - bytes_written_total));
+ if (bytes_written_partial < 0)
+ return false;
+ }
+
+ return true;
+}
+
+bool AppendToFile(const FilePath& filename, const char* data, int size) {
+ bool ret = true;
+ int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND));
+ if (fd < 0) {
+ return false;
+ }
+
+ // This call will either write all of the data or return false.
+ if (!WriteFileDescriptor(fd, data, size)) {
+ ret = false;
+ }
+
+ if (IGNORE_EINTR(close(fd)) < 0) {
+ return false;
+ }
+
+ return ret;
+}
+
+bool GetCurrentDirectory(FilePath* dir) {
+ char system_buffer[PATH_MAX] = "";
+ if (!getcwd(system_buffer, sizeof(system_buffer))) {
+ NOTREACHED();
+ return false;
+ }
+ *dir = FilePath(system_buffer);
+ return true;
+}
+
+bool SetCurrentDirectory(const FilePath& path) {
+ return chdir(path.value().c_str()) == 0;
+}
+
+bool VerifyPathControlledByUser(const FilePath& base,
+ const FilePath& path,
+ uid_t owner_uid,
+ const std::set<gid_t>& group_gids) {
+ if (base != path && !base.IsParent(path)) {
+ DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \""
+ << base.value() << "\", path = \"" << path.value() << "\"";
+ return false;
+ }
+
+ std::vector<FilePath::StringType> base_components;
+ std::vector<FilePath::StringType> path_components;
+
+ base.GetComponents(&base_components);
+ path.GetComponents(&path_components);
+
+ std::vector<FilePath::StringType>::const_iterator ib, ip;
+ for (ib = base_components.begin(), ip = path_components.begin();
+ ib != base_components.end(); ++ib, ++ip) {
+ // |base| must be a subpath of |path|, so all components should match.
+ // If these CHECKs fail, look at the test that base is a parent of
+ // path at the top of this function.
+ DCHECK(ip != path_components.end());
+ DCHECK(*ip == *ib);
+ }
+
+ FilePath current_path = base;
+ if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids))
+ return false;
+
+ for (; ip != path_components.end(); ++ip) {
+ current_path = current_path.Append(*ip);
+ if (!VerifySpecificPathControlledByUser(current_path, owner_uid,
+ group_gids))
+ return false;
+ }
+ return true;
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+bool VerifyPathControlledByAdmin(const FilePath& path) {
+ const unsigned kRootUid = 0;
+ const FilePath kFileSystemRoot("/");
+
+ // The name of the administrator group on mac os.
+ const char* const kAdminGroupNames[] = {"admin", "wheel"};
+
+ std::set<gid_t> allowed_group_ids;
+ for (int i = 0, ie = std::size(kAdminGroupNames); i < ie; ++i) {
+ struct group* group_record = getgrnam(kAdminGroupNames[i]);
+ if (!group_record) {
+ DPLOG(ERROR) << "Could not get the group ID of group \""
+ << kAdminGroupNames[i] << "\".";
+ continue;
+ }
+
+ allowed_group_ids.insert(group_record->gr_gid);
+ }
+
+ return VerifyPathControlledByUser(kFileSystemRoot, path, kRootUid,
+ allowed_group_ids);
+}
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+int GetMaximumPathComponentLength(const FilePath& path) {
+ return pathconf(path.value().c_str(), _PC_NAME_MAX);
+}
+
+#if !defined(OS_MACOSX)
+// Mac has its own implementation, this is for all other Posix systems.
+bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
+ File infile;
+ infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);
+ if (!infile.IsValid())
+ return false;
+
+ File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS);
+ if (!outfile.IsValid())
+ return false;
+
+ return CopyFileContents(&infile, &outfile);
+}
+#endif // !defined(OS_MACOSX)
+
+} // namespace base
diff --git a/gn/src/base/files/file_util_win.cc b/gn/src/base/files/file_util_win.cc
new file mode 100644
index 00000000000..a18715e12e4
--- /dev/null
+++ b/gn/src/base/files/file_util_win.cc
@@ -0,0 +1,635 @@
+// 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 "base/files/file_util.h"
+
+// windows.h includes winsock.h which isn't compatible with winsock2.h. To use winsock2.h
+// you have to include it first.
+#include <winsock2.h>
+#include <windows.h>
+
+#include <io.h>
+#include <psapi.h>
+#include <share.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <string>
+#include <string_view>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+
+// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
+// "Community Additions" comment on MSDN here:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
+#define SystemFunction036 NTAPI SystemFunction036
+#include <ntsecapi.h>
+#undef SystemFunction036
+
+namespace base {
+
+namespace {
+
+const DWORD kFileShareAll =
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+// Deletes all files and directories in a path.
+// Returns ERROR_SUCCESS on success or the Windows error code corresponding to
+// the first error encountered.
+DWORD DeleteFileRecursive(const FilePath& path,
+ const FilePath::StringType& pattern,
+ bool recursive) {
+ FileEnumerator traversal(path, false,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
+ pattern);
+ DWORD result = ERROR_SUCCESS;
+ for (FilePath current = traversal.Next(); !current.empty();
+ current = traversal.Next()) {
+ // Try to clear the read-only bit if we find it.
+ FileEnumerator::FileInfo info = traversal.GetInfo();
+ if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
+ (recursive || !info.IsDirectory())) {
+ ::SetFileAttributes(
+ ToWCharT(&current.value()),
+ info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
+ }
+
+ DWORD this_result = ERROR_SUCCESS;
+ if (info.IsDirectory()) {
+ if (recursive) {
+ this_result = DeleteFileRecursive(current, pattern, true);
+ if (this_result == ERROR_SUCCESS &&
+ !::RemoveDirectory(ToWCharT(&current.value()))) {
+ this_result = ::GetLastError();
+ }
+ }
+ } else if (!::DeleteFile(ToWCharT(&current.value()))) {
+ this_result = ::GetLastError();
+ }
+ if (result == ERROR_SUCCESS)
+ result = this_result;
+ }
+ return result;
+}
+
+// Appends |mode_char| to |mode| before the optional character set encoding; see
+// https://msdn.microsoft.com/library/yeby3zcb.aspx for details.
+void AppendModeCharacter(char16_t mode_char, std::u16string* mode) {
+ size_t comma_pos = mode->find(L',');
+ mode->insert(comma_pos == std::u16string::npos ? mode->length() : comma_pos,
+ 1, mode_char);
+}
+
+// Returns ERROR_SUCCESS on success, or a Windows error code on failure.
+DWORD DoDeleteFile(const FilePath& path, bool recursive) {
+ if (path.empty())
+ return ERROR_SUCCESS;
+
+ if (path.value().length() >= MAX_PATH)
+ return ERROR_BAD_PATHNAME;
+
+ // Handle any path with wildcards.
+ if (path.BaseName().value().find_first_of(u"*?") !=
+ FilePath::StringType::npos) {
+ return DeleteFileRecursive(path.DirName(), path.BaseName().value(),
+ recursive);
+ }
+
+ // Report success if the file or path does not exist.
+ const DWORD attr = ::GetFileAttributes(ToWCharT(&path.value()));
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ const DWORD error_code = ::GetLastError();
+ return (error_code == ERROR_FILE_NOT_FOUND ||
+ error_code == ERROR_PATH_NOT_FOUND)
+ ? ERROR_SUCCESS
+ : error_code;
+ }
+
+ // Clear the read-only bit if it is set.
+ if ((attr & FILE_ATTRIBUTE_READONLY) &&
+ !::SetFileAttributes(ToWCharT(&path.value()),
+ attr & ~FILE_ATTRIBUTE_READONLY)) {
+ return ::GetLastError();
+ }
+
+ // Perform a simple delete on anything that isn't a directory.
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ return ::DeleteFile(ToWCharT(&path.value())) ? ERROR_SUCCESS
+ : ::GetLastError();
+ }
+
+ if (recursive) {
+ const DWORD error_code = DeleteFileRecursive(path, u"*", true);
+ if (error_code != ERROR_SUCCESS)
+ return error_code;
+ }
+ return ::RemoveDirectory(ToWCharT(&path.value())) ? ERROR_SUCCESS
+ : ::GetLastError();
+}
+
+std::string RandomDataToGUIDString(const uint64_t bytes[2]) {
+ return base::StringPrintf(
+ "%08x-%04x-%04x-%04x-%012llx", static_cast<unsigned int>(bytes[0] >> 32),
+ static_cast<unsigned int>((bytes[0] >> 16) & 0x0000ffff),
+ static_cast<unsigned int>(bytes[0] & 0x0000ffff),
+ static_cast<unsigned int>(bytes[1] >> 48),
+ bytes[1] & 0x0000ffff'ffffffffULL);
+}
+
+void RandBytes(void* output, size_t output_length) {
+ char* output_ptr = static_cast<char*>(output);
+ while (output_length > 0) {
+ const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min(
+ output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max())));
+ const bool success =
+ RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE;
+ CHECK(success);
+ output_length -= output_bytes_this_pass;
+ output_ptr += output_bytes_this_pass;
+ }
+}
+
+std::string GenerateGUID() {
+ uint64_t sixteen_bytes[2];
+ // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the
+ // base version directly, and to prevent the dependency from base/ to crypto/.
+ RandBytes(&sixteen_bytes, sizeof(sixteen_bytes));
+
+ // Set the GUID to version 4 as described in RFC 4122, section 4.4.
+ // The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,
+ // where y is one of [8, 9, A, B].
+
+ // Clear the version bits and set the version to 4:
+ sixteen_bytes[0] &= 0xffffffff'ffff0fffULL;
+ sixteen_bytes[0] |= 0x00000000'00004000ULL;
+
+ // Set the two most significant bits (bits 6 and 7) of the
+ // clock_seq_hi_and_reserved to zero and one, respectively:
+ sixteen_bytes[1] &= 0x3fffffff'ffffffffULL;
+ sixteen_bytes[1] |= 0x80000000'00000000ULL;
+
+ return RandomDataToGUIDString(sixteen_bytes);
+}
+
+} // namespace
+
+FilePath MakeAbsoluteFilePath(const FilePath& input) {
+ char16_t file_path[MAX_PATH];
+ if (!_wfullpath(ToWCharT(file_path), ToWCharT(&input.value()), MAX_PATH))
+ return FilePath();
+ return FilePath(file_path);
+}
+
+bool DeleteFile(const FilePath& path, bool recursive) {
+ static constexpr char kRecursive[] = "DeleteFile.Recursive";
+ static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive";
+ const std::string_view operation(recursive ? kRecursive : kNonRecursive);
+
+ // Metrics for delete failures tracked in https://crbug.com/599084. Delete may
+ // fail for a number of reasons. Log some metrics relating to failures in the
+ // current code so that any improvements or regressions resulting from
+ // subsequent code changes can be detected.
+ const DWORD error = DoDeleteFile(path, recursive);
+ return error == ERROR_SUCCESS;
+}
+
+bool DeleteFileAfterReboot(const FilePath& path) {
+ if (path.value().length() >= MAX_PATH)
+ return false;
+
+ return MoveFileEx(ToWCharT(&path.value()), NULL,
+ MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING) !=
+ FALSE;
+}
+
+bool ReplaceFile(const FilePath& from_path,
+ const FilePath& to_path,
+ File::Error* error) {
+ // Try a simple move first. It will only succeed when |to_path| doesn't
+ // already exist.
+ if (::MoveFile(ToWCharT(&from_path.value()), ToWCharT(&to_path.value())))
+ return true;
+ File::Error move_error = File::OSErrorToFileError(GetLastError());
+
+ // Try the full-blown replace if the move fails, as ReplaceFile will only
+ // succeed when |to_path| does exist. When writing to a network share, we may
+ // not be able to change the ACLs. Ignore ACL errors then
+ // (REPLACEFILE_IGNORE_MERGE_ERRORS).
+ if (::ReplaceFile(ToWCharT(&to_path.value()), ToWCharT(&from_path.value()),
+ NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
+ return true;
+ }
+ // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
+ // |to_path| does not exist. In this case, the more relevant error comes
+ // from the call to MoveFile.
+ if (error) {
+ File::Error replace_error = File::OSErrorToFileError(GetLastError());
+ *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error
+ : replace_error;
+ }
+ return false;
+}
+
+bool PathExists(const FilePath& path) {
+ return (GetFileAttributes(ToWCharT(&path.value())) !=
+ INVALID_FILE_ATTRIBUTES);
+}
+
+bool PathIsWritable(const FilePath& path) {
+ HANDLE dir =
+ CreateFile(ToWCharT(&path.value()), FILE_ADD_FILE, kFileShareAll, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ if (dir == INVALID_HANDLE_VALUE)
+ return false;
+
+ CloseHandle(dir);
+ return true;
+}
+
+bool DirectoryExists(const FilePath& path) {
+ DWORD fileattr = GetFileAttributes(ToWCharT(&path.value()));
+ if (fileattr != INVALID_FILE_ATTRIBUTES)
+ return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ return false;
+}
+
+bool GetTempDir(FilePath* path) {
+ char16_t temp_path[MAX_PATH + 1];
+ DWORD path_len = ::GetTempPath(MAX_PATH, ToWCharT(temp_path));
+ if (path_len >= MAX_PATH || path_len <= 0)
+ return false;
+ // TODO(evanm): the old behavior of this function was to always strip the
+ // trailing slash. We duplicate this here, but it shouldn't be necessary
+ // when everyone is using the appropriate FilePath APIs.
+ *path = FilePath(temp_path).StripTrailingSeparators();
+ return true;
+}
+
+bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir) {
+ FilePath path_to_create;
+
+ for (int count = 0; count < 50; ++count) {
+ // Try create a new temporary directory with random generated name. If
+ // the one exists, keep trying another path name until we reach some limit.
+ std::u16string new_dir_name;
+ new_dir_name.assign(prefix);
+ new_dir_name.append(IntToString16(::GetCurrentProcessId()));
+ new_dir_name.push_back('_');
+ new_dir_name.append(UTF8ToUTF16(GenerateGUID()));
+
+ path_to_create = base_dir.Append(new_dir_name);
+ if (::CreateDirectory(ToWCharT(&path_to_create.value()), NULL)) {
+ *new_dir = path_to_create;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path) {
+ FilePath system_temp_dir;
+ if (!GetTempDir(&system_temp_dir))
+ return false;
+
+ return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
+}
+
+bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
+ // If the path exists, we've succeeded if it's a directory, failed otherwise.
+ DWORD fileattr = ::GetFileAttributes(ToWCharT(&full_path.value()));
+ if (fileattr != INVALID_FILE_ATTRIBUTES) {
+ if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ return true;
+ }
+ DLOG(WARNING) << "CreateDirectory(" << UTF16ToUTF8(full_path.value())
+ << "), "
+ << "conflicts with existing file.";
+ if (error) {
+ *error = File::FILE_ERROR_NOT_A_DIRECTORY;
+ }
+ return false;
+ }
+
+ // Invariant: Path does not exist as file or directory.
+
+ // Attempt to create the parent recursively. This will immediately return
+ // true if it already exists, otherwise will create all required parent
+ // directories starting with the highest-level missing parent.
+ FilePath parent_path(full_path.DirName());
+ if (parent_path.value() == full_path.value()) {
+ if (error) {
+ *error = File::FILE_ERROR_NOT_FOUND;
+ }
+ return false;
+ }
+ if (!CreateDirectoryAndGetError(parent_path, error)) {
+ DLOG(WARNING) << "Failed to create one of the parent directories.";
+ if (error) {
+ DCHECK(*error != File::FILE_OK);
+ }
+ return false;
+ }
+
+ if (!::CreateDirectory(ToWCharT(&full_path.value()), NULL)) {
+ DWORD error_code = ::GetLastError();
+ if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
+ // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we
+ // were racing with someone creating the same directory, or a file
+ // with the same path. If DirectoryExists() returns true, we lost the
+ // race to create the same directory.
+ return true;
+ } else {
+ if (error)
+ *error = File::OSErrorToFileError(error_code);
+ DLOG(WARNING) << "Failed to create directory "
+ << UTF16ToUTF8(full_path.value()) << ", last error is "
+ << error_code << ".";
+ return false;
+ }
+ } else {
+ return true;
+ }
+}
+
+bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
+ FilePath mapped_file;
+ if (!NormalizeToNativeFilePath(path, &mapped_file))
+ return false;
+ // NormalizeToNativeFilePath() will return a path that starts with
+ // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath()
+ // will find a drive letter which maps to the path's device, so
+ // that we return a path starting with a drive letter.
+ return DevicePathToDriveLetterPath(mapped_file, real_path);
+}
+
+bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
+ FilePath* out_drive_letter_path) {
+ // Get the mapping of drive letters to device paths.
+ const int kDriveMappingSize = 1024;
+ char16_t drive_mapping[kDriveMappingSize] = {'\0'};
+ if (!::GetLogicalDriveStrings(kDriveMappingSize - 1,
+ ToWCharT(drive_mapping))) {
+ DLOG(ERROR) << "Failed to get drive mapping.";
+ return false;
+ }
+
+ // The drive mapping is a sequence of null terminated strings.
+ // The last string is empty.
+ char16_t* drive_map_ptr = drive_mapping;
+ char16_t device_path_as_string[MAX_PATH];
+ char16_t drive[] = u" :";
+
+ // For each string in the drive mapping, get the junction that links
+ // to it. If that junction is a prefix of |device_path|, then we
+ // know that |drive| is the real path prefix.
+ while (*drive_map_ptr) {
+ drive[0] = drive_map_ptr[0]; // Copy the drive letter.
+
+ if (QueryDosDevice(ToWCharT(drive), ToWCharT(device_path_as_string),
+ MAX_PATH)) {
+ FilePath device_path(device_path_as_string);
+ if (device_path == nt_device_path ||
+ device_path.IsParent(nt_device_path)) {
+ *out_drive_letter_path =
+ FilePath(drive + nt_device_path.value().substr(
+ wcslen(ToWCharT(device_path_as_string))));
+ return true;
+ }
+ }
+ // Move to the next drive letter string, which starts one
+ // increment after the '\0' that terminates the current string.
+ while (*drive_map_ptr++) {
+ }
+ }
+
+ // No drive matched. The path does not start with a device junction
+ // that is mounted as a drive letter. This means there is no drive
+ // letter path to the volume that holds |device_path|, so fail.
+ return false;
+}
+
+bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
+ // In Vista, GetFinalPathNameByHandle() would give us the real path
+ // from a file handle. If we ever deprecate XP, consider changing the
+ // code below to a call to GetFinalPathNameByHandle(). The method this
+ // function uses is explained in the following msdn article:
+ // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
+ win::ScopedHandle file_handle(
+ ::CreateFile(ToWCharT(&path.value()), GENERIC_READ, kFileShareAll, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ if (!file_handle.IsValid())
+ return false;
+
+ // Create a file mapping object. Can't easily use MemoryMappedFile, because
+ // we only map the first byte, and need direct access to the handle. You can
+ // not map an empty file, this call fails in that case.
+ win::ScopedHandle file_map_handle(
+ ::CreateFileMapping(file_handle.Get(), NULL, PAGE_READONLY, 0,
+ 1, // Just one byte. No need to look at the data.
+ NULL));
+ if (!file_map_handle.IsValid())
+ return false;
+
+ // Use a view of the file to get the path to the file.
+ void* file_view =
+ MapViewOfFile(file_map_handle.Get(), FILE_MAP_READ, 0, 0, 1);
+ if (!file_view)
+ return false;
+
+ // The expansion of |path| into a full path may make it longer.
+ // GetMappedFileName() will fail if the result is longer than MAX_PATH.
+ // Pad a bit to be safe. If kMaxPathLength is ever changed to be less
+ // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
+ // not return kMaxPathLength. This would mean that only part of the
+ // path fit in |mapped_file_path|.
+ const int kMaxPathLength = MAX_PATH + 10;
+ char16_t mapped_file_path[kMaxPathLength];
+ bool success = false;
+ HANDLE cp = GetCurrentProcess();
+ if (::GetMappedFileNameW(cp, file_view, ToWCharT(mapped_file_path),
+ kMaxPathLength)) {
+ *nt_path = FilePath(mapped_file_path);
+ success = true;
+ }
+ ::UnmapViewOfFile(file_view);
+ return success;
+}
+
+// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
+// them if we do decide to.
+bool IsLink(const FilePath& file_path) {
+ return false;
+}
+
+bool GetFileInfo(const FilePath& file_path, File::Info* results) {
+ WIN32_FILE_ATTRIBUTE_DATA attr;
+ if (!GetFileAttributesEx(ToWCharT(&file_path.value()), GetFileExInfoStandard,
+ &attr)) {
+ return false;
+ }
+
+ ULARGE_INTEGER size;
+ size.HighPart = attr.nFileSizeHigh;
+ size.LowPart = attr.nFileSizeLow;
+ results->size = size.QuadPart;
+
+ results->is_directory =
+ (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ results->last_modified = *reinterpret_cast<uint64_t*>(&attr.ftLastWriteTime);
+ results->last_accessed = *reinterpret_cast<uint64_t*>(&attr.ftLastAccessTime);
+ results->creation_time = *reinterpret_cast<uint64_t*>(&attr.ftCreationTime);
+
+ return true;
+}
+
+FILE* OpenFile(const FilePath& filename, const char* mode) {
+ // 'N' is unconditionally added below, so be sure there is not one already
+ // present before a comma in |mode|.
+ DCHECK(
+ strchr(mode, 'N') == nullptr ||
+ (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ',')));
+ std::u16string w_mode = ASCIIToUTF16(mode);
+ AppendModeCharacter(L'N', &w_mode);
+ return _wfsopen(ToWCharT(&filename.value()), ToWCharT(&w_mode), _SH_DENYNO);
+}
+
+FILE* FileToFILE(File file, const char* mode) {
+ if (!file.IsValid())
+ return NULL;
+ int fd =
+ _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0);
+ if (fd < 0)
+ return NULL;
+ file.TakePlatformFile();
+ FILE* stream = _fdopen(fd, mode);
+ if (!stream)
+ _close(fd);
+ return stream;
+}
+
+int ReadFile(const FilePath& filename, char* data, int max_size) {
+ win::ScopedHandle file(CreateFile(ToWCharT(&filename.value()), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL));
+ if (!file.IsValid())
+ return -1;
+
+ DWORD read;
+ if (::ReadFile(file.Get(), data, max_size, &read, NULL))
+ return read;
+
+ return -1;
+}
+
+int WriteFile(const FilePath& filename, const char* data, int size) {
+ win::ScopedHandle file(CreateFile(ToWCharT(&filename.value()), GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL));
+ if (!file.IsValid()) {
+ DPLOG(WARNING) << "CreateFile failed for path "
+ << UTF16ToUTF8(filename.value());
+ return -1;
+ }
+
+ DWORD written;
+ BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
+ if (result && static_cast<int>(written) == size)
+ return written;
+
+ if (!result) {
+ // WriteFile failed.
+ DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value())
+ << " failed";
+ } else {
+ // Didn't write all the bytes.
+ DLOG(WARNING) << "wrote" << written << " bytes to "
+ << UTF16ToUTF8(filename.value()) << " expected " << size;
+ }
+ return -1;
+}
+
+bool AppendToFile(const FilePath& filename, const char* data, int size) {
+ win::ScopedHandle file(CreateFile(ToWCharT(&filename.value()),
+ FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0,
+ NULL));
+ if (!file.IsValid()) {
+ return false;
+ }
+
+ DWORD written;
+ BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL);
+ if (result && static_cast<int>(written) == size)
+ return true;
+
+ return false;
+}
+
+bool GetCurrentDirectory(FilePath* dir) {
+ char16_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+ DWORD len = ::GetCurrentDirectory(MAX_PATH, ToWCharT(system_buffer));
+ if (len == 0 || len > MAX_PATH)
+ return false;
+ // TODO(evanm): the old behavior of this function was to always strip the
+ // trailing slash. We duplicate this here, but it shouldn't be necessary
+ // when everyone is using the appropriate FilePath APIs.
+ std::u16string dir_str(system_buffer);
+ *dir = FilePath(dir_str).StripTrailingSeparators();
+ return true;
+}
+
+bool SetCurrentDirectory(const FilePath& directory) {
+ return ::SetCurrentDirectory(ToWCharT(&directory.value())) != 0;
+}
+
+int GetMaximumPathComponentLength(const FilePath& path) {
+ char16_t volume_path[MAX_PATH];
+ if (!GetVolumePathNameW(ToWCharT(&path.NormalizePathSeparators().value()),
+ ToWCharT(volume_path), std::size(volume_path))) {
+ return -1;
+ }
+
+ DWORD max_length = 0;
+ if (!GetVolumeInformationW(ToWCharT(volume_path), NULL, 0, NULL, &max_length,
+ NULL, NULL, 0)) {
+ return -1;
+ }
+
+ // Length of |path| with path separator appended.
+ size_t prefix = path.StripTrailingSeparators().value().size() + 1;
+ // The whole path string must be shorter than MAX_PATH. That is, it must be
+ // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1).
+ int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix));
+ return std::min(whole_path_limit, static_cast<int>(max_length));
+}
+
+bool SetNonBlocking(int fd) {
+ unsigned long nonblocking = 1;
+ if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0)
+ return true;
+ return false;
+}
+
+} // namespace base
diff --git a/gn/src/base/files/file_win.cc b/gn/src/base/files/file_win.cc
new file mode 100644
index 00000000000..e9c90e3507b
--- /dev/null
+++ b/gn/src/base/files/file_win.cc
@@ -0,0 +1,324 @@
+// 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 "base/files/file.h"
+
+#include <io.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/win/win_util.h"
+
+#include <windows.h>
+
+namespace base {
+
+// Make sure our Whence mappings match the system headers.
+static_assert(File::FROM_BEGIN == FILE_BEGIN &&
+ File::FROM_CURRENT == FILE_CURRENT &&
+ File::FROM_END == FILE_END,
+ "whence mapping must match the system headers");
+
+bool File::IsValid() const {
+ return file_.IsValid();
+}
+
+PlatformFile File::GetPlatformFile() const {
+ return file_.Get();
+}
+
+PlatformFile File::TakePlatformFile() {
+ return file_.Take();
+}
+
+void File::Close() {
+ if (!file_.IsValid())
+ return;
+
+ file_.Close();
+}
+
+int64_t File::Seek(Whence whence, int64_t offset) {
+ DCHECK(IsValid());
+
+ LARGE_INTEGER distance, res;
+ distance.QuadPart = offset;
+ DWORD move_method = static_cast<DWORD>(whence);
+ if (!SetFilePointerEx(file_.Get(), distance, &res, move_method))
+ return -1;
+ return res.QuadPart;
+}
+
+int File::Read(int64_t offset, char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_read;
+ if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped))
+ return bytes_read;
+ if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
+}
+
+int File::ReadAtCurrentPos(char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ DWORD bytes_read;
+ if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL))
+ return bytes_read;
+ if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
+}
+
+int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
+ // TODO(dbeam): trace this separately?
+ return Read(offset, data, size);
+}
+
+int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
+ // TODO(dbeam): trace this separately?
+ return ReadAtCurrentPos(data, size);
+}
+
+int File::Write(int64_t offset, const char* data, int size) {
+ DCHECK(IsValid());
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_written;
+ if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped))
+ return bytes_written;
+
+ return -1;
+}
+
+int File::WriteAtCurrentPos(const char* data, int size) {
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ DWORD bytes_written;
+ if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL))
+ return bytes_written;
+
+ return -1;
+}
+
+int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
+ return WriteAtCurrentPos(data, size);
+}
+
+int64_t File::GetLength() {
+ DCHECK(IsValid());
+
+ LARGE_INTEGER size;
+ if (!::GetFileSizeEx(file_.Get(), &size))
+ return -1;
+
+ return static_cast<int64_t>(size.QuadPart);
+}
+
+bool File::SetLength(int64_t length) {
+ DCHECK(IsValid());
+
+ // Get the current file pointer.
+ LARGE_INTEGER file_pointer;
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT))
+ return false;
+
+ LARGE_INTEGER length_li;
+ length_li.QuadPart = length;
+ // If length > file size, SetFilePointerEx() should extend the file
+ // with zeroes on all Windows standard file systems (NTFS, FATxx).
+ if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN))
+ return false;
+
+ // Set the new file length and move the file pointer to its old position.
+ // This is consistent with ftruncate()'s behavior, even when the file
+ // pointer points to a location beyond the end of the file.
+ // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
+ // promised by the interface (nor was promised by PlatformFile). See if this
+ // implementation detail can be removed.
+ return ((::SetEndOfFile(file_.Get()) != FALSE) &&
+ (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) !=
+ FALSE));
+}
+
+bool File::GetInfo(Info* info) {
+ DCHECK(IsValid());
+
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (!GetFileInformationByHandle(file_.Get(), &file_info))
+ return false;
+
+ LARGE_INTEGER size;
+ size.HighPart = file_info.nFileSizeHigh;
+ size.LowPart = file_info.nFileSizeLow;
+ info->size = size.QuadPart;
+ info->is_directory =
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ info->is_symbolic_link = false; // Windows doesn't have symbolic links.
+ info->last_modified =
+ *reinterpret_cast<uint64_t*>(&file_info.ftLastWriteTime);
+ info->last_accessed =
+ *reinterpret_cast<uint64_t*>(&file_info.ftLastAccessTime);
+ info->creation_time = *reinterpret_cast<uint64_t*>(&file_info.ftCreationTime);
+ return true;
+}
+
+File::Error File::Lock() {
+ DCHECK(IsValid());
+
+ BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
+ if (!result)
+ return GetLastFileError();
+ return FILE_OK;
+}
+
+File::Error File::Unlock() {
+ DCHECK(IsValid());
+
+ BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
+ if (!result)
+ return GetLastFileError();
+ return FILE_OK;
+}
+
+File File::Duplicate() const {
+ if (!IsValid())
+ return File();
+
+ HANDLE other_handle = nullptr;
+
+ if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle
+ GetPlatformFile(),
+ GetCurrentProcess(), // hTargetProcessHandle
+ &other_handle,
+ 0, // dwDesiredAccess ignored due to SAME_ACCESS
+ FALSE, // !bInheritHandle
+ DUPLICATE_SAME_ACCESS)) {
+ return File(GetLastFileError());
+ }
+
+ File other(other_handle);
+ return other;
+}
+
+// Static.
+File::Error File::OSErrorToFileError(DWORD last_error) {
+ switch (last_error) {
+ case ERROR_SHARING_VIOLATION:
+ return FILE_ERROR_IN_USE;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ return FILE_ERROR_EXISTS;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ return FILE_ERROR_NOT_FOUND;
+ case ERROR_ACCESS_DENIED:
+ return FILE_ERROR_ACCESS_DENIED;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return FILE_ERROR_TOO_MANY_OPENED;
+ case ERROR_OUTOFMEMORY:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return FILE_ERROR_NO_MEMORY;
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+#ifndef __MINGW32__
+ case ERROR_DISK_RESOURCES_EXHAUSTED:
+#endif
+ return FILE_ERROR_NO_SPACE;
+ case ERROR_USER_MAPPED_FILE:
+ return FILE_ERROR_INVALID_OPERATION;
+ case ERROR_NOT_READY:
+ case ERROR_SECTOR_NOT_FOUND:
+ case ERROR_DEV_NOT_EXIST:
+ case ERROR_IO_DEVICE:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_DISK_CORRUPT:
+ return FILE_ERROR_IO;
+ default:
+ // This function should only be called for errors.
+ DCHECK_NE(static_cast<DWORD>(ERROR_SUCCESS), last_error);
+ return FILE_ERROR_FAILED;
+ }
+}
+
+void File::DoInitialize(const FilePath& path, uint32_t flags) {
+ DCHECK(!IsValid());
+
+ DWORD disposition = 0;
+
+ if (flags & FLAG_OPEN)
+ disposition = OPEN_EXISTING;
+
+ if (flags & FLAG_CREATE_ALWAYS) {
+ DCHECK(!disposition);
+ DCHECK(flags & FLAG_WRITE);
+ disposition = CREATE_ALWAYS;
+ }
+
+ if (!disposition) {
+ ::SetLastError(ERROR_INVALID_PARAMETER);
+ error_details_ = FILE_ERROR_FAILED;
+ NOTREACHED();
+ return;
+ }
+
+ DWORD access = 0;
+ if (flags & FLAG_WRITE)
+ access = GENERIC_WRITE;
+ if (flags & FLAG_READ)
+ access |= GENERIC_READ;
+
+ DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ DWORD create_flags = 0;
+ file_.Set(CreateFile(ToWCharT(&path.value()), access, sharing, NULL,
+ disposition, create_flags, NULL));
+
+ if (file_.IsValid()) {
+ error_details_ = FILE_OK;
+ if (flags & FLAG_CREATE_ALWAYS)
+ created_ = true;
+ } else {
+ error_details_ = GetLastFileError();
+ }
+}
+
+bool File::Flush() {
+ DCHECK(IsValid());
+ return ::FlushFileBuffers(file_.Get()) != FALSE;
+}
+
+void File::SetPlatformFile(PlatformFile file) {
+ file_.Set(file);
+}
+
+// static
+File::Error File::GetLastFileError() {
+ return File::OSErrorToFileError(GetLastError());
+}
+
+} // namespace base
diff --git a/gn/src/base/files/platform_file.h b/gn/src/base/files/platform_file.h
new file mode 100644
index 00000000000..67c2d30a95a
--- /dev/null
+++ b/gn/src/base/files/platform_file.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium 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 BASE_FILES_PLATFORM_FILE_H_
+#define BASE_FILES_PLATFORM_FILE_H_
+
+#include "base/files/scoped_file.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#include <windows.h>
+#endif
+
+// This file defines platform-independent types for dealing with
+// platform-dependent files. If possible, use the higher-level base::File class
+// rather than these primitives.
+
+namespace base {
+
+#if defined(OS_WIN)
+
+using PlatformFile = HANDLE;
+using ScopedPlatformFile = ::base::win::ScopedHandle;
+
+// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a
+// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically
+// disallowed in constexpr. Visual Studio accepts this, however.
+const PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE;
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+using PlatformFile = int;
+using ScopedPlatformFile = ::base::ScopedFD;
+
+constexpr PlatformFile kInvalidPlatformFile = -1;
+
+#endif
+
+} // namespace base
+
+#endif // BASE_FILES_PLATFORM_FILE_H_
diff --git a/gn/src/base/files/scoped_file.cc b/gn/src/base/files/scoped_file.cc
new file mode 100644
index 00000000000..1b8733e36e8
--- /dev/null
+++ b/gn/src/base/files/scoped_file.cc
@@ -0,0 +1,49 @@
+// 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 "base/files/scoped_file.h"
+
+#include "base/logging.h"
+#include "util/build_config.h"
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <errno.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+namespace base {
+namespace internal {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+// static
+void ScopedFDCloseTraits::Free(int fd) {
+ // It's important to crash here.
+ // There are security implications to not closing a file descriptor
+ // properly. As file descriptors are "capabilities", keeping them open
+ // would make the current process keep access to a resource. Much of
+ // Chrome relies on being able to "drop" such access.
+ // It's especially problematic on Linux with the setuid sandbox, where
+ // a single open directory would bypass the entire security model.
+ int ret = IGNORE_EINTR(close(fd));
+
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FUCHSIA) || \
+ defined(OS_ANDROID) || defined(OS_BSD)
+ // NB: Some file descriptors can return errors from close() e.g. network
+ // filesystems such as NFS and Linux input devices. On Linux, macOS, and
+ // Fuchsia's POSIX layer, errors from close other than EBADF do not indicate
+ // failure to actually close the fd.
+ if (ret != 0 && errno != EBADF)
+ ret = 0;
+#endif
+
+ PCHECK(0 == ret);
+}
+
+#endif // OS_POSIX || OS_FUCHSIA
+
+} // namespace internal
+} // namespace base
diff --git a/gn/base/files/scoped_file.h b/gn/src/base/files/scoped_file.h
index 0d896504300..0d896504300 100644
--- a/gn/base/files/scoped_file.h
+++ b/gn/src/base/files/scoped_file.h
diff --git a/gn/base/files/scoped_temp_dir.cc b/gn/src/base/files/scoped_temp_dir.cc
index 01ec0f0caab..01ec0f0caab 100644
--- a/gn/base/files/scoped_temp_dir.cc
+++ b/gn/src/base/files/scoped_temp_dir.cc
diff --git a/gn/base/files/scoped_temp_dir.h b/gn/src/base/files/scoped_temp_dir.h
index 4ddb690945c..4ddb690945c 100644
--- a/gn/base/files/scoped_temp_dir.h
+++ b/gn/src/base/files/scoped_temp_dir.h
diff --git a/gn/src/base/gtest_prod_util.h b/gn/src/base/gtest_prod_util.h
new file mode 100644
index 00000000000..b3b1175b2ee
--- /dev/null
+++ b/gn/src/base/gtest_prod_util.h
@@ -0,0 +1,12 @@
+// 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 BASE_GTEST_PROD_UTIL_H_
+#define BASE_GTEST_PROD_UTIL_H_
+
+// TODO: Remove me.
+#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
+ friend struct test_case_name##test_name
+
+#endif // BASE_GTEST_PROD_UTIL_H_
diff --git a/gn/src/base/json/json_parser.cc b/gn/src/base/json/json_parser.cc
new file mode 100644
index 00000000000..df02829e883
--- /dev/null
+++ b/gn/src/base/json/json_parser.cc
@@ -0,0 +1,747 @@
+// 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 "base/json/json_parser.h"
+
+#include <cmath>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "base/values.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+const int32_t kExtendedASCIIStart = 0x80;
+
+// Simple class that checks for maximum recursion/"stack overflow."
+class StackMarker {
+ public:
+ StackMarker(int max_depth, int* depth)
+ : max_depth_(max_depth), depth_(depth) {
+ ++(*depth_);
+ DCHECK_LE(*depth_, max_depth_);
+ }
+ ~StackMarker() { --(*depth_); }
+
+ bool IsTooDeep() const { return *depth_ >= max_depth_; }
+
+ private:
+ const int max_depth_;
+ int* const depth_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackMarker);
+};
+
+constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD;
+
+} // namespace
+
+// This is U+FFFD.
+const char kUnicodeReplacementString[] = "\xEF\xBF\xBD";
+
+JSONParser::JSONParser(int options, int max_depth)
+ : options_(options),
+ max_depth_(max_depth),
+ index_(0),
+ stack_depth_(0),
+ line_number_(0),
+ index_last_line_(0),
+ error_code_(JSONReader::JSON_NO_ERROR),
+ error_line_(0),
+ error_column_(0) {
+ CHECK_LE(max_depth, JSONReader::kStackMaxDepth);
+}
+
+JSONParser::~JSONParser() = default;
+
+std::optional<Value> JSONParser::Parse(std::string_view input) {
+ input_ = input;
+ index_ = 0;
+ line_number_ = 1;
+ index_last_line_ = 0;
+
+ error_code_ = JSONReader::JSON_NO_ERROR;
+ error_line_ = 0;
+ error_column_ = 0;
+
+ // ICU and ReadUnicodeCharacter() use int32_t for lengths, so ensure
+ // that the index_ will not overflow when parsing.
+ if (!base::IsValueInRangeForNumericType<int32_t>(input.length())) {
+ ReportError(JSONReader::JSON_TOO_LARGE, 0);
+ return std::nullopt;
+ }
+
+ // When the input JSON string starts with a UTF-8 Byte-Order-Mark,
+ // advance the start position to avoid the ParseNextToken function mis-
+ // treating a Unicode BOM as an invalid character and returning NULL.
+ ConsumeIfMatch("\xEF\xBB\xBF");
+
+ // Parse the first and any nested tokens.
+ std::optional<Value> root(ParseNextToken());
+ if (!root)
+ return std::nullopt;
+
+ // Make sure the input stream is at an end.
+ if (GetNextToken() != T_END_OF_INPUT) {
+ ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1);
+ return std::nullopt;
+ }
+
+ return root;
+}
+
+JSONReader::JsonParseError JSONParser::error_code() const {
+ return error_code_;
+}
+
+std::string JSONParser::GetErrorMessage() const {
+ return FormatErrorMessage(error_line_, error_column_,
+ JSONReader::ErrorCodeToString(error_code_));
+}
+
+int JSONParser::error_line() const {
+ return error_line_;
+}
+
+int JSONParser::error_column() const {
+ return error_column_;
+}
+
+// StringBuilder ///////////////////////////////////////////////////////////////
+
+JSONParser::StringBuilder::StringBuilder() : StringBuilder(nullptr) {}
+
+JSONParser::StringBuilder::StringBuilder(const char* pos)
+ : pos_(pos), length_(0) {}
+
+JSONParser::StringBuilder::~StringBuilder() = default;
+
+JSONParser::StringBuilder& JSONParser::StringBuilder::operator=(
+ StringBuilder&& other) = default;
+
+void JSONParser::StringBuilder::Append(uint32_t point) {
+ DCHECK(IsValidCharacter(point));
+
+ if (point < kExtendedASCIIStart && !string_) {
+ DCHECK_EQ(static_cast<char>(point), pos_[length_]);
+ ++length_;
+ } else {
+ Convert();
+ if (UNLIKELY(point == kUnicodeReplacementPoint)) {
+ string_->append(kUnicodeReplacementString);
+ } else {
+ WriteUnicodeCharacter(point, &*string_);
+ }
+ }
+}
+
+void JSONParser::StringBuilder::Convert() {
+ if (string_)
+ return;
+ string_.emplace(pos_, length_);
+}
+
+std::string JSONParser::StringBuilder::DestructiveAsString() {
+ if (string_)
+ return std::move(*string_);
+ return std::string(pos_, length_);
+}
+
+// JSONParser private //////////////////////////////////////////////////////////
+
+std::optional<std::string_view> JSONParser::PeekChars(int count) {
+ if (static_cast<size_t>(index_) + count > input_.length())
+ return std::nullopt;
+ // Using std::string_view::substr() is significantly slower (according to
+ // base_perftests) than constructing a substring manually.
+ return std::string_view(input_.data() + index_, count);
+}
+
+std::optional<char> JSONParser::PeekChar() {
+ std::optional<std::string_view> chars = PeekChars(1);
+ if (chars)
+ return (*chars)[0];
+ return std::nullopt;
+}
+
+std::optional<std::string_view> JSONParser::ConsumeChars(int count) {
+ std::optional<std::string_view> chars = PeekChars(count);
+ if (chars)
+ index_ += count;
+ return chars;
+}
+
+std::optional<char> JSONParser::ConsumeChar() {
+ std::optional<std::string_view> chars = ConsumeChars(1);
+ if (chars)
+ return (*chars)[0];
+ return std::nullopt;
+}
+
+const char* JSONParser::pos() {
+ CHECK_LE(static_cast<size_t>(index_), input_.length());
+ return input_.data() + index_;
+}
+
+JSONParser::Token JSONParser::GetNextToken() {
+ EatWhitespaceAndComments();
+
+ std::optional<char> c = PeekChar();
+ if (!c)
+ return T_END_OF_INPUT;
+
+ switch (*c) {
+ case '{':
+ return T_OBJECT_BEGIN;
+ case '}':
+ return T_OBJECT_END;
+ case '[':
+ return T_ARRAY_BEGIN;
+ case ']':
+ return T_ARRAY_END;
+ case '"':
+ return T_STRING;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ return T_NUMBER;
+ case 't':
+ return T_BOOL_TRUE;
+ case 'f':
+ return T_BOOL_FALSE;
+ case 'n':
+ return T_NULL;
+ case ',':
+ return T_LIST_SEPARATOR;
+ case ':':
+ return T_OBJECT_PAIR_SEPARATOR;
+ default:
+ return T_INVALID_TOKEN;
+ }
+}
+
+void JSONParser::EatWhitespaceAndComments() {
+ while (std::optional<char> c = PeekChar()) {
+ switch (*c) {
+ case '\r':
+ case '\n':
+ index_last_line_ = index_;
+ // Don't increment line_number_ twice for "\r\n".
+ if (!(c == '\n' && index_ > 0 && input_[index_ - 1] == '\r')) {
+ ++line_number_;
+ }
+ FALLTHROUGH;
+ case ' ':
+ case '\t':
+ ConsumeChar();
+ break;
+ case '/':
+ if (!EatComment())
+ return;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+bool JSONParser::EatComment() {
+ std::optional<std::string_view> comment_start = ConsumeChars(2);
+ if (!comment_start)
+ return false;
+
+ if (comment_start == "//") {
+ // Single line comment, read to newline.
+ while (std::optional<char> c = PeekChar()) {
+ if (c == '\n' || c == '\r')
+ return true;
+ ConsumeChar();
+ }
+ } else if (comment_start == "/*") {
+ char previous_char = '\0';
+ // Block comment, read until end marker.
+ while (std::optional<char> c = PeekChar()) {
+ if (previous_char == '*' && c == '/') {
+ // EatWhitespaceAndComments will inspect pos(), which will still be on
+ // the last / of the comment, so advance once more (which may also be
+ // end of input).
+ ConsumeChar();
+ return true;
+ }
+ previous_char = *ConsumeChar();
+ }
+
+ // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT.
+ }
+
+ return false;
+}
+
+std::optional<Value> JSONParser::ParseNextToken() {
+ return ParseToken(GetNextToken());
+}
+
+std::optional<Value> JSONParser::ParseToken(Token token) {
+ switch (token) {
+ case T_OBJECT_BEGIN:
+ return ConsumeDictionary();
+ case T_ARRAY_BEGIN:
+ return ConsumeList();
+ case T_STRING:
+ return ConsumeString();
+ case T_NUMBER:
+ return ConsumeNumber();
+ case T_BOOL_TRUE:
+ case T_BOOL_FALSE:
+ case T_NULL:
+ return ConsumeLiteral();
+ default:
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return std::nullopt;
+ }
+}
+
+std::optional<Value> JSONParser::ConsumeDictionary() {
+ if (ConsumeChar() != '{') {
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return std::nullopt;
+ }
+
+ StackMarker depth_check(max_depth_, &stack_depth_);
+ if (depth_check.IsTooDeep()) {
+ ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0);
+ return std::nullopt;
+ }
+
+ std::vector<Value::DictStorage::value_type> dict_storage;
+
+ Token token = GetNextToken();
+ while (token != T_OBJECT_END) {
+ if (token != T_STRING) {
+ ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1);
+ return std::nullopt;
+ }
+
+ // First consume the key.
+ StringBuilder key;
+ if (!ConsumeStringRaw(&key)) {
+ return std::nullopt;
+ }
+
+ // Read the separator.
+ token = GetNextToken();
+ if (token != T_OBJECT_PAIR_SEPARATOR) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+
+ // The next token is the value. Ownership transfers to |dict|.
+ ConsumeChar();
+ std::optional<Value> value = ParseNextToken();
+ if (!value) {
+ // ReportError from deeper level.
+ return std::nullopt;
+ }
+
+ dict_storage.emplace_back(key.DestructiveAsString(),
+ std::make_unique<Value>(std::move(*value)));
+
+ token = GetNextToken();
+ if (token == T_LIST_SEPARATOR) {
+ ConsumeChar();
+ token = GetNextToken();
+ if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
+ ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
+ return std::nullopt;
+ }
+ } else if (token != T_OBJECT_END) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
+ return std::nullopt;
+ }
+ }
+
+ ConsumeChar(); // Closing '}'.
+
+ return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES));
+}
+
+std::optional<Value> JSONParser::ConsumeList() {
+ if (ConsumeChar() != '[') {
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return std::nullopt;
+ }
+
+ StackMarker depth_check(max_depth_, &stack_depth_);
+ if (depth_check.IsTooDeep()) {
+ ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0);
+ return std::nullopt;
+ }
+
+ Value::ListStorage list_storage;
+
+ Token token = GetNextToken();
+ while (token != T_ARRAY_END) {
+ std::optional<Value> item = ParseToken(token);
+ if (!item) {
+ // ReportError from deeper level.
+ return std::nullopt;
+ }
+
+ list_storage.push_back(std::move(*item));
+
+ token = GetNextToken();
+ if (token == T_LIST_SEPARATOR) {
+ ConsumeChar();
+ token = GetNextToken();
+ if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
+ ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
+ return std::nullopt;
+ }
+ } else if (token != T_ARRAY_END) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+ }
+
+ ConsumeChar(); // Closing ']'.
+
+ return Value(std::move(list_storage));
+}
+
+std::optional<Value> JSONParser::ConsumeString() {
+ StringBuilder string;
+ if (!ConsumeStringRaw(&string))
+ return std::nullopt;
+
+ return Value(string.DestructiveAsString());
+}
+
+bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
+ if (ConsumeChar() != '"') {
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return false;
+ }
+
+ // StringBuilder will internally build a std::string_view unless a UTF-16
+ // conversion occurs, at which point it will perform a copy into a
+ // std::string.
+ StringBuilder string(pos());
+
+ while (PeekChar()) {
+ uint32_t next_char = 0;
+ if (!ReadUnicodeCharacter(input_.data(),
+ static_cast<int32_t>(input_.length()), &index_,
+ &next_char) ||
+ !IsValidCharacter(next_char)) {
+ if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
+ ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
+ return false;
+ }
+ ConsumeChar();
+ string.Append(kUnicodeReplacementPoint);
+ continue;
+ }
+
+ if (next_char == '"') {
+ ConsumeChar();
+ *out = std::move(string);
+ return true;
+ } else if (next_char != '\\') {
+ // If this character is not an escape sequence...
+ ConsumeChar();
+ string.Append(next_char);
+ } else {
+ // And if it is an escape sequence, the input string will be adjusted
+ // (either by combining the two characters of an encoded escape sequence,
+ // or with a UTF conversion), so using std::string_view isn't possible --
+ // force a conversion.
+ string.Convert();
+
+ // Read past the escape '\' and ensure there's a character following.
+ std::optional<std::string_view> escape_sequence = ConsumeChars(2);
+ if (!escape_sequence) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
+ return false;
+ }
+
+ switch ((*escape_sequence)[1]) {
+ // Allowed esape sequences:
+ case 'x': { // UTF-8 sequence.
+ // UTF-8 \x escape sequences are not allowed in the spec, but they
+ // are supported here for backwards-compatiblity with the old parser.
+ escape_sequence = ConsumeChars(2);
+ if (!escape_sequence) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, -2);
+ return false;
+ }
+
+ int hex_digit = 0;
+ if (!HexStringToInt(*escape_sequence, &hex_digit) ||
+ !IsValidCharacter(hex_digit)) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, -2);
+ return false;
+ }
+
+ string.Append(hex_digit);
+ break;
+ }
+ case 'u': { // UTF-16 sequence.
+ // UTF units are of the form \uXXXX.
+ uint32_t code_point;
+ if (!DecodeUTF16(&code_point)) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
+ return false;
+ }
+ string.Append(code_point);
+ break;
+ }
+ case '"':
+ string.Append('"');
+ break;
+ case '\\':
+ string.Append('\\');
+ break;
+ case '/':
+ string.Append('/');
+ break;
+ case 'b':
+ string.Append('\b');
+ break;
+ case 'f':
+ string.Append('\f');
+ break;
+ case 'n':
+ string.Append('\n');
+ break;
+ case 'r':
+ string.Append('\r');
+ break;
+ case 't':
+ string.Append('\t');
+ break;
+ case 'v': // Not listed as valid escape sequence in the RFC.
+ string.Append('\v');
+ break;
+ // All other escape squences are illegal.
+ default:
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
+ return false;
+ }
+ }
+ }
+
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
+ return false;
+}
+
+// Entry is at the first X in \uXXXX.
+bool JSONParser::DecodeUTF16(uint32_t* out_code_point) {
+ std::optional<std::string_view> escape_sequence = ConsumeChars(4);
+ if (!escape_sequence)
+ return false;
+
+ // Consume the UTF-16 code unit, which may be a high surrogate.
+ int code_unit16_high = 0;
+ if (!HexStringToInt(*escape_sequence, &code_unit16_high))
+ return false;
+
+ // If this is a high surrogate, consume the next code unit to get the
+ // low surrogate.
+ if (CBU16_IS_SURROGATE(code_unit16_high)) {
+ // Make sure this is the high surrogate. If not, it's an encoding
+ // error.
+ if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high))
+ return false;
+
+ // Make sure that the token has more characters to consume the
+ // lower surrogate.
+ if (!ConsumeIfMatch("\\u"))
+ return false;
+
+ escape_sequence = ConsumeChars(4);
+ if (!escape_sequence)
+ return false;
+
+ int code_unit16_low = 0;
+ if (!HexStringToInt(*escape_sequence, &code_unit16_low))
+ return false;
+
+ if (!CBU16_IS_TRAIL(code_unit16_low))
+ return false;
+
+ uint32_t code_point =
+ CBU16_GET_SUPPLEMENTARY(code_unit16_high, code_unit16_low);
+ if (!IsValidCharacter(code_point))
+ return false;
+
+ *out_code_point = code_point;
+ } else {
+ // Not a surrogate.
+ DCHECK(CBU16_IS_SINGLE(code_unit16_high));
+ if (!IsValidCharacter(code_unit16_high)) {
+ if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
+ return false;
+ }
+ *out_code_point = kUnicodeReplacementPoint;
+ return true;
+ }
+
+ *out_code_point = code_unit16_high;
+ }
+
+ return true;
+}
+
+std::optional<Value> JSONParser::ConsumeNumber() {
+ const char* num_start = pos();
+ const int start_index = index_;
+ int end_index = start_index;
+
+ if (PeekChar() == '-')
+ ConsumeChar();
+
+ if (!ReadInt(false)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+ end_index = index_;
+
+ // The optional fraction part.
+ if (PeekChar() == '.') {
+ ConsumeChar();
+ if (!ReadInt(true)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+ end_index = index_;
+ }
+
+ // Optional exponent part.
+ std::optional<char> c = PeekChar();
+ if (c == 'e' || c == 'E') {
+ ConsumeChar();
+ if (PeekChar() == '-' || PeekChar() == '+') {
+ ConsumeChar();
+ }
+ if (!ReadInt(true)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+ end_index = index_;
+ }
+
+ // ReadInt is greedy because numbers have no easily detectable sentinel,
+ // so save off where the parser should be on exit (see Consume invariant at
+ // the top of the header), then make sure the next token is one which is
+ // valid.
+ int exit_index = index_;
+
+ switch (GetNextToken()) {
+ case T_OBJECT_END:
+ case T_ARRAY_END:
+ case T_LIST_SEPARATOR:
+ case T_END_OF_INPUT:
+ break;
+ default:
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+
+ index_ = exit_index;
+
+ std::string_view num_string(num_start, end_index - start_index);
+
+ int num_int;
+ if (StringToInt(num_string, &num_int))
+ return Value(num_int);
+
+ return std::nullopt;
+}
+
+bool JSONParser::ReadInt(bool allow_leading_zeros) {
+ size_t len = 0;
+ char first = 0;
+
+ while (std::optional<char> c = PeekChar()) {
+ if (!IsAsciiDigit(c))
+ break;
+
+ if (len == 0)
+ first = *c;
+
+ ++len;
+ ConsumeChar();
+ }
+
+ if (len == 0)
+ return false;
+
+ if (!allow_leading_zeros && len > 1 && first == '0')
+ return false;
+
+ return true;
+}
+
+std::optional<Value> JSONParser::ConsumeLiteral() {
+ if (ConsumeIfMatch("true")) {
+ return Value(true);
+ } else if (ConsumeIfMatch("false")) {
+ return Value(false);
+ } else if (ConsumeIfMatch("null")) {
+ return Value(Value::Type::NONE);
+ } else {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return std::nullopt;
+ }
+}
+
+bool JSONParser::ConsumeIfMatch(std::string_view match) {
+ if (match == PeekChars(match.size())) {
+ ConsumeChars(match.size());
+ return true;
+ }
+ return false;
+}
+
+void JSONParser::ReportError(JSONReader::JsonParseError code,
+ int column_adjust) {
+ error_code_ = code;
+ error_line_ = line_number_;
+ error_column_ = index_ - index_last_line_ + column_adjust;
+}
+
+// static
+std::string JSONParser::FormatErrorMessage(int line,
+ int column,
+ const std::string& description) {
+ if (line || column) {
+ return StringPrintf("Line: %i, column: %i, %s", line, column,
+ description.c_str());
+ }
+ return description;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/gn/src/base/json/json_parser.h b/gn/src/base/json/json_parser.h
new file mode 100644
index 00000000000..0ba1d9d7a98
--- /dev/null
+++ b/gn/src/base/json/json_parser.h
@@ -0,0 +1,260 @@
+// 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 BASE_JSON_JSON_PARSER_H_
+#define BASE_JSON_JSON_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+
+namespace base {
+
+class Value;
+
+namespace internal {
+
+class JSONParserTest;
+
+// The implementation behind the JSONReader interface. This class is not meant
+// to be used directly; it encapsulates logic that need not be exposed publicly.
+//
+// This parser guarantees O(n) time through the input string. Iteration happens
+// on the byte level, with the functions ConsumeChars() and ConsumeChar(). The
+// conversion from byte to JSON token happens without advancing the parser in
+// GetNextToken/ParseToken, that is tokenization operates on the current parser
+// position without advancing.
+//
+// Built on top of these are a family of Consume functions that iterate
+// internally. Invariant: on entry of a Consume function, the parser is wound
+// to the first byte of a valid JSON token. On exit, it is on the first byte
+// after the token that was just consumed, which would likely be the first byte
+// of the next token.
+class JSONParser {
+ public:
+ JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth);
+ ~JSONParser();
+
+ // Parses the input string according to the set options and returns the
+ // result as a Value.
+ // Wrap this in base::FooValue::From() to check the Value is of type Foo and
+ // convert to a FooValue at the same time.
+ std::optional<Value> Parse(std::string_view input);
+
+ // Returns the error code.
+ JSONReader::JsonParseError error_code() const;
+
+ // Returns the human-friendly error message.
+ std::string GetErrorMessage() const;
+
+ // Returns the error line number if parse error happened. Otherwise always
+ // returns 0.
+ int error_line() const;
+
+ // Returns the error column number if parse error happened. Otherwise always
+ // returns 0.
+ int error_column() const;
+
+ private:
+ enum Token {
+ T_OBJECT_BEGIN, // {
+ T_OBJECT_END, // }
+ T_ARRAY_BEGIN, // [
+ T_ARRAY_END, // ]
+ T_STRING,
+ T_NUMBER,
+ T_BOOL_TRUE, // true
+ T_BOOL_FALSE, // false
+ T_NULL, // null
+ T_LIST_SEPARATOR, // ,
+ T_OBJECT_PAIR_SEPARATOR, // :
+ T_END_OF_INPUT,
+ T_INVALID_TOKEN,
+ };
+
+ // A helper class used for parsing strings. One optimization performed is to
+ // create base::Value with a std::string_view to avoid unnecessary std::string
+ // copies. This is not possible if the input string needs to be decoded from
+ // UTF-16 to UTF-8, or if an escape sequence causes characters to be skipped.
+ // This class centralizes that logic.
+ class StringBuilder {
+ public:
+ // Empty constructor. Used for creating a builder with which to assign to.
+ StringBuilder();
+
+ // |pos| is the beginning of an input string, excluding the |"|.
+ explicit StringBuilder(const char* pos);
+
+ ~StringBuilder();
+
+ StringBuilder& operator=(StringBuilder&& other);
+
+ // Appends the Unicode code point |point| to the string, either by
+ // increasing the |length_| of the string if the string has not been
+ // converted, or by appending the UTF8 bytes for the code point.
+ void Append(uint32_t point);
+
+ // Converts the builder from its default std::string_view to a full
+ // std::string, performing a copy. Once a builder is converted, it cannot be
+ // made a std::string_view again.
+ void Convert();
+
+ // Returns the builder as a string, invalidating all state. This allows
+ // the internal string buffer representation to be destructively moved
+ // in cases where the builder will not be needed any more.
+ std::string DestructiveAsString();
+
+ private:
+ // The beginning of the input string.
+ const char* pos_;
+
+ // Number of bytes in |pos_| that make up the string being built.
+ size_t length_;
+
+ // The copied string representation. Will be unset until Convert() is
+ // called.
+ std::optional<std::string> string_;
+ };
+
+ // Returns the next |count| bytes of the input stream, or nullopt if fewer
+ // than |count| bytes remain.
+ std::optional<std::string_view> PeekChars(int count);
+
+ // Calls PeekChars() with a |count| of 1.
+ std::optional<char> PeekChar();
+
+ // Returns the next |count| bytes of the input stream, or nullopt if fewer
+ // than |count| bytes remain, and advances the parser position by |count|.
+ std::optional<std::string_view> ConsumeChars(int count);
+
+ // Calls ConsumeChars() with a |count| of 1.
+ std::optional<char> ConsumeChar();
+
+ // Returns a pointer to the current character position.
+ const char* pos();
+
+ // Skips over whitespace and comments to find the next token in the stream.
+ // This does not advance the parser for non-whitespace or comment chars.
+ Token GetNextToken();
+
+ // Consumes whitespace characters and comments until the next non-that is
+ // encountered.
+ void EatWhitespaceAndComments();
+ // Helper function that consumes a comment, assuming that the parser is
+ // currently wound to a '/'.
+ bool EatComment();
+
+ // Calls GetNextToken() and then ParseToken().
+ std::optional<Value> ParseNextToken();
+
+ // Takes a token that represents the start of a Value ("a structural token"
+ // in RFC terms) and consumes it, returning the result as a Value.
+ std::optional<Value> ParseToken(Token token);
+
+ // Assuming that the parser is currently wound to '{', this parses a JSON
+ // object into a Value.
+ std::optional<Value> ConsumeDictionary();
+
+ // Assuming that the parser is wound to '[', this parses a JSON list into a
+ // Value.
+ std::optional<Value> ConsumeList();
+
+ // Calls through ConsumeStringRaw and wraps it in a value.
+ std::optional<Value> ConsumeString();
+
+ // Assuming that the parser is wound to a double quote, this parses a string,
+ // decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on
+ // success and places result into |out|. Returns false on failure with
+ // error information set.
+ bool ConsumeStringRaw(StringBuilder* out);
+ // Helper function for ConsumeStringRaw() that consumes the next four or 10
+ // bytes (parser is wound to the first character of a HEX sequence, with the
+ // potential for consuming another \uXXXX for a surrogate). Returns true on
+ // success and places the code point |out_code_point|, and false on failure.
+ bool DecodeUTF16(uint32_t* out_code_point);
+
+ // Assuming that the parser is wound to the start of a valid JSON number,
+ // this parses and converts it to either an int or double value.
+ std::optional<Value> ConsumeNumber();
+ // Helper that reads characters that are ints. Returns true if a number was
+ // read and false on error.
+ bool ReadInt(bool allow_leading_zeros);
+
+ // Consumes the literal values of |true|, |false|, and |null|, assuming the
+ // parser is wound to the first character of any of those.
+ std::optional<Value> ConsumeLiteral();
+
+ // Helper function that returns true if the byte squence |match| can be
+ // consumed at the current parser position. Returns false if there are fewer
+ // than |match|-length bytes or if the sequence does not match, and the
+ // parser state is unchanged.
+ bool ConsumeIfMatch(std::string_view match);
+
+ // Sets the error information to |code| at the current column, based on
+ // |index_| and |index_last_line_|, with an optional positive/negative
+ // adjustment by |column_adjust|.
+ void ReportError(JSONReader::JsonParseError code, int column_adjust);
+
+ // Given the line and column number of an error, formats one of the error
+ // message contants from json_reader.h for human display.
+ static std::string FormatErrorMessage(int line,
+ int column,
+ const std::string& description);
+
+ // base::JSONParserOptions that control parsing.
+ const int options_;
+
+ // Maximum depth to parse.
+ const int max_depth_;
+
+ // The input stream being parsed. Note: Not guaranteed to NUL-terminated.
+ std::string_view input_;
+
+ // The index in the input stream to which the parser is wound.
+ int index_;
+
+ // The number of times the parser has recursed (current stack depth).
+ int stack_depth_;
+
+ // The line number that the parser is at currently.
+ int line_number_;
+
+ // The last value of |index_| on the previous line.
+ int index_last_line_;
+
+ // Error information.
+ JSONReader::JsonParseError error_code_;
+ int error_line_;
+ int error_column_;
+
+ friend class JSONParserTest;
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, NextChar);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeDictionary);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeList);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeString);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidUTF16EscapeSequence);
+
+ DISALLOW_COPY_AND_ASSIGN(JSONParser);
+};
+
+// Used when decoding and an invalid utf-8 sequence is encountered.
+extern const char kUnicodeReplacementString[];
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_JSON_JSON_PARSER_H_
diff --git a/gn/src/base/json/json_reader.cc b/gn/src/base/json/json_reader.cc
new file mode 100644
index 00000000000..7b5b8ebfbb2
--- /dev/null
+++ b/gn/src/base/json/json_reader.cc
@@ -0,0 +1,118 @@
+// 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 "base/json/json_reader.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/json/json_parser.h"
+#include "base/logging.h"
+#include "base/values.h"
+
+namespace base {
+
+// Chosen to support 99.9% of documents found in the wild late 2016.
+// http://crbug.com/673263
+const int JSONReader::kStackMaxDepth = 200;
+
+// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError.
+static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000,
+ "JSONReader error out of bounds");
+
+const char JSONReader::kInvalidEscape[] = "Invalid escape sequence.";
+const char JSONReader::kSyntaxError[] = "Syntax error.";
+const char JSONReader::kUnexpectedToken[] = "Unexpected token.";
+const char JSONReader::kTrailingComma[] = "Trailing comma not allowed.";
+const char JSONReader::kTooMuchNesting[] = "Too much nesting.";
+const char JSONReader::kUnexpectedDataAfterRoot[] =
+ "Unexpected data after root element.";
+const char JSONReader::kUnsupportedEncoding[] =
+ "Unsupported encoding. JSON must be UTF-8.";
+const char JSONReader::kUnquotedDictionaryKey[] =
+ "Dictionary keys must be quoted.";
+const char JSONReader::kInputTooLarge[] = "Input string is too large (>2GB).";
+
+JSONReader::JSONReader(int options, int max_depth)
+ : parser_(new internal::JSONParser(options, max_depth)) {}
+
+JSONReader::~JSONReader() = default;
+
+// static
+std::unique_ptr<Value> JSONReader::Read(std::string_view json,
+ int options,
+ int max_depth) {
+ internal::JSONParser parser(options, max_depth);
+ std::optional<Value> root = parser.Parse(json);
+ return root ? std::make_unique<Value>(std::move(*root)) : nullptr;
+}
+
+// static
+std::unique_ptr<Value> JSONReader::ReadAndReturnError(
+ std::string_view json,
+ int options,
+ int* error_code_out,
+ std::string* error_msg_out,
+ int* error_line_out,
+ int* error_column_out) {
+ internal::JSONParser parser(options);
+ std::optional<Value> root = parser.Parse(json);
+ if (!root) {
+ if (error_code_out)
+ *error_code_out = parser.error_code();
+ if (error_msg_out)
+ *error_msg_out = parser.GetErrorMessage();
+ if (error_line_out)
+ *error_line_out = parser.error_line();
+ if (error_column_out)
+ *error_column_out = parser.error_column();
+ }
+
+ return root ? std::make_unique<Value>(std::move(*root)) : nullptr;
+}
+
+// static
+std::string JSONReader::ErrorCodeToString(JsonParseError error_code) {
+ switch (error_code) {
+ case JSON_NO_ERROR:
+ return std::string();
+ case JSON_INVALID_ESCAPE:
+ return kInvalidEscape;
+ case JSON_SYNTAX_ERROR:
+ return kSyntaxError;
+ case JSON_UNEXPECTED_TOKEN:
+ return kUnexpectedToken;
+ case JSON_TRAILING_COMMA:
+ return kTrailingComma;
+ case JSON_TOO_MUCH_NESTING:
+ return kTooMuchNesting;
+ case JSON_UNEXPECTED_DATA_AFTER_ROOT:
+ return kUnexpectedDataAfterRoot;
+ case JSON_UNSUPPORTED_ENCODING:
+ return kUnsupportedEncoding;
+ case JSON_UNQUOTED_DICTIONARY_KEY:
+ return kUnquotedDictionaryKey;
+ case JSON_TOO_LARGE:
+ return kInputTooLarge;
+ case JSON_PARSE_ERROR_COUNT:
+ break;
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+std::unique_ptr<Value> JSONReader::ReadToValue(std::string_view json) {
+ std::optional<Value> value = parser_->Parse(json);
+ return value ? std::make_unique<Value>(std::move(*value)) : nullptr;
+}
+
+JSONReader::JsonParseError JSONReader::error_code() const {
+ return parser_->error_code();
+}
+
+std::string JSONReader::GetErrorMessage() const {
+ return parser_->GetErrorMessage();
+}
+
+} // namespace base
diff --git a/gn/src/base/json/json_reader.h b/gn/src/base/json/json_reader.h
new file mode 100644
index 00000000000..c45498150f3
--- /dev/null
+++ b/gn/src/base/json/json_reader.h
@@ -0,0 +1,133 @@
+// 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 JSON parser. Converts strings of JSON into a Value object (see
+// base/values.h).
+// http://www.ietf.org/rfc/rfc4627.txt?number=4627
+//
+// Known limitations/deviations from the RFC:
+// - Only knows how to parse ints within the range of a signed 32 bit int and
+// decimal numbers within a double.
+// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16
+// (BE or LE) and UTF-32 (BE or LE) as well.
+// - We limit nesting to 100 levels to prevent stack overflow (this is allowed
+// by the RFC).
+// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data
+// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input
+// UTF-8 string for the JSONReader::JsonToValue() function may start with a
+// UTF-8 BOM (0xEF, 0xBB, 0xBF).
+// To avoid the function from mis-treating a UTF-8 BOM as an invalid
+// character, the function skips a Unicode BOM at the beginning of the
+// Unicode string (converted from the input UTF-8 string) before parsing it.
+//
+// TODO(tc): Add a parsing option to to relax object keys being wrapped in
+// double quotes
+// TODO(tc): Add an option to disable comment stripping
+
+#ifndef BASE_JSON_JSON_READER_H_
+#define BASE_JSON_JSON_READER_H_
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+namespace base {
+
+class Value;
+
+namespace internal {
+class JSONParser;
+}
+
+enum JSONParserOptions {
+ // Parses the input strictly according to RFC 4627, except for where noted
+ // above.
+ JSON_PARSE_RFC = 0,
+
+ // Allows commas to exist after the last element in structures.
+ JSON_ALLOW_TRAILING_COMMAS = 1 << 0,
+
+ // If set the parser replaces invalid characters with the Unicode replacement
+ // character (U+FFFD). If not set, invalid characters trigger a hard error and
+ // parsing fails.
+ JSON_REPLACE_INVALID_CHARACTERS = 1 << 1,
+};
+
+class JSONReader {
+ public:
+ static const int kStackMaxDepth;
+
+ // Error codes during parsing.
+ enum JsonParseError {
+ JSON_NO_ERROR = 0,
+ JSON_INVALID_ESCAPE,
+ JSON_SYNTAX_ERROR,
+ JSON_UNEXPECTED_TOKEN,
+ JSON_TRAILING_COMMA,
+ JSON_TOO_MUCH_NESTING,
+ JSON_UNEXPECTED_DATA_AFTER_ROOT,
+ JSON_UNSUPPORTED_ENCODING,
+ JSON_UNQUOTED_DICTIONARY_KEY,
+ JSON_TOO_LARGE,
+ JSON_PARSE_ERROR_COUNT
+ };
+
+ // String versions of parse error codes.
+ static const char kInvalidEscape[];
+ static const char kSyntaxError[];
+ static const char kUnexpectedToken[];
+ static const char kTrailingComma[];
+ static const char kTooMuchNesting[];
+ static const char kUnexpectedDataAfterRoot[];
+ static const char kUnsupportedEncoding[];
+ static const char kUnquotedDictionaryKey[];
+ static const char kInputTooLarge[];
+
+ // Constructs a reader.
+ JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth);
+
+ ~JSONReader();
+
+ // Reads and parses |json|, returning a Value.
+ // If |json| is not a properly formed JSON string, returns nullptr.
+ // Wrap this in base::FooValue::From() to check the Value is of type Foo and
+ // convert to a FooValue at the same time.
+ static std::unique_ptr<Value> Read(std::string_view json,
+ int options = JSON_PARSE_RFC,
+ int max_depth = kStackMaxDepth);
+
+ // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out|
+ // are optional. If specified and nullptr is returned, they will be populated
+ // an error code and a formatted error message (including error location if
+ // appropriate). Otherwise, they will be unmodified.
+ static std::unique_ptr<Value> ReadAndReturnError(
+ std::string_view json,
+ int options, // JSONParserOptions
+ int* error_code_out,
+ std::string* error_msg_out,
+ int* error_line_out = nullptr,
+ int* error_column_out = nullptr);
+
+ // Converts a JSON parse error code into a human readable message.
+ // Returns an empty string if error_code is JSON_NO_ERROR.
+ static std::string ErrorCodeToString(JsonParseError error_code);
+
+ // Non-static version of Read() above.
+ std::unique_ptr<Value> ReadToValue(std::string_view json);
+
+ // Returns the error code if the last call to ReadToValue() failed.
+ // Returns JSON_NO_ERROR otherwise.
+ JsonParseError error_code() const;
+
+ // Converts error_code_ to a human-readable string, including line and column
+ // numbers if appropriate.
+ std::string GetErrorMessage() const;
+
+ private:
+ std::unique_ptr<internal::JSONParser> parser_;
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_READER_H_
diff --git a/gn/src/base/json/json_value_converter.cc b/gn/src/base/json/json_value_converter.cc
new file mode 100644
index 00000000000..ee7b0941333
--- /dev/null
+++ b/gn/src/base/json/json_value_converter.cc
@@ -0,0 +1,36 @@
+// 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 "base/json/json_value_converter.h"
+
+namespace base {
+namespace internal {
+
+bool BasicValueConverter<int>::Convert(const base::Value& value,
+ int* field) const {
+ return value.GetAsInteger(field);
+}
+
+bool BasicValueConverter<std::string>::Convert(const base::Value& value,
+ std::string* field) const {
+ return value.GetAsString(field);
+}
+
+bool BasicValueConverter<std::u16string>::Convert(const base::Value& value,
+ std::u16string* field) const {
+ return value.GetAsString(field);
+}
+
+bool BasicValueConverter<double>::Convert(const base::Value& value,
+ double* field) const {
+ return value.GetAsDouble(field);
+}
+
+bool BasicValueConverter<bool>::Convert(const base::Value& value,
+ bool* field) const {
+ return value.GetAsBoolean(field);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/gn/src/base/json/json_value_converter.h b/gn/src/base/json/json_value_converter.h
new file mode 100644
index 00000000000..f3030f2c949
--- /dev/null
+++ b/gn/src/base/json/json_value_converter.h
@@ -0,0 +1,515 @@
+// 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 BASE_JSON_JSON_VALUE_CONVERTER_H_
+#define BASE_JSON_JSON_VALUE_CONVERTER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+
+// JSONValueConverter converts a JSON value into a C++ struct in a
+// lightweight way.
+//
+// Usage:
+// For real examples, you may want to refer to _unittest.cc file.
+//
+// Assume that you have a struct like this:
+// struct Message {
+// int foo;
+// std::string bar;
+// static void RegisterJSONConverter(
+// JSONValueConverter<Message>* converter);
+// };
+//
+// And you want to parse a json data into this struct. First, you
+// need to declare RegisterJSONConverter() method in your struct.
+// // static
+// void Message::RegisterJSONConverter(
+// JSONValueConverter<Message>* converter) {
+// converter->RegisterIntField("foo", &Message::foo);
+// converter->RegisterStringField("bar", &Message::bar);
+// }
+//
+// Then, you just instantiate your JSONValueConverter of your type and call
+// Convert() method.
+// Message message;
+// JSONValueConverter<Message> converter;
+// converter.Convert(json, &message);
+//
+// Convert() returns false when it fails. Here "fail" means that the value is
+// structurally different from expected, such like a string value appears
+// for an int field. Do not report failures for missing fields.
+// Also note that Convert() will modify the passed |message| even when it
+// fails for performance reason.
+//
+// For nested field, the internal message also has to implement the registration
+// method. Then, just use RegisterNestedField() from the containing struct's
+// RegisterJSONConverter method.
+// struct Nested {
+// Message foo;
+// static void RegisterJSONConverter(...) {
+// ...
+// converter->RegisterNestedField("foo", &Nested::foo);
+// }
+// };
+//
+// For repeated field, we just assume std::vector<std::unique_ptr<ElementType>>
+// for its container and you can put RegisterRepeatedInt or some other types.
+// Use RegisterRepeatedMessage for nested repeated fields.
+//
+// Sometimes JSON format uses string representations for other types such
+// like enum, timestamp, or URL. You can use RegisterCustomField method
+// and specify a function to convert a std::string_view to your type.
+// bool ConvertFunc(std::string_view s, YourEnum* result) {
+// // do something and return true if succeed...
+// }
+// struct Message {
+// YourEnum ye;
+// ...
+// static void RegisterJSONConverter(...) {
+// ...
+// converter->RegsiterCustomField<YourEnum>(
+// "your_enum", &Message::ye, &ConvertFunc);
+// }
+// };
+
+namespace base {
+
+template <typename StructType>
+class JSONValueConverter;
+
+namespace internal {
+
+template <typename StructType>
+class FieldConverterBase {
+ public:
+ explicit FieldConverterBase(const std::string& path) : field_path_(path) {}
+ virtual ~FieldConverterBase() = default;
+ virtual bool ConvertField(const base::Value& value,
+ StructType* obj) const = 0;
+ const std::string& field_path() const { return field_path_; }
+
+ private:
+ std::string field_path_;
+ DISALLOW_COPY_AND_ASSIGN(FieldConverterBase);
+};
+
+template <typename FieldType>
+class ValueConverter {
+ public:
+ virtual ~ValueConverter() = default;
+ virtual bool Convert(const base::Value& value, FieldType* field) const = 0;
+};
+
+template <typename StructType, typename FieldType>
+class FieldConverter : public FieldConverterBase<StructType> {
+ public:
+ explicit FieldConverter(const std::string& path,
+ FieldType StructType::*field,
+ ValueConverter<FieldType>* converter)
+ : FieldConverterBase<StructType>(path),
+ field_pointer_(field),
+ value_converter_(converter) {}
+
+ bool ConvertField(const base::Value& value, StructType* dst) const override {
+ return value_converter_->Convert(value, &(dst->*field_pointer_));
+ }
+
+ private:
+ FieldType StructType::*field_pointer_;
+ std::unique_ptr<ValueConverter<FieldType>> value_converter_;
+ DISALLOW_COPY_AND_ASSIGN(FieldConverter);
+};
+
+template <typename FieldType>
+class BasicValueConverter;
+
+template <>
+class BasicValueConverter<int> : public ValueConverter<int> {
+ public:
+ BasicValueConverter() = default;
+
+ bool Convert(const base::Value& value, int* field) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<std::string> : public ValueConverter<std::string> {
+ public:
+ BasicValueConverter() = default;
+
+ bool Convert(const base::Value& value, std::string* field) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<std::u16string>
+ : public ValueConverter<std::u16string> {
+ public:
+ BasicValueConverter() = default;
+
+ bool Convert(const base::Value& value, std::u16string* field) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<double> : public ValueConverter<double> {
+ public:
+ BasicValueConverter() = default;
+
+ bool Convert(const base::Value& value, double* field) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<bool> : public ValueConverter<bool> {
+ public:
+ BasicValueConverter() = default;
+
+ bool Convert(const base::Value& value, bool* field) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <typename FieldType>
+class ValueFieldConverter : public ValueConverter<FieldType> {
+ public:
+ typedef bool (*ConvertFunc)(const base::Value* value, FieldType* field);
+
+ explicit ValueFieldConverter(ConvertFunc convert_func)
+ : convert_func_(convert_func) {}
+
+ bool Convert(const base::Value& value, FieldType* field) const override {
+ return convert_func_(&value, field);
+ }
+
+ private:
+ ConvertFunc convert_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter);
+};
+
+template <typename FieldType>
+class CustomFieldConverter : public ValueConverter<FieldType> {
+ public:
+ typedef bool (*ConvertFunc)(std::string_view value, FieldType* field);
+
+ explicit CustomFieldConverter(ConvertFunc convert_func)
+ : convert_func_(convert_func) {}
+
+ bool Convert(const base::Value& value, FieldType* field) const override {
+ std::string string_value;
+ return value.GetAsString(&string_value) &&
+ convert_func_(string_value, field);
+ }
+
+ private:
+ ConvertFunc convert_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter);
+};
+
+template <typename NestedType>
+class NestedValueConverter : public ValueConverter<NestedType> {
+ public:
+ NestedValueConverter() = default;
+
+ bool Convert(const base::Value& value, NestedType* field) const override {
+ return converter_.Convert(value, field);
+ }
+
+ private:
+ JSONValueConverter<NestedType> converter_;
+ DISALLOW_COPY_AND_ASSIGN(NestedValueConverter);
+};
+
+template <typename Element>
+class RepeatedValueConverter
+ : public ValueConverter<std::vector<std::unique_ptr<Element>>> {
+ public:
+ RepeatedValueConverter() = default;
+
+ bool Convert(const base::Value& value,
+ std::vector<std::unique_ptr<Element>>* field) const override {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list)) {
+ // The field is not a list.
+ return false;
+ }
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ std::unique_ptr<Element> e(new Element);
+ if (basic_converter_.Convert(*element, e.get())) {
+ field->push_back(std::move(e));
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ BasicValueConverter<Element> basic_converter_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter);
+};
+
+template <typename NestedType>
+class RepeatedMessageConverter
+ : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
+ public:
+ RepeatedMessageConverter() = default;
+
+ bool Convert(const base::Value& value,
+ std::vector<std::unique_ptr<NestedType>>* field) const override {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list))
+ return false;
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ std::unique_ptr<NestedType> nested(new NestedType);
+ if (converter_.Convert(*element, nested.get())) {
+ field->push_back(std::move(nested));
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ JSONValueConverter<NestedType> converter_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter);
+};
+
+template <typename NestedType>
+class RepeatedCustomValueConverter
+ : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
+ public:
+ typedef bool (*ConvertFunc)(const base::Value* value, NestedType* field);
+
+ explicit RepeatedCustomValueConverter(ConvertFunc convert_func)
+ : convert_func_(convert_func) {}
+
+ bool Convert(const base::Value& value,
+ std::vector<std::unique_ptr<NestedType>>* field) const override {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list))
+ return false;
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ std::unique_ptr<NestedType> nested(new NestedType);
+ if ((*convert_func_)(element, nested.get())) {
+ field->push_back(std::move(nested));
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ ConvertFunc convert_func_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter);
+};
+
+} // namespace internal
+
+template <class StructType>
+class JSONValueConverter {
+ public:
+ JSONValueConverter() { StructType::RegisterJSONConverter(this); }
+
+ void RegisterIntField(const std::string& field_name, int StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, int>>(
+ field_name, field, new internal::BasicValueConverter<int>));
+ }
+
+ void RegisterStringField(const std::string& field_name,
+ std::string StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, std::string>>(
+ field_name, field, new internal::BasicValueConverter<std::string>));
+ }
+
+ void RegisterStringField(const std::string& field_name,
+ std::u16string StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, std::u16string>>(
+ field_name, field,
+ new internal::BasicValueConverter<std::u16string>));
+ }
+
+ void RegisterBoolField(const std::string& field_name,
+ bool StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, bool>>(
+ field_name, field, new internal::BasicValueConverter<bool>));
+ }
+
+ void RegisterDoubleField(const std::string& field_name,
+ double StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, double>>(
+ field_name, field, new internal::BasicValueConverter<double>));
+ }
+
+ template <class NestedType>
+ void RegisterNestedField(const std::string& field_name,
+ NestedType StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, NestedType>>(
+ field_name, field, new internal::NestedValueConverter<NestedType>));
+ }
+
+ template <typename FieldType>
+ void RegisterCustomField(const std::string& field_name,
+ FieldType StructType::*field,
+ bool (*convert_func)(std::string_view, FieldType*)) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, FieldType>>(
+ field_name, field,
+ new internal::CustomFieldConverter<FieldType>(convert_func)));
+ }
+
+ template <typename FieldType>
+ void RegisterCustomValueField(const std::string& field_name,
+ FieldType StructType::*field,
+ bool (*convert_func)(const base::Value*,
+ FieldType*)) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<StructType, FieldType>>(
+ field_name, field,
+ new internal::ValueFieldConverter<FieldType>(convert_func)));
+ }
+
+ void RegisterRepeatedInt(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<int>> StructType::*field) {
+ fields_.push_back(std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<int>>>>(
+ field_name, field, new internal::RepeatedValueConverter<int>));
+ }
+
+ void RegisterRepeatedString(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<std::string>> StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<std::string>>>>(
+ field_name, field,
+ new internal::RepeatedValueConverter<std::string>));
+ }
+
+ void RegisterRepeatedString(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<std::u16string>> StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<std::u16string>>>>(
+ field_name, field,
+ new internal::RepeatedValueConverter<std::u16string>));
+ }
+
+ void RegisterRepeatedDouble(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<double>> StructType::*field) {
+ fields_.push_back(std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<double>>>>(
+ field_name, field, new internal::RepeatedValueConverter<double>));
+ }
+
+ void RegisterRepeatedBool(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<bool>> StructType::*field) {
+ fields_.push_back(std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<bool>>>>(
+ field_name, field, new internal::RepeatedValueConverter<bool>));
+ }
+
+ template <class NestedType>
+ void RegisterRepeatedCustomValue(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<NestedType>> StructType::*field,
+ bool (*convert_func)(const base::Value*, NestedType*)) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<NestedType>>>>(
+ field_name, field,
+ new internal::RepeatedCustomValueConverter<NestedType>(
+ convert_func)));
+ }
+
+ template <class NestedType>
+ void RegisterRepeatedMessage(
+ const std::string& field_name,
+ std::vector<std::unique_ptr<NestedType>> StructType::*field) {
+ fields_.push_back(
+ std::make_unique<internal::FieldConverter<
+ StructType, std::vector<std::unique_ptr<NestedType>>>>(
+ field_name, field,
+ new internal::RepeatedMessageConverter<NestedType>));
+ }
+
+ bool Convert(const base::Value& value, StructType* output) const {
+ const DictionaryValue* dictionary_value = NULL;
+ if (!value.GetAsDictionary(&dictionary_value))
+ return false;
+
+ for (size_t i = 0; i < fields_.size(); ++i) {
+ const internal::FieldConverterBase<StructType>* field_converter =
+ fields_[i].get();
+ const base::Value* field = NULL;
+ if (dictionary_value->Get(field_converter->field_path(), &field)) {
+ if (!field_converter->ConvertField(*field, output)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+ std::vector<std::unique_ptr<internal::FieldConverterBase<StructType>>>
+ fields_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_
diff --git a/gn/src/base/json/json_writer.cc b/gn/src/base/json/json_writer.cc
new file mode 100644
index 00000000000..912a7f0ad50
--- /dev/null
+++ b/gn/src/base/json/json_writer.cc
@@ -0,0 +1,175 @@
+// 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 "base/json/json_writer.h"
+
+#include <stdint.h>
+
+#include <cmath>
+#include <limits>
+
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "util/build_config.h"
+
+namespace base {
+
+#if defined(OS_WIN)
+const char kPrettyPrintLineEnding[] = "\r\n";
+#else
+const char kPrettyPrintLineEnding[] = "\n";
+#endif
+
+// static
+bool JSONWriter::Write(const Value& node, std::string* json) {
+ return WriteWithOptions(node, 0, json);
+}
+
+// static
+bool JSONWriter::WriteWithOptions(const Value& node,
+ int options,
+ std::string* json) {
+ json->clear();
+ // Is there a better way to estimate the size of the output?
+ json->reserve(1024);
+
+ JSONWriter writer(options, json);
+ bool result = writer.BuildJSONString(node, 0U);
+
+ if (options & OPTIONS_PRETTY_PRINT)
+ json->append(kPrettyPrintLineEnding);
+
+ return result;
+}
+
+JSONWriter::JSONWriter(int options, std::string* json)
+ : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
+ pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
+ json_string_(json) {
+ DCHECK(json);
+}
+
+bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
+ switch (node.type()) {
+ case Value::Type::NONE: {
+ json_string_->append("null");
+ return true;
+ }
+
+ case Value::Type::BOOLEAN: {
+ bool value;
+ bool result = node.GetAsBoolean(&value);
+ DCHECK(result);
+ json_string_->append(value ? "true" : "false");
+ return result;
+ }
+
+ case Value::Type::INTEGER: {
+ int value;
+ bool result = node.GetAsInteger(&value);
+ DCHECK(result);
+ json_string_->append(IntToString(value));
+ return result;
+ }
+
+ case Value::Type::STRING: {
+ std::string value;
+ bool result = node.GetAsString(&value);
+ DCHECK(result);
+ EscapeJSONString(value, true, json_string_);
+ return result;
+ }
+
+ case Value::Type::LIST: {
+ json_string_->push_back('[');
+ if (pretty_print_)
+ json_string_->push_back(' ');
+
+ const ListValue* list = nullptr;
+ bool first_value_has_been_output = false;
+ bool result = node.GetAsList(&list);
+ DCHECK(result);
+ for (const auto& value : *list) {
+ if (omit_binary_values_ && value.type() == Value::Type::BINARY)
+ continue;
+
+ if (first_value_has_been_output) {
+ json_string_->push_back(',');
+ if (pretty_print_)
+ json_string_->push_back(' ');
+ }
+
+ if (!BuildJSONString(value, depth))
+ result = false;
+
+ first_value_has_been_output = true;
+ }
+
+ if (pretty_print_)
+ json_string_->push_back(' ');
+ json_string_->push_back(']');
+ return result;
+ }
+
+ case Value::Type::DICTIONARY: {
+ json_string_->push_back('{');
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+
+ const DictionaryValue* dict = nullptr;
+ bool first_value_has_been_output = false;
+ bool result = node.GetAsDictionary(&dict);
+ DCHECK(result);
+ for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
+ itr.Advance()) {
+ if (omit_binary_values_ && itr.value().type() == Value::Type::BINARY) {
+ continue;
+ }
+
+ if (first_value_has_been_output) {
+ json_string_->push_back(',');
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+ }
+
+ if (pretty_print_)
+ IndentLine(depth + 1U);
+
+ EscapeJSONString(itr.key(), true, json_string_);
+ json_string_->push_back(':');
+ if (pretty_print_)
+ json_string_->push_back(' ');
+
+ if (!BuildJSONString(itr.value(), depth + 1U))
+ result = false;
+
+ first_value_has_been_output = true;
+ }
+
+ if (pretty_print_) {
+ json_string_->append(kPrettyPrintLineEnding);
+ IndentLine(depth);
+ }
+
+ json_string_->push_back('}');
+ return result;
+ }
+
+ case Value::Type::BINARY:
+ // Successful only if we're allowed to omit it.
+ DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
+ return omit_binary_values_;
+ }
+ NOTREACHED();
+ return false;
+}
+
+void JSONWriter::IndentLine(size_t depth) {
+ json_string_->append(depth * 3U, ' ');
+}
+
+} // namespace base
diff --git a/gn/src/base/json/json_writer.h b/gn/src/base/json/json_writer.h
new file mode 100644
index 00000000000..4114f7772ee
--- /dev/null
+++ b/gn/src/base/json/json_writer.h
@@ -0,0 +1,67 @@
+// 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 BASE_JSON_JSON_WRITER_H_
+#define BASE_JSON_JSON_WRITER_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace base {
+
+class Value;
+
+class JSONWriter {
+ public:
+ enum Options {
+ // This option instructs the writer that if a Binary value is encountered,
+ // the value (and key if within a dictionary) will be omitted from the
+ // output, and success will be returned. Otherwise, if a binary value is
+ // encountered, failure will be returned.
+ OPTIONS_OMIT_BINARY_VALUES = 1 << 0,
+
+ // Return a slightly nicer formatted json string (pads with whitespace to
+ // help with readability).
+ OPTIONS_PRETTY_PRINT = 1 << 2,
+ };
+
+ // Given a root node, generates a JSON string and puts it into |json|.
+ // The output string is overwritten and not appended.
+ //
+ // TODO(tc): Should we generate json if it would be invalid json (e.g.,
+ // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
+ // values)? Return true on success and false on failure.
+ static bool Write(const Value& node, std::string* json);
+
+ // Same as above but with |options| which is a bunch of JSONWriter::Options
+ // bitwise ORed together. Return true on success and false on failure.
+ static bool WriteWithOptions(const Value& node,
+ int options,
+ std::string* json);
+
+ private:
+ JSONWriter(int options, std::string* json);
+
+ // Called recursively to build the JSON string. When completed,
+ // |json_string_| will contain the JSON.
+ bool BuildJSONString(const Value& node, size_t depth);
+
+ // Adds space to json_string_ for the indent level.
+ void IndentLine(size_t depth);
+
+ bool omit_binary_values_;
+ bool pretty_print_;
+
+ // Where we write JSON data as we generate it.
+ std::string* json_string_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONWriter);
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_WRITER_H_
diff --git a/gn/src/base/json/string_escape.cc b/gn/src/base/json/string_escape.cc
new file mode 100644
index 00000000000..51a818cb479
--- /dev/null
+++ b/gn/src/base/json/string_escape.cc
@@ -0,0 +1,156 @@
+// 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 "base/json/string_escape.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base {
+
+namespace {
+
+// Format string for printing a \uXXXX escape sequence.
+const char kU16EscapeFormat[] = "\\u%04X";
+
+// The code point to output for an invalid input code unit.
+const uint32_t kReplacementCodePoint = 0xFFFD;
+
+// Used below in EscapeSpecialCodePoint().
+static_assert('<' == 0x3C, "less than sign must be 0x3c");
+
+// Try to escape the |code_point| if it is a known special character. If
+// successful, returns true and appends the escape sequence to |dest|. This
+// isn't required by the spec, but it's more readable by humans.
+bool EscapeSpecialCodePoint(uint32_t code_point, std::string* dest) {
+ // WARNING: if you add a new case here, you need to update the reader as well.
+ // Note: \v is in the reader, but not here since the JSON spec doesn't
+ // allow it.
+ switch (code_point) {
+ case '\b':
+ dest->append("\\b");
+ break;
+ case '\f':
+ dest->append("\\f");
+ break;
+ case '\n':
+ dest->append("\\n");
+ break;
+ case '\r':
+ dest->append("\\r");
+ break;
+ case '\t':
+ dest->append("\\t");
+ break;
+ case '\\':
+ dest->append("\\\\");
+ break;
+ case '"':
+ dest->append("\\\"");
+ break;
+ // Escape < to prevent script execution; escaping > is not necessary and
+ // not doing so save a few bytes.
+ case '<':
+ dest->append("\\u003C");
+ break;
+ // Escape the "Line Separator" and "Paragraph Separator" characters, since
+ // they should be treated like a new line \r or \n.
+ case 0x2028:
+ dest->append("\\u2028");
+ break;
+ case 0x2029:
+ dest->append("\\u2029");
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+template <typename S>
+bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) {
+ bool did_replacement = false;
+
+ if (put_in_quotes)
+ dest->push_back('"');
+
+ // Casting is necessary because ICU uses int32_t. Try and do so safely.
+ CHECK_LE(str.length(),
+ static_cast<size_t>(std::numeric_limits<int32_t>::max()));
+ const int32_t length = static_cast<int32_t>(str.length());
+
+ for (int32_t i = 0; i < length; ++i) {
+ uint32_t code_point;
+ if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) ||
+ code_point == static_cast<decltype(code_point)>(CBU_SENTINEL) ||
+ !IsValidCharacter(code_point)) {
+ code_point = kReplacementCodePoint;
+ did_replacement = true;
+ }
+
+ if (EscapeSpecialCodePoint(code_point, dest))
+ continue;
+
+ // Escape non-printing characters.
+ if (code_point < 32)
+ base::StringAppendF(dest, kU16EscapeFormat, code_point);
+ else
+ WriteUnicodeCharacter(code_point, dest);
+ }
+
+ if (put_in_quotes)
+ dest->push_back('"');
+
+ return !did_replacement;
+}
+
+} // namespace
+
+void EscapeJSONString(std::string_view str,
+ bool put_in_quotes,
+ std::string* dest) {
+ EscapeJSONStringImpl(str, put_in_quotes, dest);
+}
+
+void EscapeJSONString(std::u16string_view str,
+ bool put_in_quotes,
+ std::string* dest) {
+ EscapeJSONStringImpl(str, put_in_quotes, dest);
+}
+
+std::string EscapeBytesAsInvalidJSONString(std::string_view str,
+ bool put_in_quotes) {
+ std::string dest;
+
+ if (put_in_quotes)
+ dest.push_back('"');
+
+ for (std::string_view::const_iterator it = str.begin(); it != str.end();
+ ++it) {
+ unsigned char c = *it;
+ if (EscapeSpecialCodePoint(c, &dest))
+ continue;
+
+ if (c < 32 || c > 126)
+ base::StringAppendF(&dest, kU16EscapeFormat, c);
+ else
+ dest.push_back(*it);
+ }
+
+ if (put_in_quotes)
+ dest.push_back('"');
+
+ return dest;
+}
+
+} // namespace base
diff --git a/gn/src/base/json/string_escape.h b/gn/src/base/json/string_escape.h
new file mode 100644
index 00000000000..14eecdd1dc0
--- /dev/null
+++ b/gn/src/base/json/string_escape.h
@@ -0,0 +1,54 @@
+// 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.
+
+// This file defines utility functions for escaping strings suitable for JSON.
+
+#ifndef BASE_JSON_STRING_ESCAPE_H_
+#define BASE_JSON_STRING_ESCAPE_H_
+
+#include <string>
+#include <string_view>
+
+namespace base {
+
+// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units and
+// characters will pass through from the input to the output. Invalid code
+// units and characters will be replaced with the U+FFFD replacement character.
+// This function returns true if no replacement was necessary and false if
+// there was a lossy replacement. On return, |dest| will contain a valid UTF-8
+// JSON string.
+//
+// Non-printing control characters will be escaped as \uXXXX sequences for
+// readability.
+//
+// If |put_in_quotes| is true, then a leading and trailing double-quote mark
+// will be appended to |dest| as well.
+void EscapeJSONString(std::string_view str,
+ bool put_in_quotes,
+ std::string* dest);
+
+// Performs a similar function to the UTF-8 std::string_view version above,
+// converting UTF-16 code units to UTF-8 code units and escaping non-printing
+// control characters. On return, |dest| will contain a valid UTF-8 JSON string.
+void EscapeJSONString(std::u16string_view str,
+ bool put_in_quotes,
+ std::string* dest);
+
+// Given an arbitrary byte string |str|, this will escape all non-ASCII bytes
+// as \uXXXX escape sequences. This function is *NOT* meant to be used with
+// Unicode strings and does not validate |str| as one.
+//
+// CAVEAT CALLER: The output of this function may not be valid JSON, since
+// JSON requires escape sequences to be valid UTF-16 code units. This output
+// will be mangled if passed to to the base::JSONReader, since the reader will
+// interpret it as UTF-16 and convert it to UTF-8.
+//
+// The output of this function takes the *appearance* of JSON but is not in
+// fact valid according to RFC 4627.
+std::string EscapeBytesAsInvalidJSONString(std::string_view str,
+ bool put_in_quotes);
+
+} // namespace base
+
+#endif // BASE_JSON_STRING_ESCAPE_H_
diff --git a/gn/src/base/logging.cc b/gn/src/base/logging.cc
new file mode 100644
index 00000000000..ee60c4703a1
--- /dev/null
+++ b/gn/src/base/logging.cc
@@ -0,0 +1,325 @@
+// 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 "base/logging.h"
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <iterator>
+#include <thread>
+
+#include "base/macros.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <io.h>
+#include <windows.h>
+// Windows warns on using write(). It prefers _write().
+#define write(fd, buf, count) _write(fd, buf, static_cast<unsigned int>(count))
+// Windows doesn't define STDERR_FILENO. Define it here.
+#define STDERR_FILENO 2
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <errno.h>
+#include <paths.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <cstring>
+#include <ctime>
+#include <iomanip>
+#include <ostream>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "base/posix/safe_strerror.h"
+#endif
+
+namespace logging {
+
+namespace {
+
+const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
+static_assert(LOG_NUM_SEVERITIES == std::size(log_severity_names),
+ "Incorrect number of log_severity_names");
+
+const char* log_severity_name(int severity) {
+ if (severity >= 0 && severity < LOG_NUM_SEVERITIES)
+ return log_severity_names[severity];
+ return "UNKNOWN";
+}
+
+int g_min_log_level = 0;
+
+// For LOG_ERROR and above, always print to stderr.
+const int kAlwaysPrintErrorLevel = LOG_ERROR;
+
+} // namespace
+
+#if DCHECK_IS_CONFIGURABLE
+// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be
+// determined at run-time. We default it to INFO, to avoid it triggering
+// crashes before the run-time has explicitly chosen the behaviour.
+logging::LogSeverity LOG_DCHECK = LOG_INFO;
+#endif // DCHECK_IS_CONFIGURABLE
+
+// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
+// an object of the correct type on the LHS of the unused part of the ternary
+// operator.
+std::ostream* g_swallow_stream;
+
+void SetMinLogLevel(int level) {
+ g_min_log_level = std::min(LOG_FATAL, level);
+}
+
+int GetMinLogLevel() {
+ return g_min_log_level;
+}
+
+bool ShouldCreateLogMessage(int severity) {
+ if (severity < g_min_log_level)
+ return false;
+
+ // Return true here unless we know ~LogMessage won't do anything. Note that
+ // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even
+ // when g_logging_destination is LOG_NONE.
+ return severity >= kAlwaysPrintErrorLevel;
+}
+
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(const int&,
+ const int&,
+ const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&,
+ const unsigned long&,
+ const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&,
+ const unsigned int&,
+ const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&,
+ const unsigned long&,
+ const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&,
+ const std::string&,
+ const char* name);
+
+void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) {
+ (*os) << "nullptr";
+}
+
+#if defined(OS_WIN)
+LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {}
+
+LogMessage::SaveLastError::~SaveLastError() {
+ ::SetLastError(last_error_);
+}
+#endif // defined(OS_WIN)
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+ : severity_(severity) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, const char* condition)
+ : severity_(LOG_FATAL) {
+ Init(file, line);
+ stream_ << "Check failed: " << condition << ". ";
+}
+
+LogMessage::LogMessage(const char* file, int line, std::string* result)
+ : severity_(LOG_FATAL) {
+ Init(file, line);
+ stream_ << "Check failed: " << *result;
+ delete result;
+}
+
+LogMessage::LogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ std::string* result)
+ : severity_(severity) {
+ Init(file, line);
+ stream_ << "Check failed: " << *result;
+ delete result;
+}
+
+LogMessage::~LogMessage() {
+ if (severity_ == LOG_FATAL) {
+ stream_ << std::endl; // Newline to separate from log message.
+ }
+ stream_ << std::endl;
+ std::string str_newline(stream_.str());
+
+#if defined(OS_WIN)
+ OutputDebugStringA(str_newline.c_str());
+#endif
+ ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr));
+ fflush(stderr);
+
+ if (severity_ == LOG_FATAL) {
+ abort();
+ }
+}
+
+// writes the common header info to the stream
+void LogMessage::Init(const char* file, int line) {
+ std::string_view filename(file);
+ size_t last_slash_pos = filename.find_last_of("\\/");
+ if (last_slash_pos != std::string_view::npos)
+ filename.remove_prefix(last_slash_pos + 1);
+
+ // TODO(darin): It might be nice if the columns were fixed width.
+
+ stream_ << '[';
+ stream_ << std::this_thread::get_id() << ':';
+#if defined(OS_WIN)
+ SYSTEMTIME local_time;
+ GetLocalTime(&local_time);
+ stream_ << std::setfill('0') << std::setw(2) << local_time.wMonth
+ << std::setw(2) << local_time.wDay << '/' << std::setw(2)
+ << local_time.wHour << std::setw(2) << local_time.wMinute
+ << std::setw(2) << local_time.wSecond << '.' << std::setw(3)
+ << local_time.wMilliseconds << ':';
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ timeval tv;
+ gettimeofday(&tv, nullptr);
+ time_t t = tv.tv_sec;
+ struct tm local_time;
+ localtime_r(&t, &local_time);
+ struct tm* tm_time = &local_time;
+ stream_ << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
+ << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
+ << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
+ << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << ':';
+#else
+#error Unsupported platform
+#endif
+ if (severity_ >= 0)
+ stream_ << log_severity_name(severity_);
+ else
+ stream_ << "VERBOSE" << -severity_;
+
+ stream_ << ":" << filename << "(" << line << ")] ";
+
+ message_start_ = stream_.str().length();
+}
+
+#if defined(OS_WIN)
+// This has already been defined in the header, but defining it again as DWORD
+// ensures that the type used in the header is equivalent to DWORD. If not,
+// the redefinition is a compile error.
+typedef DWORD SystemErrorCode;
+#endif
+
+SystemErrorCode GetLastSystemErrorCode() {
+#if defined(OS_WIN)
+ return ::GetLastError();
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return errno;
+#endif
+}
+
+std::string SystemErrorCodeToString(SystemErrorCode error_code) {
+#if defined(OS_WIN)
+ const int kErrorMessageBufferSize = 256;
+ char msgbuf[kErrorMessageBufferSize];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf,
+ std::size(msgbuf), nullptr);
+ if (len) {
+ // Messages returned by system end with line breaks.
+ return base::CollapseWhitespaceASCII(msgbuf, true) +
+ base::StringPrintf(" (0x%lX)", error_code);
+ }
+ return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)",
+ GetLastError(), error_code);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return base::safe_strerror(error_code) +
+ base::StringPrintf(" (%d)", error_code);
+#endif // defined(OS_WIN)
+}
+
+#if defined(OS_WIN)
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err), log_message_(file, line, severity) {}
+
+Win32ErrorLogMessage::~Win32ErrorLogMessage() {
+ stream() << ": " << SystemErrorCodeToString(err_);
+}
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ErrnoLogMessage::ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err), log_message_(file, line, severity) {}
+
+ErrnoLogMessage::~ErrnoLogMessage() {
+ stream() << ": " << SystemErrorCodeToString(err_);
+}
+#endif // defined(OS_WIN)
+
+void RawLog(int level, const char* message) {
+ if (level >= g_min_log_level && message) {
+ size_t bytes_written = 0;
+ const size_t message_len = strlen(message);
+ int rv;
+ while (bytes_written < message_len) {
+ rv = HANDLE_EINTR(write(STDERR_FILENO, message + bytes_written,
+ message_len - bytes_written));
+ if (rv < 0) {
+ // Give up, nothing we can do now.
+ break;
+ }
+ bytes_written += rv;
+ }
+
+ if (message_len > 0 && message[message_len - 1] != '\n') {
+ do {
+ rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1));
+ if (rv < 0) {
+ // Give up, nothing we can do now.
+ break;
+ }
+ } while (rv != 1);
+ }
+ }
+
+ if (level == LOG_FATAL)
+ abort();
+}
+
+// This was defined at the beginning of this file.
+#undef write
+
+void LogErrorNotReached(const char* file, int line) {
+ LogMessage(file, line, LOG_ERROR).stream() << "NOTREACHED() hit.";
+}
+
+} // namespace logging
diff --git a/gn/src/base/logging.h b/gn/src/base/logging.h
new file mode 100644
index 00000000000..78cad59d179
--- /dev/null
+++ b/gn/src/base/logging.h
@@ -0,0 +1,922 @@
+// 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 BASE_LOGGING_H_
+#define BASE_LOGGING_H_
+
+#include <stddef.h>
+
+#include <cassert>
+#include <cstring>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/template_util.h"
+#include "util/build_config.h"
+
+//
+// Optional message capabilities
+// -----------------------------
+// Assertion failed messages and fatal errors are displayed in a dialog box
+// before the application exits. However, running this UI creates a message
+// loop, which causes application messages to be processed and potentially
+// dispatched to existing application windows. Since the application is in a
+// bad state when this assertion dialog is displayed, these messages may not
+// get processed and hang the dialog, or the application might go crazy.
+//
+// Therefore, it can be beneficial to display the error dialog in a separate
+// process from the main application. When the logging system needs to display
+// a fatal error dialog box, it will look for a program called
+// "DebugMessage.exe" in the same directory as the application executable. It
+// will run this application with the message as the command line, and will
+// not include the name of the application as is traditional for easier
+// parsing.
+//
+// The code for DebugMessage.exe is only one line. In WinMain, do:
+// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
+//
+// If DebugMessage.exe is not found, the logging code will use a normal
+// MessageBox, potentially causing the problems discussed above.
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging. The way to log things is to stream
+// things to LOG(<a particular severity level>). E.g.,
+//
+// LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+// DLOG(INFO) << "Found cookies";
+//
+// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles. LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+// LOG_ASSERT(assertion);
+// DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// Lastly, there is:
+//
+// PLOG(ERROR) << "Couldn't do foo";
+// DPLOG(ERROR) << "Couldn't do foo";
+// PLOG_IF(ERROR, cond) << "Couldn't do foo";
+// DPLOG_IF(ERROR, cond) << "Couldn't do foo";
+// PCHECK(condition) << "Couldn't do foo";
+// DPCHECK(condition) << "Couldn't do foo";
+//
+// which append the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+//
+// There is the special severity of DFATAL, which logs FATAL in debug mode,
+// ERROR in normal mode.
+
+namespace logging {
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged
+// up to level INFO) if this function is not called.
+void SetMinLogLevel(int level);
+
+// Gets the current log level.
+int GetMinLogLevel();
+
+// Used by LOG_IS_ON to lazy-evaluate stream arguments.
+bool ShouldCreateLogMessage(int severity);
+
+// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints
+// to Clang which control what code paths are statically analyzed,
+// and is meant to be used in conjunction with assert & assert-like functions.
+// The expression is passed straight through if analysis isn't enabled.
+//
+// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current
+// codepath and any other branching codepaths that might follow.
+#if defined(__clang_analyzer__)
+
+inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
+ return false;
+}
+
+inline constexpr bool AnalyzerAssumeTrue(bool arg) {
+ // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is
+ // false.
+ return arg || AnalyzerNoReturn();
+}
+
+#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg))
+#define ANALYZER_SKIP_THIS_PATH() \
+ static_cast<void>(::logging::AnalyzerNoReturn())
+#define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
+
+#else // !defined(__clang_analyzer__)
+
+#define ANALYZER_ASSUME_TRUE(arg) (arg)
+#define ANALYZER_SKIP_THIS_PATH()
+#define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
+
+#endif // defined(__clang_analyzer__)
+
+typedef int LogSeverity;
+const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
+// Note: the log severities are used to index into the array of names,
+// see log_severity_names.
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode
+#if defined(NDEBUG)
+const LogSeverity LOG_DFATAL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \
+ ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DCHECK, ##__VA_ARGS__)
+
+#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
+#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
+#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage)
+
+#if defined(OS_WIN)
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
+// Needed for LOG_IS_ON(ERROR).
+const LogSeverity LOG_0 = LOG_ERROR;
+#endif
+
+// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,
+// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will
+// always fire if they fail.
+#define LOG_IS_ON(severity) \
+ (::logging::ShouldCreateLogMessage(::logging::LOG_##severity))
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold. Condition is evaluated once and only once.
+#define LAZY_STREAM(stream, condition) \
+ !(condition) ? (void)0 : ::logging::LogMessageVoidify() & (stream)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_##severity.stream()
+
+#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_IF(severity, condition) \
+ LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+#define LOG_ASSERT(condition) \
+ LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \
+ << "Assert failed: " #condition ". "
+
+#if defined(OS_WIN)
+#define PLOG_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_##severity(Win32ErrorLogMessage, \
+ ::logging::GetLastSystemErrorCode()) \
+ .stream()
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define PLOG_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_##severity(ErrnoLogMessage, \
+ ::logging::GetLastSystemErrorCode()) \
+ .stream()
+#endif
+
+#define PLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
+
+#define PLOG_IF(severity, condition) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+extern std::ostream* g_swallow_stream;
+
+// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to
+// avoid the creation of an object with a non-trivial destructor (LogMessage).
+// On MSVC x86 (checked on 2015 Update 3), this causes a few additional
+// pointless instructions to be emitted even at full optimization level, even
+// though the : arm of the ternary operator is clearly never executed. Using a
+// simpler object to be &'d with Voidify() avoids these extra instructions.
+// Using a simpler POD object with a templated operator<< also works to avoid
+// these instructions. However, this causes warnings on statically defined
+// implementations of operator<<(std::ostream, ...) in some .cc files, because
+// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an
+// ostream* also is not suitable, because some compilers warn of undefined
+// behavior.
+#define EAT_STREAM_PARAMETERS \
+ true ? (void)0 \
+ : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream)
+
+// Captures the result of a CHECK_EQ (for example) and facilitates testing as a
+// boolean.
+class CheckOpResult {
+ public:
+ // |message| must be non-null if and only if the check failed.
+ CheckOpResult(std::string* message) : message_(message) {}
+ // Returns true if the check succeeded.
+ operator bool() const { return !message_; }
+ // Returns the message.
+ std::string* message() { return message_; }
+
+ private:
+ std::string* message_;
+};
+
+// Crashes in the fastest possible way with no attempt at logging.
+// There are different constraints to satisfy here, see http://crbug.com/664209
+// for more context:
+// - The trap instructions, and hence the PC value at crash time, have to be
+// distinct and not get folded into the same opcode by the compiler.
+// On Linux/Android this is tricky because GCC still folds identical
+// asm volatile blocks. The workaround is generating distinct opcodes for
+// each CHECK using the __COUNTER__ macro.
+// - The debug info for the trap instruction has to be attributed to the source
+// line that has the CHECK(), to make crash reports actionable. This rules
+// out the ability of using a inline function, at least as long as clang
+// doesn't support attribute(artificial).
+// - Failed CHECKs should produce a signal that is distinguishable from an
+// invalid memory access, to improve the actionability of crash reports.
+// - The compiler should treat the CHECK as no-return instructions, so that the
+// trap code can be efficiently packed in the prologue of the function and
+// doesn't interfere with the main execution flow.
+// - When debugging, developers shouldn't be able to accidentally step over a
+// CHECK. This is achieved by putting opcodes that will cause a non
+// continuable exception after the actual trap instruction.
+// - Don't cause too much binary bloat.
+#if defined(COMPILER_GCC)
+
+#if defined(ARCH_CPU_X86_FAMILY)
+// int 3 will generate a SIGTRAP.
+#define TRAP_SEQUENCE() \
+ asm volatile( \
+ "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__)))
+
+#elif defined(ARCH_CPU_ARMEL)
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+#define TRAP_SEQUENCE() \
+ asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256))
+
+#elif defined(ARCH_CPU_ARM64)
+// This will always generate a SIGTRAP on arm64.
+#define TRAP_SEQUENCE() \
+ asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536))
+
+#else
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define TRAP_SEQUENCE() __builtin_trap()
+#endif // ARCH_CPU_*
+
+// CHECK() and the trap sequence can be invoked from a constexpr function.
+// This could make compilation fail on GCC, as it forbids directly using inline
+// asm inside a constexpr function. However, it allows calling a lambda
+// expression including the same asm.
+// The side effect is that the top of the stacktrace will not point to the
+// calling function, but to this anonymous lambda. This is still useful as the
+// full name of the lambda will typically include the name of the function that
+// calls CHECK() and the debugger will still break at the right line of code.
+#if !defined(__clang__)
+#define WRAPPED_TRAP_SEQUENCE() \
+ do { \
+ [] { TRAP_SEQUENCE(); }(); \
+ } while (false)
+#else
+#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE()
+#endif
+
+#define IMMEDIATE_CRASH() \
+ ({ \
+ WRAPPED_TRAP_SEQUENCE(); \
+ __builtin_unreachable(); \
+ })
+
+#elif defined(COMPILER_MSVC)
+
+// Clang is cleverer about coalescing int3s, so we need to add a unique-ish
+// instruction following the __debugbreak() to have it emit distinct locations
+// for CHECKs rather than collapsing them all together. It would be nice to use
+// a short intrinsic to do this (and perhaps have only one implementation for
+// both clang and MSVC), however clang-cl currently does not support intrinsics.
+// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have
+// two implementations. Normally clang-cl's version will be 5 bytes (1 for
+// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg):
+// https://crbug.com/694670 clang-cl doesn't currently support %'ing
+// __COUNTER__, so eventually it will emit the dword form of push.
+// TODO(scottmg): Reinvestigate a short sequence that will work on both
+// compilers once clang supports more intrinsics. See https://crbug.com/693713.
+#if defined(__clang__)
+#define IMMEDIATE_CRASH() \
+ ({ \
+ {__asm int 3 __asm ud2 __asm push __COUNTER__}; \
+ __builtin_unreachable(); \
+ })
+#else
+#define IMMEDIATE_CRASH() __debugbreak()
+#endif // __clang__
+
+#else
+#error Port
+#endif
+
+// CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+//
+// We make sure CHECK et al. always evaluates their arguments, as
+// doing CHECK(FunctionWithSideEffect()) is a common idiom.
+
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG)
+
+// Make all CHECK functions discard their log strings to reduce code bloat, and
+// improve performance, for official release builds.
+//
+// This is not calling BreakDebugger since this is called frequently, and
+// calling an out-of-line function instead of a noreturn inline macro prevents
+// compiler optimizations.
+#define CHECK(condition) \
+ UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS
+
+// PCHECK includes the system error code, which is useful for determining
+// why the condition failed. In official builds, preserve only the error code
+// message so that it is available in crash reports. The stringified
+// condition and any additional stream parameters are dropped.
+#define PCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(FATAL), UNLIKELY(!(condition))); \
+ EAT_STREAM_PARAMETERS
+
+#define CHECK_OP(name, op, val1, val2) CHECK((val1)op(val2))
+
+#else // !(OFFICIAL_BUILD && NDEBUG)
+
+#if defined(_PREFAST_) && defined(OS_WIN)
+// Use __analysis_assume to tell the VC++ static analysis engine that
+// assert conditions are true, to suppress warnings. The LAZY_STREAM
+// parameter doesn't reference 'condition' in /analyze builds because
+// this evaluation confuses /analyze. The !! before condition is because
+// __analysis_assume gets confused on some conditions:
+// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/
+
+#define CHECK(condition) \
+ __analysis_assume(!!(condition)), LAZY_STREAM(LOG_STREAM(FATAL), false) \
+ << "Check failed: " #condition ". "
+
+#define PCHECK(condition) \
+ __analysis_assume(!!(condition)), LAZY_STREAM(PLOG_STREAM(FATAL), false) \
+ << "Check failed: " #condition ". "
+
+#else // _PREFAST_
+
+// Do as much work as possible out of line to reduce inline code size.
+#define CHECK(condition) \
+ LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
+ !ANALYZER_ASSUME_TRUE(condition))
+
+#define PCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
+ << "Check failed: " #condition ". "
+
+#endif // _PREFAST_
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+// The 'switch' is used to prevent the 'else' from being ambiguous when the
+// macro is used in an 'if' clause such as:
+// if (a == 1)
+// CHECK_EQ(2, a);
+#define CHECK_OP(name, op, val1, val2) \
+ switch (0) \
+ case 0: \
+ default: \
+ if (::logging::CheckOpResult true_if_passed = \
+ ::logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2)) \
+ ; \
+ else \
+ ::logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()) \
+ .stream()
+
+#endif // !(OFFICIAL_BUILD && NDEBUG)
+
+// This formats a value for a failing CHECK_XX statement. Ordinarily,
+// it uses the definition for operator<<, with a few special cases below.
+template <typename T>
+inline typename std::enable_if<
+ base::internal::SupportsOstreamOperator<const T&>::value &&
+ !std::is_function<typename std::remove_pointer<T>::type>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << v;
+}
+
+// Provide an overload for functions and function pointers. Function pointers
+// don't implicitly convert to void* but do implicitly convert to bool, so
+// without this function pointers are always printed as 1 or 0. (MSVC isn't
+// standards-conforming here and converts function pointers to regular
+// pointers, so this is a no-op for MSVC.)
+template <typename T>
+inline typename std::enable_if<
+ std::is_function<typename std::remove_pointer<T>::type>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << reinterpret_cast<const void*>(v);
+}
+
+// We need overloads for enums that don't support operator<<.
+// (i.e. scoped enums where no operator<< overload was declared).
+template <typename T>
+inline typename std::enable_if<
+ !base::internal::SupportsOstreamOperator<const T&>::value &&
+ std::is_enum<T>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << static_cast<typename std::underlying_type<T>::type>(v);
+}
+
+// We need an explicit overload for std::nullptr_t.
+void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p);
+
+// Build the error message string. This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline. Caller
+// takes ownership of the returned string.
+template <class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+ std::ostringstream ss;
+ ss << names << " (";
+ MakeCheckOpValueString(&ss, v1);
+ ss << " vs. ";
+ MakeCheckOpValueString(&ss, v2);
+ ss << ")";
+ std::string* msg = new std::string(ss.str());
+ return msg;
+}
+
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template std::string* MakeCheckOpString<int, int>(const int&,
+ const int&,
+ const char* names);
+extern template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&,
+ const unsigned long&,
+ const char* names);
+extern template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&,
+ const unsigned int&,
+ const char* names);
+extern template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&,
+ const unsigned long&,
+ const char* names);
+extern template std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&,
+ const std::string&,
+ const char* name);
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+//
+// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
+// static analysis builds, blocks analysis of the current path if the
+// condition is false.
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <class t1, class t2> \
+ inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+ const char* names) { \
+ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
+ return NULL; \
+ else \
+ return ::logging::MakeCheckOpString(v1, v2, names); \
+ } \
+ inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
+ return NULL; \
+ else \
+ return ::logging::MakeCheckOpString(v1, v2, names); \
+ }
+DEFINE_CHECK_OP_IMPL(EQ, ==)
+DEFINE_CHECK_OP_IMPL(NE, !=)
+DEFINE_CHECK_OP_IMPL(LE, <=)
+DEFINE_CHECK_OP_IMPL(LT, <)
+DEFINE_CHECK_OP_IMPL(GE, >=)
+DEFINE_CHECK_OP_IMPL(GT, >)
+#undef DEFINE_CHECK_OP_IMPL
+
+#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#define CHECK_LT(val1, val2) CHECK_OP(LT, <, val1, val2)
+#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
+#define CHECK_GT(val1, val2) CHECK_OP(GT, >, val1, val2)
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() 0
+#else
+#define DCHECK_IS_ON() 1
+#endif
+
+// Definitions for DLOG et al.
+
+#if DCHECK_IS_ON()
+
+#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
+
+#else // DCHECK_IS_ON()
+
+// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition|
+// (which may reference a variable defined only if DCHECK_IS_ON()).
+// Contrast this with DCHECK et al., which has different behavior.
+
+#define DLOG_IS_ON(severity) false
+#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+
+#endif // DCHECK_IS_ON()
+
+#define DLOG(severity) LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DPLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
+
+// Definitions for DCHECK et al.
+
+#if DCHECK_IS_ON()
+
+#if DCHECK_IS_CONFIGURABLE
+extern LogSeverity LOG_DCHECK;
+#else
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+#endif
+
+#else // DCHECK_IS_ON()
+
+// There may be users of LOG_DCHECK that are enabled independently
+// of DCHECK_IS_ON(), so default to FATAL logging for those.
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+
+#endif // DCHECK_IS_ON()
+
+// DCHECK et al. make sure to reference |condition| regardless of
+// whether DCHECKs are enabled; this is so that we don't get unused
+// variable warnings if the only use of a variable is in a DCHECK.
+// This behavior is different from DLOG_IF et al.
+//
+// Note that the definition of the DCHECK macros depends on whether or not
+// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
+// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
+
+#if defined(_PREFAST_) && defined(OS_WIN)
+// See comments on the previous use of __analysis_assume.
+
+#define DCHECK(condition) \
+ __analysis_assume(!!(condition)), LAZY_STREAM(LOG_STREAM(DCHECK), false) \
+ << "Check failed: " #condition ". "
+
+#define DPCHECK(condition) \
+ __analysis_assume(!!(condition)), LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
+ << "Check failed: " #condition ". "
+
+#else // !(defined(_PREFAST_) && defined(OS_WIN))
+
+#if DCHECK_IS_ON()
+
+#define DCHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
+ << "Check failed: " #condition ". "
+#define DPCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
+ << "Check failed: " #condition ". "
+
+#else // DCHECK_IS_ON()
+
+#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
+#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
+
+#endif // DCHECK_IS_ON()
+
+#endif // defined(_PREFAST_) && defined(OS_WIN)
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+// The 'switch' is used to prevent the 'else' from being ambiguous when the
+// macro is used in an 'if' clause such as:
+// if (a == 1)
+// DCHECK_EQ(2, a);
+#if DCHECK_IS_ON()
+
+#define DCHECK_OP(name, op, val1, val2) \
+ switch (0) \
+ case 0: \
+ default: \
+ if (::logging::CheckOpResult true_if_passed = \
+ DCHECK_IS_ON() ? ::logging::Check##name##Impl( \
+ (val1), (val2), #val1 " " #op " " #val2) \
+ : nullptr) \
+ ; \
+ else \
+ ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \
+ true_if_passed.message()) \
+ .stream()
+
+#else // DCHECK_IS_ON()
+
+// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<<
+// overloads for |val1| and |val2| to avoid potential compiler warnings about
+// unused functions. For the same reason, it also compares |val1| and |val2|
+// using |op|.
+//
+// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated
+// once. Even though |val1| and |val2| appear twice in this version of the macro
+// expansion, this is OK, since the expression is never actually evaluated.
+#define DCHECK_OP(name, op, val1, val2) \
+ EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \
+ ::logging::g_swallow_stream, val1), \
+ ::logging::MakeCheckOpValueString( \
+ ::logging::g_swallow_stream, val2), \
+ (val1)op(val2))
+
+#endif // DCHECK_IS_ON()
+
+// Equality/Inequality checks - compare two values, and log a
+// LOG_DCHECK message including the two values when the result is not
+// as expected. The values must have operator<<(ostream, ...)
+// defined.
+//
+// You may append to the error message like so:
+// DCHECK_NE(1, 2) << "The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here. In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+// DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These don't compile correctly if one of the arguments is a pointer
+// and the other is NULL. In new code, prefer nullptr instead. To
+// work around this for C++98, simply static_cast NULL to the type of the
+// desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, <, val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2)
+
+#define NOTREACHED() DCHECK(false)
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message. You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though. You should use the LOG() macro (and variants thereof)
+// above.
+class LogMessage {
+ public:
+ // Used for LOG(severity).
+ LogMessage(const char* file, int line, LogSeverity severity);
+
+ // Used for CHECK(). Implied severity = LOG_FATAL.
+ LogMessage(const char* file, int line, const char* condition);
+
+ // Used for CHECK_EQ(), etc. Takes ownership of the given string.
+ // Implied severity = LOG_FATAL.
+ LogMessage(const char* file, int line, std::string* result);
+
+ // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
+ LogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ std::string* result);
+
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ LogSeverity severity() { return severity_; }
+ std::string str() { return stream_.str(); }
+
+ private:
+ void Init(const char* file, int line);
+
+ LogSeverity severity_;
+ std::ostringstream stream_;
+ size_t message_start_; // Offset of the start of the message (past prefix
+ // info).
+
+#if defined(OS_WIN)
+ // Stores the current value of GetLastError in the constructor and restores
+ // it in the destructor by calling SetLastError.
+ // This is useful since the LogMessage class uses a lot of Win32 calls
+ // that will lose the value of GLE and the code that called the log function
+ // will have lost the thread error value when the log call returns.
+ class SaveLastError {
+ public:
+ SaveLastError();
+ ~SaveLastError();
+
+ unsigned long get_error() const { return last_error_; }
+
+ protected:
+ unsigned long last_error_;
+ };
+
+ SaveLastError last_error_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() = default;
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) {}
+};
+
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+typedef int SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+SystemErrorCode GetLastSystemErrorCode();
+std::string SystemErrorCodeToString(SystemErrorCode error_code);
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class Win32ErrorLogMessage {
+ public:
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~Win32ErrorLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Appends a formatted system message of the errno type
+class ErrnoLogMessage {
+ public:
+ ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~ErrnoLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#endif // OS_WIN
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+// statements, there's no guarantee that it will stay closed
+// after this call.
+void CloseLogFile();
+
+// Async signal safe logging mechanism.
+void RawLog(int level, const char* message);
+
+#define RAW_LOG(level, message) \
+ ::logging::RawLog(::logging::LOG_##level, message)
+
+#define RAW_CHECK(condition) \
+ do { \
+ if (!(condition)) \
+ ::logging::RawLog(::logging::LOG_FATAL, \
+ "Check failed: " #condition "\n"); \
+ } while (0)
+
+#if defined(OS_WIN)
+// Returns true if logging to file is enabled.
+bool IsLoggingToFileEnabled();
+
+// Returns the default log file path.
+std::u16string GetLogFileFullPath();
+#endif
+
+} // namespace logging
+
+// The NOTIMPLEMENTED() macro annotates codepaths which have not been
+// implemented yet. If output spam is a serious concern,
+// NOTIMPLEMENTED_LOG_ONCE can be used.
+
+#if defined(COMPILER_GCC)
+// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
+// of the current function in the NOTIMPLEMENTED message.
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
+#else
+#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
+#endif
+
+#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
+#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
+#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS
+#else
+#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
+#define NOTIMPLEMENTED_LOG_ONCE() \
+ do { \
+ static bool logged_once = false; \
+ LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
+ logged_once = true; \
+ } while (0); \
+ EAT_STREAM_PARAMETERS
+#endif
+
+#endif // BASE_LOGGING_H_
diff --git a/gn/base/mac/bundle_locations.h b/gn/src/base/mac/bundle_locations.h
index 2bda76e6411..2bda76e6411 100644
--- a/gn/base/mac/bundle_locations.h
+++ b/gn/src/base/mac/bundle_locations.h
diff --git a/gn/base/mac/mac_logging.h b/gn/src/base/mac/mac_logging.h
index 5ef75f38323..5ef75f38323 100644
--- a/gn/base/mac/mac_logging.h
+++ b/gn/src/base/mac/mac_logging.h
diff --git a/gn/base/mac/mac_logging.mm b/gn/src/base/mac/mac_logging.mm
index f7c30528bc5..f7c30528bc5 100644
--- a/gn/base/mac/mac_logging.mm
+++ b/gn/src/base/mac/mac_logging.mm
diff --git a/gn/base/mac/scoped_cftyperef.h b/gn/src/base/mac/scoped_cftyperef.h
index a602fd9cbb2..a602fd9cbb2 100644
--- a/gn/base/mac/scoped_cftyperef.h
+++ b/gn/src/base/mac/scoped_cftyperef.h
diff --git a/gn/base/mac/scoped_typeref.h b/gn/src/base/mac/scoped_typeref.h
index 659ee3426b0..659ee3426b0 100644
--- a/gn/base/mac/scoped_typeref.h
+++ b/gn/src/base/mac/scoped_typeref.h
diff --git a/gn/src/base/macros.h b/gn/src/base/macros.h
new file mode 100644
index 00000000000..750d54dfd49
--- /dev/null
+++ b/gn/src/base/macros.h
@@ -0,0 +1,52 @@
+// 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 macros and macro-like constructs (e.g., templates) that
+// are commonly used throughout Chromium source. (It may also contain things
+// that are closely related to things that are commonly used that belong in this
+// file.)
+
+#ifndef BASE_MACROS_H_
+#define BASE_MACROS_H_
+
+// Distinguish mips32.
+#if defined(__mips__) && (_MIPS_SIM == _ABIO32) && !defined(__mips32__)
+#define __mips32__
+#endif
+
+// Distinguish mips64.
+#if defined(__mips__) && (_MIPS_SIM == _ABI64) && !defined(__mips64__)
+#define __mips64__
+#endif
+
+// Put this in the declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete
+
+// Put this in the declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete
+
+// Put this in the declarations for a class to be uncopyable and unassignable.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ DISALLOW_COPY(TypeName); \
+ DISALLOW_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+// This is especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+// std::unique_ptr<MyType> my_var = ...;
+// if (TakeOwnership(my_var.get()) == SUCCESS)
+// ignore_result(my_var.release());
+//
+template <typename T>
+inline void ignore_result(const T&) {}
+
+#endif // BASE_MACROS_H_
diff --git a/gn/src/base/md5.cc b/gn/src/base/md5.cc
new file mode 100644
index 00000000000..4fcab552f5b
--- /dev/null
+++ b/gn/src/base/md5.cc
@@ -0,0 +1,301 @@
+// 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.
+
+// The original file was copied from sqlite, and was in the public domain.
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "base/md5.h"
+
+#include <stddef.h>
+#include <string.h>
+
+namespace {
+
+struct Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ uint8_t in[64];
+};
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(uint8_t* buf, unsigned longs) {
+ do {
+ uint32_t temp =
+ static_cast<uint32_t>(static_cast<unsigned>(buf[3]) << 8 | buf[2])
+ << 16 |
+ (static_cast<unsigned>(buf[1]) << 8 | buf[0]);
+ *reinterpret_cast<uint32_t*>(buf) = temp;
+ buf += 4;
+ } while (--longs);
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], const uint32_t in[16]) {
+ uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+} // namespace
+
+namespace base {
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5Context* context) {
+ struct Context* ctx = reinterpret_cast<struct Context*>(context);
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5Context* context, const std::string_view& data) {
+ struct Context* ctx = reinterpret_cast<struct Context*>(context);
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(data.data());
+ size_t len = data.size();
+
+ /* Update bitcount */
+
+ uint32_t t = ctx->bits[0];
+ if ((ctx->bits[0] = t + (static_cast<uint32_t>(len) << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += static_cast<uint32_t>(len >> 29);
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ uint8_t* p = static_cast<uint8_t*>(ctx->in + t);
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(MD5Digest* digest, MD5Context* context) {
+ struct Context* ctx = reinterpret_cast<struct Context*>(context);
+ unsigned count;
+ uint8_t* p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], &ctx->bits[0],
+ sizeof(ctx->bits[0]));
+ memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], &ctx->bits[1],
+ sizeof(ctx->bits[1]));
+
+ MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in));
+ byteReverse(reinterpret_cast<uint8_t*>(ctx->buf), 4);
+ memcpy(digest->a, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) {
+ /* MD5Final mutates the MD5Context*. Make a copy for generating the
+ intermediate value. */
+ MD5Context context_copy;
+ memcpy(&context_copy, context, sizeof(context_copy));
+ MD5Final(digest, &context_copy);
+}
+
+std::string MD5DigestToBase16(const MD5Digest& digest) {
+ static char const zEncode[] = "0123456789abcdef";
+
+ std::string ret;
+ ret.resize(32);
+
+ for (int i = 0, j = 0; i < 16; i++, j += 2) {
+ uint8_t a = digest.a[i];
+ ret[j] = zEncode[(a >> 4) & 0xf];
+ ret[j + 1] = zEncode[a & 0xf];
+ }
+ return ret;
+}
+
+void MD5Sum(const void* data, size_t length, MD5Digest* digest) {
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx,
+ std::string_view(reinterpret_cast<const char*>(data), length));
+ MD5Final(digest, &ctx);
+}
+
+std::string MD5String(const std::string_view& str) {
+ MD5Digest digest;
+ MD5Sum(str.data(), str.length(), &digest);
+ return MD5DigestToBase16(digest);
+}
+
+} // namespace base
diff --git a/gn/src/base/md5.h b/gn/src/base/md5.h
new file mode 100644
index 00000000000..d25b2fcd693
--- /dev/null
+++ b/gn/src/base/md5.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef BASE_MD5_H_
+#define BASE_MD5_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <string_view>
+
+namespace base {
+
+// MD5 stands for Message Digest algorithm 5.
+// MD5 is a robust hash function, designed for cyptography, but often used
+// for file checksums. The code is complex and slow, but has few
+// collisions.
+// See Also:
+// http://en.wikipedia.org/wiki/MD5
+
+// These functions perform MD5 operations. The simplest call is MD5Sum() to
+// generate the MD5 sum of the given data.
+//
+// You can also compute the MD5 sum of data incrementally by making multiple
+// calls to MD5Update():
+// MD5Context ctx; // intermediate MD5 data: do not use
+// MD5Init(&ctx);
+// MD5Update(&ctx, data1, length1);
+// MD5Update(&ctx, data2, length2);
+// ...
+//
+// MD5Digest digest; // the result of the computation
+// MD5Final(&digest, &ctx);
+//
+// You can call MD5DigestToBase16() to generate a string of the digest.
+
+// The output of an MD5 operation.
+struct MD5Digest {
+ uint8_t a[16];
+};
+
+// Used for storing intermediate data during an MD5 computation. Callers
+// should not access the data.
+typedef char MD5Context[88];
+
+// Initializes the given MD5 context structure for subsequent calls to
+// MD5Update().
+void MD5Init(MD5Context* context);
+
+// For the given buffer of |data| as a std::string_view, updates the given MD5
+// context with the sum of the data. You can call this any number of times
+// during the computation, except that MD5Init() must have been called first.
+void MD5Update(MD5Context* context, const std::string_view& data);
+
+// Finalizes the MD5 operation and fills the buffer with the digest.
+void MD5Final(MD5Digest* digest, MD5Context* context);
+
+// MD5IntermediateFinal() generates a digest without finalizing the MD5
+// operation. Can be used to generate digests for the input seen thus far,
+// without affecting the digest generated for the entire input.
+void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context);
+
+// Converts a digest into human-readable hexadecimal.
+std::string MD5DigestToBase16(const MD5Digest& digest);
+
+// Computes the MD5 sum of the given data buffer with the given length.
+// The given 'digest' structure will be filled with the result data.
+void MD5Sum(const void* data, size_t length, MD5Digest* digest);
+
+// Returns the MD5 (in hexadecimal) of a string.
+std::string MD5String(const std::string_view& str);
+
+} // namespace base
+
+#endif // BASE_MD5_H_
diff --git a/gn/base/memory/free_deleter.h b/gn/src/base/memory/free_deleter.h
index e12795c7a88..e12795c7a88 100644
--- a/gn/base/memory/free_deleter.h
+++ b/gn/src/base/memory/free_deleter.h
diff --git a/gn/base/memory/ptr_util.h b/gn/src/base/memory/ptr_util.h
index 42f4f49eebd..42f4f49eebd 100644
--- a/gn/base/memory/ptr_util.h
+++ b/gn/src/base/memory/ptr_util.h
diff --git a/gn/src/base/memory/raw_scoped_refptr_mismatch_checker.h b/gn/src/base/memory/raw_scoped_refptr_mismatch_checker.h
new file mode 100644
index 00000000000..4309849174d
--- /dev/null
+++ b/gn/src/base/memory/raw_scoped_refptr_mismatch_checker.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+
+#include <type_traits>
+
+#include "base/template_util.h"
+
+// It is dangerous to post a task with a T* argument where T is a subtype of
+// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the
+// object may already have been deleted since it was not held with a
+// scoped_refptr. Example: http://crbug.com/27191
+// The following set of traits are designed to generate a compile error
+// whenever this antipattern is attempted.
+
+namespace base {
+
+// This is a base internal implementation file used by task.h and callback.h.
+// Not for public consumption, so we wrap it in namespace internal.
+namespace internal {
+
+template <typename T, typename = void>
+struct IsRefCountedType : std::false_type {};
+
+template <typename T>
+struct IsRefCountedType<T,
+ std::void_t<decltype(std::declval<T*>()->AddRef()),
+ decltype(std::declval<T*>()->Release())>>
+ : std::true_type {};
+
+template <typename T>
+struct NeedsScopedRefptrButGetsRawPtr {
+ static_assert(!std::is_reference<T>::value,
+ "NeedsScopedRefptrButGetsRawPtr requires non-reference type.");
+
+ enum {
+ // Human readable translation: you needed to be a scoped_refptr if you are a
+ // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase)
+ // type.
+ value = std::is_pointer<T>::value &&
+ IsRefCountedType<std::remove_pointer_t<T>>::value
+ };
+};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
diff --git a/gn/base/memory/ref_counted.cc b/gn/src/base/memory/ref_counted.cc
index 79ab8535a5e..79ab8535a5e 100644
--- a/gn/base/memory/ref_counted.cc
+++ b/gn/src/base/memory/ref_counted.cc
diff --git a/gn/base/memory/ref_counted.h b/gn/src/base/memory/ref_counted.h
index 8ebdcd68b5b..8ebdcd68b5b 100644
--- a/gn/base/memory/ref_counted.h
+++ b/gn/src/base/memory/ref_counted.h
diff --git a/gn/base/memory/scoped_policy.h b/gn/src/base/memory/scoped_policy.h
index 5dbf2048d64..5dbf2048d64 100644
--- a/gn/base/memory/scoped_policy.h
+++ b/gn/src/base/memory/scoped_policy.h
diff --git a/gn/base/memory/scoped_refptr.h b/gn/src/base/memory/scoped_refptr.h
index a2576170bf9..a2576170bf9 100644
--- a/gn/base/memory/scoped_refptr.h
+++ b/gn/src/base/memory/scoped_refptr.h
diff --git a/gn/base/memory/weak_ptr.cc b/gn/src/base/memory/weak_ptr.cc
index 4abeb7f84ef..4abeb7f84ef 100644
--- a/gn/base/memory/weak_ptr.cc
+++ b/gn/src/base/memory/weak_ptr.cc
diff --git a/gn/base/memory/weak_ptr.h b/gn/src/base/memory/weak_ptr.h
index 3b2a0af6fb9..3b2a0af6fb9 100644
--- a/gn/base/memory/weak_ptr.h
+++ b/gn/src/base/memory/weak_ptr.h
diff --git a/gn/base/numerics/checked_math.h b/gn/src/base/numerics/checked_math.h
index 433860c58ba..433860c58ba 100644
--- a/gn/base/numerics/checked_math.h
+++ b/gn/src/base/numerics/checked_math.h
diff --git a/gn/base/numerics/checked_math_impl.h b/gn/src/base/numerics/checked_math_impl.h
index e083389ebf3..e083389ebf3 100644
--- a/gn/base/numerics/checked_math_impl.h
+++ b/gn/src/base/numerics/checked_math_impl.h
diff --git a/gn/base/numerics/clamped_math.h b/gn/src/base/numerics/clamped_math.h
index 9e8354353a6..9e8354353a6 100644
--- a/gn/base/numerics/clamped_math.h
+++ b/gn/src/base/numerics/clamped_math.h
diff --git a/gn/base/numerics/clamped_math_impl.h b/gn/src/base/numerics/clamped_math_impl.h
index 303a7e945a1..303a7e945a1 100644
--- a/gn/base/numerics/clamped_math_impl.h
+++ b/gn/src/base/numerics/clamped_math_impl.h
diff --git a/gn/base/numerics/math_constants.h b/gn/src/base/numerics/math_constants.h
index 9a5b8efdbcc..9a5b8efdbcc 100644
--- a/gn/base/numerics/math_constants.h
+++ b/gn/src/base/numerics/math_constants.h
diff --git a/gn/base/numerics/ranges.h b/gn/src/base/numerics/ranges.h
index f19320cedda..f19320cedda 100644
--- a/gn/base/numerics/ranges.h
+++ b/gn/src/base/numerics/ranges.h
diff --git a/gn/base/numerics/safe_conversions.h b/gn/src/base/numerics/safe_conversions.h
index 71d6e6113f5..71d6e6113f5 100644
--- a/gn/base/numerics/safe_conversions.h
+++ b/gn/src/base/numerics/safe_conversions.h
diff --git a/gn/base/numerics/safe_conversions_impl.h b/gn/src/base/numerics/safe_conversions_impl.h
index 8ae6a547316..8ae6a547316 100644
--- a/gn/base/numerics/safe_conversions_impl.h
+++ b/gn/src/base/numerics/safe_conversions_impl.h
diff --git a/gn/base/numerics/safe_math.h b/gn/src/base/numerics/safe_math.h
index e30be901f99..e30be901f99 100644
--- a/gn/base/numerics/safe_math.h
+++ b/gn/src/base/numerics/safe_math.h
diff --git a/gn/base/numerics/safe_math_clang_gcc_impl.h b/gn/src/base/numerics/safe_math_clang_gcc_impl.h
index 660a57f2e7f..660a57f2e7f 100644
--- a/gn/base/numerics/safe_math_clang_gcc_impl.h
+++ b/gn/src/base/numerics/safe_math_clang_gcc_impl.h
diff --git a/gn/src/base/numerics/safe_math_shared_impl.h b/gn/src/base/numerics/safe_math_shared_impl.h
new file mode 100644
index 00000000000..8518b45928e
--- /dev/null
+++ b/gn/src/base/numerics/safe_math_shared_impl.h
@@ -0,0 +1,236 @@
+// Copyright 2017 The Chromium 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 BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
+#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+
+// Where available use builtin math overflow support on Clang and GCC.
+#if ((defined(__clang__) && \
+ ((__clang_major__ > 3) || \
+ (__clang_major__ == 3 && __clang_minor__ >= 4))) || \
+ (defined(__GNUC__) && __GNUC__ >= 5))
+#include "base/numerics/safe_math_clang_gcc_impl.h"
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
+#else
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
+#endif
+
+namespace base {
+namespace internal {
+
+// These are the non-functioning boilerplate implementations of the optimized
+// safe math routines.
+#if !BASE_HAS_OPTIMIZED_SAFE_MATH
+template <typename T, typename U>
+struct CheckedAddFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct CheckedSubFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct CheckedMulFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T>
+struct ClampedNegFastOp {
+ static const bool is_supported = false;
+ static constexpr T Do(T) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<T>();
+ }
+};
+#endif // BASE_HAS_OPTIMIZED_SAFE_MATH
+#undef BASE_HAS_OPTIMIZED_SAFE_MATH
+
+// This is used for UnsignedAbs, where we need to support floating-point
+// template instantiations even though we don't actually support the operations.
+// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
+// so the float versions will not compile.
+template <typename Numeric,
+ bool IsInteger = std::is_integral<Numeric>::value,
+ bool IsFloat = std::is_floating_point<Numeric>::value>
+struct UnsignedOrFloatForSize;
+
+template <typename Numeric>
+struct UnsignedOrFloatForSize<Numeric, true, false> {
+ using type = typename std::make_unsigned<Numeric>::type;
+};
+
+template <typename Numeric>
+struct UnsignedOrFloatForSize<Numeric, false, true> {
+ using type = Numeric;
+};
+
+// Wrap the unary operations to allow SFINAE when instantiating integrals versus
+// floating points. These don't perform any overflow checking. Rather, they
+// exhibit well-defined overflow semantics and rely on the caller to detect
+// if an overflow occured.
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ // This will compile to a NEG on Intel, and is normal negation on ARM.
+ return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+ return -value;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
+ return ~value;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+ return static_cast<T>(SafeUnsignedAbs(value));
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+ return value < 0 ? -value : value;
+}
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+struct MathWrapper {
+ using math = M<typename UnderlyingType<L>::type,
+ typename UnderlyingType<R>::type,
+ void>;
+ using type = typename math::result_type;
+};
+
+// These variadic templates work out the return types.
+// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+struct ResultType;
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+struct ResultType<M, L, R> {
+ using type = typename MathWrapper<M, L, R>::type;
+};
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+struct ResultType {
+ using type =
+ typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
+};
+
+// The following macros are just boilerplate for the standard arithmetic
+// operator overloads and variadic function templates. A macro isn't the nicest
+// solution, but it beats rewriting these over and over again.
+#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
+ template <typename L, typename R, typename... Args> \
+ constexpr CLASS##Numeric< \
+ typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
+ CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
+ return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
+ args...); \
+ }
+
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
+ /* Binary arithmetic operator for all CLASS##Numeric operations. */ \
+ template <typename L, typename R, \
+ typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
+ nullptr> \
+ constexpr CLASS##Numeric< \
+ typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
+ operator OP(const L lhs, const R rhs) { \
+ return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
+ rhs); \
+ } \
+ /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
+ template <typename L> \
+ template <typename R> \
+ constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \
+ const R rhs) { \
+ return MathOp<CLASS##OP_NAME##Op>(rhs); \
+ } \
+ /* Variadic arithmetic functions that return CLASS##Numeric. */ \
+ BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
diff --git a/gn/base/posix/eintr_wrapper.h b/gn/src/base/posix/eintr_wrapper.h
index e18372e8684..e18372e8684 100644
--- a/gn/base/posix/eintr_wrapper.h
+++ b/gn/src/base/posix/eintr_wrapper.h
diff --git a/gn/base/posix/file_descriptor_shuffle.cc b/gn/src/base/posix/file_descriptor_shuffle.cc
index f628bec411c..f628bec411c 100644
--- a/gn/base/posix/file_descriptor_shuffle.cc
+++ b/gn/src/base/posix/file_descriptor_shuffle.cc
diff --git a/gn/base/posix/file_descriptor_shuffle.h b/gn/src/base/posix/file_descriptor_shuffle.h
index 68434988991..68434988991 100644
--- a/gn/base/posix/file_descriptor_shuffle.h
+++ b/gn/src/base/posix/file_descriptor_shuffle.h
diff --git a/gn/src/base/posix/safe_strerror.cc b/gn/src/base/posix/safe_strerror.cc
new file mode 100644
index 00000000000..252b5df2d00
--- /dev/null
+++ b/gn/src/base/posix/safe_strerror.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2006-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.
+
+#if defined(__ANDROID__)
+// Post-L versions of bionic define the GNU-specific strerror_r if _GNU_SOURCE
+// is defined, but the symbol is renamed to __gnu_strerror_r which only exists
+// on those later versions. To preserve ABI compatibility with older versions,
+// undefine _GNU_SOURCE and use the POSIX version.
+#undef _GNU_SOURCE
+#endif
+
+#include "base/posix/safe_strerror.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "util/build_config.h"
+
+namespace base {
+
+#if defined(__GLIBC__)
+#define USE_HISTORICAL_STRERRO_R 1
+#else
+#define USE_HISTORICAL_STRERRO_R 0
+#endif
+
+#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
+// GCC will complain about the unused second wrap function unless we tell it
+// that we meant for them to be potentially unused, which is exactly what this
+// attribute is for.
+#define POSSIBLY_UNUSED __attribute__((unused))
+#else
+#define POSSIBLY_UNUSED
+#endif
+
+#if USE_HISTORICAL_STRERRO_R
+// glibc has two strerror_r functions: a historical GNU-specific one that
+// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
+// that returns int. This wraps the GNU-specific one.
+static void POSSIBLY_UNUSED
+wrap_posix_strerror_r(char* (*strerror_r_ptr)(int, char*, size_t),
+ int err,
+ char* buf,
+ size_t len) {
+ // GNU version.
+ char* rc = (*strerror_r_ptr)(err, buf, len);
+ if (rc != buf) {
+ // glibc did not use buf and returned a static string instead. Copy it
+ // into buf.
+ buf[0] = '\0';
+ strncat(buf, rc, len - 1);
+ }
+ // The GNU version never fails. Unknown errors get an "unknown error" message.
+ // The result is always null terminated.
+}
+#endif // USE_HISTORICAL_STRERRO_R
+
+// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
+// does not define the behaviour for some of the edge cases, so we wrap it to
+// guarantee that they are handled. This is compiled on all POSIX platforms, but
+// it will only be used on Linux if the POSIX strerror_r implementation is
+// being used (see below).
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(int (*strerror_r_ptr)(int,
+ char*,
+ size_t),
+ int err,
+ char* buf,
+ size_t len) {
+ int old_errno = errno;
+ // Have to cast since otherwise we get an error if this is the GNU version
+ // (but in such a scenario this function is never called). Sadly we can't use
+ // C++-style casts because the appropriate one is reinterpret_cast but it's
+ // considered illegal to reinterpret_cast a type to itself, so we get an
+ // error in the opposite case.
+ int result = (*strerror_r_ptr)(err, buf, len);
+ if (result == 0) {
+ // POSIX is vague about whether the string will be terminated, although
+ // it indirectly implies that typically ERANGE will be returned, instead
+ // of truncating the string. We play it safe by always terminating the
+ // string explicitly.
+ buf[len - 1] = '\0';
+ } else {
+ // Error. POSIX is vague about whether the return value is itself a system
+ // error code or something else. On Linux currently it is -1 and errno is
+ // set. On BSD-derived systems it is a system error and errno is unchanged.
+ // We try and detect which case it is so as to put as much useful info as
+ // we can into our message.
+ int strerror_error; // The error encountered in strerror
+ int new_errno = errno;
+ if (new_errno != old_errno) {
+ // errno was changed, so probably the return value is just -1 or something
+ // else that doesn't provide any info, and errno is the error.
+ strerror_error = new_errno;
+ } else {
+ // Either the error from strerror_r was the same as the previous value, or
+ // errno wasn't used. Assume the latter.
+ strerror_error = result;
+ }
+ // snprintf truncates and always null-terminates.
+ snprintf(buf, len, "Error %d while retrieving error %d", strerror_error,
+ err);
+ }
+ errno = old_errno;
+}
+
+void safe_strerror_r(int err, char* buf, size_t len) {
+ if (buf == nullptr || len <= 0) {
+ return;
+ }
+ // If using glibc (i.e., Linux), the compiler will automatically select the
+ // appropriate overloaded function based on the function type of strerror_r.
+ // The other one will be elided from the translation unit since both are
+ // static.
+ wrap_posix_strerror_r(&strerror_r, err, buf, len);
+}
+
+std::string safe_strerror(int err) {
+ const int buffer_size = 256;
+ char buf[buffer_size];
+ safe_strerror_r(err, buf, sizeof(buf));
+ return std::string(buf);
+}
+
+} // namespace base
diff --git a/gn/base/posix/safe_strerror.h b/gn/src/base/posix/safe_strerror.h
index 48e9db97ec0..48e9db97ec0 100644
--- a/gn/base/posix/safe_strerror.h
+++ b/gn/src/base/posix/safe_strerror.h
diff --git a/gn/base/scoped_clear_errno.h b/gn/src/base/scoped_clear_errno.h
index 44a0c6274ba..44a0c6274ba 100644
--- a/gn/base/scoped_clear_errno.h
+++ b/gn/src/base/scoped_clear_errno.h
diff --git a/gn/base/scoped_generic.h b/gn/src/base/scoped_generic.h
index 6a56f645fdc..6a56f645fdc 100644
--- a/gn/base/scoped_generic.h
+++ b/gn/src/base/scoped_generic.h
diff --git a/gn/base/sha1.cc b/gn/src/base/sha1.cc
index 9c22be1983a..9c22be1983a 100644
--- a/gn/base/sha1.cc
+++ b/gn/src/base/sha1.cc
diff --git a/gn/base/sha1.h b/gn/src/base/sha1.h
index 482ebe60fac..482ebe60fac 100644
--- a/gn/base/sha1.h
+++ b/gn/src/base/sha1.h
diff --git a/gn/src/base/stl_util.h b/gn/src/base/stl_util.h
new file mode 100644
index 00000000000..d08c018a0c2
--- /dev/null
+++ b/gn/src/base/stl_util.h
@@ -0,0 +1,272 @@
+// 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.
+
+// Derived from google3/util/gtl/stl_util.h
+
+#ifndef BASE_STL_UTIL_H_
+#define BASE_STL_UTIL_H_
+
+#include <algorithm>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+// Calls erase on iterators of matching elements.
+template <typename Container, typename Predicate>
+void IterateAndEraseIf(Container& container, Predicate pred) {
+ for (auto it = container.begin(); it != container.end();) {
+ if (pred(*it))
+ it = container.erase(it);
+ else
+ ++it;
+ }
+}
+
+} // namespace internal
+
+// Test to see if a set or map contains a particular key.
+// Returns true if the key is in the collection.
+template <typename Collection, typename Key>
+bool ContainsKey(const Collection& collection, const Key& key) {
+ return collection.find(key) != collection.end();
+}
+
+namespace internal {
+
+template <typename Collection>
+class HasKeyType {
+ template <typename C>
+ static std::true_type test(typename C::key_type*);
+ template <typename C>
+ static std::false_type test(...);
+
+ public:
+ static constexpr bool value = decltype(test<Collection>(nullptr))::value;
+};
+
+} // namespace internal
+
+// Test to see if a collection like a vector contains a particular value.
+// Returns true if the value is in the collection.
+// Don't use this on collections such as sets or maps. This is enforced by
+// disabling this method if the collection defines a key_type.
+template <typename Collection,
+ typename Value,
+ typename std::enable_if<!internal::HasKeyType<Collection>::value,
+ int>::type = 0>
+bool ContainsValue(const Collection& collection, const Value& value) {
+ return std::find(std::begin(collection), std::end(collection), value) !=
+ std::end(collection);
+}
+
+// Returns true if the container is sorted.
+template <typename Container>
+bool STLIsSorted(const Container& cont) {
+ // Note: Use reverse iterator on container to ensure we only require
+ // value_type to implement operator<.
+ return std::adjacent_find(cont.rbegin(), cont.rend(),
+ std::less<typename Container::value_type>()) ==
+ cont.rend();
+}
+
+// Returns a new ResultType containing the difference of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType difference;
+ std::set_difference(a1.begin(), a1.end(), a2.begin(), a2.end(),
+ std::inserter(difference, difference.end()));
+ return difference;
+}
+
+// Returns a new ResultType containing the union of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType result;
+ std::set_union(a1.begin(), a1.end(), a2.begin(), a2.end(),
+ std::inserter(result, result.end()));
+ return result;
+}
+
+// Returns a new ResultType containing the intersection of two sorted
+// containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType result;
+ std::set_intersection(a1.begin(), a1.end(), a2.begin(), a2.end(),
+ std::inserter(result, result.end()));
+ return result;
+}
+
+// Returns true if the sorted container |a1| contains all elements of the sorted
+// container |a2|.
+template <typename Arg1, typename Arg2>
+bool STLIncludes(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ return std::includes(a1.begin(), a1.end(), a2.begin(), a2.end());
+}
+
+// Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
+// http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
+// They provide a generic way to erase elements from a container.
+// The functions here implement these for the standard containers until those
+// functions are available in the C++ standard.
+// For Chromium containers overloads should be defined in their own headers
+// (like standard containers).
+// Note: there is no std::erase for standard associative containers so we don't
+// have it either.
+
+template <typename CharT, typename Traits, typename Allocator, typename Value>
+void Erase(std::basic_string<CharT, Traits, Allocator>& container,
+ const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <typename CharT, typename Traits, typename Allocator, class Predicate>
+void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
+ Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::deque<T, Allocator>& container, const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::vector<T, Allocator>& container, const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
+ // Unlike std::forward_list::remove, this function template accepts
+ // heterogeneous types and does not force a conversion to the container's
+ // value type before invoking the == operator.
+ container.remove_if([&](const T& cur) { return cur == value; });
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
+ container.remove_if(pred);
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::list<T, Allocator>& container, const Value& value) {
+ // Unlike std::list::remove, this function template accepts heterogeneous
+ // types and does not force a conversion to the container's value type before
+ // invoking the == operator.
+ container.remove_if([&](const T& cur) { return cur == value; });
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
+ container.remove_if(pred);
+}
+
+template <class Key, class T, class Compare, class Allocator, class Predicate>
+void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class T, class Compare, class Allocator, class Predicate>
+void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class Compare, class Allocator, class Predicate>
+void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class Compare, class Allocator, class Predicate>
+void EraseIf(std::multiset<Key, Compare, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class T,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class T,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(
+ std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+} // namespace base
+
+#endif // BASE_STL_UTIL_H_
diff --git a/gn/src/base/strings/string_number_conversions.cc b/gn/src/base/strings/string_number_conversions.cc
new file mode 100644
index 00000000000..9156fa3df14
--- /dev/null
+++ b/gn/src/base/strings/string_number_conversions.cc
@@ -0,0 +1,458 @@
+// 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 "base/strings/string_number_conversions.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <wctype.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/scoped_clear_errno.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace base {
+
+namespace {
+
+template <typename STR, typename INT>
+struct IntToStringT {
+ static STR IntToString(INT value) {
+ // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
+ // So round up to allocate 3 output characters per byte, plus 1 for '-'.
+ const size_t kOutputBufSize =
+ 3 * sizeof(INT) + std::numeric_limits<INT>::is_signed;
+
+ // Create the string in a temporary buffer, write it back to front, and
+ // then return the substr of what we ended up using.
+ using CHR = typename STR::value_type;
+ CHR outbuf[kOutputBufSize];
+
+ // The ValueOrDie call below can never fail, because UnsignedAbs is valid
+ // for all valid inputs.
+ typename std::make_unsigned<INT>::type res =
+ CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
+
+ CHR* end = outbuf + kOutputBufSize;
+ CHR* i = end;
+ do {
+ --i;
+ DCHECK(i != outbuf);
+ *i = static_cast<CHR>((res % 10) + '0');
+ res /= 10;
+ } while (res != 0);
+ if (IsValueNegative(value)) {
+ --i;
+ DCHECK(i != outbuf);
+ *i = static_cast<CHR>('-');
+ }
+ return STR(i, end);
+ }
+};
+
+// Utility to convert a character to a digit in a given base
+template <typename CHAR, int BASE, bool BASE_LTE_10>
+class BaseCharToDigit {};
+
+// Faster specialization for bases <= 10
+template <typename CHAR, int BASE>
+class BaseCharToDigit<CHAR, BASE, true> {
+ public:
+ static bool Convert(CHAR c, uint8_t* digit) {
+ if (c >= '0' && c < '0' + BASE) {
+ *digit = static_cast<uint8_t>(c - '0');
+ return true;
+ }
+ return false;
+ }
+};
+
+// Specialization for bases where 10 < base <= 36
+template <typename CHAR, int BASE>
+class BaseCharToDigit<CHAR, BASE, false> {
+ public:
+ static bool Convert(CHAR c, uint8_t* digit) {
+ if (c >= '0' && c <= '9') {
+ *digit = c - '0';
+ } else if (c >= 'a' && c < 'a' + BASE - 10) {
+ *digit = c - 'a' + 10;
+ } else if (c >= 'A' && c < 'A' + BASE - 10) {
+ *digit = c - 'A' + 10;
+ } else {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <int BASE, typename CHAR>
+bool CharToDigit(CHAR c, uint8_t* digit) {
+ return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
+}
+
+// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it
+// is locale independent, whereas the functions we are replacing were
+// locale-dependent. TBD what is desired, but for the moment let's not
+// introduce a change in behaviour.
+template <typename CHAR>
+class WhitespaceHelper {};
+
+template <>
+class WhitespaceHelper<char> {
+ public:
+ static bool Invoke(char c) {
+ return 0 != isspace(static_cast<unsigned char>(c));
+ }
+};
+
+template <>
+class WhitespaceHelper<char16_t> {
+ public:
+ static bool Invoke(char16_t c) { return 0 != iswspace(c); }
+};
+
+template <typename CHAR>
+bool LocalIsWhitespace(CHAR c) {
+ return WhitespaceHelper<CHAR>::Invoke(c);
+}
+
+// IteratorRangeToNumberTraits should provide:
+// - a typedef for iterator_type, the iterator type used as input.
+// - a typedef for value_type, the target numeric type.
+// - static functions min, max (returning the minimum and maximum permitted
+// values)
+// - constant kBase, the base in which to interpret the input
+template <typename IteratorRangeToNumberTraits>
+class IteratorRangeToNumber {
+ public:
+ typedef IteratorRangeToNumberTraits traits;
+ typedef typename traits::iterator_type const_iterator;
+ typedef typename traits::value_type value_type;
+
+ // Generalized iterator-range-to-number conversion.
+ //
+ static bool Invoke(const_iterator begin,
+ const_iterator end,
+ value_type* output) {
+ bool valid = true;
+
+ while (begin != end && LocalIsWhitespace(*begin)) {
+ valid = false;
+ ++begin;
+ }
+
+ if (begin != end && *begin == '-') {
+ if (!std::numeric_limits<value_type>::is_signed) {
+ *output = 0;
+ valid = false;
+ } else if (!Negative::Invoke(begin + 1, end, output)) {
+ valid = false;
+ }
+ } else {
+ if (begin != end && *begin == '+') {
+ ++begin;
+ }
+ if (!Positive::Invoke(begin, end, output)) {
+ valid = false;
+ }
+ }
+
+ return valid;
+ }
+
+ private:
+ // Sign provides:
+ // - a static function, CheckBounds, that determines whether the next digit
+ // causes an overflow/underflow
+ // - a static function, Increment, that appends the next digit appropriately
+ // according to the sign of the number being parsed.
+ template <typename Sign>
+ class Base {
+ public:
+ static bool Invoke(const_iterator begin,
+ const_iterator end,
+ typename traits::value_type* output) {
+ *output = 0;
+
+ if (begin == end) {
+ return false;
+ }
+
+ // Note: no performance difference was found when using template
+ // specialization to remove this check in bases other than 16
+ if (traits::kBase == 16 && end - begin > 2 && *begin == '0' &&
+ (*(begin + 1) == 'x' || *(begin + 1) == 'X')) {
+ begin += 2;
+ }
+
+ for (const_iterator current = begin; current != end; ++current) {
+ uint8_t new_digit = 0;
+
+ if (!CharToDigit<traits::kBase>(*current, &new_digit)) {
+ return false;
+ }
+
+ if (current != begin) {
+ if (!Sign::CheckBounds(output, new_digit)) {
+ return false;
+ }
+ *output *= traits::kBase;
+ }
+
+ Sign::Increment(new_digit, output);
+ }
+ return true;
+ }
+ };
+
+ class Positive : public Base<Positive> {
+ public:
+ static bool CheckBounds(value_type* output, uint8_t new_digit) {
+ if (*output > static_cast<value_type>(traits::max() / traits::kBase) ||
+ (*output == static_cast<value_type>(traits::max() / traits::kBase) &&
+ new_digit > traits::max() % traits::kBase)) {
+ *output = traits::max();
+ return false;
+ }
+ return true;
+ }
+ static void Increment(uint8_t increment, value_type* output) {
+ *output += increment;
+ }
+ };
+
+ class Negative : public Base<Negative> {
+ public:
+ static bool CheckBounds(value_type* output, uint8_t new_digit) {
+ if (*output < traits::min() / traits::kBase ||
+ (*output == traits::min() / traits::kBase &&
+ new_digit > 0 - traits::min() % traits::kBase)) {
+ *output = traits::min();
+ return false;
+ }
+ return true;
+ }
+ static void Increment(uint8_t increment, value_type* output) {
+ *output -= increment;
+ }
+ };
+};
+
+template <typename ITERATOR, typename VALUE, int BASE>
+class BaseIteratorRangeToNumberTraits {
+ public:
+ typedef ITERATOR iterator_type;
+ typedef VALUE value_type;
+ static value_type min() { return std::numeric_limits<value_type>::min(); }
+ static value_type max() { return std::numeric_limits<value_type>::max(); }
+ static const int kBase = BASE;
+};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, int, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, uint32_t, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToInt64Traits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, int64_t, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUInt64Traits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, uint64_t, 16> {};
+
+typedef BaseHexIteratorRangeToIntTraits<std::string_view::const_iterator>
+ HexIteratorRangeToIntTraits;
+
+typedef BaseHexIteratorRangeToUIntTraits<std::string_view::const_iterator>
+ HexIteratorRangeToUIntTraits;
+
+typedef BaseHexIteratorRangeToInt64Traits<std::string_view::const_iterator>
+ HexIteratorRangeToInt64Traits;
+
+typedef BaseHexIteratorRangeToUInt64Traits<std::string_view::const_iterator>
+ HexIteratorRangeToUInt64Traits;
+
+template <typename VALUE, int BASE>
+class StringPieceToNumberTraits
+ : public BaseIteratorRangeToNumberTraits<std::string_view::const_iterator,
+ VALUE,
+ BASE> {};
+
+template <typename VALUE>
+bool StringToIntImpl(std::string_view input, VALUE* output) {
+ return IteratorRangeToNumber<StringPieceToNumberTraits<VALUE, 10>>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+template <typename VALUE, int BASE>
+class StringPiece16ToNumberTraits : public BaseIteratorRangeToNumberTraits<
+ std::u16string_view::const_iterator,
+ VALUE,
+ BASE> {};
+
+template <typename VALUE>
+bool String16ToIntImpl(std::u16string_view input, VALUE* output) {
+ return IteratorRangeToNumber<StringPiece16ToNumberTraits<VALUE, 10>>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+} // namespace
+
+std::string NumberToString(int value) {
+ return IntToStringT<std::string, int>::IntToString(value);
+}
+
+std::u16string NumberToString16(int value) {
+ return IntToStringT<std::u16string, int>::IntToString(value);
+}
+
+std::string NumberToString(unsigned value) {
+ return IntToStringT<std::string, unsigned>::IntToString(value);
+}
+
+std::u16string NumberToString16(unsigned value) {
+ return IntToStringT<std::u16string, unsigned>::IntToString(value);
+}
+
+std::string NumberToString(long value) {
+ return IntToStringT<std::string, long>::IntToString(value);
+}
+
+std::u16string NumberToString16(long value) {
+ return IntToStringT<std::u16string, long>::IntToString(value);
+}
+
+std::string NumberToString(unsigned long value) {
+ return IntToStringT<std::string, unsigned long>::IntToString(value);
+}
+
+std::u16string NumberToString16(unsigned long value) {
+ return IntToStringT<std::u16string, unsigned long>::IntToString(value);
+}
+
+std::string NumberToString(long long value) {
+ return IntToStringT<std::string, long long>::IntToString(value);
+}
+
+std::u16string NumberToString16(long long value) {
+ return IntToStringT<std::u16string, long long>::IntToString(value);
+}
+
+std::string NumberToString(unsigned long long value) {
+ return IntToStringT<std::string, unsigned long long>::IntToString(value);
+}
+
+std::u16string NumberToString16(unsigned long long value) {
+ return IntToStringT<std::u16string, unsigned long long>::IntToString(value);
+}
+
+bool StringToInt(std::string_view input, int* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToInt(std::u16string_view input, int* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToUint(std::string_view input, unsigned* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToUint(std::u16string_view input, unsigned* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToInt64(std::string_view input, int64_t* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToInt64(std::u16string_view input, int64_t* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToUint64(std::string_view input, uint64_t* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToUint64(std::u16string_view input, uint64_t* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToSizeT(std::string_view input, size_t* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToSizeT(std::u16string_view input, size_t* output) {
+ return String16ToIntImpl(input, output);
+}
+
+// Note: if you need to add String16ToDouble, first ask yourself if it's
+// really necessary. If it is, probably the best implementation here is to
+// convert to 8-bit and then use the 8-bit version.
+
+// Note: if you need to add an iterator range version of StringToDouble, first
+// ask yourself if it's really necessary. If it is, probably the best
+// implementation here is to instantiate a string and use the string version.
+
+std::string HexEncode(const void* bytes, size_t size) {
+ static const char kHexChars[] = "0123456789ABCDEF";
+
+ // Each input byte creates two output hex characters.
+ std::string ret(size * 2, '\0');
+
+ for (size_t i = 0; i < size; ++i) {
+ char b = reinterpret_cast<const char*>(bytes)[i];
+ ret[(i * 2)] = kHexChars[(b >> 4) & 0xf];
+ ret[(i * 2) + 1] = kHexChars[b & 0xf];
+ }
+ return ret;
+}
+
+bool HexStringToInt(std::string_view input, int* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToUInt(std::string_view input, uint32_t* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToInt64(std::string_view input, int64_t* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToInt64Traits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToUInt64(std::string_view input, uint64_t* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToUInt64Traits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToBytes(std::string_view input, std::vector<uint8_t>* output) {
+ DCHECK_EQ(output->size(), 0u);
+ size_t count = input.size();
+ if (count == 0 || (count % 2) != 0)
+ return false;
+ for (uintptr_t i = 0; i < count / 2; ++i) {
+ uint8_t msb = 0; // most significant 4 bits
+ uint8_t lsb = 0; // least significant 4 bits
+ if (!CharToDigit<16>(input[i * 2], &msb) ||
+ !CharToDigit<16>(input[i * 2 + 1], &lsb)) {
+ return false;
+ }
+ output->push_back((msb << 4) | lsb);
+ }
+ return true;
+}
+
+} // namespace base
diff --git a/gn/src/base/strings/string_number_conversions.h b/gn/src/base/strings/string_number_conversions.h
new file mode 100644
index 00000000000..81360a0cbdc
--- /dev/null
+++ b/gn/src/base/strings/string_number_conversions.h
@@ -0,0 +1,131 @@
+// 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 BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
+#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "util/build_config.h"
+
+namespace base {
+
+// Number -> string conversions ------------------------------------------------
+
+// Ignores locale! see warning above.
+std::string NumberToString(int value);
+std::u16string NumberToString16(int value);
+std::string NumberToString(unsigned int value);
+std::u16string NumberToString16(unsigned int value);
+std::string NumberToString(long value);
+std::u16string NumberToString16(long value);
+std::string NumberToString(unsigned long value);
+std::u16string NumberToString16(unsigned long value);
+std::string NumberToString(long long value);
+std::u16string NumberToString16(long long value);
+std::string NumberToString(unsigned long long value);
+std::u16string NumberToString16(unsigned long long value);
+
+// Type-specific naming for backwards compatibility.
+//
+// TODO(brettw) these should be removed and callers converted to the overloaded
+// "NumberToString" variant.
+inline std::string IntToString(int value) {
+ return NumberToString(value);
+}
+inline std::u16string IntToString16(int value) {
+ return NumberToString16(value);
+}
+inline std::string UintToString(unsigned value) {
+ return NumberToString(value);
+}
+inline std::u16string UintToString16(unsigned value) {
+ return NumberToString16(value);
+}
+inline std::string Int64ToString(int64_t value) {
+ return NumberToString(value);
+}
+inline std::u16string Int64ToString16(int64_t value) {
+ return NumberToString16(value);
+}
+
+// String -> number conversions ------------------------------------------------
+
+// Perform a best-effort conversion of the input string to a numeric type,
+// setting |*output| to the result of the conversion. Returns true for
+// "perfect" conversions; returns false in the following cases:
+// - Overflow. |*output| will be set to the maximum value supported
+// by the data type.
+// - Underflow. |*output| will be set to the minimum value supported
+// by the data type.
+// - Trailing characters in the string after parsing the number. |*output|
+// will be set to the value of the number that was parsed.
+// - Leading whitespace in the string before parsing the number. |*output| will
+// be set to the value of the number that was parsed.
+// - No characters parseable as a number at the beginning of the string.
+// |*output| will be set to 0.
+// - Empty string. |*output| will be set to 0.
+// WARNING: Will write to |output| even when returning false.
+// Read the comments above carefully.
+bool StringToInt(std::string_view input, int* output);
+bool StringToInt(std::u16string_view input, int* output);
+
+bool StringToUint(std::string_view input, unsigned* output);
+bool StringToUint(std::u16string_view input, unsigned* output);
+
+bool StringToInt64(std::string_view input, int64_t* output);
+bool StringToInt64(std::u16string_view input, int64_t* output);
+
+bool StringToUint64(std::string_view input, uint64_t* output);
+bool StringToUint64(std::u16string_view input, uint64_t* output);
+
+bool StringToSizeT(std::string_view input, size_t* output);
+bool StringToSizeT(std::u16string_view input, size_t* output);
+
+// Hex encoding ----------------------------------------------------------------
+
+// Returns a hex string representation of a binary buffer. The returned hex
+// string will be in upper case. This function does not check if |size| is
+// within reasonable limits since it's written with trusted data in mind. If
+// you suspect that the data you want to format might be large, the absolute
+// max size for |size| should be is
+// std::numeric_limits<size_t>::max() / 2
+std::string HexEncode(const void* bytes, size_t size);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// -0x80000000 < |input| < 0x7FFFFFFF.
+bool HexStringToInt(std::string_view input, int* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// 0x00000000 < |input| < 0xFFFFFFFF.
+// The string is not required to start with 0x.
+bool HexStringToUInt(std::string_view input, uint32_t* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// -0x8000000000000000 < |input| < 0x7FFFFFFFFFFFFFFF.
+bool HexStringToInt64(std::string_view input, int64_t* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// 0x0000000000000000 < |input| < 0xFFFFFFFFFFFFFFFF.
+// The string is not required to start with 0x.
+bool HexStringToUInt64(std::string_view input, uint64_t* output);
+
+// Similar to the previous functions, except that output is a vector of bytes.
+// |*output| will contain as many bytes as were successfully parsed prior to the
+// error. There is no overflow, but input.size() must be evenly divisible by 2.
+// Leading 0x or +/- are not allowed.
+bool HexStringToBytes(std::string_view input, std::vector<uint8_t>* output);
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
diff --git a/gn/src/base/strings/string_split.cc b/gn/src/base/strings/string_split.cc
new file mode 100644
index 00000000000..f4c7eb22d02
--- /dev/null
+++ b/gn/src/base/strings/string_split.cc
@@ -0,0 +1,260 @@
+// 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 "base/strings/string_split.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base {
+
+namespace {
+
+// Returns either the ASCII or UTF-16 whitespace.
+template <typename char_type>
+std::basic_string_view<char_type> WhitespaceForType();
+template <>
+std::u16string_view WhitespaceForType<char16_t>() {
+ return kWhitespaceUTF16;
+}
+template <>
+std::string_view WhitespaceForType<char>() {
+ return kWhitespaceASCII;
+}
+
+// Optimize the single-character case to call find() on the string instead,
+// since this is the common case and can be made faster. This could have been
+// done with template specialization too, but would have been less clear.
+//
+// There is no corresponding FindFirstNotOf because std::string_view already
+// implements these different versions that do the optimized searching.
+size_t FindFirstOf(std::string_view piece, char c, size_t pos) {
+ return piece.find(c, pos);
+}
+size_t FindFirstOf(std::u16string_view piece, char16_t c, size_t pos) {
+ return piece.find(c, pos);
+}
+size_t FindFirstOf(std::string_view piece,
+ std::string_view one_of,
+ size_t pos) {
+ return piece.find_first_of(one_of, pos);
+}
+size_t FindFirstOf(std::u16string_view piece,
+ std::u16string_view one_of,
+ size_t pos) {
+ return piece.find_first_of(one_of, pos);
+}
+
+// General string splitter template. Can take 8- or 16-bit input, can produce
+// the corresponding string or std::string_view output, and can take single- or
+// multiple-character delimiters.
+//
+// DelimiterType is either a character (Str::value_type) or a string piece of
+// multiple characters (std::basic_string_view<char>). std::string_view has a
+// version of find for both of these cases, and the single-character version is
+// the most common and can be implemented faster, which is why this is a
+// template.
+template <typename char_type, typename OutputStringType, typename DelimiterType>
+static std::vector<OutputStringType> SplitStringT(
+ std::basic_string_view<char_type> str,
+ DelimiterType delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<OutputStringType> result;
+ if (str.empty())
+ return result;
+
+ using ViewType = std::basic_string_view<char_type>;
+
+ size_t start = 0;
+ while (start != ViewType::npos) {
+ size_t end = FindFirstOf(str, delimiter, start);
+
+ ViewType piece;
+ if (end == ViewType::npos) {
+ piece = str.substr(start);
+ start = ViewType::npos;
+ } else {
+ piece = str.substr(start, end - start);
+ start = end + 1;
+ }
+
+ if (whitespace == TRIM_WHITESPACE)
+ piece = TrimString(piece, WhitespaceForType<char_type>(), TRIM_ALL);
+
+ if (result_type == SPLIT_WANT_ALL || !piece.empty())
+ result.emplace_back(piece);
+ }
+ return result;
+}
+
+bool AppendStringKeyValue(std::string_view input,
+ char delimiter,
+ StringPairs* result) {
+ // Always append a new item regardless of success (it might be empty). The
+ // below code will copy the strings directly into the result pair.
+ result->resize(result->size() + 1);
+ auto& result_pair = result->back();
+
+ // Find the delimiter.
+ size_t end_key_pos = input.find_first_of(delimiter);
+ if (end_key_pos == std::string::npos) {
+ return false; // No delimiter.
+ }
+ result_pair.first.assign(input.substr(0, end_key_pos));
+
+ // Find the value string.
+ std::string_view remains =
+ input.substr(end_key_pos, input.size() - end_key_pos);
+ size_t begin_value_pos = remains.find_first_not_of(delimiter);
+ if (begin_value_pos == std::string_view::npos) {
+ return false; // No value.
+ }
+ result_pair.second.assign(
+ remains.substr(begin_value_pos, remains.size() - begin_value_pos));
+
+ return true;
+}
+
+template <typename char_type, typename OutputStringType>
+void SplitStringUsingSubstrT(std::basic_string_view<char_type> input,
+ std::basic_string_view<char_type> delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type,
+ std::vector<OutputStringType>* result) {
+ using Piece = std::basic_string_view<char_type>;
+ using size_type = typename Piece::size_type;
+
+ result->clear();
+ for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
+ begin_index = end_index + delimiter.size()) {
+ end_index = input.find(delimiter, begin_index);
+ Piece term = end_index == Piece::npos
+ ? input.substr(begin_index)
+ : input.substr(begin_index, end_index - begin_index);
+
+ if (whitespace == TRIM_WHITESPACE)
+ term = TrimString(term, WhitespaceForType<char_type>(), TRIM_ALL);
+
+ if (result_type == SPLIT_WANT_ALL || !term.empty())
+ result->emplace_back(term);
+ }
+}
+
+} // namespace
+
+std::vector<std::string> SplitString(std::string_view input,
+ std::string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<char, std::string, char>(input, separators[0],
+ whitespace, result_type);
+ }
+ return SplitStringT<char, std::string, std::string_view>(
+ input, separators, whitespace, result_type);
+}
+
+std::vector<std::u16string> SplitString(std::u16string_view input,
+ std::u16string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<char16_t, std::u16string, char16_t>(
+ input, separators[0], whitespace, result_type);
+ }
+ return SplitStringT<char16_t, std::u16string, std::u16string_view>(
+ input, separators, whitespace, result_type);
+}
+
+std::vector<std::string_view> SplitStringPiece(std::string_view input,
+ std::string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<char, std::string_view, char>(input, separators[0],
+ whitespace, result_type);
+ }
+ return SplitStringT<char, std::string_view, std::string_view>(
+ input, separators, whitespace, result_type);
+}
+
+std::vector<std::u16string_view> SplitStringPiece(
+ std::u16string_view input,
+ std::u16string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<char16_t, std::u16string_view, char16_t>(
+ input, separators[0], whitespace, result_type);
+ }
+ return SplitStringT<char16_t, std::u16string_view, std::u16string_view>(
+ input, separators, whitespace, result_type);
+}
+
+bool SplitStringIntoKeyValuePairs(std::string_view input,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs) {
+ key_value_pairs->clear();
+
+ std::vector<std::string_view> pairs =
+ SplitStringPiece(input, std::string(1, key_value_pair_delimiter),
+ TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ key_value_pairs->reserve(pairs.size());
+
+ bool success = true;
+ for (const std::string_view& pair : pairs) {
+ if (!AppendStringKeyValue(pair, key_value_delimiter, key_value_pairs)) {
+ // Don't return here, to allow for pairs without associated
+ // value or key; just record that the split failed.
+ success = false;
+ }
+ }
+ return success;
+}
+
+std::vector<std::u16string> SplitStringUsingSubstr(
+ std::u16string_view input,
+ std::u16string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<std::u16string> result;
+ SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+ return result;
+}
+
+std::vector<std::string> SplitStringUsingSubstr(std::string_view input,
+ std::string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<std::string> result;
+ SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+ return result;
+}
+
+std::vector<std::u16string_view> SplitStringPieceUsingSubstr(
+ std::u16string_view input,
+ std::u16string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<std::u16string_view> result;
+ SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+ return result;
+}
+
+std::vector<std::string_view> SplitStringPieceUsingSubstr(
+ std::string_view input,
+ std::string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<std::string_view> result;
+ SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+ return result;
+}
+
+} // namespace base
diff --git a/gn/src/base/strings/string_split.h b/gn/src/base/strings/string_split.h
new file mode 100644
index 00000000000..7ba14e19598
--- /dev/null
+++ b/gn/src/base/strings/string_split.h
@@ -0,0 +1,122 @@
+// 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 BASE_STRINGS_STRING_SPLIT_H_
+#define BASE_STRINGS_STRING_SPLIT_H_
+
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace base {
+
+enum WhitespaceHandling {
+ KEEP_WHITESPACE,
+ TRIM_WHITESPACE,
+};
+
+enum SplitResult {
+ // Strictly return all results.
+ //
+ // If the input is ",," and the separator is ',' this will return a
+ // vector of three empty strings.
+ SPLIT_WANT_ALL,
+
+ // Only nonempty results will be added to the results. Multiple separators
+ // will be coalesced. Separators at the beginning and end of the input will
+ // be ignored. With TRIM_WHITESPACE, whitespace-only results will be dropped.
+ //
+ // If the input is ",," and the separator is ',', this will return an empty
+ // vector.
+ SPLIT_WANT_NONEMPTY,
+};
+
+// Split the given string on ANY of the given separators, returning copies of
+// the result.
+//
+// To split on either commas or semicolons, keeping all whitespace:
+//
+// std::vector<std::string> tokens = base::SplitString(
+// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+std::vector<std::string> SplitString(std::string_view input,
+ std::string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+std::vector<std::u16string> SplitString(std::u16string_view input,
+ std::u16string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+// Like SplitString above except it returns a vector of StringPieces which
+// reference the original buffer without copying. Although you have to be
+// careful to keep the original string unmodified, this provides an efficient
+// way to iterate through tokens in a string.
+//
+// To iterate through all whitespace-separated tokens in an input string:
+//
+// for (const auto& cur :
+// base::SplitStringPiece(input, base::kWhitespaceASCII,
+// base::KEEP_WHITESPACE,
+// base::SPLIT_WANT_NONEMPTY)) {
+// ...
+std::vector<std::string_view> SplitStringPiece(std::string_view input,
+ std::string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+std::vector<std::u16string_view> SplitStringPiece(
+ std::u16string_view input,
+ std::u16string_view separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+using StringPairs = std::vector<std::pair<std::string, std::string>>;
+
+// Splits |line| into key value pairs according to the given delimiters and
+// removes whitespace leading each key and trailing each value. Returns true
+// only if each pair has a non-empty key and value. |key_value_pairs| will
+// include ("","") pairs for entries without |key_value_delimiter|.
+bool SplitStringIntoKeyValuePairs(std::string_view input,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs);
+
+// Similar to SplitString, but use a substring delimiter instead of a list of
+// characters that are all possible delimiters.
+std::vector<std::u16string> SplitStringUsingSubstr(
+ std::u16string_view input,
+ std::u16string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+std::vector<std::string> SplitStringUsingSubstr(std::string_view input,
+ std::string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
+// which reference the original buffer without copying. Although you have to be
+// careful to keep the original string unmodified, this provides an efficient
+// way to iterate through tokens in a string.
+//
+// To iterate through all newline-separated tokens in an input string:
+//
+// for (const auto& cur :
+// base::SplitStringUsingSubstr(input, "\r\n",
+// base::KEEP_WHITESPACE,
+// base::SPLIT_WANT_NONEMPTY)) {
+// ...
+std::vector<std::u16string_view> SplitStringPieceUsingSubstr(
+ std::u16string_view input,
+ std::u16string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+std::vector<std::string_view> SplitStringPieceUsingSubstr(
+ std::string_view input,
+ std::string_view delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_SPLIT_H_
diff --git a/gn/src/base/strings/string_tokenizer.h b/gn/src/base/strings/string_tokenizer.h
new file mode 100644
index 00000000000..fe9401de8da
--- /dev/null
+++ b/gn/src/base/strings/string_tokenizer.h
@@ -0,0 +1,250 @@
+// 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.
+
+#ifndef BASE_STRINGS_STRING_TOKENIZER_H_
+#define BASE_STRINGS_STRING_TOKENIZER_H_
+
+#include <algorithm>
+#include <string>
+#include <string_view>
+
+namespace base {
+
+// StringTokenizerT is a simple string tokenizer class. It works like an
+// iterator that with each step (see the Advance method) updates members that
+// refer to the next token in the input string. The user may optionally
+// configure the tokenizer to return delimiters.
+//
+// EXAMPLE 1:
+//
+// char input[] = "this is a test";
+// CStringTokenizer t(input, input + strlen(input), " ");
+// while (t.GetNext()) {
+// printf("%s\n", t.token().c_str());
+// }
+//
+// Output:
+//
+// this
+// is
+// a
+// test
+//
+//
+// EXAMPLE 2:
+//
+// std::string input = "no-cache=\"foo, bar\", private";
+// StringTokenizer t(input, ", ");
+// t.set_quote_chars("\"");
+// while (t.GetNext()) {
+// printf("%s\n", t.token().c_str());
+// }
+//
+// Output:
+//
+// no-cache="foo, bar"
+// private
+//
+//
+// EXAMPLE 3:
+//
+// bool next_is_option = false, next_is_value = false;
+// std::string input = "text/html; charset=UTF-8; foo=bar";
+// StringTokenizer t(input, "; =");
+// t.set_options(StringTokenizer::RETURN_DELIMS);
+// while (t.GetNext()) {
+// if (t.token_is_delim()) {
+// switch (*t.token_begin()) {
+// case ';':
+// next_is_option = true;
+// break;
+// case '=':
+// next_is_value = true;
+// break;
+// }
+// } else {
+// const char* label;
+// if (next_is_option) {
+// label = "option-name";
+// next_is_option = false;
+// } else if (next_is_value) {
+// label = "option-value";
+// next_is_value = false;
+// } else {
+// label = "mime-type";
+// }
+// printf("%s: %s\n", label, t.token().c_str());
+// }
+// }
+//
+//
+template <class str, class const_iterator>
+class StringTokenizerT {
+ public:
+ typedef typename str::value_type char_type;
+
+ // Options that may be pass to set_options()
+ enum {
+ // Specifies the delimiters should be returned as tokens
+ RETURN_DELIMS = 1 << 0,
+ };
+
+ // The string object must live longer than the tokenizer. In particular, this
+ // should not be constructed with a temporary. The deleted rvalue constructor
+ // blocks the most obvious instances of this (e.g. passing a string literal to
+ // the constructor), but caution must still be exercised.
+ StringTokenizerT(const str& string, const str& delims) {
+ Init(string.begin(), string.end(), delims);
+ }
+
+ // Don't allow temporary strings to be used with string tokenizer, since
+ // Init() would otherwise save iterators to a temporary string.
+ StringTokenizerT(str&&, const str& delims) = delete;
+
+ StringTokenizerT(const_iterator string_begin,
+ const_iterator string_end,
+ const str& delims) {
+ Init(string_begin, string_end, delims);
+ }
+
+ // Set the options for this tokenizer. By default, this is 0.
+ void set_options(int options) { options_ = options; }
+
+ // Set the characters to regard as quotes. By default, this is empty. When
+ // a quote char is encountered, the tokenizer will switch into a mode where
+ // it ignores delimiters that it finds. It switches out of this mode once it
+ // finds another instance of the quote char. If a backslash is encountered
+ // within a quoted string, then the next character is skipped.
+ void set_quote_chars(const str& quotes) { quotes_ = quotes; }
+
+ // Call this method to advance the tokenizer to the next delimiter. This
+ // returns false if the tokenizer is complete. This method must be called
+ // before calling any of the token* methods.
+ bool GetNext() {
+ if (quotes_.empty() && options_ == 0)
+ return QuickGetNext();
+ else
+ return FullGetNext();
+ }
+
+ // Start iterating through tokens from the beginning of the string.
+ void Reset() { token_end_ = start_pos_; }
+
+ // Returns true if token is a delimiter. When the tokenizer is constructed
+ // with the RETURN_DELIMS option, this method can be used to check if the
+ // returned token is actually a delimiter.
+ bool token_is_delim() const { return token_is_delim_; }
+
+ // If GetNext() returned true, then these methods may be used to read the
+ // value of the token.
+ const_iterator token_begin() const { return token_begin_; }
+ const_iterator token_end() const { return token_end_; }
+ str token() const { return str(token_begin_, token_end_); }
+ std::basic_string_view<typename str::value_type> token_piece() const {
+ return std::basic_string_view<typename str::value_type>(
+ &*token_begin_, std::distance(token_begin_, token_end_));
+ }
+
+ private:
+ void Init(const_iterator string_begin,
+ const_iterator string_end,
+ const str& delims) {
+ start_pos_ = string_begin;
+ token_begin_ = string_begin;
+ token_end_ = string_begin;
+ end_ = string_end;
+ delims_ = delims;
+ options_ = 0;
+ token_is_delim_ = false;
+ }
+
+ // Implementation of GetNext() for when we have no quote characters. We have
+ // two separate implementations because AdvanceOne() is a hot spot in large
+ // text files with large tokens.
+ bool QuickGetNext() {
+ token_is_delim_ = false;
+ for (;;) {
+ token_begin_ = token_end_;
+ if (token_end_ == end_)
+ return false;
+ ++token_end_;
+ if (delims_.find(*token_begin_) == str::npos)
+ break;
+ // else skip over delimiter.
+ }
+ while (token_end_ != end_ && delims_.find(*token_end_) == str::npos)
+ ++token_end_;
+ return true;
+ }
+
+ // Implementation of GetNext() for when we have to take quotes into account.
+ bool FullGetNext() {
+ AdvanceState state;
+ token_is_delim_ = false;
+ for (;;) {
+ token_begin_ = token_end_;
+ if (token_end_ == end_)
+ return false;
+ ++token_end_;
+ if (AdvanceOne(&state, *token_begin_))
+ break;
+ if (options_ & RETURN_DELIMS) {
+ token_is_delim_ = true;
+ return true;
+ }
+ // else skip over delimiter.
+ }
+ while (token_end_ != end_ && AdvanceOne(&state, *token_end_))
+ ++token_end_;
+ return true;
+ }
+
+ bool IsDelim(char_type c) const { return delims_.find(c) != str::npos; }
+
+ bool IsQuote(char_type c) const { return quotes_.find(c) != str::npos; }
+
+ struct AdvanceState {
+ bool in_quote;
+ bool in_escape;
+ char_type quote_char;
+ AdvanceState() : in_quote(false), in_escape(false), quote_char('\0') {}
+ };
+
+ // Returns true if a delimiter was not hit.
+ bool AdvanceOne(AdvanceState* state, char_type c) {
+ if (state->in_quote) {
+ if (state->in_escape) {
+ state->in_escape = false;
+ } else if (c == '\\') {
+ state->in_escape = true;
+ } else if (c == state->quote_char) {
+ state->in_quote = false;
+ }
+ } else {
+ if (IsDelim(c))
+ return false;
+ state->in_quote = IsQuote(state->quote_char = c);
+ }
+ return true;
+ }
+
+ const_iterator start_pos_;
+ const_iterator token_begin_;
+ const_iterator token_end_;
+ const_iterator end_;
+ str delims_;
+ str quotes_;
+ int options_;
+ bool token_is_delim_;
+};
+
+typedef StringTokenizerT<std::string, std::string::const_iterator>
+ StringTokenizer;
+typedef StringTokenizerT<std::u16string, std::u16string::const_iterator>
+ WStringTokenizer;
+typedef StringTokenizerT<std::string, const char*> CStringTokenizer;
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_TOKENIZER_H_
diff --git a/gn/src/base/strings/string_util.cc b/gn/src/base/strings/string_util.cc
new file mode 100644
index 00000000000..ba918ad3c80
--- /dev/null
+++ b/gn/src/base/strings/string_util.cc
@@ -0,0 +1,1047 @@
+// 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/strings/string_util.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "util/build_config.h"
+
+namespace base {
+
+namespace {
+
+// Used by ReplaceStringPlaceholders to track the position in the string of
+// replaced parameters.
+struct ReplacementOffset {
+ ReplacementOffset(uintptr_t parameter, size_t offset)
+ : parameter(parameter), offset(offset) {}
+
+ // Index of the parameter.
+ uintptr_t parameter;
+
+ // Starting position in the string.
+ size_t offset;
+};
+
+static bool CompareParameter(const ReplacementOffset& elem1,
+ const ReplacementOffset& elem2) {
+ return elem1.parameter < elem2.parameter;
+}
+
+// Assuming that a pointer is the size of a "machine word", then
+// uintptr_t is an integer type that is also a machine word.
+typedef uintptr_t MachineWord;
+const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
+
+inline bool IsAlignedToMachineWord(const void* pointer) {
+ return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask);
+}
+
+template <typename T>
+inline T* AlignToMachineWord(T* pointer) {
+ return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) &
+ ~kMachineWordAlignmentMask);
+}
+
+template <size_t size, typename CharacterType>
+struct NonASCIIMask;
+template <>
+struct NonASCIIMask<4, char16_t> {
+ static inline uint32_t value() { return 0xFF80FF80U; }
+};
+template <>
+struct NonASCIIMask<4, char> {
+ static inline uint32_t value() { return 0x80808080U; }
+};
+template <>
+struct NonASCIIMask<8, char16_t> {
+ static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
+};
+template <>
+struct NonASCIIMask<8, char> {
+ static inline uint64_t value() { return 0x8080808080808080ULL; }
+};
+
+} // namespace
+
+namespace {
+
+template <typename StringType>
+StringType ToLowerASCIIImpl(
+ std::basic_string_view<typename StringType::value_type> str) {
+ StringType ret;
+ ret.reserve(str.size());
+ for (size_t i = 0; i < str.size(); i++)
+ ret.push_back(ToLowerASCII(str[i]));
+ return ret;
+}
+
+template <typename StringType>
+StringType ToUpperASCIIImpl(
+ std::basic_string_view<typename StringType::value_type> str) {
+ StringType ret;
+ ret.reserve(str.size());
+ for (size_t i = 0; i < str.size(); i++)
+ ret.push_back(ToUpperASCII(str[i]));
+ return ret;
+}
+
+} // namespace
+
+std::string ToLowerASCII(std::string_view str) {
+ return ToLowerASCIIImpl<std::string>(str);
+}
+
+std::u16string ToLowerASCII(std::u16string_view str) {
+ return ToLowerASCIIImpl<std::u16string>(str);
+}
+
+std::string ToUpperASCII(std::string_view str) {
+ return ToUpperASCIIImpl<std::string>(str);
+}
+
+std::u16string ToUpperASCII(std::u16string_view str) {
+ return ToUpperASCIIImpl<std::u16string>(str);
+}
+
+template <class StringType>
+int CompareCaseInsensitiveASCIIT(
+ std::basic_string_view<typename StringType::value_type> a,
+ std::basic_string_view<typename StringType::value_type> b) {
+ // Find the first characters that aren't equal and compare them. If the end
+ // of one of the strings is found before a nonequal character, the lengths
+ // of the strings are compared.
+ size_t i = 0;
+ while (i < a.length() && i < b.length()) {
+ typename StringType::value_type lower_a = ToLowerASCII(a[i]);
+ typename StringType::value_type lower_b = ToLowerASCII(b[i]);
+ if (lower_a < lower_b)
+ return -1;
+ if (lower_a > lower_b)
+ return 1;
+ i++;
+ }
+
+ // End of one string hit before finding a different character. Expect the
+ // common case to be "strings equal" at this point so check that first.
+ if (a.length() == b.length())
+ return 0;
+
+ if (a.length() < b.length())
+ return -1;
+ return 1;
+}
+
+int CompareCaseInsensitiveASCII(std::string_view a, std::string_view b) {
+ return CompareCaseInsensitiveASCIIT<std::string>(a, b);
+}
+
+int CompareCaseInsensitiveASCII(std::u16string_view a, std::u16string_view b) {
+ return CompareCaseInsensitiveASCIIT<std::u16string>(a, b);
+}
+
+bool EqualsCaseInsensitiveASCII(std::string_view a, std::string_view b) {
+ if (a.length() != b.length())
+ return false;
+ return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
+}
+
+bool EqualsCaseInsensitiveASCII(std::u16string_view a, std::u16string_view b) {
+ if (a.length() != b.length())
+ return false;
+ return CompareCaseInsensitiveASCIIT<std::u16string>(a, b) == 0;
+}
+
+template <class StringType>
+bool ReplaceCharsT(
+ const StringType& input,
+ std::basic_string_view<typename StringType::value_type> find_any_of_these,
+ std::basic_string_view<typename StringType::value_type> replace_with,
+ StringType* output);
+
+bool ReplaceChars(const std::u16string& input,
+ std::u16string_view replace_chars,
+ const std::u16string& replace_with,
+ std::u16string* output) {
+ return ReplaceCharsT(input, replace_chars, std::u16string_view(replace_with),
+ output);
+}
+
+bool ReplaceChars(const std::string& input,
+ std::string_view replace_chars,
+ const std::string& replace_with,
+ std::string* output) {
+ return ReplaceCharsT(input, replace_chars, std::string_view(replace_with),
+ output);
+}
+
+bool RemoveChars(const std::u16string& input,
+ std::u16string_view remove_chars,
+ std::u16string* output) {
+ return ReplaceCharsT(input, remove_chars, std::u16string_view(), output);
+}
+
+bool RemoveChars(const std::string& input,
+ std::string_view remove_chars,
+ std::string* output) {
+ return ReplaceCharsT(input, remove_chars, std::string_view(), output);
+}
+
+template <typename Str>
+TrimPositions TrimStringT(
+ const Str& input,
+ std::basic_string_view<typename Str::value_type> trim_chars,
+ TrimPositions positions,
+ Str* output) {
+ // Find the edges of leading/trailing whitespace as desired. Need to use
+ // a std::string_view version of input to be able to call find* on it with the
+ // std::string_view version of trim_chars (normally the trim_chars will be a
+ // constant so avoid making a copy).
+ std::basic_string_view<typename Str::value_type> input_piece(input);
+ const size_t last_char = input.length() - 1;
+ const size_t first_good_char = (positions & TRIM_LEADING)
+ ? input_piece.find_first_not_of(trim_chars)
+ : 0;
+ const size_t last_good_char = (positions & TRIM_TRAILING)
+ ? input_piece.find_last_not_of(trim_chars)
+ : last_char;
+
+ // When the string was all trimmed, report that we stripped off characters
+ // from whichever position the caller was interested in. For empty input, we
+ // stripped no characters, but we still need to clear |output|.
+ if (input.empty() || (first_good_char == Str::npos) ||
+ (last_good_char == Str::npos)) {
+ bool input_was_empty = input.empty(); // in case output == &input
+ output->clear();
+ return input_was_empty ? TRIM_NONE : positions;
+ }
+
+ // Trim.
+ *output = input.substr(first_good_char, last_good_char - first_good_char + 1);
+
+ // Return where we trimmed from.
+ return static_cast<TrimPositions>(
+ ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
+ ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
+}
+
+bool TrimString(const std::u16string& input,
+ std::u16string_view trim_chars,
+ std::u16string* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+bool TrimString(const std::string& input,
+ std::string_view trim_chars,
+ std::string* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+template <typename char_type>
+std::basic_string_view<char_type> TrimStringPieceT(
+ std::basic_string_view<char_type> input,
+ std::basic_string_view<char_type> trim_chars,
+ TrimPositions positions) {
+ size_t begin =
+ (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
+ if (begin == std::basic_string_view<char_type>::npos)
+ return std::basic_string_view<char_type>(); // All trimmed.
+
+ size_t end = (positions & TRIM_TRAILING)
+ ? input.find_last_not_of(trim_chars) + 1
+ : input.size();
+ return input.substr(begin, end - begin);
+}
+
+std::u16string_view TrimString(std::u16string_view input,
+ std::u16string_view trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+std::string_view TrimString(std::string_view input,
+ std::string_view trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+void TruncateUTF8ToByteSize(const std::string& input,
+ const size_t byte_size,
+ std::string* output) {
+ DCHECK(output);
+ if (byte_size > input.length()) {
+ *output = input;
+ return;
+ }
+ DCHECK_LE(byte_size,
+ static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
+ // Note: This cast is necessary because CBU8_NEXT uses int32_ts.
+ int32_t truncation_length = static_cast<int32_t>(byte_size);
+ int32_t char_index = truncation_length - 1;
+ const char* data = input.data();
+
+ // Using CBU8, we will move backwards from the truncation point
+ // to the beginning of the string looking for a valid UTF8
+ // character. Once a full UTF8 character is found, we will
+ // truncate the string to the end of that character.
+ while (char_index >= 0) {
+ int32_t prev = char_index;
+ base_icu::UChar32 code_point = 0;
+ CBU8_NEXT(data, char_index, truncation_length, code_point);
+ if (!IsValidCharacter(code_point) || !IsValidCodepoint(code_point)) {
+ char_index = prev - 1;
+ } else {
+ break;
+ }
+ }
+
+ if (char_index >= 0)
+ *output = input.substr(0, char_index);
+ else
+ output->clear();
+}
+
+TrimPositions TrimWhitespace(const std::u16string& input,
+ TrimPositions positions,
+ std::u16string* output) {
+ return TrimStringT(input, std::u16string_view(kWhitespaceUTF16), positions,
+ output);
+}
+
+std::u16string_view TrimWhitespace(std::u16string_view input,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, std::u16string_view(kWhitespaceUTF16),
+ positions);
+}
+
+TrimPositions TrimWhitespaceASCII(const std::string& input,
+ TrimPositions positions,
+ std::string* output) {
+ return TrimStringT(input, std::string_view(kWhitespaceASCII), positions,
+ output);
+}
+
+std::string_view TrimWhitespaceASCII(std::string_view input,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, std::string_view(kWhitespaceASCII), positions);
+}
+
+template <typename STR>
+STR CollapseWhitespaceT(const STR& text, bool trim_sequences_with_line_breaks) {
+ STR result;
+ result.resize(text.size());
+
+ // Set flags to pretend we're already in a trimmed whitespace sequence, so we
+ // will trim any leading whitespace.
+ bool in_whitespace = true;
+ bool already_trimmed = true;
+
+ int chars_written = 0;
+ for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
+ if (IsUnicodeWhitespace(*i)) {
+ if (!in_whitespace) {
+ // Reduce all whitespace sequences to a single space.
+ in_whitespace = true;
+ result[chars_written++] = L' ';
+ }
+ if (trim_sequences_with_line_breaks && !already_trimmed &&
+ ((*i == '\n') || (*i == '\r'))) {
+ // Whitespace sequences containing CR or LF are eliminated entirely.
+ already_trimmed = true;
+ --chars_written;
+ }
+ } else {
+ // Non-whitespace chracters are copied straight across.
+ in_whitespace = false;
+ already_trimmed = false;
+ result[chars_written++] = *i;
+ }
+ }
+
+ if (in_whitespace && !already_trimmed) {
+ // Any trailing whitespace is eliminated.
+ --chars_written;
+ }
+
+ result.resize(chars_written);
+ return result;
+}
+
+std::u16string CollapseWhitespace(const std::u16string& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+std::string CollapseWhitespaceASCII(const std::string& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+bool ContainsOnlyChars(std::string_view input, std::string_view characters) {
+ return input.find_first_not_of(characters) == std::string_view::npos;
+}
+
+bool ContainsOnlyChars(std::u16string_view input,
+ std::u16string_view characters) {
+ return input.find_first_not_of(characters) == std::u16string_view::npos;
+}
+
+template <class Char>
+inline bool DoIsStringASCII(const Char* characters, size_t length) {
+ MachineWord all_char_bits = 0;
+ const Char* end = characters + length;
+
+ // Prologue: align the input.
+ while (!IsAlignedToMachineWord(characters) && characters != end) {
+ all_char_bits |= *characters;
+ ++characters;
+ }
+
+ // Compare the values of CPU word size.
+ const Char* word_end = AlignToMachineWord(end);
+ const size_t loop_increment = sizeof(MachineWord) / sizeof(Char);
+ while (characters < word_end) {
+ all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
+ characters += loop_increment;
+ }
+
+ // Process the remaining bytes.
+ while (characters != end) {
+ all_char_bits |= *characters;
+ ++characters;
+ }
+
+ MachineWord non_ascii_bit_mask =
+ NonASCIIMask<sizeof(MachineWord), Char>::value();
+ return !(all_char_bits & non_ascii_bit_mask);
+}
+
+bool IsStringASCII(std::string_view str) {
+ return DoIsStringASCII(str.data(), str.length());
+}
+
+bool IsStringASCII(std::u16string_view str) {
+ return DoIsStringASCII(str.data(), str.length());
+}
+
+bool IsStringUTF8(std::string_view str) {
+ const char* src = str.data();
+ int32_t src_len = static_cast<int32_t>(str.length());
+ int32_t char_index = 0;
+
+ while (char_index < src_len) {
+ int32_t code_point;
+ CBU8_NEXT(src, char_index, src_len, code_point);
+ if (!IsValidCharacter(code_point))
+ return false;
+ }
+ return true;
+}
+
+// Implementation note: Normally this function will be called with a hardcoded
+// constant for the lowercase_ascii parameter. Constructing a std::string_view
+// from a C constant requires running strlen, so the result will be two passes
+// through the buffers, one to file the length of lowercase_ascii, and one to
+// compare each letter.
+//
+// This function could have taken a const char* to avoid this and only do one
+// pass through the string. But the strlen is faster than the case-insensitive
+// compares and lets us early-exit in the case that the strings are different
+// lengths (will often be the case for non-matches). So whether one approach or
+// the other will be faster depends on the case.
+//
+// The hardcoded strings are typically very short so it doesn't matter, and the
+// string piece gives additional flexibility for the caller (doesn't have to be
+// null terminated) so we choose the std::string_view route.
+template <typename Str>
+static inline bool DoLowerCaseEqualsASCII(
+ std::basic_string_view<typename Str::value_type> str,
+ std::string_view lowercase_ascii) {
+ if (str.size() != lowercase_ascii.size())
+ return false;
+ for (size_t i = 0; i < str.size(); i++) {
+ if (ToLowerASCII(str[i]) != lowercase_ascii[i])
+ return false;
+ }
+ return true;
+}
+
+bool LowerCaseEqualsASCII(std::string_view str,
+ std::string_view lowercase_ascii) {
+ return DoLowerCaseEqualsASCII<std::string>(str, lowercase_ascii);
+}
+
+bool LowerCaseEqualsASCII(std::u16string_view str,
+ std::string_view lowercase_ascii) {
+ return DoLowerCaseEqualsASCII<std::u16string>(str, lowercase_ascii);
+}
+
+bool EqualsASCII(std::u16string_view str, std::string_view ascii) {
+ if (str.length() != ascii.length())
+ return false;
+ return std::equal(ascii.begin(), ascii.end(), str.begin());
+}
+
+template <typename char_type>
+bool StartsWithT(std::basic_string_view<char_type> str,
+ std::basic_string_view<char_type> search_for,
+ CompareCase case_sensitivity) {
+ if (search_for.size() > str.size())
+ return false;
+
+ std::basic_string_view<char_type> source = str.substr(0, search_for.size());
+
+ switch (case_sensitivity) {
+ case CompareCase::SENSITIVE:
+ return source == search_for;
+
+ case CompareCase::INSENSITIVE_ASCII:
+ return std::equal(search_for.begin(), search_for.end(), source.begin(),
+ CaseInsensitiveCompareASCII<char_type>());
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool StartsWith(std::string_view str,
+ std::string_view search_for,
+ CompareCase case_sensitivity) {
+ return StartsWithT(str, search_for, case_sensitivity);
+}
+
+bool StartsWith(std::u16string_view str,
+ std::u16string_view search_for,
+ CompareCase case_sensitivity) {
+ return StartsWithT(str, search_for, case_sensitivity);
+}
+
+template <typename Str>
+bool EndsWithT(std::basic_string_view<typename Str::value_type> str,
+ std::basic_string_view<typename Str::value_type> search_for,
+ CompareCase case_sensitivity) {
+ if (search_for.size() > str.size())
+ return false;
+
+ std::basic_string_view<typename Str::value_type> source =
+ str.substr(str.size() - search_for.size(), search_for.size());
+
+ switch (case_sensitivity) {
+ case CompareCase::SENSITIVE:
+ return source == search_for;
+
+ case CompareCase::INSENSITIVE_ASCII:
+ return std::equal(
+ source.begin(), source.end(), search_for.begin(),
+ CaseInsensitiveCompareASCII<typename Str::value_type>());
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool EndsWith(std::string_view str,
+ std::string_view search_for,
+ CompareCase case_sensitivity) {
+ return EndsWithT<std::string>(str, search_for, case_sensitivity);
+}
+
+bool EndsWith(std::u16string_view str,
+ std::u16string_view search_for,
+ CompareCase case_sensitivity) {
+ return EndsWithT<std::u16string>(str, search_for, case_sensitivity);
+}
+
+char HexDigitToInt(char16_t c) {
+ DCHECK(IsHexDigit(c));
+ if (c >= '0' && c <= '9')
+ return static_cast<char>(c - '0');
+ if (c >= 'A' && c <= 'F')
+ return static_cast<char>(c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return static_cast<char>(c - 'a' + 10);
+ return 0;
+}
+
+bool IsUnicodeWhitespace(char16_t c) {
+ // kWhitespaceWide is a NULL-terminated string
+ for (const char16_t* cur = kWhitespaceUTF16; *cur; ++cur) {
+ if (*cur == c)
+ return true;
+ }
+ return false;
+}
+
+static const char* const kByteStringsUnlocalized[] = {" B", " kB", " MB",
+ " GB", " TB", " PB"};
+
+std::u16string FormatBytesUnlocalized(int64_t bytes) {
+ double unit_amount = static_cast<double>(bytes);
+ size_t dimension = 0;
+ const int kKilo = 1024;
+ while (unit_amount >= kKilo &&
+ dimension < std::size(kByteStringsUnlocalized) - 1) {
+ unit_amount /= kKilo;
+ dimension++;
+ }
+
+ char buf[64];
+ if (bytes != 0 && dimension > 0 && unit_amount < 100) {
+ base::snprintf(buf, std::size(buf), "%.1lf%s", unit_amount,
+ kByteStringsUnlocalized[dimension]);
+ } else {
+ base::snprintf(buf, std::size(buf), "%.0lf%s", unit_amount,
+ kByteStringsUnlocalized[dimension]);
+ }
+
+ return ASCIIToUTF16(buf);
+}
+
+// A Matcher for DoReplaceMatchesAfterOffset() that matches substrings.
+template <class StringType>
+struct SubstringMatcher {
+ std::basic_string_view<typename StringType::value_type> find_this;
+
+ size_t Find(const StringType& input, size_t pos) {
+ return input.find(find_this.data(), pos, find_this.length());
+ }
+ size_t MatchSize() { return find_this.length(); }
+};
+
+// A Matcher for DoReplaceMatchesAfterOffset() that matches single characters.
+template <class StringType>
+struct CharacterMatcher {
+ std::basic_string_view<typename StringType::value_type> find_any_of_these;
+
+ size_t Find(const StringType& input, size_t pos) {
+ return input.find_first_of(find_any_of_these.data(), pos,
+ find_any_of_these.length());
+ }
+ constexpr size_t MatchSize() { return 1; }
+};
+
+enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST };
+
+// Runs in O(n) time in the length of |str|, and transforms the string without
+// reallocating when possible. Returns |true| if any matches were found.
+//
+// This is parameterized on a |Matcher| traits type, so that it can be the
+// implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset().
+template <class StringType, class Matcher>
+bool DoReplaceMatchesAfterOffset(
+ StringType* str,
+ size_t initial_offset,
+ Matcher matcher,
+ std::basic_string_view<typename StringType::value_type> replace_with,
+ ReplaceType replace_type) {
+ using CharTraits = typename StringType::traits_type;
+
+ const size_t find_length = matcher.MatchSize();
+ if (!find_length)
+ return false;
+
+ // If the find string doesn't appear, there's nothing to do.
+ size_t first_match = matcher.Find(*str, initial_offset);
+ if (first_match == StringType::npos)
+ return false;
+
+ // If we're only replacing one instance, there's no need to do anything
+ // complicated.
+ const size_t replace_length = replace_with.length();
+ if (replace_type == ReplaceType::REPLACE_FIRST) {
+ str->replace(first_match, find_length, replace_with.data(), replace_length);
+ return true;
+ }
+
+ // If the find and replace strings are the same length, we can simply use
+ // replace() on each instance, and finish the entire operation in O(n) time.
+ if (find_length == replace_length) {
+ auto* buffer = &((*str)[0]);
+ for (size_t offset = first_match; offset != StringType::npos;
+ offset = matcher.Find(*str, offset + replace_length)) {
+ CharTraits::copy(buffer + offset, replace_with.data(), replace_length);
+ }
+ return true;
+ }
+
+ // Since the find and replace strings aren't the same length, a loop like the
+ // one above would be O(n^2) in the worst case, as replace() will shift the
+ // entire remaining string each time. We need to be more clever to keep things
+ // O(n).
+ //
+ // When the string is being shortened, it's possible to just shift the matches
+ // down in one pass while finding, and truncate the length at the end of the
+ // search.
+ //
+ // If the string is being lengthened, more work is required. The strategy used
+ // here is to make two find() passes through the string. The first pass counts
+ // the number of matches to determine the new size. The second pass will
+ // either construct the new string into a new buffer (if the existing buffer
+ // lacked capacity), or else -- if there is room -- create a region of scratch
+ // space after |first_match| by shifting the tail of the string to a higher
+ // index, and doing in-place moves from the tail to lower indices thereafter.
+ size_t str_length = str->length();
+ size_t expansion = 0;
+ if (replace_length > find_length) {
+ // This operation lengthens the string; determine the new length by counting
+ // matches.
+ const size_t expansion_per_match = (replace_length - find_length);
+ size_t num_matches = 0;
+ for (size_t match = first_match; match != StringType::npos;
+ match = matcher.Find(*str, match + find_length)) {
+ expansion += expansion_per_match;
+ ++num_matches;
+ }
+ const size_t final_length = str_length + expansion;
+
+ if (str->capacity() < final_length) {
+ // If we'd have to allocate a new buffer to grow the string, build the
+ // result directly into the new allocation via append().
+ StringType src(str->get_allocator());
+ str->swap(src);
+ str->reserve(final_length);
+
+ size_t pos = 0;
+ for (size_t match = first_match;; match = matcher.Find(src, pos)) {
+ str->append(src, pos, match - pos);
+ str->append(replace_with.data(), replace_length);
+ pos = match + find_length;
+
+ // A mid-loop test/break enables skipping the final Find() call; the
+ // number of matches is known, so don't search past the last one.
+ if (!--num_matches)
+ break;
+ }
+
+ // Handle substring after the final match.
+ str->append(src, pos, str_length - pos);
+ return true;
+ }
+
+ // Prepare for the copy/move loop below -- expand the string to its final
+ // size by shifting the data after the first match to the end of the resized
+ // string.
+ size_t shift_src = first_match + find_length;
+ size_t shift_dst = shift_src + expansion;
+
+ // Big |expansion| factors (relative to |str_length|) require padding up to
+ // |shift_dst|.
+ if (shift_dst > str_length)
+ str->resize(shift_dst);
+
+ str->replace(shift_dst, str_length - shift_src, *str, shift_src,
+ str_length - shift_src);
+ str_length = final_length;
+ }
+
+ // We can alternate replacement and move operations. This won't overwrite the
+ // unsearched region of the string so long as |write_offset| <= |read_offset|;
+ // that condition is always satisfied because:
+ //
+ // (a) If the string is being shortened, |expansion| is zero and
+ // |write_offset| grows slower than |read_offset|.
+ //
+ // (b) If the string is being lengthened, |write_offset| grows faster than
+ // |read_offset|, but |expansion| is big enough so that |write_offset|
+ // will only catch up to |read_offset| at the point of the last match.
+ auto* buffer = &((*str)[0]);
+ size_t write_offset = first_match;
+ size_t read_offset = first_match + expansion;
+ do {
+ if (replace_length) {
+ CharTraits::copy(buffer + write_offset, replace_with.data(),
+ replace_length);
+ write_offset += replace_length;
+ }
+ read_offset += find_length;
+
+ // min() clamps StringType::npos (the largest unsigned value) to str_length.
+ size_t match = std::min(matcher.Find(*str, read_offset), str_length);
+
+ size_t length = match - read_offset;
+ if (length) {
+ CharTraits::move(buffer + write_offset, buffer + read_offset, length);
+ write_offset += length;
+ read_offset += length;
+ }
+ } while (read_offset < str_length);
+
+ // If we're shortening the string, truncate it now.
+ str->resize(write_offset);
+ return true;
+}
+
+template <class StringType>
+bool ReplaceCharsT(
+ const StringType& input,
+ std::basic_string_view<typename StringType::value_type> find_any_of_these,
+ std::basic_string_view<typename StringType::value_type> replace_with,
+ StringType* output) {
+ // Commonly, this is called with output and input being the same string; in
+ // that case, this assignment is inexpensive.
+ *output = input;
+
+ return DoReplaceMatchesAfterOffset(
+ output, 0, CharacterMatcher<StringType>{find_any_of_these}, replace_with,
+ ReplaceType::REPLACE_ALL);
+}
+
+void ReplaceFirstSubstringAfterOffset(std::u16string* str,
+ size_t start_offset,
+ std::u16string_view find_this,
+ std::u16string_view replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<std::u16string>{find_this},
+ replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceFirstSubstringAfterOffset(std::string* str,
+ size_t start_offset,
+ std::string_view find_this,
+ std::string_view replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<std::string>{find_this},
+ replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceSubstringsAfterOffset(std::u16string* str,
+ size_t start_offset,
+ std::u16string_view find_this,
+ std::u16string_view replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<std::u16string>{find_this},
+ replace_with, ReplaceType::REPLACE_ALL);
+}
+
+void ReplaceSubstringsAfterOffset(std::string* str,
+ size_t start_offset,
+ std::string_view find_this,
+ std::string_view replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<std::string>{find_this},
+ replace_with, ReplaceType::REPLACE_ALL);
+}
+
+template <class string_type>
+inline typename string_type::value_type* WriteIntoT(string_type* str,
+ size_t length_with_null) {
+ DCHECK_GT(length_with_null, 1u);
+ str->reserve(length_with_null);
+ str->resize(length_with_null - 1);
+ return &((*str)[0]);
+}
+
+char* WriteInto(std::string* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
+}
+
+char16_t* WriteInto(std::u16string* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
+}
+
+#if defined(_MSC_VER) && !defined(__clang__)
+// Work around VC++ code-gen bug. https://crbug.com/804884
+#pragma optimize("", off)
+#endif
+
+// Generic version for all JoinString overloads. |list_type| must be a sequence
+// (std::vector or std::initializer_list) of strings/string_views of any type.
+template <typename char_type, typename list_type>
+static std::basic_string<char_type> JoinStringT(
+ const list_type& parts,
+ std::basic_string_view<char_type> sep) {
+ if (parts.size() == 0)
+ return std::basic_string<char_type>();
+
+ // Pre-allocate the eventual size of the string. Start with the size of all of
+ // the separators (note that this *assumes* parts.size() > 0).
+ size_t total_size = (parts.size() - 1) * sep.size();
+ for (const auto& part : parts)
+ total_size += part.size();
+ std::basic_string<char_type> result;
+ result.reserve(total_size);
+
+ auto iter = parts.begin();
+ DCHECK(iter != parts.end());
+ result.append(*iter);
+ ++iter;
+
+ for (; iter != parts.end(); ++iter) {
+ result.append(sep);
+ result.append(*iter);
+ }
+
+ // Sanity-check that we pre-allocated correctly.
+ DCHECK_EQ(total_size, result.size());
+
+ return result;
+}
+
+std::string JoinString(const std::vector<std::string>& parts,
+ std::string_view separator) {
+ return JoinStringT(parts, separator);
+}
+
+std::u16string JoinString(const std::vector<std::u16string>& parts,
+ std::u16string_view separator) {
+ return JoinStringT(parts, separator);
+}
+
+#if defined(_MSC_VER) && !defined(__clang__)
+// Work around VC++ code-gen bug. https://crbug.com/804884
+#pragma optimize("", on)
+#endif
+
+std::string JoinString(const std::vector<std::string_view>& parts,
+ std::string_view separator) {
+ return JoinStringT(parts, separator);
+}
+
+std::u16string JoinString(const std::vector<std::u16string_view>& parts,
+ std::u16string_view separator) {
+ return JoinStringT(parts, separator);
+}
+
+std::string JoinString(std::initializer_list<std::string_view> parts,
+ std::string_view separator) {
+ return JoinStringT(parts, separator);
+}
+
+std::u16string JoinString(std::initializer_list<std::u16string_view> parts,
+ std::u16string_view separator) {
+ return JoinStringT(parts, separator);
+}
+
+template <class FormatStringType, class OutStringType>
+OutStringType DoReplaceStringPlaceholders(
+ const FormatStringType& format_string,
+ const std::vector<OutStringType>& subst,
+ std::vector<size_t>* offsets) {
+ size_t substitutions = subst.size();
+ DCHECK_LT(substitutions, 10U);
+
+ size_t sub_length = 0;
+ for (const auto& cur : subst)
+ sub_length += cur.length();
+
+ OutStringType formatted;
+ formatted.reserve(format_string.length() + sub_length);
+
+ std::vector<ReplacementOffset> r_offsets;
+ for (auto i = format_string.begin(); i != format_string.end(); ++i) {
+ if ('$' == *i) {
+ if (i + 1 != format_string.end()) {
+ ++i;
+ if ('$' == *i) {
+ while (i != format_string.end() && '$' == *i) {
+ formatted.push_back('$');
+ ++i;
+ }
+ --i;
+ } else {
+ if (*i < '1' || *i > '9') {
+ DLOG(ERROR) << "Invalid placeholder: $" << *i;
+ continue;
+ }
+ uintptr_t index = *i - '1';
+ if (offsets) {
+ ReplacementOffset r_offset(index,
+ static_cast<int>(formatted.size()));
+ r_offsets.insert(
+ std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
+ &CompareParameter),
+ r_offset);
+ }
+ if (index < substitutions)
+ formatted.append(subst.at(index));
+ }
+ }
+ } else {
+ formatted.push_back(*i);
+ }
+ }
+ if (offsets) {
+ for (const auto& cur : r_offsets)
+ offsets->push_back(cur.offset);
+ }
+ return formatted;
+}
+
+std::u16string ReplaceStringPlaceholders(
+ const std::u16string& format_string,
+ const std::vector<std::u16string>& subst,
+ std::vector<size_t>* offsets) {
+ return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+std::string ReplaceStringPlaceholders(std::string_view format_string,
+ const std::vector<std::string>& subst,
+ std::vector<size_t>* offsets) {
+ return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+std::u16string ReplaceStringPlaceholders(const std::u16string& format_string,
+ const std::u16string& a,
+ size_t* offset) {
+ std::vector<size_t> offsets;
+ std::vector<std::u16string> subst;
+ subst.push_back(a);
+ std::u16string result =
+ ReplaceStringPlaceholders(format_string, subst, &offsets);
+
+ DCHECK_EQ(1U, offsets.size());
+ if (offset)
+ *offset = offsets[0];
+ return result;
+}
+
+// The following code is compatible with the OpenBSD lcpy interface. See:
+// http://www.gratisoft.us/todd/papers/strlcpy.html
+// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
+
+namespace {
+
+template <typename CHAR>
+size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
+ for (size_t i = 0; i < dst_size; ++i) {
+ if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL.
+ return i;
+ }
+
+ // We were left off at dst_size. We over copied 1 byte. Null terminate.
+ if (dst_size != 0)
+ dst[dst_size - 1] = 0;
+
+ // Count the rest of the |src|, and return it's length in characters.
+ while (src[dst_size])
+ ++dst_size;
+ return dst_size;
+}
+
+} // namespace
+
+} // namespace base
diff --git a/gn/src/base/strings/string_util.h b/gn/src/base/strings/string_util.h
new file mode 100644
index 00000000000..9ed66b2ece8
--- /dev/null
+++ b/gn/src/base/strings/string_util.h
@@ -0,0 +1,424 @@
+// 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 defines utility functions for working with strings.
+
+#ifndef BASE_STRINGS_STRING_UTIL_H_
+#define BASE_STRINGS_STRING_UTIL_H_
+
+#include <ctype.h>
+#include <stdarg.h> // va_list
+#include <stddef.h>
+#include <stdint.h>
+
+#include <initializer_list>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "util/build_config.h"
+
+namespace base {
+
+// C standard-library functions that aren't cross-platform are provided as
+// "base::...", and their prototypes are listed below. These functions are
+// then implemented as inline calls to the platform-specific equivalents in the
+// platform-specific headers.
+
+// Wrapper for vsnprintf that always null-terminates and always returns the
+// number of characters that would be in an untruncated formatted
+// string, even when truncation occurs.
+int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments)
+ PRINTF_FORMAT(3, 0);
+
+// Some of these implementations need to be inlined.
+
+// We separate the declaration from the implementation of this inline
+// function just so the PRINTF_FORMAT works.
+inline int snprintf(char* buffer,
+ size_t size,
+ _Printf_format_string_ const char* format,
+ ...) PRINTF_FORMAT(3, 4);
+inline int snprintf(char* buffer,
+ size_t size,
+ _Printf_format_string_ const char* format,
+ ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ int result = vsnprintf(buffer, size, format, arguments);
+ va_end(arguments);
+ return result;
+}
+
+// ASCII-specific tolower. The standard library's tolower is locale sensitive,
+// so we don't want to use it here.
+inline char ToLowerASCII(char c) {
+ return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+inline char16_t ToLowerASCII(char16_t c) {
+ return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+
+// ASCII-specific toupper. The standard library's toupper is locale sensitive,
+// so we don't want to use it here.
+inline char ToUpperASCII(char c) {
+ return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+}
+inline char16_t ToUpperASCII(char16_t c) {
+ return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+}
+
+// Converts the given string to it's ASCII-lowercase equivalent.
+std::string ToLowerASCII(std::string_view str);
+std::u16string ToLowerASCII(std::u16string_view str);
+
+// Converts the given string to it's ASCII-uppercase equivalent.
+std::string ToUpperASCII(std::string_view str);
+std::u16string ToUpperASCII(std::u16string_view str);
+
+// Functor for case-insensitive ASCII comparisons for STL algorithms like
+// std::search.
+//
+// Note that a full Unicode version of this functor is not possible to write
+// because case mappings might change the number of characters, depend on
+// context (combining accents), and require handling UTF-16. If you need
+// proper Unicode support, use base::i18n::ToLower/FoldCase and then just
+// use a normal operator== on the result.
+template <typename Char>
+struct CaseInsensitiveCompareASCII {
+ public:
+ bool operator()(Char x, Char y) const {
+ return ToLowerASCII(x) == ToLowerASCII(y);
+ }
+};
+
+// Like strcasecmp for case-insensitive ASCII characters only. Returns:
+// -1 (a < b)
+// 0 (a == b)
+// 1 (a > b)
+// (unlike strcasecmp which can return values greater or less than 1/-1). For
+// full Unicode support, use base::i18n::ToLower or base::i18h::FoldCase
+// and then just call the normal string operators on the result.
+int CompareCaseInsensitiveASCII(std::string_view a, std::string_view b);
+int CompareCaseInsensitiveASCII(std::u16string_view a, std::u16string_view b);
+
+// Equality for ASCII case-insensitive comparisons. For full Unicode support,
+// use base::i18n::ToLower or base::i18h::FoldCase and then compare with either
+// == or !=.
+bool EqualsCaseInsensitiveASCII(std::string_view a, std::string_view b);
+bool EqualsCaseInsensitiveASCII(std::u16string_view a, std::u16string_view b);
+
+// Contains the set of characters representing whitespace in the corresponding
+// encoding. Null-terminated. The ASCII versions are the whitespaces as defined
+// by HTML5, and don't include control characters.
+extern const char16_t kWhitespaceUTF16[]; // Includes Unicode.
+extern const char kWhitespaceASCII[];
+extern const char16_t kWhitespaceASCIIAs16[]; // No unicode.
+
+// Null-terminated string representing the UTF-8 byte order mark.
+extern const char kUtf8ByteOrderMark[];
+
+// Removes characters in |remove_chars| from anywhere in |input|. Returns true
+// if any characters were removed. |remove_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+bool RemoveChars(const std::u16string& input,
+ std::u16string_view remove_chars,
+ std::u16string* output);
+bool RemoveChars(const std::string& input,
+ std::string_view remove_chars,
+ std::string* output);
+
+// Replaces characters in |replace_chars| from anywhere in |input| with
+// |replace_with|. Each character in |replace_chars| will be replaced with
+// the |replace_with| string. Returns true if any characters were replaced.
+// |replace_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+bool ReplaceChars(const std::u16string& input,
+ std::u16string_view replace_chars,
+ const std::u16string& replace_with,
+ std::u16string* output);
+bool ReplaceChars(const std::string& input,
+ std::string_view replace_chars,
+ const std::string& replace_with,
+ std::string* output);
+
+enum TrimPositions {
+ TRIM_NONE = 0,
+ TRIM_LEADING = 1 << 0,
+ TRIM_TRAILING = 1 << 1,
+ TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
+};
+
+// Removes characters in |trim_chars| from the beginning and end of |input|.
+// The 8-bit version only works on 8-bit characters, not UTF-8. Returns true if
+// any characters were removed.
+//
+// It is safe to use the same variable for both |input| and |output| (this is
+// the normal usage to trim in-place).
+bool TrimString(const std::u16string& input,
+ std::u16string_view trim_chars,
+ std::u16string* output);
+bool TrimString(const std::string& input,
+ std::string_view trim_chars,
+ std::string* output);
+
+// std::string_view versions of the above. The returned pieces refer to the
+// original buffer.
+std::u16string_view TrimString(std::u16string_view input,
+ std::u16string_view trim_chars,
+ TrimPositions positions);
+std::string_view TrimString(std::string_view input,
+ std::string_view trim_chars,
+ TrimPositions positions);
+
+// Truncates a string to the nearest UTF-8 character that will leave
+// the string less than or equal to the specified byte size.
+void TruncateUTF8ToByteSize(const std::string& input,
+ const size_t byte_size,
+ std::string* output);
+
+// Trims any whitespace from either end of the input string.
+//
+// The std::string_view versions return a substring referencing the input
+// buffer. The ASCII versions look only for ASCII whitespace.
+//
+// The std::string versions return where whitespace was found.
+// NOTE: Safe to use the same variable for both input and output.
+TrimPositions TrimWhitespace(const std::u16string& input,
+ TrimPositions positions,
+ std::u16string* output);
+std::u16string_view TrimWhitespace(std::u16string_view input,
+ TrimPositions positions);
+TrimPositions TrimWhitespaceASCII(const std::string& input,
+ TrimPositions positions,
+ std::string* output);
+std::string_view TrimWhitespaceASCII(std::string_view input,
+ TrimPositions positions);
+
+// Searches for CR or LF characters. Removes all contiguous whitespace
+// strings that contain them. This is useful when trying to deal with text
+// copied from terminals.
+// Returns |text|, with the following three transformations:
+// (1) Leading and trailing whitespace is trimmed.
+// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
+// sequences containing a CR or LF are trimmed.
+// (3) All other whitespace sequences are converted to single spaces.
+std::u16string CollapseWhitespace(const std::u16string& text,
+ bool trim_sequences_with_line_breaks);
+std::string CollapseWhitespaceASCII(const std::string& text,
+ bool trim_sequences_with_line_breaks);
+
+// Returns true if |input| is empty or contains only characters found in
+// |characters|.
+bool ContainsOnlyChars(std::string_view input, std::string_view characters);
+bool ContainsOnlyChars(std::u16string_view input,
+ std::u16string_view characters);
+
+// Returns true if the specified string matches the criteria. How can a wide
+// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
+// first case) or characters that use only 8-bits and whose 8-bit
+// representation looks like a UTF-8 string (the second case).
+//
+// Note that IsStringUTF8 checks not only if the input is structurally
+// valid but also if it doesn't contain any non-character codepoint
+// (e.g. U+FFFE). It's done on purpose because all the existing callers want
+// to have the maximum 'discriminating' power from other encodings. If
+// there's a use case for just checking the structural validity, we have to
+// add a new function for that.
+//
+// IsStringASCII assumes the input is likely all ASCII, and does not leave early
+// if it is not the case.
+bool IsStringUTF8(std::string_view str);
+bool IsStringASCII(std::string_view str);
+bool IsStringASCII(std::u16string_view str);
+
+// Compare the lower-case form of the given string against the given
+// previously-lower-cased ASCII string (typically a constant).
+bool LowerCaseEqualsASCII(std::string_view str,
+ std::string_view lowecase_ascii);
+bool LowerCaseEqualsASCII(std::u16string_view str,
+ std::string_view lowecase_ascii);
+
+// Performs a case-sensitive string compare of the given 16-bit string against
+// the given 8-bit ASCII string (typically a constant). The behavior is
+// undefined if the |ascii| string is not ASCII.
+bool EqualsASCII(std::u16string_view str, std::string_view ascii);
+
+// Indicates case sensitivity of comparisons. Only ASCII case insensitivity
+// is supported. Full Unicode case-insensitive conversions would need to go in
+// base/i18n so it can use ICU.
+//
+// If you need to do Unicode-aware case-insensitive StartsWith/EndsWith, it's
+// best to call base::i18n::ToLower() or base::i18n::FoldCase() (see
+// base/i18n/case_conversion.h for usage advice) on the arguments, and then use
+// the results to a case-sensitive comparison.
+enum class CompareCase {
+ SENSITIVE,
+ INSENSITIVE_ASCII,
+};
+
+bool StartsWith(std::string_view str,
+ std::string_view search_for,
+ CompareCase case_sensitivity);
+bool StartsWith(std::u16string_view str,
+ std::u16string_view search_for,
+ CompareCase case_sensitivity);
+bool EndsWith(std::string_view str,
+ std::string_view search_for,
+ CompareCase case_sensitivity);
+bool EndsWith(std::u16string_view str,
+ std::u16string_view search_for,
+ CompareCase case_sensitivity);
+
+// Determines the type of ASCII character, independent of locale (the C
+// library versions will change based on locale).
+template <typename Char>
+inline bool IsAsciiWhitespace(Char c) {
+ return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+}
+template <typename Char>
+inline bool IsAsciiAlpha(Char c) {
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+template <typename Char>
+inline bool IsAsciiUpper(Char c) {
+ return c >= 'A' && c <= 'Z';
+}
+template <typename Char>
+inline bool IsAsciiLower(Char c) {
+ return c >= 'a' && c <= 'z';
+}
+template <typename Char>
+inline bool IsAsciiDigit(Char c) {
+ return c >= '0' && c <= '9';
+}
+
+template <typename Char>
+inline bool IsHexDigit(Char c) {
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f');
+}
+
+// Returns the integer corresponding to the given hex character. For example:
+// '4' -> 4
+// 'a' -> 10
+// 'B' -> 11
+// Assumes the input is a valid hex character. DCHECKs in debug builds if not.
+char HexDigitToInt(char16_t c);
+
+// Returns true if it's a Unicode whitespace character.
+bool IsUnicodeWhitespace(char16_t c);
+
+// Return a byte string in human-readable format with a unit suffix. Not
+// appropriate for use in any UI; use of FormatBytes and friends in ui/base is
+// highly recommended instead. TODO(avi): Figure out how to get callers to use
+// FormatBytes instead; remove this.
+std::u16string FormatBytesUnlocalized(int64_t bytes);
+
+// Starting at |start_offset| (usually 0), replace the first instance of
+// |find_this| with |replace_with|.
+void ReplaceFirstSubstringAfterOffset(std::u16string* str,
+ size_t start_offset,
+ std::u16string_view find_this,
+ std::u16string_view replace_with);
+void ReplaceFirstSubstringAfterOffset(std::string* str,
+ size_t start_offset,
+ std::string_view find_this,
+ std::string_view replace_with);
+
+// Starting at |start_offset| (usually 0), look through |str| and replace all
+// instances of |find_this| with |replace_with|.
+//
+// This does entire substrings; use std::replace in <algorithm> for single
+// characters, for example:
+// std::replace(str.begin(), str.end(), 'a', 'b');
+void ReplaceSubstringsAfterOffset(std::u16string* str,
+ size_t start_offset,
+ std::u16string_view find_this,
+ std::u16string_view replace_with);
+void ReplaceSubstringsAfterOffset(std::string* str,
+ size_t start_offset,
+ std::string_view find_this,
+ std::string_view replace_with);
+
+// Reserves enough memory in |str| to accommodate |length_with_null| characters,
+// sets the size of |str| to |length_with_null - 1| characters, and returns a
+// pointer to the underlying contiguous array of characters. This is typically
+// used when calling a function that writes results into a character array, but
+// the caller wants the data to be managed by a string-like object. It is
+// convenient in that is can be used inline in the call, and fast in that it
+// avoids copying the results of the call from a char* into a string.
+//
+// |length_with_null| must be at least 2, since otherwise the underlying string
+// would have size 0, and trying to access &((*str)[0]) in that case can result
+// in a number of problems.
+//
+// Internally, this takes linear time because the resize() call 0-fills the
+// underlying array for potentially all
+// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
+// could avoid this aspect of the resize() call, as we expect the caller to
+// immediately write over this memory, but there is no other way to set the size
+// of the string, and not doing that will mean people who access |str| rather
+// than str.c_str() will get back a string of whatever size |str| had on entry
+// to this function (probably 0).
+char* WriteInto(std::string* str, size_t length_with_null);
+char16_t* WriteInto(std::u16string* str, size_t length_with_null);
+
+// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list
+// of strings into a single string, inserting |separator| (which may be empty)
+// in between all elements.
+//
+// If possible, callers should build a vector of std::string_views and use the
+// std::string_view variant, so that they do not create unnecessary copies of
+// strings. For example, instead of using SplitString, modifying the vector,
+// then using JoinString, use SplitStringPiece followed by JoinString so that no
+// copies of those strings are created until the final join operation.
+//
+// Use StrCat (in base/strings/strcat.h) if you don't need a separator.
+std::string JoinString(const std::vector<std::string>& parts,
+ std::string_view separator);
+std::u16string JoinString(const std::vector<std::u16string>& parts,
+ std::u16string_view separator);
+std::string JoinString(const std::vector<std::string_view>& parts,
+ std::string_view separator);
+std::u16string JoinString(const std::vector<std::u16string_view>& parts,
+ std::u16string_view separator);
+// Explicit initializer_list overloads are required to break ambiguity when used
+// with a literal initializer list (otherwise the compiler would not be able to
+// decide between the string and std::string_view overloads).
+std::string JoinString(std::initializer_list<std::string_view> parts,
+ std::string_view separator);
+std::u16string JoinString(std::initializer_list<std::u16string_view> parts,
+ std::u16string_view separator);
+
+// Replace $1-$2-$3..$9 in the format string with values from |subst|.
+// Additionally, any number of consecutive '$' characters is replaced by that
+// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
+// NULL. This only allows you to use up to nine replacements.
+std::u16string ReplaceStringPlaceholders(
+ const std::u16string& format_string,
+ const std::vector<std::u16string>& subst,
+ std::vector<size_t>* offsets);
+
+std::string ReplaceStringPlaceholders(std::string_view format_string,
+ const std::vector<std::string>& subst,
+ std::vector<size_t>* offsets);
+
+// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
+std::u16string ReplaceStringPlaceholders(const std::u16string& format_string,
+ const std::u16string& a,
+ size_t* offset);
+
+} // namespace base
+
+#if defined(OS_WIN)
+#include "base/strings/string_util_win.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "base/strings/string_util_posix.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
+
+#endif // BASE_STRINGS_STRING_UTIL_H_
diff --git a/gn/src/base/strings/string_util_constants.cc b/gn/src/base/strings/string_util_constants.cc
new file mode 100644
index 00000000000..de8845451f8
--- /dev/null
+++ b/gn/src/base/strings/string_util_constants.cc
@@ -0,0 +1,57 @@
+// 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/strings/string_util.h"
+
+namespace base {
+
+#define WHITESPACE_UNICODE \
+ 0x0009, /* CHARACTER TABULATION */ \
+ 0x000A, /* LINE FEED (LF) */ \
+ 0x000B, /* LINE TABULATION */ \
+ 0x000C, /* FORM FEED (FF) */ \
+ 0x000D, /* CARRIAGE RETURN (CR) */ \
+ 0x0020, /* SPACE */ \
+ 0x0085, /* NEXT LINE (NEL) */ \
+ 0x00A0, /* NO-BREAK SPACE */ \
+ 0x1680, /* OGHAM SPACE MARK */ \
+ 0x2000, /* EN QUAD */ \
+ 0x2001, /* EM QUAD */ \
+ 0x2002, /* EN SPACE */ \
+ 0x2003, /* EM SPACE */ \
+ 0x2004, /* THREE-PER-EM SPACE */ \
+ 0x2005, /* FOUR-PER-EM SPACE */ \
+ 0x2006, /* SIX-PER-EM SPACE */ \
+ 0x2007, /* FIGURE SPACE */ \
+ 0x2008, /* PUNCTUATION SPACE */ \
+ 0x2009, /* THIN SPACE */ \
+ 0x200A, /* HAIR SPACE */ \
+ 0x2028, /* LINE SEPARATOR */ \
+ 0x2029, /* PARAGRAPH SEPARATOR */ \
+ 0x202F, /* NARROW NO-BREAK SPACE */ \
+ 0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
+ 0x3000, /* IDEOGRAPHIC SPACE */ \
+ 0
+
+const char16_t kWhitespaceUTF16[] = {WHITESPACE_UNICODE};
+
+const char kWhitespaceASCII[] = {0x09, // CHARACTER TABULATION
+ 0x0A, // LINE FEED (LF)
+ 0x0B, // LINE TABULATION
+ 0x0C, // FORM FEED (FF)
+ 0x0D, // CARRIAGE RETURN (CR)
+ 0x20, // SPACE
+ 0};
+
+const char16_t kWhitespaceASCIIAs16[] = {0x09, // CHARACTER TABULATION
+ 0x0A, // LINE FEED (LF)
+ 0x0B, // LINE TABULATION
+ 0x0C, // FORM FEED (FF)
+ 0x0D, // CARRIAGE RETURN (CR)
+ 0x20, // SPACE
+ 0};
+
+const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
+
+} // namespace base
diff --git a/gn/src/base/strings/string_util_posix.h b/gn/src/base/strings/string_util_posix.h
new file mode 100644
index 00000000000..83be1dbcdf5
--- /dev/null
+++ b/gn/src/base/strings/string_util_posix.h
@@ -0,0 +1,25 @@
+// 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 BASE_STRINGS_STRING_UTIL_POSIX_H_
+#define BASE_STRINGS_STRING_UTIL_POSIX_H_
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+inline int vsnprintf(char* buffer,
+ size_t size,
+ const char* format,
+ va_list arguments) {
+ return ::vsnprintf(buffer, size, format, arguments);
+}
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_UTIL_POSIX_H_
diff --git a/gn/src/base/strings/string_util_win.h b/gn/src/base/strings/string_util_win.h
new file mode 100644
index 00000000000..717a72cf860
--- /dev/null
+++ b/gn/src/base/strings/string_util_win.h
@@ -0,0 +1,29 @@
+// 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 BASE_STRINGS_STRING_UTIL_WIN_H_
+#define BASE_STRINGS_STRING_UTIL_WIN_H_
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+inline int vsnprintf(char* buffer,
+ size_t size,
+ const char* format,
+ va_list arguments) {
+ int length = vsnprintf_s(buffer, size, size - 1, format, arguments);
+ if (length < 0)
+ return _vscprintf(format, arguments);
+ return length;
+}
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_UTIL_WIN_H_
diff --git a/gn/base/strings/stringize_macros.h b/gn/src/base/strings/stringize_macros.h
index 251c443e95a..251c443e95a 100644
--- a/gn/base/strings/stringize_macros.h
+++ b/gn/src/base/strings/stringize_macros.h
diff --git a/gn/src/base/strings/stringprintf.cc b/gn/src/base/strings/stringprintf.cc
new file mode 100644
index 00000000000..c00bdbc0065
--- /dev/null
+++ b/gn/src/base/strings/stringprintf.cc
@@ -0,0 +1,143 @@
+// 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/strings/stringprintf.h"
+
+#include <errno.h>
+#include <stddef.h>
+
+#include <iterator>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/scoped_clear_errno.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+namespace base {
+
+namespace {
+
+// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
+// is the size of the buffer. These return the number of characters in the
+// formatted string excluding the NUL terminator. If the buffer is not
+// large enough to accommodate the formatted string without truncation, they
+// return the number of characters that would be in the fully-formatted string
+// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms).
+inline int vsnprintfT(char* buffer,
+ size_t buf_size,
+ const char* format,
+ va_list argptr) {
+ return base::vsnprintf(buffer, buf_size, format, argptr);
+}
+
+// Templatized backend for StringPrintF/StringAppendF. This does not finalize
+// the va_list, the caller is expected to do that.
+template <class StringType>
+static void StringAppendVT(StringType* dst,
+ const typename StringType::value_type* format,
+ va_list ap) {
+ // First try with a small fixed size buffer.
+ // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
+ // and StringUtilTest.StringPrintfBounds.
+ typename StringType::value_type stack_buf[1024];
+
+ va_list ap_copy;
+ va_copy(ap_copy, ap);
+
+#if !defined(OS_WIN)
+ ScopedClearErrno clear_errno;
+#endif
+ int result = vsnprintfT(stack_buf, std::size(stack_buf), format, ap_copy);
+ va_end(ap_copy);
+
+ if (result >= 0 && result < static_cast<int>(std::size(stack_buf))) {
+ // It fit.
+ dst->append(stack_buf, result);
+ return;
+ }
+
+ // Repeatedly increase buffer size until it fits.
+ int mem_length = std::size(stack_buf);
+ while (true) {
+ if (result < 0) {
+#if defined(OS_WIN)
+ // On Windows, vsnprintfT always returns the number of characters in a
+ // fully-formatted string, so if we reach this point, something else is
+ // wrong and no amount of buffer-doubling is going to fix it.
+ return;
+#else
+ if (errno != 0 && errno != EOVERFLOW)
+ return;
+ // Try doubling the buffer size.
+ mem_length *= 2;
+#endif
+ } else {
+ // We need exactly "result + 1" characters.
+ mem_length = result + 1;
+ }
+
+ if (mem_length > 32 * 1024 * 1024) {
+ // That should be plenty, don't try anything larger. This protects
+ // against huge allocations when using vsnprintfT implementations that
+ // return -1 for reasons other than overflow without setting errno.
+ DLOG(WARNING) << "Unable to printf the requested string due to size.";
+ return;
+ }
+
+ std::vector<typename StringType::value_type> mem_buf(mem_length);
+
+ // NOTE: You can only use a va_list once. Since we're in a while loop, we
+ // need to make a new copy each time so we don't use up the original.
+ va_copy(ap_copy, ap);
+ result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
+ va_end(ap_copy);
+
+ if ((result >= 0) && (result < mem_length)) {
+ // It fit.
+ dst->append(&mem_buf[0], result);
+ return;
+ }
+ }
+}
+
+} // namespace
+
+std::string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+std::string StringPrintV(const char* format, va_list ap) {
+ std::string result;
+ StringAppendV(&result, format, ap);
+ return result;
+}
+
+const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
+
+} // namespace base
diff --git a/gn/src/base/strings/stringprintf.h b/gn/src/base/strings/stringprintf.h
new file mode 100644
index 00000000000..e6e07467474
--- /dev/null
+++ b/gn/src/base/strings/stringprintf.h
@@ -0,0 +1,42 @@
+// 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 BASE_STRINGS_STRINGPRINTF_H_
+#define BASE_STRINGS_STRINGPRINTF_H_
+
+#include <stdarg.h> // va_list
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "util/build_config.h"
+
+namespace base {
+
+// Return a C++ string given printf-like input.
+std::string StringPrintf(_Printf_format_string_ const char* format, ...)
+ PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
+
+// Return a C++ string given vprintf-like input.
+std::string StringPrintV(const char* format, va_list ap)
+ PRINTF_FORMAT(1, 0) WARN_UNUSED_RESULT;
+
+// Store result into a supplied string and return it.
+const std::string& SStringPrintf(std::string* dst,
+ _Printf_format_string_ const char* format,
+ ...) PRINTF_FORMAT(2, 3);
+
+// Append result to a supplied string.
+void StringAppendF(std::string* dst,
+ _Printf_format_string_ const char* format,
+ ...) PRINTF_FORMAT(2, 3);
+
+// Lower-level routine that takes a va_list and appends to a specified
+// string. All other routines are just convenience wrappers around it.
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+ PRINTF_FORMAT(2, 0);
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRINGPRINTF_H_
diff --git a/gn/src/base/strings/utf_offset_string_conversions.cc b/gn/src/base/strings/utf_offset_string_conversions.cc
new file mode 100644
index 00000000000..ee55a244dfc
--- /dev/null
+++ b/gn/src/base/strings/utf_offset_string_conversions.cc
@@ -0,0 +1,266 @@
+// 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.
+
+#include "base/strings/utf_offset_string_conversions.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversion_utils.h"
+
+namespace base {
+
+OffsetAdjuster::Adjustment::Adjustment(size_t original_offset,
+ size_t original_length,
+ size_t output_length)
+ : original_offset(original_offset),
+ original_length(original_length),
+ output_length(output_length) {}
+
+// static
+void OffsetAdjuster::AdjustOffsets(const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_adjustment,
+ size_t limit) {
+ DCHECK(offsets_for_adjustment);
+ for (std::vector<size_t>::iterator i(offsets_for_adjustment->begin());
+ i != offsets_for_adjustment->end(); ++i)
+ AdjustOffset(adjustments, &(*i), limit);
+}
+
+// static
+void OffsetAdjuster::AdjustOffset(const Adjustments& adjustments,
+ size_t* offset,
+ size_t limit) {
+ DCHECK(offset);
+ if (*offset == std::u16string::npos)
+ return;
+ int adjustment = 0;
+ for (Adjustments::const_iterator i = adjustments.begin();
+ i != adjustments.end(); ++i) {
+ if (*offset <= i->original_offset)
+ break;
+ if (*offset < (i->original_offset + i->original_length)) {
+ *offset = std::u16string::npos;
+ return;
+ }
+ adjustment += static_cast<int>(i->original_length - i->output_length);
+ }
+ *offset -= adjustment;
+
+ if (*offset > limit)
+ *offset = std::u16string::npos;
+}
+
+// static
+void OffsetAdjuster::UnadjustOffsets(
+ const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_unadjustment) {
+ if (!offsets_for_unadjustment || adjustments.empty())
+ return;
+ for (std::vector<size_t>::iterator i(offsets_for_unadjustment->begin());
+ i != offsets_for_unadjustment->end(); ++i)
+ UnadjustOffset(adjustments, &(*i));
+}
+
+// static
+void OffsetAdjuster::UnadjustOffset(const Adjustments& adjustments,
+ size_t* offset) {
+ if (*offset == std::u16string::npos)
+ return;
+ int adjustment = 0;
+ for (Adjustments::const_iterator i = adjustments.begin();
+ i != adjustments.end(); ++i) {
+ if (*offset + adjustment <= i->original_offset)
+ break;
+ adjustment += static_cast<int>(i->original_length - i->output_length);
+ if ((*offset + adjustment) < (i->original_offset + i->original_length)) {
+ *offset = std::u16string::npos;
+ return;
+ }
+ }
+ *offset += adjustment;
+}
+
+// static
+void OffsetAdjuster::MergeSequentialAdjustments(
+ const Adjustments& first_adjustments,
+ Adjustments* adjustments_on_adjusted_string) {
+ Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin();
+ Adjustments::const_iterator first_iter = first_adjustments.begin();
+ // Simultaneously iterate over all |adjustments_on_adjusted_string| and
+ // |first_adjustments|, adding adjustments to or correcting the adjustments
+ // in |adjustments_on_adjusted_string| as we go. |shift| keeps track of the
+ // current number of characters collapsed by |first_adjustments| up to this
+ // point. |currently_collapsing| keeps track of the number of characters
+ // collapsed by |first_adjustments| into the current |adjusted_iter|'s
+ // length. These are characters that will change |shift| as soon as we're
+ // done processing the current |adjusted_iter|; they are not yet reflected in
+ // |shift|.
+ size_t shift = 0;
+ size_t currently_collapsing = 0;
+ while (adjusted_iter != adjustments_on_adjusted_string->end()) {
+ if ((first_iter == first_adjustments.end()) ||
+ ((adjusted_iter->original_offset + shift +
+ adjusted_iter->original_length) <= first_iter->original_offset)) {
+ // Entire |adjusted_iter| (accounting for its shift and including its
+ // whole original length) comes before |first_iter|.
+ //
+ // Correct the offset at |adjusted_iter| and move onto the next
+ // adjustment that needs revising.
+ adjusted_iter->original_offset += shift;
+ shift += currently_collapsing;
+ currently_collapsing = 0;
+ ++adjusted_iter;
+ } else if ((adjusted_iter->original_offset + shift) >
+ first_iter->original_offset) {
+ // |first_iter| comes before the |adjusted_iter| (as adjusted by |shift|).
+
+ // It's not possible for the adjustments to overlap. (It shouldn't
+ // be possible that we have an |adjusted_iter->original_offset| that,
+ // when adjusted by the computed |shift|, is in the middle of
+ // |first_iter|'s output's length. After all, that would mean the
+ // current adjustment_on_adjusted_string somehow points to an offset
+ // that was supposed to have been eliminated by the first set of
+ // adjustments.)
+ DCHECK_LE(first_iter->original_offset + first_iter->output_length,
+ adjusted_iter->original_offset + shift);
+
+ // Add the |first_adjustment_iter| to the full set of adjustments while
+ // making sure |adjusted_iter| continues pointing to the same element.
+ // We do this by inserting the |first_adjustment_iter| right before
+ // |adjusted_iter|, then incrementing |adjusted_iter| so it points to
+ // the following element.
+ shift += first_iter->original_length - first_iter->output_length;
+ adjusted_iter =
+ adjustments_on_adjusted_string->insert(adjusted_iter, *first_iter);
+ ++adjusted_iter;
+ ++first_iter;
+ } else {
+ // The first adjustment adjusted something that then got further adjusted
+ // by the second set of adjustments. In other words, |first_iter| points
+ // to something in the range covered by |adjusted_iter|'s length (after
+ // accounting for |shift|). Precisely,
+ // adjusted_iter->original_offset + shift
+ // <=
+ // first_iter->original_offset
+ // <=
+ // adjusted_iter->original_offset + shift +
+ // adjusted_iter->original_length
+
+ // Modify the current |adjusted_iter| to include whatever collapsing
+ // happened in |first_iter|, then advance to the next |first_adjustments|
+ // because we dealt with the current one.
+ const int collapse = static_cast<int>(first_iter->original_length) -
+ static_cast<int>(first_iter->output_length);
+ // This function does not know how to deal with a string that expands and
+ // then gets modified, only strings that collapse and then get modified.
+ DCHECK_GT(collapse, 0);
+ adjusted_iter->original_length += collapse;
+ currently_collapsing += collapse;
+ ++first_iter;
+ }
+ }
+ DCHECK_EQ(0u, currently_collapsing);
+ if (first_iter != first_adjustments.end()) {
+ // Only first adjustments are left. These do not need to be modified.
+ // (Their offsets are already correct with respect to the original string.)
+ // Append them all.
+ DCHECK(adjusted_iter == adjustments_on_adjusted_string->end());
+ adjustments_on_adjusted_string->insert(
+ adjustments_on_adjusted_string->end(), first_iter,
+ first_adjustments.end());
+ }
+}
+
+// Converts the given source Unicode character type to the given destination
+// Unicode character type as a STL string. The given input buffer and size
+// determine the source, and the given output STL string will be replaced by
+// the result. If non-NULL, |adjustments| is set to reflect the all the
+// alterations to the string that are not one-character-to-one-character.
+// It will always be sorted by increasing offset.
+template <typename SrcChar, typename DestStdString>
+bool ConvertUnicode(const SrcChar* src,
+ size_t src_len,
+ DestStdString* output,
+ OffsetAdjuster::Adjustments* adjustments) {
+ if (adjustments)
+ adjustments->clear();
+ // ICU requires 32-bit numbers.
+ bool success = true;
+ int32_t src_len32 = static_cast<int32_t>(src_len);
+ for (int32_t i = 0; i < src_len32; i++) {
+ uint32_t code_point;
+ size_t original_i = i;
+ size_t chars_written = 0;
+ if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
+ chars_written = WriteUnicodeCharacter(code_point, output);
+ } else {
+ chars_written = WriteUnicodeCharacter(0xFFFD, output);
+ success = false;
+ }
+
+ // Only bother writing an adjustment if this modification changed the
+ // length of this character.
+ // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last
+ // character read, not after it (so that incrementing it in the loop
+ // increment will place it at the right location), so we need to account
+ // for that in determining the amount that was read.
+ if (adjustments && ((i - original_i + 1) != chars_written)) {
+ adjustments->push_back(OffsetAdjuster::Adjustment(
+ original_i, i - original_i + 1, chars_written));
+ }
+ }
+ return success;
+}
+
+bool UTF8ToUTF16WithAdjustments(
+ const char* src,
+ size_t src_len,
+ std::u16string* output,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ PrepareForUTF16Or32Output(src, src_len, output);
+ return ConvertUnicode(src, src_len, output, adjustments);
+}
+
+std::u16string UTF8ToUTF16WithAdjustments(
+ const std::string_view& utf8,
+ base::OffsetAdjuster::Adjustments* adjustments) {
+ std::u16string result;
+ UTF8ToUTF16WithAdjustments(utf8.data(), utf8.length(), &result, adjustments);
+ return result;
+}
+
+std::u16string UTF8ToUTF16AndAdjustOffsets(
+ const std::string_view& utf8,
+ std::vector<size_t>* offsets_for_adjustment) {
+ for (size_t& offset : *offsets_for_adjustment) {
+ if (offset > utf8.length())
+ offset = std::u16string::npos;
+ }
+ OffsetAdjuster::Adjustments adjustments;
+ std::u16string result = UTF8ToUTF16WithAdjustments(utf8, &adjustments);
+ OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
+ return result;
+}
+
+std::string UTF16ToUTF8AndAdjustOffsets(
+ const std::u16string_view& utf16,
+ std::vector<size_t>* offsets_for_adjustment) {
+ for (size_t& offset : *offsets_for_adjustment) {
+ if (offset > utf16.length())
+ offset = std::u16string::npos;
+ }
+ std::string result;
+ PrepareForUTF8Output(utf16.data(), utf16.length(), &result);
+ OffsetAdjuster::Adjustments adjustments;
+ ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments);
+ OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
+ return result;
+}
+
+} // namespace base
diff --git a/gn/src/base/strings/utf_offset_string_conversions.h b/gn/src/base/strings/utf_offset_string_conversions.h
new file mode 100644
index 00000000000..8d6c5e80a8c
--- /dev/null
+++ b/gn/src/base/strings/utf_offset_string_conversions.h
@@ -0,0 +1,111 @@
+// 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.
+
+#ifndef BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace base {
+
+// A helper class and associated data structures to adjust offsets into a
+// string in response to various adjustments one might do to that string
+// (e.g., eliminating a range). For details on offsets, see the comments by
+// the AdjustOffsets() function below.
+class OffsetAdjuster {
+ public:
+ struct Adjustment {
+ Adjustment(size_t original_offset,
+ size_t original_length,
+ size_t output_length);
+
+ size_t original_offset;
+ size_t original_length;
+ size_t output_length;
+ };
+ typedef std::vector<Adjustment> Adjustments;
+
+ // Adjusts all offsets in |offsets_for_adjustment| to reflect the adjustments
+ // recorded in |adjustments|. Adjusted offsets greater than |limit| will be
+ // set to std::u16string::npos.
+ //
+ // Offsets represents insertion/selection points between characters: if |src|
+ // is "abcd", then 0 is before 'a', 2 is between 'b' and 'c', and 4 is at the
+ // end of the string. Valid input offsets range from 0 to |src_len|. On
+ // exit, each offset will have been modified to point at the same logical
+ // position in the output string. If an offset cannot be successfully
+ // adjusted (e.g., because it points into the middle of a multibyte sequence),
+ // it will be set to std::u16string::npos.
+ static void AdjustOffsets(const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_adjustment,
+ size_t limit = std::u16string::npos);
+
+ // Adjusts the single |offset| to reflect the adjustments recorded in
+ // |adjustments|.
+ static void AdjustOffset(const Adjustments& adjustments,
+ size_t* offset,
+ size_t limit = std::u16string::npos);
+
+ // Adjusts all offsets in |offsets_for_unadjustment| to reflect the reverse
+ // of the adjustments recorded in |adjustments|. In other words, the offsets
+ // provided represent offsets into an adjusted string and the caller wants
+ // to know the offsets they correspond to in the original string. If an
+ // offset cannot be successfully unadjusted (e.g., because it points into
+ // the middle of a multibyte sequence), it will be set to
+ // std::u16string::npos.
+ static void UnadjustOffsets(const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_unadjustment);
+
+ // Adjusts the single |offset| to reflect the reverse of the adjustments
+ // recorded in |adjustments|.
+ static void UnadjustOffset(const Adjustments& adjustments, size_t* offset);
+
+ // Combines two sequential sets of adjustments, storing the combined revised
+ // adjustments in |adjustments_on_adjusted_string|. That is, suppose a
+ // string was altered in some way, with the alterations recorded as
+ // adjustments in |first_adjustments|. Then suppose the resulting string is
+ // further altered, with the alterations recorded as adjustments scored in
+ // |adjustments_on_adjusted_string|, with the offsets recorded in these
+ // adjustments being with respect to the intermediate string. This function
+ // combines the two sets of adjustments into one, storing the result in
+ // |adjustments_on_adjusted_string|, whose offsets are correct with respect
+ // to the original string.
+ //
+ // Assumes both parameters are sorted by increasing offset.
+ //
+ // WARNING: Only supports |first_adjustments| that involve collapsing ranges
+ // of text, not expanding ranges.
+ static void MergeSequentialAdjustments(
+ const Adjustments& first_adjustments,
+ Adjustments* adjustments_on_adjusted_string);
+};
+
+// Like the conversions in utf_string_conversions.h, but also fills in an
+// |adjustments| parameter that reflects the alterations done to the string.
+// It may be NULL.
+bool UTF8ToUTF16WithAdjustments(const char* src,
+ size_t src_len,
+ std::u16string* output,
+ base::OffsetAdjuster::Adjustments* adjustments);
+std::u16string UTF8ToUTF16WithAdjustments(
+ const std::string_view& utf8,
+ base::OffsetAdjuster::Adjustments* adjustments);
+// As above, but instead internally examines the adjustments and applies them
+// to |offsets_for_adjustment|. Input offsets greater than the length of the
+// input string will be set to std::u16string::npos. See comments by
+// AdjustOffsets().
+std::u16string UTF8ToUTF16AndAdjustOffsets(
+ const std::string_view& utf8,
+ std::vector<size_t>* offsets_for_adjustment);
+std::string UTF16ToUTF8AndAdjustOffsets(
+ const std::u16string_view& utf16,
+ std::vector<size_t>* offsets_for_adjustment);
+
+} // namespace base
+
+#endif // BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
diff --git a/gn/src/base/strings/utf_string_conversion_utils.cc b/gn/src/base/strings/utf_string_conversion_utils.cc
new file mode 100644
index 00000000000..32aa3e3cd28
--- /dev/null
+++ b/gn/src/base/strings/utf_string_conversion_utils.cc
@@ -0,0 +1,132 @@
+// 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.
+
+#include "base/strings/utf_string_conversion_utils.h"
+
+#include "base/third_party/icu/icu_utf.h"
+#include "util/build_config.h"
+
+namespace base {
+
+// ReadUnicodeCharacter --------------------------------------------------------
+
+bool ReadUnicodeCharacter(const char* src,
+ int32_t src_len,
+ int32_t* char_index,
+ uint32_t* code_point_out) {
+ // U8_NEXT expects to be able to use -1 to signal an error, so we must
+ // use a signed type for code_point. But this function returns false
+ // on error anyway, so code_point_out is unsigned.
+ int32_t code_point;
+ CBU8_NEXT(src, *char_index, src_len, code_point);
+ *code_point_out = static_cast<uint32_t>(code_point);
+
+ // The ICU macro above moves to the next char, we want to point to the last
+ // char consumed.
+ (*char_index)--;
+
+ // Validate the decoded value.
+ return IsValidCodepoint(code_point);
+}
+
+bool ReadUnicodeCharacter(const char16_t* src,
+ int32_t src_len,
+ int32_t* char_index,
+ uint32_t* code_point) {
+ if (CBU16_IS_SURROGATE(src[*char_index])) {
+ if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) ||
+ *char_index + 1 >= src_len || !CBU16_IS_TRAIL(src[*char_index + 1])) {
+ // Invalid surrogate pair.
+ return false;
+ }
+
+ // Valid surrogate pair.
+ *code_point =
+ CBU16_GET_SUPPLEMENTARY(src[*char_index], src[*char_index + 1]);
+ (*char_index)++;
+ } else {
+ // Not a surrogate, just one 16-bit word.
+ *code_point = src[*char_index];
+ }
+
+ return IsValidCodepoint(*code_point);
+}
+
+// WriteUnicodeCharacter -------------------------------------------------------
+
+size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output) {
+ if (code_point <= 0x7f) {
+ // Fast path the common case of one byte.
+ output->push_back(static_cast<char>(code_point));
+ return 1;
+ }
+
+ // CBU8_APPEND_UNSAFE can append up to 4 bytes.
+ size_t char_offset = output->length();
+ size_t original_char_offset = char_offset;
+ output->resize(char_offset + CBU8_MAX_LENGTH);
+
+ CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+
+ // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so
+ // it will represent the new length of the string.
+ output->resize(char_offset);
+ return char_offset - original_char_offset;
+}
+
+size_t WriteUnicodeCharacter(uint32_t code_point, std::u16string* output) {
+ if (CBU16_LENGTH(code_point) == 1) {
+ // Thie code point is in the Basic Multilingual Plane (BMP).
+ output->push_back(static_cast<char16_t>(code_point));
+ return 1;
+ }
+ // Non-BMP characters use a double-character encoding.
+ size_t char_offset = output->length();
+ output->resize(char_offset + CBU16_MAX_LENGTH);
+ CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+ return CBU16_MAX_LENGTH;
+}
+
+// Generalized Unicode converter -----------------------------------------------
+
+template <typename CHAR>
+void PrepareForUTF8Output(const CHAR* src,
+ size_t src_len,
+ std::string* output) {
+ output->clear();
+ if (src_len == 0)
+ return;
+ if (src[0] < 0x80) {
+ // Assume that the entire input will be ASCII.
+ output->reserve(src_len);
+ } else {
+ // Assume that the entire input is non-ASCII and will have 3 bytes per char.
+ output->reserve(src_len * 3);
+ }
+}
+
+// Instantiate versions we know callers will need.
+template void PrepareForUTF8Output(const char16_t*, size_t, std::string*);
+
+template <typename STRING>
+void PrepareForUTF16Or32Output(const char* src,
+ size_t src_len,
+ STRING* output) {
+ output->clear();
+ if (src_len == 0)
+ return;
+ if (static_cast<unsigned char>(src[0]) < 0x80) {
+ // Assume the input is all ASCII, which means 1:1 correspondence.
+ output->reserve(src_len);
+ } else {
+ // Otherwise assume that the UTF-8 sequences will have 2 bytes for each
+ // character.
+ output->reserve(src_len / 2);
+ }
+}
+
+// Instantiate versions we know callers will need.
+template void PrepareForUTF16Or32Output(const char*, size_t, std::u16string*);
+
+} // namespace base
diff --git a/gn/src/base/strings/utf_string_conversion_utils.h b/gn/src/base/strings/utf_string_conversion_utils.h
new file mode 100644
index 00000000000..e24a7db93eb
--- /dev/null
+++ b/gn/src/base/strings/utf_string_conversion_utils.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
+#define BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
+
+// Low-level UTF handling functions. Most code will want to use the functions
+// in utf_string_conversions.h
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+namespace base {
+
+inline bool IsValidCodepoint(uint32_t code_point) {
+ // Excludes the surrogate code points ([0xD800, 0xDFFF]) and
+ // codepoints larger than 0x10FFFF (the highest codepoint allowed).
+ // Non-characters and unassigned codepoints are allowed.
+ return code_point < 0xD800u ||
+ (code_point >= 0xE000u && code_point <= 0x10FFFFu);
+}
+
+inline bool IsValidCharacter(uint32_t code_point) {
+ // Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in
+ // 0xFFFE or 0xFFFF) from the set of valid code points.
+ return code_point < 0xD800u ||
+ (code_point >= 0xE000u && code_point < 0xFDD0u) ||
+ (code_point > 0xFDEFu && code_point <= 0x10FFFFu &&
+ (code_point & 0xFFFEu) != 0xFFFEu);
+}
+
+// ReadUnicodeCharacter --------------------------------------------------------
+
+// Reads a UTF-8 stream, placing the next code point into the given output
+// |*code_point|. |src| represents the entire string to read, and |*char_index|
+// is the character offset within the string to start reading at. |*char_index|
+// will be updated to index the last character read, such that incrementing it
+// (as in a for loop) will take the reader to the next character.
+//
+// Returns true on success. On false, |*code_point| will be invalid.
+bool ReadUnicodeCharacter(const char* src,
+ int32_t src_len,
+ int32_t* char_index,
+ uint32_t* code_point_out);
+
+// Reads a UTF-16 character. The usage is the same as the 8-bit version above.
+bool ReadUnicodeCharacter(const char16_t* src,
+ int32_t src_len,
+ int32_t* char_index,
+ uint32_t* code_point);
+
+// WriteUnicodeCharacter -------------------------------------------------------
+
+// Appends a UTF-8 character to the given 8-bit string. Returns the number of
+// bytes written.
+size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output);
+
+// Appends the given code point as a UTF-16 character to the given 16-bit
+// string. Returns the number of 16-bit values written.
+size_t WriteUnicodeCharacter(uint32_t code_point, std::u16string* output);
+
+// Generalized Unicode converter -----------------------------------------------
+
+// Guesses the length of the output in UTF-8 in bytes, clears that output
+// string, and reserves that amount of space. We assume that the input
+// character types are unsigned, which will be true for UTF-16 and -32 on our
+// systems.
+template <typename CHAR>
+void PrepareForUTF8Output(const CHAR* src, size_t src_len, std::string* output);
+
+// Prepares an output buffer (containing either UTF-16 or -32 data) given some
+// UTF-8 input that will be converted to it. See PrepareForUTF8Output().
+template <typename STRING>
+void PrepareForUTF16Or32Output(const char* src, size_t src_len, STRING* output);
+
+} // namespace base
+
+#endif // BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
diff --git a/gn/src/base/strings/utf_string_conversions.cc b/gn/src/base/strings/utf_string_conversions.cc
new file mode 100644
index 00000000000..4b4e4208f87
--- /dev/null
+++ b/gn/src/base/strings/utf_string_conversions.cc
@@ -0,0 +1,195 @@
+// Copyright (c) 2018 The Chromium 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/strings/utf_string_conversions.h"
+
+#include <stdint.h>
+
+#include <string_view>
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "util/build_config.h"
+
+namespace base {
+
+namespace {
+
+constexpr int32_t kErrorCodePoint = 0xFFFD;
+
+// Size coefficient ----------------------------------------------------------
+// The maximum number of codeunits in the destination encoding corresponding to
+// one codeunit in the source encoding.
+
+template <typename SrcChar, typename DestChar>
+struct SizeCoefficient {
+ static_assert(sizeof(SrcChar) < sizeof(DestChar),
+ "Default case: from a smaller encoding to the bigger one");
+
+ // ASCII symbols are encoded by one codeunit in all encodings.
+ static constexpr int value = 1;
+};
+
+template <>
+struct SizeCoefficient<char16_t, char> {
+ // One UTF-16 codeunit corresponds to at most 3 codeunits in UTF-8.
+ static constexpr int value = 3;
+};
+
+template <typename SrcChar, typename DestChar>
+constexpr int size_coefficient_v =
+ SizeCoefficient<std::decay_t<SrcChar>, std::decay_t<DestChar>>::value;
+
+// UnicodeAppendUnsafe --------------------------------------------------------
+// Function overloads that write code_point to the output string. Output string
+// has to have enough space for the codepoint.
+
+void UnicodeAppendUnsafe(char* out, int32_t* size, uint32_t code_point) {
+ CBU8_APPEND_UNSAFE(out, *size, code_point);
+}
+
+void UnicodeAppendUnsafe(char16_t* out, int32_t* size, uint32_t code_point) {
+ CBU16_APPEND_UNSAFE(out, *size, code_point);
+}
+
+// DoUTFConversion ------------------------------------------------------------
+// Main driver of UTFConversion specialized for different Src encodings.
+// dest has to have enough room for the converted text.
+
+template <typename DestChar>
+bool DoUTFConversion(const char* src,
+ int32_t src_len,
+ DestChar* dest,
+ int32_t* dest_len) {
+ bool success = true;
+
+ for (int32_t i = 0; i < src_len;) {
+ int32_t code_point;
+ CBU8_NEXT(src, i, src_len, code_point);
+
+ if (!IsValidCodepoint(code_point)) {
+ success = false;
+ code_point = kErrorCodePoint;
+ }
+
+ UnicodeAppendUnsafe(dest, dest_len, code_point);
+ }
+
+ return success;
+}
+
+template <typename DestChar>
+bool DoUTFConversion(const char16_t* src,
+ int32_t src_len,
+ DestChar* dest,
+ int32_t* dest_len) {
+ bool success = true;
+
+ auto ConvertSingleChar = [&success](char16_t in) -> int32_t {
+ if (!CBU16_IS_SINGLE(in) || !IsValidCodepoint(in)) {
+ success = false;
+ return kErrorCodePoint;
+ }
+ return in;
+ };
+
+ int32_t i = 0;
+
+ // Always have another symbol in order to avoid checking boundaries in the
+ // middle of the surrogate pair.
+ while (i < src_len - 1) {
+ int32_t code_point;
+
+ if (CBU16_IS_LEAD(src[i]) && CBU16_IS_TRAIL(src[i + 1])) {
+ code_point = CBU16_GET_SUPPLEMENTARY(src[i], src[i + 1]);
+ if (!IsValidCodepoint(code_point)) {
+ code_point = kErrorCodePoint;
+ success = false;
+ }
+ i += 2;
+ } else {
+ code_point = ConvertSingleChar(src[i]);
+ ++i;
+ }
+
+ UnicodeAppendUnsafe(dest, dest_len, code_point);
+ }
+
+ if (i < src_len)
+ UnicodeAppendUnsafe(dest, dest_len, ConvertSingleChar(src[i]));
+
+ return success;
+}
+
+// UTFConversion --------------------------------------------------------------
+// Function template for generating all UTF conversions.
+
+template <typename InputString, typename DestString>
+bool UTFConversion(const InputString& src_str, DestString* dest_str) {
+ if (IsStringASCII(src_str)) {
+ dest_str->assign(src_str.begin(), src_str.end());
+ return true;
+ }
+
+ dest_str->resize(src_str.length() *
+ size_coefficient_v<typename InputString::value_type,
+ typename DestString::value_type>);
+
+ // Empty string is ASCII => it OK to call operator[].
+ auto* dest = &(*dest_str)[0];
+
+ // ICU requires 32 bit numbers.
+ int32_t src_len32 = static_cast<int32_t>(src_str.length());
+ int32_t dest_len32 = 0;
+
+ bool res = DoUTFConversion(src_str.data(), src_len32, dest, &dest_len32);
+
+ dest_str->resize(dest_len32);
+ dest_str->shrink_to_fit();
+
+ return res;
+}
+
+} // namespace
+
+// UTF16 <-> UTF8 --------------------------------------------------------------
+
+bool UTF8ToUTF16(const char* src, size_t src_len, std::u16string* output) {
+ return UTFConversion(std::string_view(src, src_len), output);
+}
+
+std::u16string UTF8ToUTF16(std::string_view utf8) {
+ std::u16string ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF8ToUTF16(utf8.data(), utf8.size(), &ret);
+ return ret;
+}
+
+bool UTF16ToUTF8(const char16_t* src, size_t src_len, std::string* output) {
+ return UTFConversion(std::u16string_view(src, src_len), output);
+}
+
+std::string UTF16ToUTF8(std::u16string_view utf16) {
+ std::string ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF16ToUTF8(utf16.data(), utf16.length(), &ret);
+ return ret;
+}
+
+// ASCII <-> UTF-16 -----------------------------------------------------------
+
+std::u16string ASCIIToUTF16(std::string_view ascii) {
+ DCHECK(IsStringASCII(ascii)) << ascii;
+ return std::u16string(ascii.begin(), ascii.end());
+}
+
+std::string UTF16ToASCII(std::u16string_view utf16) {
+ DCHECK(IsStringASCII(utf16)) << UTF16ToUTF8(utf16);
+ return std::string(utf16.begin(), utf16.end());
+}
+
+} // namespace base
diff --git a/gn/src/base/strings/utf_string_conversions.h b/gn/src/base/strings/utf_string_conversions.h
new file mode 100644
index 00000000000..116e6a560b2
--- /dev/null
+++ b/gn/src/base/strings/utf_string_conversions.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <string_view>
+
+namespace base {
+
+bool UTF8ToUTF16(const char* src, size_t src_len, std::u16string* output);
+std::u16string UTF8ToUTF16(std::string_view utf8);
+bool UTF16ToUTF8(const char16_t* src, size_t src_len, std::string* output);
+std::string UTF16ToUTF8(std::u16string_view utf16);
+
+// This converts an ASCII string, typically a hardcoded constant, to a UTF16
+// string.
+std::u16string ASCIIToUTF16(std::string_view ascii);
+
+// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
+// beforehand.
+std::string UTF16ToASCII(std::u16string_view utf16);
+
+} // namespace base
+
+#endif // BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
diff --git a/gn/base/sys_byteorder.h b/gn/src/base/sys_byteorder.h
index bb7b4c3f95c..bb7b4c3f95c 100644
--- a/gn/base/sys_byteorder.h
+++ b/gn/src/base/sys_byteorder.h
diff --git a/gn/src/base/template_util.h b/gn/src/base/template_util.h
new file mode 100644
index 00000000000..41028c4485d
--- /dev/null
+++ b/gn/src/base/template_util.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef BASE_TEMPLATE_UTIL_H_
+#define BASE_TEMPLATE_UTIL_H_
+
+#include <iosfwd>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+namespace base {
+
+namespace internal {
+
+// Uses expression SFINAE to detect whether using operator<< would work.
+template <typename T, typename = void>
+struct SupportsOstreamOperator : std::false_type {};
+template <typename T>
+struct SupportsOstreamOperator<T,
+ decltype(void(std::declval<std::ostream&>()
+ << std::declval<T>()))>
+ : std::true_type {};
+
+// Used to detech whether the given type is an iterator. This is normally used
+// with std::enable_if to provide disambiguation for functions that take
+// templatzed iterators as input.
+template <typename T, typename = void>
+struct is_iterator : std::false_type {};
+
+template <typename T>
+struct is_iterator<
+ T,
+ std::void_t<typename std::iterator_traits<T>::iterator_category>>
+ : std::true_type {};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_TEMPLATE_UTIL_H_
diff --git a/gn/base/third_party/icu/LICENSE b/gn/src/base/third_party/icu/LICENSE
index 2882e4ebda9..2882e4ebda9 100644
--- a/gn/base/third_party/icu/LICENSE
+++ b/gn/src/base/third_party/icu/LICENSE
diff --git a/gn/base/third_party/icu/README.chromium b/gn/src/base/third_party/icu/README.chromium
index 297e89a2edd..297e89a2edd 100644
--- a/gn/base/third_party/icu/README.chromium
+++ b/gn/src/base/third_party/icu/README.chromium
diff --git a/gn/base/third_party/icu/icu_utf.cc b/gn/src/base/third_party/icu/icu_utf.cc
index 8dcb401fd9a..8dcb401fd9a 100644
--- a/gn/base/third_party/icu/icu_utf.cc
+++ b/gn/src/base/third_party/icu/icu_utf.cc
diff --git a/gn/base/third_party/icu/icu_utf.h b/gn/src/base/third_party/icu/icu_utf.h
index b626b398404..b626b398404 100644
--- a/gn/base/third_party/icu/icu_utf.h
+++ b/gn/src/base/third_party/icu/icu_utf.h
diff --git a/gn/base/timer/elapsed_timer.cc b/gn/src/base/timer/elapsed_timer.cc
index 68992f4e0a2..68992f4e0a2 100644
--- a/gn/base/timer/elapsed_timer.cc
+++ b/gn/src/base/timer/elapsed_timer.cc
diff --git a/gn/base/timer/elapsed_timer.h b/gn/src/base/timer/elapsed_timer.h
index fbbc83d970a..fbbc83d970a 100644
--- a/gn/base/timer/elapsed_timer.h
+++ b/gn/src/base/timer/elapsed_timer.h
diff --git a/gn/base/value_iterators.cc b/gn/src/base/value_iterators.cc
index ba9c73072f2..ba9c73072f2 100644
--- a/gn/base/value_iterators.cc
+++ b/gn/src/base/value_iterators.cc
diff --git a/gn/base/value_iterators.h b/gn/src/base/value_iterators.h
index 4c814c5b1b2..4c814c5b1b2 100644
--- a/gn/base/value_iterators.h
+++ b/gn/src/base/value_iterators.h
diff --git a/gn/src/base/values.cc b/gn/src/base/values.cc
new file mode 100644
index 00000000000..25b10e334cf
--- /dev/null
+++ b/gn/src/base/values.cc
@@ -0,0 +1,1309 @@
+// 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 "base/values.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <new>
+#include <ostream>
+#include <utility>
+
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace base {
+
+namespace {
+
+const char* const kTypeNames[] = {"null", "boolean", "integer", "string",
+ "binary", "dictionary", "list"};
+static_assert(std::size(kTypeNames) ==
+ static_cast<size_t>(Value::Type::LIST) + 1,
+ "kTypeNames Has Wrong Size");
+
+std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node);
+
+// Make a deep copy of |node|, but don't include empty lists or dictionaries
+// in the copy. It's possible for this function to return NULL and it
+// expects |node| to always be non-NULL.
+std::unique_ptr<Value> CopyListWithoutEmptyChildren(const Value& list) {
+ Value copy(Value::Type::LIST);
+ for (const auto& entry : list.GetList()) {
+ std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(entry);
+ if (child_copy)
+ copy.GetList().push_back(std::move(*child_copy));
+ }
+ return copy.GetList().empty() ? nullptr
+ : std::make_unique<Value>(std::move(copy));
+}
+
+std::unique_ptr<DictionaryValue> CopyDictionaryWithoutEmptyChildren(
+ const DictionaryValue& dict) {
+ std::unique_ptr<DictionaryValue> copy;
+ for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+ std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(it.value());
+ if (child_copy) {
+ if (!copy)
+ copy = std::make_unique<DictionaryValue>();
+ copy->SetWithoutPathExpansion(it.key(), std::move(child_copy));
+ }
+ }
+ return copy;
+}
+
+std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node) {
+ switch (node.type()) {
+ case Value::Type::LIST:
+ return CopyListWithoutEmptyChildren(static_cast<const ListValue&>(node));
+
+ case Value::Type::DICTIONARY:
+ return CopyDictionaryWithoutEmptyChildren(
+ static_cast<const DictionaryValue&>(node));
+
+ default:
+ return std::make_unique<Value>(node.Clone());
+ }
+}
+
+} // namespace
+
+// static
+std::unique_ptr<Value> Value::CreateWithCopiedBuffer(const char* buffer,
+ size_t size) {
+ return std::make_unique<Value>(BlobStorage(buffer, buffer + size));
+}
+
+// static
+Value Value::FromUniquePtrValue(std::unique_ptr<Value> val) {
+ return std::move(*val);
+}
+
+// static
+std::unique_ptr<Value> Value::ToUniquePtrValue(Value val) {
+ return std::make_unique<Value>(std::move(val));
+}
+
+Value::Value(Value&& that) noexcept {
+ InternalMoveConstructFrom(std::move(that));
+}
+
+Value::Value() noexcept : type_(Type::NONE) {}
+
+Value::Value(Type type) : type_(type) {
+ // Initialize with the default value.
+ switch (type_) {
+ case Type::NONE:
+ return;
+
+ case Type::BOOLEAN:
+ bool_value_ = false;
+ return;
+ case Type::INTEGER:
+ int_value_ = 0;
+ return;
+ case Type::STRING:
+ new (&string_value_) std::string();
+ return;
+ case Type::BINARY:
+ new (&binary_value_) BlobStorage();
+ return;
+ case Type::DICTIONARY:
+ new (&dict_) DictStorage();
+ return;
+ case Type::LIST:
+ new (&list_) ListStorage();
+ return;
+ }
+}
+
+Value::Value(bool in_bool) : type_(Type::BOOLEAN), bool_value_(in_bool) {}
+
+Value::Value(int in_int) : type_(Type::INTEGER), int_value_(in_int) {}
+
+Value::Value(const char* in_string) : Value(std::string(in_string)) {}
+
+Value::Value(std::string_view in_string) : Value(std::string(in_string)) {}
+
+Value::Value(std::string&& in_string) noexcept
+ : type_(Type::STRING), string_value_(std::move(in_string)) {
+ DCHECK(IsStringUTF8(string_value_));
+}
+
+Value::Value(const char16_t* in_string16)
+ : Value(std::u16string_view(in_string16)) {}
+
+Value::Value(std::u16string_view in_string16)
+ : Value(UTF16ToUTF8(in_string16)) {}
+
+Value::Value(const BlobStorage& in_blob)
+ : type_(Type::BINARY), binary_value_(in_blob) {}
+
+Value::Value(BlobStorage&& in_blob) noexcept
+ : type_(Type::BINARY), binary_value_(std::move(in_blob)) {}
+
+Value::Value(const DictStorage& in_dict) : type_(Type::DICTIONARY), dict_() {
+ dict_.reserve(in_dict.size());
+ for (const auto& it : in_dict) {
+ dict_.try_emplace(dict_.end(), it.first,
+ std::make_unique<Value>(it.second->Clone()));
+ }
+}
+
+Value::Value(DictStorage&& in_dict) noexcept
+ : type_(Type::DICTIONARY), dict_(std::move(in_dict)) {}
+
+Value::Value(const ListStorage& in_list) : type_(Type::LIST), list_() {
+ list_.reserve(in_list.size());
+ for (const auto& val : in_list)
+ list_.emplace_back(val.Clone());
+}
+
+Value::Value(ListStorage&& in_list) noexcept
+ : type_(Type::LIST), list_(std::move(in_list)) {}
+
+Value& Value::operator=(Value&& that) noexcept {
+ InternalCleanup();
+ InternalMoveConstructFrom(std::move(that));
+
+ return *this;
+}
+
+Value Value::Clone() const {
+ switch (type_) {
+ case Type::NONE:
+ return Value();
+ case Type::BOOLEAN:
+ return Value(bool_value_);
+ case Type::INTEGER:
+ return Value(int_value_);
+ case Type::STRING:
+ return Value(string_value_);
+ case Type::BINARY:
+ return Value(binary_value_);
+ case Type::DICTIONARY:
+ return Value(dict_);
+ case Type::LIST:
+ return Value(list_);
+ }
+
+ NOTREACHED();
+ return Value();
+}
+
+Value::~Value() {
+ InternalCleanup();
+}
+
+// static
+const char* Value::GetTypeName(Value::Type type) {
+ DCHECK_GE(static_cast<int>(type), 0);
+ DCHECK_LT(static_cast<size_t>(type), std::size(kTypeNames));
+ return kTypeNames[static_cast<size_t>(type)];
+}
+
+bool Value::GetBool() const {
+ CHECK(is_bool());
+ return bool_value_;
+}
+
+int Value::GetInt() const {
+ CHECK(is_int());
+ return int_value_;
+}
+
+const std::string& Value::GetString() const {
+ CHECK(is_string());
+ return string_value_;
+}
+
+const Value::BlobStorage& Value::GetBlob() const {
+ CHECK(is_blob());
+ return binary_value_;
+}
+
+Value::ListStorage& Value::GetList() {
+ CHECK(is_list());
+ return list_;
+}
+
+const Value::ListStorage& Value::GetList() const {
+ CHECK(is_list());
+ return list_;
+}
+
+Value* Value::FindKey(std::string_view key) {
+ return const_cast<Value*>(static_cast<const Value*>(this)->FindKey(key));
+}
+
+const Value* Value::FindKey(std::string_view key) const {
+ CHECK(is_dict());
+ auto found = dict_.find(key);
+ if (found == dict_.end())
+ return nullptr;
+ return found->second.get();
+}
+
+Value* Value::FindKeyOfType(std::string_view key, Type type) {
+ return const_cast<Value*>(
+ static_cast<const Value*>(this)->FindKeyOfType(key, type));
+}
+
+const Value* Value::FindKeyOfType(std::string_view key, Type type) const {
+ const Value* result = FindKey(key);
+ if (!result || result->type() != type)
+ return nullptr;
+ return result;
+}
+
+bool Value::RemoveKey(std::string_view key) {
+ CHECK(is_dict());
+ // NOTE: Can't directly return dict_->erase(key) due to MSVC warning C4800.
+ return dict_.erase(key) != 0;
+}
+
+Value* Value::SetKey(std::string_view key, Value value) {
+ CHECK(is_dict());
+ // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
+ // an explicit conversion from std::string_view to std::string if necessary.
+ auto val_ptr = std::make_unique<Value>(std::move(value));
+ auto result = dict_.try_emplace(key, std::move(val_ptr));
+ if (!result.second) {
+ // val_ptr is guaranteed to be still intact at this point.
+ result.first->second = std::move(val_ptr);
+ }
+ return result.first->second.get();
+}
+
+Value* Value::SetKey(std::string&& key, Value value) {
+ CHECK(is_dict());
+ return dict_
+ .insert_or_assign(std::move(key),
+ std::make_unique<Value>(std::move(value)))
+ .first->second.get();
+}
+
+Value* Value::SetKey(const char* key, Value value) {
+ return SetKey(std::string_view(key), std::move(value));
+}
+
+Value* Value::FindPath(std::initializer_list<std::string_view> path) {
+ return const_cast<Value*>(const_cast<const Value*>(this)->FindPath(path));
+}
+
+Value* Value::FindPath(span<const std::string_view> path) {
+ return const_cast<Value*>(const_cast<const Value*>(this)->FindPath(path));
+}
+
+const Value* Value::FindPath(
+ std::initializer_list<std::string_view> path) const {
+ DCHECK_GE(path.size(), 2u) << "Use FindKey() for a path of length 1.";
+ return FindPath(make_span(path.begin(), path.size()));
+}
+
+const Value* Value::FindPath(span<const std::string_view> path) const {
+ const Value* cur = this;
+ for (const std::string_view& component : path) {
+ if (!cur->is_dict() || (cur = cur->FindKey(component)) == nullptr)
+ return nullptr;
+ }
+ return cur;
+}
+
+Value* Value::FindPathOfType(std::initializer_list<std::string_view> path,
+ Type type) {
+ return const_cast<Value*>(
+ const_cast<const Value*>(this)->FindPathOfType(path, type));
+}
+
+Value* Value::FindPathOfType(span<const std::string_view> path, Type type) {
+ return const_cast<Value*>(
+ const_cast<const Value*>(this)->FindPathOfType(path, type));
+}
+
+const Value* Value::FindPathOfType(std::initializer_list<std::string_view> path,
+ Type type) const {
+ DCHECK_GE(path.size(), 2u) << "Use FindKeyOfType() for a path of length 1.";
+ return FindPathOfType(make_span(path.begin(), path.size()), type);
+}
+
+const Value* Value::FindPathOfType(span<const std::string_view> path,
+ Type type) const {
+ const Value* result = FindPath(path);
+ if (!result || result->type() != type)
+ return nullptr;
+ return result;
+}
+
+Value* Value::SetPath(std::initializer_list<std::string_view> path,
+ Value value) {
+ DCHECK_GE(path.size(), 2u) << "Use SetKey() for a path of length 1.";
+ return SetPath(make_span(path.begin(), path.size()), std::move(value));
+}
+
+Value* Value::SetPath(span<const std::string_view> path, Value value) {
+ DCHECK_NE(path.begin(), path.end()); // Can't be empty path.
+
+ // Walk/construct intermediate dictionaries. The last element requires
+ // special handling so skip it in this loop.
+ Value* cur = this;
+ const std::string_view* cur_path = path.begin();
+ for (; (cur_path + 1) < path.end(); ++cur_path) {
+ if (!cur->is_dict())
+ return nullptr;
+
+ // Use lower_bound to avoid doing the search twice for missing keys.
+ const std::string_view path_component = *cur_path;
+ auto found = cur->dict_.lower_bound(path_component);
+ if (found == cur->dict_.end() || found->first != path_component) {
+ // No key found, insert one.
+ auto inserted = cur->dict_.try_emplace(
+ found, path_component, std::make_unique<Value>(Type::DICTIONARY));
+ cur = inserted->second.get();
+ } else {
+ cur = found->second.get();
+ }
+ }
+
+ // "cur" will now contain the last dictionary to insert or replace into.
+ if (!cur->is_dict())
+ return nullptr;
+ return cur->SetKey(*cur_path, std::move(value));
+}
+
+bool Value::RemovePath(std::initializer_list<std::string_view> path) {
+ DCHECK_GE(path.size(), 2u) << "Use RemoveKey() for a path of length 1.";
+ return RemovePath(make_span(path.begin(), path.size()));
+}
+
+bool Value::RemovePath(span<const std::string_view> path) {
+ if (!is_dict() || path.empty())
+ return false;
+
+ if (path.size() == 1)
+ return RemoveKey(path[0]);
+
+ auto found = dict_.find(path[0]);
+ if (found == dict_.end() || !found->second->is_dict())
+ return false;
+
+ bool removed = found->second->RemovePath(path.subspan(1));
+ if (removed && found->second->dict_.empty())
+ dict_.erase(found);
+
+ return removed;
+}
+
+Value::dict_iterator_proxy Value::DictItems() {
+ CHECK(is_dict());
+ return dict_iterator_proxy(&dict_);
+}
+
+Value::const_dict_iterator_proxy Value::DictItems() const {
+ CHECK(is_dict());
+ return const_dict_iterator_proxy(&dict_);
+}
+
+size_t Value::DictSize() const {
+ CHECK(is_dict());
+ return dict_.size();
+}
+
+bool Value::DictEmpty() const {
+ CHECK(is_dict());
+ return dict_.empty();
+}
+
+bool Value::GetAsBoolean(bool* out_value) const {
+ if (out_value && is_bool()) {
+ *out_value = bool_value_;
+ return true;
+ }
+ return is_bool();
+}
+
+bool Value::GetAsInteger(int* out_value) const {
+ if (out_value && is_int()) {
+ *out_value = int_value_;
+ return true;
+ }
+ return is_int();
+}
+
+bool Value::GetAsString(std::string* out_value) const {
+ if (out_value && is_string()) {
+ *out_value = string_value_;
+ return true;
+ }
+ return is_string();
+}
+
+bool Value::GetAsString(std::u16string* out_value) const {
+ if (out_value && is_string()) {
+ *out_value = UTF8ToUTF16(string_value_);
+ return true;
+ }
+ return is_string();
+}
+
+bool Value::GetAsString(const Value** out_value) const {
+ if (out_value && is_string()) {
+ *out_value = static_cast<const Value*>(this);
+ return true;
+ }
+ return is_string();
+}
+
+bool Value::GetAsString(std::string_view* out_value) const {
+ if (out_value && is_string()) {
+ *out_value = string_value_;
+ return true;
+ }
+ return is_string();
+}
+
+bool Value::GetAsList(ListValue** out_value) {
+ if (out_value && is_list()) {
+ *out_value = static_cast<ListValue*>(this);
+ return true;
+ }
+ return is_list();
+}
+
+bool Value::GetAsList(const ListValue** out_value) const {
+ if (out_value && is_list()) {
+ *out_value = static_cast<const ListValue*>(this);
+ return true;
+ }
+ return is_list();
+}
+
+bool Value::GetAsDictionary(DictionaryValue** out_value) {
+ if (out_value && is_dict()) {
+ *out_value = static_cast<DictionaryValue*>(this);
+ return true;
+ }
+ return is_dict();
+}
+
+bool Value::GetAsDictionary(const DictionaryValue** out_value) const {
+ if (out_value && is_dict()) {
+ *out_value = static_cast<const DictionaryValue*>(this);
+ return true;
+ }
+ return is_dict();
+}
+
+Value* Value::DeepCopy() const {
+ return new Value(Clone());
+}
+
+std::unique_ptr<Value> Value::CreateDeepCopy() const {
+ return std::make_unique<Value>(Clone());
+}
+
+bool operator==(const Value& lhs, const Value& rhs) {
+ if (lhs.type_ != rhs.type_)
+ return false;
+
+ switch (lhs.type_) {
+ case Value::Type::NONE:
+ return true;
+ case Value::Type::BOOLEAN:
+ return lhs.bool_value_ == rhs.bool_value_;
+ case Value::Type::INTEGER:
+ return lhs.int_value_ == rhs.int_value_;
+ case Value::Type::STRING:
+ return lhs.string_value_ == rhs.string_value_;
+ case Value::Type::BINARY:
+ return lhs.binary_value_ == rhs.binary_value_;
+ // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
+ // are completely inlined.
+ case Value::Type::DICTIONARY:
+ if (lhs.dict_.size() != rhs.dict_.size())
+ return false;
+ return std::equal(
+ std::begin(lhs.dict_), std::end(lhs.dict_), std::begin(rhs.dict_),
+ [](const auto& u, const auto& v) {
+ return std::tie(u.first, *u.second) == std::tie(v.first, *v.second);
+ });
+ case Value::Type::LIST:
+ return lhs.list_ == rhs.list_;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+bool operator!=(const Value& lhs, const Value& rhs) {
+ return !(lhs == rhs);
+}
+
+bool operator<(const Value& lhs, const Value& rhs) {
+ if (lhs.type_ != rhs.type_)
+ return lhs.type_ < rhs.type_;
+
+ switch (lhs.type_) {
+ case Value::Type::NONE:
+ return false;
+ case Value::Type::BOOLEAN:
+ return lhs.bool_value_ < rhs.bool_value_;
+ case Value::Type::INTEGER:
+ return lhs.int_value_ < rhs.int_value_;
+ case Value::Type::STRING:
+ return lhs.string_value_ < rhs.string_value_;
+ case Value::Type::BINARY:
+ return lhs.binary_value_ < rhs.binary_value_;
+ // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
+ // are completely inlined.
+ case Value::Type::DICTIONARY:
+ return std::lexicographical_compare(
+ std::begin(lhs.dict_), std::end(lhs.dict_), std::begin(rhs.dict_),
+ std::end(rhs.dict_),
+ [](const Value::DictStorage::value_type& u,
+ const Value::DictStorage::value_type& v) {
+ return std::tie(u.first, *u.second) < std::tie(v.first, *v.second);
+ });
+ case Value::Type::LIST:
+ return lhs.list_ < rhs.list_;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+bool operator>(const Value& lhs, const Value& rhs) {
+ return rhs < lhs;
+}
+
+bool operator<=(const Value& lhs, const Value& rhs) {
+ return !(rhs < lhs);
+}
+
+bool operator>=(const Value& lhs, const Value& rhs) {
+ return !(lhs < rhs);
+}
+
+bool Value::Equals(const Value* other) const {
+ DCHECK(other);
+ return *this == *other;
+}
+
+void Value::InternalMoveConstructFrom(Value&& that) {
+ type_ = that.type_;
+
+ switch (type_) {
+ case Type::NONE:
+ return;
+ case Type::BOOLEAN:
+ bool_value_ = that.bool_value_;
+ return;
+ case Type::INTEGER:
+ int_value_ = that.int_value_;
+ return;
+ case Type::STRING:
+ new (&string_value_) std::string(std::move(that.string_value_));
+ return;
+ case Type::BINARY:
+ new (&binary_value_) BlobStorage(std::move(that.binary_value_));
+ return;
+ case Type::DICTIONARY:
+ new (&dict_) DictStorage(std::move(that.dict_));
+ return;
+ case Type::LIST:
+ new (&list_) ListStorage(std::move(that.list_));
+ return;
+ }
+}
+
+void Value::InternalCleanup() {
+ switch (type_) {
+ case Type::NONE:
+ case Type::BOOLEAN:
+ case Type::INTEGER:
+ // Nothing to do
+ return;
+
+ case Type::STRING:
+ string_value_.~basic_string();
+ return;
+ case Type::BINARY:
+ binary_value_.~BlobStorage();
+ return;
+ case Type::DICTIONARY:
+ dict_.~DictStorage();
+ return;
+ case Type::LIST:
+ list_.~ListStorage();
+ return;
+ }
+}
+
+///////////////////// DictionaryValue ////////////////////
+
+// static
+std::unique_ptr<DictionaryValue> DictionaryValue::From(
+ std::unique_ptr<Value> value) {
+ DictionaryValue* out;
+ if (value && value->GetAsDictionary(&out)) {
+ ignore_result(value.release());
+ return WrapUnique(out);
+ }
+ return nullptr;
+}
+
+DictionaryValue::DictionaryValue() : Value(Type::DICTIONARY) {}
+DictionaryValue::DictionaryValue(const DictStorage& in_dict) : Value(in_dict) {}
+DictionaryValue::DictionaryValue(DictStorage&& in_dict) noexcept
+ : Value(std::move(in_dict)) {}
+
+bool DictionaryValue::HasKey(std::string_view key) const {
+ DCHECK(IsStringUTF8(key));
+ auto current_entry = dict_.find(key);
+ DCHECK((current_entry == dict_.end()) || current_entry->second);
+ return current_entry != dict_.end();
+}
+
+void DictionaryValue::Clear() {
+ dict_.clear();
+}
+
+Value* DictionaryValue::Set(std::string_view path,
+ std::unique_ptr<Value> in_value) {
+ DCHECK(IsStringUTF8(path));
+ DCHECK(in_value);
+
+ std::string_view current_path(path);
+ Value* current_dictionary = this;
+ for (size_t delimiter_position = current_path.find('.');
+ delimiter_position != std::string_view::npos;
+ delimiter_position = current_path.find('.')) {
+ // Assume that we're indexing into a dictionary.
+ std::string_view key = current_path.substr(0, delimiter_position);
+ Value* child_dictionary =
+ current_dictionary->FindKeyOfType(key, Type::DICTIONARY);
+ if (!child_dictionary) {
+ child_dictionary =
+ current_dictionary->SetKey(key, Value(Type::DICTIONARY));
+ }
+
+ current_dictionary = child_dictionary;
+ current_path = current_path.substr(delimiter_position + 1);
+ }
+
+ return static_cast<DictionaryValue*>(current_dictionary)
+ ->SetWithoutPathExpansion(current_path, std::move(in_value));
+}
+
+Value* DictionaryValue::SetBoolean(std::string_view path, bool in_value) {
+ return Set(path, std::make_unique<Value>(in_value));
+}
+
+Value* DictionaryValue::SetInteger(std::string_view path, int in_value) {
+ return Set(path, std::make_unique<Value>(in_value));
+}
+
+Value* DictionaryValue::SetString(std::string_view path,
+ std::string_view in_value) {
+ return Set(path, std::make_unique<Value>(in_value));
+}
+
+Value* DictionaryValue::SetString(std::string_view path,
+ const std::u16string& in_value) {
+ return Set(path, std::make_unique<Value>(in_value));
+}
+
+DictionaryValue* DictionaryValue::SetDictionary(
+ std::string_view path,
+ std::unique_ptr<DictionaryValue> in_value) {
+ return static_cast<DictionaryValue*>(Set(path, std::move(in_value)));
+}
+
+ListValue* DictionaryValue::SetList(std::string_view path,
+ std::unique_ptr<ListValue> in_value) {
+ return static_cast<ListValue*>(Set(path, std::move(in_value)));
+}
+
+Value* DictionaryValue::SetWithoutPathExpansion(
+ std::string_view key,
+ std::unique_ptr<Value> in_value) {
+ // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
+ // an explicit conversion from std::string_view to std::string if necessary.
+ auto result = dict_.try_emplace(key, std::move(in_value));
+ if (!result.second) {
+ // in_value is guaranteed to be still intact at this point.
+ result.first->second = std::move(in_value);
+ }
+ return result.first->second.get();
+}
+
+bool DictionaryValue::Get(std::string_view path,
+ const Value** out_value) const {
+ DCHECK(IsStringUTF8(path));
+ std::string_view current_path(path);
+ const DictionaryValue* current_dictionary = this;
+ for (size_t delimiter_position = current_path.find('.');
+ delimiter_position != std::string::npos;
+ delimiter_position = current_path.find('.')) {
+ const DictionaryValue* child_dictionary = nullptr;
+ if (!current_dictionary->GetDictionaryWithoutPathExpansion(
+ current_path.substr(0, delimiter_position), &child_dictionary)) {
+ return false;
+ }
+
+ current_dictionary = child_dictionary;
+ current_path = current_path.substr(delimiter_position + 1);
+ }
+
+ return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
+}
+
+bool DictionaryValue::Get(std::string_view path, Value** out_value) {
+ return static_cast<const DictionaryValue&>(*this).Get(
+ path, const_cast<const Value**>(out_value));
+}
+
+bool DictionaryValue::GetBoolean(std::string_view path,
+ bool* bool_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsBoolean(bool_value);
+}
+
+bool DictionaryValue::GetInteger(std::string_view path, int* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool DictionaryValue::GetString(std::string_view path,
+ std::string* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetString(std::string_view path,
+ std::u16string* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetStringASCII(std::string_view path,
+ std::string* out_value) const {
+ std::string out;
+ if (!GetString(path, &out))
+ return false;
+
+ if (!IsStringASCII(out)) {
+ NOTREACHED();
+ return false;
+ }
+
+ out_value->assign(out);
+ return true;
+}
+
+bool DictionaryValue::GetBinary(std::string_view path,
+ const Value** out_value) const {
+ const Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->is_blob())
+ return false;
+
+ if (out_value)
+ *out_value = value;
+
+ return true;
+}
+
+bool DictionaryValue::GetBinary(std::string_view path, Value** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetBinary(
+ path, const_cast<const Value**>(out_value));
+}
+
+bool DictionaryValue::GetDictionary(std::string_view path,
+ const DictionaryValue** out_value) const {
+ const Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->is_dict())
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const DictionaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetDictionary(std::string_view path,
+ DictionaryValue** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetDictionary(
+ path, const_cast<const DictionaryValue**>(out_value));
+}
+
+bool DictionaryValue::GetList(std::string_view path,
+ const ListValue** out_value) const {
+ const Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->is_list())
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const ListValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetList(std::string_view path, ListValue** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetList(
+ path, const_cast<const ListValue**>(out_value));
+}
+
+bool DictionaryValue::GetWithoutPathExpansion(std::string_view key,
+ const Value** out_value) const {
+ DCHECK(IsStringUTF8(key));
+ auto entry_iterator = dict_.find(key);
+ if (entry_iterator == dict_.end())
+ return false;
+
+ if (out_value)
+ *out_value = entry_iterator->second.get();
+ return true;
+}
+
+bool DictionaryValue::GetWithoutPathExpansion(std::string_view key,
+ Value** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetWithoutPathExpansion(
+ key, const_cast<const Value**>(out_value));
+}
+
+bool DictionaryValue::GetBooleanWithoutPathExpansion(std::string_view key,
+ bool* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsBoolean(out_value);
+}
+
+bool DictionaryValue::GetIntegerWithoutPathExpansion(std::string_view key,
+ int* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool DictionaryValue::GetStringWithoutPathExpansion(
+ std::string_view key,
+ std::string* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetStringWithoutPathExpansion(
+ std::string_view key,
+ std::u16string* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetDictionaryWithoutPathExpansion(
+ std::string_view key,
+ const DictionaryValue** out_value) const {
+ const Value* value;
+ bool result = GetWithoutPathExpansion(key, &value);
+ if (!result || !value->is_dict())
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const DictionaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetDictionaryWithoutPathExpansion(
+ std::string_view key,
+ DictionaryValue** out_value) {
+ const DictionaryValue& const_this =
+ static_cast<const DictionaryValue&>(*this);
+ return const_this.GetDictionaryWithoutPathExpansion(
+ key, const_cast<const DictionaryValue**>(out_value));
+}
+
+bool DictionaryValue::GetListWithoutPathExpansion(
+ std::string_view key,
+ const ListValue** out_value) const {
+ const Value* value;
+ bool result = GetWithoutPathExpansion(key, &value);
+ if (!result || !value->is_list())
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const ListValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetListWithoutPathExpansion(std::string_view key,
+ ListValue** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetListWithoutPathExpansion(
+ key, const_cast<const ListValue**>(out_value));
+}
+
+bool DictionaryValue::Remove(std::string_view path,
+ std::unique_ptr<Value>* out_value) {
+ DCHECK(IsStringUTF8(path));
+ std::string_view current_path(path);
+ DictionaryValue* current_dictionary = this;
+ size_t delimiter_position = current_path.rfind('.');
+ if (delimiter_position != std::string_view::npos) {
+ if (!GetDictionary(current_path.substr(0, delimiter_position),
+ &current_dictionary))
+ return false;
+ current_path = current_path.substr(delimiter_position + 1);
+ }
+
+ return current_dictionary->RemoveWithoutPathExpansion(current_path,
+ out_value);
+}
+
+bool DictionaryValue::RemoveWithoutPathExpansion(
+ std::string_view key,
+ std::unique_ptr<Value>* out_value) {
+ DCHECK(IsStringUTF8(key));
+ auto entry_iterator = dict_.find(key);
+ if (entry_iterator == dict_.end())
+ return false;
+
+ if (out_value)
+ *out_value = std::move(entry_iterator->second);
+ dict_.erase(entry_iterator);
+ return true;
+}
+
+bool DictionaryValue::RemovePath(std::string_view path,
+ std::unique_ptr<Value>* out_value) {
+ bool result = false;
+ size_t delimiter_position = path.find('.');
+
+ if (delimiter_position == std::string::npos)
+ return RemoveWithoutPathExpansion(path, out_value);
+
+ std::string_view subdict_path = path.substr(0, delimiter_position);
+ DictionaryValue* subdict = nullptr;
+ if (!GetDictionary(subdict_path, &subdict))
+ return false;
+ result = subdict->RemovePath(path.substr(delimiter_position + 1), out_value);
+ if (result && subdict->empty())
+ RemoveWithoutPathExpansion(subdict_path, nullptr);
+
+ return result;
+}
+
+std::unique_ptr<DictionaryValue> DictionaryValue::DeepCopyWithoutEmptyChildren()
+ const {
+ std::unique_ptr<DictionaryValue> copy =
+ CopyDictionaryWithoutEmptyChildren(*this);
+ if (!copy)
+ copy = std::make_unique<DictionaryValue>();
+ return copy;
+}
+
+void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) {
+ CHECK(dictionary->is_dict());
+ for (DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd(); it.Advance()) {
+ const Value* merge_value = &it.value();
+ // Check whether we have to merge dictionaries.
+ if (merge_value->is_dict()) {
+ DictionaryValue* sub_dict;
+ if (GetDictionaryWithoutPathExpansion(it.key(), &sub_dict)) {
+ sub_dict->MergeDictionary(
+ static_cast<const DictionaryValue*>(merge_value));
+ continue;
+ }
+ }
+ // All other cases: Make a copy and hook it up.
+ SetKey(it.key(), merge_value->Clone());
+ }
+}
+
+void DictionaryValue::Swap(DictionaryValue* other) {
+ CHECK(other->is_dict());
+ dict_.swap(other->dict_);
+}
+
+DictionaryValue::Iterator::Iterator(const DictionaryValue& target)
+ : target_(target), it_(target.dict_.begin()) {}
+
+DictionaryValue::Iterator::Iterator(const Iterator& other) = default;
+
+DictionaryValue::Iterator::~Iterator() = default;
+
+DictionaryValue* DictionaryValue::DeepCopy() const {
+ return new DictionaryValue(dict_);
+}
+
+std::unique_ptr<DictionaryValue> DictionaryValue::CreateDeepCopy() const {
+ return std::make_unique<DictionaryValue>(dict_);
+}
+
+///////////////////// ListValue ////////////////////
+
+// static
+std::unique_ptr<ListValue> ListValue::From(std::unique_ptr<Value> value) {
+ ListValue* out;
+ if (value && value->GetAsList(&out)) {
+ ignore_result(value.release());
+ return WrapUnique(out);
+ }
+ return nullptr;
+}
+
+ListValue::ListValue() : Value(Type::LIST) {}
+ListValue::ListValue(const ListStorage& in_list) : Value(in_list) {}
+ListValue::ListValue(ListStorage&& in_list) noexcept
+ : Value(std::move(in_list)) {}
+
+void ListValue::Clear() {
+ list_.clear();
+}
+
+void ListValue::Reserve(size_t n) {
+ list_.reserve(n);
+}
+
+bool ListValue::Set(size_t index, std::unique_ptr<Value> in_value) {
+ if (!in_value)
+ return false;
+
+ if (index >= list_.size())
+ list_.resize(index + 1);
+
+ list_[index] = std::move(*in_value);
+ return true;
+}
+
+bool ListValue::Get(size_t index, const Value** out_value) const {
+ if (index >= list_.size())
+ return false;
+
+ if (out_value)
+ *out_value = &list_[index];
+
+ return true;
+}
+
+bool ListValue::Get(size_t index, Value** out_value) {
+ return static_cast<const ListValue&>(*this).Get(
+ index, const_cast<const Value**>(out_value));
+}
+
+bool ListValue::GetBoolean(size_t index, bool* bool_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsBoolean(bool_value);
+}
+
+bool ListValue::GetInteger(size_t index, int* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool ListValue::GetString(size_t index, std::string* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool ListValue::GetString(size_t index, std::u16string* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool ListValue::GetDictionary(size_t index,
+ const DictionaryValue** out_value) const {
+ const Value* value;
+ bool result = Get(index, &value);
+ if (!result || !value->is_dict())
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const DictionaryValue*>(value);
+
+ return true;
+}
+
+bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) {
+ return static_cast<const ListValue&>(*this).GetDictionary(
+ index, const_cast<const DictionaryValue**>(out_value));
+}
+
+bool ListValue::GetList(size_t index, const ListValue** out_value) const {
+ const Value* value;
+ bool result = Get(index, &value);
+ if (!result || !value->is_list())
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const ListValue*>(value);
+
+ return true;
+}
+
+bool ListValue::GetList(size_t index, ListValue** out_value) {
+ return static_cast<const ListValue&>(*this).GetList(
+ index, const_cast<const ListValue**>(out_value));
+}
+
+bool ListValue::Remove(size_t index, std::unique_ptr<Value>* out_value) {
+ if (index >= list_.size())
+ return false;
+
+ if (out_value)
+ *out_value = std::make_unique<Value>(std::move(list_[index]));
+
+ list_.erase(list_.begin() + index);
+ return true;
+}
+
+bool ListValue::Remove(const Value& value, size_t* index) {
+ auto it = std::find(list_.begin(), list_.end(), value);
+
+ if (it == list_.end())
+ return false;
+
+ if (index)
+ *index = std::distance(list_.begin(), it);
+
+ list_.erase(it);
+ return true;
+}
+
+ListValue::iterator ListValue::Erase(iterator iter,
+ std::unique_ptr<Value>* out_value) {
+ if (out_value)
+ *out_value = std::make_unique<Value>(std::move(*iter));
+
+ return list_.erase(iter);
+}
+
+void ListValue::Append(std::unique_ptr<Value> in_value) {
+ list_.push_back(std::move(*in_value));
+}
+
+void ListValue::AppendBoolean(bool in_value) {
+ list_.emplace_back(in_value);
+}
+
+void ListValue::AppendInteger(int in_value) {
+ list_.emplace_back(in_value);
+}
+
+void ListValue::AppendString(std::string_view in_value) {
+ list_.emplace_back(in_value);
+}
+
+void ListValue::AppendString(const std::u16string& in_value) {
+ list_.emplace_back(in_value);
+}
+
+void ListValue::AppendStrings(const std::vector<std::string>& in_values) {
+ list_.reserve(list_.size() + in_values.size());
+ for (const auto& in_value : in_values)
+ list_.emplace_back(in_value);
+}
+
+void ListValue::AppendStrings(const std::vector<std::u16string>& in_values) {
+ list_.reserve(list_.size() + in_values.size());
+ for (const auto& in_value : in_values)
+ list_.emplace_back(in_value);
+}
+
+bool ListValue::AppendIfNotPresent(std::unique_ptr<Value> in_value) {
+ DCHECK(in_value);
+ if (ContainsValue(list_, *in_value))
+ return false;
+
+ list_.push_back(std::move(*in_value));
+ return true;
+}
+
+bool ListValue::Insert(size_t index, std::unique_ptr<Value> in_value) {
+ DCHECK(in_value);
+ if (index > list_.size())
+ return false;
+
+ list_.insert(list_.begin() + index, std::move(*in_value));
+ return true;
+}
+
+ListValue::const_iterator ListValue::Find(const Value& value) const {
+ return std::find(list_.begin(), list_.end(), value);
+}
+
+void ListValue::Swap(ListValue* other) {
+ CHECK(other->is_list());
+ list_.swap(other->list_);
+}
+
+ListValue* ListValue::DeepCopy() const {
+ return new ListValue(list_);
+}
+
+std::unique_ptr<ListValue> ListValue::CreateDeepCopy() const {
+ return std::make_unique<ListValue>(list_);
+}
+
+ValueSerializer::~ValueSerializer() = default;
+
+ValueDeserializer::~ValueDeserializer() = default;
+
+std::ostream& operator<<(std::ostream& out, const Value& value) {
+ std::string json;
+ JSONWriter::WriteWithOptions(value, JSONWriter::OPTIONS_PRETTY_PRINT, &json);
+ return out << json;
+}
+
+std::ostream& operator<<(std::ostream& out, const Value::Type& type) {
+ if (static_cast<int>(type) < 0 ||
+ static_cast<size_t>(type) >= std::size(kTypeNames))
+ return out << "Invalid Type (index = " << static_cast<int>(type) << ")";
+ return out << Value::GetTypeName(type);
+}
+
+} // namespace base
diff --git a/gn/src/base/values.h b/gn/src/base/values.h
new file mode 100644
index 00000000000..b32c8f95b97
--- /dev/null
+++ b/gn/src/base/values.h
@@ -0,0 +1,763 @@
+// 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 specifies a recursive data storage class called Value intended for
+// storing settings and other persistable data.
+//
+// A Value represents something that can be stored in JSON or passed to/from
+// JavaScript. As such, it is NOT a generalized variant type, since only the
+// types supported by JavaScript/JSON are supported.
+//
+// IN PARTICULAR this means that there is no support for int64_t or unsigned
+// numbers. Writing JSON with such types would violate the spec. If you need
+// something like this, make a string value containing the number you want.
+//
+// NOTE: A Value parameter that is always a Value::STRING should just be passed
+// as a std::string. Similarly for Values that are always Value::DICTIONARY
+// (should be flat_map), Value::LIST (should be std::vector), et cetera.
+
+#ifndef BASE_VALUES_H_
+#define BASE_VALUES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <iosfwd>
+#include <map>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "base/value_iterators.h"
+
+namespace base {
+
+class DictionaryValue;
+class ListValue;
+class Value;
+
+// The Value class is the base class for Values. A Value can be instantiated
+// via passing the appropriate type or backing storage to the constructor.
+//
+// See the file-level comment above for more information.
+//
+// base::Value is currently in the process of being refactored. Design doc:
+// https://docs.google.com/document/d/1uDLu5uTRlCWePxQUEHc8yNQdEoE1BDISYdpggWEABnw
+//
+// Previously (which is how most code that currently exists is written), Value
+// used derived types to implement the individual data types, and base::Value
+// was just a base class to refer to them. This required everything be heap
+// allocated.
+//
+// OLD WAY:
+//
+// std::unique_ptr<base::Value> GetFoo() {
+// std::unique_ptr<DictionaryValue> dict;
+// dict->SetString("mykey", foo);
+// return dict;
+// }
+//
+// The new design makes base::Value a variant type that holds everything in
+// a union. It is now recommended to pass by value with std::move rather than
+// use heap allocated values. The DictionaryValue and ListValue subclasses
+// exist only as a compatibility shim that we're in the process of removing.
+//
+// NEW WAY:
+//
+// base::Value GetFoo() {
+// base::Value dict(base::Value::Type::DICTIONARY);
+// dict.SetKey("mykey", base::Value(foo));
+// return dict;
+// }
+class Value {
+ public:
+ using BlobStorage = std::vector<char>;
+ using DictStorage = flat_map<std::string, std::unique_ptr<Value>>;
+ using ListStorage = std::vector<Value>;
+
+ enum class Type {
+ NONE = 0,
+ BOOLEAN,
+ INTEGER,
+ STRING,
+ BINARY,
+ DICTIONARY,
+ LIST
+ // Note: Do not add more types. See the file-level comment above for why.
+ };
+
+ // For situations where you want to keep ownership of your buffer, this
+ // factory method creates a new BinaryValue by copying the contents of the
+ // buffer that's passed in.
+ // DEPRECATED, use std::make_unique<Value>(const BlobStorage&) instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ static std::unique_ptr<Value> CreateWithCopiedBuffer(const char* buffer,
+ size_t size);
+
+ // Adaptors for converting from the old way to the new way and vice versa.
+ static Value FromUniquePtrValue(std::unique_ptr<Value> val);
+ static std::unique_ptr<Value> ToUniquePtrValue(Value val);
+
+ Value(Value&& that) noexcept;
+ Value() noexcept; // A null value.
+
+ // Value's copy constructor and copy assignment operator are deleted. Use this
+ // to obtain a deep copy explicitly.
+ Value Clone() const;
+
+ explicit Value(Type type);
+ explicit Value(bool in_bool);
+ explicit Value(int in_int);
+
+ // Value(const char*) and Value(const char16_t*) are required despite
+ // Value(std::string_view) and Value(std::u16string_view) because otherwise
+ // the compiler will choose the Value(bool) constructor for these arguments.
+ // Value(std::string&&) allow for efficient move construction.
+ explicit Value(const char* in_string);
+ explicit Value(std::string_view in_string);
+ explicit Value(std::string&& in_string) noexcept;
+ explicit Value(const char16_t* in_string16);
+ explicit Value(std::u16string_view in_string16);
+
+ explicit Value(const BlobStorage& in_blob);
+ explicit Value(BlobStorage&& in_blob) noexcept;
+
+ explicit Value(const DictStorage& in_dict);
+ explicit Value(DictStorage&& in_dict) noexcept;
+
+ explicit Value(const ListStorage& in_list);
+ explicit Value(ListStorage&& in_list) noexcept;
+
+ Value& operator=(Value&& that) noexcept;
+
+ ~Value();
+
+ // Returns the name for a given |type|.
+ static const char* GetTypeName(Type type);
+
+ // Returns the type of the value stored by the current Value object.
+ Type type() const { return type_; }
+
+ // Returns true if the current object represents a given type.
+ bool is_none() const { return type() == Type::NONE; }
+ bool is_bool() const { return type() == Type::BOOLEAN; }
+ bool is_int() const { return type() == Type::INTEGER; }
+ bool is_string() const { return type() == Type::STRING; }
+ bool is_blob() const { return type() == Type::BINARY; }
+ bool is_dict() const { return type() == Type::DICTIONARY; }
+ bool is_list() const { return type() == Type::LIST; }
+
+ // These will all fatally assert if the type doesn't match.
+ bool GetBool() const;
+ int GetInt() const;
+ const std::string& GetString() const;
+ const BlobStorage& GetBlob() const;
+
+ ListStorage& GetList();
+ const ListStorage& GetList() const;
+
+ // |FindKey| looks up |key| in the underlying dictionary. If found, it returns
+ // a pointer to the element. Otherwise it returns nullptr.
+ // returned. Callers are expected to perform a check against null before using
+ // the pointer.
+ // Note: This fatally asserts if type() is not Type::DICTIONARY.
+ //
+ // Example:
+ // auto* found = FindKey("foo");
+ Value* FindKey(std::string_view key);
+ const Value* FindKey(std::string_view key) const;
+
+ // |FindKeyOfType| is similar to |FindKey|, but it also requires the found
+ // value to have type |type|. If no type is found, or the found value is of a
+ // different type nullptr is returned.
+ // Callers are expected to perform a check against null before using the
+ // pointer.
+ // Note: This fatally asserts if type() is not Type::DICTIONARY.
+ //
+ // Example:
+ // auto* found = FindKey("foo", Type::INTEGER);
+ Value* FindKeyOfType(std::string_view key, Type type);
+ const Value* FindKeyOfType(std::string_view key, Type type) const;
+
+ // |SetKey| looks up |key| in the underlying dictionary and sets the mapped
+ // value to |value|. If |key| could not be found, a new element is inserted.
+ // A pointer to the modified item is returned.
+ // Note: This fatally asserts if type() is not Type::DICTIONARY.
+ //
+ // Example:
+ // SetKey("foo", std::move(myvalue));
+ Value* SetKey(std::string_view key, Value value);
+ // This overload results in a performance improvement for std::string&&.
+ Value* SetKey(std::string&& key, Value value);
+ // This overload is necessary to avoid ambiguity for const char* arguments.
+ Value* SetKey(const char* key, Value value);
+
+ // This attemps to remove the value associated with |key|. In case of failure,
+ // e.g. the key does not exist, |false| is returned and the underlying
+ // dictionary is not changed. In case of success, |key| is deleted from the
+ // dictionary and the method returns |true|.
+ // Note: This fatally asserts if type() is not Type::DICTIONARY.
+ //
+ // Example:
+ // bool success = RemoveKey("foo");
+ bool RemoveKey(std::string_view key);
+
+ // Searches a hierarchy of dictionary values for a given value. If a path
+ // of dictionaries exist, returns the item at that path. If any of the path
+ // components do not exist or if any but the last path components are not
+ // dictionaries, returns nullptr.
+ //
+ // The type of the leaf Value is not checked.
+ //
+ // Implementation note: This can't return an iterator because the iterator
+ // will actually be into another Value, so it can't be compared to iterators
+ // from this one (in particular, the DictItems().end() iterator).
+ //
+ // Example:
+ // auto* found = FindPath({"foo", "bar"});
+ //
+ // std::vector<std::string_view> components = ...
+ // auto* found = FindPath(components);
+ //
+ // Note: If there is only one component in the path, use FindKey() instead.
+ Value* FindPath(std::initializer_list<std::string_view> path);
+ Value* FindPath(span<const std::string_view> path);
+ const Value* FindPath(std::initializer_list<std::string_view> path) const;
+ const Value* FindPath(span<const std::string_view> path) const;
+
+ // Like FindPath() but will only return the value if the leaf Value type
+ // matches the given type. Will return nullptr otherwise.
+ //
+ // Note: If there is only one component in the path, use FindKeyOfType()
+ // instead.
+ Value* FindPathOfType(std::initializer_list<std::string_view> path,
+ Type type);
+ Value* FindPathOfType(span<const std::string_view> path, Type type);
+ const Value* FindPathOfType(std::initializer_list<std::string_view> path,
+ Type type) const;
+ const Value* FindPathOfType(span<const std::string_view> path,
+ Type type) const;
+
+ // Sets the given path, expanding and creating dictionary keys as necessary.
+ //
+ // If the current value is not a dictionary, the function returns nullptr. If
+ // path components do not exist, they will be created. If any but the last
+ // components matches a value that is not a dictionary, the function will fail
+ // (it will not overwrite the value) and return nullptr. The last path
+ // component will be unconditionally overwritten if it exists, and created if
+ // it doesn't.
+ //
+ // Example:
+ // value.SetPath({"foo", "bar"}, std::move(myvalue));
+ //
+ // std::vector<std::string_view> components = ...
+ // value.SetPath(components, std::move(myvalue));
+ //
+ // Note: If there is only one component in the path, use SetKey() instead.
+ Value* SetPath(std::initializer_list<std::string_view> path, Value value);
+ Value* SetPath(span<const std::string_view> path, Value value);
+
+ // Tries to remove a Value at the given path.
+ //
+ // If the current value is not a dictionary or any path components does not
+ // exist, this operation fails, leaves underlying Values untouched and returns
+ // |false|. In case intermediate dictionaries become empty as a result of this
+ // path removal, they will be removed as well.
+ //
+ // Example:
+ // bool success = value.RemovePath({"foo", "bar"});
+ //
+ // std::vector<std::string_view> components = ...
+ // bool success = value.RemovePath(components);
+ //
+ // Note: If there is only one component in the path, use RemoveKey() instead.
+ bool RemovePath(std::initializer_list<std::string_view> path);
+ bool RemovePath(span<const std::string_view> path);
+
+ using dict_iterator_proxy = detail::dict_iterator_proxy;
+ using const_dict_iterator_proxy = detail::const_dict_iterator_proxy;
+
+ // |DictItems| returns a proxy object that exposes iterators to the underlying
+ // dictionary. These are intended for iteration over all items in the
+ // dictionary and are compatible with for-each loops and standard library
+ // algorithms.
+ // Note: This fatally asserts if type() is not Type::DICTIONARY.
+ dict_iterator_proxy DictItems();
+ const_dict_iterator_proxy DictItems() const;
+
+ // Returns the size of the dictionary, and if the dictionary is empty.
+ // Note: This fatally asserts if type() is not Type::DICTIONARY.
+ size_t DictSize() const;
+ bool DictEmpty() const;
+
+ // These methods allow the convenient retrieval of the contents of the Value.
+ // If the current object can be converted into the given type, the value is
+ // returned through the |out_value| parameter and true is returned;
+ // otherwise, false is returned and |out_value| is unchanged.
+ // DEPRECATED, use GetBool() instead.
+ bool GetAsBoolean(bool* out_value) const;
+ // DEPRECATED, use GetInt() instead.
+ bool GetAsInteger(int* out_value) const;
+ // DEPRECATED, use GetString() instead.
+ bool GetAsString(std::string* out_value) const;
+ bool GetAsString(std::u16string* out_value) const;
+ bool GetAsString(const Value** out_value) const;
+ bool GetAsString(std::string_view* out_value) const;
+ // ListValue::From is the equivalent for std::unique_ptr conversions.
+ // DEPRECATED, use GetList() instead.
+ bool GetAsList(ListValue** out_value);
+ bool GetAsList(const ListValue** out_value) const;
+ // DictionaryValue::From is the equivalent for std::unique_ptr conversions.
+ bool GetAsDictionary(DictionaryValue** out_value);
+ bool GetAsDictionary(const DictionaryValue** out_value) const;
+ // Note: Do not add more types. See the file-level comment above for why.
+
+ // This creates a deep copy of the entire Value tree, and returns a pointer
+ // to the copy. The caller gets ownership of the copy, of course.
+ // Subclasses return their own type directly in their overrides;
+ // this works because C++ supports covariant return types.
+ // DEPRECATED, use Value::Clone() instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ Value* DeepCopy() const;
+ // DEPRECATED, use Value::Clone() instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ std::unique_ptr<Value> CreateDeepCopy() const;
+
+ // Comparison operators so that Values can easily be used with standard
+ // library algorithms and associative containers.
+ friend bool operator==(const Value& lhs, const Value& rhs);
+ friend bool operator!=(const Value& lhs, const Value& rhs);
+ friend bool operator<(const Value& lhs, const Value& rhs);
+ friend bool operator>(const Value& lhs, const Value& rhs);
+ friend bool operator<=(const Value& lhs, const Value& rhs);
+ friend bool operator>=(const Value& lhs, const Value& rhs);
+
+ // Compares if two Value objects have equal contents.
+ // DEPRECATED, use operator==(const Value& lhs, const Value& rhs) instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ bool Equals(const Value* other) const;
+
+ // Estimates dynamic memory usage.
+ // See base/trace_event/memory_usage_estimator.h for more info.
+ size_t EstimateMemoryUsage() const;
+
+ protected:
+ // TODO(crbug.com/646113): Make these private once DictionaryValue and
+ // ListValue are properly inlined.
+ Type type_;
+
+ union {
+ bool bool_value_;
+ int int_value_;
+ std::string string_value_;
+ BlobStorage binary_value_;
+ DictStorage dict_;
+ ListStorage list_;
+ };
+
+ private:
+ void InternalMoveConstructFrom(Value&& that);
+ void InternalCleanup();
+
+ DISALLOW_COPY_AND_ASSIGN(Value);
+};
+
+// DictionaryValue provides a key-value dictionary with (optional) "path"
+// parsing for recursive access; see the comment at the top of the file. Keys
+// are |std::string|s and should be UTF-8 encoded.
+class DictionaryValue : public Value {
+ public:
+ using const_iterator = DictStorage::const_iterator;
+ using iterator = DictStorage::iterator;
+
+ // Returns |value| if it is a dictionary, nullptr otherwise.
+ static std::unique_ptr<DictionaryValue> From(std::unique_ptr<Value> value);
+
+ DictionaryValue();
+ explicit DictionaryValue(const DictStorage& in_dict);
+ explicit DictionaryValue(DictStorage&& in_dict) noexcept;
+
+ // Returns true if the current dictionary has a value for the given key.
+ // DEPRECATED, use Value::FindKey(key) instead.
+ bool HasKey(std::string_view key) const;
+
+ // Returns the number of Values in this dictionary.
+ size_t size() const { return dict_.size(); }
+
+ // Returns whether the dictionary is empty.
+ bool empty() const { return dict_.empty(); }
+
+ // Clears any current contents of this dictionary.
+ void Clear();
+
+ // Sets the Value associated with the given path starting from this object.
+ // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+ // into the next DictionaryValue down. Obviously, "." can't be used
+ // within a key, but there are no other restrictions on keys.
+ // If the key at any step of the way doesn't exist, or exists but isn't
+ // a DictionaryValue, a new DictionaryValue will be created and attached
+ // to the path in that location. |in_value| must be non-null.
+ // Returns a pointer to the inserted value.
+ // DEPRECATED, use Value::SetPath(path, value) instead.
+ Value* Set(std::string_view path, std::unique_ptr<Value> in_value);
+
+ // Convenience forms of Set(). These methods will replace any existing
+ // value at that path, even if it has a different type.
+ // DEPRECATED, use Value::SetPath(path, Value(bool)) instead.
+ Value* SetBoolean(std::string_view path, bool in_value);
+ // DEPRECATED, use Value::SetPath(path, Value(int)) instead.
+ Value* SetInteger(std::string_view path, int in_value);
+ // DEPRECATED, use Value::SetPath(path, Value(std::string_view)) instead.
+ Value* SetString(std::string_view path, std::string_view in_value);
+ // DEPRECATED, use Value::SetPath(path, Value(const string& 16)) instead.
+ Value* SetString(std::string_view path, const std::u16string& in_value);
+ // DEPRECATED, use Value::SetPath(path, Value(Type::DICTIONARY)) instead.
+ DictionaryValue* SetDictionary(std::string_view path,
+ std::unique_ptr<DictionaryValue> in_value);
+ // DEPRECATED, use Value::SetPath(path, Value(Type::LIST)) instead.
+ ListValue* SetList(std::string_view path,
+ std::unique_ptr<ListValue> in_value);
+
+ // Like Set(), but without special treatment of '.'. This allows e.g. URLs to
+ // be used as paths.
+ // DEPRECATED, use Value::SetKey(key, value) instead.
+ Value* SetWithoutPathExpansion(std::string_view key,
+ std::unique_ptr<Value> in_value);
+
+ // Gets the Value associated with the given path starting from this object.
+ // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+ // into the next DictionaryValue down. If the path can be resolved
+ // successfully, the value for the last key in the path will be returned
+ // through the |out_value| parameter, and the function will return true.
+ // Otherwise, it will return false and |out_value| will be untouched.
+ // Note that the dictionary always owns the value that's returned.
+ // |out_value| is optional and will only be set if non-NULL.
+ // DEPRECATED, use Value::FindPath(path) instead.
+ bool Get(std::string_view path, const Value** out_value) const;
+ // DEPRECATED, use Value::FindPath(path) instead.
+ bool Get(std::string_view path, Value** out_value);
+
+ // These are convenience forms of Get(). The value will be retrieved
+ // and the return value will be true if the path is valid and the value at
+ // the end of the path can be returned in the form specified.
+ // |out_value| is optional and will only be set if non-NULL.
+ // DEPRECATED, use Value::FindPath(path) and Value::GetBool() instead.
+ bool GetBoolean(std::string_view path, bool* out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetInt() instead.
+ bool GetInteger(std::string_view path, int* out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetString() instead.
+ bool GetString(std::string_view path, std::string* out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetString() instead.
+ bool GetString(std::string_view path, std::u16string* out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetString() instead.
+ bool GetStringASCII(std::string_view path, std::string* out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetBlob() instead.
+ bool GetBinary(std::string_view path, const Value** out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetBlob() instead.
+ bool GetBinary(std::string_view path, Value** out_value);
+ // DEPRECATED, use Value::FindPath(path) and Value's Dictionary API instead.
+ bool GetDictionary(std::string_view path,
+ const DictionaryValue** out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value's Dictionary API instead.
+ bool GetDictionary(std::string_view path, DictionaryValue** out_value);
+ // DEPRECATED, use Value::FindPath(path) and Value::GetList() instead.
+ bool GetList(std::string_view path, const ListValue** out_value) const;
+ // DEPRECATED, use Value::FindPath(path) and Value::GetList() instead.
+ bool GetList(std::string_view path, ListValue** out_value);
+
+ // Like Get(), but without special treatment of '.'. This allows e.g. URLs to
+ // be used as paths.
+ // DEPRECATED, use Value::FindKey(key) instead.
+ bool GetWithoutPathExpansion(std::string_view key,
+ const Value** out_value) const;
+ // DEPRECATED, use Value::FindKey(key) instead.
+ bool GetWithoutPathExpansion(std::string_view key, Value** out_value);
+ // DEPRECATED, use Value::FindKey(key) and Value::GetBool() instead.
+ bool GetBooleanWithoutPathExpansion(std::string_view key,
+ bool* out_value) const;
+ // DEPRECATED, use Value::FindKey(key) and Value::GetInt() instead.
+ bool GetIntegerWithoutPathExpansion(std::string_view key,
+ int* out_value) const;
+ // DEPRECATED, use Value::FindKey(key) and Value::GetString() instead.
+ bool GetStringWithoutPathExpansion(std::string_view key,
+ std::string* out_value) const;
+ // DEPRECATED, use Value::FindKey(key) and Value::GetString() instead.
+ bool GetStringWithoutPathExpansion(std::string_view key,
+ std::u16string* out_value) const;
+ // DEPRECATED, use Value::FindKey(key) and Value's Dictionary API instead.
+ bool GetDictionaryWithoutPathExpansion(
+ std::string_view key,
+ const DictionaryValue** out_value) const;
+ // DEPRECATED, use Value::FindKey(key) and Value's Dictionary API instead.
+ bool GetDictionaryWithoutPathExpansion(std::string_view key,
+ DictionaryValue** out_value);
+ // DEPRECATED, use Value::FindKey(key) and Value::GetList() instead.
+ bool GetListWithoutPathExpansion(std::string_view key,
+ const ListValue** out_value) const;
+ // DEPRECATED, use Value::FindKey(key) and Value::GetList() instead.
+ bool GetListWithoutPathExpansion(std::string_view key, ListValue** out_value);
+
+ // Removes the Value with the specified path from this dictionary (or one
+ // of its child dictionaries, if the path is more than just a local key).
+ // If |out_value| is non-NULL, the removed Value will be passed out via
+ // |out_value|. If |out_value| is NULL, the removed value will be deleted.
+ // This method returns true if |path| is a valid path; otherwise it will
+ // return false and the DictionaryValue object will be unchanged.
+ // DEPRECATED, use Value::RemovePath(path) instead.
+ bool Remove(std::string_view path, std::unique_ptr<Value>* out_value);
+
+ // Like Remove(), but without special treatment of '.'. This allows e.g. URLs
+ // to be used as paths.
+ // DEPRECATED, use Value::RemoveKey(key) instead.
+ bool RemoveWithoutPathExpansion(std::string_view key,
+ std::unique_ptr<Value>* out_value);
+
+ // Removes a path, clearing out all dictionaries on |path| that remain empty
+ // after removing the value at |path|.
+ // DEPRECATED, use Value::RemovePath(path) instead.
+ bool RemovePath(std::string_view path, std::unique_ptr<Value>* out_value);
+
+ using Value::RemovePath; // DictionaryValue::RemovePath shadows otherwise.
+
+ // Makes a copy of |this| but doesn't include empty dictionaries and lists in
+ // the copy. This never returns NULL, even if |this| itself is empty.
+ std::unique_ptr<DictionaryValue> DeepCopyWithoutEmptyChildren() const;
+
+ // Merge |dictionary| into this dictionary. This is done recursively, i.e. any
+ // sub-dictionaries will be merged as well. In case of key collisions, the
+ // passed in dictionary takes precedence and data already present will be
+ // replaced. Values within |dictionary| are deep-copied, so |dictionary| may
+ // be freed any time after this call.
+ void MergeDictionary(const DictionaryValue* dictionary);
+
+ // Swaps contents with the |other| dictionary.
+ void Swap(DictionaryValue* other);
+
+ // This class provides an iterator over both keys and values in the
+ // dictionary. It can't be used to modify the dictionary.
+ // DEPRECATED, use Value::DictItems() instead.
+ class Iterator {
+ public:
+ explicit Iterator(const DictionaryValue& target);
+ Iterator(const Iterator& other);
+ ~Iterator();
+
+ bool IsAtEnd() const { return it_ == target_.dict_.end(); }
+ void Advance() { ++it_; }
+
+ const std::string& key() const { return it_->first; }
+ const Value& value() const { return *it_->second; }
+
+ private:
+ const DictionaryValue& target_;
+ DictStorage::const_iterator it_;
+ };
+
+ // Iteration.
+ // DEPRECATED, use Value::DictItems() instead.
+ iterator begin() { return dict_.begin(); }
+ iterator end() { return dict_.end(); }
+
+ // DEPRECATED, use Value::DictItems() instead.
+ const_iterator begin() const { return dict_.begin(); }
+ const_iterator end() const { return dict_.end(); }
+
+ // DEPRECATED, use Value::Clone() instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ DictionaryValue* DeepCopy() const;
+ // DEPRECATED, use Value::Clone() instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ std::unique_ptr<DictionaryValue> CreateDeepCopy() const;
+};
+
+// This type of Value represents a list of other Value values.
+class ListValue : public Value {
+ public:
+ using const_iterator = ListStorage::const_iterator;
+ using iterator = ListStorage::iterator;
+
+ // Returns |value| if it is a list, nullptr otherwise.
+ static std::unique_ptr<ListValue> From(std::unique_ptr<Value> value);
+
+ ListValue();
+ explicit ListValue(const ListStorage& in_list);
+ explicit ListValue(ListStorage&& in_list) noexcept;
+
+ // Clears the contents of this ListValue
+ // DEPRECATED, use GetList()::clear() instead.
+ void Clear();
+
+ // Returns the number of Values in this list.
+ // DEPRECATED, use GetList()::size() instead.
+ size_t GetSize() const { return list_.size(); }
+
+ // Returns whether the list is empty.
+ // DEPRECATED, use GetList()::empty() instead.
+ bool empty() const { return list_.empty(); }
+
+ // Reserves storage for at least |n| values.
+ // DEPRECATED, use GetList()::reserve() instead.
+ void Reserve(size_t n);
+
+ // Sets the list item at the given index to be the Value specified by
+ // the value given. If the index beyond the current end of the list, null
+ // Values will be used to pad out the list.
+ // Returns true if successful, or false if the index was negative or
+ // the value is a null pointer.
+ // DEPRECATED, use GetList()::operator[] instead.
+ bool Set(size_t index, std::unique_ptr<Value> in_value);
+
+ // Gets the Value at the given index. Modifies |out_value| (and returns true)
+ // only if the index falls within the current list range.
+ // Note that the list always owns the Value passed out via |out_value|.
+ // |out_value| is optional and will only be set if non-NULL.
+ // DEPRECATED, use GetList()::operator[] instead.
+ bool Get(size_t index, const Value** out_value) const;
+ bool Get(size_t index, Value** out_value);
+
+ // Convenience forms of Get(). Modifies |out_value| (and returns true)
+ // only if the index is valid and the Value at that index can be returned
+ // in the specified form.
+ // |out_value| is optional and will only be set if non-NULL.
+ // DEPRECATED, use GetList()::operator[]::GetBool() instead.
+ bool GetBoolean(size_t index, bool* out_value) const;
+ // DEPRECATED, use GetList()::operator[]::GetInt() instead.
+ bool GetInteger(size_t index, int* out_value) const;
+ // DEPRECATED, use GetList()::operator[]::GetString() instead.
+ bool GetString(size_t index, std::string* out_value) const;
+ bool GetString(size_t index, std::u16string* out_value) const;
+
+ bool GetDictionary(size_t index, const DictionaryValue** out_value) const;
+ bool GetDictionary(size_t index, DictionaryValue** out_value);
+
+ using Value::GetList;
+ // DEPRECATED, use GetList()::operator[]::GetList() instead.
+ bool GetList(size_t index, const ListValue** out_value) const;
+ bool GetList(size_t index, ListValue** out_value);
+
+ // Removes the Value with the specified index from this list.
+ // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+ // passed out via |out_value|. If |out_value| is NULL, the removed value will
+ // be deleted. This method returns true if |index| is valid; otherwise
+ // it will return false and the ListValue object will be unchanged.
+ // DEPRECATED, use GetList()::erase() instead.
+ bool Remove(size_t index, std::unique_ptr<Value>* out_value);
+
+ // Removes the first instance of |value| found in the list, if any, and
+ // deletes it. |index| is the location where |value| was found. Returns false
+ // if not found.
+ // DEPRECATED, use GetList()::erase() instead.
+ bool Remove(const Value& value, size_t* index);
+
+ // Removes the element at |iter|. If |out_value| is NULL, the value will be
+ // deleted, otherwise ownership of the value is passed back to the caller.
+ // Returns an iterator pointing to the location of the element that
+ // followed the erased element.
+ // DEPRECATED, use GetList()::erase() instead.
+ iterator Erase(iterator iter, std::unique_ptr<Value>* out_value);
+
+ // Appends a Value to the end of the list.
+ // DEPRECATED, use GetList()::push_back() instead.
+ void Append(std::unique_ptr<Value> in_value);
+
+ // Convenience forms of Append.
+ // DEPRECATED, use GetList()::emplace_back() instead.
+ void AppendBoolean(bool in_value);
+ void AppendInteger(int in_value);
+ void AppendString(std::string_view in_value);
+ void AppendString(const std::u16string& in_value);
+ // DEPRECATED, use GetList()::emplace_back() in a loop instead.
+ void AppendStrings(const std::vector<std::string>& in_values);
+ void AppendStrings(const std::vector<std::u16string>& in_values);
+
+ // Appends a Value if it's not already present. Returns true if successful,
+ // or false if the value was already
+ // DEPRECATED, use std::find() with GetList()::push_back() instead.
+ bool AppendIfNotPresent(std::unique_ptr<Value> in_value);
+
+ // Insert a Value at index.
+ // Returns true if successful, or false if the index was out of range.
+ // DEPRECATED, use GetList()::insert() instead.
+ bool Insert(size_t index, std::unique_ptr<Value> in_value);
+
+ // Searches for the first instance of |value| in the list using the Equals
+ // method of the Value type.
+ // Returns a const_iterator to the found item or to end() if none exists.
+ // DEPRECATED, use std::find() instead.
+ const_iterator Find(const Value& value) const;
+
+ // Swaps contents with the |other| list.
+ // DEPRECATED, use GetList()::swap() instead.
+ void Swap(ListValue* other);
+
+ // Iteration.
+ // DEPRECATED, use GetList()::begin() instead.
+ iterator begin() { return list_.begin(); }
+ // DEPRECATED, use GetList()::end() instead.
+ iterator end() { return list_.end(); }
+
+ // DEPRECATED, use GetList()::begin() instead.
+ const_iterator begin() const { return list_.begin(); }
+ // DEPRECATED, use GetList()::end() instead.
+ const_iterator end() const { return list_.end(); }
+
+ // DEPRECATED, use Value::Clone() instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ ListValue* DeepCopy() const;
+ // DEPRECATED, use Value::Clone() instead.
+ // TODO(crbug.com/646113): Delete this and migrate callsites.
+ std::unique_ptr<ListValue> CreateDeepCopy() const;
+};
+
+// This interface is implemented by classes that know how to serialize
+// Value objects.
+class ValueSerializer {
+ public:
+ virtual ~ValueSerializer();
+
+ virtual bool Serialize(const Value& root) = 0;
+};
+
+// This interface is implemented by classes that know how to deserialize Value
+// objects.
+class ValueDeserializer {
+ public:
+ virtual ~ValueDeserializer();
+
+ // This method deserializes the subclass-specific format into a Value object.
+ // If the return value is non-NULL, the caller takes ownership of returned
+ // Value. If the return value is NULL, and if error_code is non-NULL,
+ // error_code will be set with the underlying error.
+ // If |error_message| is non-null, it will be filled in with a formatted
+ // error message including the location of the error if appropriate.
+ virtual std::unique_ptr<Value> Deserialize(int* error_code,
+ std::string* error_str) = 0;
+};
+
+// Stream operator so Values can be used in assertion statements. In order that
+// gtest uses this operator to print readable output on test failures, we must
+// override each specific type. Otherwise, the default template implementation
+// is preferred over an upcast.
+std::ostream& operator<<(std::ostream& out, const Value& value);
+
+inline std::ostream& operator<<(std::ostream& out,
+ const DictionaryValue& value) {
+ return out << static_cast<const Value&>(value);
+}
+
+inline std::ostream& operator<<(std::ostream& out, const ListValue& value) {
+ return out << static_cast<const Value&>(value);
+}
+
+// Stream operator so that enum class Types can be used in log statements.
+std::ostream& operator<<(std::ostream& out, const Value::Type& type);
+
+} // namespace base
+
+#endif // BASE_VALUES_H_
diff --git a/gn/src/base/win/registry.cc b/gn/src/base/win/registry.cc
new file mode 100644
index 00000000000..5acfda72fc0
--- /dev/null
+++ b/gn/src/base/win/registry.cc
@@ -0,0 +1,623 @@
+// 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 "base/win/registry.h"
+
+#include <shlwapi.h>
+#include <stddef.h>
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/win/win_util.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+// RegEnumValue() reports the number of characters from the name that were
+// written to the buffer, not how many there are. This constant is the maximum
+// name size, such that a buffer with this size should read any name.
+const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
+
+// Registry values are read as BYTE* but can have char16_t* data whose last
+// char16_t is truncated. This function converts the reported |byte_size| to
+// a size in char16_t that can store a truncated char16_t if necessary.
+inline DWORD to_wchar_size(DWORD byte_size) {
+ return (byte_size + sizeof(char16_t) - 1) / sizeof(char16_t);
+}
+
+// Mask to pull WOW64 access flags out of REGSAM access.
+const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
+
+} // namespace
+
+// RegKey ----------------------------------------------------------------------
+
+RegKey::RegKey() : key_(NULL), wow64access_(0) {}
+
+RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) {}
+
+RegKey::RegKey(HKEY rootkey, const char16_t* subkey, REGSAM access)
+ : key_(NULL), wow64access_(0) {
+ if (rootkey) {
+ if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
+ Create(rootkey, subkey, access);
+ else
+ Open(rootkey, subkey, access);
+ } else {
+ DCHECK(!subkey);
+ wow64access_ = access & kWow64AccessMask;
+ }
+}
+
+RegKey::~RegKey() {
+ Close();
+}
+
+LONG RegKey::Create(HKEY rootkey, const char16_t* subkey, REGSAM access) {
+ DWORD disposition_value;
+ return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
+}
+
+LONG RegKey::CreateWithDisposition(HKEY rootkey,
+ const char16_t* subkey,
+ DWORD* disposition,
+ REGSAM access) {
+ DCHECK(rootkey && subkey && access && disposition);
+ HKEY subhkey = NULL;
+ LONG result = RegCreateKeyEx(rootkey, ToWCharT(subkey), 0, NULL,
+ REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
+ disposition);
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subhkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
+
+ return result;
+}
+
+LONG RegKey::CreateKey(const char16_t* name, REGSAM access) {
+ DCHECK(name && access);
+ // After the application has accessed an alternate registry view using one of
+ // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
+ // (create, delete, or open) on child registry keys must explicitly use the
+ // same flag. Otherwise, there can be unexpected behavior.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
+ if ((access & kWow64AccessMask) != wow64access_) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+ HKEY subkey = NULL;
+ LONG result =
+ RegCreateKeyEx(key_, ToWCharT(name), 0, NULL, REG_OPTION_NON_VOLATILE,
+ access, NULL, &subkey, NULL);
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
+
+ return result;
+}
+
+LONG RegKey::Open(HKEY rootkey, const char16_t* subkey, REGSAM access) {
+ DCHECK(rootkey && subkey && access);
+ HKEY subhkey = NULL;
+
+ LONG result = RegOpenKeyEx(rootkey, ToWCharT(subkey), 0, access, &subhkey);
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subhkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
+
+ return result;
+}
+
+LONG RegKey::OpenKey(const char16_t* relative_key_name, REGSAM access) {
+ DCHECK(relative_key_name && access);
+ // After the application has accessed an alternate registry view using one of
+ // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
+ // (create, delete, or open) on child registry keys must explicitly use the
+ // same flag. Otherwise, there can be unexpected behavior.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
+ if ((access & kWow64AccessMask) != wow64access_) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+ HKEY subkey = NULL;
+ LONG result =
+ RegOpenKeyEx(key_, ToWCharT(relative_key_name), 0, access, &subkey);
+
+ // We have to close the current opened key before replacing it with the new
+ // one.
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
+ return result;
+}
+
+void RegKey::Close() {
+ if (key_) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ wow64access_ = 0;
+ }
+}
+
+// TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
+void RegKey::Set(HKEY key) {
+ if (key_ != key) {
+ Close();
+ key_ = key;
+ }
+}
+
+HKEY RegKey::Take() {
+ DCHECK_EQ(wow64access_, 0u);
+ HKEY key = key_;
+ key_ = NULL;
+ return key;
+}
+
+bool RegKey::HasValue(const char16_t* name) const {
+ return RegQueryValueEx(key_, ToWCharT(name), 0, NULL, NULL, NULL) ==
+ ERROR_SUCCESS;
+}
+
+DWORD RegKey::GetValueCount() const {
+ DWORD count = 0;
+ LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+ return (result == ERROR_SUCCESS) ? count : 0;
+}
+
+LONG RegKey::GetValueNameAt(int index, std::u16string* name) const {
+ char16_t buf[256];
+ DWORD bufsize = std::size(buf);
+ LONG r = ::RegEnumValue(key_, index, ToWCharT(buf), &bufsize, NULL, NULL,
+ NULL, NULL);
+ if (r == ERROR_SUCCESS)
+ *name = buf;
+
+ return r;
+}
+
+LONG RegKey::DeleteKey(const char16_t* name) {
+ DCHECK(key_);
+ DCHECK(name);
+ HKEY subkey = NULL;
+
+ // Verify the key exists before attempting delete to replicate previous
+ // behavior.
+ LONG result = RegOpenKeyEx(key_, ToWCharT(name), 0,
+ READ_CONTROL | wow64access_, &subkey);
+ if (result != ERROR_SUCCESS)
+ return result;
+ RegCloseKey(subkey);
+
+ return RegDelRecurse(key_, std::u16string(name), wow64access_);
+}
+
+LONG RegKey::DeleteEmptyKey(const char16_t* name) {
+ DCHECK(key_);
+ DCHECK(name);
+
+ HKEY target_key = NULL;
+ LONG result = RegOpenKeyEx(key_, ToWCharT(name), 0, KEY_READ | wow64access_,
+ &target_key);
+
+ if (result != ERROR_SUCCESS)
+ return result;
+
+ DWORD count = 0;
+ result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+
+ RegCloseKey(target_key);
+
+ if (result != ERROR_SUCCESS)
+ return result;
+
+ if (count == 0)
+ return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
+
+ return ERROR_DIR_NOT_EMPTY;
+}
+
+LONG RegKey::DeleteValue(const char16_t* value_name) {
+ DCHECK(key_);
+ LONG result = RegDeleteValue(key_, ToWCharT(value_name));
+ return result;
+}
+
+LONG RegKey::ReadValueDW(const char16_t* name, DWORD* out_value) const {
+ DCHECK(out_value);
+ DWORD type = REG_DWORD;
+ DWORD size = sizeof(DWORD);
+ DWORD local_value = 0;
+ LONG result = ReadValue(name, &local_value, &size, &type);
+ if (result == ERROR_SUCCESS) {
+ if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
+ *out_value = local_value;
+ else
+ result = ERROR_CANTREAD;
+ }
+
+ return result;
+}
+
+LONG RegKey::ReadInt64(const char16_t* name, int64_t* out_value) const {
+ DCHECK(out_value);
+ DWORD type = REG_QWORD;
+ int64_t local_value = 0;
+ DWORD size = sizeof(local_value);
+ LONG result = ReadValue(name, &local_value, &size, &type);
+ if (result == ERROR_SUCCESS) {
+ if ((type == REG_QWORD || type == REG_BINARY) &&
+ size == sizeof(local_value))
+ *out_value = local_value;
+ else
+ result = ERROR_CANTREAD;
+ }
+
+ return result;
+}
+
+LONG RegKey::ReadValue(const char16_t* name, std::u16string* out_value) const {
+ DCHECK(out_value);
+ const size_t kMaxStringLength = 1024; // This is after expansion.
+ // Use the one of the other forms of ReadValue if 1024 is too small for you.
+ char16_t raw_value[kMaxStringLength];
+ DWORD type = REG_SZ, size = sizeof(raw_value);
+ LONG result = ReadValue(name, raw_value, &size, &type);
+ if (result == ERROR_SUCCESS) {
+ if (type == REG_SZ) {
+ *out_value = raw_value;
+ } else if (type == REG_EXPAND_SZ) {
+ char16_t expanded[kMaxStringLength];
+ size = ExpandEnvironmentStrings(ToWCharT(raw_value), ToWCharT(expanded),
+ kMaxStringLength);
+ // Success: returns the number of char16_t's copied
+ // Fail: buffer too small, returns the size required
+ // Fail: other, returns 0
+ if (size == 0 || size > kMaxStringLength) {
+ result = ERROR_MORE_DATA;
+ } else {
+ *out_value = expanded;
+ }
+ } else {
+ // Not a string. Oops.
+ result = ERROR_CANTREAD;
+ }
+ }
+
+ return result;
+}
+
+LONG RegKey::ReadValue(const char16_t* name,
+ void* data,
+ DWORD* dsize,
+ DWORD* dtype) const {
+ LONG result = RegQueryValueEx(key_, ToWCharT(name), 0, dtype,
+ reinterpret_cast<LPBYTE>(data), dsize);
+ return result;
+}
+
+LONG RegKey::ReadValues(const char16_t* name,
+ std::vector<std::u16string>* values) {
+ values->clear();
+
+ DWORD type = REG_MULTI_SZ;
+ DWORD size = 0;
+ LONG result = ReadValue(name, NULL, &size, &type);
+ if (result != ERROR_SUCCESS || size == 0)
+ return result;
+
+ if (type != REG_MULTI_SZ)
+ return ERROR_CANTREAD;
+
+ std::vector<char16_t> buffer(size / sizeof(char16_t));
+ result = ReadValue(name, &buffer[0], &size, NULL);
+ if (result != ERROR_SUCCESS || size == 0)
+ return result;
+
+ // Parse the double-null-terminated list of strings.
+ // Note: This code is paranoid to not read outside of |buf|, in the case where
+ // it may not be properly terminated.
+ const char16_t* entry = &buffer[0];
+ const char16_t* buffer_end = entry + (size / sizeof(char16_t));
+ while (entry < buffer_end && entry[0] != '\0') {
+ const char16_t* entry_end = std::find(entry, buffer_end, L'\0');
+ values->push_back(std::u16string(entry, entry_end));
+ entry = entry_end + 1;
+ }
+ return 0;
+}
+
+LONG RegKey::WriteValue(const char16_t* name, DWORD in_value) {
+ return WriteValue(name, &in_value, static_cast<DWORD>(sizeof(in_value)),
+ REG_DWORD);
+}
+
+LONG RegKey::WriteValue(const char16_t* name, const char16_t* in_value) {
+ return WriteValue(
+ name, in_value,
+ static_cast<DWORD>(sizeof(*in_value) * (wcslen(ToWCharT(in_value)) + 1)),
+ REG_SZ);
+}
+
+LONG RegKey::WriteValue(const char16_t* name,
+ const void* data,
+ DWORD dsize,
+ DWORD dtype) {
+ DCHECK(data || !dsize);
+
+ LONG result =
+ RegSetValueEx(key_, ToWCharT(name), 0, dtype,
+ reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
+ return result;
+}
+
+// static
+LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
+ const char16_t* lpSubKey,
+ REGSAM samDesired,
+ DWORD Reserved) {
+ typedef LSTATUS(WINAPI * RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
+
+ RegDeleteKeyExPtr reg_delete_key_ex_func =
+ reinterpret_cast<RegDeleteKeyExPtr>(
+ GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
+
+ if (reg_delete_key_ex_func)
+ return reg_delete_key_ex_func(hKey, ToWCharT(lpSubKey), samDesired,
+ Reserved);
+
+ // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
+ return RegDeleteKey(hKey, ToWCharT(lpSubKey));
+}
+
+// static
+LONG RegKey::RegDelRecurse(HKEY root_key,
+ const std::u16string& name,
+ REGSAM access) {
+ // First, see if the key can be deleted without having to recurse.
+ LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
+ if (result == ERROR_SUCCESS)
+ return result;
+
+ HKEY target_key = NULL;
+ result = RegOpenKeyEx(root_key, ToWCharT(&name), 0,
+ KEY_ENUMERATE_SUB_KEYS | access, &target_key);
+
+ if (result == ERROR_FILE_NOT_FOUND)
+ return ERROR_SUCCESS;
+ if (result != ERROR_SUCCESS)
+ return result;
+
+ std::u16string subkey_name(name);
+
+ // Check for an ending slash and add one if it is missing.
+ if (!name.empty() && subkey_name[name.length() - 1] != '\\')
+ subkey_name += u"\\";
+
+ // Enumerate the keys
+ result = ERROR_SUCCESS;
+ const DWORD kMaxKeyNameLength = MAX_PATH;
+ const size_t base_key_length = subkey_name.length();
+ std::u16string key_name;
+ while (result == ERROR_SUCCESS) {
+ DWORD key_size = kMaxKeyNameLength;
+ result = RegEnumKeyEx(target_key, 0,
+ ToWCharT(WriteInto(&key_name, kMaxKeyNameLength)),
+ &key_size, NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS)
+ break;
+
+ key_name.resize(key_size);
+ subkey_name.resize(base_key_length);
+ subkey_name += key_name;
+
+ if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
+ break;
+ }
+
+ RegCloseKey(target_key);
+
+ // Try again to delete the key.
+ result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
+
+ return result;
+}
+
+// RegistryValueIterator ------------------------------------------------------
+
+RegistryValueIterator::RegistryValueIterator(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access)
+ : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') {
+ Initialize(root_key, folder_key, wow64access);
+}
+
+RegistryValueIterator::RegistryValueIterator(HKEY root_key,
+ const char16_t* folder_key)
+ : name_(MAX_PATH, L'\0'), value_(MAX_PATH, L'\0') {
+ Initialize(root_key, folder_key, 0);
+}
+
+void RegistryValueIterator::Initialize(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access) {
+ DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
+ LONG result = RegOpenKeyEx(root_key, ToWCharT(folder_key), 0,
+ KEY_READ | wow64access, &key_);
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ } else {
+ DWORD count = 0;
+ result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ } else {
+ index_ = count - 1;
+ }
+ }
+
+ Read();
+}
+
+RegistryValueIterator::~RegistryValueIterator() {
+ if (key_)
+ ::RegCloseKey(key_);
+}
+
+DWORD RegistryValueIterator::ValueCount() const {
+ DWORD count = 0;
+ LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+ if (result != ERROR_SUCCESS)
+ return 0;
+
+ return count;
+}
+
+bool RegistryValueIterator::Valid() const {
+ return key_ != NULL && index_ >= 0;
+}
+
+void RegistryValueIterator::operator++() {
+ --index_;
+ Read();
+}
+
+bool RegistryValueIterator::Read() {
+ if (Valid()) {
+ DWORD capacity = static_cast<DWORD>(name_.capacity());
+ DWORD name_size = capacity;
+ // |value_size_| is in bytes. Reserve the last character for a NUL.
+ value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(char16_t));
+ LONG result = ::RegEnumValue(
+ key_, index_, ToWCharT(WriteInto(&name_, name_size)), &name_size, NULL,
+ &type_, reinterpret_cast<BYTE*>(value_.data()), &value_size_);
+
+ if (result == ERROR_MORE_DATA) {
+ // Registry key names are limited to 255 characters and fit within
+ // MAX_PATH (which is 260) but registry value names can use up to 16,383
+ // characters and the value itself is not limited
+ // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
+ // ms724872(v=vs.85).aspx).
+ // Resize the buffers and retry if their size caused the failure.
+ DWORD value_size_in_wchars = to_wchar_size(value_size_);
+ if (value_size_in_wchars + 1 > value_.size())
+ value_.resize(value_size_in_wchars + 1, L'\0');
+ value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(char16_t));
+ name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
+ result = ::RegEnumValue(
+ key_, index_, ToWCharT(WriteInto(&name_, name_size)), &name_size,
+ NULL, &type_, reinterpret_cast<BYTE*>(value_.data()), &value_size_);
+ }
+
+ if (result == ERROR_SUCCESS) {
+ DCHECK_LT(to_wchar_size(value_size_), value_.size());
+ value_[to_wchar_size(value_size_)] = L'\0';
+ return true;
+ }
+ }
+
+ name_[0] = L'\0';
+ value_[0] = L'\0';
+ value_size_ = 0;
+ return false;
+}
+
+// RegistryKeyIterator --------------------------------------------------------
+
+RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
+ const char16_t* folder_key) {
+ Initialize(root_key, folder_key, 0);
+}
+
+RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access) {
+ Initialize(root_key, folder_key, wow64access);
+}
+
+RegistryKeyIterator::~RegistryKeyIterator() {
+ if (key_)
+ ::RegCloseKey(key_);
+}
+
+DWORD RegistryKeyIterator::SubkeyCount() const {
+ DWORD count = 0;
+ LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ if (result != ERROR_SUCCESS)
+ return 0;
+
+ return count;
+}
+
+bool RegistryKeyIterator::Valid() const {
+ return key_ != NULL && index_ >= 0;
+}
+
+void RegistryKeyIterator::operator++() {
+ --index_;
+ Read();
+}
+
+bool RegistryKeyIterator::Read() {
+ if (Valid()) {
+ DWORD ncount = std::size(name_);
+ FILETIME written;
+ LONG r = ::RegEnumKeyEx(key_, index_, ToWCharT(name_), &ncount, NULL, NULL,
+ NULL, &written);
+ if (ERROR_SUCCESS == r)
+ return true;
+ }
+
+ name_[0] = '\0';
+ return false;
+}
+
+void RegistryKeyIterator::Initialize(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access) {
+ DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
+ LONG result = RegOpenKeyEx(root_key, ToWCharT(folder_key), 0,
+ KEY_READ | wow64access, &key_);
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ } else {
+ DWORD count = 0;
+ result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ } else {
+ index_ = count - 1;
+ }
+ }
+
+ Read();
+}
+
+} // namespace win
+} // namespace base
diff --git a/gn/src/base/win/registry.h b/gn/src/base/win/registry.h
new file mode 100644
index 00000000000..f56d2a2bed1
--- /dev/null
+++ b/gn/src/base/win/registry.h
@@ -0,0 +1,252 @@
+// 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 BASE_WIN_REGISTRY_H_
+#define BASE_WIN_REGISTRY_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include <windows.h>
+
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+// Utility class to read, write and manipulate the Windows Registry.
+// Registry vocabulary primer: a "key" is like a folder, in which there
+// are "values", which are <name, data> pairs, with an associated data type.
+//
+// Note:
+// * ReadValue family of functions guarantee that the out-parameter
+// is not touched in case of failure.
+// * Functions returning LONG indicate success as ERROR_SUCCESS or an
+// error as a (non-zero) win32 error code.
+class RegKey {
+ public:
+ RegKey();
+ explicit RegKey(HKEY key);
+ RegKey(HKEY rootkey, const char16_t* subkey, REGSAM access);
+ ~RegKey();
+
+ LONG Create(HKEY rootkey, const char16_t* subkey, REGSAM access);
+
+ LONG CreateWithDisposition(HKEY rootkey,
+ const char16_t* subkey,
+ DWORD* disposition,
+ REGSAM access);
+
+ // Creates a subkey or open it if it already exists.
+ LONG CreateKey(const char16_t* name, REGSAM access);
+
+ // Opens an existing reg key.
+ LONG Open(HKEY rootkey, const char16_t* subkey, REGSAM access);
+
+ // Opens an existing reg key, given the relative key name.
+ LONG OpenKey(const char16_t* relative_key_name, REGSAM access);
+
+ // Closes this reg key.
+ void Close();
+
+ // Replaces the handle of the registry key and takes ownership of the handle.
+ void Set(HKEY key);
+
+ // Transfers ownership away from this object.
+ HKEY Take();
+
+ // Returns false if this key does not have the specified value, or if an error
+ // occurrs while attempting to access it.
+ bool HasValue(const char16_t* value_name) const;
+
+ // Returns the number of values for this key, or 0 if the number cannot be
+ // determined.
+ DWORD GetValueCount() const;
+
+ // Determines the nth value's name.
+ LONG GetValueNameAt(int index, std::u16string* name) const;
+
+ // True while the key is valid.
+ bool Valid() const { return key_ != NULL; }
+
+ // Kills a key and everything that lives below it; please be careful when
+ // using it.
+ LONG DeleteKey(const char16_t* name);
+
+ // Deletes an empty subkey. If the subkey has subkeys or values then this
+ // will fail.
+ LONG DeleteEmptyKey(const char16_t* name);
+
+ // Deletes a single value within the key.
+ LONG DeleteValue(const char16_t* name);
+
+ // Getters:
+
+ // Reads a REG_DWORD (uint32_t) into |out_value|. If |name| is null or empty,
+ // reads the key's default value, if any.
+ LONG ReadValueDW(const char16_t* name, DWORD* out_value) const;
+
+ // Reads a REG_QWORD (int64_t) into |out_value|. If |name| is null or empty,
+ // reads the key's default value, if any.
+ LONG ReadInt64(const char16_t* name, int64_t* out_value) const;
+
+ // Reads a string into |out_value|. If |name| is null or empty, reads
+ // the key's default value, if any.
+ LONG ReadValue(const char16_t* name, std::u16string* out_value) const;
+
+ // Reads a REG_MULTI_SZ registry field into a vector of strings. Clears
+ // |values| initially and adds further strings to the list. Returns
+ // ERROR_CANTREAD if type is not REG_MULTI_SZ.
+ LONG ReadValues(const char16_t* name, std::vector<std::u16string>* values);
+
+ // Reads raw data into |data|. If |name| is null or empty, reads the key's
+ // default value, if any.
+ LONG ReadValue(const char16_t* name,
+ void* data,
+ DWORD* dsize,
+ DWORD* dtype) const;
+
+ // Setters:
+
+ // Sets an int32_t value.
+ LONG WriteValue(const char16_t* name, DWORD in_value);
+
+ // Sets a string value.
+ LONG WriteValue(const char16_t* name, const char16_t* in_value);
+
+ // Sets raw data, including type.
+ LONG WriteValue(const char16_t* name,
+ const void* data,
+ DWORD dsize,
+ DWORD dtype);
+
+ HKEY Handle() const { return key_; }
+
+ private:
+ // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to
+ // RegDeleteKey.
+ static LONG RegDeleteKeyExWrapper(HKEY hKey,
+ const char16_t* lpSubKey,
+ REGSAM samDesired,
+ DWORD Reserved);
+
+ // Recursively deletes a key and all of its subkeys.
+ static LONG RegDelRecurse(HKEY root_key,
+ const std::u16string& name,
+ REGSAM access);
+
+ HKEY key_; // The registry key being iterated.
+ REGSAM wow64access_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegKey);
+};
+
+// Iterates the entries found in a particular folder on the registry.
+class RegistryValueIterator {
+ public:
+ // Constructs a Registry Value Iterator with default WOW64 access.
+ RegistryValueIterator(HKEY root_key, const char16_t* folder_key);
+
+ // Constructs a Registry Key Iterator with specific WOW64 access, one of
+ // KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0.
+ // Note: |wow64access| should be the same access used to open |root_key|
+ // previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE).
+ // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
+ RegistryValueIterator(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access);
+
+ ~RegistryValueIterator();
+
+ DWORD ValueCount() const;
+
+ // True while the iterator is valid.
+ bool Valid() const;
+
+ // Advances to the next registry entry.
+ void operator++();
+
+ const char16_t* Name() const { return name_.c_str(); }
+ const char16_t* Value() const { return value_.data(); }
+ // ValueSize() is in bytes.
+ DWORD ValueSize() const { return value_size_; }
+ DWORD Type() const { return type_; }
+
+ int Index() const { return index_; }
+
+ private:
+ // Reads in the current values.
+ bool Read();
+
+ void Initialize(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access);
+
+ // The registry key being iterated.
+ HKEY key_;
+
+ // Current index of the iteration.
+ int index_;
+
+ // Current values.
+ std::u16string name_;
+ std::vector<char16_t> value_;
+ DWORD value_size_;
+ DWORD type_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryValueIterator);
+};
+
+class RegistryKeyIterator {
+ public:
+ // Constructs a Registry Key Iterator with default WOW64 access.
+ RegistryKeyIterator(HKEY root_key, const char16_t* folder_key);
+
+ // Constructs a Registry Value Iterator with specific WOW64 access, one of
+ // KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0.
+ // Note: |wow64access| should be the same access used to open |root_key|
+ // previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE).
+ // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
+ RegistryKeyIterator(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access);
+
+ ~RegistryKeyIterator();
+
+ DWORD SubkeyCount() const;
+
+ // True while the iterator is valid.
+ bool Valid() const;
+
+ // Advances to the next entry in the folder.
+ void operator++();
+
+ const char16_t* Name() const { return name_; }
+
+ int Index() const { return index_; }
+
+ private:
+ // Reads in the current values.
+ bool Read();
+
+ void Initialize(HKEY root_key,
+ const char16_t* folder_key,
+ REGSAM wow64access);
+
+ // The registry key being iterated.
+ HKEY key_;
+
+ // Current index of the iteration.
+ int index_;
+
+ char16_t name_[MAX_PATH];
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryKeyIterator);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_REGISTRY_H_
diff --git a/gn/src/base/win/scoped_handle.cc b/gn/src/base/win/scoped_handle.cc
new file mode 100644
index 00000000000..cc034afe90a
--- /dev/null
+++ b/gn/src/base/win/scoped_handle.cc
@@ -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.
+
+#include "base/win/scoped_handle.h"
+#include <windows.h>
+
+namespace base {
+namespace win {
+
+// Static.
+bool HandleTraits::CloseHandle(HANDLE handle) {
+ return ::CloseHandle(handle);
+}
+
+// Static.
+void VerifierTraits::StartTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {}
+
+// Static.
+void VerifierTraits::StopTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {}
+
+void DisableHandleVerifier() {}
+
+void OnHandleBeingClosed(HANDLE handle) {}
+
+} // namespace win
+} // namespace base
diff --git a/gn/src/base/win/scoped_handle.h b/gn/src/base/win/scoped_handle.h
new file mode 100644
index 00000000000..4573af397cb
--- /dev/null
+++ b/gn/src/base/win/scoped_handle.h
@@ -0,0 +1,172 @@
+// 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 BASE_WIN_SCOPED_HANDLE_H_
+#define BASE_WIN_SCOPED_HANDLE_H_
+
+#include <windows.h>
+
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+// TODO(rvargas): remove this with the rest of the verifier.
+#if defined(COMPILER_MSVC)
+#include <intrin.h>
+#define BASE_WIN_GET_CALLER _ReturnAddress()
+#elif defined(COMPILER_GCC)
+#define BASE_WIN_GET_CALLER \
+ __builtin_extract_return_addr(\ __builtin_return_address(0))
+#endif
+
+namespace base {
+namespace win {
+
+// Generic wrapper for raw handles that takes care of closing handles
+// automatically. The class interface follows the style of
+// the ScopedFILE class with two additions:
+// - IsValid() method can tolerate multiple invalid handle values such as NULL
+// and INVALID_HANDLE_VALUE (-1) for Win32 handles.
+// - Set() (and the constructors and assignment operators that call it)
+// preserve the Windows LastError code. This ensures that GetLastError() can
+// be called after stashing a handle in a GenericScopedHandle object. Doing
+// this explicitly is necessary because of bug 528394 and VC++ 2015.
+template <class Traits, class Verifier>
+class GenericScopedHandle {
+ public:
+ typedef typename Traits::Handle Handle;
+
+ GenericScopedHandle() : handle_(Traits::NullHandle()) {}
+
+ explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) {
+ Set(handle);
+ }
+
+ GenericScopedHandle(GenericScopedHandle&& other)
+ : handle_(Traits::NullHandle()) {
+ Set(other.Take());
+ }
+
+ ~GenericScopedHandle() { Close(); }
+
+ bool IsValid() const { return Traits::IsHandleValid(handle_); }
+
+ GenericScopedHandle& operator=(GenericScopedHandle&& other) {
+ DCHECK_NE(this, &other);
+ Set(other.Take());
+ return *this;
+ }
+
+ void Set(Handle handle) {
+ if (handle_ != handle) {
+ // Preserve old LastError to avoid bug 528394.
+ auto last_error = ::GetLastError();
+ Close();
+
+ if (Traits::IsHandleValid(handle)) {
+ handle_ = handle;
+ }
+ ::SetLastError(last_error);
+ }
+ }
+
+ Handle Get() const { return handle_; }
+
+ // Transfers ownership away from this object.
+ Handle Take() {
+ Handle temp = handle_;
+ handle_ = Traits::NullHandle();
+ return temp;
+ }
+
+ // Explicitly closes the owned handle.
+ void Close() {
+ if (Traits::IsHandleValid(handle_)) {
+ Traits::CloseHandle(handle_);
+ handle_ = Traits::NullHandle();
+ }
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierWrongOwner);
+ FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierUntrackedHandle);
+ Handle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(GenericScopedHandle);
+};
+
+#undef BASE_WIN_GET_CALLER
+
+// The traits class for Win32 handles that can be closed via CloseHandle() API.
+class HandleTraits {
+ public:
+ typedef HANDLE Handle;
+
+ // Closes the handle.
+ static bool CloseHandle(HANDLE handle);
+
+ // Returns true if the handle value is valid.
+ static bool IsHandleValid(HANDLE handle) {
+ return handle != NULL && handle != INVALID_HANDLE_VALUE;
+ }
+
+ // Returns NULL handle value.
+ static HANDLE NullHandle() { return NULL; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HandleTraits);
+};
+
+// Do-nothing verifier.
+class DummyVerifierTraits {
+ public:
+ typedef HANDLE Handle;
+
+ static void StartTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {}
+ static void StopTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {}
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DummyVerifierTraits);
+};
+
+// Performs actual run-time tracking.
+class VerifierTraits {
+ public:
+ typedef HANDLE Handle;
+
+ static void StartTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2);
+ static void StopTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits);
+};
+
+typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle;
+
+// This function may be called by the embedder to disable the use of
+// VerifierTraits at runtime. It has no effect if DummyVerifierTraits is used
+// for ScopedHandle.
+void DisableHandleVerifier();
+
+// This should be called whenever the OS is closing a handle, if extended
+// verification of improper handle closing is desired. If |handle| is being
+// tracked by the handle verifier and ScopedHandle is not the one closing it,
+// a CHECK is generated.
+void OnHandleBeingClosed(HANDLE handle);
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_HANDLE_H_
diff --git a/gn/base/win/scoped_process_information.cc b/gn/src/base/win/scoped_process_information.cc
index 93fb9e63449..93fb9e63449 100644
--- a/gn/base/win/scoped_process_information.cc
+++ b/gn/src/base/win/scoped_process_information.cc
diff --git a/gn/base/win/scoped_process_information.h b/gn/src/base/win/scoped_process_information.h
index 557555deba4..557555deba4 100644
--- a/gn/base/win/scoped_process_information.h
+++ b/gn/src/base/win/scoped_process_information.h
diff --git a/gn/src/base/win/win_util.h b/gn/src/base/win/win_util.h
new file mode 100644
index 00000000000..cab52f71ff1
--- /dev/null
+++ b/gn/src/base/win/win_util.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium 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 BASE_WIN_WIN_UTIL_H_
+#define BASE_WIN_WIN_UTIL_H_
+
+#include <string>
+#include <string_view>
+
+namespace base {
+
+// Windows API calls take wchar_t but on that platform wchar_t should be the
+// same as a char16_t.
+inline const wchar_t* ToWCharT(const std::u16string* s) {
+ static_assert(sizeof(std::u16string::value_type) == sizeof(wchar_t));
+ return reinterpret_cast<const wchar_t*>(s->c_str());
+}
+
+inline const wchar_t* ToWCharT(const char16_t* s) {
+ return reinterpret_cast<const wchar_t*>(s);
+}
+
+inline wchar_t* ToWCharT(char16_t* s) {
+ return reinterpret_cast<wchar_t*>(s);
+}
+
+} // namespace base
+
+#endif // BASE_WIN_WIN_UTIL_H_
diff --git a/gn/src/gn/action_target_generator.cc b/gn/src/gn/action_target_generator.cc
new file mode 100644
index 00000000000..73fa28895a7
--- /dev/null
+++ b/gn/src/gn/action_target_generator.cc
@@ -0,0 +1,223 @@
+// 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 "gn/action_target_generator.h"
+
+#include "base/stl_util.h"
+#include "gn/build_settings.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+ActionTargetGenerator::ActionTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
+
+ActionTargetGenerator::~ActionTargetGenerator() = default;
+
+void ActionTargetGenerator::DoRun() {
+ target_->set_output_type(output_type_);
+
+ if (!FillSources())
+ return;
+ if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) {
+ // Foreach rules must always have some sources to have an effect.
+ *err_ =
+ Err(function_call_, "action_foreach target has no sources.",
+ "If you don't specify any sources, there is nothing to run your\n"
+ "script over.");
+ return;
+ }
+
+ if (!FillInputs())
+ return;
+
+ if (!FillScript())
+ return;
+
+ if (!FillScriptArgs())
+ return;
+
+ if (!FillResponseFileContents())
+ return;
+
+ if (!FillOutputs(output_type_ == Target::ACTION_FOREACH))
+ return;
+
+ if (!FillDepfile())
+ return;
+
+ if (!FillPool())
+ return;
+
+ if (!FillCheckIncludes())
+ return;
+
+ if (!CheckOutputs())
+ return;
+
+ // Action outputs don't depend on the current toolchain so we can skip adding
+ // that dependency.
+
+ // response_file_contents and {{response_file_name}} in the args must go
+ // together.
+ const auto& required_args_substitutions =
+ target_->action_values().args().required_types();
+ bool has_rsp_file_name = base::ContainsValue(required_args_substitutions,
+ &SubstitutionRspFileName);
+ if (target_->action_values().uses_rsp_file() && !has_rsp_file_name) {
+ *err_ = Err(
+ function_call_, "Missing {{response_file_name}} in args.",
+ "This target defines response_file_contents but doesn't use\n"
+ "{{response_file_name}} in the args, which means the response file\n"
+ "will be unused.");
+ return;
+ }
+ if (!target_->action_values().uses_rsp_file() && has_rsp_file_name) {
+ *err_ = Err(
+ function_call_, "Missing response_file_contents definition.",
+ "This target uses {{response_file_name}} in the args, but does not\n"
+ "define response_file_contents which means the response file\n"
+ "will be empty.");
+ return;
+ }
+}
+
+bool ActionTargetGenerator::FillScript() {
+ // If this gets called, the target type requires a script, so error out
+ // if it doesn't have one.
+ const Value* value = scope_->GetValue(variables::kScript, true);
+ if (!value) {
+ *err_ = Err(function_call_, "This target type requires a \"script\".");
+ return false;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
+ *value, err_, scope_->settings()->build_settings()->root_path_utf8());
+ if (err_->has_error())
+ return false;
+ target_->action_values().set_script(script_file);
+ return true;
+}
+
+bool ActionTargetGenerator::FillScriptArgs() {
+ const Value* value = scope_->GetValue(variables::kArgs, true);
+ if (!value)
+ return true; // Nothing to do.
+
+ if (!target_->action_values().args().Parse(*value, err_))
+ return false;
+ if (!EnsureValidSubstitutions(
+ target_->action_values().args().required_types(),
+ &IsValidScriptArgsSubstitution, value->origin(), err_))
+ return false;
+
+ return true;
+}
+
+bool ActionTargetGenerator::FillResponseFileContents() {
+ const Value* value = scope_->GetValue(variables::kResponseFileContents, true);
+ if (!value)
+ return true; // Nothing to do.
+
+ if (!target_->action_values().rsp_file_contents().Parse(*value, err_))
+ return false;
+ if (!EnsureValidSubstitutions(
+ target_->action_values().rsp_file_contents().required_types(),
+ &IsValidSourceSubstitution, value->origin(), err_))
+ return false;
+
+ return true;
+}
+
+bool ActionTargetGenerator::FillDepfile() {
+ const Value* value = scope_->GetValue(variables::kDepfile, true);
+ if (!value)
+ return true;
+
+ SubstitutionPattern depfile;
+ if (!depfile.Parse(*value, err_))
+ return false;
+ if (!EnsureSubstitutionIsInOutputDir(depfile, *value))
+ return false;
+
+ target_->action_values().set_depfile(depfile);
+ return true;
+}
+
+bool ActionTargetGenerator::FillPool() {
+ const Value* value = scope_->GetValue(variables::kPool, true);
+ if (!value)
+ return true;
+
+ Label label =
+ Label::Resolve(scope_->GetSourceDir(),
+ scope_->settings()->build_settings()->root_path_utf8(),
+ ToolchainLabelForScope(scope_), *value, err_);
+ if (err_->has_error())
+ return false;
+
+ LabelPtrPair<Pool> pair(label);
+ pair.origin = target_->defined_from();
+
+ target_->action_values().set_pool(std::move(pair));
+ return true;
+}
+
+bool ActionTargetGenerator::CheckOutputs() {
+ const SubstitutionList& outputs = target_->action_values().outputs();
+ if (outputs.list().empty()) {
+ *err_ =
+ Err(function_call_, "Action has no outputs.",
+ "If you have no outputs, the build system can not tell when your\n"
+ "script needs to be run.");
+ return false;
+ }
+
+ if (output_type_ == Target::ACTION) {
+ if (!outputs.required_types().empty()) {
+ *err_ = Err(
+ function_call_, "Action has patterns in the output.",
+ "An action target should have the outputs completely specified. If\n"
+ "you want to provide a mapping from source to output, use an\n"
+ "\"action_foreach\" target.");
+ return false;
+ }
+ } else if (output_type_ == Target::ACTION_FOREACH) {
+ // A foreach target should always have a pattern in the outputs.
+ if (outputs.required_types().empty()) {
+ *err_ = Err(
+ function_call_, "action_foreach should have a pattern in the output.",
+ "An action_foreach target should have a source expansion pattern in\n"
+ "it to map source file to unique output file name. Otherwise, the\n"
+ "build system can't determine when your script needs to be run.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ActionTargetGenerator::FillInputs() {
+ const Value* value = scope_->GetValue(variables::kInputs, true);
+ if (!value)
+ return true;
+
+ Target::FileList dest_inputs;
+ if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &dest_inputs, err_))
+ return false;
+ target_->config_values().inputs().swap(dest_inputs);
+ return true;
+}
diff --git a/gn/src/gn/action_target_generator.h b/gn/src/gn/action_target_generator.h
new file mode 100644
index 00000000000..5d7f88c527f
--- /dev/null
+++ b/gn/src/gn/action_target_generator.h
@@ -0,0 +1,41 @@
+// 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 TOOLS_GN_ACTION_TARGET_GENERATOR_H_
+#define TOOLS_GN_ACTION_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target.h"
+#include "gn/target_generator.h"
+
+// Populates a Target with the values from an action[_foreach] rule.
+class ActionTargetGenerator : public TargetGenerator {
+ public:
+ ActionTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err);
+ ~ActionTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ bool FillScript();
+ bool FillScriptArgs();
+ bool FillResponseFileContents();
+ bool FillDepfile();
+ bool FillPool();
+ bool FillInputs();
+
+ // Checks for errors in the outputs variable.
+ bool CheckOutputs();
+
+ Target::OutputType output_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActionTargetGenerator);
+};
+
+#endif // TOOLS_GN_ACTION_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/action_target_generator_unittest.cc b/gn/src/gn/action_target_generator_unittest.cc
new file mode 100644
index 00000000000..08ff8af3d81
--- /dev/null
+++ b/gn/src/gn/action_target_generator_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 "gn/scheduler.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using ActionTargetGenerator = TestWithScheduler;
+
+// Tests that actions can't have output substitutions.
+TEST_F(ActionTargetGenerator, ActionOutputSubstitutions) {
+ TestWithScope setup;
+ Scope::ItemVector items_;
+ setup.scope()->set_item_collector(&items_);
+
+ // First test one with no substitutions, this should be valid.
+ TestParseInput input_good(
+ R"(action("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/one.txt" ]
+ })");
+ ASSERT_FALSE(input_good.has_error());
+
+ // This should run fine.
+ Err err;
+ input_good.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // Same thing with a pattern in the output should fail.
+ TestParseInput input_bad(
+ R"(action("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/{{source_name_part}}.txt" ]
+ })");
+ ASSERT_FALSE(input_bad.has_error());
+
+ // This should run fine.
+ input_bad.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+}
+
+// Tests that arg and response file substitutions are validated for
+// action_foreach targets.
+TEST_F(ActionTargetGenerator, ActionForeachSubstitutions) {
+ TestWithScope setup;
+ Scope::ItemVector items_;
+ setup.scope()->set_item_collector(&items_);
+
+ // Args listing a response file but missing a response file definition should
+ // fail.
+ TestParseInput input_missing_resp_file(
+ R"(action_foreach("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/{{source_name_part}}" ]
+ args = [ "{{response_file_name}}" ]
+ })");
+ ASSERT_FALSE(input_missing_resp_file.has_error());
+ Err err;
+ input_missing_resp_file.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+
+ // Adding a response file definition should pass.
+ err = Err();
+ TestParseInput input_resp_file(
+ R"(action_foreach("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/{{source_name_part}}" ]
+ args = [ "{{response_file_name}}" ]
+ response_file_contents = [ "{{source_name_part}}" ]
+ })");
+ ASSERT_FALSE(input_resp_file.has_error());
+ input_resp_file.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // Defining a response file but not referencing it should fail.
+ err = Err();
+ TestParseInput input_missing_rsp_args(
+ R"(action_foreach("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/{{source_name_part}}" ]
+ args = [ "{{source_name_part}}" ]
+ response_file_contents = [ "{{source_name_part}}" ]
+ })");
+ ASSERT_FALSE(input_missing_rsp_args.has_error());
+ input_missing_rsp_args.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+
+ // Bad substitutions in args.
+ err = Err();
+ TestParseInput input_bad_args(
+ R"(action_foreach("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/{{source_name_part}}" ]
+ args = [ "{{response_file_name}} {{ldflags}}" ]
+ response_file_contents = [ "{{source_name_part}}" ]
+ })");
+ ASSERT_FALSE(input_bad_args.has_error());
+ input_bad_args.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+
+ // Bad substitutions in response file contents.
+ err = Err();
+ TestParseInput input_bad_rsp(
+ R"(action_foreach("foo") {
+ script = "//foo.py"
+ sources = [ "//bar.txt" ]
+ outputs = [ "//out/Debug/{{source_name_part}}" ]
+ args = [ "{{response_file_name}}" ]
+ response_file_contents = [ "{{source_name_part}} {{ldflags}}" ]
+ })");
+ ASSERT_FALSE(input_bad_rsp.has_error());
+ input_bad_rsp.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+}
diff --git a/gn/src/gn/action_values.cc b/gn/src/gn/action_values.cc
new file mode 100644
index 00000000000..56810e4c66f
--- /dev/null
+++ b/gn/src/gn/action_values.cc
@@ -0,0 +1,31 @@
+// 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 "gn/action_values.h"
+
+#include "gn/settings.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+
+ActionValues::ActionValues() = default;
+
+ActionValues::~ActionValues() = default;
+
+void ActionValues::GetOutputsAsSourceFiles(
+ const Target* target,
+ std::vector<SourceFile>* result) const {
+ if (target->output_type() == Target::BUNDLE_DATA) {
+ // The bundle_data target has no output, the real output will be generated
+ // by the create_bundle target.
+ } else if (target->output_type() == Target::COPY_FILES ||
+ target->output_type() == Target::ACTION_FOREACH) {
+ // Copy and foreach applies the outputs to the sources.
+ SubstitutionWriter::ApplyListToSources(target, target->settings(), outputs_,
+ target->sources(), result);
+ } else {
+ // Actions (and anything else that happens to specify an output) just use
+ // the output list with no substitution.
+ SubstitutionWriter::GetListAsSourceFiles(outputs_, result);
+ }
+}
diff --git a/gn/src/gn/action_values.h b/gn/src/gn/action_values.h
new file mode 100644
index 00000000000..af6ab12e370
--- /dev/null
+++ b/gn/src/gn/action_values.h
@@ -0,0 +1,70 @@
+// 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 TOOLS_GN_ACTION_VALUES_H_
+#define TOOLS_GN_ACTION_VALUES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/label_ptr.h"
+#include "gn/source_file.h"
+#include "gn/substitution_list.h"
+
+class Pool;
+class Target;
+
+// Holds the values (outputs, args, script name, etc.) for either an action or
+// an action_foreach target.
+class ActionValues {
+ public:
+ ActionValues();
+ ~ActionValues();
+
+ // Filename of the script to execute.
+ const SourceFile& script() const { return script_; }
+ void set_script(const SourceFile& s) { script_ = s; }
+
+ // Arguments to the script.
+ SubstitutionList& args() { return args_; }
+ const SubstitutionList& args() const { return args_; }
+
+ // Files created by the script. These are strings rather than SourceFiles
+ // since they will often contain {{source expansions}}.
+ SubstitutionList& outputs() { return outputs_; }
+ const SubstitutionList& outputs() const { return outputs_; }
+
+ // Expands the outputs() above to the final SourceFile list.
+ void GetOutputsAsSourceFiles(const Target* target,
+ std::vector<SourceFile>* result) const;
+
+ // Depfile generated by the script.
+ const SubstitutionPattern& depfile() const { return depfile_; }
+ bool has_depfile() const { return !depfile_.ranges().empty(); }
+ void set_depfile(const SubstitutionPattern& depfile) { depfile_ = depfile; }
+
+ // Response file contents. Empty means no response file.
+ SubstitutionList& rsp_file_contents() { return rsp_file_contents_; }
+ const SubstitutionList& rsp_file_contents() const {
+ return rsp_file_contents_;
+ }
+ bool uses_rsp_file() const { return !rsp_file_contents_.list().empty(); }
+
+ // Pool option
+ const LabelPtrPair<Pool>& pool() const { return pool_; }
+ void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
+
+ private:
+ SourceFile script_;
+ SubstitutionList args_;
+ SubstitutionList outputs_;
+ SubstitutionPattern depfile_;
+ SubstitutionList rsp_file_contents_;
+ LabelPtrPair<Pool> pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActionValues);
+};
+
+#endif // TOOLS_GN_ACTION_VALUES_H_
diff --git a/gn/src/gn/analyzer.cc b/gn/src/gn/analyzer.cc
new file mode 100644
index 00000000000..a42779ae5dd
--- /dev/null
+++ b/gn/src/gn/analyzer.cc
@@ -0,0 +1,492 @@
+// Copyright 2016 The Chromium 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 "gn/analyzer.h"
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "gn/builder.h"
+#include "gn/config.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/loader.h"
+#include "gn/location.h"
+#include "gn/pool.h"
+#include "gn/source_file.h"
+#include "gn/target.h"
+
+namespace {
+
+struct Inputs {
+ std::vector<SourceFile> source_vec;
+ std::vector<Label> compile_vec;
+ std::vector<Label> test_vec;
+ bool compile_included_all = false;
+ std::set<const SourceFile*> source_files;
+ std::set<Label> compile_labels;
+ std::set<Label> test_labels;
+};
+
+struct Outputs {
+ std::string status;
+ std::string error;
+ bool compile_includes_all = false;
+ std::set<Label> compile_labels;
+ std::set<Label> test_labels;
+ std::set<Label> invalid_labels;
+};
+
+std::set<Label> LabelsFor(const std::set<const Target*>& targets) {
+ std::set<Label> labels;
+ for (auto* target : targets)
+ labels.insert(target->label());
+ return labels;
+}
+
+std::set<const Target*> Intersect(const std::set<const Target*>& l,
+ const std::set<const Target*>& r) {
+ std::set<const Target*> result;
+ std::set_intersection(l.begin(), l.end(), r.begin(), r.end(),
+ std::inserter(result, result.begin()));
+ return result;
+}
+
+std::vector<std::string> GetStringVector(const base::DictionaryValue& dict,
+ const std::string& key,
+ Err* err) {
+ std::vector<std::string> strings;
+ const base::ListValue* lst;
+ bool ret = dict.GetList(key, &lst);
+ if (!ret) {
+ *err = Err(Location(), "Input does not have a key named \"" + key +
+ "\" with a list value.");
+ return strings;
+ }
+
+ for (size_t i = 0; i < lst->GetSize(); i++) {
+ std::string s;
+ ret = lst->GetString(i, &s);
+ if (!ret) {
+ *err = Err(Location(), "Item " + std::to_string(i) + " of \"" + key +
+ "\" is not a string.");
+ strings.clear();
+ return strings;
+ }
+ strings.push_back(std::move(s));
+ }
+ *err = Err();
+ return strings;
+}
+
+void WriteString(base::DictionaryValue& dict,
+ const std::string& key,
+ const std::string& value) {
+ dict.SetKey(key, base::Value(value));
+};
+
+void WriteLabels(const Label& default_toolchain,
+ base::DictionaryValue& dict,
+ const std::string& key,
+ const std::set<Label>& labels) {
+ std::vector<std::string> strings;
+ auto value = std::make_unique<base::ListValue>();
+ for (const auto& l : labels)
+ strings.push_back(l.GetUserVisibleName(default_toolchain));
+ std::sort(strings.begin(), strings.end());
+ value->AppendStrings(strings);
+ dict.SetWithoutPathExpansion(key, std::move(value));
+}
+
+Label AbsoluteOrSourceAbsoluteStringToLabel(const Label& default_toolchain,
+ const std::string& s,
+ Err* err) {
+ if (!IsPathSourceAbsolute(s) && !IsPathAbsolute(s)) {
+ *err = Err(Location(),
+ "\"" + s + "\" is not a source-absolute or absolute path.");
+ return Label();
+ }
+ return Label::Resolve(SourceDir("//"), std::string_view(), default_toolchain,
+ Value(nullptr, s), err);
+}
+
+Err JSONToInputs(const Label& default_toolchain,
+ const std::string input,
+ Inputs* inputs) {
+ int error_code_out;
+ std::string error_msg_out;
+ int error_line_out;
+ int error_column_out;
+ std::unique_ptr<base::Value> value = base::JSONReader::ReadAndReturnError(
+ input, base::JSONParserOptions::JSON_PARSE_RFC, &error_code_out,
+ &error_msg_out, &error_line_out, &error_column_out);
+ if (!value)
+ return Err(Location(), "Input is not valid JSON:" + error_msg_out);
+
+ const base::DictionaryValue* dict;
+ if (!value->GetAsDictionary(&dict))
+ return Err(Location(), "Input is not a dictionary.");
+
+ Err err;
+ std::vector<std::string> strings;
+ strings = GetStringVector(*dict, "files", &err);
+ if (err.has_error())
+ return err;
+ for (auto& s : strings) {
+ if (!IsPathSourceAbsolute(s) && !IsPathAbsolute(s)) {
+ return Err(Location(),
+ "\"" + s + "\" is not a source-absolute or absolute path.");
+ }
+ inputs->source_vec.emplace_back(std::move(s));
+ }
+
+ strings = GetStringVector(*dict, "additional_compile_targets", &err);
+ if (err.has_error())
+ return err;
+
+ inputs->compile_included_all = false;
+ for (auto& s : strings) {
+ if (s == "all") {
+ inputs->compile_included_all = true;
+ } else {
+ inputs->compile_vec.push_back(
+ AbsoluteOrSourceAbsoluteStringToLabel(default_toolchain, s, &err));
+ if (err.has_error())
+ return err;
+ }
+ }
+
+ strings = GetStringVector(*dict, "test_targets", &err);
+ if (err.has_error())
+ return err;
+ for (auto& s : strings) {
+ inputs->test_vec.push_back(
+ AbsoluteOrSourceAbsoluteStringToLabel(default_toolchain, s, &err));
+ if (err.has_error())
+ return err;
+ }
+
+ for (auto& s : inputs->source_vec)
+ inputs->source_files.insert(&s);
+ for (auto& l : inputs->compile_vec)
+ inputs->compile_labels.insert(l);
+ for (auto& l : inputs->test_vec)
+ inputs->test_labels.insert(l);
+ return Err();
+}
+
+std::string OutputsToJSON(const Outputs& outputs,
+ const Label& default_toolchain,
+ Err* err) {
+ std::string output;
+ auto value = std::make_unique<base::DictionaryValue>();
+
+ if (outputs.error.size()) {
+ WriteString(*value, "error", outputs.error);
+ WriteLabels(default_toolchain, *value, "invalid_targets",
+ outputs.invalid_labels);
+ } else {
+ WriteString(*value, "status", outputs.status);
+ if (outputs.compile_includes_all) {
+ auto compile_targets = std::make_unique<base::ListValue>();
+ compile_targets->AppendString("all");
+ value->SetWithoutPathExpansion("compile_targets",
+ std::move(compile_targets));
+ } else {
+ WriteLabels(default_toolchain, *value, "compile_targets",
+ outputs.compile_labels);
+ }
+ WriteLabels(default_toolchain, *value, "test_targets", outputs.test_labels);
+ }
+
+ if (!base::JSONWriter::Write(*value.get(), &output))
+ *err = Err(Location(), "Failed to marshal JSON value for output");
+ return output;
+}
+
+} // namespace
+
+Analyzer::Analyzer(const Builder& builder,
+ const SourceFile& build_config_file,
+ const SourceFile& dot_file,
+ const SourceFileSet& build_args_dependency_files)
+ : all_items_(builder.GetAllResolvedItems()),
+ default_toolchain_(builder.loader()->GetDefaultToolchain()),
+ build_config_file_(build_config_file),
+ dot_file_(dot_file),
+ build_args_dependency_files_(build_args_dependency_files) {
+ for (const auto* item : all_items_) {
+ labels_to_items_[item->label()] = item;
+
+ // Fill dep_map_.
+ if (item->AsTarget()) {
+ for (const auto& dep_target_pair :
+ item->AsTarget()->GetDeps(Target::DEPS_ALL))
+ dep_map_.insert(std::make_pair(dep_target_pair.ptr, item));
+
+ for (const auto& dep_config_pair : item->AsTarget()->configs())
+ dep_map_.insert(std::make_pair(dep_config_pair.ptr, item));
+
+ dep_map_.insert(std::make_pair(item->AsTarget()->toolchain(), item));
+
+ if (item->AsTarget()->output_type() == Target::ACTION ||
+ item->AsTarget()->output_type() == Target::ACTION_FOREACH) {
+ const LabelPtrPair<Pool>& pool =
+ item->AsTarget()->action_values().pool();
+ if (pool.ptr)
+ dep_map_.insert(std::make_pair(pool.ptr, item));
+ }
+ } else if (item->AsConfig()) {
+ for (const auto& dep_config_pair : item->AsConfig()->configs())
+ dep_map_.insert(std::make_pair(dep_config_pair.ptr, item));
+ } else if (item->AsToolchain()) {
+ for (const auto& dep_pair : item->AsToolchain()->deps())
+ dep_map_.insert(std::make_pair(dep_pair.ptr, item));
+ } else {
+ DCHECK(item->AsPool());
+ }
+ }
+}
+
+Analyzer::~Analyzer() = default;
+
+std::string Analyzer::Analyze(const std::string& input, Err* err) const {
+ Inputs inputs;
+ Outputs outputs;
+
+ Err local_err = JSONToInputs(default_toolchain_, input, &inputs);
+ if (local_err.has_error()) {
+ outputs.error = local_err.message();
+ return OutputsToJSON(outputs, default_toolchain_, err);
+ }
+
+ std::set<Label> invalid_labels;
+ for (const auto& label : InvalidLabels(inputs.compile_labels))
+ invalid_labels.insert(label);
+ for (const auto& label : InvalidLabels(inputs.test_labels))
+ invalid_labels.insert(label);
+ if (!invalid_labels.empty()) {
+ outputs.error = "Invalid targets";
+ outputs.invalid_labels = invalid_labels;
+ return OutputsToJSON(outputs, default_toolchain_, err);
+ }
+
+ if (WereMainGNFilesModified(inputs.source_files)) {
+ outputs.status = "Found dependency (all)";
+ if (inputs.compile_included_all) {
+ outputs.compile_includes_all = true;
+ } else {
+ outputs.compile_labels.insert(inputs.compile_labels.begin(),
+ inputs.compile_labels.end());
+ outputs.compile_labels.insert(inputs.test_labels.begin(),
+ inputs.test_labels.end());
+ }
+ outputs.test_labels = inputs.test_labels;
+ return OutputsToJSON(outputs, default_toolchain_, err);
+ }
+
+ std::set<const Item*> affected_items =
+ GetAllAffectedItems(inputs.source_files);
+ std::set<const Target*> affected_targets;
+ for (const Item* affected_item : affected_items) {
+ if (affected_item->AsTarget())
+ affected_targets.insert(affected_item->AsTarget());
+ }
+
+ if (affected_targets.empty()) {
+ outputs.status = "No dependency";
+ return OutputsToJSON(outputs, default_toolchain_, err);
+ }
+
+ std::set<const Target*> root_targets;
+ for (const auto* item : all_items_) {
+ if (item->AsTarget() && dep_map_.find(item) == dep_map_.end())
+ root_targets.insert(item->AsTarget());
+ }
+
+ std::set<const Target*> compile_targets = TargetsFor(inputs.compile_labels);
+ if (inputs.compile_included_all) {
+ for (auto* root_target : root_targets)
+ compile_targets.insert(root_target);
+ }
+ std::set<const Target*> filtered_targets = Filter(compile_targets);
+ outputs.compile_labels =
+ LabelsFor(Intersect(filtered_targets, affected_targets));
+
+ // If every target is affected, simply compile All instead of listing all
+ // the targets to make the output easier to read.
+ if (inputs.compile_included_all &&
+ outputs.compile_labels.size() == filtered_targets.size())
+ outputs.compile_includes_all = true;
+
+ std::set<const Target*> test_targets = TargetsFor(inputs.test_labels);
+ outputs.test_labels = LabelsFor(Intersect(test_targets, affected_targets));
+
+ if (outputs.compile_labels.empty() && outputs.test_labels.empty())
+ outputs.status = "No dependency";
+ else
+ outputs.status = "Found dependency";
+ return OutputsToJSON(outputs, default_toolchain_, err);
+}
+
+std::set<const Item*> Analyzer::GetAllAffectedItems(
+ const std::set<const SourceFile*>& source_files) const {
+ std::set<const Item*> directly_affected_items;
+ for (auto* source_file : source_files)
+ AddItemsDirectlyReferringToFile(source_file, &directly_affected_items);
+
+ std::set<const Item*> all_affected_items;
+ for (auto* affected_item : directly_affected_items)
+ AddAllItemsReferringToItem(affected_item, &all_affected_items);
+
+ return all_affected_items;
+}
+
+std::set<Label> Analyzer::InvalidLabels(const std::set<Label>& labels) const {
+ std::set<Label> invalid_labels;
+ for (const Label& label : labels) {
+ if (labels_to_items_.find(label) == labels_to_items_.end())
+ invalid_labels.insert(label);
+ }
+ return invalid_labels;
+}
+
+std::set<const Target*> Analyzer::TargetsFor(
+ const std::set<Label>& labels) const {
+ std::set<const Target*> targets;
+ for (const auto& label : labels) {
+ auto it = labels_to_items_.find(label);
+ if (it != labels_to_items_.end()) {
+ DCHECK(it->second->AsTarget());
+ targets.insert(it->second->AsTarget());
+ }
+ }
+ return targets;
+}
+
+std::set<const Target*> Analyzer::Filter(
+ const std::set<const Target*>& targets) const {
+ std::set<const Target*> seen;
+ std::set<const Target*> filtered;
+ for (const auto* target : targets)
+ FilterTarget(target, &seen, &filtered);
+ return filtered;
+}
+
+void Analyzer::FilterTarget(const Target* target,
+ std::set<const Target*>* seen,
+ std::set<const Target*>* filtered) const {
+ if (seen->find(target) == seen->end()) {
+ seen->insert(target);
+ if (target->output_type() != Target::GROUP) {
+ filtered->insert(target);
+ } else {
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
+ FilterTarget(pair.ptr, seen, filtered);
+ }
+ }
+}
+
+bool Analyzer::ItemRefersToFile(const Item* item,
+ const SourceFile* file) const {
+ for (const auto& cur_file : item->build_dependency_files()) {
+ if (cur_file == *file)
+ return true;
+ }
+
+ if (const Config* config = item->AsConfig()) {
+ for (const auto& config_pair: config->configs()) {
+ if (ItemRefersToFile(config_pair.ptr, file))
+ return true;
+ }
+ }
+
+ if (!item->AsTarget())
+ return false;
+
+ const Target* target = item->AsTarget();
+ for (const auto& cur_file : target->sources()) {
+ if (cur_file == *file)
+ return true;
+ }
+ for (const auto& cur_file : target->public_headers()) {
+ if (cur_file == *file)
+ return true;
+ }
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ for (const auto& cur_file : iter.cur().inputs()) {
+ if (cur_file == *file)
+ return true;
+ }
+ }
+ for (const auto& cur_file : target->data()) {
+ if (cur_file == file->value())
+ return true;
+ if (cur_file.back() == '/' &&
+ base::StartsWith(file->value(), cur_file, base::CompareCase::SENSITIVE))
+ return true;
+ }
+
+ if (target->action_values().script().value() == file->value())
+ return true;
+
+ std::vector<SourceFile> outputs;
+ target->action_values().GetOutputsAsSourceFiles(target, &outputs);
+ for (const auto& cur_file : outputs) {
+ if (cur_file == *file)
+ return true;
+ }
+ return false;
+}
+
+void Analyzer::AddItemsDirectlyReferringToFile(
+ const SourceFile* file,
+ std::set<const Item*>* directly_affected_items) const {
+ for (const auto* item : all_items_) {
+ if (ItemRefersToFile(item, file))
+ directly_affected_items->insert(item);
+ }
+}
+
+void Analyzer::AddAllItemsReferringToItem(
+ const Item* item,
+ std::set<const Item*>* all_affected_items) const {
+ if (all_affected_items->find(item) != all_affected_items->end())
+ return; // Already found this item.
+
+ all_affected_items->insert(item);
+
+ auto dep_begin = dep_map_.lower_bound(item);
+ auto dep_end = dep_map_.upper_bound(item);
+ for (auto cur_dep = dep_begin; cur_dep != dep_end; ++cur_dep)
+ AddAllItemsReferringToItem(cur_dep->second, all_affected_items);
+}
+
+bool Analyzer::WereMainGNFilesModified(
+ const std::set<const SourceFile*>& modified_files) const {
+ for (const auto* file : modified_files) {
+ if (*file == dot_file_)
+ return true;
+
+ if (*file == build_config_file_)
+ return true;
+
+ for (const auto& build_args_dependency_file :
+ build_args_dependency_files_) {
+ if (*file == build_args_dependency_file)
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/gn/src/gn/analyzer.h b/gn/src/gn/analyzer.h
new file mode 100644
index 00000000000..2ffdb527038
--- /dev/null
+++ b/gn/src/gn/analyzer.h
@@ -0,0 +1,104 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_ANALYZER_H_
+#define TOOLS_GN_ANALYZER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "gn/builder.h"
+#include "gn/item.h"
+#include "gn/label.h"
+#include "gn/source_file.h"
+
+// An Analyzer can answer questions about a build graph. It is used
+// to answer queries for the `refs` and `analyze` commands, where we
+// need to look at the graph in ways that can't easily be determined
+// from just a single Target.
+class Analyzer {
+ public:
+ Analyzer(const Builder& builder,
+ const SourceFile& build_config_file,
+ const SourceFile& dot_file,
+ const SourceFileSet& build_args_dependency_files);
+ ~Analyzer();
+
+ // Figures out from a Buider and a JSON-formatted string containing lists
+ // of files and targets, which targets would be affected by modifications
+ // to the files . See the help text for the analyze command (kAnalyze_Help)
+ // for the specification of the input and output string formats and the
+ // expected behavior of the method.
+ std::string Analyze(const std::string& input, Err* err) const;
+
+ private:
+ // Returns the set of all items that might be affected, directly or
+ // indirectly, by modifications to the given source files.
+ std::set<const Item*> GetAllAffectedItems(
+ const std::set<const SourceFile*>& source_files) const;
+
+ // Returns the set of labels that do not refer to objects in the graph.
+ std::set<Label> InvalidLabels(const std::set<Label>& labels) const;
+
+ // Returns the set of all targets that have a label in the given set.
+ // Invalid (or missing) labels will be ignored.
+ std::set<const Target*> TargetsFor(const std::set<Label>& labels) const;
+
+ // Returns a filtered set of the given targets, meaning that for each of the
+ // given targets,
+ // - if the target is not a group, add it to the set
+ // - if the target is a group, recursively filter each dependency and add
+ // its filtered results to the set.
+ //
+ // For example, if we had:
+ //
+ // group("foobar") { deps = [ ":foo", ":bar" ] }
+ // group("bar") { deps = [ ":baz", ":quux" ] }
+ // executable("foo") { ... }
+ // executable("baz") { ... }
+ // executable("quux") { ... }
+ //
+ // Then the filtered version of {"foobar"} would be {":foo", ":baz",
+ // ":quux"}. This is used by the analyze command in order to only build
+ // the affected dependencies of a group (and not also build the unaffected
+ // ones).
+ //
+ // This filtering behavior is also known as "pruning" the list of targets.
+ std::set<const Target*> Filter(const std::set<const Target*>& targets) const;
+
+ // Filter an individual target and adds the results to filtered
+ // (see Filter(), above).
+ void FilterTarget(const Target*,
+ std::set<const Target*>* seen,
+ std::set<const Target*>* filtered) const;
+
+ bool ItemRefersToFile(const Item* item, const SourceFile* file) const;
+
+ void AddItemsDirectlyReferringToFile(
+ const SourceFile* file,
+ std::set<const Item*>* affected_items) const;
+
+ void AddAllItemsReferringToItem(const Item* item,
+ std::set<const Item*>* affected_items) const;
+
+ // Main GN files stand for files whose context are used globally to execute
+ // every other build files, this list includes dot file, build config file,
+ // build args files etc.
+ bool WereMainGNFilesModified(
+ const std::set<const SourceFile*>& modified_files) const;
+
+ std::vector<const Item*> all_items_;
+ std::map<Label, const Item*> labels_to_items_;
+ Label default_toolchain_;
+
+ // Maps items to the list of items that depend on them.
+ std::multimap<const Item*, const Item*> dep_map_;
+
+ const SourceFile build_config_file_;
+ const SourceFile dot_file_;
+ const SourceFileSet build_args_dependency_files_;
+};
+
+#endif // TOOLS_GN_ANALYZER_H_
diff --git a/gn/src/gn/analyzer_unittest.cc b/gn/src/gn/analyzer_unittest.cc
new file mode 100644
index 00000000000..558700e9c76
--- /dev/null
+++ b/gn/src/gn/analyzer_unittest.cc
@@ -0,0 +1,755 @@
+// Copyright 2016 The Chromium 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 "gn/analyzer.h"
+
+#include "gn/c_tool.h"
+#include "gn/build_settings.h"
+#include "gn/builder.h"
+#include "gn/config.h"
+#include "gn/general_tool.h"
+#include "gn/loader.h"
+#include "gn/pool.h"
+#include "gn/settings.h"
+#include "gn/source_file.h"
+#include "gn/substitution_list.h"
+#include "gn/target.h"
+#include "gn/tool.h"
+#include "gn/toolchain.h"
+#include "util/test/test.h"
+
+namespace gn_analyzer_unittest {
+
+class MockLoader : public Loader {
+ public:
+ MockLoader() = default;
+
+ void Load(const SourceFile& file,
+ const LocationRange& origin,
+ const Label& toolchain_name) override {}
+ void ToolchainLoaded(const Toolchain* toolchain) override {}
+ Label GetDefaultToolchain() const override {
+ return Label(SourceDir("//tc/"), "default");
+ }
+ const Settings* GetToolchainSettings(const Label& label) const override {
+ return nullptr;
+ }
+ SourceFile BuildFileForLabel(const Label& label) const override {
+ return SourceFile(label.dir().value() + "BUILD.gn");
+ }
+
+ private:
+ ~MockLoader() override = default;
+};
+
+class AnalyzerTest : public testing::Test {
+ public:
+ AnalyzerTest()
+ : loader_(new MockLoader),
+ builder_(loader_.get()),
+ settings_(&build_settings_, std::string()),
+ other_settings_(&build_settings_, std::string()) {
+ build_settings_.SetBuildDir(SourceDir("//out/"));
+
+ settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
+ settings_.set_default_toolchain_label(settings_.toolchain_label());
+ tc_dir_ = settings_.toolchain_label().dir();
+ tc_name_ = settings_.toolchain_label().name();
+
+ other_settings_.set_toolchain_label(Label(SourceDir("//other/"), "tc"));
+ other_settings_.set_default_toolchain_label(
+ other_settings_.toolchain_label());
+ tc_other_dir_ = other_settings_.toolchain_label().dir();
+ tc_other_name_ = other_settings_.toolchain_label().name();
+ }
+
+ std::unique_ptr<Target> MakeTarget(const std::string& dir,
+ const std::string& name) {
+ Label label(SourceDir(dir), name, tc_dir_, tc_name_);
+ return std::make_unique<Target>(&settings_, label);
+ }
+
+ std::unique_ptr<Target> MakeTargetOtherToolchain(const std::string& dir,
+ const std::string& name) {
+ Label label(SourceDir(dir), name, tc_other_dir_, tc_other_name_);
+ return std::make_unique<Target>(&other_settings_, label);
+ }
+
+ std::unique_ptr<Config> MakeConfig(const std::string& dir,
+ const std::string& name) {
+ Label label(SourceDir(dir), name, tc_dir_, tc_name_);
+ return std::make_unique<Config>(&settings_, label);
+ }
+
+ std::unique_ptr<Pool> MakePool(const std::string& dir,
+ const std::string& name) {
+ Label label(SourceDir(dir), name, tc_dir_, tc_name_);
+ return std::make_unique<Pool>(&settings_, label);
+ }
+
+ void RunAnalyzerTest(const std::string& input,
+ const std::string& expected_output) {
+ Analyzer analyzer(builder_, SourceFile("//build/config/BUILDCONFIG.gn"),
+ SourceFile("//.gn"),
+ {SourceFile("//out/debug/args.gn"),
+ SourceFile("//build/default_args.gn")});
+ Err err;
+ std::string actual_output = analyzer.Analyze(input, &err);
+ EXPECT_EQ(err.has_error(), false);
+ EXPECT_EQ(expected_output, actual_output);
+ }
+
+ protected:
+ scoped_refptr<MockLoader> loader_;
+ Builder builder_;
+ BuildSettings build_settings_;
+
+ Settings settings_;
+ SourceDir tc_dir_;
+ std::string tc_name_;
+
+ Settings other_settings_;
+ SourceDir tc_other_dir_;
+ std::string tc_other_name_;
+};
+
+// Tests that a target is marked as affected if its sources are modified.
+TEST_F(AnalyzerTest, TargetRefersToSources) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/file_name.cc" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->sources().push_back(SourceFile("//dir/file_name.cc"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/file_name.cc" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that a target is marked as affected if its public headers are modified.
+TEST_F(AnalyzerTest, TargetRefersToPublicHeaders) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/header_name.h" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->public_headers().push_back(SourceFile("//dir/header_name.h"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/header_name.h" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that a target is marked as affected if its inputs are modified.
+TEST_F(AnalyzerTest, TargetRefersToInputs) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/extra_input.cc" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ SourceFile extra_input(SourceFile("//dir/extra_input.cc"));
+ t_raw->config_values().inputs().push_back(extra_input);
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/extra_input.cc" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+
+ t_raw->config_values().inputs().clear();
+ std::unique_ptr<Config> c = MakeConfig("//dir", "config_name");
+ c->own_values().inputs().push_back(extra_input);
+ t_raw->configs().push_back(LabelConfigPair(c.get()));
+ builder_.ItemDefined(std::move(c));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/extra_input.cc" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that a target is marked as affected if a sub-config is modified.
+//
+// This test uses two levels of sub-configs to ensure the config hierarchy
+// is completely traversed.
+TEST_F(AnalyzerTest, SubConfigIsModified) {
+ std::unique_ptr<Config> ssc = MakeConfig("//dir3", "subsubconfig_name");
+ ssc->build_dependency_files().insert(SourceFile("//dir3/BUILD.gn"));
+
+ std::unique_ptr<Config> sc = MakeConfig("//dir2", "subconfig_name");
+ sc->configs().push_back(LabelConfigPair(ssc->label()));
+
+ std::unique_ptr<Config> c = MakeConfig("//dir", "config_name");
+ c->configs().push_back(LabelConfigPair(sc->label()));
+
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ t->configs().push_back(LabelConfigPair(c.get()));
+
+ builder_.ItemDefined(std::move(ssc));
+ builder_.ItemDefined(std::move(sc));
+ builder_.ItemDefined(std::move(c));
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir3/BUILD.gn" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that a target is marked as affected if its data are modified.
+TEST_F(AnalyzerTest, TargetRefersToData) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/data.html" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->data().push_back("//dir/data.html");
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/data.html" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that a target is marked as affected if the target is an action and its
+// action script is modified.
+TEST_F(AnalyzerTest, TargetRefersToActionScript) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ t->set_output_type(Target::ACTION);
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/script.py" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->action_values().set_script(SourceFile("//dir/script.py"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/script.py" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that a target is marked as affected if its build dependency files are
+// modified.
+TEST_F(AnalyzerTest, TargetRefersToBuildDependencyFiles) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ builder_.ItemDefined(std::move(t));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that if a target is marked as affected, then it propagates to dependent
+// test_targets.
+TEST_F(AnalyzerTest, AffectedTargetpropagatesToDependentTargets) {
+ std::unique_ptr<Target> t1 = MakeTarget("//dir", "target_name1");
+ std::unique_ptr<Target> t2 = MakeTarget("//dir", "target_name2");
+ std::unique_ptr<Target> t3 = MakeTarget("//dir", "target_name3");
+ t1->private_deps().push_back(LabelTargetPair(t2.get()));
+ t2->private_deps().push_back(LabelTargetPair(t3.get()));
+ builder_.ItemDefined(std::move(t1));
+ builder_.ItemDefined(std::move(t2));
+
+ Target* t3_raw = t3.get();
+ builder_.ItemDefined(std::move(t3));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t3_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name1","//dir:target_name2"])"
+ "}");
+}
+
+// Tests that if a config is marked as affected, then it propagates to dependent
+// test_targets.
+TEST_F(AnalyzerTest, AffectedConfigpropagatesToDependentTargets) {
+ std::unique_ptr<Config> c = MakeConfig("//dir", "config_name");
+ Config* c_raw = c.get();
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ t->configs().push_back(LabelConfigPair(c.get()));
+ builder_.ItemDefined(std::move(t));
+ builder_.ItemDefined(std::move(c));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ c_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that if toolchain is marked as affected, then it propagates to
+// dependent test_targets.
+TEST_F(AnalyzerTest, AffectedToolchainpropagatesToDependentTargets) {
+ std::unique_ptr<Target> target = MakeTarget("//dir", "target_name");
+ target->set_output_type(Target::EXECUTABLE);
+ Toolchain* toolchain = new Toolchain(&settings_, settings_.toolchain_label());
+
+ // The tool is not used anywhere, but is required to construct the dependency
+ // between a target and the toolchain.
+ std::unique_ptr<Tool> fake_tool = Tool::CreateTool(CTool::kCToolLink);
+ fake_tool->set_outputs(
+ SubstitutionList::MakeForTest("//out/debug/output.txt"));
+ toolchain->SetTool(std::move(fake_tool));
+ builder_.ItemDefined(std::move(target));
+ builder_.ItemDefined(std::unique_ptr<Item>(toolchain));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//tc/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ toolchain->build_dependency_files().insert(SourceFile("//tc/BUILD.gn"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//tc/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that if a pool is marked as affected, then it propagates to dependent
+// targets.
+TEST_F(AnalyzerTest, AffectedPoolpropagatesToDependentTargets) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ t->set_output_type(Target::ACTION);
+ std::unique_ptr<Pool> p = MakePool("//dir", "pool_name");
+ Pool* p_raw = p.get();
+ t->action_values().set_pool(LabelPtrPair<Pool>(p.get()));
+
+ builder_.ItemDefined(std::move(t));
+ builder_.ItemDefined(std::move(p));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ p_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["all"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that when dependency was found, the "compile_targets" in the output is
+// not "all".
+TEST_F(AnalyzerTest, CompileTargetsAllWasPruned) {
+ std::unique_ptr<Target> t1 = MakeTarget("//dir", "target_name1");
+ std::unique_ptr<Target> t2 = MakeTarget("//dir", "target_name2");
+ t2->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
+ builder_.ItemDefined(std::move(t1));
+ builder_.ItemDefined(std::move(t2));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": []
+ })",
+ "{"
+ R"("compile_targets":["//dir:target_name2"],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":[])"
+ "}");
+}
+
+// Tests that output is "No dependency" when no dependency is found.
+TEST_F(AnalyzerTest, NoDependency) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ builder_.ItemDefined(std::move(t));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//dir/BUILD.gn" ],
+ "additional_compile_targets": [ "all" ],
+ "test_targets": []
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+}
+
+// Tests that output is "No dependency" when no files or targets are provided.
+TEST_F(AnalyzerTest, NoFilesNoTargets) {
+ RunAnalyzerTest(
+ R"({
+ "files": [],
+ "additional_compile_targets": [],
+ "test_targets": []
+ })",
+ "{"
+ R"("compile_targets":[],)"
+ R"("status":"No dependency",)"
+ R"("test_targets":[])"
+ "}");
+}
+
+// Tests that output displays proper error message when given files aren't
+// source-absolute or absolute path.
+TEST_F(AnalyzerTest, FilesArentSourceAbsolute) {
+ RunAnalyzerTest(
+ R"({
+ "files": [ "a.cc" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("error":)"
+ R"("\"a.cc\" is not a source-absolute or absolute path.",)"
+ R"("invalid_targets":[])"
+ "}");
+}
+
+// Tests that output displays proper error message when input is ill-formed.
+TEST_F(AnalyzerTest, WrongInputFields) {
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//a.cc" ],
+ "compile_targets": [],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("error":)"
+ R"("Input does not have a key named )"
+ R"(\"additional_compile_targets\" with a list value.",)"
+ R"("invalid_targets":[])"
+ "}");
+}
+
+// Bails out early with "Found dependency (all)" if dot file is modified.
+TEST_F(AnalyzerTest, DotFileWasModified) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ builder_.ItemDefined(std::move(t));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//.gn" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["//dir:target_name"],)"
+ R"/("status":"Found dependency (all)",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Bails out early with "Found dependency (all)" if master build config file is
+// modified.
+TEST_F(AnalyzerTest, BuildConfigFileWasModified) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ builder_.ItemDefined(std::move(t));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//build/config/BUILDCONFIG.gn" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["//dir:target_name"],)"
+ R"/("status":"Found dependency (all)",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Bails out early with "Found dependency (all)" if a build args dependency file
+// is modified.
+TEST_F(AnalyzerTest, BuildArgsDependencyFileWasModified) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ builder_.ItemDefined(std::move(t));
+
+ RunAnalyzerTest(
+ R"({
+ "files": [ "//build/default_args.gn" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name" ]
+ })",
+ "{"
+ R"("compile_targets":["//dir:target_name"],)"
+ R"/("status":"Found dependency (all)",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+}
+
+// Tests that targets in explicitly labelled with the default toolchain are
+// included when their sources change.
+// change.
+TEST_F(AnalyzerTest, TargetToolchainSpecifiedRefersToSources) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ Target* t_raw = t.get();
+ builder_.ItemDefined(std::move(t));
+
+ RunAnalyzerTest(
+ R"/({
+ "files": [ "//dir/file_name.cc" ],
+ "additional_compile_targets": ["all"],
+ "test_targets": [ "//dir:target_name(//tc:default)" ]
+ })/",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->sources().push_back(SourceFile("//dir/file_name.cc"));
+
+ RunAnalyzerTest(
+ R"*({
+ "files": [ "//dir/file_name.cc" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name(//tc:default)" ]
+ })*",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"Found dependency",)/"
+ R"/("test_targets":["//dir:target_name"])/"
+ "}");
+}
+
+// Tests that targets in alternate toolchains are affected when their sources
+// change.
+TEST_F(AnalyzerTest, TargetAlternateToolchainRefersToSources) {
+ std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+ std::unique_ptr<Target> t_alt =
+ MakeTargetOtherToolchain("//dir", "target_name");
+ Target* t_raw = t.get();
+ Target* t_alt_raw = t_alt.get();
+ builder_.ItemDefined(std::move(t));
+ builder_.ItemDefined(std::move(t_alt));
+
+ RunAnalyzerTest(
+ R"/({
+ "files": [ "//dir/file_name.cc" ],
+ "additional_compile_targets": ["all"],
+ "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+ })/",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"No dependency",)/"
+ R"("test_targets":[])"
+ "}");
+
+ t_raw->sources().push_back(SourceFile("//dir/file_name.cc"));
+ t_alt_raw->sources().push_back(SourceFile("//dir/alt_file_name.cc"));
+
+ RunAnalyzerTest(
+ R"*({
+ "files": [ "//dir/file_name.cc" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+ })*",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"Found dependency",)/"
+ R"("test_targets":["//dir:target_name"])"
+ "}");
+
+ RunAnalyzerTest(
+ R"*({
+ "files": [ "//dir/alt_file_name.cc" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+ })*",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"Found dependency",)/"
+ R"/("test_targets":["//dir:target_name(//other:tc)"])/"
+ "}");
+
+ RunAnalyzerTest(
+ R"*({
+ "files": [ "//dir/file_name.cc", "//dir/alt_file_name.cc" ],
+ "additional_compile_targets": [],
+ "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+ })*",
+ "{"
+ R"("compile_targets":[],)"
+ R"/("status":"Found dependency",)/"
+ R"/("test_targets":["//dir:target_name","//dir:target_name(//other:tc)"])/"
+ "}");
+}
+
+} // namespace gn_analyzer_unittest
diff --git a/gn/src/gn/args.cc b/gn/src/gn/args.cc
new file mode 100644
index 00000000000..d7f8b660608
--- /dev/null
+++ b/gn/src/gn/args.cc
@@ -0,0 +1,437 @@
+// 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 "gn/args.h"
+
+#include "gn/settings.h"
+#include "gn/source_file.h"
+#include "gn/string_utils.h"
+#include "gn/variables.h"
+#include "util/build_config.h"
+#include "util/sys_info.h"
+
+const char kBuildArgs_Help[] =
+ R"(Build Arguments Overview
+
+ Build arguments are variables passed in from outside of the build that build
+ files can query to determine how the build works.
+
+How build arguments are set
+
+ First, system default arguments are set based on the current system. The
+ built-in arguments are:
+ - host_cpu
+ - host_os
+ - current_cpu
+ - current_os
+ - target_cpu
+ - target_os
+
+ Next, project-specific overrides are applied. These are specified inside
+ the default_args variable of //.gn. See "gn help dotfile" for more.
+
+ If specified, arguments from the --args command line flag are used. If that
+ flag is not specified, args from previous builds in the build directory will
+ be used (this is in the file args.gn in the build directory).
+
+ Last, for targets being compiled with a non-default toolchain, the toolchain
+ overrides are applied. These are specified in the toolchain_args section of a
+ toolchain definition. The use-case for this is that a toolchain may be
+ building code for a different platform, and that it may want to always
+ specify Posix, for example. See "gn help toolchain" for more.
+
+ If you specify an override for a build argument that never appears in a
+ "declare_args" call, a nonfatal error will be displayed.
+
+Examples
+
+ gn args out/FooBar
+ Create the directory out/FooBar and open an editor. You would type
+ something like this into that file:
+ enable_doom_melon=false
+ os="android"
+
+ gn gen out/FooBar --args="enable_doom_melon=true os=\"android\""
+ This will overwrite the build directory with the given arguments. (Note
+ that the quotes inside the args command will usually need to be escaped
+ for your shell to pass through strings values.)
+
+How build arguments are used
+
+ If you want to use an argument, you use declare_args() and specify default
+ values. These default values will apply if none of the steps listed in the
+ "How build arguments are set" section above apply to the given argument, but
+ the defaults will not override any of these.
+
+ Often, the root build config file will declare global arguments that will be
+ passed to all buildfiles. Individual build files can also specify arguments
+ that apply only to those files. It is also useful to specify build args in an
+ "import"-ed file if you want such arguments to apply to multiple buildfiles.
+)";
+
+namespace {
+
+// Removes all entries in |overrides| that are in |declared_overrides|.
+void RemoveDeclaredOverrides(const Scope::KeyValueMap& declared_arguments,
+ Scope::KeyValueMap* overrides) {
+ for (Scope::KeyValueMap::iterator override = overrides->begin();
+ override != overrides->end();) {
+ if (declared_arguments.find(override->first) == declared_arguments.end())
+ ++override;
+ else
+ overrides->erase(override++);
+ }
+}
+
+} // namespace
+
+Args::ValueWithOverride::ValueWithOverride()
+ : default_value(), has_override(false), override_value() {}
+
+Args::ValueWithOverride::ValueWithOverride(const Value& def_val)
+ : default_value(def_val), has_override(false), override_value() {}
+
+Args::ValueWithOverride::~ValueWithOverride() = default;
+
+Args::Args() = default;
+
+Args::Args(const Args& other)
+ : overrides_(other.overrides_),
+ all_overrides_(other.all_overrides_),
+ declared_arguments_per_toolchain_(
+ other.declared_arguments_per_toolchain_),
+ toolchain_overrides_(other.toolchain_overrides_) {}
+
+Args::~Args() = default;
+
+void Args::AddArgOverride(const char* name, const Value& value) {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ overrides_[std::string_view(name)] = value;
+ all_overrides_[std::string_view(name)] = value;
+}
+
+void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ for (const auto& cur_override : overrides) {
+ overrides_[cur_override.first] = cur_override.second;
+ all_overrides_[cur_override.first] = cur_override.second;
+ }
+}
+
+void Args::AddDefaultArgOverrides(const Scope::KeyValueMap& overrides) {
+ std::lock_guard<std::mutex> lock(lock_);
+ for (const auto& cur_override : overrides)
+ overrides_[cur_override.first] = cur_override.second;
+}
+
+const Value* Args::GetArgOverride(const char* name) const {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ Scope::KeyValueMap::const_iterator found =
+ all_overrides_.find(std::string_view(name));
+ if (found == all_overrides_.end())
+ return nullptr;
+ return &found->second;
+}
+
+void Args::SetupRootScope(Scope* dest,
+ const Scope::KeyValueMap& toolchain_overrides) const {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ SetSystemVarsLocked(dest);
+
+ // Apply overrides for already declared args.
+ // (i.e. the system vars we set above)
+ ApplyOverridesLocked(overrides_, dest);
+ ApplyOverridesLocked(toolchain_overrides, dest);
+
+ OverridesForToolchainLocked(dest) = toolchain_overrides;
+
+ SaveOverrideRecordLocked(toolchain_overrides);
+}
+
+bool Args::DeclareArgs(const Scope::KeyValueMap& args,
+ Scope* scope_to_set,
+ Err* err) const {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ Scope::KeyValueMap& declared_arguments(
+ DeclaredArgumentsForToolchainLocked(scope_to_set));
+
+ const Scope::KeyValueMap& toolchain_overrides(
+ OverridesForToolchainLocked(scope_to_set));
+
+ for (const auto& arg : args) {
+ // Verify that the value hasn't already been declared. We want each value
+ // to be declared only once.
+ //
+ // The tricky part is that a buildfile can be interpreted multiple times
+ // when used from different toolchains, so we can't just check that we've
+ // seen it before. Instead, we check that the location matches.
+ Scope::KeyValueMap::iterator previously_declared =
+ declared_arguments.find(arg.first);
+ if (previously_declared != declared_arguments.end()) {
+ if (previously_declared->second.origin() != arg.second.origin()) {
+ // Declaration location mismatch.
+ *err = Err(
+ arg.second.origin(), "Duplicate build argument declaration.",
+ "Here you're declaring an argument that was already declared "
+ "elsewhere.\nYou can only declare each argument once in the entire "
+ "build so there is one\ncanonical place for documentation and the "
+ "default value. Either move this\nargument to the build config "
+ "file (for visibility everywhere) or to a .gni file\nthat you "
+ "\"import\" from the files where you need it (preferred).");
+ err->AppendSubErr(Err(previously_declared->second.origin(),
+ "Previous declaration.",
+ "See also \"gn help buildargs\" for more on how "
+ "build arguments work."));
+ return false;
+ }
+ } else {
+ declared_arguments.insert(arg);
+ }
+
+ // In all the cases below, mark the variable used. If a variable is set
+ // that's only used in one toolchain, we don't want to report unused
+ // variable errors in other toolchains. Also, in some cases it's reasonable
+ // for the build file to overwrite the value with a different value based
+ // on some other condition without dereferencing the value first.
+
+ // Check whether this argument has been overridden on the toolchain level
+ // and use the override instead.
+ Scope::KeyValueMap::const_iterator toolchain_override =
+ toolchain_overrides.find(arg.first);
+ if (toolchain_override != toolchain_overrides.end()) {
+ scope_to_set->SetValue(toolchain_override->first,
+ toolchain_override->second,
+ toolchain_override->second.origin());
+ scope_to_set->MarkUsed(arg.first);
+ continue;
+ }
+
+ // Check whether this argument has been overridden and use the override
+ // instead.
+ Scope::KeyValueMap::const_iterator override = overrides_.find(arg.first);
+ if (override != overrides_.end()) {
+ scope_to_set->SetValue(override->first, override->second,
+ override->second.origin());
+ scope_to_set->MarkUsed(override->first);
+ continue;
+ }
+
+ scope_to_set->SetValue(arg.first, arg.second, arg.second.origin());
+ scope_to_set->MarkUsed(arg.first);
+ }
+
+ return true;
+}
+
+bool Args::VerifyAllOverridesUsed(Err* err) const {
+ std::lock_guard<std::mutex> lock(lock_);
+ Scope::KeyValueMap unused_overrides(all_overrides_);
+ for (const auto& map_pair : declared_arguments_per_toolchain_)
+ RemoveDeclaredOverrides(map_pair.second, &unused_overrides);
+
+ if (unused_overrides.empty())
+ return true;
+
+ // Some assignments in args.gn had no effect. Show an error for the first
+ // unused assignment.
+ std::string_view name = unused_overrides.begin()->first;
+ const Value& value = unused_overrides.begin()->second;
+
+ std::string err_help(
+ "The variable \"" + name +
+ "\" was set as a build argument\n"
+ "but never appeared in a declare_args() block in any buildfile.\n\n"
+ "To view all possible args, run \"gn args --list <out_dir>\"");
+
+ // Use all declare_args for a spelling suggestion.
+ std::vector<std::string_view> candidates;
+ for (const auto& map_pair : declared_arguments_per_toolchain_) {
+ for (const auto& declared_arg : map_pair.second)
+ candidates.push_back(declared_arg.first);
+ }
+ std::string_view suggestion = SpellcheckString(name, candidates);
+ if (!suggestion.empty())
+ err_help = "Did you mean \"" + suggestion + "\"?\n\n" + err_help;
+
+ *err = Err(value.origin(), "Build argument has no effect.", err_help);
+ return false;
+}
+
+Args::ValueWithOverrideMap Args::GetAllArguments() const {
+ ValueWithOverrideMap result;
+
+ std::lock_guard<std::mutex> lock(lock_);
+
+ // Sort the keys from declared_arguments_per_toolchain_ so
+ // the return value will be deterministic.
+ std::vector<const Settings*> keys;
+ keys.reserve(declared_arguments_per_toolchain_.size());
+ for (const auto& map_pair : declared_arguments_per_toolchain_) {
+ keys.push_back(map_pair.first);
+ }
+ std::sort(keys.begin(), keys.end(),
+ [](const Settings* a, const Settings* b) -> bool {
+ return a->toolchain_label() < b->toolchain_label();
+ });
+
+ // Default values.
+ for (const auto& key : keys) {
+ const auto& value = declared_arguments_per_toolchain_[key];
+ for (const auto& arg : value)
+ result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second)));
+ }
+
+ // Merge in overrides.
+ for (const auto& over : overrides_) {
+ auto found = result.find(over.first);
+ if (found != result.end()) {
+ found->second.has_override = true;
+ found->second.override_value = over.second;
+ }
+ }
+
+ return result;
+}
+
+void Args::SetSystemVarsLocked(Scope* dest) const {
+ // Host OS.
+ const char* os = nullptr;
+#if defined(OS_WIN) || defined(OS_MSYS)
+ os = "win";
+#elif defined(OS_MACOSX)
+ os = "mac";
+#elif defined(OS_LINUX)
+ os = "linux";
+#elif defined(OS_FREEBSD)
+ os = "freebsd";
+#elif defined(OS_AIX)
+ os = "aix";
+#elif defined(OS_OPENBSD)
+ os = "openbsd";
+#elif defined(OS_HAIKU)
+ os = "haiku";
+#elif defined(OS_SOLARIS)
+ os = "solaris";
+#elif defined(OS_NETBSD)
+ os = "netbsd";
+#else
+#error Unknown OS type.
+#endif
+ // NOTE: Adding a new port? Please follow
+ // https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
+
+ // Host architecture.
+ static const char kX86[] = "x86";
+ static const char kX64[] = "x64";
+ static const char kArm[] = "arm";
+ static const char kArm64[] = "arm64";
+ static const char kMips[] = "mipsel";
+ static const char kMips64[] = "mips64el";
+ static const char kS390X[] = "s390x";
+ static const char kPPC64[] = "ppc64";
+ static const char kRISCV32[] = "riscv32";
+ static const char kRISCV64[] = "riscv64";
+ static const char kE2K[] = "e2k";
+ const char* arch = nullptr;
+
+ // Set the host CPU architecture based on the underlying OS, not
+ // whatever the current bit-tedness of the GN binary is.
+ std::string os_arch = OperatingSystemArchitecture();
+ if (os_arch == "x86" || os_arch == "BePC")
+ arch = kX86;
+ else if (os_arch == "x86_64")
+ arch = kX64;
+ else if (os_arch == "aarch64" || os_arch == "arm64")
+ arch = kArm64;
+ else if (os_arch.substr(0, 3) == "arm")
+ arch = kArm;
+ else if (os_arch == "mips")
+ arch = kMips;
+ else if (os_arch == "mips64")
+ arch = kMips64;
+ else if (os_arch == "s390x")
+ arch = kS390X;
+ else if (os_arch == "ppc64" || os_arch == "ppc64le")
+ // We handle the endianness inside //build/config/host_byteorder.gni.
+ // This allows us to use the same toolchain as ppc64 BE
+ // and specific flags are included using the host_byteorder logic.
+ arch = kPPC64;
+ else if (os_arch == "riscv32")
+ arch = kRISCV32;
+ else if (os_arch == "riscv64")
+ arch = kRISCV64;
+ else if (os_arch == "e2k")
+ arch = kE2K;
+ else
+ CHECK(false) << "OS architecture not handled. (" << os_arch << ")";
+
+ // Save the OS and architecture as build arguments that are implicitly
+ // declared. This is so they can be overridden in a toolchain build args
+ // override, and so that they will appear in the "gn args" output.
+ Value empty_string(nullptr, std::string());
+
+ Value os_val(nullptr, std::string(os));
+ dest->SetValue(variables::kHostOs, os_val, nullptr);
+ dest->SetValue(variables::kTargetOs, empty_string, nullptr);
+ dest->SetValue(variables::kCurrentOs, empty_string, nullptr);
+
+ Value arch_val(nullptr, std::string(arch));
+ dest->SetValue(variables::kHostCpu, arch_val, nullptr);
+ dest->SetValue(variables::kTargetCpu, empty_string, nullptr);
+ dest->SetValue(variables::kCurrentCpu, empty_string, nullptr);
+
+ Scope::KeyValueMap& declared_arguments(
+ DeclaredArgumentsForToolchainLocked(dest));
+ declared_arguments[variables::kHostOs] = os_val;
+ declared_arguments[variables::kCurrentOs] = empty_string;
+ declared_arguments[variables::kTargetOs] = empty_string;
+ declared_arguments[variables::kHostCpu] = arch_val;
+ declared_arguments[variables::kCurrentCpu] = empty_string;
+ declared_arguments[variables::kTargetCpu] = empty_string;
+
+ // Mark these variables used so the build config file can override them
+ // without geting a warning about overwriting an unused variable.
+ dest->MarkUsed(variables::kHostCpu);
+ dest->MarkUsed(variables::kCurrentCpu);
+ dest->MarkUsed(variables::kTargetCpu);
+ dest->MarkUsed(variables::kHostOs);
+ dest->MarkUsed(variables::kCurrentOs);
+ dest->MarkUsed(variables::kTargetOs);
+}
+
+void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
+ Scope* scope) const {
+ const Scope::KeyValueMap& declared_arguments(
+ DeclaredArgumentsForToolchainLocked(scope));
+
+ // Only set a value if it has been declared.
+ for (const auto& val : values) {
+ Scope::KeyValueMap::const_iterator declared =
+ declared_arguments.find(val.first);
+
+ if (declared == declared_arguments.end())
+ continue;
+
+ scope->SetValue(val.first, val.second, val.second.origin());
+ }
+}
+
+void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const {
+ for (const auto& val : values)
+ all_overrides_[val.first] = val.second;
+}
+
+Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked(
+ Scope* scope) const {
+ return declared_arguments_per_toolchain_[scope->settings()];
+}
+
+Scope::KeyValueMap& Args::OverridesForToolchainLocked(Scope* scope) const {
+ return toolchain_overrides_[scope->settings()];
+}
diff --git a/gn/src/gn/args.h b/gn/src/gn/args.h
new file mode 100644
index 00000000000..992d48f18dd
--- /dev/null
+++ b/gn/src/gn/args.h
@@ -0,0 +1,147 @@
+// 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 TOOLS_GN_ARGS_H_
+#define TOOLS_GN_ARGS_H_
+
+#include <map>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "gn/scope.h"
+
+class Err;
+class SourceFile;
+
+extern const char kBuildArgs_Help[];
+
+// Manages build arguments. It stores the global arguments specified on the
+// command line, and sets up the root scope with the proper values.
+//
+// This class tracks accesses so we can report errors about unused variables.
+// The use case is if the user specifies an override on the command line, but
+// no buildfile actually uses that variable. We want to be able to report that
+// the argument was unused.
+class Args {
+ public:
+ struct ValueWithOverride {
+ ValueWithOverride();
+ ValueWithOverride(const Value& def_val);
+ ~ValueWithOverride();
+
+ Value default_value; // Default value given in declare_args.
+
+ bool has_override; // True indicates override_value is valid.
+ Value override_value; // From .gn or the current build's "gn args".
+ };
+ using ValueWithOverrideMap = std::map<std::string_view, ValueWithOverride>;
+
+ Args();
+ Args(const Args& other);
+ ~Args();
+
+ // Specifies overrides of the build arguments. These are normally specified
+ // on the command line.
+ void AddArgOverride(const char* name, const Value& value);
+ void AddArgOverrides(const Scope::KeyValueMap& overrides);
+
+ // Specifies default overrides of the build arguments. These are normally
+ // specified in the .gn file.
+ void AddDefaultArgOverrides(const Scope::KeyValueMap& overrides);
+
+ // Returns the value corresponding to the given argument name, or NULL if no
+ // argument is set.
+ const Value* GetArgOverride(const char* name) const;
+
+ // Sets up the root scope for a toolchain. This applies the default system
+ // flags and saves the toolchain overrides so they can be applied to
+ // declare_args blocks that appear when loading files in that toolchain.
+ void SetupRootScope(Scope* dest,
+ const Scope::KeyValueMap& toolchain_overrides) const;
+
+ // Sets up the given scope with arguments passed in.
+ //
+ // If the values specified in the args are not already set, the values in
+ // the args list will be used (which are assumed to be the defaults), but
+ // they will not override the system defaults or the current overrides.
+ //
+ // All args specified in the input will be marked as "used".
+ //
+ // On failure, the err will be set and it will return false.
+ bool DeclareArgs(const Scope::KeyValueMap& args,
+ Scope* scope_to_set,
+ Err* err) const;
+
+ // Checks to see if any of the overrides ever used were never declared as
+ // arguments. If there are, this returns false and sets the error.
+ bool VerifyAllOverridesUsed(Err* err) const;
+
+ // Returns information about all arguments, both defaults and overrides.
+ // This is used for the help system which is not performance critical. Use a
+ // map instead of a hash map so the arguments are sorted alphabetically.
+ ValueWithOverrideMap GetAllArguments() const;
+
+ // Returns the set of build files that may affect the build arguments, please
+ // refer to Scope for how this is determined.
+ const SourceFileSet& build_args_dependency_files() const {
+ return build_args_dependency_files_;
+ }
+
+ void set_build_args_dependency_files(
+ const SourceFileSet& build_args_dependency_files) {
+ build_args_dependency_files_ = build_args_dependency_files;
+ }
+
+ private:
+ using ArgumentsPerToolchain =
+ std::unordered_map<const Settings*, Scope::KeyValueMap>;
+
+ // Sets the default config based on the current system.
+ void SetSystemVarsLocked(Scope* scope) const;
+
+ // Sets the given already declared vars on the given scope.
+ void ApplyOverridesLocked(const Scope::KeyValueMap& values,
+ Scope* scope) const;
+
+ void SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const;
+
+ // Returns the KeyValueMap used for arguments declared for the specified
+ // toolchain.
+ Scope::KeyValueMap& DeclaredArgumentsForToolchainLocked(Scope* scope) const;
+
+ // Returns the KeyValueMap used for overrides for the specified
+ // toolchain.
+ Scope::KeyValueMap& OverridesForToolchainLocked(Scope* scope) const;
+
+ // Since this is called during setup which we assume is single-threaded,
+ // this is not protected by the lock. It should be set only during init.
+ Scope::KeyValueMap overrides_;
+
+ mutable std::mutex lock_;
+
+ // Maintains a list of all overrides we've ever seen. This is the main
+ // |overrides_| as well as toolchain overrides. Tracking this allows us to
+ // check for overrides that were specified but never used.
+ mutable Scope::KeyValueMap all_overrides_;
+
+ // Maps from Settings (which corresponds to a toolchain) to the map of
+ // declared variables. This is used to tracks all variables declared in any
+ // buildfile. This is so we can see if the user set variables on the command
+ // line that are not used anywhere. Each map is toolchain specific as each
+ // toolchain may define variables in different locations.
+ mutable ArgumentsPerToolchain declared_arguments_per_toolchain_;
+
+ // Overrides for individual toolchains. This is necessary so we
+ // can apply the correct override for the current toolchain, once
+ // we see an argument declaration.
+ mutable ArgumentsPerToolchain toolchain_overrides_;
+
+ SourceFileSet build_args_dependency_files_;
+
+ DISALLOW_ASSIGN(Args);
+};
+
+#endif // TOOLS_GN_ARGS_H_
diff --git a/gn/src/gn/args_unittest.cc b/gn/src/gn/args_unittest.cc
new file mode 100644
index 00000000000..650db6daa27
--- /dev/null
+++ b/gn/src/gn/args_unittest.cc
@@ -0,0 +1,81 @@
+// 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 "gn/args.h"
+
+#include "gn/scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+// Assertions for VerifyAllOverridesUsed() and DeclareArgs() with multiple
+// toolchains.
+TEST(ArgsTest, VerifyAllOverridesUsed) {
+ TestWithScope setup1, setup2;
+ Args args;
+ Scope::KeyValueMap key_value_map1;
+ Err err;
+ LiteralNode assignment1;
+
+ setup1.scope()->SetValue("a", Value(nullptr, true), &assignment1);
+ setup1.scope()->GetCurrentScopeValues(&key_value_map1);
+ EXPECT_TRUE(args.DeclareArgs(key_value_map1, setup1.scope(), &err));
+
+ LiteralNode assignment2;
+ setup2.scope()->SetValue("b", Value(nullptr, true), &assignment2);
+ Scope::KeyValueMap key_value_map2;
+ setup2.scope()->GetCurrentScopeValues(&key_value_map2);
+ EXPECT_TRUE(args.DeclareArgs(key_value_map2, setup2.scope(), &err));
+
+ // Override "a", shouldn't see any errors as "a" was defined.
+ args.AddArgOverride("a", Value(nullptr, true));
+ EXPECT_TRUE(args.VerifyAllOverridesUsed(&err));
+
+ // Override "a", & "b", shouldn't see any errors as both were defined.
+ args.AddArgOverride("b", Value(nullptr, true));
+ EXPECT_TRUE(args.VerifyAllOverridesUsed(&err));
+
+ // Override "a", "b" and "c", should fail as "c" was not defined.
+ args.AddArgOverride("c", Value(nullptr, true));
+ EXPECT_FALSE(args.VerifyAllOverridesUsed(&err));
+}
+
+// Ensure that arg overrides get only set after the they were declared.
+TEST(ArgsTest, VerifyOverrideScope) {
+ TestWithScope setup;
+ Args args;
+ Err err;
+
+ args.AddArgOverride("a", Value(nullptr, "avalue"));
+ args.AddArgOverride("current_os", Value(nullptr, "theiros"));
+
+ Scope::KeyValueMap toolchain_overrides;
+ toolchain_overrides["b"] = Value(nullptr, "bvalue");
+ toolchain_overrides["current_os"] = Value(nullptr, "myos");
+ args.SetupRootScope(setup.scope(), toolchain_overrides);
+
+ // Overrides of arguments not yet declared aren't applied yet.
+ EXPECT_EQ(nullptr, setup.scope()->GetValue("a"));
+ EXPECT_EQ(nullptr, setup.scope()->GetValue("b"));
+
+ // |current_os| is a system var. and already declared.
+ // Thus it should have our override value.
+ ASSERT_NE(nullptr, setup.scope()->GetValue("current_os"));
+ EXPECT_EQ(Value(nullptr, "myos"), *setup.scope()->GetValue("current_os"));
+
+ Scope::KeyValueMap key_value_map1;
+ key_value_map1["a"] = Value(nullptr, "avalue2");
+ key_value_map1["b"] = Value(nullptr, "bvalue2");
+ key_value_map1["c"] = Value(nullptr, "cvalue2");
+ EXPECT_TRUE(args.DeclareArgs(key_value_map1, setup.scope(), &err));
+
+ ASSERT_NE(nullptr, setup.scope()->GetValue("a"));
+ EXPECT_EQ(Value(nullptr, "avalue"), *setup.scope()->GetValue("a"));
+
+ ASSERT_NE(nullptr, setup.scope()->GetValue("b"));
+ EXPECT_EQ(Value(nullptr, "bvalue"), *setup.scope()->GetValue("b"));
+
+ // This wasn't overwritten, so it should have the default value.
+ ASSERT_NE(nullptr, setup.scope()->GetValue("c"));
+ EXPECT_EQ(Value(nullptr, "cvalue2"), *setup.scope()->GetValue("c"));
+}
diff --git a/gn/src/gn/binary_target_generator.cc b/gn/src/gn/binary_target_generator.cc
new file mode 100644
index 00000000000..485884e81bf
--- /dev/null
+++ b/gn/src/gn/binary_target_generator.cc
@@ -0,0 +1,248 @@
+// 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 "gn/binary_target_generator.h"
+
+#include "gn/config_values_generator.h"
+#include "gn/deps_iterator.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/rust_values_generator.h"
+#include "gn/rust_variables.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/swift_values_generator.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+BinaryTargetGenerator::BinaryTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
+
+BinaryTargetGenerator::~BinaryTargetGenerator() = default;
+
+void BinaryTargetGenerator::DoRun() {
+ target_->set_output_type(output_type_);
+
+ if (!FillOutputName())
+ return;
+
+ if (!FillOutputPrefixOverride())
+ return;
+
+ if (!FillOutputDir())
+ return;
+
+ if (!FillOutputExtension())
+ return;
+
+ if (!FillSources())
+ return;
+
+ if (!FillPublic())
+ return;
+
+ if (!FillFriends())
+ return;
+
+ if (!FillCheckIncludes())
+ return;
+
+ if (!FillConfigs())
+ return;
+
+ if (!FillAllowCircularIncludesFrom())
+ return;
+
+ if (!FillCompleteStaticLib())
+ return;
+
+ if (!ValidateSources())
+ return;
+
+ if (target_->source_types_used().RustSourceUsed()) {
+ RustValuesGenerator rustgen(target_, scope_, function_call_, err_);
+ rustgen.Run();
+ if (err_->has_error())
+ return;
+ }
+
+ if (target_->source_types_used().SwiftSourceUsed()) {
+ SwiftValuesGenerator swiftgen(target_, scope_, err_);
+ swiftgen.Run();
+ if (err_->has_error())
+ return;
+ }
+
+ // Config values (compiler flags, etc.) set directly on this target.
+ ConfigValuesGenerator gen(&target_->config_values(), scope_,
+ scope_->GetSourceDir(), err_);
+ gen.Run();
+ if (err_->has_error())
+ return;
+}
+
+bool BinaryTargetGenerator::FillSources() {
+ bool ret = TargetGenerator::FillSources();
+ for (std::size_t i = 0; i < target_->sources().size(); ++i) {
+ const auto& source = target_->sources()[i];
+ switch (source.type()) {
+ case SourceFile::SOURCE_CPP:
+ case SourceFile::SOURCE_MODULEMAP:
+ case SourceFile::SOURCE_H:
+ case SourceFile::SOURCE_C:
+ case SourceFile::SOURCE_M:
+ case SourceFile::SOURCE_MM:
+ case SourceFile::SOURCE_S:
+ case SourceFile::SOURCE_ASM:
+ case SourceFile::SOURCE_O:
+ case SourceFile::SOURCE_DEF:
+ case SourceFile::SOURCE_GO:
+ case SourceFile::SOURCE_RS:
+ case SourceFile::SOURCE_RC:
+ case SourceFile::SOURCE_SWIFT:
+ // These are allowed.
+ break;
+ case SourceFile::SOURCE_UNKNOWN:
+ case SourceFile::SOURCE_SWIFTMODULE:
+ case SourceFile::SOURCE_NUMTYPES:
+ *err_ =
+ Err(scope_->GetValue(variables::kSources, true)->list_value()[i],
+ std::string("Only source, header, and object files belong in "
+ "the sources of a ") +
+ Target::GetStringForOutputType(target_->output_type()) +
+ ". " + source.value() + " is not one of the valid types.");
+ }
+
+ target_->source_types_used().Set(source.type());
+ }
+ return ret;
+}
+
+bool BinaryTargetGenerator::FillCompleteStaticLib() {
+ if (target_->output_type() == Target::STATIC_LIBRARY) {
+ const Value* value = scope_->GetValue(variables::kCompleteStaticLib, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
+ return false;
+ target_->set_complete_static_lib(value->boolean_value());
+ }
+ return true;
+}
+
+bool BinaryTargetGenerator::FillFriends() {
+ const Value* value = scope_->GetValue(variables::kFriend, true);
+ if (value) {
+ return ExtractListOfLabelPatterns(scope_->settings()->build_settings(),
+ *value, scope_->GetSourceDir(),
+ &target_->friends(), err_);
+ }
+ return true;
+}
+
+bool BinaryTargetGenerator::FillOutputName() {
+ const Value* value = scope_->GetValue(variables::kOutputName, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->set_output_name(value->string_value());
+ return true;
+}
+
+bool BinaryTargetGenerator::FillOutputPrefixOverride() {
+ const Value* value = scope_->GetValue(variables::kOutputPrefixOverride, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
+ return false;
+ target_->set_output_prefix_override(value->boolean_value());
+ return true;
+}
+
+bool BinaryTargetGenerator::FillOutputDir() {
+ const Value* value = scope_->GetValue(variables::kOutputDir, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Treat empty string as the default and do nothing.
+
+ const BuildSettings* build_settings = scope_->settings()->build_settings();
+ SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
+ *value, err_, build_settings->root_path_utf8());
+ if (err_->has_error())
+ return false;
+
+ if (!EnsureStringIsInOutputDir(build_settings->build_dir(), dir.value(),
+ value->origin(), err_))
+ return false;
+ target_->set_output_dir(dir);
+ return true;
+}
+
+bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
+ const Value* value =
+ scope_->GetValue(variables::kAllowCircularIncludesFrom, true);
+ if (!value)
+ return true;
+
+ UniqueVector<Label> circular;
+ ExtractListOfUniqueLabels(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(),
+ ToolchainLabelForScope(scope_), &circular, err_);
+ if (err_->has_error())
+ return false;
+
+ // Validate that all circular includes entries are in the deps.
+ for (const auto& cur : circular) {
+ bool found_dep = false;
+ for (const auto& dep_pair : target_->GetDeps(Target::DEPS_LINKED)) {
+ if (dep_pair.label == cur) {
+ found_dep = true;
+ break;
+ }
+ }
+ if (!found_dep) {
+ *err_ = Err(*value, "Label not in deps.",
+ "The label \"" + cur.GetUserVisibleName(false) +
+ "\"\nwas not in the deps of this target. "
+ "allow_circular_includes_from only allows\ntargets "
+ "present in the "
+ "deps.");
+ return false;
+ }
+ }
+
+ // Add to the set.
+ for (const auto& cur : circular)
+ target_->allow_circular_includes_from().insert(cur);
+ return true;
+}
+
+bool BinaryTargetGenerator::ValidateSources() {
+ // For Rust targets, if the only source file is the root `sources` can be
+ // omitted/empty.
+ if (scope_->GetValue(variables::kRustCrateRoot, false)) {
+ target_->source_types_used().Set(SourceFile::SOURCE_RS);
+ }
+
+ if (target_->source_types_used().MixedSourceUsed()) {
+ *err_ =
+ Err(function_call_, "More than one language used in target sources.",
+ "Mixed sources are not allowed, unless they are "
+ "compilation-compatible (e.g. Objective C and C++).");
+ return false;
+ }
+ return true;
+}
diff --git a/gn/src/gn/binary_target_generator.h b/gn/src/gn/binary_target_generator.h
new file mode 100644
index 00000000000..27dcb7a738a
--- /dev/null
+++ b/gn/src/gn/binary_target_generator.h
@@ -0,0 +1,41 @@
+// 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 TOOLS_GN_BINARY_TARGET_GENERATOR_H_
+#define TOOLS_GN_BINARY_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target.h"
+#include "gn/target_generator.h"
+
+// Populates a Target with the values from a binary rule (executable, shared
+// library, or static library).
+class BinaryTargetGenerator : public TargetGenerator {
+ public:
+ BinaryTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err);
+ ~BinaryTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+ bool FillSources() override;
+
+ private:
+ bool FillCompleteStaticLib();
+ bool FillFriends();
+ bool FillOutputName();
+ bool FillOutputPrefixOverride();
+ bool FillOutputDir();
+ bool FillAllowCircularIncludesFrom();
+ bool ValidateSources();
+
+ Target::OutputType output_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryTargetGenerator);
+};
+
+#endif // TOOLS_GN_BINARY_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/build_settings.cc b/gn/src/gn/build_settings.cc
new file mode 100644
index 00000000000..22c11451f5a
--- /dev/null
+++ b/gn/src/gn/build_settings.cc
@@ -0,0 +1,76 @@
+// 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 "gn/build_settings.h"
+
+#include <utility>
+
+#include "base/files/file_util.h"
+#include "gn/filesystem_utils.h"
+
+BuildSettings::BuildSettings() = default;
+
+BuildSettings::BuildSettings(const BuildSettings& other)
+ : dotfile_name_(other.dotfile_name_),
+ root_path_(other.root_path_),
+ root_path_utf8_(other.root_path_utf8_),
+ secondary_source_path_(other.secondary_source_path_),
+ python_path_(other.python_path_),
+ ninja_required_version_(other.ninja_required_version_),
+ build_config_file_(other.build_config_file_),
+ arg_file_template_path_(other.arg_file_template_path_),
+ build_dir_(other.build_dir_),
+ build_args_(other.build_args_) {}
+
+void BuildSettings::SetRootTargetLabel(const Label& r) {
+ root_target_label_ = r;
+}
+
+void BuildSettings::SetRootPath(const base::FilePath& r) {
+ DCHECK(r.value()[r.value().size() - 1] != base::FilePath::kSeparators[0]);
+ root_path_ = r.NormalizePathSeparatorsTo('/');
+ root_path_utf8_ = FilePathToUTF8(root_path_);
+}
+
+void BuildSettings::SetSecondarySourcePath(const SourceDir& d) {
+ secondary_source_path_ = GetFullPath(d).NormalizePathSeparatorsTo('/');
+}
+
+void BuildSettings::SetBuildDir(const SourceDir& d) {
+ build_dir_ = d;
+}
+
+base::FilePath BuildSettings::GetFullPath(const SourceFile& file) const {
+ return file.Resolve(root_path_).NormalizePathSeparatorsTo('/');
+}
+
+base::FilePath BuildSettings::GetFullPath(const SourceDir& dir) const {
+ return dir.Resolve(root_path_).NormalizePathSeparatorsTo('/');
+}
+
+base::FilePath BuildSettings::GetFullPath(const std::string& path,
+ bool as_file) const {
+ return ResolvePath(path, as_file, root_path_).NormalizePathSeparatorsTo('/');
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(
+ const SourceFile& file) const {
+ return file.Resolve(secondary_source_path_).NormalizePathSeparatorsTo('/');
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(const SourceDir& dir) const {
+ return dir.Resolve(secondary_source_path_).NormalizePathSeparatorsTo('/');
+}
+
+base::FilePath BuildSettings::GetFullPathSecondary(const std::string& path,
+ bool as_file) const {
+ return ResolvePath(path, as_file, secondary_source_path_)
+ .NormalizePathSeparatorsTo('/');
+}
+
+void BuildSettings::ItemDefined(std::unique_ptr<Item> item) const {
+ DCHECK(item);
+ if (item_defined_callback_)
+ item_defined_callback_(std::move(item));
+}
diff --git a/gn/src/gn/build_settings.h b/gn/src/gn/build_settings.h
new file mode 100644
index 00000000000..f8dc502589a
--- /dev/null
+++ b/gn/src/gn/build_settings.h
@@ -0,0 +1,152 @@
+// 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 TOOLS_GN_BUILD_SETTINGS_H_
+#define TOOLS_GN_BUILD_SETTINGS_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gn/args.h"
+#include "gn/label.h"
+#include "gn/scope.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/version.h"
+
+class Item;
+
+// Settings for one build, which is one toplevel output directory. There
+// may be multiple Settings objects that refer to this, one for each toolchain.
+class BuildSettings {
+ public:
+ using ItemDefinedCallback = std::function<void(std::unique_ptr<Item>)>;
+ using PrintCallback = std::function<void(const std::string&)>;
+
+ BuildSettings();
+ BuildSettings(const BuildSettings& other);
+
+ // Root target label.
+ const Label& root_target_label() const { return root_target_label_; }
+ void SetRootTargetLabel(const Label& r);
+
+ // Absolute path of the source root on the local system. Everything is
+ // relative to this. Does not end in a [back]slash.
+ const base::FilePath& root_path() const { return root_path_; }
+ const base::FilePath& dotfile_name() const { return dotfile_name_; }
+ const std::string& root_path_utf8() const { return root_path_utf8_; }
+ void SetRootPath(const base::FilePath& r);
+ void set_dotfile_name(const base::FilePath& d) { dotfile_name_ = d; }
+
+ // When nonempty, specifies a parallel directory higherarchy in which to
+ // search for buildfiles if they're not found in the root higherarchy. This
+ // allows us to keep buildfiles in a separate tree during development.
+ const base::FilePath& secondary_source_path() const {
+ return secondary_source_path_;
+ }
+ void SetSecondarySourcePath(const SourceDir& d);
+
+ // Path of the python executable to run scripts with.
+ base::FilePath python_path() const { return python_path_; }
+ void set_python_path(const base::FilePath& p) { python_path_ = p; }
+
+ // Required Ninja version.
+ const Version& ninja_required_version() const {
+ return ninja_required_version_;
+ }
+ void set_ninja_required_version(Version v) { ninja_required_version_ = v; }
+
+ const SourceFile& build_config_file() const { return build_config_file_; }
+ void set_build_config_file(const SourceFile& f) { build_config_file_ = f; }
+
+ // Path to a file containing the default text to use when running `gn args`.
+ const SourceFile& arg_file_template_path() const {
+ return arg_file_template_path_;
+ }
+ void set_arg_file_template_path(const SourceFile& f) {
+ arg_file_template_path_ = f;
+ }
+
+ // The build directory is the root of all output files. The default toolchain
+ // files go into here, and non-default toolchains will have separate
+ // toolchain-specific root directories inside this.
+ const SourceDir& build_dir() const { return build_dir_; }
+ void SetBuildDir(const SourceDir& dir);
+
+ // The build args are normally specified on the command-line.
+ Args& build_args() { return build_args_; }
+ const Args& build_args() const { return build_args_; }
+
+ // Returns the full absolute OS path cooresponding to the given file in the
+ // root source tree.
+ base::FilePath GetFullPath(const SourceFile& file) const;
+ base::FilePath GetFullPath(const SourceDir& dir) const;
+ // Works the same way as other GetFullPath.
+ // Parameter as_file defines whether path should be treated as a
+ // SourceFile or SourceDir value.
+ base::FilePath GetFullPath(const std::string& path, bool as_file) const;
+
+ // Returns the absolute OS path inside the secondary source path. Will return
+ // an empty FilePath if the secondary source path is empty. When loading a
+ // buildfile, the GetFullPath should always be consulted first.
+ base::FilePath GetFullPathSecondary(const SourceFile& file) const;
+ base::FilePath GetFullPathSecondary(const SourceDir& dir) const;
+ // Works the same way as other GetFullPathSecondary.
+ // Parameter as_file defines whether path should be treated as a
+ // SourceFile or SourceDir value.
+ base::FilePath GetFullPathSecondary(const std::string& path,
+ bool as_file) const;
+
+ // Called when an item is defined from a background thread.
+ void ItemDefined(std::unique_ptr<Item> item) const;
+ void set_item_defined_callback(ItemDefinedCallback cb) {
+ item_defined_callback_ = cb;
+ }
+
+ // Defines a callback that will be used to override the behavior of the
+ // print function. This is used in tests to collect print output. If the
+ // callback is is_null() (the default) the output will be printed to the
+ // console.
+ const PrintCallback& print_callback() const { return print_callback_; }
+ void set_print_callback(const PrintCallback& cb) { print_callback_ = cb; }
+
+ // A list of files that can call exec_script(). If the returned pointer is
+ // null, exec_script may be called from anywhere.
+ const SourceFileSet* exec_script_whitelist() const {
+ return exec_script_whitelist_.get();
+ }
+ void set_exec_script_whitelist(std::unique_ptr<SourceFileSet> list) {
+ exec_script_whitelist_ = std::move(list);
+ }
+
+ private:
+ Label root_target_label_;
+ base::FilePath dotfile_name_;
+ base::FilePath root_path_;
+ std::string root_path_utf8_;
+ base::FilePath secondary_source_path_;
+ base::FilePath python_path_;
+
+ // See 40045b9 for the reason behind using 1.7.2 as the default version.
+ Version ninja_required_version_{1, 7, 2};
+
+ SourceFile build_config_file_;
+ SourceFile arg_file_template_path_;
+ SourceDir build_dir_;
+ Args build_args_;
+
+ ItemDefinedCallback item_defined_callback_;
+ PrintCallback print_callback_;
+
+ std::unique_ptr<SourceFileSet> exec_script_whitelist_;
+
+ DISALLOW_ASSIGN(BuildSettings);
+};
+
+#endif // TOOLS_GN_BUILD_SETTINGS_H_
diff --git a/gn/src/gn/builder.cc b/gn/src/gn/builder.cc
new file mode 100644
index 00000000000..063493524a9
--- /dev/null
+++ b/gn/src/gn/builder.cc
@@ -0,0 +1,602 @@
+// 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 "gn/builder.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "gn/action_values.h"
+#include "gn/config.h"
+#include "gn/deps_iterator.h"
+#include "gn/err.h"
+#include "gn/loader.h"
+#include "gn/pool.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+
+namespace {
+
+using BuilderRecordSet = BuilderRecord::BuilderRecordSet;
+
+// Recursively looks in the tree for a given node, returning true if it
+// was found in the dependecy graph. This is used to see if a given node
+// participates in a cycle.
+//
+// If this returns true, the cycle will be in *path. This should point to an
+// empty vector for the first call. During computation, the path will contain
+// the full dependency path to the current node.
+//
+// Return false means no cycle was found.
+bool RecursiveFindCycle(const BuilderRecord* search_in,
+ std::vector<const BuilderRecord*>* path) {
+ path->push_back(search_in);
+ for (auto* cur : search_in->unresolved_deps()) {
+ std::vector<const BuilderRecord*>::iterator found =
+ std::find(path->begin(), path->end(), cur);
+ if (found != path->end()) {
+ // This item is already in the set, we found the cycle. Everything before
+ // the first definition of cur is irrelevant to the cycle.
+ path->erase(path->begin(), found);
+ path->push_back(cur);
+ return true;
+ }
+
+ if (RecursiveFindCycle(cur, path))
+ return true; // Found cycle.
+ }
+ path->pop_back();
+ return false;
+}
+
+} // namespace
+
+Builder::Builder(Loader* loader) : loader_(loader) {}
+
+Builder::~Builder() = default;
+
+void Builder::ItemDefined(std::unique_ptr<Item> item) {
+ ScopedTrace trace(TraceItem::TRACE_DEFINE_TARGET, item->label());
+ trace.SetToolchain(item->settings()->toolchain_label());
+
+ BuilderRecord::ItemType type = BuilderRecord::TypeOfItem(item.get());
+
+ Err err;
+ BuilderRecord* record =
+ GetOrCreateRecordOfType(item->label(), item->defined_from(), type, &err);
+ if (!record) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ // Check that it's not been already defined.
+ if (record->item()) {
+ err = Err(item->defined_from(), "Duplicate definition.",
+ "The item\n " + item->label().GetUserVisibleName(false) +
+ "\nwas already defined.");
+ err.AppendSubErr(
+ Err(record->item()->defined_from(), "Previous definition:"));
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ record->set_item(std::move(item));
+
+ // Do target-specific dependency setup. This will also schedule dependency
+ // loads for targets that are required.
+ switch (type) {
+ case BuilderRecord::ITEM_TARGET:
+ TargetDefined(record, &err);
+ break;
+ case BuilderRecord::ITEM_CONFIG:
+ ConfigDefined(record, &err);
+ break;
+ case BuilderRecord::ITEM_TOOLCHAIN:
+ ToolchainDefined(record, &err);
+ break;
+ default:
+ break;
+ }
+ if (err.has_error()) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ if (record->can_resolve()) {
+ if (!ResolveItem(record, &err)) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+ }
+}
+
+const Item* Builder::GetItem(const Label& label) const {
+ const BuilderRecord* record = GetRecord(label);
+ if (!record)
+ return nullptr;
+ return record->item();
+}
+
+const Toolchain* Builder::GetToolchain(const Label& label) const {
+ const BuilderRecord* record = GetRecord(label);
+ if (!record)
+ return nullptr;
+ if (!record->item())
+ return nullptr;
+ return record->item()->AsToolchain();
+}
+
+std::vector<const BuilderRecord*> Builder::GetAllRecords() const {
+ std::vector<const BuilderRecord*> result;
+ result.reserve(records_.size());
+ for (const auto& record : records_)
+ result.push_back(record.second.get());
+ return result;
+}
+
+std::vector<const Item*> Builder::GetAllResolvedItems() const {
+ std::vector<const Item*> result;
+ result.reserve(records_.size());
+ for (const auto& record : records_) {
+ if (record.second->type() != BuilderRecord::ITEM_UNKNOWN &&
+ record.second->should_generate() && record.second->item()) {
+ result.push_back(record.second->item());
+ }
+ }
+
+ return result;
+}
+
+std::vector<const Target*> Builder::GetAllResolvedTargets() const {
+ std::vector<const Target*> result;
+ result.reserve(records_.size());
+ for (const auto& record : records_) {
+ if (record.second->type() == BuilderRecord::ITEM_TARGET &&
+ record.second->should_generate() && record.second->item())
+ result.push_back(record.second->item()->AsTarget());
+ }
+ return result;
+}
+
+const BuilderRecord* Builder::GetRecord(const Label& label) const {
+ // Forward to the non-const version.
+ return const_cast<Builder*>(this)->GetRecord(label);
+}
+
+BuilderRecord* Builder::GetRecord(const Label& label) {
+ auto found = records_.find(label);
+ return (found != records_.end()) ? found->second.get() : nullptr;
+}
+
+bool Builder::CheckForBadItems(Err* err) const {
+ // Look for errors where we find a defined node with an item that refers to
+ // an undefined one with no item. There may be other nodes in turn depending
+ // on our defined one, but listing those isn't helpful: we want to find the
+ // broken link.
+ //
+ // This finds normal "missing dependency" errors but does not find circular
+ // dependencies because in this case all items in the cycle will be GENERATED
+ // but none will be resolved. If this happens, we'll check explicitly for
+ // that below.
+ std::vector<const BuilderRecord*> bad_records;
+ std::string depstring;
+ for (const auto& record_pair : records_) {
+ const BuilderRecord* src = record_pair.second.get();
+ if (!src->should_generate())
+ continue; // Skip ungenerated nodes.
+
+ if (!src->resolved()) {
+ bad_records.push_back(src);
+
+ // Check dependencies.
+ for (auto* dest : src->unresolved_deps()) {
+ if (!dest->item()) {
+ depstring += src->label().GetUserVisibleName(true) + "\n needs " +
+ dest->label().GetUserVisibleName(true) + "\n";
+ }
+ }
+ }
+ }
+
+ if (!depstring.empty()) {
+ *err = Err(Location(), "Unresolved dependencies.", depstring);
+ return false;
+ }
+
+ if (!bad_records.empty()) {
+ // Our logic above found a bad node but didn't identify the problem. This
+ // normally means a circular dependency.
+ depstring = CheckForCircularDependencies(bad_records);
+ if (depstring.empty()) {
+ // Something's very wrong, just dump out the bad nodes.
+ depstring =
+ "I have no idea what went wrong, but these are unresolved, "
+ "possibly due to an\ninternal error:";
+ for (auto* bad_record : bad_records) {
+ depstring +=
+ "\n\"" + bad_record->label().GetUserVisibleName(false) + "\"";
+ }
+ *err = Err(Location(), "", depstring);
+ } else {
+ *err = Err(Location(), "Dependency cycle:", depstring);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool Builder::TargetDefined(BuilderRecord* record, Err* err) {
+ Target* target = record->item()->AsTarget();
+
+ if (!AddDeps(record, target->public_deps(), err) ||
+ !AddDeps(record, target->private_deps(), err) ||
+ !AddDeps(record, target->data_deps(), err) ||
+ !AddDeps(record, target->configs().vector(), err) ||
+ !AddDeps(record, target->all_dependent_configs(), err) ||
+ !AddDeps(record, target->public_configs(), err) ||
+ !AddActionValuesDep(record, target->action_values(), err) ||
+ !AddToolchainDep(record, target, err))
+ return false;
+
+ // All targets in the default toolchain get generated by default. We also
+ // check if this target was previously marked as "required" and force setting
+ // the bit again so the target's dependencies (which we now know) get the
+ // required bit pushed to them.
+ if (record->should_generate() || target->settings()->is_default())
+ RecursiveSetShouldGenerate(record, true);
+
+ return true;
+}
+
+bool Builder::ConfigDefined(BuilderRecord* record, Err* err) {
+ Config* config = record->item()->AsConfig();
+ if (!AddDeps(record, config->configs(), err))
+ return false;
+
+ // Make sure all deps of this config are scheduled to be loaded. For other
+ // item types like targets, the "should generate" flag is propagated around
+ // to mark whether this should happen. We could call
+ // RecursiveSetShouldGenerate to do this step here, but since configs nor
+ // anything they depend on is actually written, the "generate" flag isn't
+ // relevant and means extra book keeping. Just force load any deps of this
+ // config.
+ for (auto* cur : record->all_deps())
+ ScheduleItemLoadIfNecessary(cur);
+
+ return true;
+}
+
+bool Builder::ToolchainDefined(BuilderRecord* record, Err* err) {
+ Toolchain* toolchain = record->item()->AsToolchain();
+
+ if (!AddDeps(record, toolchain->deps(), err))
+ return false;
+
+ for (const auto& tool : toolchain->tools()) {
+ if (tool.second->pool().label.is_null())
+ continue;
+
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ tool.second->pool().label, tool.second->pool().origin,
+ BuilderRecord::ITEM_POOL, err);
+ if (!dep_record)
+ return false;
+ record->AddDep(dep_record);
+ }
+
+ // The default toolchain gets generated by default. Also propogate the
+ // generate flag if it depends on items in a non-default toolchain.
+ if (record->should_generate() ||
+ toolchain->settings()->default_toolchain_label() == toolchain->label())
+ RecursiveSetShouldGenerate(record, true);
+
+ loader_->ToolchainLoaded(toolchain);
+ return true;
+}
+
+BuilderRecord* Builder::GetOrCreateRecordOfType(const Label& label,
+ const ParseNode* request_from,
+ BuilderRecord::ItemType type,
+ Err* err) {
+ BuilderRecord* record = GetRecord(label);
+ if (!record) {
+ // Not seen this record yet, create a new one.
+ auto new_record = std::make_unique<BuilderRecord>(type, label);
+ new_record->set_originally_referenced_from(request_from);
+ record = new_record.get();
+ records_[label] = std::move(new_record);
+ return record;
+ }
+
+ // Check types.
+ if (record->type() != type) {
+ std::string msg =
+ "The type of " + label.GetUserVisibleName(false) + "\nhere is a " +
+ BuilderRecord::GetNameForType(type) + " but was previously seen as a " +
+ BuilderRecord::GetNameForType(record->type()) +
+ ".\n\n"
+ "The most common cause is that the label of a config was put in the\n"
+ "in the deps section of a target (or vice-versa).";
+ *err = Err(request_from, "Item type does not match.", msg);
+ if (record->originally_referenced_from()) {
+ err->AppendSubErr(
+ Err(record->originally_referenced_from(), std::string()));
+ }
+ return nullptr;
+ }
+
+ return record;
+}
+
+BuilderRecord* Builder::GetResolvedRecordOfType(const Label& label,
+ const ParseNode* origin,
+ BuilderRecord::ItemType type,
+ Err* err) {
+ BuilderRecord* record = GetRecord(label);
+ if (!record) {
+ *err = Err(origin, "Item not found",
+ "\"" + label.GetUserVisibleName(false) +
+ "\" doesn't\n"
+ "refer to an existent thing.");
+ return nullptr;
+ }
+
+ const Item* item = record->item();
+ if (!item) {
+ *err = Err(
+ origin, "Item not resolved.",
+ "\"" + label.GetUserVisibleName(false) + "\" hasn't been resolved.\n");
+ return nullptr;
+ }
+
+ if (!BuilderRecord::IsItemOfType(item, type)) {
+ *err =
+ Err(origin,
+ std::string("This is not a ") + BuilderRecord::GetNameForType(type),
+ "\"" + label.GetUserVisibleName(false) + "\" refers to a " +
+ item->GetItemTypeName() + " instead of a " +
+ BuilderRecord::GetNameForType(type) + ".");
+ return nullptr;
+ }
+ return record;
+}
+
+bool Builder::AddDeps(BuilderRecord* record,
+ const LabelConfigVector& configs,
+ Err* err) {
+ for (const auto& config : configs) {
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ config.label, config.origin, BuilderRecord::ITEM_CONFIG, err);
+ if (!dep_record)
+ return false;
+ record->AddDep(dep_record);
+ }
+ return true;
+}
+
+bool Builder::AddDeps(BuilderRecord* record,
+ const UniqueVector<LabelConfigPair>& configs,
+ Err* err) {
+ for (const auto& config : configs) {
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ config.label, config.origin, BuilderRecord::ITEM_CONFIG, err);
+ if (!dep_record)
+ return false;
+ record->AddDep(dep_record);
+ }
+ return true;
+}
+
+bool Builder::AddDeps(BuilderRecord* record,
+ const LabelTargetVector& targets,
+ Err* err) {
+ for (const auto& target : targets) {
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ target.label, target.origin, BuilderRecord::ITEM_TARGET, err);
+ if (!dep_record)
+ return false;
+ record->AddDep(dep_record);
+ }
+ return true;
+}
+
+bool Builder::AddActionValuesDep(BuilderRecord* record,
+ const ActionValues& action_values,
+ Err* err) {
+ if (action_values.pool().label.is_null())
+ return true;
+
+ BuilderRecord* pool_record = GetOrCreateRecordOfType(
+ action_values.pool().label, action_values.pool().origin,
+ BuilderRecord::ITEM_POOL, err);
+ if (!pool_record)
+ return false;
+ record->AddDep(pool_record);
+
+ return true;
+}
+
+bool Builder::AddToolchainDep(BuilderRecord* record,
+ const Target* target,
+ Err* err) {
+ BuilderRecord* toolchain_record = GetOrCreateRecordOfType(
+ target->settings()->toolchain_label(), target->defined_from(),
+ BuilderRecord::ITEM_TOOLCHAIN, err);
+ if (!toolchain_record)
+ return false;
+ record->AddDep(toolchain_record);
+
+ return true;
+}
+
+void Builder::RecursiveSetShouldGenerate(BuilderRecord* record, bool force) {
+ if (!record->should_generate()) {
+ record->set_should_generate(true);
+
+ // This may have caused the item to go into "resolved and generated" state.
+ if (record->resolved() && resolved_and_generated_callback_)
+ resolved_and_generated_callback_(record);
+ } else if (!force) {
+ return; // Already set and we're not required to iterate dependencies.
+ }
+
+ for (auto* cur : record->all_deps()) {
+ if (!cur->should_generate()) {
+ ScheduleItemLoadIfNecessary(cur);
+ RecursiveSetShouldGenerate(cur, false);
+ }
+ }
+}
+
+void Builder::ScheduleItemLoadIfNecessary(BuilderRecord* record) {
+ const ParseNode* origin = record->originally_referenced_from();
+ loader_->Load(record->label(), origin ? origin->GetRange() : LocationRange());
+}
+
+bool Builder::ResolveItem(BuilderRecord* record, Err* err) {
+ DCHECK(record->can_resolve() && !record->resolved());
+
+ if (record->type() == BuilderRecord::ITEM_TARGET) {
+ Target* target = record->item()->AsTarget();
+ if (!ResolveDeps(&target->public_deps(), err) ||
+ !ResolveDeps(&target->private_deps(), err) ||
+ !ResolveDeps(&target->data_deps(), err) ||
+ !ResolveConfigs(&target->configs(), err) ||
+ !ResolveConfigs(&target->all_dependent_configs(), err) ||
+ !ResolveConfigs(&target->public_configs(), err) ||
+ !ResolveActionValues(&target->action_values(), err) ||
+ !ResolveToolchain(target, err))
+ return false;
+ } else if (record->type() == BuilderRecord::ITEM_CONFIG) {
+ Config* config = record->item()->AsConfig();
+ if (!ResolveConfigs(&config->configs(), err))
+ return false;
+ } else if (record->type() == BuilderRecord::ITEM_TOOLCHAIN) {
+ Toolchain* toolchain = record->item()->AsToolchain();
+ if (!ResolveDeps(&toolchain->deps(), err))
+ return false;
+ if (!ResolvePools(toolchain, err))
+ return false;
+ }
+
+ record->set_resolved(true);
+
+ if (!record->item()->OnResolved(err))
+ return false;
+ if (record->should_generate() && resolved_and_generated_callback_)
+ resolved_and_generated_callback_(record);
+
+ // Recursively update everybody waiting on this item to be resolved.
+ for (BuilderRecord* waiting : record->waiting_on_resolution()) {
+ DCHECK(waiting->unresolved_deps().find(record) !=
+ waiting->unresolved_deps().end());
+ waiting->unresolved_deps().erase(record);
+
+ if (waiting->can_resolve()) {
+ if (!ResolveItem(waiting, err))
+ return false;
+ }
+ }
+ record->waiting_on_resolution().clear();
+ return true;
+}
+
+bool Builder::ResolveDeps(LabelTargetVector* deps, Err* err) {
+ for (LabelTargetPair& cur : *deps) {
+ DCHECK(!cur.ptr);
+
+ BuilderRecord* record = GetResolvedRecordOfType(
+ cur.label, cur.origin, BuilderRecord::ITEM_TARGET, err);
+ if (!record)
+ return false;
+ cur.ptr = record->item()->AsTarget();
+ }
+ return true;
+}
+
+bool Builder::ResolveConfigs(UniqueVector<LabelConfigPair>* configs, Err* err) {
+ for (const auto& cur : *configs) {
+ DCHECK(!cur.ptr);
+
+ BuilderRecord* record = GetResolvedRecordOfType(
+ cur.label, cur.origin, BuilderRecord::ITEM_CONFIG, err);
+ if (!record)
+ return false;
+ const_cast<LabelConfigPair&>(cur).ptr = record->item()->AsConfig();
+ }
+ return true;
+}
+
+bool Builder::ResolveToolchain(Target* target, Err* err) {
+ BuilderRecord* record = GetResolvedRecordOfType(
+ target->settings()->toolchain_label(), target->defined_from(),
+ BuilderRecord::ITEM_TOOLCHAIN, err);
+ if (!record) {
+ *err = Err(
+ target->defined_from(), "Toolchain for target not defined.",
+ "I was hoping to find a toolchain " +
+ target->settings()->toolchain_label().GetUserVisibleName(false));
+ return false;
+ }
+
+ if (!target->SetToolchain(record->item()->AsToolchain(), err))
+ return false;
+
+ return true;
+}
+
+bool Builder::ResolveActionValues(ActionValues* action_values, Err* err) {
+ if (action_values->pool().label.is_null())
+ return true;
+
+ BuilderRecord* record = GetResolvedRecordOfType(
+ action_values->pool().label, action_values->pool().origin,
+ BuilderRecord::ITEM_POOL, err);
+ if (!record)
+ return false;
+ action_values->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
+
+ return true;
+}
+
+bool Builder::ResolvePools(Toolchain* toolchain, Err* err) {
+ for (const auto& tool : toolchain->tools()) {
+ if (tool.second->pool().label.is_null())
+ continue;
+
+ BuilderRecord* record = GetResolvedRecordOfType(
+ tool.second->pool().label, toolchain->defined_from(),
+ BuilderRecord::ITEM_POOL, err);
+ if (!record) {
+ *err = Err(tool.second->pool().origin, "Pool for tool not defined.",
+ "I was hoping to find a pool " +
+ tool.second->pool().label.GetUserVisibleName(false));
+ return false;
+ }
+
+ tool.second->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
+ }
+
+ return true;
+}
+
+std::string Builder::CheckForCircularDependencies(
+ const std::vector<const BuilderRecord*>& bad_records) const {
+ std::vector<const BuilderRecord*> cycle;
+ if (!RecursiveFindCycle(bad_records[0], &cycle))
+ return std::string(); // Didn't find a cycle, something else is wrong.
+
+ std::string ret;
+ for (size_t i = 0; i < cycle.size(); i++) {
+ ret += " " + cycle[i]->label().GetUserVisibleName(loader_->GetDefaultToolchain());
+ if (i != cycle.size() - 1)
+ ret += " ->";
+ ret += "\n";
+ }
+
+ return ret;
+}
diff --git a/gn/src/gn/builder.h b/gn/src/gn/builder.h
new file mode 100644
index 00000000000..77cdc5e7a79
--- /dev/null
+++ b/gn/src/gn/builder.h
@@ -0,0 +1,146 @@
+// 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 TOOLS_GN_BUILDER_H_
+#define TOOLS_GN_BUILDER_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "gn/builder_record.h"
+#include "gn/label.h"
+#include "gn/label_ptr.h"
+#include "gn/unique_vector.h"
+
+class ActionValues;
+class Err;
+class Loader;
+class ParseNode;
+
+// The builder assembles the dependency tree. It is not threadsafe and runs on
+// the main thread only. See also BuilderRecord.
+class Builder {
+ public:
+ using ResolvedGeneratedCallback = std::function<void(const BuilderRecord*)>;
+
+ explicit Builder(Loader* loader);
+ ~Builder();
+
+ // The resolved callback is called when a target has been both resolved and
+ // marked generated. This will be executed only on the main thread.
+ void set_resolved_and_generated_callback(
+ const ResolvedGeneratedCallback& cb) {
+ resolved_and_generated_callback_ = cb;
+ }
+
+ Loader* loader() const { return loader_; }
+
+ void ItemDefined(std::unique_ptr<Item> item);
+
+ // Returns NULL if there is not a thing with the corresponding label.
+ const Item* GetItem(const Label& label) const;
+ const Toolchain* GetToolchain(const Label& label) const;
+
+ std::vector<const BuilderRecord*> GetAllRecords() const;
+
+ // Returns items which should be generated and which are defined.
+ std::vector<const Item*> GetAllResolvedItems() const;
+
+ // Returns targets which should be generated and which are defined.
+ std::vector<const Target*> GetAllResolvedTargets() const;
+
+ // Returns the record for the given label, or NULL if it doesn't exist.
+ // Mostly used for unit tests.
+ const BuilderRecord* GetRecord(const Label& label) const;
+ BuilderRecord* GetRecord(const Label& label);
+
+ // If there are any undefined references, returns false and sets the error.
+ bool CheckForBadItems(Err* err) const;
+
+ private:
+ bool TargetDefined(BuilderRecord* record, Err* err);
+ bool ConfigDefined(BuilderRecord* record, Err* err);
+ bool ToolchainDefined(BuilderRecord* record, Err* err);
+
+ // Returns the record associated with the given label. This function checks
+ // that if we already have references for it, the type matches. If no record
+ // exists yet, a new one will be created.
+ //
+ // If any of the conditions fail, the return value will be null and the error
+ // will be set. request_from is used as the source of the error.
+ BuilderRecord* GetOrCreateRecordOfType(const Label& label,
+ const ParseNode* request_from,
+ BuilderRecord::ItemType type,
+ Err* err);
+
+ // Returns the record associated with the given label. This function checks
+ // that it's already been resolved to the correct type.
+ //
+ // If any of the conditions fail, the return value will be null and the error
+ // will be set. request_from is used as the source of the error.
+ BuilderRecord* GetResolvedRecordOfType(const Label& label,
+ const ParseNode* request_from,
+ BuilderRecord::ItemType type,
+ Err* err);
+
+ bool AddDeps(BuilderRecord* record,
+ const LabelConfigVector& configs,
+ Err* err);
+ bool AddDeps(BuilderRecord* record,
+ const UniqueVector<LabelConfigPair>& configs,
+ Err* err);
+ bool AddDeps(BuilderRecord* record,
+ const LabelTargetVector& targets,
+ Err* err);
+ bool AddActionValuesDep(BuilderRecord* record,
+ const ActionValues& action_values,
+ Err* err);
+ bool AddToolchainDep(BuilderRecord* record, const Target* target, Err* err);
+
+ // Given a target, sets the "should generate" bit and pushes it through the
+ // dependency tree. Any time the bit it set, we ensure that the given item is
+ // scheduled to be loaded.
+ //
+ // If the force flag is set, we'll ignore the current state of the record's
+ // should_generate flag, and set it on the dependents every time. This is
+ // used when defining a target: the "should generate" may have been set
+ // before the item was defined (if it is required by something that is
+ // required). In this case, we need to re-push the "should generate" flag
+ // to the item's dependencies.
+ void RecursiveSetShouldGenerate(BuilderRecord* record, bool force);
+
+ void ScheduleItemLoadIfNecessary(BuilderRecord* record);
+
+ // This takes a BuilderRecord with resolved depdencies, and fills in the
+ // target's Label*Vectors with the resolved pointers.
+ bool ResolveItem(BuilderRecord* record, Err* err);
+
+ // Fills in the pointers in the given vector based on the labels. We assume
+ // that everything should be resolved by this point, so will return an error
+ // if anything isn't found or if the type doesn't match.
+ bool ResolveDeps(LabelTargetVector* deps, Err* err);
+ bool ResolveConfigs(UniqueVector<LabelConfigPair>* configs, Err* err);
+ bool ResolveActionValues(ActionValues* action_values, Err* err);
+ bool ResolveToolchain(Target* target, Err* err);
+ bool ResolvePools(Toolchain* toolchain, Err* err);
+
+ // Given a list of unresolved records, tries to find any circular
+ // dependencies and returns the string describing the problem. If no circular
+ // deps were found, returns the empty string.
+ std::string CheckForCircularDependencies(
+ const std::vector<const BuilderRecord*>& bad_records) const;
+
+ // Non owning pointer.
+ Loader* loader_;
+
+ std::map<Label, std::unique_ptr<BuilderRecord>> records_;
+
+ ResolvedGeneratedCallback resolved_and_generated_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Builder);
+};
+
+#endif // TOOLS_GN_BUILDER_H_
diff --git a/gn/src/gn/builder_record.cc b/gn/src/gn/builder_record.cc
new file mode 100644
index 00000000000..cbe8dae83d6
--- /dev/null
+++ b/gn/src/gn/builder_record.cc
@@ -0,0 +1,67 @@
+// 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 "gn/builder_record.h"
+
+#include "gn/item.h"
+
+BuilderRecord::BuilderRecord(ItemType type, const Label& label)
+ : type_(type), label_(label) {}
+
+// static
+const char* BuilderRecord::GetNameForType(ItemType type) {
+ switch (type) {
+ case ITEM_TARGET:
+ return "target";
+ case ITEM_CONFIG:
+ return "config";
+ case ITEM_TOOLCHAIN:
+ return "toolchain";
+ case ITEM_POOL:
+ return "pool";
+ case ITEM_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+// static
+bool BuilderRecord::IsItemOfType(const Item* item, ItemType type) {
+ switch (type) {
+ case ITEM_TARGET:
+ return !!item->AsTarget();
+ case ITEM_CONFIG:
+ return !!item->AsConfig();
+ case ITEM_TOOLCHAIN:
+ return !!item->AsToolchain();
+ case ITEM_POOL:
+ return !!item->AsPool();
+ case ITEM_UNKNOWN:
+ default:
+ return false;
+ }
+}
+
+// static
+BuilderRecord::ItemType BuilderRecord::TypeOfItem(const Item* item) {
+ if (item->AsTarget())
+ return ITEM_TARGET;
+ if (item->AsConfig())
+ return ITEM_CONFIG;
+ if (item->AsToolchain())
+ return ITEM_TOOLCHAIN;
+ if (item->AsPool())
+ return ITEM_POOL;
+
+ NOTREACHED();
+ return ITEM_UNKNOWN;
+}
+
+void BuilderRecord::AddDep(BuilderRecord* record) {
+ all_deps_.insert(record);
+ if (!record->resolved()) {
+ unresolved_deps_.insert(record);
+ record->waiting_on_resolution_.insert(this);
+ }
+}
diff --git a/gn/src/gn/builder_record.h b/gn/src/gn/builder_record.h
new file mode 100644
index 00000000000..1ccb19aaf61
--- /dev/null
+++ b/gn/src/gn/builder_record.h
@@ -0,0 +1,110 @@
+// 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 TOOLS_GN_BUILDER_RECORD_H_
+#define TOOLS_GN_BUILDER_RECORD_H_
+
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/macros.h"
+#include "gn/item.h"
+#include "gn/location.h"
+
+class ParseNode;
+
+// This class is used by the builder to manage the loading of the dependency
+// tree. It holds a reference to an item and links to other records that the
+// item depends on, both resolved ones, and unresolved ones.
+//
+// If a target depends on another one that hasn't been defined yet, we'll make
+// a placeholder BuilderRecord with no item, and try to load the buildfile
+// associated with the new item. The item will get filled in when we encounter
+// the declaration for the item (or when we're done and realize there are
+// undefined items).
+//
+// You can also have null item pointers when the target is not required for
+// the current build (should_generate is false).
+class BuilderRecord {
+ public:
+ using BuilderRecordSet = std::set<BuilderRecord*>;
+
+ enum ItemType {
+ ITEM_UNKNOWN,
+ ITEM_TARGET,
+ ITEM_CONFIG,
+ ITEM_TOOLCHAIN,
+ ITEM_POOL
+ };
+
+ BuilderRecord(ItemType type, const Label& label);
+
+ ItemType type() const { return type_; }
+ const Label& label() const { return label_; }
+
+ // Returns a user-ready name for the given type. e.g. "target".
+ static const char* GetNameForType(ItemType type);
+
+ // Returns true if the given item is of the given type.
+ static bool IsItemOfType(const Item* item, ItemType type);
+
+ // Returns the type enum for the given item.
+ static ItemType TypeOfItem(const Item* item);
+
+ Item* item() { return item_.get(); }
+ const Item* item() const { return item_.get(); }
+ void set_item(std::unique_ptr<Item> item) { item_ = std::move(item); }
+
+ // Indicates from where this item was originally referenced from that caused
+ // it to be loaded. For targets for which we encountered the declaration
+ // before a reference, this will be the empty range.
+ const ParseNode* originally_referenced_from() const {
+ return originally_referenced_from_;
+ }
+ void set_originally_referenced_from(const ParseNode* pn) {
+ originally_referenced_from_ = pn;
+ }
+
+ bool should_generate() const { return should_generate_; }
+ void set_should_generate(bool sg) { should_generate_ = sg; }
+
+ bool resolved() const { return resolved_; }
+ void set_resolved(bool r) { resolved_ = r; }
+
+ bool can_resolve() const { return item_ && unresolved_deps_.empty(); }
+
+ // All records this one is depending on.
+ BuilderRecordSet& all_deps() { return all_deps_; }
+ const BuilderRecordSet& all_deps() const { return all_deps_; }
+
+ // Unresolved records this one is depending on. A subset of all... above.
+ BuilderRecordSet& unresolved_deps() { return unresolved_deps_; }
+ const BuilderRecordSet& unresolved_deps() const { return unresolved_deps_; }
+
+ // Records that are waiting on this one to be resolved. This is the other
+ // end of the "unresolved deps" arrow.
+ BuilderRecordSet& waiting_on_resolution() { return waiting_on_resolution_; }
+ const BuilderRecordSet& waiting_on_resolution() const {
+ return waiting_on_resolution_;
+ }
+
+ void AddDep(BuilderRecord* record);
+
+ private:
+ ItemType type_;
+ Label label_;
+ std::unique_ptr<Item> item_;
+ const ParseNode* originally_referenced_from_ = nullptr;
+ bool should_generate_ = false;
+ bool resolved_ = false;
+
+ BuilderRecordSet all_deps_;
+ BuilderRecordSet unresolved_deps_;
+ BuilderRecordSet waiting_on_resolution_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuilderRecord);
+};
+
+#endif // TOOLS_GN_BUILDER_RECORD_H_
diff --git a/gn/src/gn/builder_unittest.cc b/gn/src/gn/builder_unittest.cc
new file mode 100644
index 00000000000..b0f48f03481
--- /dev/null
+++ b/gn/src/gn/builder_unittest.cc
@@ -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.
+
+#include "gn/builder.h"
+#include "gn/config.h"
+#include "gn/loader.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "gn/toolchain.h"
+#include "util/test/test.h"
+
+namespace gn_builder_unittest {
+
+class MockLoader : public Loader {
+ public:
+ MockLoader() = default;
+
+ // Loader implementation:
+ void Load(const SourceFile& file,
+ const LocationRange& origin,
+ const Label& toolchain_name) override {
+ files_.push_back(file);
+ }
+ void ToolchainLoaded(const Toolchain* toolchain) override {}
+ Label GetDefaultToolchain() const override { return Label(); }
+ const Settings* GetToolchainSettings(const Label& label) const override {
+ return nullptr;
+ }
+ SourceFile BuildFileForLabel(const Label& label) const override {
+ return SourceFile(label.dir().value() + "BUILD.gn");
+ }
+
+ bool HasLoadedNone() const { return files_.empty(); }
+
+ // Returns true if one/two loads have been requested and they match the given
+ // file(s). This will clear the records so it will be empty for the next call.
+ bool HasLoadedOne(const SourceFile& file) {
+ if (files_.size() != 1u) {
+ files_.clear();
+ return false;
+ }
+ bool match = (files_[0] == file);
+ files_.clear();
+ return match;
+ }
+ bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
+ if (files_.size() != 2u) {
+ files_.clear();
+ return false;
+ }
+
+ bool match = ((files_[0] == a && files_[1] == b) ||
+ (files_[0] == b && files_[1] == a));
+ files_.clear();
+ return match;
+ }
+
+ private:
+ ~MockLoader() override = default;
+
+ std::vector<SourceFile> files_;
+};
+
+class BuilderTest : public testing::Test {
+ public:
+ BuilderTest()
+ : loader_(new MockLoader),
+ builder_(loader_.get()),
+ settings_(&build_settings_, std::string()),
+ scope_(&settings_) {
+ build_settings_.SetBuildDir(SourceDir("//out/"));
+ settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
+ settings_.set_default_toolchain_label(settings_.toolchain_label());
+ }
+
+ Toolchain* DefineToolchain() {
+ Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
+ TestWithScope::SetupToolchain(tc);
+ builder_.ItemDefined(std::unique_ptr<Item>(tc));
+ return tc;
+ }
+
+ protected:
+ scoped_refptr<MockLoader> loader_;
+ Builder builder_;
+ BuildSettings build_settings_;
+ Settings settings_;
+ Scope scope_;
+};
+
+TEST_F(BuilderTest, BasicDeps) {
+ SourceDir toolchain_dir = settings_.toolchain_label().dir();
+ std::string toolchain_name = settings_.toolchain_label().name();
+
+ // Construct a dependency chain: A -> B -> C. Define A first with a
+ // forward-reference to B, then C, then B to test the different orders that
+ // the dependencies are hooked up.
+ Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
+ Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
+ Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
+
+ // The builder will take ownership of the pointers.
+ Target* a = new Target(&settings_, a_label);
+ a->public_deps().push_back(LabelTargetPair(b_label));
+ a->set_output_type(Target::EXECUTABLE);
+ builder_.ItemDefined(std::unique_ptr<Item>(a));
+
+ // Should have requested that B and the toolchain is loaded.
+ EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
+ SourceFile("//b/BUILD.gn")));
+
+ // Define the toolchain.
+ DefineToolchain();
+ BuilderRecord* toolchain_record =
+ builder_.GetRecord(settings_.toolchain_label());
+ ASSERT_TRUE(toolchain_record);
+ EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
+
+ // A should be unresolved with an item
+ BuilderRecord* a_record = builder_.GetRecord(a_label);
+ EXPECT_TRUE(a_record->item());
+ EXPECT_FALSE(a_record->resolved());
+ EXPECT_FALSE(a_record->can_resolve());
+
+ // B should be unresolved, have no item, and no deps.
+ BuilderRecord* b_record = builder_.GetRecord(b_label);
+ EXPECT_FALSE(b_record->item());
+ EXPECT_FALSE(b_record->resolved());
+ EXPECT_FALSE(b_record->can_resolve());
+ EXPECT_TRUE(b_record->all_deps().empty());
+
+ // A should have two deps: B and the toolchain. Only B should be unresolved.
+ EXPECT_EQ(2u, a_record->all_deps().size());
+ EXPECT_EQ(1u, a_record->unresolved_deps().size());
+ EXPECT_NE(a_record->all_deps().end(),
+ a_record->all_deps().find(toolchain_record));
+ EXPECT_NE(a_record->all_deps().end(), a_record->all_deps().find(b_record));
+ EXPECT_NE(a_record->unresolved_deps().end(),
+ a_record->unresolved_deps().find(b_record));
+
+ // B should be marked as having A waiting on it.
+ EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
+ EXPECT_NE(b_record->waiting_on_resolution().end(),
+ b_record->waiting_on_resolution().find(a_record));
+
+ // Add the C target.
+ Target* c = new Target(&settings_, c_label);
+ c->set_output_type(Target::STATIC_LIBRARY);
+ c->visibility().SetPublic();
+ builder_.ItemDefined(std::unique_ptr<Item>(c));
+
+ // C only depends on the already-loaded toolchain so we shouldn't have
+ // requested anything else.
+ EXPECT_TRUE(loader_->HasLoadedNone());
+
+ // Add the B target.
+ Target* b = new Target(&settings_, b_label);
+ a->public_deps().push_back(LabelTargetPair(c_label));
+ b->set_output_type(Target::SHARED_LIBRARY);
+ b->visibility().SetPublic();
+ builder_.ItemDefined(std::unique_ptr<Item>(b));
+
+ // B depends only on the already-loaded C and toolchain so we shouldn't have
+ // requested anything else.
+ EXPECT_TRUE(loader_->HasLoadedNone());
+
+ // All targets should now be resolved.
+ BuilderRecord* c_record = builder_.GetRecord(c_label);
+ EXPECT_TRUE(a_record->resolved());
+ EXPECT_TRUE(b_record->resolved());
+ EXPECT_TRUE(c_record->resolved());
+
+ EXPECT_TRUE(a_record->unresolved_deps().empty());
+ EXPECT_TRUE(b_record->unresolved_deps().empty());
+ EXPECT_TRUE(c_record->unresolved_deps().empty());
+
+ EXPECT_TRUE(a_record->waiting_on_resolution().empty());
+ EXPECT_TRUE(b_record->waiting_on_resolution().empty());
+ EXPECT_TRUE(c_record->waiting_on_resolution().empty());
+}
+
+// Tests that the "should generate" flag is set and propagated properly.
+TEST_F(BuilderTest, ShouldGenerate) {
+ DefineToolchain();
+
+ // Define a secondary toolchain.
+ Settings settings2(&build_settings_, "secondary/");
+ Label toolchain_label2(SourceDir("//tc/"), "secondary");
+ settings2.set_toolchain_label(toolchain_label2);
+ Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
+ TestWithScope::SetupToolchain(tc2);
+ builder_.ItemDefined(std::unique_ptr<Item>(tc2));
+
+ // Construct a dependency chain: A -> B. A is in the default toolchain, B
+ // is not.
+ Label a_label(SourceDir("//foo/"), "a", settings_.toolchain_label().dir(),
+ "a");
+ Label b_label(SourceDir("//foo/"), "b", toolchain_label2.dir(),
+ toolchain_label2.name());
+
+ // First define B.
+ Target* b = new Target(&settings2, b_label);
+ b->visibility().SetPublic();
+ b->set_output_type(Target::EXECUTABLE);
+ builder_.ItemDefined(std::unique_ptr<Item>(b));
+
+ // B should not be marked generated by default.
+ BuilderRecord* b_record = builder_.GetRecord(b_label);
+ EXPECT_FALSE(b_record->should_generate());
+
+ // Define A with a dependency on B.
+ Target* a = new Target(&settings_, a_label);
+ a->public_deps().push_back(LabelTargetPair(b_label));
+ a->set_output_type(Target::EXECUTABLE);
+ builder_.ItemDefined(std::unique_ptr<Item>(a));
+
+ // A should have the generate bit set since it's in the default toolchain.
+ BuilderRecord* a_record = builder_.GetRecord(a_label);
+ EXPECT_TRUE(a_record->should_generate());
+
+ // It should have gotten pushed to B.
+ EXPECT_TRUE(b_record->should_generate());
+}
+
+// Tests that configs applied to a config get loaded (bug 536844).
+TEST_F(BuilderTest, ConfigLoad) {
+ SourceDir toolchain_dir = settings_.toolchain_label().dir();
+ std::string toolchain_name = settings_.toolchain_label().name();
+
+ // Construct a dependency chain: A -> B -> C. Define A first with a
+ // forward-reference to B, then C, then B to test the different orders that
+ // the dependencies are hooked up.
+ Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
+ Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
+ Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
+
+ // The builder will take ownership of the pointers.
+ Config* a = new Config(&settings_, a_label);
+ a->configs().push_back(LabelConfigPair(b_label));
+ builder_.ItemDefined(std::unique_ptr<Item>(a));
+
+ // Should have requested that B is loaded.
+ EXPECT_TRUE(loader_->HasLoadedOne(SourceFile("//b/BUILD.gn")));
+}
+
+} // namespace gn_builder_unittest
diff --git a/gn/src/gn/bundle_data.cc b/gn/src/gn/bundle_data.cc
new file mode 100644
index 00000000000..6b7fe77f86d
--- /dev/null
+++ b/gn/src/gn/bundle_data.cc
@@ -0,0 +1,195 @@
+// Copyright 2016 The Chromium 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 "gn/bundle_data.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "gn/filesystem_utils.h"
+#include "gn/label_pattern.h"
+#include "gn/output_file.h"
+#include "gn/settings.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+
+namespace {
+
+// Return directory of |path| without the trailing directory separator.
+std::string_view FindDirNoTrailingSeparator(std::string_view path) {
+ std::string_view::size_type pos = path.find_last_of("/\\");
+ if (pos == std::string_view::npos)
+ return std::string_view();
+ return std::string_view(path.data(), pos);
+}
+
+bool IsSourceFileFromAssetsCatalog(std::string_view source,
+ SourceFile* asset_catalog) {
+ // Check whether |source| matches one of the following pattern:
+ // .*\.xcassets/Contents.json
+ // .*\.xcassets/[^/]*\.appiconset/[^/]*
+ // .*\.xcassets/[^/]*\.colorset/[^/]*
+ // .*\.xcassets/[^/]*\.dataset/[^/]*
+ // .*\.xcassets/[^/]*\.imageset/[^/]*
+ // .*\.xcassets/[^/]*\.launchimage/[^/]*
+ // .*\.xcassets/[^/]*\.symbolset/[^/]*
+ bool is_file_from_asset_catalog = false;
+ std::string_view dir = FindDirNoTrailingSeparator(source);
+ if (base::EndsWith(source, "/Contents.json", base::CompareCase::SENSITIVE) &&
+ base::EndsWith(dir, ".xcassets", base::CompareCase::SENSITIVE)) {
+ is_file_from_asset_catalog = true;
+ } else if (base::EndsWith(dir, ".appiconset", base::CompareCase::SENSITIVE) ||
+ base::EndsWith(dir, ".colorset", base::CompareCase::SENSITIVE) ||
+ base::EndsWith(dir, ".dataset", base::CompareCase::SENSITIVE) ||
+ base::EndsWith(dir, ".imageset", base::CompareCase::SENSITIVE) ||
+ base::EndsWith(dir, ".launchimage",
+ base::CompareCase::SENSITIVE) ||
+ base::EndsWith(dir, ".symbolset", base::CompareCase::SENSITIVE)) {
+ dir = FindDirNoTrailingSeparator(dir);
+ is_file_from_asset_catalog =
+ base::EndsWith(dir, ".xcassets", base::CompareCase::SENSITIVE);
+ }
+ if (is_file_from_asset_catalog && asset_catalog) {
+ std::string asset_catalog_path(dir);
+ *asset_catalog = SourceFile(std::move(asset_catalog_path));
+ }
+ return is_file_from_asset_catalog;
+}
+
+} // namespace
+
+BundleData::BundleData() = default;
+
+BundleData::~BundleData() = default;
+
+void BundleData::AddBundleData(const Target* target) {
+ DCHECK_EQ(target->output_type(), Target::BUNDLE_DATA);
+ for (const auto& pattern : bundle_deps_filter_) {
+ if (pattern.Matches(target->label()))
+ return;
+ }
+ bundle_deps_.push_back(target);
+}
+
+void BundleData::OnTargetResolved(Target* owning_target) {
+ // Only initialize file_rules_ and assets_catalog_sources for "create_bundle"
+ // target (properties are only used by those targets).
+ if (owning_target->output_type() != Target::CREATE_BUNDLE)
+ return;
+
+ UniqueVector<const Target*> assets_catalog_deps;
+ UniqueVector<SourceFile> assets_catalog_sources;
+
+ for (const Target* target : bundle_deps_) {
+ SourceFiles file_rule_sources;
+ for (const SourceFile& source_file : target->sources()) {
+ SourceFile assets_catalog;
+ if (IsSourceFileFromAssetsCatalog(source_file.value(), &assets_catalog)) {
+ assets_catalog_sources.push_back(assets_catalog);
+ assets_catalog_deps.push_back(target);
+ } else {
+ file_rule_sources.push_back(source_file);
+ }
+ }
+
+ if (!file_rule_sources.empty()) {
+ DCHECK_EQ(target->action_values().outputs().list().size(), 1u);
+ file_rules_.push_back(
+ BundleFileRule(target, file_rule_sources,
+ target->action_values().outputs().list()[0]));
+ }
+ }
+
+ assets_catalog_deps_.insert(assets_catalog_deps_.end(),
+ assets_catalog_deps.begin(),
+ assets_catalog_deps.end());
+ assets_catalog_sources_.insert(assets_catalog_sources_.end(),
+ assets_catalog_sources.begin(),
+ assets_catalog_sources.end());
+
+ GetSourceFiles(&owning_target->sources());
+}
+
+void BundleData::GetSourceFiles(SourceFiles* sources) const {
+ for (const BundleFileRule& file_rule : file_rules_) {
+ sources->insert(sources->end(), file_rule.sources().begin(),
+ file_rule.sources().end());
+ }
+ sources->insert(sources->end(), assets_catalog_sources_.begin(),
+ assets_catalog_sources_.end());
+ if (!code_signing_script_.is_null()) {
+ sources->insert(sources->end(), code_signing_sources_.begin(),
+ code_signing_sources_.end());
+ }
+}
+
+bool BundleData::GetOutputFiles(const Settings* settings,
+ const Target* target,
+ OutputFiles* outputs,
+ Err* err) const {
+ SourceFiles outputs_as_sources;
+ if (!GetOutputsAsSourceFiles(settings, target, &outputs_as_sources, err))
+ return false;
+ for (const SourceFile& source_file : outputs_as_sources)
+ outputs->push_back(OutputFile(settings->build_settings(), source_file));
+ return true;
+}
+
+bool BundleData::GetOutputsAsSourceFiles(const Settings* settings,
+ const Target* target,
+ SourceFiles* outputs_as_source,
+ Err* err) const {
+ for (const BundleFileRule& file_rule : file_rules_) {
+ for (const SourceFile& source : file_rule.sources()) {
+ SourceFile expanded_source_file;
+ if (!file_rule.ApplyPatternToSource(settings, target, *this, source,
+ &expanded_source_file, err))
+ return false;
+ outputs_as_source->push_back(expanded_source_file);
+ }
+ }
+
+ if (!assets_catalog_sources_.empty())
+ outputs_as_source->push_back(GetCompiledAssetCatalogPath());
+
+ if (!partial_info_plist_.is_null())
+ outputs_as_source->push_back(partial_info_plist_);
+
+ if (!code_signing_script_.is_null()) {
+ std::vector<SourceFile> code_signing_output_files;
+ SubstitutionWriter::GetListAsSourceFiles(code_signing_outputs_,
+ &code_signing_output_files);
+ outputs_as_source->insert(outputs_as_source->end(),
+ code_signing_output_files.begin(),
+ code_signing_output_files.end());
+ }
+
+ if (!root_dir_.is_null())
+ outputs_as_source->push_back(GetBundleRootDirOutput(settings));
+
+ return true;
+}
+
+SourceFile BundleData::GetCompiledAssetCatalogPath() const {
+ DCHECK(!assets_catalog_sources_.empty());
+ std::string assets_car_path = resources_dir_.value() + "/Assets.car";
+ return SourceFile(std::move(assets_car_path));
+}
+
+SourceFile BundleData::GetBundleRootDirOutput(const Settings* settings) const {
+ std::string root_dir_value = root_dir().value();
+ size_t last_separator = root_dir_value.rfind('/');
+ if (last_separator != std::string::npos)
+ root_dir_value = root_dir_value.substr(0, last_separator);
+
+ return SourceFile(std::move(root_dir_value));
+}
+
+SourceDir BundleData::GetBundleRootDirOutputAsDir(
+ const Settings* settings) const {
+ return SourceDir(GetBundleRootDirOutput(settings).value());
+}
+
+SourceDir BundleData::GetBundleDir(const Settings* settings) const{
+ return GetBundleRootDirOutput(settings).GetDir();
+}
diff --git a/gn/src/gn/bundle_data.h b/gn/src/gn/bundle_data.h
new file mode 100644
index 00000000000..3c4feb15e63
--- /dev/null
+++ b/gn/src/gn/bundle_data.h
@@ -0,0 +1,214 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_BUNDLE_DATA_H_
+#define TOOLS_GN_BUNDLE_DATA_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "gn/action_values.h"
+#include "gn/bundle_file_rule.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/substitution_list.h"
+#include "gn/unique_vector.h"
+
+class LabelPattern;
+class OutputFile;
+class Settings;
+class Target;
+
+// BundleData holds the information required by "create_bundle" target.
+class BundleData {
+ public:
+ using UniqueTargets = UniqueVector<const Target*>;
+ using SourceFiles = std::vector<SourceFile>;
+ using OutputFiles = std::vector<OutputFile>;
+ using BundleFileRules = std::vector<BundleFileRule>;
+
+ BundleData();
+ ~BundleData();
+
+ // Adds a bundle_data target to the recursive collection of all bundle_data
+ // that the target depends on.
+ void AddBundleData(const Target* target);
+
+ // Called upon resolution of the target owning this instance of BundleData.
+ // |owning_target| is the owning target.
+ void OnTargetResolved(Target* owning_target);
+
+ // Returns the list of inputs.
+ void GetSourceFiles(SourceFiles* sources) const;
+
+ // Returns the list of outputs.
+ bool GetOutputFiles(const Settings* settings,
+ const Target* target,
+ OutputFiles* outputs,
+ Err* err) const;
+
+ // Returns the list of outputs as SourceFile.
+ bool GetOutputsAsSourceFiles(const Settings* settings,
+ const Target* target,
+ SourceFiles* outputs_as_source,
+ Err* err) const;
+
+ // Returns the path to the compiled asset catalog. Only valid if
+ // assets_catalog_sources() is not empty.
+ SourceFile GetCompiledAssetCatalogPath() const;
+
+ // Returns the path to the top-level directory of the bundle. This is
+ // based on root_dir(), but since that can be Bundle.app/Contents/ or
+ // any other subpath, this is just the most top-level directory (e.g.,
+ // just Bundle.app/).
+ //
+ // Note that this is a SourceFile instead of a SourceDir. This is because
+ // the output of a create_bundle rule is a single logical unit, even though
+ // it is really a directory containing many outputs. This allows other
+ // targets to treat the bundle as a single unit, rather than a collection
+ // of its contents.
+ SourceFile GetBundleRootDirOutput(const Settings* settings) const;
+
+ // Performs GetBundleRootDirOutput but returns the result as a directory.
+ SourceDir GetBundleRootDirOutputAsDir(const Settings* settings) const;
+
+ // Returns directory where bundle is
+ SourceDir GetBundleDir(const Settings* settings) const;
+
+ // Returns the list of inputs for the compilation of the asset catalog.
+ SourceFiles& assets_catalog_sources() { return assets_catalog_sources_; }
+ const SourceFiles& assets_catalog_sources() const {
+ return assets_catalog_sources_;
+ }
+
+ // Returns the list of dependencies for the compilation of the asset catalog.
+ std::vector<const Target*> assets_catalog_deps() const {
+ return assets_catalog_deps_;
+ }
+
+ BundleFileRules& file_rules() { return file_rules_; }
+ const BundleFileRules& file_rules() const { return file_rules_; }
+
+ SourceDir& root_dir() { return root_dir_; }
+ const SourceDir& root_dir() const { return root_dir_; }
+
+ SourceDir& contents_dir() { return contents_dir_; }
+ const SourceDir& contents_dir() const { return contents_dir_; }
+
+ SourceDir& resources_dir() { return resources_dir_; }
+ const SourceDir& resources_dir() const { return resources_dir_; }
+
+ SourceDir& executable_dir() { return executable_dir_; }
+ const SourceDir& executable_dir() const { return executable_dir_; }
+
+ std::map<std::string, std::string>& xcode_extra_attributes() {
+ return xcode_extra_attributes_;
+ }
+ const std::map<std::string, std::string>& xcode_extra_attributes() const {
+ return xcode_extra_attributes_;
+ }
+
+ std::string& product_type() { return product_type_; }
+ const std::string& product_type() const { return product_type_; }
+
+ std::string& xcode_test_application_name() {
+ return xcode_test_application_name_;
+ }
+ const std::string& xcode_test_application_name() const {
+ return xcode_test_application_name_;
+ }
+
+ void set_partial_info_plist(const SourceFile& partial_info_plist) {
+ partial_info_plist_ = partial_info_plist;
+ }
+ const SourceFile& partial_info_plist() const { return partial_info_plist_; }
+
+ void set_code_signing_script(const SourceFile& script_file) {
+ code_signing_script_ = script_file;
+ }
+ const SourceFile& code_signing_script() const { return code_signing_script_; }
+
+ std::vector<SourceFile>& code_signing_sources() {
+ return code_signing_sources_;
+ }
+ const std::vector<SourceFile>& code_signing_sources() const {
+ return code_signing_sources_;
+ }
+
+ SubstitutionList& code_signing_outputs() { return code_signing_outputs_; }
+ const SubstitutionList& code_signing_outputs() const {
+ return code_signing_outputs_;
+ }
+
+ SubstitutionList& code_signing_args() { return code_signing_args_; }
+ const SubstitutionList& code_signing_args() const {
+ return code_signing_args_;
+ }
+
+ std::vector<LabelPattern>& bundle_deps_filter() {
+ return bundle_deps_filter_;
+ }
+ const std::vector<LabelPattern>& bundle_deps_filter() const {
+ return bundle_deps_filter_;
+ }
+
+ SubstitutionList& xcasset_compiler_flags() {
+ return xcasset_compiler_flags_;
+ }
+ const SubstitutionList& xcasset_compiler_flags() const {
+ return xcasset_compiler_flags_;
+ }
+
+ // Recursive collection of all bundle_data that the target depends on.
+ const UniqueTargets& bundle_deps() const { return bundle_deps_; }
+
+ // Returns whether the bundle is a framework bundle.
+ bool is_framework() const {
+ return product_type_ == "com.apple.product-type.framework";
+ }
+
+ private:
+ SourceFiles assets_catalog_sources_;
+ std::vector<const Target*> assets_catalog_deps_;
+ BundleFileRules file_rules_;
+ UniqueTargets bundle_deps_;
+ std::vector<LabelPattern> bundle_deps_filter_;
+
+ // All those values are subdirectories relative to root_build_dir, and apart
+ // from root_dir_, they are either equal to root_dir_ or subdirectories of it.
+ SourceDir root_dir_;
+ SourceDir contents_dir_;
+ SourceDir resources_dir_;
+ SourceDir executable_dir_;
+
+ // The specified attributes will append to the build settings of the generated
+ // Xcode target.
+ std::map<std::string, std::string> xcode_extra_attributes_;
+
+ // This is the target type as known to Xcode. This is only used to generate
+ // the Xcode project file when using --ide=xcode.
+ std::string product_type_;
+
+ // Each Xcode unit test or ui test target must have a test application target,
+ // and this value corresponds to the target name. This is only used to
+ // generate the Xcode project when using --ide=xcode.
+ std::string xcode_test_application_name_;
+
+ // Path to the partial Info.plist generated by the asset catalog compiler
+ // (corresponds to {{bundle_partial_info_plist}} expansion).
+ SourceFile partial_info_plist_;
+
+ // Holds the values (script name, sources, outputs, script arguments) for the
+ // code signing step if defined.
+ SourceFile code_signing_script_;
+ std::vector<SourceFile> code_signing_sources_;
+ SubstitutionList code_signing_outputs_;
+ SubstitutionList code_signing_args_;
+ SubstitutionList xcasset_compiler_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(BundleData);
+};
+
+#endif // TOOLS_GN_BUNDLE_DATA_H_
diff --git a/gn/src/gn/bundle_data_target_generator.cc b/gn/src/gn/bundle_data_target_generator.cc
new file mode 100644
index 00000000000..0246cff5a18
--- /dev/null
+++ b/gn/src/gn/bundle_data_target_generator.cc
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium 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 "gn/bundle_data_target_generator.h"
+
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/substitution_type.h"
+#include "gn/target.h"
+#include "gn/value.h"
+#include "gn/variables.h"
+
+BundleDataTargetGenerator::BundleDataTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err) {}
+
+BundleDataTargetGenerator::~BundleDataTargetGenerator() = default;
+
+void BundleDataTargetGenerator::DoRun() {
+ target_->set_output_type(Target::BUNDLE_DATA);
+
+ if (!FillSources())
+ return;
+ if (!FillOutputs())
+ return;
+
+ if (target_->sources().empty()) {
+ *err_ = Err(function_call_,
+ "Empty sources for bundle_data target."
+ "You have to specify at least one file in the \"sources\".");
+ return;
+ }
+ if (target_->action_values().outputs().list().size() != 1) {
+ *err_ = Err(
+ function_call_, "Target bundle_data must have exactly one output.",
+ "You must specify exactly one value in the \"output\" array for the"
+ "destination\ninto the generated bundle (see \"gn help bundle_data\"). "
+ "If there are multiple\nsources to copy, use source expansion (see "
+ "\"gn help source_expansion\").");
+ return;
+ }
+}
+
+bool BundleDataTargetGenerator::FillOutputs() {
+ const Value* value = scope_->GetValue(variables::kOutputs, true);
+ if (!value)
+ return true;
+
+ SubstitutionList& outputs = target_->action_values().outputs();
+ if (!outputs.Parse(*value, err_))
+ return false;
+
+ // Check the substitutions used are valid for this purpose.
+ for (const Substitution* type : outputs.required_types()) {
+ if (!IsValidBundleDataSubstitution(type)) {
+ *err_ = Err(value->origin(), "Invalid substitution type.",
+ "The substitution " + std::string(type->name) +
+ " isn't valid for something\n"
+ "operating on a bundle_data file such as this.");
+ return false;
+ }
+ }
+
+ // Validate that outputs are in the bundle.
+ CHECK(outputs.list().size() == value->list_value().size());
+ for (size_t i = 0; i < outputs.list().size(); i++) {
+ if (!EnsureSubstitutionIsInBundleDir(outputs.list()[i],
+ value->list_value()[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool BundleDataTargetGenerator::EnsureSubstitutionIsInBundleDir(
+ const SubstitutionPattern& pattern,
+ const Value& original_value) {
+ if (pattern.ranges().empty()) {
+ // Pattern is empty, error out (this prevents weirdness below).
+ *err_ = Err(original_value, "This has an empty value in it.");
+ return false;
+ }
+
+ if (SubstitutionIsInBundleDir(pattern.ranges()[0].type))
+ return true;
+
+ *err_ = Err(original_value, "File is not inside bundle directory.",
+ "The given file should be in the output directory. Normally you\n"
+ "would specify {{bundle_resources_dir}} or such substitution.");
+ return false;
+}
diff --git a/gn/src/gn/bundle_data_target_generator.h b/gn/src/gn/bundle_data_target_generator.h
new file mode 100644
index 00000000000..05ea820ce8b
--- /dev/null
+++ b/gn/src/gn/bundle_data_target_generator.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_BUNDLE_DATA_TARGET_GENERATOR_H_
+#define TOOLS_GN_BUNDLE_DATA_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target_generator.h"
+
+// Populates a Target with the values from a bundle_data rule.
+class BundleDataTargetGenerator : public TargetGenerator {
+ public:
+ BundleDataTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ ~BundleDataTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ bool FillOutputs();
+
+ bool EnsureSubstitutionIsInBundleDir(const SubstitutionPattern& pattern,
+ const Value& original_value);
+
+ DISALLOW_COPY_AND_ASSIGN(BundleDataTargetGenerator);
+};
+
+#endif // TOOLS_GN_BUNDLE_DATA_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/bundle_file_rule.cc b/gn/src/gn/bundle_file_rule.cc
new file mode 100644
index 00000000000..719e8aacb1c
--- /dev/null
+++ b/gn/src/gn/bundle_file_rule.cc
@@ -0,0 +1,110 @@
+// Copyright 2016 The Chromium 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 "gn/bundle_file_rule.h"
+
+#include "base/strings/stringprintf.h"
+#include "gn/output_file.h"
+#include "gn/settings.h"
+#include "gn/substitution_pattern.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/variables.h"
+
+namespace {
+
+Err ErrMissingPropertyForExpansion(const Settings* settings,
+ const Target* target,
+ const BundleFileRule* bundle_file_rule,
+ const char* property_name) {
+ std::string label = bundle_file_rule->target()->label().GetUserVisibleName(
+ settings->default_toolchain_label());
+
+ return Err(target->defined_from(),
+ base::StringPrintf("Property %s is required.", property_name),
+ base::StringPrintf(
+ "In order to expand {{%s}} in %s, the "
+ "property needs to be defined in the create_bundle target.",
+ property_name, label.c_str()));
+}
+
+} // namespace
+
+BundleFileRule::BundleFileRule(const Target* bundle_data_target,
+ const std::vector<SourceFile> sources,
+ const SubstitutionPattern& pattern)
+ : target_(bundle_data_target), sources_(sources), pattern_(pattern) {
+ // target_ may be null during testing.
+ DCHECK(!target_ || target_->output_type() == Target::BUNDLE_DATA);
+}
+
+BundleFileRule::BundleFileRule(const BundleFileRule& other) = default;
+
+BundleFileRule::~BundleFileRule() = default;
+
+bool BundleFileRule::ApplyPatternToSource(const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ SourceFile* expanded_source_file,
+ Err* err) const {
+ std::string output_path;
+ for (const auto& subrange : pattern_.ranges()) {
+ if (subrange.type == &SubstitutionLiteral) {
+ output_path.append(subrange.literal);
+ } else if (subrange.type == &SubstitutionBundleRootDir) {
+ if (bundle_data.root_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleRootDir);
+ return false;
+ }
+ output_path.append(bundle_data.root_dir().value());
+ } else if (subrange.type == &SubstitutionBundleContentsDir) {
+ if (bundle_data.contents_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleContentsDir);
+ return false;
+ }
+ output_path.append(bundle_data.contents_dir().value());
+ } else if (subrange.type == &SubstitutionBundleResourcesDir) {
+ if (bundle_data.resources_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleResourcesDir);
+ return false;
+ }
+ output_path.append(bundle_data.resources_dir().value());
+ } else if (subrange.type == &SubstitutionBundleExecutableDir) {
+ if (bundle_data.executable_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleExecutableDir);
+ return false;
+ }
+ output_path.append(bundle_data.executable_dir().value());
+ } else {
+ output_path.append(SubstitutionWriter::GetSourceSubstitution(
+ target_, target_->settings(), source_file, subrange.type,
+ SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir()));
+ }
+ }
+ *expanded_source_file = SourceFile(std::move(output_path));
+ return true;
+}
+
+bool BundleFileRule::ApplyPatternToSourceAsOutputFile(
+ const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ OutputFile* expanded_output_file,
+ Err* err) const {
+ SourceFile expanded_source_file;
+ if (!ApplyPatternToSource(settings, target, bundle_data, source_file,
+ &expanded_source_file, err)) {
+ return false;
+ }
+
+ *expanded_output_file =
+ OutputFile(settings->build_settings(), expanded_source_file);
+ return true;
+}
diff --git a/gn/src/gn/bundle_file_rule.h b/gn/src/gn/bundle_file_rule.h
new file mode 100644
index 00000000000..acc824a243b
--- /dev/null
+++ b/gn/src/gn/bundle_file_rule.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_BUNDLE_FILE_RULE_H_
+#define TOOLS_GN_BUNDLE_FILE_RULE_H_
+
+#include <vector>
+
+#include "gn/source_file.h"
+#include "gn/substitution_pattern.h"
+
+class BundleData;
+class Settings;
+class SourceFile;
+class Target;
+class OutputFile;
+
+// BundleFileRule contains the information found in a "bundle_data" target.
+class BundleFileRule {
+ public:
+ BundleFileRule(const Target* bundle_data_target,
+ const std::vector<SourceFile> sources,
+ const SubstitutionPattern& pattern);
+ BundleFileRule(const BundleFileRule& other);
+ ~BundleFileRule();
+
+ // Applies the substitution pattern to a source file, returning the result
+ // as either a SourceFile or an OutputFile.
+ bool ApplyPatternToSource(const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ SourceFile* expanded_source_file,
+ Err* err) const;
+ bool ApplyPatternToSourceAsOutputFile(const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ OutputFile* expanded_output_file,
+ Err* err) const;
+
+ // Returns the associated target (of type Target::BUNDLE_DATA). May be
+ // null during testing.
+ const Target* target() const { return target_; }
+
+ // Returns the list of SourceFiles.
+ const std::vector<SourceFile>& sources() const { return sources_; }
+
+ private:
+ const Target* target_;
+ std::vector<SourceFile> sources_;
+ SubstitutionPattern pattern_;
+};
+
+#endif // TOOLS_GN_BUNDLE_FILE_RULE_H_
diff --git a/gn/src/gn/c_include_iterator.cc b/gn/src/gn/c_include_iterator.cc
new file mode 100644
index 00000000000..f8626e17ab3
--- /dev/null
+++ b/gn/src/gn/c_include_iterator.cc
@@ -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.
+
+#include "gn/c_include_iterator.h"
+
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "gn/input_file.h"
+#include "gn/location.h"
+
+namespace {
+
+enum IncludeType {
+ INCLUDE_NONE,
+ INCLUDE_SYSTEM, // #include <...>
+ INCLUDE_USER // #include "..."
+};
+
+// Returns a new string piece referencing the same buffer as the argument, but
+// with leading space trimmed. This only checks for space and tab characters
+// since we're dealing with lines in C source files.
+std::string_view TrimLeadingWhitespace(const std::string_view& str) {
+ size_t new_begin = 0;
+ while (new_begin < str.size() &&
+ (str[new_begin] == ' ' || str[new_begin] == '\t'))
+ new_begin++;
+ return str.substr(new_begin);
+}
+
+// We don't want to count comment lines and preprocessor lines toward our
+// "max lines to look at before giving up" since the beginnings of some files
+// may have a lot of comments.
+//
+// We only handle C-style "//" comments since this is the normal commenting
+// style used in Chrome, and do so pretty stupidly. We don't want to write a
+// full C++ parser here, we're just trying to get a good heuristic for checking
+// the file.
+//
+// We assume the line has leading whitespace trimmed. We also assume that empty
+// lines have already been filtered out.
+bool ShouldCountTowardNonIncludeLines(const std::string_view& line) {
+ if (base::StartsWith(line, "//", base::CompareCase::SENSITIVE))
+ return false; // Don't count comments.
+ if (base::StartsWith(line, "/*", base::CompareCase::SENSITIVE) ||
+ base::StartsWith(line, " *", base::CompareCase::SENSITIVE))
+ return false; // C-style comment blocks with stars along the left side.
+ if (base::StartsWith(line, "#", base::CompareCase::SENSITIVE))
+ return false; // Don't count preprocessor.
+ if (base::ContainsOnlyChars(line, base::kWhitespaceASCII))
+ return false; // Don't count whitespace lines.
+ return true; // Count everything else.
+}
+
+// Given a line, checks to see if it looks like an include or import and
+// extract the path. The type of include is returned. Returns INCLUDE_NONE on
+// error or if this is not an include line.
+//
+// The 1-based character number on the line that the include was found at
+// will be filled into *begin_char.
+IncludeType ExtractInclude(const std::string_view& line,
+ std::string_view* path,
+ int* begin_char) {
+ static const char kInclude[] = "include";
+ static const size_t kIncludeLen = std::size(kInclude) - 1; // No null.
+ static const char kImport[] = "import";
+ static const size_t kImportLen = std::size(kImport) - 1; // No null.
+
+ std::string_view trimmed = TrimLeadingWhitespace(line);
+ if (trimmed.empty())
+ return INCLUDE_NONE;
+
+ if (trimmed[0] != '#')
+ return INCLUDE_NONE;
+
+ trimmed = TrimLeadingWhitespace(trimmed.substr(1));
+
+ std::string_view contents;
+ if (base::StartsWith(trimmed, std::string_view(kInclude, kIncludeLen),
+ base::CompareCase::SENSITIVE))
+ contents = TrimLeadingWhitespace(trimmed.substr(kIncludeLen));
+ else if (base::StartsWith(trimmed, std::string_view(kImport, kImportLen),
+ base::CompareCase::SENSITIVE))
+ contents = TrimLeadingWhitespace(trimmed.substr(kImportLen));
+
+ if (contents.empty())
+ return INCLUDE_NONE;
+
+ IncludeType type = INCLUDE_NONE;
+ char terminating_char = 0;
+ if (contents[0] == '"') {
+ type = INCLUDE_USER;
+ terminating_char = '"';
+ } else if (contents[0] == '<') {
+ type = INCLUDE_SYSTEM;
+ terminating_char = '>';
+ } else {
+ return INCLUDE_NONE;
+ }
+
+ // Count everything to next "/> as the contents.
+ size_t terminator_index = contents.find(terminating_char, 1);
+ if (terminator_index == std::string_view::npos)
+ return INCLUDE_NONE;
+
+ *path = contents.substr(1, terminator_index - 1);
+ // Note: one based so we do "+ 1".
+ *begin_char = static_cast<int>(path->data() - line.data()) + 1;
+ return type;
+}
+
+// Returns true if this line has a "nogncheck" comment associated with it.
+bool HasNoCheckAnnotation(const std::string_view& line) {
+ return line.find("nogncheck") != std::string_view::npos;
+}
+
+} // namespace
+
+const int CIncludeIterator::kMaxNonIncludeLines = 10;
+
+CIncludeIterator::CIncludeIterator(const InputFile* input)
+ : input_file_(input), file_(input->contents()) {}
+
+CIncludeIterator::~CIncludeIterator() = default;
+
+bool CIncludeIterator::GetNextIncludeString(
+ IncludeStringWithLocation* include) {
+ std::string_view line;
+ int cur_line_number = 0;
+ while (lines_since_last_include_ <= kMaxNonIncludeLines &&
+ GetNextLine(&line, &cur_line_number)) {
+ std::string_view include_contents;
+ int begin_char;
+ IncludeType type = ExtractInclude(line, &include_contents, &begin_char);
+ if (HasNoCheckAnnotation(line))
+ continue;
+ if (type != INCLUDE_NONE) {
+ include->contents = include_contents;
+ include->location = LocationRange(
+ Location(input_file_, cur_line_number, begin_char,
+ -1 /* TODO(scottmg): Is this important? */),
+ Location(input_file_, cur_line_number,
+ begin_char + static_cast<int>(include_contents.size()),
+ -1 /* TODO(scottmg): Is this important? */));
+ include->system_style_include = (type == INCLUDE_SYSTEM);
+
+ lines_since_last_include_ = 0;
+ return true;
+ }
+
+ if (ShouldCountTowardNonIncludeLines(line))
+ lines_since_last_include_++;
+ }
+ return false;
+}
+
+bool CIncludeIterator::GetNextLine(std::string_view* line, int* line_number) {
+ if (offset_ == file_.size())
+ return false;
+
+ size_t begin = offset_;
+ while (offset_ < file_.size() && file_[offset_] != '\n')
+ offset_++;
+ line_number_++;
+
+ *line = file_.substr(begin, offset_ - begin);
+ *line_number = line_number_;
+
+ // If we didn't hit EOF, skip past the newline for the next one.
+ if (offset_ < file_.size())
+ offset_++;
+ return true;
+}
diff --git a/gn/src/gn/c_include_iterator.h b/gn/src/gn/c_include_iterator.h
new file mode 100644
index 00000000000..325d57e9d15
--- /dev/null
+++ b/gn/src/gn/c_include_iterator.h
@@ -0,0 +1,61 @@
+// 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 TOOLS_GN_C_INCLUDE_ITERATOR_H_
+#define TOOLS_GN_C_INCLUDE_ITERATOR_H_
+
+#include <stddef.h>
+
+#include <string_view>
+
+#include "base/macros.h"
+#include "gn/location.h"
+
+class InputFile;
+
+struct IncludeStringWithLocation {
+ std::string_view contents;
+ LocationRange location;
+ bool system_style_include = false;
+};
+
+// Iterates through #includes in C source and header files.
+class CIncludeIterator {
+ public:
+ // The InputFile pointed to must outlive this class.
+ explicit CIncludeIterator(const InputFile* input);
+ ~CIncludeIterator();
+
+ // Fills in the string with the contents of the next include, and the
+ // location with where it came from, and returns true, or returns false if
+ // there are no more includes.
+ bool GetNextIncludeString(IncludeStringWithLocation* include);
+
+ // Maximum numbef of non-includes we'll tolerate before giving up. This does
+ // not count comments or preprocessor.
+ static const int kMaxNonIncludeLines;
+
+ private:
+ // Returns false on EOF, otherwise fills in the given line and the one-based
+ // line number into *line_number;
+ bool GetNextLine(std::string_view* line, int* line_number);
+
+ const InputFile* input_file_;
+
+ // This just points into input_file_.contents() for convenience.
+ std::string_view file_;
+
+ // 0-based offset into the file.
+ size_t offset_ = 0;
+
+ int line_number_ = 0; // One-based. Indicates the last line we read.
+
+ // Number of lines we've processed since seeing the last include (or the
+ // beginning of the file) with some exceptions.
+ int lines_since_last_include_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(CIncludeIterator);
+};
+
+#endif // TOOLS_GN_C_INCLUDE_ITERATOR_H_
diff --git a/gn/src/gn/c_include_iterator_unittest.cc b/gn/src/gn/c_include_iterator_unittest.cc
new file mode 100644
index 00000000000..b162cedadbc
--- /dev/null
+++ b/gn/src/gn/c_include_iterator_unittest.cc
@@ -0,0 +1,181 @@
+// 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 <stddef.h>
+
+#include "gn/c_include_iterator.h"
+#include "gn/input_file.h"
+#include "gn/location.h"
+#include "util/test/test.h"
+
+namespace {
+
+bool RangeIs(const LocationRange& range,
+ int line,
+ int begin_char,
+ int end_char) {
+ return range.begin().line_number() == line &&
+ range.end().line_number() == line &&
+ range.begin().column_number() == begin_char &&
+ range.end().column_number() == end_char;
+}
+
+} // namespace
+
+TEST(CIncludeIterator, Basic) {
+ std::string buffer;
+ buffer.append("// Some comment\n");
+ buffer.append("\n");
+ buffer.append("#include \"foo/bar.h\"\n");
+ buffer.append("\n");
+ buffer.append("#include <stdio.h>\n");
+ buffer.append("\n");
+ buffer.append(" #include \"foo/baz.h\"\n"); // Leading whitespace
+ buffer.append("#include \"la/deda.h\"\n");
+ // Line annotated with "// nogncheck"
+ buffer.append("#include \"should_be_skipped.h\" // nogncheck\n");
+ buffer.append("#import \"weird_mac_import.h\"\n");
+ buffer.append("\n");
+ buffer.append("void SomeCode() {\n");
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ CIncludeIterator iter(&file);
+
+ IncludeStringWithLocation include;
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("foo/bar.h", include.contents);
+ EXPECT_TRUE(RangeIs(include.location, 3, 11, 20)) << include.location.begin().Describe(true);
+ EXPECT_FALSE(include.system_style_include);
+
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("stdio.h", include.contents);
+ EXPECT_TRUE(RangeIs(include.location, 5, 11, 18)) << include.location.begin().Describe(true);
+ EXPECT_TRUE(include.system_style_include);
+
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("foo/baz.h", include.contents);
+ EXPECT_TRUE(RangeIs(include.location, 7, 12, 21)) << include.location.begin().Describe(true);
+ EXPECT_FALSE(include.system_style_include);
+
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("la/deda.h", include.contents);
+ EXPECT_TRUE(RangeIs(include.location, 8, 11, 20)) << include.location.begin().Describe(true);
+ EXPECT_FALSE(include.system_style_include);
+
+ // The line annotated with "nogncheck" should be skipped.
+
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("weird_mac_import.h", include.contents);
+ EXPECT_TRUE(RangeIs(include.location, 10, 10, 28)) << include.location.begin().Describe(true);
+ EXPECT_FALSE(include.system_style_include);
+
+ EXPECT_FALSE(iter.GetNextIncludeString(&include));
+}
+
+// Tests that we don't search for includes indefinitely.
+TEST(CIncludeIterator, GiveUp) {
+ std::string buffer;
+ for (size_t i = 0; i < 1000; i++)
+ buffer.append("x\n");
+ buffer.append("#include \"foo/bar.h\"\n");
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ IncludeStringWithLocation include;
+
+ CIncludeIterator iter(&file);
+ EXPECT_FALSE(iter.GetNextIncludeString(&include));
+ EXPECT_TRUE(include.contents.empty());
+}
+
+// Don't count blank lines, comments, and preprocessor when giving up.
+TEST(CIncludeIterator, DontGiveUp) {
+ std::string buffer;
+ for (size_t i = 0; i < 1000; i++)
+ buffer.push_back('\n');
+ for (size_t i = 0; i < 1000; i++)
+ buffer.append("// comment\n");
+ for (size_t i = 0; i < 1000; i++)
+ buffer.append("#preproc\n");
+ buffer.append("#include \"foo/bar.h\"\n");
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ IncludeStringWithLocation include;
+
+ CIncludeIterator iter(&file);
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("foo/bar.h", include.contents);
+}
+
+// Tests that we'll tolerate some small numbers of non-includes interspersed
+// with real includes.
+TEST(CIncludeIterator, TolerateNonIncludes) {
+ const size_t kSkip = CIncludeIterator::kMaxNonIncludeLines - 2;
+ const size_t kGroupCount = 100;
+
+ std::string include_str("foo/bar.h");
+
+ // Allow a series of includes with blanks in between.
+ std::string buffer;
+ for (size_t group = 0; group < kGroupCount; group++) {
+ for (size_t i = 0; i < kSkip; i++)
+ buffer.append("foo\n");
+ buffer.append("#include \"" + include_str + "\"\n");
+ }
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ IncludeStringWithLocation include;
+
+ CIncludeIterator iter(&file);
+ for (size_t group = 0; group < kGroupCount; group++) {
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ(include_str, std::string(include.contents));
+ }
+ EXPECT_FALSE(iter.GetNextIncludeString(&include));
+}
+
+// Tests that comments of the form
+// /*
+// *
+// */
+// are not counted toward the non-include line count.
+TEST(CIncludeIterator, CStyleComments) {
+ std::string buffer("/*");
+ for (size_t i = 0; i < 1000; i++)
+ buffer.append(" *\n");
+ buffer.append(" */\n\n");
+ buffer.append("#include \"foo/bar.h\"\n");
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ IncludeStringWithLocation include;
+
+ CIncludeIterator iter(&file);
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("foo/bar.h", include.contents);
+}
+
+// Tests that spaces between the hash and directive are ignored.
+TEST(CIncludeIterator, SpacesAfterHash) {
+ std::string buffer("# include \"foo/bar.h\"\n");
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ IncludeStringWithLocation include;
+
+ CIncludeIterator iter(&file);
+ EXPECT_TRUE(iter.GetNextIncludeString(&include));
+ EXPECT_EQ("foo/bar.h", include.contents);
+
+ EXPECT_FALSE(iter.GetNextIncludeString(&include));
+}
diff --git a/gn/src/gn/c_substitution_type.cc b/gn/src/gn/c_substitution_type.cc
new file mode 100644
index 00000000000..ea47ce9da9c
--- /dev/null
+++ b/gn/src/gn/c_substitution_type.cc
@@ -0,0 +1,140 @@
+// Copyright 2019 The Chromium 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 "gn/c_substitution_type.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "gn/err.h"
+
+const SubstitutionTypes CSubstitutions = {
+ &CSubstitutionAsmFlags,
+ &CSubstitutionCFlags,
+ &CSubstitutionCFlagsC,
+ &CSubstitutionCFlagsCc,
+ &CSubstitutionCFlagsObjC,
+ &CSubstitutionCFlagsObjCc,
+ &CSubstitutionDefines,
+ &CSubstitutionFrameworkDirs,
+ &CSubstitutionIncludeDirs,
+ &CSubstitutionModuleDeps,
+ &CSubstitutionModuleDepsNoSelf,
+ &CSubstitutionSwiftModules,
+
+ &CSubstitutionLinkerInputs,
+ &CSubstitutionLinkerInputsNewline,
+ &CSubstitutionLdFlags,
+ &CSubstitutionLibs,
+ &CSubstitutionSoLibs,
+ &CSubstitutionFrameworks,
+ &CSubstitutionRlibs,
+
+ &CSubstitutionArFlags,
+
+ &CSubstitutionSwiftModuleName,
+ &CSubstitutionSwiftBridgeHeader,
+ &CSubstitutionSwiftModuleDirs,
+ &CSubstitutionSwiftFlags,
+};
+
+// Valid for compiler tools.
+const Substitution CSubstitutionAsmFlags = {"{{asmflags}}", "asmflags"};
+const Substitution CSubstitutionCFlags = {"{{cflags}}", "cflags"};
+const Substitution CSubstitutionCFlagsC = {"{{cflags_c}}", "cflags_c"};
+const Substitution CSubstitutionCFlagsCc = {"{{cflags_cc}}", "cflags_cc"};
+const Substitution CSubstitutionCFlagsObjC = {"{{cflags_objc}}", "cflags_objc"};
+const Substitution CSubstitutionCFlagsObjCc = {"{{cflags_objcc}}",
+ "cflags_objcc"};
+const Substitution CSubstitutionDefines = {"{{defines}}", "defines"};
+const Substitution CSubstitutionFrameworkDirs = {"{{framework_dirs}}",
+ "framework_dirs"};
+const Substitution CSubstitutionIncludeDirs = {"{{include_dirs}}",
+ "include_dirs"};
+const Substitution CSubstitutionModuleDeps = {"{{module_deps}}", "module_deps"};
+const Substitution CSubstitutionModuleDepsNoSelf = {"{{module_deps_no_self}}",
+ "module_deps_no_self"};
+
+// Valid for linker tools.
+const Substitution CSubstitutionLinkerInputs = {"{{inputs}}", "in"};
+const Substitution CSubstitutionLinkerInputsNewline = {"{{inputs_newline}}",
+ "in_newline"};
+const Substitution CSubstitutionLdFlags = {"{{ldflags}}", "ldflags"};
+const Substitution CSubstitutionLibs = {"{{libs}}", "libs"};
+const Substitution CSubstitutionSoLibs = {"{{solibs}}", "solibs"};
+const Substitution CSubstitutionRlibs = {"{{rlibs}}", "rlibs"};
+const Substitution CSubstitutionFrameworks = {"{{frameworks}}", "frameworks"};
+const Substitution CSubstitutionSwiftModules = {"{{swiftmodules}}",
+ "swiftmodules"};
+
+// Valid for alink only.
+const Substitution CSubstitutionArFlags = {"{{arflags}}", "arflags"};
+
+// Valid for swift only.
+const Substitution CSubstitutionSwiftModuleName = {"{{module_name}}",
+ "module_name"};
+const Substitution CSubstitutionSwiftBridgeHeader = {"{{bridge_header}}",
+ "bridge_header"};
+const Substitution CSubstitutionSwiftModuleDirs = {"{{module_dirs}}",
+ "module_dirs"};
+const Substitution CSubstitutionSwiftFlags = {"{{swiftflags}}", "swiftflags"};
+
+bool IsValidCompilerSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
+ type == &SubstitutionSource || type == &CSubstitutionAsmFlags ||
+ type == &CSubstitutionCFlags || type == &CSubstitutionCFlagsC ||
+ type == &CSubstitutionCFlagsCc || type == &CSubstitutionCFlagsObjC ||
+ type == &CSubstitutionCFlagsObjCc || type == &CSubstitutionDefines ||
+ type == &CSubstitutionFrameworkDirs ||
+ type == &CSubstitutionIncludeDirs ||
+ type == &CSubstitutionModuleDeps ||
+ type == &CSubstitutionModuleDepsNoSelf;
+}
+
+bool IsValidCompilerOutputsSubstitution(const Substitution* type) {
+ // All tool types except "output" (which would be infinitely recursive).
+ return (IsValidToolSubstitution(type) && type != &SubstitutionOutput) ||
+ IsValidSourceSubstitution(type);
+}
+
+bool IsValidSwiftCompilerSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) ||
+ type == &CSubstitutionSwiftModuleName ||
+ type == &CSubstitutionLinkerInputs ||
+ type == &CSubstitutionIncludeDirs ||
+ type == &CSubstitutionSwiftBridgeHeader ||
+ type == &CSubstitutionSwiftModuleDirs ||
+ type == &CSubstitutionSwiftFlags || type == &CSubstitutionDefines;
+}
+
+bool IsValidSwiftCompilerOutputsSubstitution(const Substitution* type) {
+ return (IsValidSwiftCompilerSubstitution(type) &&
+ type != &SubstitutionOutput) ||
+ IsValidSourceSubstitution(type);
+}
+
+bool IsValidLinkerSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &SubstitutionOutputDir ||
+ type == &SubstitutionOutputExtension ||
+ type == &CSubstitutionLinkerInputs ||
+ type == &CSubstitutionLinkerInputsNewline ||
+ type == &CSubstitutionLdFlags || type == &CSubstitutionLibs ||
+ type == &CSubstitutionSoLibs || type == &CSubstitutionFrameworks ||
+ type == &CSubstitutionRlibs || type == &CSubstitutionSwiftModules;
+}
+
+bool IsValidLinkerOutputsSubstitution(const Substitution* type) {
+ // All valid compiler outputs plus the output extension.
+ return IsValidCompilerOutputsSubstitution(type) ||
+ type == &SubstitutionOutputDir || type == &SubstitutionOutputExtension;
+}
+
+bool IsValidALinkSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) ||
+ type == &SubstitutionOutputDir ||
+ type == &SubstitutionOutputExtension ||
+ type == &CSubstitutionLinkerInputs ||
+ type == &CSubstitutionLinkerInputsNewline ||
+ type == &CSubstitutionArFlags;
+}
diff --git a/gn/src/gn/c_substitution_type.h b/gn/src/gn/c_substitution_type.h
new file mode 100644
index 00000000000..1f4e90e06a3
--- /dev/null
+++ b/gn/src/gn/c_substitution_type.h
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_C_SUBSTITUTION_TYPE_H_
+#define TOOLS_GN_C_SUBSTITUTION_TYPE_H_
+
+#include <set>
+#include <vector>
+
+#include "gn/substitution_type.h"
+
+// The set of substitutions available to all tools.
+extern const SubstitutionTypes CSubstitutions;
+
+// Valid for compiler tools.
+extern const Substitution CSubstitutionAsmFlags;
+extern const Substitution CSubstitutionCFlags;
+extern const Substitution CSubstitutionCFlagsC;
+extern const Substitution CSubstitutionCFlagsCc;
+extern const Substitution CSubstitutionCFlagsObjC;
+extern const Substitution CSubstitutionCFlagsObjCc;
+extern const Substitution CSubstitutionDefines;
+extern const Substitution CSubstitutionFrameworkDirs;
+extern const Substitution CSubstitutionIncludeDirs;
+extern const Substitution CSubstitutionModuleDeps;
+extern const Substitution CSubstitutionModuleDepsNoSelf;
+
+// Valid for linker tools.
+extern const Substitution CSubstitutionLinkerInputs;
+extern const Substitution CSubstitutionLinkerInputsNewline;
+extern const Substitution CSubstitutionLdFlags;
+extern const Substitution CSubstitutionLibs;
+extern const Substitution CSubstitutionSoLibs;
+extern const Substitution CSubstitutionFrameworks;
+extern const Substitution CSubstitutionRlibs;
+extern const Substitution CSubstitutionSwiftModules;
+
+// Valid for alink only.
+extern const Substitution CSubstitutionArFlags;
+
+// Valid for swift only.
+extern const Substitution CSubstitutionSwiftModuleName;
+extern const Substitution CSubstitutionSwiftBridgeHeader;
+extern const Substitution CSubstitutionSwiftModuleDirs;
+extern const Substitution CSubstitutionSwiftFlags;
+
+// Both compiler and linker tools.
+bool IsValidCompilerSubstitution(const Substitution* type);
+bool IsValidCompilerOutputsSubstitution(const Substitution* type);
+bool IsValidSwiftCompilerSubstitution(const Substitution* type);
+bool IsValidSwiftCompilerOutputsSubstitution(const Substitution* type);
+bool IsValidLinkerSubstitution(const Substitution* type);
+bool IsValidLinkerOutputsSubstitution(const Substitution* type);
+bool IsValidALinkSubstitution(const Substitution* type);
+
+#endif // TOOLS_GN_C_SUBSTITUTION_TYPE_H_
diff --git a/gn/src/gn/c_tool.cc b/gn/src/gn/c_tool.cc
new file mode 100644
index 00000000000..767b3241a11
--- /dev/null
+++ b/gn/src/gn/c_tool.cc
@@ -0,0 +1,266 @@
+// Copyright 2019 The Chromium 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 "gn/c_tool.h"
+
+#include "base/strings/stringprintf.h"
+#include "gn/c_substitution_type.h"
+#include "gn/target.h"
+
+const char* CTool::kCToolCc = "cc";
+const char* CTool::kCToolCxx = "cxx";
+const char* CTool::kCToolCxxModule = "cxx_module";
+const char* CTool::kCToolObjC = "objc";
+const char* CTool::kCToolObjCxx = "objcxx";
+const char* CTool::kCToolRc = "rc";
+const char* CTool::kCToolAsm = "asm";
+const char* CTool::kCToolSwift = "swift";
+const char* CTool::kCToolAlink = "alink";
+const char* CTool::kCToolSolink = "solink";
+const char* CTool::kCToolSolinkModule = "solink_module";
+const char* CTool::kCToolLink = "link";
+
+CTool::CTool(const char* n)
+ : Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) {
+ CHECK(ValidateName(n));
+ set_framework_switch("-framework ");
+ set_weak_framework_switch("-weak_framework ");
+ set_framework_dir_switch("-F");
+ set_lib_dir_switch("-L");
+ set_lib_switch("-l");
+ set_linker_arg("");
+}
+
+CTool::~CTool() = default;
+
+CTool* CTool::AsC() {
+ return this;
+}
+const CTool* CTool::AsC() const {
+ return this;
+}
+
+bool CTool::ValidateName(const char* name) const {
+ return name == kCToolCc || name == kCToolCxx || name == kCToolCxxModule ||
+ name == kCToolObjC || name == kCToolObjCxx || name == kCToolRc ||
+ name == kCToolSwift || name == kCToolAsm || name == kCToolAlink ||
+ name == kCToolSolink || name == kCToolSolinkModule ||
+ name == kCToolLink;
+}
+
+void CTool::SetComplete() {
+ SetToolComplete();
+ link_output_.FillRequiredTypes(&substitution_bits_);
+ depend_output_.FillRequiredTypes(&substitution_bits_);
+}
+
+bool CTool::ValidateRuntimeOutputs(Err* err) {
+ if (runtime_outputs().list().empty())
+ return true; // Empty is always OK.
+
+ if (name_ != kCToolSolink && name_ != kCToolSolinkModule &&
+ name_ != kCToolLink) {
+ *err = Err(defined_from(), "This tool specifies runtime_outputs.",
+ "This is only valid for linker tools (alink doesn't count).");
+ return false;
+ }
+
+ for (const SubstitutionPattern& pattern : runtime_outputs().list()) {
+ if (!IsPatternInOutputList(outputs(), pattern)) {
+ *err = Err(defined_from(), "This tool's runtime_outputs is bad.",
+ "It must be a subset of the outputs. The bad one is:\n " +
+ pattern.AsString());
+ return false;
+ }
+ }
+ return true;
+}
+
+// Validates either link_output or depend_output. To generalize to either, pass
+// the associated pattern, and the variable name that should appear in error
+// messages.
+bool CTool::ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
+ const char* variable_name,
+ Err* err) {
+ if (pattern.empty())
+ return true; // Empty is always OK.
+
+ // It should only be specified for certain tool types.
+ if (name_ != kCToolSolink && name_ != kCToolSolinkModule) {
+ *err = Err(defined_from(),
+ "This tool specifies a " + std::string(variable_name) + ".",
+ "This is only valid for solink and solink_module tools.");
+ return false;
+ }
+
+ if (!IsPatternInOutputList(outputs(), pattern)) {
+ *err = Err(defined_from(), "This tool's link_output is bad.",
+ "It must match one of the outputs.");
+ return false;
+ }
+
+ return true;
+}
+
+bool CTool::ReadPrecompiledHeaderType(Scope* scope, Err* err) {
+ const Value* value = scope->GetValue("precompiled_header_type", true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Accept empty string, do nothing (default is "no PCH").
+
+ if (value->string_value() == "gcc") {
+ set_precompiled_header_type(PCH_GCC);
+ return true;
+ } else if (value->string_value() == "msvc") {
+ set_precompiled_header_type(PCH_MSVC);
+ return true;
+ }
+ *err = Err(*value, "Invalid precompiled_header_type",
+ "Must either be empty, \"gcc\", or \"msvc\".");
+ return false;
+}
+
+bool CTool::ReadDepsFormat(Scope* scope, Err* err) {
+ const Value* value = scope->GetValue("depsformat", true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ if (value->string_value() == "gcc") {
+ set_depsformat(DEPS_GCC);
+ } else if (value->string_value() == "msvc") {
+ set_depsformat(DEPS_MSVC);
+ } else {
+ *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
+ return false;
+ }
+ return true;
+}
+
+bool CTool::ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ bool required,
+ SubstitutionList* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ SubstitutionList list;
+ if (!list.Parse(*value, err))
+ return false;
+
+ // Validate the right kinds of patterns are used.
+ if (list.list().empty() && required) {
+ *err =
+ Err(defined_from(),
+ base::StringPrintf("\"%s\" must be specified for this tool.", var));
+ return false;
+ }
+
+ for (const auto& cur_type : list.required_types()) {
+ if (!ValidateOutputSubstitution(cur_type)) {
+ *err = Err(*value, "Pattern not valid here.",
+ "You used the pattern " + std::string(cur_type->name) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+
+ *field = std::move(list);
+ return true;
+}
+
+bool CTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ if (!Tool::InitTool(scope, toolchain, err)) {
+ return false;
+ }
+
+ // All C tools should have outputs.
+ if (!ReadOutputsPatternList(scope, "outputs", /*required=*/true, &outputs_,
+ err)) {
+ return false;
+ }
+
+ if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) ||
+ !ReadString(scope, "framework_switch", &framework_switch_, err) ||
+ !ReadString(scope, "weak_framework_switch", &weak_framework_switch_,
+ err) ||
+ !ReadString(scope, "framework_dir_switch", &framework_dir_switch_, err) ||
+ !ReadString(scope, "lib_switch", &lib_switch_, err) ||
+ !ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) ||
+ !ReadPattern(scope, "link_output", &link_output_, err) ||
+ !ReadString(scope, "swiftmodule_switch", &swiftmodule_switch_, err) ||
+ !ReadPattern(scope, "depend_output", &depend_output_, err)) {
+ return false;
+ }
+
+ // Swift tool can optionally specify partial_outputs.
+ if (name_ == kCToolSwift) {
+ if (!ReadOutputsPatternList(scope, "partial_outputs", /*required=*/false,
+ &partial_outputs_, err)) {
+ return false;
+ }
+ }
+
+ // Validate link_output and depend_output.
+ if (!ValidateLinkAndDependOutput(link_output(), "link_output", err)) {
+ return false;
+ }
+ if (!ValidateLinkAndDependOutput(depend_output(), "depend_output", err)) {
+ return false;
+ }
+ if ((!link_output().empty() && depend_output().empty()) ||
+ (link_output().empty() && !depend_output().empty())) {
+ *err = Err(defined_from(),
+ "Both link_output and depend_output should either "
+ "be specified or they should both be empty.");
+ return false;
+ }
+
+ if (!ValidateRuntimeOutputs(err)) {
+ return false;
+ }
+ return true;
+}
+
+bool CTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolCxxModule ||
+ name_ == kCToolObjC || name_ == kCToolObjCxx || name_ == kCToolRc ||
+ name_ == kCToolAsm)
+ return IsValidCompilerSubstitution(sub_type);
+ if (name_ == kCToolSwift)
+ return IsValidSwiftCompilerSubstitution(sub_type);
+ else if (name_ == kCToolAlink)
+ return IsValidALinkSubstitution(sub_type);
+ else if (name_ == kCToolSolink || name_ == kCToolSolinkModule ||
+ name_ == kCToolLink)
+ return IsValidLinkerSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
+
+bool CTool::ValidateOutputSubstitution(const Substitution* sub_type) const {
+ if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolCxxModule ||
+ name_ == kCToolObjC || name_ == kCToolObjCxx || name_ == kCToolRc ||
+ name_ == kCToolAsm)
+ return IsValidCompilerOutputsSubstitution(sub_type);
+ if (name_ == kCToolSwift)
+ return IsValidSwiftCompilerOutputsSubstitution(sub_type);
+ // ALink uses the standard output file patterns as other linker tools.
+ else if (name_ == kCToolAlink || name_ == kCToolSolink ||
+ name_ == kCToolSolinkModule || name_ == kCToolLink)
+ return IsValidLinkerOutputsSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
diff --git a/gn/src/gn/c_tool.h b/gn/src/gn/c_tool.h
new file mode 100644
index 00000000000..fe32e53a591
--- /dev/null
+++ b/gn/src/gn/c_tool.h
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_C_TOOL_H_
+#define TOOLS_GN_C_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/label.h"
+#include "gn/label_ptr.h"
+#include "gn/scope.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+#include "gn/tool.h"
+#include "gn/toolchain.h"
+
+class CTool : public Tool {
+ public:
+ // C compiler tools
+ static const char* kCToolCc;
+ static const char* kCToolCxx;
+ static const char* kCToolCxxModule;
+ static const char* kCToolObjC;
+ static const char* kCToolObjCxx;
+ static const char* kCToolRc;
+ static const char* kCToolAsm;
+ static const char* kCToolSwift;
+
+ // C linker tools
+ static const char* kCToolAlink;
+ static const char* kCToolSolink;
+ static const char* kCToolSolinkModule;
+ static const char* kCToolLink;
+
+ enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
+
+ enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
+
+ CTool(const char* n);
+ ~CTool();
+
+ // Manual RTTI and required functions ---------------------------------------
+
+ bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+ bool ValidateName(const char* name) const override;
+ void SetComplete() override;
+ bool ValidateSubstitution(const Substitution* sub_type) const override;
+
+ CTool* AsC() override;
+ const CTool* AsC() const override;
+
+ // Getters/setters ----------------------------------------------------------
+ //
+ // After the tool has had its attributes set, the caller must call
+ // SetComplete(), at which point no other changes can be made.
+
+ DepsFormat depsformat() const { return depsformat_; }
+ void set_depsformat(DepsFormat f) {
+ DCHECK(!complete_);
+ depsformat_ = f;
+ }
+
+ PrecompiledHeaderType precompiled_header_type() const {
+ return precompiled_header_type_;
+ }
+ void set_precompiled_header_type(PrecompiledHeaderType pch_type) {
+ DCHECK(!complete_);
+ precompiled_header_type_ = pch_type;
+ }
+
+ // Should match files in the outputs() if nonempty.
+ const SubstitutionPattern& link_output() const { return link_output_; }
+ void set_link_output(SubstitutionPattern link_out) {
+ DCHECK(!complete_);
+ link_output_ = std::move(link_out);
+ }
+
+ const SubstitutionPattern& depend_output() const { return depend_output_; }
+ void set_depend_output(SubstitutionPattern dep_out) {
+ DCHECK(!complete_);
+ depend_output_ = std::move(dep_out);
+ }
+
+ // Other functions ----------------------------------------------------------
+
+ // Returns true if this tool has separate outputs for dependency tracking
+ // and linking.
+ bool has_separate_solink_files() const {
+ return !link_output_.empty() || !depend_output_.empty();
+ }
+
+ private:
+ // Initialization functions -------------------------------------------------
+ //
+ // Initialization methods used by InitTool(). If successful, will set the
+ // field and return true, otherwise will return false. Must be called before
+ // SetComplete().
+ bool ValidateOutputSubstitution(const Substitution* sub_type) const;
+ bool ValidateRuntimeOutputs(Err* err);
+ // Validates either link_output or depend_output. To generalize to either,
+ // pass
+ // the associated pattern, and the variable name that should appear in error
+ // messages.
+ bool ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
+ const char* variable_name,
+ Err* err);
+ bool ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ bool required,
+ SubstitutionList* field,
+ Err* err);
+ bool ReadPrecompiledHeaderType(Scope* scope, Err* err);
+ bool ReadDepsFormat(Scope* scope, Err* err);
+
+ DepsFormat depsformat_;
+ PrecompiledHeaderType precompiled_header_type_;
+ SubstitutionPattern link_output_;
+ SubstitutionPattern depend_output_;
+
+ DISALLOW_COPY_AND_ASSIGN(CTool);
+};
+
+#endif // TOOLS_GN_C_TOOL_H_
diff --git a/gn/src/gn/command_analyze.cc b/gn/src/gn/command_analyze.cc
new file mode 100644
index 00000000000..5c6c1939730
--- /dev/null
+++ b/gn/src/gn/command_analyze.cc
@@ -0,0 +1,148 @@
+// 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 <algorithm>
+#include <iterator>
+#include <set>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "gn/analyzer.h"
+#include "gn/commands.h"
+#include "gn/filesystem_utils.h"
+#include "gn/location.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/string_utils.h"
+
+namespace commands {
+
+const char kAnalyze[] = "analyze";
+const char kAnalyze_HelpShort[] =
+ "analyze: Analyze which targets are affected by a list of files.";
+const char kAnalyze_Help[] =
+ R"*(gn analyze <out_dir> <input_path> <output_path>
+
+ Analyze which targets are affected by a list of files.
+
+ This command takes three arguments:
+
+ out_dir is the path to the build directory.
+
+ input_path is a path to a file containing a JSON object with three fields:
+
+ - "files": A list of the filenames to check.
+
+ - "test_targets": A list of the labels for targets that are needed to run
+ the tests we wish to run.
+
+ - "additional_compile_targets": A list of the labels for targets that we
+ wish to rebuild, but aren't necessarily needed for testing. The important
+ difference between this field and "test_targets" is that if an item in
+ the additional_compile_targets list refers to a group, then any
+ dependencies of that group will be returned if they are out of date, but
+ the group itself does not need to be. If the dependencies themselves are
+ groups, the same filtering is repeated. This filtering can be used to
+ avoid rebuilding dependencies of a group that are unaffected by the input
+ files. The list may also contain the string "all" to refer to a
+ pseudo-group that contains every root target in the build graph.
+
+ This filtering behavior is also known as "pruning" the list of compile
+ targets.
+
+ If input_path is -, input is read from stdin.
+
+ output_path is a path indicating where the results of the command are to be
+ written. The results will be a file containing a JSON object with one or more
+ of following fields:
+
+ - "compile_targets": A list of the labels derived from the input
+ compile_targets list that are affected by the input files. Due to the way
+ the filtering works for compile targets as described above, this list may
+ contain targets that do not appear in the input list.
+
+ - "test_targets": A list of the labels from the input test_targets list that
+ are affected by the input files. This list will be a proper subset of the
+ input list.
+
+ - "invalid_targets": A list of any names from the input that do not exist in
+ the build graph. If this list is non-empty, the "error" field will also be
+ set to "Invalid targets".
+
+ - "status": A string containing one of three values:
+
+ - "Found dependency"
+ - "No dependency"
+ - "Found dependency (all)"
+
+ In the first case, the lists returned in compile_targets and test_targets
+ should be passed to ninja to build. In the second case, nothing was
+ affected and no build is necessary. In the third case, GN could not
+ determine the correct answer and returned the input as the output in order
+ to be safe.
+
+ - "error": This will only be present if an error occurred, and will contain
+ a string describing the error. This includes cases where the input file is
+ not in the right format, or contains invalid targets.
+
+ If output_path is -, output is written to stdout.
+
+ The command returns 1 if it is unable to read the input file or write the
+ output file, or if there is something wrong with the build such that gen
+ would also fail, and 0 otherwise. In particular, it returns 0 even if the
+ "error" key is non-empty and a non-fatal error occurred. In other words, it
+ tries really hard to always write something to the output JSON and convey
+ errors that way rather than via return codes.
+)*";
+
+int RunAnalyze(const std::vector<std::string>& args) {
+ if (args.size() != 3) {
+ Err(Location(), "Unknown command format. See \"gn help analyze\"",
+ "Usage: \"gn analyze <out_dir> <input_path> <output_path>")
+ .PrintToStdout();
+ return 1;
+ }
+
+ std::string input;
+ if (args[1] == "-") {
+ input = ReadStdin();
+ } else {
+ bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
+ if (!ret) {
+ Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
+ return 1;
+ }
+ }
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false) || !setup->Run())
+ return 1;
+
+ Err err;
+ Analyzer analyzer(
+ setup->builder(), setup->build_settings().build_config_file(),
+ setup->GetDotFile(),
+ setup->build_settings().build_args().build_args_dependency_files());
+ std::string output = analyzer.Analyze(input, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ if (args[2] == "-") {
+ OutputString(output + "\n");
+ } else {
+ WriteFile(UTF8ToFilePath(args[2]), output, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_args.cc b/gn/src/gn/command_args.cc
new file mode 100644
index 00000000000..2689f882e1e
--- /dev/null
+++ b/gn/src/gn/command_args.cc
@@ -0,0 +1,511 @@
+// 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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <map>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "gn/commands.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/tokenizer.h"
+#include "gn/trace.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include <shellapi.h>
+#endif
+
+namespace commands {
+
+namespace {
+
+const char kSwitchList[] = "list";
+const char kSwitchShort[] = "short";
+const char kSwitchOverridesOnly[] = "overrides-only";
+const char kSwitchJson[] = "json";
+
+bool DoesLineBeginWithComment(const std::string_view& line) {
+ // Skip whitespace.
+ size_t i = 0;
+ while (i < line.size() && base::IsAsciiWhitespace(line[i]))
+ i++;
+
+ return i < line.size() && line[i] == '#';
+}
+
+// Returns the offset of the beginning of the line identified by |offset|.
+size_t BackUpToLineBegin(const std::string& data, size_t offset) {
+ // Degenerate case of an empty line. Below we'll try to return the
+ // character after the newline, but that will be incorrect in this case.
+ if (offset == 0 || Tokenizer::IsNewline(data, offset))
+ return offset;
+
+ size_t cur = offset;
+ do {
+ cur--;
+ if (Tokenizer::IsNewline(data, cur))
+ return cur + 1; // Want the first character *after* the newline.
+ } while (cur > 0);
+ return 0;
+}
+
+// Assumes DoesLineBeginWithComment(), this strips the # character from the
+// beginning and normalizes preceding whitespace.
+std::string StripHashFromLine(const std::string_view& line, bool pad) {
+ // Replace the # sign and everything before it with 3 spaces, so that a
+ // normal comment that has a space after the # will be indented 4 spaces
+ // (which makes our formatting come out nicely). If the comment is indented
+ // from there, we want to preserve that indenting.
+ std::string line_stripped(line.substr(line.find('#') + 1));
+ if (pad)
+ return " " + line_stripped;
+
+ // If not padding, strip the leading space if present.
+ if (!line_stripped.empty() && line_stripped[0] == ' ')
+ return line_stripped.substr(1);
+ return line_stripped;
+}
+
+// Tries to find the comment before the setting of the given value.
+void GetContextForValue(const Value& value,
+ std::string* location_str,
+ int* line_no,
+ std::string* comment,
+ bool pad_comment = true) {
+ Location location = value.origin()->GetRange().begin();
+ const InputFile* file = location.file();
+ if (!file)
+ return;
+
+ *location_str = file->name().value();
+ *line_no = location.line_number();
+
+ const std::string& data = file->contents();
+ size_t line_off =
+ Tokenizer::ByteOffsetOfNthLine(data, location.line_number());
+
+ while (line_off > 1) {
+ line_off -= 2; // Back up to end of previous line.
+ size_t previous_line_offset = BackUpToLineBegin(data, line_off);
+
+ std::string_view line(&data[previous_line_offset],
+ line_off - previous_line_offset + 1);
+ if (!DoesLineBeginWithComment(line))
+ break;
+
+ comment->insert(0, StripHashFromLine(line, pad_comment) + "\n");
+ line_off = previous_line_offset;
+ }
+}
+
+// Prints the value and origin for a default value. Default values always list
+// an origin and if there is no origin, print a message about it being
+// internally set. Overrides can't be internally set so the location handling
+// is a bit different.
+//
+// The default value also contains the docstring.
+void PrintDefaultValueInfo(std::string_view name, const Value& value) {
+ OutputString(value.ToString(true) + "\n");
+ if (value.origin()) {
+ int line_no;
+ std::string location, comment;
+ GetContextForValue(value, &location, &line_no, &comment);
+ OutputString(" From " + location + ":" + base::IntToString(line_no) +
+ "\n");
+ if (!comment.empty())
+ OutputString("\n" + comment);
+ } else {
+ OutputString(" (Internally set; try `gn help " + std::string(name) +
+ "`.)\n");
+ }
+}
+
+// Override value is null if there is no override.
+void PrintArgHelp(const std::string_view& name,
+ const Args::ValueWithOverride& val) {
+ OutputString(std::string(name), DECORATION_YELLOW);
+ OutputString("\n");
+
+ if (val.has_override) {
+ // Override present, print both it and the default.
+ OutputString(" Current value = " + val.override_value.ToString(true) +
+ "\n");
+ if (val.override_value.origin()) {
+ int line_no;
+ std::string location, comment;
+ GetContextForValue(val.override_value, &location, &line_no, &comment);
+ OutputString(" From " + location + ":" + base::IntToString(line_no) +
+ "\n");
+ }
+ OutputString(" Overridden from the default = ");
+ PrintDefaultValueInfo(name, val.default_value);
+ } else {
+ // No override.
+ OutputString(" Current value (from the default) = ");
+ PrintDefaultValueInfo(name, val.default_value);
+ }
+}
+
+void BuildArgJson(base::Value& dict,
+ const std::string_view& name,
+ const Args::ValueWithOverride& arg,
+ bool short_only) {
+ assert(dict.is_dict());
+
+ // Fetch argument name.
+ dict.SetKey("name", base::Value(name));
+
+ // Fetch overridden value inforrmation (if present).
+ if (arg.has_override) {
+ base::DictionaryValue override_dict;
+ override_dict.SetKey("value",
+ base::Value(arg.override_value.ToString(true)));
+ if (arg.override_value.origin() && !short_only) {
+ int line_no;
+ std::string location, comment;
+ GetContextForValue(arg.override_value, &location, &line_no, &comment,
+ /*pad_comment=*/false);
+ override_dict.SetKey("file", base::Value(location));
+ override_dict.SetKey("line", base::Value(line_no));
+ }
+ dict.SetKey("current", std::move(override_dict));
+ }
+
+ // Fetch default value information, and comment (if present).
+ base::DictionaryValue default_dict;
+ std::string comment;
+ default_dict.SetKey("value", base::Value(arg.default_value.ToString(true)));
+ if (arg.default_value.origin() && !short_only) {
+ int line_no;
+ std::string location;
+ GetContextForValue(arg.default_value, &location, &line_no, &comment,
+ /*pad_comment=*/false);
+ default_dict.SetKey("file", base::Value(location));
+ default_dict.SetKey("line", base::Value(line_no));
+ }
+ dict.SetKey("default", std::move(default_dict));
+ if (!comment.empty() && !short_only)
+ dict.SetKey("comment", base::Value(comment));
+}
+
+int ListArgs(const std::string& build_dir) {
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(build_dir, false) || !setup->Run())
+ return 1;
+
+ Args::ValueWithOverrideMap args =
+ setup->build_settings().build_args().GetAllArguments();
+ std::string list_value =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kSwitchList);
+ if (!list_value.empty()) {
+ // List just the one specified as the parameter to --list.
+ auto found = args.find(list_value);
+ if (found == args.end()) {
+ Err(Location(), "Unknown build argument.",
+ "You asked for \"" + list_value +
+ "\" which I didn't find in any "
+ "build file\nassociated with this build.")
+ .PrintToStdout();
+ return 1;
+ }
+
+ // Delete everything from the map except the one requested.
+ Args::ValueWithOverrideMap::value_type preserved = *found;
+ args.clear();
+ args.insert(preserved);
+ }
+
+ // Cache this to avoid looking it up for each |arg| in the loops below.
+ const bool overrides_only =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchOverridesOnly);
+ const bool short_only =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort);
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchJson)) {
+ // Convert all args to JSON, serialize and print them
+ auto list = std::make_unique<base::ListValue>();
+ for (const auto& arg : args) {
+ if (overrides_only && !arg.second.has_override)
+ continue;
+ list->GetList().emplace_back(base::DictionaryValue());
+ BuildArgJson(list->GetList().back(), arg.first, arg.second, short_only);
+ }
+ std::string s;
+ base::JSONWriter::WriteWithOptions(
+ *list.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
+ OutputString(s);
+ return 0;
+ }
+
+ if (short_only) {
+ // Short <key>=<current_value> output.
+ for (const auto& arg : args) {
+ if (overrides_only && !arg.second.has_override)
+ continue;
+ OutputString(std::string(arg.first));
+ OutputString(" = ");
+ if (arg.second.has_override)
+ OutputString(arg.second.override_value.ToString(true));
+ else
+ OutputString(arg.second.default_value.ToString(true));
+ OutputString("\n");
+ }
+ return 0;
+ }
+
+ // Long output.
+ for (const auto& arg : args) {
+ if (overrides_only && !arg.second.has_override)
+ continue;
+ PrintArgHelp(arg.first, arg.second);
+ OutputString("\n");
+ }
+
+ return 0;
+}
+
+#if defined(OS_WIN)
+
+bool RunEditor(const base::FilePath& file_to_edit) {
+ SHELLEXECUTEINFO info;
+ memset(&info, 0, sizeof(info));
+ info.cbSize = sizeof(info);
+ info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_CLASSNAME;
+ info.lpFile = reinterpret_cast<LPCWSTR>(file_to_edit.value().c_str());
+ info.nShow = SW_SHOW;
+ info.lpClass = L".txt";
+ if (!::ShellExecuteEx(&info)) {
+ Err(Location(), "Couldn't run editor.",
+ "Just edit \"" + FilePathToUTF8(file_to_edit) + "\" manually instead.")
+ .PrintToStdout();
+ return false;
+ }
+
+ if (!info.hProcess) {
+ // Windows re-used an existing process.
+ OutputString("\"" + FilePathToUTF8(file_to_edit) +
+ "\" opened in editor, save it and press <Enter> when done.\n");
+ getchar();
+ } else {
+ OutputString("Waiting for editor on \"" + FilePathToUTF8(file_to_edit) +
+ "\"...\n");
+ ::WaitForSingleObject(info.hProcess, INFINITE);
+ ::CloseHandle(info.hProcess);
+ }
+ return true;
+}
+
+#else // POSIX
+
+bool RunEditor(const base::FilePath& file_to_edit) {
+ const char* editor_ptr = getenv("GN_EDITOR");
+ if (!editor_ptr)
+ editor_ptr = getenv("VISUAL");
+ if (!editor_ptr)
+ editor_ptr = getenv("EDITOR");
+ if (!editor_ptr)
+ editor_ptr = "vi";
+
+ std::string cmd(editor_ptr);
+ cmd.append(" \"");
+
+ // Its impossible to do this properly since we don't know the user's shell,
+ // but quoting and escaping internal quotes should handle 99.999% of all
+ // cases.
+ std::string escaped_name = file_to_edit.value();
+ base::ReplaceSubstringsAfterOffset(&escaped_name, 0, "\"", "\\\"");
+ cmd.append(escaped_name);
+ cmd.push_back('"');
+
+ OutputString("Waiting for editor on \"" + file_to_edit.value() + "\"...\n");
+ return system(cmd.c_str()) == 0;
+}
+
+#endif
+
+int EditArgsFile(const std::string& build_dir) {
+ {
+ // Scope the setup. We only use it for some basic state. We'll do the
+ // "real" build below in the gen command.
+ Setup setup;
+ // Don't fill build arguments. We're about to edit the file which supplies
+ // these in the first place.
+ setup.set_fill_arguments(false);
+ if (!setup.DoSetup(build_dir, true))
+ return 1;
+
+ // Ensure the file exists. Need to normalize path separators since on
+ // Windows they can come out as forward slashes here, and that confuses some
+ // of the commands.
+ BuildSettings build_settings = setup.build_settings();
+ base::FilePath arg_file =
+ build_settings.GetFullPath(setup.GetBuildArgFile())
+ .NormalizePathSeparators();
+ if (!base::PathExists(arg_file)) {
+ std::string argfile_default_contents =
+ "# Build arguments go here.\n"
+ "# See \"gn args <out_dir> --list\" for available build "
+ "arguments.\n";
+
+ SourceFile template_path = build_settings.arg_file_template_path();
+ if (!template_path.is_null()) {
+ base::FilePath full_path =
+ build_settings.GetFullPath(template_path).NormalizePathSeparators();
+ if (!base::PathExists(full_path)) {
+ Err err =
+ Err(Location(), std::string("Can't load arg_file_template:\n ") +
+ template_path.value());
+ err.PrintToStdout();
+ return 1;
+ }
+
+ // Ignore the return code; if the read fails (unlikely), we'll just
+ // use the default contents.
+ base::ReadFileToString(full_path, &argfile_default_contents);
+ }
+#if defined(OS_WIN)
+ // Use Windows lineendings for this file since it will often open in
+ // Notepad which can't handle Unix ones.
+ base::ReplaceSubstringsAfterOffset(&argfile_default_contents, 0, "\n",
+ "\r\n");
+#endif
+ base::CreateDirectory(arg_file.DirName());
+ base::WriteFile(arg_file, argfile_default_contents.c_str(),
+ static_cast<int>(argfile_default_contents.size()));
+ }
+
+ ScopedTrace editor_trace(TraceItem::TRACE_SETUP, "Waiting for editor");
+ if (!RunEditor(arg_file))
+ return 1;
+ }
+
+ // Now do a normal "gen" command.
+ OutputString("Generating files...\n");
+ std::vector<std::string> gen_commands;
+ gen_commands.push_back(build_dir);
+ return RunGen(gen_commands);
+}
+
+} // namespace
+
+const char kArgs[] = "args";
+const char kArgs_HelpShort[] =
+ "args: Display or configure arguments declared by the build.";
+const char kArgs_Help[] =
+ R"(gn args: (command-line tool)
+
+ Display or configure arguments declared by the build.
+
+ gn args <out_dir> [--list] [--short] [--args] [--overrides-only]
+
+ See also "gn help buildargs" for a more high-level overview of how
+ build arguments work.
+
+Usage
+
+ gn args <out_dir>
+ Open the arguments for the given build directory in an editor. If the
+ given build directory doesn't exist, it will be created and an empty args
+ file will be opened in the editor. You would type something like this
+ into that file:
+ enable_doom_melon=false
+ os="android"
+
+ To find your editor on Posix, GN will search the environment variables in
+ order: GN_EDITOR, VISUAL, and EDITOR. On Windows GN will open the command
+ associated with .txt files.
+
+ Note: you can edit the build args manually by editing the file "args.gn"
+ in the build directory and then running "gn gen <out_dir>".
+
+ gn args <out_dir> --list[=<exact_arg>] [--short] [--overrides-only] [--json]
+ Lists all build arguments available in the current configuration, or, if
+ an exact_arg is specified for the list flag, just that one build
+ argument.
+
+ The output will list the declaration location, current value for the
+ build, default value (if different than the current value), and comment
+ preceding the declaration.
+
+ If --short is specified, only the names and current values will be
+ printed.
+
+ If --overrides-only is specified, only the names and current values of
+ arguments that have been overridden (i.e. non-default arguments) will
+ be printed. Overrides come from the <out_dir>/args.gn file and //.gn
+
+ If --json is specified, the output will be emitted in json format.
+ JSON schema for output:
+ [
+ {
+ "name": variable_name,
+ "current": {
+ "value": overridden_value,
+ "file": file_name,
+ "line": line_no
+ },
+ "default": {
+ "value": default_value,
+ "file": file_name,
+ "line": line_no
+ },
+ "comment": comment_string
+ },
+ ...
+ ]
+
+Examples
+
+ gn args out/Debug
+ Opens an editor with the args for out/Debug.
+
+ gn args out/Debug --list --short
+ Prints all arguments with their default values for the out/Debug
+ build.
+
+ gn args out/Debug --list --short --overrides-only
+ Prints overridden arguments for the out/Debug build.
+
+ gn args out/Debug --list=target_cpu
+ Prints information about the "target_cpu" argument for the "
+ "out/Debug
+ build.
+
+ gn args --list --args="os=\"android\" enable_doom_melon=true"
+ Prints all arguments with the default values for a build with the
+ given arguments set (which may affect the values of other
+ arguments).
+)";
+
+int RunArgs(const std::vector<std::string>& args) {
+ if (args.size() != 1) {
+ Err(Location(), "Exactly one build dir needed.",
+ "Usage: \"gn args <out_dir>\"\n"
+ "Or see \"gn help args\" for more variants.")
+ .PrintToStdout();
+ return 1;
+ }
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchList))
+ return ListArgs(args[0]);
+ return EditArgsFile(args[0]);
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_check.cc b/gn/src/gn/command_check.cc
new file mode 100644
index 00000000000..536bc13f843
--- /dev/null
+++ b/gn/src/gn/command_check.cc
@@ -0,0 +1,285 @@
+// 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 <stddef.h>
+
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "gn/commands.h"
+#include "gn/header_checker.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+
+namespace commands {
+
+const char kNoGnCheck_Help[] =
+ R"(nogncheck: Skip an include line from checking.
+
+ GN's header checker helps validate that the includes match the build
+ dependency graph. Sometimes an include might be conditional or otherwise
+ problematic, but you want to specifically allow it. In this case, it can be
+ whitelisted.
+
+ Include lines containing the substring "nogncheck" will be excluded from
+ header checking. The most common case is a conditional include:
+
+ #if defined(ENABLE_DOOM_MELON)
+ #include "tools/doom_melon/doom_melon.h" // nogncheck
+ #endif
+
+ If the build file has a conditional dependency on the corresponding target
+ that matches the conditional include, everything will always link correctly:
+
+ source_set("mytarget") {
+ ...
+ if (enable_doom_melon) {
+ defines = [ "ENABLE_DOOM_MELON" ]
+ deps += [ "//tools/doom_melon" ]
+ }
+
+ But GN's header checker does not understand preprocessor directives, won't
+ know it matches the build dependencies, and will flag this include as
+ incorrect when the condition is false.
+
+More information
+
+ The topic "gn help check" has general information on how checking works and
+ advice on fixing problems. Targets can also opt-out of checking, see
+ "gn help check_includes".
+)";
+
+const char kCheck[] = "check";
+const char kCheck_HelpShort[] = "check: Check header dependencies.";
+const char kCheck_Help[] =
+ R"(gn check <out_dir> [<label_pattern>] [--force] [--check-generated]
+
+ GN's include header checker validates that the includes for C-like source
+ files match the build dependency graph.
+
+ "gn check" is the same thing as "gn gen" with the "--check" flag except that
+ this command does not write out any build files. It's intended to be an easy
+ way to manually trigger include file checking.
+
+ The <label_pattern> can take exact labels or patterns that match more than
+ one (although not general regular expressions). If specified, only those
+ matching targets will be checked. See "gn help label_pattern" for details.
+
+Command-specific switches
+
+ --check-generated
+ Generated files are normally not checked since they do not exist
+ until after a build. With this flag, those generated files that
+ can be found on disk are also checked.
+
+ --check-system
+ Check system style includes (using <angle brackets>) in addition to
+ "double quote" includes.
+
+)" DEFAULT_TOOLCHAIN_SWITCH_HELP
+ R"(
+ --force
+ Ignores specifications of "check_includes = false" and checks all
+ target's files that match the target label.
+
+What gets checked
+
+ The .gn file may specify a list of targets to be checked in the list
+ check_targets (see "gn help dotfile"). Alternatively, the .gn file may
+ specify a list of targets not to be checked in no_check_targets. If a label
+ pattern is specified on the command line, neither check_targets or
+ no_check_targets is used.
+
+ Targets can opt-out from checking with "check_includes = false" (see
+ "gn help check_includes").
+
+ For targets being checked:
+
+ - GN opens all C-like source files in the targets to be checked and scans
+ the top for includes.
+
+ - Generated files (that might not exist yet) are ignored unless
+ the --check-generated flag is provided.
+
+ - Includes with a "nogncheck" annotation are skipped (see
+ "gn help nogncheck").
+
+ - Includes using "quotes" are always checked.
+ If system style checking is enabled, includes using <angle brackets>
+ are also checked.
+
+ - Include paths are assumed to be relative to any of the "include_dirs" for
+ the target (including the implicit current dir).
+
+ - GN does not run the preprocessor so will not understand conditional
+ includes.
+
+ - Only includes matching known files in the build are checked: includes
+ matching unknown paths are ignored.
+
+ For an include to be valid:
+
+ - The included file must be in the current target, or there must be a path
+ following only public dependencies to a target with the file in it
+ ("gn path" is a good way to diagnose problems).
+
+ - There can be multiple targets with an included file: only one needs to be
+ valid for the include to be allowed.
+
+ - If there are only "sources" in a target, all are considered to be public
+ and can be included by other targets with a valid public dependency path.
+
+ - If a target lists files as "public", only those files are able to be
+ included by other targets. Anything in the sources will be considered
+ private and will not be includable regardless of dependency paths.
+
+ - Outputs from actions are treated like public sources on that target.
+
+ - A target can include headers from a target that depends on it if the
+ other target is annotated accordingly. See "gn help
+ allow_circular_includes_from".
+
+Advice on fixing problems
+
+ If you have a third party project that is difficult to fix or doesn't care
+ about include checks it's generally best to exclude that target from checking
+ altogether via "check_includes = false".
+
+ If you have conditional includes, make sure the build conditions and the
+ preprocessor conditions match, and annotate the line with "nogncheck" (see
+ "gn help nogncheck" for an example).
+
+ If two targets are hopelessly intertwined, use the
+ "allow_circular_includes_from" annotation. Ideally each should have identical
+ dependencies so configs inherited from those dependencies are consistent (see
+ "gn help allow_circular_includes_from").
+
+ If you have a standalone header file or files that need to be shared between
+ a few targets, you can consider making a source_set listing only those
+ headers as public sources. With only header files, the source set will be a
+ no-op from a build perspective, but will give a central place to refer to
+ those headers. That source set's files will still need to pass "gn check" in
+ isolation.
+
+ In rare cases it makes sense to list a header in more than one target if it
+ could be considered conceptually a member of both.
+
+Examples
+
+ gn check out/Debug
+ Check everything.
+
+ gn check out/Default //foo:bar
+ Check only the files in the //foo:bar target.
+
+ gn check out/Default "//foo/*
+ Check only the files in targets in the //foo directory tree.
+)";
+
+int RunCheck(const std::vector<std::string>& args) {
+ if (args.size() != 1 && args.size() != 2) {
+ Err(Location(), "Unknown command format. See \"gn help check\"",
+ "Usage: \"gn check <out_dir> [<target_label>]\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup();
+ if (!setup->DoSetup(args[0], false))
+ return 1;
+ if (!setup->Run())
+ return 1;
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool default_toolchain_only = cmdline->HasSwitch(switches::kDefaultToolchain);
+
+ std::vector<const Target*> all_targets =
+ setup->builder().GetAllResolvedTargets();
+
+ bool filtered_by_build_config = false;
+ std::vector<const Target*> targets_to_check;
+ if (args.size() > 1) {
+ // Compute the targets to check.
+ std::vector<std::string> inputs(args.begin() + 1, args.end());
+ UniqueVector<const Target*> target_matches;
+ UniqueVector<const Config*> config_matches;
+ UniqueVector<const Toolchain*> toolchain_matches;
+ UniqueVector<SourceFile> file_matches;
+ if (!ResolveFromCommandLineInput(setup, inputs, default_toolchain_only,
+ &target_matches, &config_matches,
+ &toolchain_matches, &file_matches))
+ return 1;
+
+ if (target_matches.size() == 0) {
+ OutputString("No matching targets.\n");
+ return 1;
+ }
+ targets_to_check.insert(targets_to_check.begin(), target_matches.begin(),
+ target_matches.end());
+ } else {
+ // No argument means to check everything allowed by the filter in
+ // the build config file.
+ if (setup->check_patterns()) {
+ FilterTargetsByPatterns(all_targets, *setup->check_patterns(),
+ &targets_to_check);
+ filtered_by_build_config = targets_to_check.size() != all_targets.size();
+ } else if (setup->no_check_patterns()) {
+ FilterOutTargetsByPatterns(all_targets, *setup->no_check_patterns(),
+ &targets_to_check);
+ filtered_by_build_config = targets_to_check.size() != all_targets.size();
+ } else {
+ // No global filter, check everything.
+ targets_to_check = all_targets;
+ }
+ }
+
+ bool force = cmdline->HasSwitch("force");
+ bool check_generated = cmdline->HasSwitch("check-generated");
+ bool check_system =
+ setup->check_system_includes() || cmdline->HasSwitch("check-system");
+
+ if (!CheckPublicHeaders(&setup->build_settings(), all_targets,
+ targets_to_check, force, check_generated,
+ check_system))
+ return 1;
+
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) {
+ if (filtered_by_build_config) {
+ // Tell the user about the implicit filtering since this is obscure.
+ OutputString(base::StringPrintf(
+ "%d targets out of %d checked based on the check_targets or "
+ "no_check_targets defined in \".gn\".\n",
+ static_cast<int>(targets_to_check.size()),
+ static_cast<int>(all_targets.size())));
+ }
+ OutputString("Header dependency check OK\n", DECORATION_GREEN);
+ }
+ return 0;
+}
+
+bool CheckPublicHeaders(const BuildSettings* build_settings,
+ const std::vector<const Target*>& all_targets,
+ const std::vector<const Target*>& to_check,
+ bool force_check,
+ bool check_generated,
+ bool check_system) {
+ ScopedTrace trace(TraceItem::TRACE_CHECK_HEADERS, "Check headers");
+
+ scoped_refptr<HeaderChecker> header_checker(new HeaderChecker(
+ build_settings, all_targets, check_generated, check_system));
+
+ std::vector<Err> header_errors;
+ header_checker->Run(to_check, force_check, &header_errors);
+ for (size_t i = 0; i < header_errors.size(); i++) {
+ if (i > 0)
+ OutputString("___________________\n", DECORATION_YELLOW);
+ header_errors[i].PrintToStdout();
+ }
+ return header_errors.empty();
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_clean.cc b/gn/src/gn/command_clean.cc
new file mode 100644
index 00000000000..4f2548c23d1
--- /dev/null
+++ b/gn/src/gn/command_clean.cc
@@ -0,0 +1,139 @@
+// 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/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "gn/commands.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/setup.h"
+
+namespace {
+
+// 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.
+std::string ExtractGNBuildCommands(const base::FilePath& build_ninja_file) {
+ std::string file_contents;
+ if (!base::ReadFileToString(build_ninja_file, &file_contents))
+ return std::string();
+
+ std::vector<std::string_view> lines = base::SplitStringPiece(
+ file_contents, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ std::string result;
+ int num_blank_lines = 0;
+ for (const auto& line : lines) {
+ result.append(line);
+ result.push_back('\n');
+ if (line.empty())
+ ++num_blank_lines;
+ if (num_blank_lines == 3)
+ break;
+ }
+
+ return result;
+}
+
+bool CleanOneDir(const std::string& dir) {
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(dir, false))
+ return false;
+
+ base::FilePath build_dir(setup->build_settings().GetFullPath(
+ SourceDir(setup->build_settings().build_dir().value())));
+
+ // NOTE: Not all GN builds have args.gn file hence we check here
+ // if a build.ninja.d files exists instead.
+ base::FilePath build_ninja_d_file = build_dir.AppendASCII("build.ninja.d");
+ if (!base::PathExists(build_ninja_d_file)) {
+ Err(Location(),
+ base::StringPrintf(
+ "%s does not look like a build directory.\n",
+ FilePathToUTF8(build_ninja_d_file.DirName().value()).c_str()))
+ .PrintToStdout();
+ return false;
+ }
+
+ // Erase everything but the args file, and write a dummy build.ninja file that
+ // will automatically rerun GN the next time Ninja is run.
+ base::FilePath build_ninja_file = build_dir.AppendASCII("build.ninja");
+ std::string build_commands = ExtractGNBuildCommands(build_ninja_file);
+ if (build_commands.empty()) {
+ // Couldn't parse the build.ninja file.
+ Err(Location(), "Couldn't read build.ninja in this directory.",
+ "Try running \"gn gen\" on it and then re-running \"gn clean\".")
+ .PrintToStdout();
+ return false;
+ }
+
+ base::FileEnumerator traversal(
+ build_dir, false,
+ base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
+ for (base::FilePath current = traversal.Next(); !current.empty();
+ current = traversal.Next()) {
+ if (base::ToLowerASCII(current.BaseName().value()) !=
+ FILE_PATH_LITERAL("args.gn")) {
+ base::DeleteFile(current, true);
+ }
+ }
+
+ // Write the build.ninja file sufficiently to regenerate itself.
+ if (base::WriteFile(build_ninja_file, build_commands.data(),
+ static_cast<int>(build_commands.size())) == -1) {
+ Err(Location(), std::string("Failed to write build.ninja."))
+ .PrintToStdout();
+ return false;
+ }
+
+ // Write a .d file for the build which references a nonexistant file.
+ // This will make Ninja always mark the build as dirty.
+ std::string dummy_content("build.ninja: nonexistant_file.gn\n");
+ if (base::WriteFile(build_ninja_d_file, dummy_content.data(),
+ static_cast<int>(dummy_content.size())) == -1) {
+ Err(Location(), std::string("Failed to write build.ninja.d."))
+ .PrintToStdout();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace commands {
+
+const char kClean[] = "clean";
+const char kClean_HelpShort[] = "clean: Cleans the output directory.";
+const char kClean_Help[] =
+ "gn clean <out_dir>...\n"
+ "\n"
+ " Deletes the contents of the output directory except for args.gn and\n"
+ " creates a Ninja build environment sufficient to regenerate the build.\n";
+
+int RunClean(const std::vector<std::string>& args) {
+ if (args.empty()) {
+ Err(Location(), "Missing argument.", "Usage: \"gn clean <out_dir>...\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ for (const auto& dir : args) {
+ if (!CleanOneDir(dir))
+ return 1;
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_clean_stale.cc b/gn/src/gn/command_clean_stale.cc
new file mode 100644
index 00000000000..72f621ddff5
--- /dev/null
+++ b/gn/src/gn/command_clean_stale.cc
@@ -0,0 +1,98 @@
+// Copyright 2020 The Chromium 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 <optional>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "gn/commands.h"
+#include "gn/err.h"
+#include "gn/ninja_tools.h"
+#include "gn/setup.h"
+#include "gn/source_dir.h"
+#include "gn/switches.h"
+#include "gn/version.h"
+
+namespace commands {
+
+namespace {
+
+bool CleanStaleOneDir(const base::FilePath& ninja_executable,
+ const std::string& dir) {
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(dir, false))
+ return false;
+
+ base::FilePath build_dir(setup->build_settings().GetFullPath(
+ SourceDir(setup->build_settings().build_dir().value())));
+
+ // The order of operations for these tools is:
+ // 1. cleandead - This eliminates old files from the build directory.
+ // 2. recompact - This prunes old entries from the ninja log and deps files.
+ //
+ // This order is ideal because the files removed by cleandead will no longer
+ // be found during the recompact, so ninja can prune their entries.
+ Err err;
+ if (!InvokeNinjaCleanDeadTool(ninja_executable, build_dir, &err)) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ if (!InvokeNinjaRecompactTool(ninja_executable, build_dir, &err)) {
+ err.PrintToStdout();
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+const char kCleanStale[] = "clean_stale";
+const char kCleanStale_HelpShort[] =
+ "clean_stale: Cleans the stale output files from the output directory.";
+const char kCleanStale_Help[] =
+ R"(gn clean_stale [--ninja-executable=...] <out_dir>...
+
+ Removes the no longer needed output files from the build directory and prunes
+ their records from the ninja build log and dependency database. These are
+ output files that were generated from previous builds, but the current build
+ graph no longer references them.
+
+ This command requires a ninja executable of at least version 1.10.0. The
+ executable must be provided by the --ninja-executable switch.
+
+Options
+
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use.
+)";
+
+int RunCleanStale(const std::vector<std::string>& args) {
+ if (args.empty()) {
+ Err(Location(), "Missing argument.",
+ "Usage: \"gn clean_stale <out_dir>...\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ base::FilePath ninja_executable = cmdline->GetSwitchValuePath(switches::kNinjaExecutable);
+ if (ninja_executable.empty()) {
+ Err(Location(), "No --ninja-executable provided.",
+ "--clean-stale requires a ninja executable to run. You can "
+ "provide one on the command line via --ninja-executable.")
+ .PrintToStdout();
+ return 1;
+ }
+
+ for (const std::string& dir : args) {
+ if (!CleanStaleOneDir(ninja_executable, dir))
+ return 1;
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_desc.cc b/gn/src/gn/command_desc.cc
new file mode 100644
index 00000000000..82a38229708
--- /dev/null
+++ b/gn/src/gn/command_desc.cc
@@ -0,0 +1,716 @@
+// 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 <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <sstream>
+
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "gn/commands.h"
+#include "gn/config.h"
+#include "gn/desc_builder.h"
+#include "gn/rust_variables.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/swift_variables.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/variables.h"
+
+namespace commands {
+
+namespace {
+
+// Desc-specific command line switches.
+const char kBlame[] = "blame";
+const char kTree[] = "tree";
+const char kAll[] = "all";
+
+void PrintDictValue(const base::Value* value,
+ int indentLevel,
+ bool use_first_indent) {
+ std::string indent(indentLevel * 2, ' ');
+ const base::ListValue* list_value = nullptr;
+ const base::DictionaryValue* dict_value = nullptr;
+ std::string string_value;
+ bool bool_value = false;
+ int int_value = 0;
+ if (use_first_indent)
+ OutputString(indent);
+ if (value->GetAsList(&list_value)) {
+ OutputString("[\n");
+ bool first = true;
+ for (const auto& v : *list_value) {
+ if (!first)
+ OutputString(",\n");
+ PrintDictValue(&v, indentLevel + 1, true);
+ first = false;
+ }
+ OutputString("\n" + indent + "]");
+ } else if (value->GetAsString(&string_value)) {
+ OutputString("\"" + string_value + "\"");
+ } else if (value->GetAsBoolean(&bool_value)) {
+ OutputString(bool_value ? "true" : "false");
+ } else if (value->GetAsDictionary(&dict_value)) {
+ OutputString("{\n");
+ std::string indent_plus_one((indentLevel + 1) * 2, ' ');
+ base::DictionaryValue::Iterator iter(*dict_value);
+ bool first = true;
+ while (!iter.IsAtEnd()) {
+ if (!first)
+ OutputString(",\n");
+ OutputString(indent_plus_one + iter.key() + " = ");
+ PrintDictValue(&iter.value(), indentLevel + 1, false);
+ iter.Advance();
+ first = false;
+ }
+ OutputString("\n" + indent + "}");
+ } else if (value->GetAsInteger(&int_value)) {
+ OutputString(base::IntToString(int_value));
+ } else if (value->is_none()) {
+ OutputString("<null>");
+ }
+}
+
+// Prints value with specified indentation level
+void PrintValue(const base::Value* value, int indentLevel) {
+ std::string indent(indentLevel * 2, ' ');
+ const base::ListValue* list_value = nullptr;
+ const base::DictionaryValue* dict_value = nullptr;
+ std::string string_value;
+ bool bool_value = false;
+ int int_value = 0;
+ if (value->GetAsList(&list_value)) {
+ for (const auto& v : *list_value) {
+ PrintValue(&v, indentLevel);
+ }
+ } else if (value->GetAsString(&string_value)) {
+ OutputString(indent);
+ OutputString(string_value);
+ OutputString("\n");
+ } else if (value->GetAsBoolean(&bool_value)) {
+ OutputString(indent);
+ OutputString(bool_value ? "true" : "false");
+ OutputString("\n");
+ } else if (value->GetAsDictionary(&dict_value)) {
+ base::DictionaryValue::Iterator iter(*dict_value);
+ while (!iter.IsAtEnd()) {
+ OutputString(indent + iter.key() + "\n");
+ PrintValue(&iter.value(), indentLevel + 1);
+ iter.Advance();
+ }
+ } else if (value->GetAsInteger(&int_value)) {
+ OutputString(indent);
+ OutputString(base::IntToString(int_value));
+ OutputString("\n");
+ } else if (value->is_none()) {
+ OutputString(indent + "<null>\n");
+ }
+}
+
+// Default handler for property
+void DefaultHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
+ OutputString("\n");
+ OutputString(name);
+ OutputString("\n");
+ PrintValue(value, 1);
+}
+
+// Specific handler for properties that need different treatment
+
+// Prints the dict in GN scope-sytle.
+void MetadataHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintDictValue(value, 0, true);
+ OutputString("\n");
+ return;
+ }
+ OutputString("\n");
+ OutputString(name);
+ OutputString("\n");
+ PrintDictValue(value, 1, true);
+ OutputString("\n");
+}
+
+// Prints label and property value on one line, capitalizing the label.
+void LabelHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
+ std::string label = name;
+ label[0] = base::ToUpperASCII(label[0]);
+ std::string string_value;
+ if (value->GetAsString(&string_value)) {
+ OutputString(name + ": ", DECORATION_YELLOW);
+ OutputString(string_value + "\n");
+ }
+}
+
+void VisibilityHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
+ const base::ListValue* list;
+ if (value->GetAsList(&list)) {
+ if (list->empty()) {
+ base::Value str("(no visibility)");
+ DefaultHandler(name, &str, value_only);
+ } else {
+ DefaultHandler(name, value, value_only);
+ }
+ }
+}
+
+void PublicHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
+ std::string p;
+ if (value->GetAsString(&p)) {
+ if (p == "*") {
+ base::Value str("[All headers listed in the sources are public.]");
+ DefaultHandler(name, &str, value_only);
+ return;
+ }
+ }
+ DefaultHandler(name, value, value_only);
+}
+
+void ConfigsHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ if (tree)
+ DefaultHandler(name + " tree (in order applying)", value, value_only);
+ else
+ DefaultHandler(name + " (in order applying, try also --tree)", value,
+ value_only);
+}
+
+void DepsHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ if (tree) {
+ DefaultHandler("Dependency tree", value, value_only);
+ } else {
+ if (!all) {
+ DefaultHandler(
+ "Direct dependencies "
+ "(try also \"--all\", \"--tree\", or even \"--all --tree\")",
+ value, value_only);
+ } else {
+ DefaultHandler("All recursive dependencies", value, value_only);
+ }
+ }
+}
+
+// Outputs need special processing when output patterns are present.
+void ProcessOutputs(base::DictionaryValue* target, bool files_only) {
+ base::ListValue* patterns = nullptr;
+ base::ListValue* outputs = nullptr;
+ target->GetList("output_patterns", &patterns);
+ target->GetList(variables::kOutputs, &outputs);
+
+ int indent = 0;
+ if (outputs || patterns) {
+ if (!files_only) {
+ OutputString("\noutputs\n");
+ indent = 1;
+ }
+ if (patterns) {
+ if (!files_only) {
+ OutputString(" Output patterns\n");
+ indent = 2;
+ }
+ PrintValue(patterns, indent);
+ if (!files_only)
+ OutputString("\n Resolved output file list\n");
+ }
+ if (outputs)
+ PrintValue(outputs, indent);
+
+ target->Remove("output_patterns", nullptr);
+ target->Remove(variables::kOutputs, nullptr);
+ }
+}
+
+using DescHandlerFunc = void (*)(const std::string& name,
+ const base::Value* value,
+ bool value_only);
+std::map<std::string, DescHandlerFunc> GetHandlers() {
+ return {{"type", LabelHandler},
+ {"toolchain", LabelHandler},
+ {variables::kVisibility, VisibilityHandler},
+ {variables::kMetadata, MetadataHandler},
+ {variables::kTestonly, DefaultHandler},
+ {variables::kCheckIncludes, DefaultHandler},
+ {variables::kAllowCircularIncludesFrom, DefaultHandler},
+ {variables::kSources, DefaultHandler},
+ {variables::kPublic, PublicHandler},
+ {variables::kInputs, DefaultHandler},
+ {variables::kConfigs, ConfigsHandler},
+ {variables::kPublicConfigs, ConfigsHandler},
+ {variables::kAllDependentConfigs, ConfigsHandler},
+ {variables::kScript, DefaultHandler},
+ {variables::kArgs, DefaultHandler},
+ {variables::kDepfile, DefaultHandler},
+ {"bundle_data", DefaultHandler},
+ {variables::kArflags, DefaultHandler},
+ {variables::kAsmflags, DefaultHandler},
+ {variables::kCflags, DefaultHandler},
+ {variables::kCflagsC, DefaultHandler},
+ {variables::kCflagsCC, DefaultHandler},
+ {variables::kCflagsObjC, DefaultHandler},
+ {variables::kCflagsObjCC, DefaultHandler},
+ {variables::kDefines, DefaultHandler},
+ {variables::kFrameworkDirs, DefaultHandler},
+ {variables::kFrameworks, DefaultHandler},
+ {variables::kIncludeDirs, DefaultHandler},
+ {variables::kLdflags, DefaultHandler},
+ {variables::kPrecompiledHeader, DefaultHandler},
+ {variables::kPrecompiledSource, DefaultHandler},
+ {variables::kDeps, DepsHandler},
+ {variables::kLibs, DefaultHandler},
+ {variables::kLibDirs, DefaultHandler},
+ {variables::kDataKeys, DefaultHandler},
+ {variables::kRebase, DefaultHandler},
+ {variables::kWalkKeys, DefaultHandler},
+ {variables::kWeakFrameworks, DefaultHandler},
+ {variables::kWriteOutputConversion, DefaultHandler},
+ {variables::kRustCrateName, DefaultHandler},
+ {variables::kRustCrateRoot, DefaultHandler},
+ {variables::kSwiftModuleName, DefaultHandler},
+ {variables::kSwiftBridgeHeader, DefaultHandler},
+ {"runtime_deps", DefaultHandler}};
+}
+
+void HandleProperty(const std::string& what,
+ const std::map<std::string, DescHandlerFunc>& handler_map,
+ std::unique_ptr<base::Value>& v,
+ std::unique_ptr<base::DictionaryValue>& dict) {
+ if (dict->Remove(what, &v)) {
+ auto pair = handler_map.find(what);
+ if (pair != handler_map.end())
+ pair->second(what, v.get(), false);
+ }
+}
+
+bool PrintTarget(const Target* target,
+ const std::string& what,
+ bool single_target,
+ const std::map<std::string, DescHandlerFunc>& handler_map,
+ bool all,
+ bool tree,
+ bool blame) {
+ std::unique_ptr<base::DictionaryValue> dict =
+ DescBuilder::DescriptionForTarget(target, what, all, tree, blame);
+ if (!what.empty() && dict->empty()) {
+ OutputString("Don't know how to display \"" + what + "\" for \"" +
+ Target::GetStringForOutputType(target->output_type()) +
+ "\".\n");
+ return false;
+ }
+ // Print single value
+ if (!what.empty() && dict->size() == 1 && single_target) {
+ if (what == variables::kOutputs) {
+ ProcessOutputs(dict.get(), true);
+ return true;
+ }
+ base::DictionaryValue::Iterator iter(*dict);
+ auto pair = handler_map.find(what);
+ if (pair != handler_map.end())
+ pair->second(what, &iter.value(), true);
+ return true;
+ }
+
+ OutputString("Target ", DECORATION_YELLOW);
+ OutputString(target->label().GetUserVisibleName(false));
+ OutputString("\n");
+
+ std::unique_ptr<base::Value> v;
+ // Entries with DefaultHandler are present to enforce order
+ HandleProperty("type", handler_map, v, dict);
+ HandleProperty("toolchain", handler_map, v, dict);
+ HandleProperty(variables::kSwiftModuleName, handler_map, v, dict);
+ HandleProperty(variables::kRustCrateName, handler_map, v, dict);
+ HandleProperty(variables::kRustCrateRoot, handler_map, v, dict);
+ HandleProperty(variables::kVisibility, handler_map, v, dict);
+ HandleProperty(variables::kMetadata, handler_map, v, dict);
+ HandleProperty(variables::kTestonly, handler_map, v, dict);
+ HandleProperty(variables::kCheckIncludes, handler_map, v, dict);
+ HandleProperty(variables::kAllowCircularIncludesFrom, handler_map, v, dict);
+ HandleProperty(variables::kSources, handler_map, v, dict);
+ HandleProperty(variables::kSwiftBridgeHeader, handler_map, v, dict);
+ HandleProperty(variables::kPublic, handler_map, v, dict);
+ HandleProperty(variables::kInputs, handler_map, v, dict);
+ HandleProperty(variables::kConfigs, handler_map, v, dict);
+ HandleProperty(variables::kPublicConfigs, handler_map, v, dict);
+ HandleProperty(variables::kAllDependentConfigs, handler_map, v, dict);
+ HandleProperty(variables::kScript, handler_map, v, dict);
+ HandleProperty(variables::kArgs, handler_map, v, dict);
+ HandleProperty(variables::kDepfile, handler_map, v, dict);
+ ProcessOutputs(dict.get(), false);
+ HandleProperty("bundle_data", handler_map, v, dict);
+ HandleProperty(variables::kArflags, handler_map, v, dict);
+ HandleProperty(variables::kAsmflags, handler_map, v, dict);
+ HandleProperty(variables::kCflags, handler_map, v, dict);
+ HandleProperty(variables::kCflagsC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsCC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
+ HandleProperty(variables::kSwiftflags, handler_map, v, dict);
+ HandleProperty(variables::kDefines, handler_map, v, dict);
+ HandleProperty(variables::kFrameworkDirs, handler_map, v, dict);
+ HandleProperty(variables::kFrameworks, handler_map, v, dict);
+ HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
+ HandleProperty(variables::kLdflags, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
+ HandleProperty(variables::kDeps, handler_map, v, dict);
+ HandleProperty(variables::kLibs, handler_map, v, dict);
+ HandleProperty(variables::kLibDirs, handler_map, v, dict);
+ HandleProperty(variables::kDataKeys, handler_map, v, dict);
+ HandleProperty(variables::kRebase, handler_map, v, dict);
+ HandleProperty(variables::kWalkKeys, handler_map, v, dict);
+ HandleProperty(variables::kWeakFrameworks, handler_map, v, dict);
+ HandleProperty(variables::kWriteOutputConversion, handler_map, v, dict);
+
+#undef HandleProperty
+
+ // Process the rest (if any)
+ base::DictionaryValue::Iterator iter(*dict);
+ while (!iter.IsAtEnd()) {
+ DefaultHandler(iter.key(), &iter.value(), false);
+ iter.Advance();
+ }
+
+ return true;
+}
+
+bool PrintConfig(const Config* config,
+ const std::string& what,
+ bool single_config,
+ const std::map<std::string, DescHandlerFunc>& handler_map) {
+ std::unique_ptr<base::DictionaryValue> dict =
+ DescBuilder::DescriptionForConfig(config, what);
+ if (!what.empty() && dict->empty()) {
+ OutputString("Don't know how to display \"" + what + "\" for a config.\n");
+ return false;
+ }
+ // Print single value
+ if (!what.empty() && dict->size() == 1 && single_config) {
+ base::DictionaryValue::Iterator iter(*dict);
+ auto pair = handler_map.find(what);
+ if (pair != handler_map.end())
+ pair->second(what, &iter.value(), true);
+ return true;
+ }
+
+ OutputString("Config: ", DECORATION_YELLOW);
+ OutputString(config->label().GetUserVisibleName(false));
+ OutputString("\n");
+
+ std::unique_ptr<base::Value> v;
+ HandleProperty("toolchain", handler_map, v, dict);
+ if (!config->configs().empty()) {
+ OutputString(
+ "(This is a composite config, the values below are after the\n"
+ "expansion of the child configs.)\n");
+ }
+ HandleProperty(variables::kArflags, handler_map, v, dict);
+ HandleProperty(variables::kAsmflags, handler_map, v, dict);
+ HandleProperty(variables::kCflags, handler_map, v, dict);
+ HandleProperty(variables::kCflagsC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsCC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
+ HandleProperty(variables::kSwiftflags, handler_map, v, dict);
+ HandleProperty(variables::kDefines, handler_map, v, dict);
+ HandleProperty(variables::kFrameworkDirs, handler_map, v, dict);
+ HandleProperty(variables::kFrameworks, handler_map, v, dict);
+ HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
+ HandleProperty(variables::kInputs, handler_map, v, dict);
+ HandleProperty(variables::kLdflags, handler_map, v, dict);
+ HandleProperty(variables::kLibs, handler_map, v, dict);
+ HandleProperty(variables::kLibDirs, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
+ HandleProperty(variables::kWeakFrameworks, handler_map, v, dict);
+
+#undef HandleProperty
+
+ return true;
+}
+
+} // namespace
+
+// desc ------------------------------------------------------------------------
+
+const char kDesc[] = "desc";
+const char kDesc_HelpShort[] =
+ "desc: Show lots of insightful information about a target or config.";
+const char kDesc_Help[] =
+ R"(gn desc
+
+ gn desc <out_dir> <label or pattern> [<what to show>] [--blame]
+ [--format=json]
+
+ Displays information about a given target or config. The build parameters
+ will be taken for the build in the given <out_dir>.
+
+ The <label or pattern> can be a target label, a config label, or a label
+ pattern (see "gn help label_pattern"). A label pattern will only match
+ targets.
+
+Possibilities for <what to show>
+
+ (If unspecified an overall summary will be displayed.)
+
+ all_dependent_configs
+ allow_circular_includes_from
+ arflags [--blame]
+ args
+ cflags [--blame]
+ cflags_c [--blame]
+ cflags_cc [--blame]
+ check_includes
+ configs [--tree] (see below)
+ data_keys
+ defines [--blame]
+ depfile
+ deps [--all] [--tree] (see below)
+ framework_dirs
+ frameworks
+ include_dirs [--blame]
+ inputs
+ ldflags [--blame]
+ lib_dirs
+ libs
+ metadata
+ output_conversion
+ outputs
+ public_configs
+ public
+ rebase
+ script
+ sources
+ testonly
+ visibility
+ walk_keys
+ weak_frameworks
+
+ runtime_deps
+ Compute all runtime deps for the given target. This is a computed list
+ and does not correspond to any GN variable, unlike most other values
+ here.
+
+ The output is a list of file names relative to the build directory. See
+ "gn help runtime_deps" for how this is computed. This also works with
+ "--blame" to see the source of the dependency.
+
+Shared flags
+
+)"
+
+ DEFAULT_TOOLCHAIN_SWITCH_HELP
+
+ R"(
+ --format=json
+ Format the output as JSON instead of text.
+
+Target flags
+
+ --blame
+ Used with any value specified on a config, this will name the config that
+ causes that target to get the flag. This doesn't currently work for libs,
+ lib_dirs, frameworks, weak_frameworks and framework_dirs because those are
+ inherited and are more complicated to figure out the blame (patches
+ welcome).
+
+Configs
+
+ The "configs" section will list all configs that apply. For targets this will
+ include configs specified in the "configs" variable of the target, and also
+ configs pushed onto this target via public or "all dependent" configs.
+
+ Configs can have child configs. Specifying --tree will show the hierarchy.
+
+Printing outputs
+
+ The "outputs" section will list all outputs that apply, including the outputs
+ computed from the tool definition (eg for "executable", "static_library", ...
+ targets).
+
+Printing deps
+
+ Deps will include all public, private, and data deps (TODO this could be
+ clarified and enhanced) sorted in order applying. The following may be used:
+
+ --all
+ Collects all recursive dependencies and prints a sorted flat list. Also
+ usable with --tree (see below).
+
+)"
+
+ TARGET_PRINTING_MODE_COMMAND_LINE_HELP
+ "\n" TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
+
+ R"(
+ --tree
+ Print a dependency tree. By default, duplicates will be elided with "..."
+ but when --all and -tree are used together, no eliding will be performed.
+
+ The "deps", "public_deps", and "data_deps" will all be included in the
+ tree.
+
+ Tree output can not be used with the filtering or output flags: --as,
+ --type, --testonly.
+
+)"
+
+ TARGET_TYPE_FILTER_COMMAND_LINE_HELP
+
+ R"(
+Note
+
+ This command will show the full name of directories and source files, but
+ when directories and source paths are written to the build file, they will be
+ adjusted to be relative to the build directory. So the values for paths
+ displayed by this command won't match (but should mean the same thing).
+
+Examples
+
+ gn desc out/Debug //base:base
+ Summarizes the given target.
+
+ gn desc out/Foo :base_unittests deps --tree
+ Shows a dependency tree of the "base_unittests" project in
+ the current directory.
+
+ gn desc out/Debug //base defines --blame
+ Shows defines set for the //base:base target, annotated by where
+ each one was set from.
+)";
+
+int RunDesc(const std::vector<std::string>& args) {
+ if (args.size() != 2 && args.size() != 3) {
+ Err(Location(), "Unknown command format. See \"gn help desc\"",
+ "Usage: \"gn desc <out_dir> <target_name> [<what to display>]\"")
+ .PrintToStdout();
+ return 1;
+ }
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false))
+ return 1;
+ if (!setup->Run())
+ return 1;
+
+ // Resolve target(s) and config from inputs.
+ UniqueVector<const Target*> target_matches;
+ UniqueVector<const Config*> config_matches;
+ UniqueVector<const Toolchain*> toolchain_matches;
+ UniqueVector<SourceFile> file_matches;
+
+ std::vector<std::string> target_list;
+ target_list.push_back(args[1]);
+
+ if (!ResolveFromCommandLineInput(
+ setup, target_list, cmdline->HasSwitch(switches::kDefaultToolchain),
+ &target_matches, &config_matches, &toolchain_matches, &file_matches))
+ return 1;
+
+ std::string what_to_print;
+ if (args.size() == 3)
+ what_to_print = args[2];
+
+ bool json = cmdline->GetSwitchValueASCII("format") == "json";
+
+ if (target_matches.empty() && config_matches.empty()) {
+ OutputString(
+ "The input " + args[1] + " matches no targets, configs or files.\n",
+ DECORATION_YELLOW);
+ return 1;
+ }
+
+ if (json) {
+ // Convert all targets/configs to JSON, serialize and print them
+ auto res = std::make_unique<base::DictionaryValue>();
+ if (!target_matches.empty()) {
+ for (const auto* target : target_matches) {
+ res->SetWithoutPathExpansion(
+ target->label().GetUserVisibleName(
+ target->settings()->default_toolchain_label()),
+ DescBuilder::DescriptionForTarget(
+ target, what_to_print, cmdline->HasSwitch(kAll),
+ cmdline->HasSwitch(kTree), cmdline->HasSwitch(kBlame)));
+ }
+ } else if (!config_matches.empty()) {
+ for (const auto* config : config_matches) {
+ res->SetWithoutPathExpansion(
+ config->label().GetUserVisibleName(false),
+ DescBuilder::DescriptionForConfig(config, what_to_print));
+ }
+ }
+ std::string s;
+ base::JSONWriter::WriteWithOptions(
+ *res.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
+ OutputString(s);
+ } else {
+ // Regular (non-json) formatted output
+ bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1;
+ std::map<std::string, DescHandlerFunc> handlers = GetHandlers();
+
+ bool printed_output = false;
+ for (const Target* target : target_matches) {
+ if (printed_output)
+ OutputString("\n\n");
+ printed_output = true;
+
+ if (!PrintTarget(target, what_to_print, !multiple_outputs, handlers,
+ cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree),
+ cmdline->HasSwitch(kBlame)))
+ return 1;
+ }
+ for (const Config* config : config_matches) {
+ if (printed_output)
+ OutputString("\n\n");
+ printed_output = true;
+
+ if (!PrintConfig(config, what_to_print, !multiple_outputs, handlers))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_format.cc b/gn/src/gn/command_format.cc
new file mode 100644
index 00000000000..e94fb4fbc6d
--- /dev/null
+++ b/gn/src/gn/command_format.cc
@@ -0,0 +1,1437 @@
+// 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 "gn/command_format.h"
+
+#include <stddef.h>
+
+#include <sstream>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "gn/commands.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file.h"
+#include "gn/parser.h"
+#include "gn/scheduler.h"
+#include "gn/setup.h"
+#include "gn/source_file.h"
+#include "gn/string_utils.h"
+#include "gn/switches.h"
+#include "gn/tokenizer.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+namespace commands {
+
+const char kSwitchDryRun[] = "dry-run";
+const char kSwitchDumpTree[] = "dump-tree";
+const char kSwitchReadTree[] = "read-tree";
+const char kSwitchStdin[] = "stdin";
+const char kSwitchTreeTypeJSON[] = "json";
+const char kSwitchTreeTypeText[] = "text";
+
+const char kFormat[] = "format";
+const char kFormat_HelpShort[] = "format: Format .gn files.";
+const char kFormat_Help[] =
+ R"(gn format [--dump-tree] (--stdin | <list of build_files...>)
+
+ Formats .gn file to a standard format.
+
+ The contents of some lists ('sources', 'deps', etc.) will be sorted to a
+ canonical order. To suppress this, you can add a comment of the form "#
+ NOSORT" immediately preceding the assignment. e.g.
+
+ # NOSORT
+ sources = [
+ "z.cc",
+ "a.cc",
+ ]
+
+Arguments
+
+ --dry-run
+ Prints the list of files that would be reformatted but does not write
+ anything to disk. This is useful for presubmit/lint-type checks.
+ - Exit code 0: successful format, matches on disk.
+ - Exit code 1: general failure (parse error, etc.)
+ - Exit code 2: successful format, but differs from on disk.
+
+ --dump-tree[=( text | json )]
+ Dumps the parse tree to stdout and does not update the file or print
+ formatted output. If no format is specified, text format will be used.
+
+ --stdin
+ Read input from stdin and write to stdout rather than update a file
+ in-place.
+
+ --read-tree=json
+ Reads an AST from stdin in the format output by --dump-tree=json and
+ uses that as the parse tree. (The only read-tree format currently
+ supported is json.) The given .gn file will be overwritten. This can be
+ used to programmatically transform .gn files.
+
+Examples
+ gn format //some/BUILD.gn //some/other/BUILD.gn //and/another/BUILD.gn
+ gn format some\\BUILD.gn
+ gn format /abspath/some/BUILD.gn
+ gn format --stdin
+ gn format --read-tree=json //rewritten/BUILD.gn
+)";
+
+namespace {
+
+const int kIndentSize = 2;
+const int kMaximumWidth = 80;
+
+const int kPenaltyLineBreak = 500;
+const int kPenaltyHorizontalSeparation = 100;
+const int kPenaltyExcess = 10000;
+const int kPenaltyBrokenLineOnOneLiner = 5000;
+
+enum Precedence {
+ kPrecedenceLowest,
+ kPrecedenceAssign,
+ kPrecedenceOr,
+ kPrecedenceAnd,
+ kPrecedenceCompare,
+ kPrecedenceAdd,
+ kPrecedenceUnary,
+ kPrecedenceSuffix,
+};
+
+int CountLines(const std::string& str) {
+ return static_cast<int>(base::SplitStringPiece(str, "\n",
+ base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_ALL)
+ .size());
+}
+
+class Printer {
+ public:
+ Printer();
+ ~Printer();
+
+ void Block(const ParseNode* file);
+
+ std::string String() const { return output_; }
+
+ private:
+ // Format a list of values using the given style.
+ enum SequenceStyle {
+ kSequenceStyleList,
+ kSequenceStyleBracedBlock,
+ kSequenceStyleBracedBlockAlreadyOpen,
+ };
+
+ struct Metrics {
+ Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
+ int first_length;
+ int longest_length;
+ bool multiline;
+ };
+
+ // Add to output.
+ void Print(std::string_view str);
+
+ // Add the current margin (as spaces) to the output.
+ void PrintMargin();
+
+ void TrimAndPrintToken(const Token& token);
+
+ void PrintTrailingCommentsWrapped(const std::vector<Token>& comments);
+
+ void FlushComments();
+
+ void PrintSuffixComments(const ParseNode* node);
+
+ // End the current line, flushing end of line comments.
+ void Newline();
+
+ // Remove trailing spaces from the current line.
+ void Trim();
+
+ // Whether there's a blank separator line at the current position.
+ bool HaveBlankLine();
+
+ // Sort a list on the RHS if the LHS is one of the following:
+ // 'sources': sorted alphabetically.
+ // 'deps' or ends in 'deps': sorted such that relative targets are first,
+ // followed by global targets, each internally sorted alphabetically.
+ // 'visibility': same as 'deps'.
+ void SortIfApplicable(const BinaryOpNode* binop);
+
+ // Sort contiguous import() function calls in the given ordered list of
+ // statements (the body of a block or scope).
+ template <class PARSENODE>
+ void SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements);
+
+ // Heuristics to decide if there should be a blank line added between two
+ // items. For various "small" items, it doesn't look nice if there's too much
+ // vertical whitespace added.
+ bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
+
+ // Get the 0-based x position on the current line.
+ int CurrentColumn() const;
+
+ // Get the current line in the output;
+ int CurrentLine() const;
+
+ // Adds an opening ( if prec is less than the outers (to maintain evalution
+ // order for a subexpression). If an opening paren is emitted, *parenthesized
+ // will be set so it can be closed at the end of the expression.
+ void AddParen(int prec, int outer_prec, bool* parenthesized);
+
+ // Print the expression given by |root| to the output buffer and appends
+ // |suffix| to that output. Returns a penalty that represents the cost of
+ // adding that output to the buffer (where higher is worse). The value of
+ // outer_prec gives the precedence of the operator outside this Expr. If that
+ // operator binds tighter than root's, Expr() must introduce parentheses.
+ int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
+
+ // Generic penalties for exceeding maximum width, adding more lines, etc.
+ int AssessPenalty(const std::string& output);
+
+ // Tests if any lines exceed the maximum width.
+ bool ExceedsMaximumWidth(const std::string& output);
+
+ // Format a list of values using the given style.
+ // |end| holds any trailing comments to be printed just before the closing
+ // bracket.
+ template <class PARSENODE> // Just for const covariance.
+ void Sequence(SequenceStyle style,
+ const std::vector<std::unique_ptr<PARSENODE>>& list,
+ const ParseNode* end,
+ bool force_multiline);
+
+ // Returns the penalty.
+ int FunctionCall(const FunctionCallNode* func_call,
+ const std::string& suffix);
+
+ // Create a clone of this Printer in a similar state (other than the output,
+ // but including margins, etc.) to be used for dry run measurements.
+ void InitializeSub(Printer* sub);
+
+ template <class PARSENODE>
+ bool ListWillBeMultiline(const std::vector<std::unique_ptr<PARSENODE>>& list,
+ const ParseNode* end);
+
+ std::string output_; // Output buffer.
+ std::vector<Token> comments_; // Pending end-of-line comments.
+ int margin() const { return stack_.back().margin; }
+
+ int penalty_depth_;
+ int GetPenaltyForLineBreak() const {
+ return penalty_depth_ * kPenaltyLineBreak;
+ }
+
+ struct IndentState {
+ IndentState()
+ : margin(0),
+ continuation_requires_indent(false),
+ parent_is_boolean_or(false) {}
+ IndentState(int margin,
+ bool continuation_requires_indent,
+ bool parent_is_boolean_or)
+ : margin(margin),
+ continuation_requires_indent(continuation_requires_indent),
+ parent_is_boolean_or(parent_is_boolean_or) {}
+
+ // The left margin (number of spaces).
+ int margin;
+
+ bool continuation_requires_indent;
+
+ bool parent_is_boolean_or;
+ };
+ // Stack used to track
+ std::vector<IndentState> stack_;
+
+ // Gives the precedence for operators in a BinaryOpNode.
+ std::map<std::string_view, Precedence> precedence_;
+
+ DISALLOW_COPY_AND_ASSIGN(Printer);
+};
+
+Printer::Printer() : penalty_depth_(0) {
+ output_.reserve(100 << 10);
+ precedence_["="] = kPrecedenceAssign;
+ precedence_["+="] = kPrecedenceAssign;
+ precedence_["-="] = kPrecedenceAssign;
+ precedence_["||"] = kPrecedenceOr;
+ precedence_["&&"] = kPrecedenceAnd;
+ precedence_["<"] = kPrecedenceCompare;
+ precedence_[">"] = kPrecedenceCompare;
+ precedence_["=="] = kPrecedenceCompare;
+ precedence_["!="] = kPrecedenceCompare;
+ precedence_["<="] = kPrecedenceCompare;
+ precedence_[">="] = kPrecedenceCompare;
+ precedence_["+"] = kPrecedenceAdd;
+ precedence_["-"] = kPrecedenceAdd;
+ precedence_["!"] = kPrecedenceUnary;
+ stack_.push_back(IndentState());
+}
+
+Printer::~Printer() = default;
+
+void Printer::Print(std::string_view str) {
+ output_.append(str);
+}
+
+void Printer::PrintMargin() {
+ output_ += std::string(margin(), ' ');
+}
+
+void Printer::TrimAndPrintToken(const Token& token) {
+ std::string trimmed;
+ TrimWhitespaceASCII(std::string(token.value()), base::TRIM_ALL, &trimmed);
+ Print(trimmed);
+}
+
+// Assumes that the margin is set to the indent level where the comments should
+// be aligned. This doesn't de-wrap, it only wraps. So if a suffix comment
+// causes the line to exceed 80 col it will be wrapped, but the subsequent line
+// would fit on the then-broken line it will not be merged with it. This is
+// partly because it's difficult to implement at this level, but also because
+// it can break hand-authored line breaks where they're starting a new paragraph
+// or statement.
+void Printer::PrintTrailingCommentsWrapped(const std::vector<Token>& comments) {
+ bool have_empty_line = true;
+ auto start_next_line = [this, &have_empty_line]() {
+ Trim();
+ Print("\n");
+ PrintMargin();
+ have_empty_line = true;
+ };
+ for (const auto& c : comments) {
+ if (!have_empty_line) {
+ start_next_line();
+ }
+
+ std::string trimmed;
+ TrimWhitespaceASCII(std::string(c.value()), base::TRIM_ALL, &trimmed);
+
+ if (margin() + trimmed.size() <= kMaximumWidth) {
+ Print(trimmed);
+ have_empty_line = false;
+ } else {
+ bool continuation = false;
+ std::vector<std::string> split_on_spaces = base::SplitString(
+ c.value(), " ", base::WhitespaceHandling::TRIM_WHITESPACE,
+ base::SplitResult::SPLIT_WANT_NONEMPTY);
+ for (size_t j = 0; j < split_on_spaces.size(); ++j) {
+ if (have_empty_line && continuation) {
+ Print("# ");
+ }
+ Print(split_on_spaces[j]);
+ Print(" ");
+ if (split_on_spaces[j] != "#") {
+ have_empty_line = false;
+ }
+ if (!have_empty_line &&
+ (j < split_on_spaces.size() - 1 &&
+ CurrentColumn() + split_on_spaces[j + 1].size() > kMaximumWidth)) {
+ start_next_line();
+ continuation = true;
+ }
+ }
+ }
+ }
+}
+
+// Used during penalty evaluation, similar to Newline().
+void Printer::PrintSuffixComments(const ParseNode* node) {
+ if (node->comments() && !node->comments()->suffix().empty()) {
+ Print(" ");
+ stack_.push_back(IndentState(CurrentColumn(), false, false));
+ PrintTrailingCommentsWrapped(node->comments()->suffix());
+ stack_.pop_back();
+ }
+}
+
+void Printer::FlushComments() {
+ if (!comments_.empty()) {
+ Print(" ");
+ // Save the margin, and temporarily set it to where the first comment
+ // starts so that multiple suffix comments are vertically aligned.
+ stack_.push_back(IndentState(CurrentColumn(), false, false));
+ PrintTrailingCommentsWrapped(comments_);
+ stack_.pop_back();
+ comments_.clear();
+ }
+}
+
+void Printer::Newline() {
+ FlushComments();
+ Trim();
+ Print("\n");
+ PrintMargin();
+}
+
+void Printer::Trim() {
+ size_t n = output_.size();
+ while (n > 0 && output_[n - 1] == ' ')
+ --n;
+ output_.resize(n);
+}
+
+bool Printer::HaveBlankLine() {
+ size_t n = output_.size();
+ while (n > 0 && output_[n - 1] == ' ')
+ --n;
+ return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
+}
+
+void Printer::SortIfApplicable(const BinaryOpNode* binop) {
+ if (const Comments* comments = binop->comments()) {
+ const std::vector<Token>& before = comments->before();
+ if (!before.empty() && (before.front().value() == "# NOSORT" ||
+ before.back().value() == "# NOSORT")) {
+ // Allow disabling of sort for specific actions that might be
+ // order-sensitive.
+ return;
+ }
+ }
+ const IdentifierNode* ident = binop->left()->AsIdentifier();
+ const ListNode* list = binop->right()->AsList();
+ if ((binop->op().value() == "=" || binop->op().value() == "+=" ||
+ binop->op().value() == "-=") &&
+ ident && list) {
+ const std::string_view lhs = ident->value().value();
+ if (base::EndsWith(lhs, "sources", base::CompareCase::SENSITIVE) ||
+ lhs == "public")
+ const_cast<ListNode*>(list)->SortAsStringsList();
+ else if (base::EndsWith(lhs, "deps", base::CompareCase::SENSITIVE) ||
+ lhs == "visibility")
+ const_cast<ListNode*>(list)->SortAsTargetsList();
+ }
+}
+
+template <class PARSENODE>
+void Printer::SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements) {
+ // Build a set of ranges by indices of FunctionCallNode's that are imports.
+
+ std::vector<std::vector<size_t>> import_statements;
+
+ auto is_import = [](const PARSENODE* p) {
+ const FunctionCallNode* func_call = p->AsFunctionCall();
+ return func_call && func_call->function().value() == "import";
+ };
+
+ std::vector<size_t> current_group;
+ for (size_t i = 0; i < statements.size(); ++i) {
+ if (is_import(statements[i].get())) {
+ if (i > 0 && (!is_import(statements[i - 1].get()) ||
+ ShouldAddBlankLineInBetween(statements[i - 1].get(),
+ statements[i].get()))) {
+ if (!current_group.empty()) {
+ import_statements.push_back(current_group);
+ current_group.clear();
+ }
+ }
+ current_group.push_back(i);
+ }
+ }
+
+ if (!current_group.empty())
+ import_statements.push_back(current_group);
+
+ struct CompareByImportFile {
+ bool operator()(const std::unique_ptr<PARSENODE>& a,
+ const std::unique_ptr<PARSENODE>& b) const {
+ const auto& a_args = a->AsFunctionCall()->args()->contents();
+ const auto& b_args = b->AsFunctionCall()->args()->contents();
+ std::string_view a_name;
+ std::string_view b_name;
+
+ // Non-literal imports are treated as empty names, and order is
+ // maintained. Arbitrarily complex expressions in import() are
+ // rare, and it probably doesn't make sense to sort non-string
+ // literals anyway, see format_test_data/083.gn.
+ if (!a_args.empty() && a_args[0]->AsLiteral())
+ a_name = a_args[0]->AsLiteral()->value().value();
+ if (!b_args.empty() && b_args[0]->AsLiteral())
+ b_name = b_args[0]->AsLiteral()->value().value();
+
+ auto is_absolute = [](std::string_view import) {
+ return import.size() >= 3 && import[0] == '"' && import[1] == '/' &&
+ import[2] == '/';
+ };
+ int a_is_rel = !is_absolute(a_name);
+ int b_is_rel = !is_absolute(b_name);
+
+ return std::tie(a_is_rel, a_name) < std::tie(b_is_rel, b_name);
+ }
+ };
+
+ int line_after_previous = -1;
+
+ for (const auto& group : import_statements) {
+ size_t begin = group[0];
+ size_t end = group.back() + 1;
+
+ // Save the original line number so that ranges can be re-assigned. They're
+ // contiguous because of the partitioning code above. Later formatting
+ // relies on correct line number to know whether to insert blank lines,
+ // which is why these need to be fixed up. Additionally, to handle multiple
+ // imports on one line, they're assigned sequential line numbers, and
+ // subsequent blocks will be gapped from them.
+ int start_line =
+ std::max(statements[begin]->GetRange().begin().line_number(),
+ line_after_previous + 1);
+
+ std::sort(statements.begin() + begin, statements.begin() + end,
+ CompareByImportFile());
+
+ const PARSENODE* prev = nullptr;
+ for (size_t i = begin; i < end; ++i) {
+ const PARSENODE* node = statements[i].get();
+ int line_number =
+ prev ? prev->GetRange().end().line_number() + 1 : start_line;
+ if (node->comments() && !node->comments()->before().empty())
+ line_number++;
+ const_cast<FunctionCallNode*>(node->AsFunctionCall())
+ ->SetNewLocation(line_number);
+ prev = node;
+ line_after_previous = line_number + 1;
+ }
+ }
+}
+
+namespace {
+
+int SuffixCommentTreeWalk(const ParseNode* node) {
+ // Check all the children for suffix comments. This is conceptually simple,
+ // but ugly as there's not a generic parse tree walker. This walker goes
+ // lowest child first so that if it's valid that's returned.
+ if (!node)
+ return -1;
+
+#define RETURN_IF_SET(x) \
+ if (int result = (x); result >= 0) \
+ return result;
+
+ if (const AccessorNode* accessor = node->AsAccessor()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(accessor->subscript()));
+ RETURN_IF_SET(SuffixCommentTreeWalk(accessor->member()));
+ } else if (const BinaryOpNode* binop = node->AsBinaryOp()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(binop->right()));
+ } else if (const BlockNode* block = node->AsBlock()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(block->End()));
+ } else if (const ConditionNode* condition = node->AsCondition()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(condition->if_false()));
+ RETURN_IF_SET(SuffixCommentTreeWalk(condition->if_true()));
+ RETURN_IF_SET(SuffixCommentTreeWalk(condition->condition()));
+ } else if (const FunctionCallNode* func_call = node->AsFunctionCall()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(func_call->block()));
+ RETURN_IF_SET(SuffixCommentTreeWalk(func_call->args()));
+ } else if (node->AsIdentifier()) {
+ // Nothing.
+ } else if (const ListNode* list = node->AsList()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(list->End()));
+ } else if (node->AsLiteral()) {
+ // Nothing.
+ } else if (const UnaryOpNode* unaryop = node->AsUnaryOp()) {
+ RETURN_IF_SET(SuffixCommentTreeWalk(unaryop->operand()));
+ } else if (node->AsBlockComment()) {
+ // Nothing.
+ } else if (node->AsEnd()) {
+ // Nothing.
+ } else {
+ CHECK(false) << "Unhandled case in SuffixCommentTreeWalk.";
+ }
+
+#undef RETURN_IF_SET
+
+ // Check this node if there are no child comments.
+ if (node->comments() && !node->comments()->suffix().empty()) {
+ return node->comments()->suffix().back().location().line_number();
+ }
+
+ return -1;
+};
+
+// If there are suffix comments on the first node or its children, they might
+// carry down multiple lines. Otherwise, use the node's normal end range. This
+// function is needed because the parse tree doesn't include comments in the
+// location ranges, and it's not a straightforword change to add them. So this
+// is effectively finding the "real" range for |root| including suffix comments.
+// Note that it's not enough to simply look at |root|'s suffix comments because
+// in the case of:
+//
+// a =
+// b + c # something
+// # or other
+// x = y
+//
+// the comments are attached to a BinOp+ which is a child of BinOp=, not
+// directly to the BinOp= which will be what's being used to determine if there
+// should be a blank line inserted before the |x| line.
+int FindLowestSuffixComment(const ParseNode* root) {
+ LocationRange range = root->GetRange();
+ int end = range.end().line_number();
+ int result = SuffixCommentTreeWalk(root);
+ return (result == -1 || result < end) ? end : result;
+}
+
+} // namespace
+
+bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
+ const ParseNode* b) {
+ LocationRange b_range = b->GetRange();
+ int a_end = FindLowestSuffixComment(a);
+
+ // If they're already separated by 1 or more lines, then we want to keep a
+ // blank line.
+ return (b_range.begin().line_number() > a_end + 1) ||
+ // Always put a blank line before a block comment.
+ b->AsBlockComment();
+}
+
+int Printer::CurrentColumn() const {
+ int n = 0;
+ while (n < static_cast<int>(output_.size()) &&
+ output_[output_.size() - 1 - n] != '\n') {
+ ++n;
+ }
+ return n;
+}
+
+int Printer::CurrentLine() const {
+ int count = 1;
+ for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != nullptr;) {
+ ++count;
+ ++p;
+ }
+ return count;
+}
+
+void Printer::Block(const ParseNode* root) {
+ const BlockNode* block = root->AsBlock();
+
+ if (block->comments()) {
+ for (const auto& c : block->comments()->before()) {
+ TrimAndPrintToken(c);
+ Newline();
+ }
+ }
+
+ SortImports(const_cast<std::vector<std::unique_ptr<ParseNode>>&>(
+ block->statements()));
+
+ size_t i = 0;
+ for (const auto& stmt : block->statements()) {
+ Expr(stmt.get(), kPrecedenceLowest, std::string());
+ Newline();
+ if (stmt->comments()) {
+ // Why are before() not printed here too? before() are handled inside
+ // Expr(), as are suffix() which are queued to the next Newline().
+ // However, because it's a general expression handler, it doesn't insert
+ // the newline itself, which only happens between block statements. So,
+ // the after are handled explicitly here.
+ for (const auto& c : stmt->comments()->after()) {
+ TrimAndPrintToken(c);
+ Newline();
+ }
+ }
+ if (i < block->statements().size() - 1 &&
+ (ShouldAddBlankLineInBetween(block->statements()[i].get(),
+ block->statements()[i + 1].get()))) {
+ Newline();
+ }
+ ++i;
+ }
+
+ if (block->comments()) {
+ if (!block->statements().empty() &&
+ block->statements().back()->AsBlockComment()) {
+ // If the block ends in a comment, and there's a comment following it,
+ // then the two comments were originally separate, so keep them that way.
+ Newline();
+ }
+ for (const auto& c : block->comments()->after()) {
+ TrimAndPrintToken(c);
+ Newline();
+ }
+ }
+}
+
+int Printer::AssessPenalty(const std::string& output) {
+ int penalty = 0;
+ std::vector<std::string> lines = base::SplitString(
+ output, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+ penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
+ for (const auto& line : lines) {
+ if (line.size() > kMaximumWidth)
+ penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
+ }
+ return penalty;
+}
+
+bool Printer::ExceedsMaximumWidth(const std::string& output) {
+ for (const auto& line : base::SplitString(output, "\n", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_ALL)) {
+ std::string_view trimmed =
+ TrimString(line, " ", base::TrimPositions::TRIM_TRAILING);
+ if (trimmed.size() > kMaximumWidth) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
+ if (prec < outer_prec) {
+ Print("(");
+ *parenthesized = true;
+ }
+}
+
+int Printer::Expr(const ParseNode* root,
+ int outer_prec,
+ const std::string& suffix) {
+ std::string at_end = suffix;
+ int penalty = 0;
+ penalty_depth_++;
+
+ if (root->comments()) {
+ if (!root->comments()->before().empty()) {
+ Trim();
+ // If there's already other text on the line, start a new line.
+ if (CurrentColumn() > 0)
+ Print("\n");
+ // We're printing a line comment, so we need to be at the current margin.
+ PrintMargin();
+ for (const auto& c : root->comments()->before()) {
+ TrimAndPrintToken(c);
+ Newline();
+ }
+ }
+ }
+
+ bool parenthesized = false;
+
+ if (const AccessorNode* accessor = root->AsAccessor()) {
+ AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
+ Print(accessor->base().value());
+ if (accessor->member()) {
+ Print(".");
+ Expr(accessor->member(), kPrecedenceLowest, std::string());
+ } else {
+ CHECK(accessor->subscript());
+ Print("[");
+ Expr(accessor->subscript(), kPrecedenceLowest, "]");
+ }
+ } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
+ CHECK(precedence_.find(binop->op().value()) != precedence_.end());
+
+ SortIfApplicable(binop);
+
+ Precedence prec = precedence_[binop->op().value()];
+
+ // Since binary operators format left-to-right, it is ok for the left side
+ // use the same operator without parentheses, so the left uses prec. For the
+ // same reason, the right side cannot reuse the same operator, or else "x +
+ // (y + z)" would format as "x + y + z" which means "(x + y) + z". So, treat
+ // the right expression as appearing one precedence level higher.
+ // However, because the source parens are not in the parse tree, as a
+ // special case for && and || we insert strictly-redundant-but-helpful-for-
+ // human-readers parentheses.
+ int prec_left = prec;
+ int prec_right = prec + 1;
+ if (binop->op().value() == "&&" && stack_.back().parent_is_boolean_or) {
+ Print("(");
+ parenthesized = true;
+ } else {
+ AddParen(prec_left, outer_prec, &parenthesized);
+ }
+
+ if (parenthesized)
+ at_end = ")" + at_end;
+
+ int start_line = CurrentLine();
+ int start_column = CurrentColumn();
+ bool is_assignment = binop->op().value() == "=" ||
+ binop->op().value() == "+=" ||
+ binop->op().value() == "-=";
+
+ int indent_column = start_column;
+ if (is_assignment) {
+ // Default to a double-indent for wrapped assignments.
+ indent_column = margin() + kIndentSize * 2;
+
+ // A special case for the long lists and scope assignments that are
+ // common in .gn files, don't indent them + 4, even though they're just
+ // continuations when they're simple lists like "x = [ a, b, c, ... ]" or
+ // scopes like "x = { a = 1 b = 2 }". Put back to "normal" indenting.
+ if (const ListNode* right_as_list = binop->right()->AsList()) {
+ if (ListWillBeMultiline(right_as_list->contents(),
+ right_as_list->End()))
+ indent_column = start_column;
+ } else {
+ if (binop->right()->AsBlock())
+ indent_column = start_column;
+ }
+ }
+ if (stack_.back().continuation_requires_indent)
+ indent_column += kIndentSize * 2;
+
+ stack_.push_back(IndentState(indent_column,
+ stack_.back().continuation_requires_indent,
+ binop->op().value() == "||"));
+ Printer sub_left;
+ InitializeSub(&sub_left);
+ sub_left.Expr(binop->left(), prec_left,
+ std::string(" ") + std::string(binop->op().value()));
+ bool left_is_multiline = CountLines(sub_left.String()) > 1;
+ // Avoid walking the whole left redundantly times (see timing of Format.046)
+ // so pull the output and comments from subprinter.
+ Print(sub_left.String().substr(start_column));
+ std::copy(sub_left.comments_.begin(), sub_left.comments_.end(),
+ std::back_inserter(comments_));
+
+ // Single line.
+ Printer sub1;
+ InitializeSub(&sub1);
+ sub1.Print(" ");
+ int penalty_current_line = sub1.Expr(binop->right(), prec_right, at_end);
+ sub1.PrintSuffixComments(root);
+ sub1.FlushComments();
+ penalty_current_line += AssessPenalty(sub1.String());
+ if (!is_assignment && left_is_multiline) {
+ // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
+ // for trying to continue as if this were one line.
+ penalty_current_line +=
+ (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
+ }
+
+ // Break after operator.
+ Printer sub2;
+ InitializeSub(&sub2);
+ sub2.Newline();
+ int penalty_next_line = sub2.Expr(binop->right(), prec_right, at_end);
+ sub2.PrintSuffixComments(root);
+ sub2.FlushComments();
+ penalty_next_line += AssessPenalty(sub2.String());
+
+ // Force a list on the RHS that would normally be a single line into
+ // multiline.
+ bool tried_rhs_multiline = false;
+ Printer sub3;
+ InitializeSub(&sub3);
+ int penalty_multiline_rhs_list = std::numeric_limits<int>::max();
+ const ListNode* rhs_list = binop->right()->AsList();
+ if (is_assignment && rhs_list &&
+ !ListWillBeMultiline(rhs_list->contents(), rhs_list->End())) {
+ sub3.Print(" ");
+ sub3.stack_.push_back(IndentState(start_column, false, false));
+ sub3.Sequence(kSequenceStyleList, rhs_list->contents(), rhs_list->End(),
+ true);
+ sub3.PrintSuffixComments(root);
+ sub3.FlushComments();
+ sub3.stack_.pop_back();
+ penalty_multiline_rhs_list = AssessPenalty(sub3.String());
+ tried_rhs_multiline = true;
+ }
+
+ // If in all cases it was forced past 80col, then we don't break to avoid
+ // breaking after '=' in the case of:
+ // variable = "... very long string ..."
+ // as breaking and indenting doesn't make things much more readable, even
+ // though there's fewer characters past the maximum width.
+ bool exceeds_maximum_all_ways =
+ ExceedsMaximumWidth(sub1.String()) &&
+ ExceedsMaximumWidth(sub2.String()) &&
+ (!tried_rhs_multiline || ExceedsMaximumWidth(sub3.String()));
+
+ if (penalty_current_line < penalty_next_line || exceeds_maximum_all_ways) {
+ Print(" ");
+ Expr(binop->right(), prec_right, at_end);
+ at_end = "";
+ } else if (tried_rhs_multiline &&
+ penalty_multiline_rhs_list < penalty_next_line) {
+ // Force a multiline list on the right.
+ Print(" ");
+ stack_.push_back(IndentState(start_column, false, false));
+ Sequence(kSequenceStyleList, rhs_list->contents(), rhs_list->End(), true);
+ stack_.pop_back();
+ } else {
+ // Otherwise, put first argument and op, and indent next.
+ Newline();
+ penalty += std::abs(CurrentColumn() - start_column) *
+ kPenaltyHorizontalSeparation;
+ Expr(binop->right(), prec_right, at_end);
+ at_end = "";
+ }
+ stack_.pop_back();
+ penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
+ } else if (const BlockNode* block = root->AsBlock()) {
+ Sequence(kSequenceStyleBracedBlock, block->statements(), block->End(),
+ false);
+ } else if (const ConditionNode* condition = root->AsCondition()) {
+ Print("if (");
+ CHECK(at_end.empty());
+ Expr(condition->condition(), kPrecedenceLowest, ") {");
+ Sequence(kSequenceStyleBracedBlockAlreadyOpen,
+ condition->if_true()->statements(), condition->if_true()->End(),
+ false);
+ if (condition->if_false()) {
+ Print(" else ");
+ // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
+ // ConditionNode::Execute.
+ bool is_else_if = condition->if_false()->AsBlock() == nullptr;
+ if (is_else_if) {
+ Expr(condition->if_false(), kPrecedenceLowest, std::string());
+ } else {
+ Sequence(kSequenceStyleBracedBlock,
+ condition->if_false()->AsBlock()->statements(),
+ condition->if_false()->AsBlock()->End(), false);
+ }
+ }
+ } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
+ penalty += FunctionCall(func_call, at_end);
+ at_end = "";
+ } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
+ Print(identifier->value().value());
+ } else if (const ListNode* list = root->AsList()) {
+ Sequence(kSequenceStyleList, list->contents(), list->End(),
+ /*force_multiline=*/false);
+ } else if (const LiteralNode* literal = root->AsLiteral()) {
+ Print(literal->value().value());
+ } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
+ Print(unaryop->op().value());
+ Expr(unaryop->operand(), kPrecedenceUnary, std::string());
+ } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
+ Print(block_comment->comment().value());
+ } else if (const EndNode* end = root->AsEnd()) {
+ Print(end->value().value());
+ } else {
+ CHECK(false) << "Unhandled case in Expr.";
+ }
+
+ // Defer any end of line comment until we reach the newline.
+ if (root->comments() && !root->comments()->suffix().empty()) {
+ std::copy(root->comments()->suffix().begin(),
+ root->comments()->suffix().end(), std::back_inserter(comments_));
+ }
+
+ Print(at_end);
+
+ penalty_depth_--;
+ return penalty;
+}
+
+template <class PARSENODE>
+void Printer::Sequence(SequenceStyle style,
+ const std::vector<std::unique_ptr<PARSENODE>>& list,
+ const ParseNode* end,
+ bool force_multiline) {
+ if (style == kSequenceStyleList) {
+ Print("[");
+ } else if (style == kSequenceStyleBracedBlock) {
+ Print("{");
+ } else if (style == kSequenceStyleBracedBlockAlreadyOpen) {
+ style = kSequenceStyleBracedBlock;
+ }
+
+ if (style == kSequenceStyleBracedBlock) {
+ force_multiline = true;
+ SortImports(const_cast<std::vector<std::unique_ptr<PARSENODE>>&>(list));
+ }
+
+ force_multiline |= ListWillBeMultiline(list, end);
+
+ if (list.size() == 0 && !force_multiline) {
+ // No elements, and not forcing newlines, print nothing.
+ } else if (list.size() == 1 && !force_multiline) {
+ Print(" ");
+ Expr(list[0].get(), kPrecedenceLowest, std::string());
+ CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
+ Print(" ");
+ } else {
+ stack_.push_back(IndentState(margin() + kIndentSize,
+ style == kSequenceStyleList, false));
+ size_t i = 0;
+ for (const auto& x : list) {
+ Newline();
+ // If:
+ // - we're going to output some comments, and;
+ // - we haven't just started this multiline list, and;
+ // - there isn't already a blank line here;
+ // Then: insert one.
+ if (i != 0 && x->comments() && !x->comments()->before().empty() &&
+ !HaveBlankLine()) {
+ Newline();
+ }
+ bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
+ bool want_comma =
+ body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
+ Expr(x.get(), kPrecedenceLowest, want_comma ? "," : std::string());
+ CHECK(!x->comments() || x->comments()->after().empty());
+ if (body_of_list) {
+ if (i < list.size() - 1 &&
+ ShouldAddBlankLineInBetween(list[i].get(), list[i + 1].get()))
+ Newline();
+ }
+ ++i;
+ }
+
+ // Trailing comments.
+ if (end->comments() && !end->comments()->before().empty()) {
+ if (list.size() >= 2)
+ Newline();
+ for (const auto& c : end->comments()->before()) {
+ Newline();
+ TrimAndPrintToken(c);
+ }
+ }
+
+ stack_.pop_back();
+ Newline();
+ }
+
+ // Defer any end of line comment until we reach the newline.
+ if (end->comments() && !end->comments()->suffix().empty()) {
+ std::copy(end->comments()->suffix().begin(),
+ end->comments()->suffix().end(), std::back_inserter(comments_));
+ }
+
+ if (style == kSequenceStyleList)
+ Print("]");
+ else if (style == kSequenceStyleBracedBlock)
+ Print("}");
+}
+
+int Printer::FunctionCall(const FunctionCallNode* func_call,
+ const std::string& suffix) {
+ int start_line = CurrentLine();
+ int start_column = CurrentColumn();
+ Print(func_call->function().value());
+ Print("(");
+
+ bool have_block = func_call->block() != nullptr;
+ bool force_multiline = false;
+
+ const auto& list = func_call->args()->contents();
+ const ParseNode* end = func_call->args()->End();
+
+ if (end->comments() && !end->comments()->before().empty())
+ force_multiline = true;
+
+ // If there's before line comments, make sure we have a place to put them.
+ for (const auto& i : list) {
+ if (i->comments() && !i->comments()->before().empty())
+ force_multiline = true;
+ }
+
+ // Calculate the penalties for 3 possible layouts:
+ // 1. all on same line;
+ // 2. starting on same line, broken at each comma but paren aligned;
+ // 3. broken to next line + 4, broken at each comma.
+ std::string terminator = ")";
+ if (have_block)
+ terminator += " {";
+ terminator += suffix;
+
+ // Special case to make function calls of one arg taking a long list of
+ // boolean operators not indent.
+ bool continuation_requires_indent =
+ list.size() != 1 || !list[0]->AsBinaryOp();
+
+ // 1: Same line.
+ Printer sub1;
+ InitializeSub(&sub1);
+ sub1.stack_.push_back(
+ IndentState(CurrentColumn(), continuation_requires_indent, false));
+ int penalty_one_line = 0;
+ for (size_t i = 0; i < list.size(); ++i) {
+ penalty_one_line += sub1.Expr(list[i].get(), kPrecedenceLowest,
+ i < list.size() - 1 ? ", " : std::string());
+ }
+ sub1.Print(terminator);
+ penalty_one_line += AssessPenalty(sub1.String());
+ // This extra penalty prevents a short second argument from being squeezed in
+ // after a first argument that went multiline (and instead preferring a
+ // variant below).
+ penalty_one_line +=
+ (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
+
+ // 2: Starting on same line, broken at commas.
+ Printer sub2;
+ InitializeSub(&sub2);
+ sub2.stack_.push_back(
+ IndentState(CurrentColumn(), continuation_requires_indent, false));
+ int penalty_multiline_start_same_line = 0;
+ for (size_t i = 0; i < list.size(); ++i) {
+ penalty_multiline_start_same_line +=
+ sub2.Expr(list[i].get(), kPrecedenceLowest,
+ i < list.size() - 1 ? "," : std::string());
+ if (i < list.size() - 1) {
+ sub2.Newline();
+ }
+ }
+ sub2.Print(terminator);
+ penalty_multiline_start_same_line += AssessPenalty(sub2.String());
+
+ // 3: Starting on next line, broken at commas.
+ Printer sub3;
+ InitializeSub(&sub3);
+ sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2,
+ continuation_requires_indent, false));
+ sub3.Newline();
+ int penalty_multiline_start_next_line = 0;
+ for (size_t i = 0; i < list.size(); ++i) {
+ if (i == 0) {
+ penalty_multiline_start_next_line +=
+ std::abs(sub3.CurrentColumn() - start_column) *
+ kPenaltyHorizontalSeparation;
+ }
+ penalty_multiline_start_next_line +=
+ sub3.Expr(list[i].get(), kPrecedenceLowest,
+ i < list.size() - 1 ? "," : std::string());
+ if (i < list.size() - 1) {
+ sub3.Newline();
+ }
+ }
+ sub3.Print(terminator);
+ penalty_multiline_start_next_line += AssessPenalty(sub3.String());
+
+ int penalty = penalty_multiline_start_next_line;
+ bool fits_on_current_line = false;
+ if (penalty_one_line < penalty_multiline_start_next_line ||
+ penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
+ fits_on_current_line = true;
+ penalty = penalty_one_line;
+ if (penalty_multiline_start_same_line < penalty_one_line) {
+ penalty = penalty_multiline_start_same_line;
+ force_multiline = true;
+ }
+ } else {
+ force_multiline = true;
+ }
+
+ if (list.size() == 0 && !force_multiline) {
+ // No elements, and not forcing newlines, print nothing.
+ } else {
+ if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
+ stack_.push_back(IndentState(margin() + kIndentSize * 2,
+ continuation_requires_indent, false));
+ Newline();
+ } else {
+ stack_.push_back(
+ IndentState(CurrentColumn(), continuation_requires_indent, false));
+ }
+
+ for (size_t i = 0; i < list.size(); ++i) {
+ const auto& x = list[i];
+ if (i > 0) {
+ if (fits_on_current_line && !force_multiline)
+ Print(" ");
+ else
+ Newline();
+ }
+ bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
+ Expr(x.get(), kPrecedenceLowest, want_comma ? "," : std::string());
+ CHECK(!x->comments() || x->comments()->after().empty());
+ if (i < list.size() - 1) {
+ if (!want_comma)
+ Newline();
+ }
+ }
+
+ // Trailing comments.
+ if (end->comments() && !end->comments()->before().empty()) {
+ if (!list.empty())
+ Newline();
+ for (const auto& c : end->comments()->before()) {
+ Newline();
+ TrimAndPrintToken(c);
+ }
+ Newline();
+ }
+ stack_.pop_back();
+ }
+
+ // Defer any end of line comment until we reach the newline.
+ if (end->comments() && !end->comments()->suffix().empty()) {
+ std::copy(end->comments()->suffix().begin(),
+ end->comments()->suffix().end(), std::back_inserter(comments_));
+ }
+
+ Print(")");
+ Print(suffix);
+
+ if (have_block) {
+ Print(" ");
+ Sequence(kSequenceStyleBracedBlock, func_call->block()->statements(),
+ func_call->block()->End(), false);
+ }
+ return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
+}
+
+void Printer::InitializeSub(Printer* sub) {
+ sub->stack_ = stack_;
+ sub->comments_ = comments_;
+ sub->penalty_depth_ = penalty_depth_;
+ sub->Print(std::string(CurrentColumn(), 'x'));
+}
+
+template <class PARSENODE>
+bool Printer::ListWillBeMultiline(
+ const std::vector<std::unique_ptr<PARSENODE>>& list,
+ const ParseNode* end) {
+ if (list.size() > 1)
+ return true;
+
+ if (end && end->comments() && !end->comments()->before().empty())
+ return true;
+
+ // If there's before or suffix line comments, make sure we have a place to put
+ // them.
+ for (const auto& i : list) {
+ if (i->comments() && (!i->comments()->before().empty() ||
+ !i->comments()->suffix().empty())) {
+ return true;
+ }
+ }
+
+ // When a scope is used as a list entry, it's too complicated to go one a
+ // single line (the block will always be formatted multiline itself).
+ if (list.size() >= 1 && list[0]->AsBlock())
+ return true;
+
+ return false;
+}
+
+void DoFormat(const ParseNode* root,
+ TreeDumpMode dump_tree,
+ std::string* output,
+ std::string* dump_output) {
+ if (dump_tree == TreeDumpMode::kPlainText) {
+ std::ostringstream os;
+ RenderToText(root->GetJSONNode(), 0, os);
+ *dump_output = os.str();
+ } else if (dump_tree == TreeDumpMode::kJSON) {
+ std::string os;
+ base::JSONWriter::WriteWithOptions(
+ root->GetJSONNode(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &os);
+ *dump_output = os;
+ }
+
+ Printer pr;
+ pr.Block(root);
+ *output = pr.String();
+}
+
+} // namespace
+
+bool FormatJsonToString(const std::string& json, std::string* output) {
+ base::JSONReader reader;
+ std::unique_ptr<base::Value> json_root = reader.Read(json);
+ std::unique_ptr<ParseNode> root = ParseNode::BuildFromJSON(*json_root);
+ DoFormat(root.get(), TreeDumpMode::kInactive, output, nullptr);
+ return true;
+}
+
+bool FormatStringToString(const std::string& input,
+ TreeDumpMode dump_tree,
+ std::string* output,
+ std::string* dump_output) {
+ SourceFile source_file;
+ InputFile file(source_file);
+ file.SetContents(input);
+ Err err;
+ // Tokenize.
+ std::vector<Token> tokens =
+ Tokenizer::Tokenize(&file, &err, WhitespaceTransform::kInvalidToSpace);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ // Parse.
+ std::unique_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ DoFormat(parse_node.get(), dump_tree, output, dump_output);
+ return true;
+}
+
+int RunFormat(const std::vector<std::string>& args) {
+#if defined(OS_WIN)
+ // Set to binary mode to prevent converting newlines to \r\n.
+ _setmode(_fileno(stdout), _O_BINARY);
+ _setmode(_fileno(stderr), _O_BINARY);
+#endif
+
+ bool dry_run =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
+ TreeDumpMode dump_tree = TreeDumpMode::kInactive;
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree)) {
+ std::string tree_type =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ kSwitchDumpTree);
+ if (tree_type == kSwitchTreeTypeJSON) {
+ dump_tree = TreeDumpMode::kJSON;
+ } else if (tree_type.empty() || tree_type == kSwitchTreeTypeText) {
+ dump_tree = TreeDumpMode::kPlainText;
+ } else {
+ Err(Location(), tree_type +
+ " is an invalid value for --dump-tree. Specify "
+ "\"" +
+ kSwitchTreeTypeText + "\" or \"" +
+ kSwitchTreeTypeJSON + "\".\n")
+ .PrintToStdout();
+ return 1;
+ }
+ }
+ bool from_stdin =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
+
+ if (dry_run) {
+ // --dry-run only works with an actual file to compare to.
+ from_stdin = false;
+ }
+
+ bool quiet =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet);
+
+ if (from_stdin) {
+ if (args.size() != 0) {
+ Err(Location(), "Expecting no arguments when reading from stdin.\n")
+ .PrintToStdout();
+ return 1;
+ }
+ std::string input = ReadStdin();
+ std::string output;
+ std::string dump_output;
+ if (!FormatStringToString(input, dump_tree, &output, &dump_output))
+ return 1;
+ printf("%s", dump_output.c_str());
+ printf("%s", output.c_str());
+ return 0;
+ }
+
+ if (args.size() == 0) {
+ Err(Location(), "Expecting one or more arguments, see `gn help format`.\n")
+ .PrintToStdout();
+ return 1;
+ }
+
+ Setup setup;
+ SourceDir source_dir =
+ SourceDirForCurrentDirectory(setup.build_settings().root_path());
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchReadTree)) {
+ std::string tree_type =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ kSwitchReadTree);
+ if (tree_type != kSwitchTreeTypeJSON) {
+ Err(Location(), "Only json supported for read-tree.\n").PrintToStdout();
+ return 1;
+ }
+
+ if (args.size() != 1) {
+ Err(Location(),
+ "Expect exactly one .gn when reading tree from json on stdin.\n")
+ .PrintToStdout();
+ return 1;
+ }
+ Err err;
+ SourceFile file =
+ source_dir.ResolveRelativeFile(Value(nullptr, args[0]), &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
+ base::FilePath to_format = setup.build_settings().GetFullPath(file);
+ std::string output;
+ FormatJsonToString(ReadStdin(), &output);
+ if (base::WriteFile(to_format, output.data(),
+ static_cast<int>(output.size())) == -1) {
+ Err(Location(), std::string("Failed to write output to \"") +
+ FilePathToUTF8(to_format) + std::string("\"."))
+ .PrintToStdout();
+ return 1;
+ }
+ if (!quiet) {
+ printf("Wrote rebuilt from json to '%s'.\n",
+ FilePathToUTF8(to_format).c_str());
+ }
+ return 0;
+ }
+
+ // TODO(scottmg): Eventually, this list of files should be processed in
+ // parallel.
+ int exit_code = 0;
+ for (const auto& arg : args) {
+ Err err;
+ SourceFile file = source_dir.ResolveRelativeFile(Value(nullptr, arg), &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ exit_code = 1;
+ continue;
+ }
+
+ base::FilePath to_format = setup.build_settings().GetFullPath(file);
+ std::string original_contents;
+ if (!base::ReadFileToString(to_format, &original_contents)) {
+ Err(Location(),
+ std::string("Couldn't read \"") + FilePathToUTF8(to_format))
+ .PrintToStdout();
+ exit_code = 1;
+ continue;
+ }
+
+ std::string output_string;
+ std::string dump_output_string;
+ if (!FormatStringToString(original_contents, dump_tree, &output_string,
+ &dump_output_string)) {
+ exit_code = 1;
+ continue;
+ }
+ printf("%s", dump_output_string.c_str());
+ if (dump_tree == TreeDumpMode::kInactive) {
+ if (dry_run) {
+ if (original_contents != output_string) {
+ printf("%s\n", arg.c_str());
+ exit_code = 2;
+ }
+ continue;
+ }
+ // Update the file in-place.
+ if (original_contents != output_string) {
+ if (base::WriteFile(to_format, output_string.data(),
+ static_cast<int>(output_string.size())) == -1) {
+ Err(Location(),
+ std::string("Failed to write formatted output back to \"") +
+ FilePathToUTF8(to_format) + std::string("\"."))
+ .PrintToStdout();
+ exit_code = 1;
+ continue;
+ }
+ if (!quiet) {
+ printf("Wrote formatted to '%s'.\n",
+ FilePathToUTF8(to_format).c_str());
+ }
+ }
+ }
+ }
+
+ return exit_code;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_format.h b/gn/src/gn/command_format.h
new file mode 100644
index 00000000000..0eb667fa253
--- /dev/null
+++ b/gn/src/gn/command_format.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_COMAND_FORMAT_H_
+#define TOOLS_GN_COMAND_FORMAT_H_
+
+#include <string>
+
+class Setup;
+class SourceFile;
+
+namespace commands {
+
+enum class TreeDumpMode {
+ // Normal operation mode. Format the input file.
+ kInactive,
+
+ // Output the token tree with indented plain text. For debugging.
+ kPlainText,
+
+ // Output the token tree in JSON format. Used for exporting a tree to another
+ // program.
+ kJSON
+};
+
+bool FormatJsonToString(const std::string& input, std::string* output);
+
+bool FormatStringToString(const std::string& input,
+ TreeDumpMode dump_tree,
+ std::string* output,
+ std::string* dump_output);
+
+} // namespace commands
+
+#endif // TOOLS_GN_COMAND_FORMAT_H_
diff --git a/gn/src/gn/command_format_unittest.cc b/gn/src/gn/command_format_unittest.cc
new file mode 100644
index 00000000000..9112dd36229
--- /dev/null
+++ b/gn/src/gn/command_format_unittest.cc
@@ -0,0 +1,138 @@
+// 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 "gn/command_format.h"
+
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "gn/commands.h"
+#include "gn/setup.h"
+#include "gn/test_with_scheduler.h"
+#include "util/exe_path.h"
+#include "util/test/test.h"
+
+using FormatTest = TestWithScheduler;
+
+#define FORMAT_TEST(n) \
+ TEST_F(FormatTest, n) { \
+ ::Setup setup; \
+ std::string input; \
+ std::string out; \
+ std::string expected; \
+ base::FilePath src_dir = \
+ GetExePath().DirName().Append(FILE_PATH_LITERAL("..")); \
+ base::SetCurrentDirectory(src_dir); \
+ ASSERT_TRUE(base::ReadFileToString( \
+ base::FilePath(FILE_PATH_LITERAL("src/gn/format_test_data/") \
+ FILE_PATH_LITERAL(#n) FILE_PATH_LITERAL(".gn")), \
+ &input)); \
+ ASSERT_TRUE(base::ReadFileToString( \
+ base::FilePath(FILE_PATH_LITERAL("src/gn/format_test_data/") \
+ FILE_PATH_LITERAL(#n) \
+ FILE_PATH_LITERAL(".golden")), \
+ &expected)); \
+ EXPECT_TRUE(commands::FormatStringToString( \
+ input, commands::TreeDumpMode::kInactive, &out, nullptr)); \
+ EXPECT_EQ(expected, out); \
+ /* Make sure formatting the output doesn't cause further changes. */ \
+ std::string out_again; \
+ EXPECT_TRUE(commands::FormatStringToString( \
+ out, commands::TreeDumpMode::kInactive, &out_again, nullptr)); \
+ ASSERT_EQ(out, out_again); \
+ /* Make sure we can roundtrip to json without any changes. */ \
+ std::string as_json; \
+ std::string unused; \
+ EXPECT_TRUE(commands::FormatStringToString( \
+ out_again, commands::TreeDumpMode::kJSON, &unused, &as_json)); \
+ std::string rewritten; \
+ EXPECT_TRUE(commands::FormatJsonToString(as_json, &rewritten)); \
+ ASSERT_EQ(out, rewritten); \
+ }
+
+// These are expanded out this way rather than a runtime loop so that
+// --gtest_filter works as expected for individual test running.
+FORMAT_TEST(001)
+FORMAT_TEST(002)
+FORMAT_TEST(003)
+FORMAT_TEST(004)
+FORMAT_TEST(005)
+FORMAT_TEST(006)
+FORMAT_TEST(007)
+FORMAT_TEST(008)
+FORMAT_TEST(009)
+FORMAT_TEST(010)
+FORMAT_TEST(011)
+FORMAT_TEST(012)
+FORMAT_TEST(013)
+FORMAT_TEST(014)
+FORMAT_TEST(015)
+FORMAT_TEST(016)
+FORMAT_TEST(017)
+FORMAT_TEST(018)
+FORMAT_TEST(019)
+FORMAT_TEST(020)
+FORMAT_TEST(021)
+FORMAT_TEST(022)
+FORMAT_TEST(023)
+FORMAT_TEST(024)
+FORMAT_TEST(025)
+FORMAT_TEST(026)
+FORMAT_TEST(027)
+FORMAT_TEST(028)
+FORMAT_TEST(029)
+FORMAT_TEST(030)
+FORMAT_TEST(031)
+FORMAT_TEST(032)
+FORMAT_TEST(033)
+// TODO(scottmg): args+rebase_path unnecessarily split: FORMAT_TEST(034)
+FORMAT_TEST(035)
+FORMAT_TEST(036)
+FORMAT_TEST(037)
+FORMAT_TEST(038)
+FORMAT_TEST(039)
+FORMAT_TEST(040)
+FORMAT_TEST(041)
+FORMAT_TEST(042)
+FORMAT_TEST(043)
+FORMAT_TEST(044)
+FORMAT_TEST(045)
+FORMAT_TEST(046)
+FORMAT_TEST(047)
+FORMAT_TEST(048)
+// TODO(scottmg): Eval is broken (!) and comment output might have extra ,
+// FORMAT_TEST(049)
+FORMAT_TEST(050)
+FORMAT_TEST(051)
+FORMAT_TEST(052)
+FORMAT_TEST(053)
+FORMAT_TEST(054)
+FORMAT_TEST(055)
+FORMAT_TEST(056)
+FORMAT_TEST(057)
+FORMAT_TEST(058)
+FORMAT_TEST(059)
+FORMAT_TEST(060)
+FORMAT_TEST(061)
+FORMAT_TEST(062)
+FORMAT_TEST(063)
+FORMAT_TEST(064)
+FORMAT_TEST(065)
+FORMAT_TEST(066)
+FORMAT_TEST(067)
+FORMAT_TEST(068)
+FORMAT_TEST(069)
+FORMAT_TEST(070)
+FORMAT_TEST(071)
+FORMAT_TEST(072)
+FORMAT_TEST(073)
+FORMAT_TEST(074)
+FORMAT_TEST(075)
+FORMAT_TEST(076)
+FORMAT_TEST(077)
+FORMAT_TEST(078)
+FORMAT_TEST(079)
+FORMAT_TEST(080)
+FORMAT_TEST(081)
+FORMAT_TEST(082)
+FORMAT_TEST(083)
diff --git a/gn/src/gn/command_gen.cc b/gn/src/gn/command_gen.cc
new file mode 100644
index 00000000000..e5248b9f45b
--- /dev/null
+++ b/gn/src/gn/command_gen.cc
@@ -0,0 +1,695 @@
+// 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 <mutex>
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/timer/elapsed_timer.h"
+#include "gn/build_settings.h"
+#include "gn/commands.h"
+#include "gn/compile_commands_writer.h"
+#include "gn/eclipse_writer.h"
+#include "gn/filesystem_utils.h"
+#include "gn/json_project_writer.h"
+#include "gn/ninja_target_writer.h"
+#include "gn/ninja_tools.h"
+#include "gn/ninja_writer.h"
+#include "gn/qt_creator_writer.h"
+#include "gn/runtime_deps.h"
+#include "gn/rust_project_writer.h"
+#include "gn/scheduler.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/visual_studio_writer.h"
+#include "gn/xcode_writer.h"
+
+namespace commands {
+
+namespace {
+
+const char kSwitchCheck[] = "check";
+const char kSwitchCleanStale[] = "clean-stale";
+const char kSwitchFilters[] = "filters";
+const char kSwitchIde[] = "ide";
+const char kSwitchIdeValueEclipse[] = "eclipse";
+const char kSwitchIdeValueQtCreator[] = "qtcreator";
+const char kSwitchIdeValueVs[] = "vs";
+const char kSwitchIdeValueVs2013[] = "vs2013";
+const char kSwitchIdeValueVs2015[] = "vs2015";
+const char kSwitchIdeValueVs2017[] = "vs2017";
+const char kSwitchIdeValueVs2019[] = "vs2019";
+const char kSwitchIdeValueWinSdk[] = "winsdk";
+const char kSwitchIdeValueXcode[] = "xcode";
+const char kSwitchIdeValueJson[] = "json";
+const char kSwitchIdeRootTarget[] = "ide-root-target";
+const char kSwitchNinjaExecutable[] = "ninja-executable";
+const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
+const char kSwitchNoDeps[] = "no-deps";
+const char kSwitchSln[] = "sln";
+const char kSwitchXcodeProject[] = "xcode-project";
+const char kSwitchXcodeBuildSystem[] = "xcode-build-system";
+const char kSwitchXcodeBuildsystemValueLegacy[] = "legacy";
+const char kSwitchXcodeBuildsystemValueNew[] = "new";
+const char kSwitchJsonFileName[] = "json-file-name";
+const char kSwitchJsonIdeScript[] = "json-ide-script";
+const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
+const char kSwitchExportCompileCommands[] = "export-compile-commands";
+const char kSwitchExportRustProject[] = "export-rust-project";
+
+// Collects Ninja rules for each toolchain. The lock protectes the rules.
+struct TargetWriteInfo {
+ std::mutex lock;
+ NinjaWriter::PerToolchainRules rules;
+};
+
+// Called on worker thread to write the ninja file.
+void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
+ std::string rule = NinjaTargetWriter::RunAndWriteFile(target);
+ DCHECK(!rule.empty());
+
+ {
+ std::lock_guard<std::mutex> lock(write_info->lock);
+ write_info->rules[target->toolchain()].emplace_back(target,
+ std::move(rule));
+ }
+}
+
+// Called on the main thread.
+void ItemResolvedAndGeneratedCallback(TargetWriteInfo* write_info,
+ const BuilderRecord* record) {
+ const Item* item = record->item();
+ const Target* target = item->AsTarget();
+ if (target) {
+ g_scheduler->ScheduleWork(
+ [write_info, target]() { BackgroundDoWrite(write_info, target); });
+ }
+}
+
+// Returns a pointer to the target with the given file as an output, or null
+// if no targets generate the file. This is brute force since this is an
+// error condition and performance shouldn't matter.
+const Target* FindTargetThatGeneratesFile(const Builder& builder,
+ const SourceFile& file) {
+ std::vector<const Target*> targets = builder.GetAllResolvedTargets();
+ if (targets.empty())
+ return nullptr;
+
+ OutputFile output_file(targets[0]->settings()->build_settings(), file);
+ for (const Target* target : targets) {
+ for (const auto& cur_output : target->computed_outputs()) {
+ if (cur_output == output_file)
+ return target;
+ }
+ }
+ return nullptr;
+}
+
+// Prints an error that the given file was present as a source or input in
+// the given target(s) but was not generated by any of its dependencies.
+void PrintInvalidGeneratedInput(const Builder& builder,
+ const SourceFile& file,
+ const std::vector<const Target*>& targets) {
+ std::string err;
+
+ // Only show the toolchain labels (which can be confusing) if something
+ // isn't the default.
+ bool show_toolchains = false;
+ const Label& default_toolchain =
+ targets[0]->settings()->default_toolchain_label();
+ for (const Target* target : targets) {
+ if (target->settings()->toolchain_label() != default_toolchain) {
+ show_toolchains = true;
+ break;
+ }
+ }
+
+ const Target* generator = FindTargetThatGeneratesFile(builder, file);
+ if (generator &&
+ generator->settings()->toolchain_label() != default_toolchain)
+ show_toolchains = true;
+
+ const std::string target_str = targets.size() > 1 ? "targets" : "target";
+ err += "The file:\n";
+ err += " " + file.value() + "\n";
+ err += "is listed as an input or source for the " + target_str + ":\n";
+ for (const Target* target : targets)
+ err += " " + target->label().GetUserVisibleName(show_toolchains) + "\n";
+
+ if (generator) {
+ err += "but this file was not generated by any dependencies of the " +
+ target_str + ". The target\nthat generates the file is:\n ";
+ err += generator->label().GetUserVisibleName(show_toolchains);
+ } else {
+ err += "but no targets in the build generate that file.";
+ }
+
+ Err(Location(), "Input to " + target_str + " not generated by a dependency.",
+ err)
+ .PrintToStdout();
+}
+
+bool CheckForInvalidGeneratedInputs(Setup* setup) {
+ std::multimap<SourceFile, const Target*> unknown_inputs =
+ g_scheduler->GetUnknownGeneratedInputs();
+ if (unknown_inputs.empty())
+ return true; // No bad files.
+
+ int errors_found = 0;
+ auto cur = unknown_inputs.begin();
+ while (cur != unknown_inputs.end()) {
+ errors_found++;
+ auto end_of_range = unknown_inputs.upper_bound(cur->first);
+
+ // Package the values more conveniently for printing.
+ SourceFile bad_input = cur->first;
+ std::vector<const Target*> targets;
+ while (cur != end_of_range)
+ targets.push_back((cur++)->second);
+
+ PrintInvalidGeneratedInput(setup->builder(), bad_input, targets);
+ OutputString("\n");
+ }
+
+ OutputString(
+ "If you have generated inputs, there needs to be a dependency path "
+ "between the\ntwo targets in addition to just listing the files. For "
+ "indirect dependencies,\nthe intermediate ones must be public_deps. "
+ "data_deps don't count since they're\nonly runtime dependencies. If "
+ "you think a dependency chain exists, it might be\nbecause the chain "
+ "is private. Try \"gn path\" to analyze.\n");
+
+ if (errors_found > 1) {
+ OutputString(base::StringPrintf("\n%d generated input errors found.\n",
+ errors_found),
+ DECORATION_YELLOW);
+ }
+ return false;
+}
+
+bool RunIdeWriter(const std::string& ide,
+ const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err) {
+ const base::CommandLine* command_line =
+ base::CommandLine::ForCurrentProcess();
+ bool quiet = command_line->HasSwitch(switches::kQuiet);
+ base::ElapsedTimer timer;
+
+ if (ide == kSwitchIdeValueEclipse) {
+ bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err);
+ if (res && !quiet) {
+ OutputString("Generating Eclipse settings took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+ } else if (ide == kSwitchIdeValueVs || ide == kSwitchIdeValueVs2013 ||
+ ide == kSwitchIdeValueVs2015 || ide == kSwitchIdeValueVs2017 ||
+ ide == kSwitchIdeValueVs2019) {
+ VisualStudioWriter::Version version = VisualStudioWriter::Version::Vs2019;
+ if (ide == kSwitchIdeValueVs2013)
+ version = VisualStudioWriter::Version::Vs2013;
+ else if (ide == kSwitchIdeValueVs2015)
+ version = VisualStudioWriter::Version::Vs2015;
+ else if (ide == kSwitchIdeValueVs2017)
+ version = VisualStudioWriter::Version::Vs2017;
+
+ std::string sln_name;
+ if (command_line->HasSwitch(kSwitchSln))
+ sln_name = command_line->GetSwitchValueASCII(kSwitchSln);
+ std::string filters;
+ if (command_line->HasSwitch(kSwitchFilters))
+ filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+ std::string win_kit;
+ if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
+ win_kit = command_line->GetSwitchValueASCII(kSwitchIdeValueWinSdk);
+ std::string ninja_extra_args;
+ if (command_line->HasSwitch(kSwitchNinjaExtraArgs))
+ ninja_extra_args =
+ command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs);
+ bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
+ bool res = VisualStudioWriter::RunAndWriteFiles(
+ build_settings, builder, version, sln_name, filters, win_kit,
+ ninja_extra_args, no_deps, err);
+ if (res && !quiet) {
+ OutputString("Generating Visual Studio projects took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+ } else if (ide == kSwitchIdeValueXcode) {
+ XcodeWriter::Options options = {
+ command_line->GetSwitchValueASCII(kSwitchXcodeProject),
+ command_line->GetSwitchValueASCII(kSwitchIdeRootTarget),
+ command_line->GetSwitchValueASCII(kSwitchNinjaExecutable),
+ command_line->GetSwitchValueASCII(kSwitchFilters),
+ XcodeBuildSystem::kLegacy,
+ };
+
+ if (options.project_name.empty()) {
+ options.project_name = "all";
+ }
+
+ const std::string build_system =
+ command_line->GetSwitchValueASCII(kSwitchXcodeBuildSystem);
+ if (!build_system.empty()) {
+ if (build_system == kSwitchXcodeBuildsystemValueNew) {
+ options.build_system = XcodeBuildSystem::kNew;
+ } else if (build_system == kSwitchXcodeBuildsystemValueLegacy) {
+ options.build_system = XcodeBuildSystem::kLegacy;
+ } else {
+ *err = Err(Location(), "Unknown build system: " + build_system);
+ return false;
+ }
+ }
+
+ bool res =
+ XcodeWriter::RunAndWriteFiles(build_settings, builder, options, err);
+ if (res && !quiet) {
+ OutputString("Generating Xcode projects took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+ } else if (ide == kSwitchIdeValueQtCreator) {
+ std::string root_target;
+ if (command_line->HasSwitch(kSwitchIdeRootTarget))
+ root_target = command_line->GetSwitchValueASCII(kSwitchIdeRootTarget);
+ bool res = QtCreatorWriter::RunAndWriteFile(build_settings, builder, err,
+ root_target);
+ if (res && !quiet) {
+ OutputString("Generating QtCreator projects took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+ } else if (ide == kSwitchIdeValueJson) {
+ std::string file_name =
+ command_line->GetSwitchValueASCII(kSwitchJsonFileName);
+ if (file_name.empty())
+ file_name = "project.json";
+ std::string exec_script =
+ command_line->GetSwitchValueASCII(kSwitchJsonIdeScript);
+ std::string exec_script_extra_args =
+ command_line->GetSwitchValueASCII(kSwitchJsonIdeScriptArgs);
+ std::string filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+
+ bool res = JSONProjectWriter::RunAndWriteFiles(
+ build_settings, builder, file_name, exec_script, exec_script_extra_args,
+ filters, quiet, err);
+ if (res && !quiet) {
+ OutputString("Generating JSON projects took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+ }
+
+ *err = Err(Location(), "Unknown IDE: " + ide);
+ return false;
+}
+
+bool RunRustProjectWriter(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err) {
+ const base::CommandLine* command_line =
+ base::CommandLine::ForCurrentProcess();
+ bool quiet = command_line->HasSwitch(switches::kQuiet);
+ base::ElapsedTimer timer;
+
+ std::string file_name = "rust-project.json";
+ bool res = RustProjectWriter::RunAndWriteFiles(build_settings, builder,
+ file_name, quiet, err);
+ if (res && !quiet) {
+ OutputString("Generating rust-project.json took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+}
+
+bool RunCompileCommandsWriter(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err) {
+ const base::CommandLine* command_line =
+ base::CommandLine::ForCurrentProcess();
+ bool quiet = command_line->HasSwitch(switches::kQuiet);
+ base::ElapsedTimer timer;
+
+ std::string file_name = "compile_commands.json";
+ std::string target_filters =
+ command_line->GetSwitchValueASCII(kSwitchExportCompileCommands);
+
+ bool res = CompileCommandsWriter::RunAndWriteFiles(
+ build_settings, builder, file_name, target_filters, quiet, err);
+ if (res && !quiet) {
+ OutputString("Generating compile_commands took " +
+ base::Int64ToString(timer.Elapsed().InMilliseconds()) +
+ "ms\n");
+ }
+ return res;
+}
+
+bool RunNinjaPostProcessTools(const BuildSettings* build_settings,
+ base::FilePath ninja_executable,
+ bool is_regeneration,
+ bool clean_stale,
+ Err* err) {
+ // If the user did not specify an executable, skip running the post processing
+ // tools. Since these tools can re-write ninja build log and dep logs, it is
+ // really important that ninja executable used for tools matches the
+ // executable that is used for builds.
+ if (ninja_executable.empty()) {
+ if (clean_stale) {
+ *err = Err(Location(), "No --ninja-executable provided.",
+ "--clean-stale requires a ninja executable to run. You can "
+ "provide one on the command line via --ninja-executable.");
+ return false;
+ }
+
+ return true;
+ }
+
+ base::FilePath build_dir =
+ build_settings->GetFullPath(build_settings->build_dir());
+
+ if (clean_stale) {
+ if (build_settings->ninja_required_version() < Version{1, 10, 0}) {
+ *err = Err(Location(), "Need a ninja executable at least version 1.10.0.",
+ "--clean-stale requires a ninja executable of version 1.10.0 "
+ "or later.");
+ return false;
+ }
+
+ if (!InvokeNinjaCleanDeadTool(ninja_executable, build_dir, err)) {
+ return false;
+ }
+
+ if(!InvokeNinjaRecompactTool(ninja_executable, build_dir, err)) {
+ return false;
+ }
+ }
+
+ // If we have a ninja version that supports restat, we should restat the
+ // build.ninja file so the next ninja invocation will use the right mtime. If
+ // gen is being invoked as part of a re-gen (ie, ninja is invoking gn gen),
+ // then we can elide this restat, as ninja will restat build.ninja anyways
+ // after it is complete.
+ if (!is_regeneration &&
+ build_settings->ninja_required_version() >= Version{1, 10, 0}) {
+ std::vector<base::FilePath> files_to_restat{
+ base::FilePath(FILE_PATH_LITERAL("build.ninja"))};
+ if (!InvokeNinjaRestatTool(ninja_executable, build_dir, files_to_restat,
+ err)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+const char kGen[] = "gen";
+const char kGen_HelpShort[] = "gen: Generate ninja files.";
+const char kGen_Help[] =
+ R"(gn gen [--check] [<ide options>] <out_dir>
+
+ Generates ninja files from the current tree and puts them in the given output
+ directory.
+
+ The output directory can be a source-repo-absolute path name such as:
+ //out/foo
+ Or it can be a directory relative to the current directory such as:
+ out/foo
+
+ "gn gen --check" is the same as running "gn check". "gn gen --check=system" is
+ the same as running "gn check --check-system". See "gn help check" for
+ documentation on that mode.
+
+ See "gn help switches" for the common command-line switches.
+
+General options
+
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use. This executable will
+ be used as an IDE option to indicate which ninja to use for building. This
+ executable will also be used as part of the gen process for triggering a
+ restat on generated ninja files and for use with --clean-stale.
+
+ --clean-stale
+ This option will cause no longer needed output files to be removed from
+ the build directory, and their records pruned from the ninja build log and
+ dependency database after the ninja build graph has been generated. This
+ option requires a ninja executable of at least version 1.10.0. It can be
+ provided by the --ninja-executable switch. Also see "gn help clean_stale".
+
+IDE options
+
+ GN optionally generates files for IDE. Files won't be overwritten if their
+ contents don't change. Possibilities for <ide options>
+
+ --ide=<ide_name>
+ Generate files for an IDE. Currently supported values:
+ "eclipse" - Eclipse CDT settings file.
+ "vs" - Visual Studio project/solution files.
+ (default Visual Studio version: 2019)
+ "vs2013" - Visual Studio 2013 project/solution files.
+ "vs2015" - Visual Studio 2015 project/solution files.
+ "vs2017" - Visual Studio 2017 project/solution files.
+ "vs2019" - Visual Studio 2019 project/solution files.
+ "xcode" - Xcode workspace/solution files.
+ "qtcreator" - QtCreator project files.
+ "json" - JSON file containing target information
+
+ --filters=<path_prefixes>
+ Semicolon-separated list of label patterns used to limit the set of
+ generated projects (see "gn help label_pattern"). Only matching targets
+ and their dependencies will be included in the solution. Only used for
+ Visual Studio, Xcode and JSON.
+
+Visual Studio Flags
+
+ --sln=<file_name>
+ Override default sln file name ("all"). Solution file is written to the
+ root build directory.
+
+ --no-deps
+ Don't include targets dependencies to the solution. Changes the way how
+ --filters option works. Only directly matching targets are included.
+
+ --winsdk=<sdk_version>
+ Use the specified Windows 10 SDK version to generate project files.
+ As an example, "10.0.15063.0" can be specified to use Creators Update SDK
+ instead of the default one.
+
+ --ninja-extra-args=<string>
+ This string is passed without any quoting to the ninja invocation
+ command-line. Can be used to configure ninja flags, like "-j".
+
+Xcode Flags
+
+ --xcode-project=<file_name>
+ Override default Xcode project file name ("all"). The project file is
+ written to the root build directory.
+
+ --xcode-build-system=<value>
+ Configure the build system to use for the Xcode project. Supported
+ values are (default to "legacy"):
+ "legacy" - Legacy Build system
+ "new" - New Build System
+
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use when building.
+
+ --ninja-extra-args=<string>
+ This string is passed without any quoting to the ninja invocation
+ command-line. Can be used to configure ninja flags, like "-j".
+
+ --ide-root-target=<target_name>
+ Name of the target corresponding to "All" target in Xcode. If unset,
+ "All" invokes ninja without any target and builds everything.
+
+QtCreator Flags
+
+ --ide-root-target=<target_name>
+ Name of the root target for which the QtCreator project will be generated
+ to contain files of it and its dependencies. If unset, the whole build
+ graph will be emitted.
+
+
+Eclipse IDE Support
+
+ GN DOES NOT generate Eclipse CDT projects. Instead, it generates a settings
+ file which can be imported into an Eclipse CDT project. The XML file contains
+ a list of include paths and defines. Because GN does not generate a full
+ .cproject definition, it is not possible to properly define includes/defines
+ for each file individually. Instead, one set of includes/defines is generated
+ for the entire project. This works fairly well but may still result in a few
+ indexer issues here and there.
+
+Generic JSON Output
+
+ Dumps target information to a JSON file and optionally invokes a
+ python script on the generated file. See the comments at the beginning
+ of json_project_writer.cc and desc_builder.cc for an overview of the JSON
+ file format.
+
+ --json-file-name=<json_file_name>
+ Overrides default file name (project.json) of generated JSON file.
+
+ --json-ide-script=<path_to_python_script>
+ Executes python script after the JSON file is generated or updated with
+ new content. Path can be project absolute (//), system absolute (/) or
+ relative, in which case the output directory will be base. Path to
+ generated JSON file will be first argument when invoking script.
+
+ --json-ide-script-args=<argument>
+ Optional second argument that will passed to executed script.
+
+Compilation Database
+
+ --export-rust-project
+ Produces a rust-project.json file in the root of the build directory
+ This is used for various tools in the Rust ecosystem allowing for the
+ replay of individual compilations independent of the build system.
+ This is an unstable format and likely to change without warning.
+
+ --export-compile-commands[=<target_name1,target_name2...>]
+ Produces a compile_commands.json file in the root of the build directory
+ containing an array of “command objects”, where each command object
+ specifies one way a translation unit is compiled in the project. If a list
+ of target_name is supplied, only targets that are reachable from any
+ target in any build file whose name is target_name will be used for
+ “command objects” generation, otherwise all available targets will be used.
+ This is used for various Clang-based tooling, allowing for the replay of
+ individual compilations independent of the build system.
+ e.g. "foo" will match:
+ - "//path/to/src:foo"
+ - "//other/path:foo"
+ - "//foo:foo"
+ and not match:
+ - "//foo:bar"
+)";
+
+int RunGen(const std::vector<std::string>& args) {
+ base::ElapsedTimer timer;
+
+ if (args.size() != 1) {
+ Err(Location(), "Need exactly one build directory to generate.",
+ "I expected something more like \"gn gen out/foo\"\n"
+ "You can also see \"gn help gen\".")
+ .PrintToStdout();
+ return 1;
+ }
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup();
+ // Generate an empty args.gn file if it does not exists
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kArgs)) {
+ setup->set_gen_empty_args(true);
+ }
+ if (!setup->DoSetup(args[0], true))
+ return 1;
+
+ const base::CommandLine* command_line =
+ base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(kSwitchCheck)) {
+ setup->set_check_public_headers(true);
+ if (command_line->GetSwitchValueASCII(kSwitchCheck) == "system")
+ setup->set_check_system_includes(true);
+ }
+
+ // Cause the load to also generate the ninja files for each target.
+ TargetWriteInfo write_info;
+ setup->builder().set_resolved_and_generated_callback(
+ [&write_info](const BuilderRecord* record) {
+ ItemResolvedAndGeneratedCallback(&write_info, record);
+ });
+
+ // Do the actual load. This will also write out the target ninja files.
+ if (!setup->Run())
+ return 1;
+
+ // Sort the targets in each toolchain according to their label. This makes
+ // the ninja files have deterministic content.
+ for (auto& cur_toolchain : write_info.rules) {
+ std::sort(cur_toolchain.second.begin(), cur_toolchain.second.end(),
+ [](const NinjaWriter::TargetRulePair& a,
+ const NinjaWriter::TargetRulePair& b) {
+ return a.first->label() < b.first->label();
+ });
+ }
+
+ Err err;
+ // Write the root ninja files.
+ if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(), setup->builder(),
+ write_info.rules, &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ if (!RunNinjaPostProcessTools(
+ &setup->build_settings(),
+ command_line->GetSwitchValuePath(switches::kNinjaExecutable),
+ command_line->HasSwitch(switches::kRegeneration),
+ command_line->HasSwitch(kSwitchCleanStale), &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ if (!WriteRuntimeDepsFilesIfNecessary(&setup->build_settings(),
+ setup->builder(), &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ if (!CheckForInvalidGeneratedInputs(setup))
+ return 1;
+
+ if (command_line->HasSwitch(kSwitchIde) &&
+ !RunIdeWriter(command_line->GetSwitchValueASCII(kSwitchIde),
+ &setup->build_settings(), setup->builder(), &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ if (command_line->HasSwitch(kSwitchExportCompileCommands) &&
+ !RunCompileCommandsWriter(&setup->build_settings(), setup->builder(),
+ &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ if (command_line->HasSwitch(kSwitchExportRustProject) &&
+ !RunRustProjectWriter(&setup->build_settings(), setup->builder(), &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ TickDelta elapsed_time = timer.Elapsed();
+
+ if (!command_line->HasSwitch(switches::kQuiet)) {
+ OutputString("Done. ", DECORATION_GREEN);
+
+ size_t targets_collected = 0;
+ for (const auto& rules : write_info.rules)
+ targets_collected += rules.second.size();
+
+ std::string stats =
+ "Made " + base::NumberToString(targets_collected) + " targets from " +
+ base::IntToString(
+ setup->scheduler().input_file_manager()->GetInputFileCount()) +
+ " files in " + base::Int64ToString(elapsed_time.InMilliseconds()) +
+ "ms\n";
+ OutputString(stats);
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_help.cc b/gn/src/gn/command_help.cc
new file mode 100644
index 00000000000..f03a4e74515
--- /dev/null
+++ b/gn/src/gn/command_help.cc
@@ -0,0 +1,376 @@
+// 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 "base/command_line.h"
+#include "gn/args.h"
+#include "gn/commands.h"
+#include "gn/err.h"
+#include "gn/functions.h"
+#include "gn/input_conversion.h"
+#include "gn/label.h"
+#include "gn/label_pattern.h"
+#include "gn/metadata.h"
+#include "gn/ninja_build_writer.h"
+#include "gn/output_conversion.h"
+#include "gn/parser.h"
+#include "gn/pattern.h"
+#include "gn/runtime_deps.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_writer.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/variables.h"
+
+namespace commands {
+
+namespace {
+
+// Some names exist in multiple sections, these prefixes are used for the
+// internal links to disambiguate when writing markdown.
+const char kCommandLinkPrefix[] = "cmd_";
+const char kFunctionLinkPrefix[] = "func_";
+const char kVariableLinkPrefix[] = "var_";
+
+void PrintToplevelHelp() {
+ PrintSectionHelp("Commands", "<command>", "commands");
+ for (const auto& cmd : commands::GetCommands())
+ PrintShortHelp(cmd.second.help_short, kCommandLinkPrefix + cmd.first);
+
+ // Target declarations.
+ PrintSectionHelp("Target declarations", "<function>", "targets");
+ for (const auto& func : functions::GetFunctions()) {
+ if (func.second.is_target)
+ PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
+ }
+
+ // Functions.
+ PrintSectionHelp("Buildfile functions", "<function>", "functions");
+ for (const auto& func : functions::GetFunctions()) {
+ if (!func.second.is_target)
+ PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
+ }
+
+ // Built-in variables.
+ PrintSectionHelp("Built-in predefined variables", "<variable>",
+ "predefined_variables");
+ for (const auto& builtin : variables::GetBuiltinVariables()) {
+ PrintShortHelp(builtin.second.help_short,
+ kVariableLinkPrefix + builtin.first);
+ }
+
+ // Target variables.
+ PrintSectionHelp("Variables you set in targets", "<variable>",
+ "target_variables");
+ for (const auto& target : variables::GetTargetVariables()) {
+ PrintShortHelp(target.second.help_short,
+ kVariableLinkPrefix + target.first);
+ }
+
+ PrintSectionHelp("Other help topics", "", "other");
+ PrintShortHelp("all: Print all the help at once");
+ PrintShortHelp("buildargs: How build arguments work.", "buildargs");
+ PrintShortHelp("dotfile: Info about the toplevel .gn file.", "dotfile");
+ PrintShortHelp("execution: Build graph and execution overview.", "execution");
+ PrintShortHelp("grammar: Language and grammar for GN build files.",
+ "grammar");
+ PrintShortHelp(
+ "input_conversion: Processing input from exec_script and read_file.",
+ "io_conversion");
+ PrintShortHelp("file_pattern: Matching more than one file.", "file_pattern");
+ PrintShortHelp("label_pattern: Matching more than one label.",
+ "label_pattern");
+ PrintShortHelp("labels: About labels.", "labels");
+ PrintShortHelp("metadata_collection: About metadata and its collection.",
+ "metadata_collection");
+ PrintShortHelp("ninja_rules: How Ninja build rules are named.",
+ "ninja_rules");
+ PrintShortHelp("nogncheck: Annotating includes for checking.", "nogncheck");
+ PrintShortHelp(
+ "output_conversion: Specifies how to transform a value to output.",
+ "io_conversion");
+ PrintShortHelp("runtime_deps: How runtime dependency computation works.",
+ "runtime_deps");
+ PrintShortHelp("source_expansion: Map sources to outputs for scripts.",
+ "source_expansion");
+ PrintShortHelp("switches: Show available command-line switches.",
+ "switch_list");
+}
+
+void PrintSwitchHelp() {
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
+
+ // This uses "switch_list" for the tag because Markdown seems to generate
+ // implicit tags for headings that match the strings, and some headings are
+ // labeled "switches".
+ PrintLongHelp(R"(Available global switches
+
+ Do "gn help --the_switch_you_want_help_on" for more. Individual commands may
+ take command-specific switches not listed here. See the help on your specific
+ command for more.
+)",
+ "switch_list");
+
+ if (is_markdown)
+ OutputString("```\n", DECORATION_NONE);
+
+ for (const auto& s : switches::GetSwitches())
+ PrintShortHelp(s.second.short_help);
+
+ if (is_markdown)
+ OutputString("```\n", DECORATION_NONE);
+
+ OutputString("\n");
+}
+
+void PrintAllHelp() {
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
+
+ if (is_markdown) {
+ OutputString("# GN Reference\n\n");
+ OutputString(
+ "*This page is automatically generated from* "
+ "`gn help --markdown all`.\n\n");
+
+ // Generate our own table of contents so that we have more control
+ // over what's in and out.
+ OutputString("## Contents\n\n");
+ }
+
+ PrintToplevelHelp();
+
+ OutputString("\n");
+
+ if (is_markdown) {
+ OutputString("## <a name=\"commands\"></a>Commands\n\n", DECORATION_NONE,
+ NO_ESCAPING);
+ }
+ for (const auto& c : commands::GetCommands())
+ PrintLongHelp(c.second.help, kCommandLinkPrefix + c.first);
+
+ if (is_markdown) {
+ OutputString("## <a name=\"targets\"></a>Target declarations\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
+ for (const auto& f : functions::GetFunctions()) {
+ if (f.second.is_target)
+ PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
+ }
+
+ if (is_markdown) {
+ OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
+ for (const auto& f : functions::GetFunctions()) {
+ if (!f.second.is_target)
+ PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
+ }
+
+ if (is_markdown) {
+ OutputString(
+ "## <a name=\"predefined_variables\"></a>"
+ "Built-in predefined variables\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
+ for (const auto& v : variables::GetBuiltinVariables())
+ PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
+
+ if (is_markdown) {
+ OutputString(
+ "## <a name=\"target_variables\"></a>"
+ "Variables you set in targets\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
+ for (const auto& v : variables::GetTargetVariables())
+ PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
+
+ if (is_markdown) {
+ OutputString("## <a name=\"other\"></a>Other help topics\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
+ PrintLongHelp(kBuildArgs_Help, "buildargs");
+ PrintLongHelp(kDotfile_Help, "dotfile");
+ PrintLongHelp(kExecution_Help, "execution");
+ PrintLongHelp(kGrammar_Help, "grammar");
+ PrintLongHelp(kInputOutputConversion_Help, "io_conversion");
+ PrintLongHelp(kFilePattern_Help, "file_pattern");
+ PrintLongHelp(kLabelPattern_Help, "label_pattern");
+ PrintLongHelp(kLabels_Help, "labels");
+ PrintLongHelp(kMetadata_Help, "metadata_collection");
+ PrintLongHelp(kNinjaRules_Help, "ninja_rules");
+ PrintLongHelp(kNoGnCheck_Help, "nogncheck");
+ PrintLongHelp(kRuntimeDeps_Help, "runtime_deps");
+ PrintLongHelp(kSourceExpansion_Help, "source_expansion");
+
+ PrintSwitchHelp();
+}
+
+// Prints help on the given switch. There should be no leading hyphens. Returns
+// true if the switch was found and help was printed. False means the switch is
+// unknown.
+bool PrintHelpOnSwitch(const std::string& what) {
+ const switches::SwitchInfoMap& all = switches::GetSwitches();
+ switches::SwitchInfoMap::const_iterator found =
+ all.find(std::string_view(what));
+ if (found == all.end())
+ return false;
+ PrintLongHelp(found->second.long_help);
+ return true;
+}
+
+// Special-case help for ambiguous "args" case.
+void PrintArgsHelp() {
+ PrintLongHelp(
+ "The string \"args\" is both a command and a variable for action "
+ "targets.\nShowing help for both...\n\n");
+ PrintLongHelp(commands::kArgs_Help);
+ PrintLongHelp(
+ "\n----------------------------------------------------------------------"
+ "---------\n\n");
+ PrintLongHelp(variables::kArgs_Help);
+}
+
+} // namespace
+
+const char kHelp[] = "help";
+const char kHelp_HelpShort[] = "help: Does what you think.";
+const char kHelp_Help[] =
+ R"(gn help <anything>
+
+ Yo dawg, I heard you like help on your help so I put help on the help in the
+ help.
+
+ You can also use "all" as the parameter to get all help at once.
+
+Switches
+
+ --markdown
+ Format output in markdown syntax.
+
+Example
+
+ gn help --markdown all
+ Dump all help to stdout in markdown format.
+)";
+
+int RunHelp(const std::vector<std::string>& args) {
+ std::string what;
+ if (args.size() == 0) {
+ // If no argument is specified, check for switches to allow things like
+ // "gn help --args" for help on the args switch.
+ const base::CommandLine::SwitchMap& switches =
+ base::CommandLine::ForCurrentProcess()->GetSwitches();
+ if (switches.empty()) {
+ // Still nothing, show help overview.
+ PrintToplevelHelp();
+ return 0;
+ }
+
+ // Switch help needs to be done separately. The CommandLine will strip the
+ // switch separators so --args will come out as "args" which is then
+ // ambiguous with the variable named "args".
+ if (!PrintHelpOnSwitch(switches.begin()->first))
+ PrintToplevelHelp();
+ return 0;
+ } else {
+ what = args[0];
+ }
+
+ std::vector<std::string_view> all_help_topics;
+
+ // Special-case ambiguous topics.
+ if (what == "args") {
+ PrintArgsHelp();
+ return 0;
+ }
+
+ // Check commands.
+ const commands::CommandInfoMap& command_map = commands::GetCommands();
+ auto found_command = command_map.find(what);
+ if (found_command != command_map.end()) {
+ PrintLongHelp(found_command->second.help);
+ return 0;
+ }
+ for (const auto& entry : command_map)
+ all_help_topics.push_back(entry.first);
+
+ // Check functions.
+ const functions::FunctionInfoMap& function_map = functions::GetFunctions();
+ auto found_function = function_map.find(what);
+ if (found_function != function_map.end())
+ PrintLongHelp(found_function->second.help);
+ for (const auto& entry : function_map)
+ all_help_topics.push_back(entry.first);
+
+ // Builtin variables.
+ const variables::VariableInfoMap& builtin_vars =
+ variables::GetBuiltinVariables();
+ auto found_builtin_var = builtin_vars.find(what);
+ if (found_builtin_var != builtin_vars.end())
+ PrintLongHelp(found_builtin_var->second.help);
+ for (const auto& entry : builtin_vars)
+ all_help_topics.push_back(entry.first);
+
+ // Target variables.
+ const variables::VariableInfoMap& target_vars =
+ variables::GetTargetVariables();
+ auto found_target_var = target_vars.find(what);
+ if (found_target_var != target_vars.end())
+ PrintLongHelp(found_target_var->second.help);
+ for (const auto& entry : target_vars)
+ all_help_topics.push_back(entry.first);
+
+ if (found_function != function_map.end() ||
+ found_builtin_var != builtin_vars.end() ||
+ found_target_var != target_vars.end())
+ return 0;
+
+ // Random other topics.
+ std::map<std::string, void (*)()> random_topics;
+ random_topics["all"] = PrintAllHelp;
+ random_topics["execution"] = []() { PrintLongHelp(kExecution_Help); };
+ random_topics["buildargs"] = []() { PrintLongHelp(kBuildArgs_Help); };
+ random_topics["dotfile"] = []() { PrintLongHelp(kDotfile_Help); };
+ random_topics["grammar"] = []() { PrintLongHelp(kGrammar_Help); };
+ random_topics["io_conversion"] = []() {
+ PrintLongHelp(kInputOutputConversion_Help);
+ };
+ random_topics["file_pattern"] = []() { PrintLongHelp(kFilePattern_Help); };
+ random_topics["label_pattern"] = []() { PrintLongHelp(kLabelPattern_Help); };
+ random_topics["labels"] = []() { PrintLongHelp(kLabels_Help); };
+ random_topics["metadata_collection"] = []() {
+ PrintLongHelp(kMetadata_Help);
+ };
+ random_topics["ninja_rules"] = []() { PrintLongHelp(kNinjaRules_Help); };
+ random_topics["nogncheck"] = []() { PrintLongHelp(kNoGnCheck_Help); };
+ random_topics["runtime_deps"] = []() { PrintLongHelp(kRuntimeDeps_Help); };
+ random_topics["source_expansion"] = []() {
+ PrintLongHelp(kSourceExpansion_Help);
+ };
+ random_topics["switches"] = PrintSwitchHelp;
+ auto found_random_topic = random_topics.find(what);
+ if (found_random_topic != random_topics.end()) {
+ found_random_topic->second();
+ return 0;
+ }
+ for (const auto& entry : random_topics)
+ all_help_topics.push_back(entry.first);
+
+ // No help on this.
+ Err(Location(), "No help on \"" + what + "\".").PrintToStdout();
+ std::string_view suggestion = SpellcheckString(what, all_help_topics);
+ if (suggestion.empty()) {
+ OutputString("Run `gn help` for a list of available topics.\n",
+ DECORATION_NONE);
+ } else {
+ OutputString("Did you mean `gn help " + std::string(suggestion) + "`?\n",
+ DECORATION_NONE);
+ }
+ return 1;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_ls.cc b/gn/src/gn/command_ls.cc
new file mode 100644
index 00000000000..0dbf911a26a
--- /dev/null
+++ b/gn/src/gn/command_ls.cc
@@ -0,0 +1,104 @@
+// 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 <algorithm>
+#include <set>
+
+#include "base/command_line.h"
+#include "gn/commands.h"
+#include "gn/label_pattern.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+
+namespace commands {
+
+const char kLs[] = "ls";
+const char kLs_HelpShort[] = "ls: List matching targets.";
+const char kLs_Help[] =
+ R"(gn ls <out_dir> [<label_pattern>] [--default-toolchain] [--as=...]
+ [--type=...] [--testonly=...]
+
+ Lists all targets matching the given pattern for the given build directory.
+ By default, only targets in the default toolchain will be matched unless a
+ toolchain is explicitly supplied.
+
+ If the label pattern is unspecified, list all targets. The label pattern is
+ not a general regular expression (see "gn help label_pattern"). If you need
+ more complex expressions, pipe the result through grep.
+
+Options
+
+)" TARGET_PRINTING_MODE_COMMAND_LINE_HELP "\n" DEFAULT_TOOLCHAIN_SWITCH_HELP
+ "\n" TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
+ "\n" TARGET_TYPE_FILTER_COMMAND_LINE_HELP
+ R"(
+Examples
+
+ gn ls out/Debug
+ Lists all targets in the default toolchain.
+
+ gn ls out/Debug "//base/*"
+ Lists all targets in the directory base and all subdirectories.
+
+ gn ls out/Debug "//base:*"
+ Lists all targets defined in //base/BUILD.gn.
+
+ gn ls out/Debug //base --as=output
+ Lists the build output file for //base:base
+
+ gn ls out/Debug --type=executable
+ Lists all executables produced by the build.
+
+ gn ls out/Debug "//base/*" --as=output | xargs ninja -C out/Debug
+ Builds all targets in //base and all subdirectories.
+)";
+
+int RunLs(const std::vector<std::string>& args) {
+ if (args.size() == 0) {
+ Err(Location(), "Unknown command format. See \"gn help ls\"",
+ "Usage: \"gn ls <build dir> [<label_pattern>]*\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false) || !setup->Run())
+ return 1;
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool default_toolchain_only = cmdline->HasSwitch(switches::kDefaultToolchain);
+
+ std::vector<const Target*> matches;
+ if (args.size() > 1) {
+ // Some patterns or explicit labels were specified.
+ std::vector<std::string> inputs(args.begin() + 1, args.end());
+
+ UniqueVector<const Target*> target_matches;
+ UniqueVector<const Config*> config_matches;
+ UniqueVector<const Toolchain*> toolchain_matches;
+ UniqueVector<SourceFile> file_matches;
+ if (!ResolveFromCommandLineInput(setup, inputs, default_toolchain_only,
+ &target_matches, &config_matches,
+ &toolchain_matches, &file_matches))
+ return 1;
+ matches.insert(matches.begin(), target_matches.begin(),
+ target_matches.end());
+ } else if (default_toolchain_only) {
+ // List all resolved targets in the default toolchain.
+ for (auto* target : setup->builder().GetAllResolvedTargets()) {
+ if (target->settings()->is_default())
+ matches.push_back(target);
+ }
+ } else {
+ // List all resolved targets.
+ matches = setup->builder().GetAllResolvedTargets();
+ }
+ FilterAndPrintTargets(false, &matches);
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_meta.cc b/gn/src/gn/command_meta.cc
new file mode 100644
index 00000000000..6ee5fe2fdcb
--- /dev/null
+++ b/gn/src/gn/command_meta.cc
@@ -0,0 +1,170 @@
+// 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 <algorithm>
+#include <set>
+
+#include "base/command_line.h"
+#include "base/strings/string_split.h"
+#include "gn/commands.h"
+#include "gn/metadata_walk.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+
+namespace commands {
+
+const char kMeta[] = "meta";
+const char kMeta_HelpShort[] = "meta: List target metadata collection results.";
+const char kMeta_Help[] =
+ R"(gn meta
+
+ gn meta <out_dir> <target>* --data=<key>[,<key>*]* [--walk=<key>[,<key>*]*]
+ [--rebase=<dest dir>]
+
+ Lists collected metaresults of all given targets for the given data key(s),
+ collecting metadata dependencies as specified by the given walk key(s).
+
+ See `gn help generated_file` for more information on the walk.
+
+Arguments
+
+ <target(s)>
+ A list of target labels from which to initiate the walk.
+
+ --data
+ A list of keys from which to extract data. In each target walked, its metadata
+ scope is checked for the presence of these keys. If present, the contents of
+ those variable in the scope are appended to the results list.
+
+ --walk (optional)
+ A list of keys from which to control the walk. In each target walked, its
+ metadata scope is checked for the presence of any of these keys. If present,
+ the contents of those variables is checked to ensure that it is a label of
+ a valid dependency of the target and then added to the set of targets to walk.
+ If the empty string ("") is present in any of these keys, all deps and data_deps
+ are added to the walk set.
+
+ --rebase (optional)
+ A destination directory onto which to rebase any paths found. If set, all
+ collected metadata will be rebased onto this path. This option will throw errors
+ if collected metadata is not a list of strings.
+
+Examples
+
+ gn meta out/Debug "//base/foo" --data=files
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of its dependency tree.
+
+ gn meta out/Debug "//base/foo" --data=files --data=other
+ Lists collected metaresults for the `files` and `other` keys in the
+ //base/foo:foo target and all of its dependency tree.
+
+ gn meta out/Debug "//base/foo" --data=files --walk=stop
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of the dependencies listed in the `stop` key (and so on).
+
+ gn meta out/Debug "//base/foo" --data=files --rebase="/"
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of its dependency tree, rebasing the strings in the `files`
+ key onto the source directory of the target's declaration relative to "/".
+)";
+
+int RunMeta(const std::vector<std::string>& args) {
+ if (args.size() == 0) {
+ Err(Location(), "Unknown command format. See \"gn help meta\"",
+ "Usage: \"gn meta <out_dir> <target>* --data=<key>[,<key>*] "
+ "[--walk=<key>[,<key>*]*] [--rebase=<dest dir>]\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false) || !setup->Run())
+ return 1;
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ std::string rebase_dir =
+ cmdline->GetSwitchValueASCII(switches::kMetaRebaseFiles);
+ std::string data_keys_str =
+ cmdline->GetSwitchValueASCII(switches::kMetaDataKeys);
+ std::string walk_keys_str =
+ cmdline->GetSwitchValueASCII(switches::kMetaWalkKeys);
+
+ std::vector<std::string> inputs(args.begin() + 1, args.end());
+
+ UniqueVector<const Target*> targets;
+ for (const auto& input : inputs) {
+ const Target* target = ResolveTargetFromCommandLineString(setup, input);
+ if (!target) {
+ Err(Location(), "Unknown target " + input).PrintToStdout();
+ return 1;
+ }
+ targets.push_back(target);
+ }
+
+ std::vector<std::string> data_keys = base::SplitString(
+ data_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (data_keys.empty()) {
+ return 1;
+ }
+ std::vector<std::string> walk_keys = base::SplitString(
+ walk_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ Err err;
+ std::set<const Target*> targets_walked;
+ SourceDir rebase_source_dir(rebase_dir);
+ // When SourceDir constructor is supplied with an empty string,
+ // a trailing slash will be added. This prevent SourceDir::is_null()
+ // from returning true. Explicitly remove this traling slash here.
+ if (rebase_dir.empty()) {
+ rebase_source_dir = SourceDir();
+ }
+ std::vector<Value> result =
+ WalkMetadata(targets, data_keys, walk_keys, rebase_source_dir,
+ &targets_walked, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ OutputString("Metadata values\n", DECORATION_DIM);
+ for (const auto& value : result)
+ OutputString("\n" + value.ToString(false) + "\n");
+
+ // TODO(juliehockett): We should have better dep tracing and error support for
+ // this. Also possibly data about where different values came from.
+ OutputString("\nExtracted from:\n", DECORATION_DIM);
+ bool first = true;
+ for (const auto* target : targets_walked) {
+ if (!first) {
+ first = false;
+ OutputString(", ", DECORATION_DIM);
+ }
+ OutputString(target->label().GetUserVisibleName(true) + "\n");
+ }
+ OutputString("\nusing data keys:\n", DECORATION_DIM);
+ first = true;
+ for (const auto& key : data_keys) {
+ if (!first) {
+ first = false;
+ OutputString(", ");
+ }
+ OutputString(key + "\n");
+ }
+ if (!walk_keys.empty()) {
+ OutputString("\nand using walk keys:\n", DECORATION_DIM);
+ first = true;
+ for (const auto& key : walk_keys) {
+ if (!first) {
+ first = false;
+ OutputString(", ");
+ }
+ OutputString(key + "\n");
+ }
+ }
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_outputs.cc b/gn/src/gn/command_outputs.cc
new file mode 100644
index 00000000000..90d1b1e7e20
--- /dev/null
+++ b/gn/src/gn/command_outputs.cc
@@ -0,0 +1,155 @@
+// Copyright 2020 The Chromium 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 <stddef.h>
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "gn/commands.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+
+namespace commands {
+
+const char kOutputs[] = "outputs";
+const char kOutputs_HelpShort[] = "outputs: Which files a source/target make.";
+const char kOutputs_Help[] =
+ R"(gn outputs <out_dir> <list of target or file names...>
+
+ Lists the output files corresponding to the given target(s) or file name(s).
+ There can be multiple outputs because there can be more than one output
+ generated by a build step, and there can be more than one toolchain matched.
+ You can also list multiple inputs which will generate a union of all the
+ outputs from those inputs.
+
+ - The input target/file names are relative to the current directory.
+
+ - The output file names are relative to the root build directory.
+
+ This command is useful for finding a ninja command that will build only a
+ portion of the build.
+
+Target outputs
+
+ If the parameter is a target name that includes a toolchain, it will match
+ only that target in that toolchain. If no toolchain is specified, it will
+ match all targets with that name in any toolchain.
+
+ The result will be the outputs specified by that target which could be a
+ library, executable, output of an action, a stamp file, etc.
+
+File outputs
+
+ If the parameter is a file name it will compute the output for that compile
+ step for all targets in all toolchains that contain that file as a source
+ file.
+
+ If the source is not compiled (e.g. a header or text file), the command will
+ produce no output.
+
+ If the source is listed as an "input" to a binary target or action will
+ resolve to that target's outputs.
+
+Example
+
+ gn outputs out/debug some/directory:some_target
+ Find the outputs of a given target.
+
+ gn outputs out/debug src/project/my_file.cc | xargs ninja -C out/debug
+ Compiles just the given source file in all toolchains it's referenced in.
+
+ git diff --name-only | xargs gn outputs out/x64 | xargs ninja -C out/x64
+ Compiles all files changed in git.
+)";
+
+int RunOutputs(const std::vector<std::string>& args) {
+ if (args.size() < 2) {
+ Err(Location(),
+ "Expected a build dir and one or more input files or targets.\n"
+ "Usage: \"gn outputs <out_dir> <target-or-file>*\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false))
+ return 1;
+ if (!setup->Run())
+ return 1;
+
+ std::vector<std::string> inputs(args.begin() + 1, args.end());
+
+ UniqueVector<const Target*> target_matches;
+ UniqueVector<const Config*> config_matches;
+ UniqueVector<const Toolchain*> toolchain_matches;
+ UniqueVector<SourceFile> file_matches;
+ if (!ResolveFromCommandLineInput(setup, inputs, false, &target_matches,
+ &config_matches, &toolchain_matches,
+ &file_matches))
+ return 1;
+
+ // We only care about targets and files.
+ if (target_matches.empty() && file_matches.empty()) {
+ Err(Location(), "The input matched no targets or files.").PrintToStdout();
+ return 1;
+ }
+
+ // Resulting outputs.
+ std::vector<OutputFile> outputs;
+
+ // Files. This must go first because it may add to the "targets" list.
+ std::vector<const Target*> all_targets =
+ setup->builder().GetAllResolvedTargets();
+ for (const SourceFile& file : file_matches) {
+ std::vector<TargetContainingFile> targets;
+ GetTargetsContainingFile(setup, all_targets, file, false, &targets);
+ if (targets.empty()) {
+ Err(Location(), base::StringPrintf("No targets reference the file '%s'.",
+ file.value().c_str()))
+ .PrintToStdout();
+ return 1;
+ }
+
+ // There can be more than one target that references this file, evaluate the
+ // output name in all of them.
+ for (const TargetContainingFile& pair : targets) {
+ if (pair.second == HowTargetContainsFile::kInputs) {
+ // Inputs maps to the target itself. This will be evaluated below.
+ target_matches.push_back(pair.first);
+ } else if (pair.second == HowTargetContainsFile::kSources) {
+ // Source file, check it.
+ const char* computed_tool = nullptr;
+ std::vector<OutputFile> file_outputs;
+ pair.first->GetOutputFilesForSource(file, &computed_tool,
+ &file_outputs);
+ outputs.insert(outputs.end(), file_outputs.begin(), file_outputs.end());
+ }
+ }
+ }
+
+ // Targets.
+ for (const Target* target : target_matches) {
+ std::vector<SourceFile> output_files;
+ Err err;
+ if (!target->GetOutputsAsSourceFiles(LocationRange(), true, &output_files,
+ &err)) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ // Convert to OutputFiles.
+ for (const SourceFile& file : output_files)
+ outputs.emplace_back(&setup->build_settings(), file);
+ }
+
+ // Print.
+ for (const OutputFile& output_file : outputs)
+ printf("%s\n", output_file.value().c_str());
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_path.cc b/gn/src/gn/command_path.cc
new file mode 100644
index 00000000000..9ea7f72c34b
--- /dev/null
+++ b/gn/src/gn/command_path.cc
@@ -0,0 +1,413 @@
+// 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 <stddef.h>
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "gn/commands.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+
+namespace commands {
+
+namespace {
+
+enum class DepType { NONE, PUBLIC, PRIVATE, DATA };
+
+// The dependency paths are stored in a vector. Assuming the chain:
+// A --[public]--> B --[private]--> C
+// The stack will look like:
+// [0] = A, NONE (this has no dep type since nobody depends on it)
+// [1] = B, PUBLIC
+// [2] = C, PRIVATE
+using TargetDep = std::pair<const Target*, DepType>;
+using PathVector = std::vector<TargetDep>;
+
+// How to search.
+enum class PrivateDeps { INCLUDE, EXCLUDE };
+enum class DataDeps { INCLUDE, EXCLUDE };
+enum class PrintWhat { ONE, ALL };
+
+struct Options {
+ Options()
+ : print_what(PrintWhat::ONE), public_only(false), with_data(false) {}
+
+ PrintWhat print_what;
+ bool public_only;
+ bool with_data;
+};
+
+using WorkQueue = std::list<PathVector>;
+
+struct Stats {
+ Stats() : public_paths(0), other_paths(0) {}
+
+ int total_paths() const { return public_paths + other_paths; }
+
+ int public_paths;
+ int other_paths;
+
+ // Stores targets that have a path to the destination, and whether that
+ // path is public, private, or data.
+ std::map<const Target*, DepType> found_paths;
+};
+
+// If the implicit_last_dep is not "none", this type indicates the
+// classification of the elided last part of path.
+DepType ClassifyPath(const PathVector& path, DepType implicit_last_dep) {
+ DepType result;
+ if (implicit_last_dep != DepType::NONE)
+ result = implicit_last_dep;
+ else
+ result = DepType::PUBLIC;
+
+ // Skip the 0th one since that is always NONE.
+ for (size_t i = 1; i < path.size(); i++) {
+ // PRIVATE overrides PUBLIC, and DATA overrides everything (the idea is
+ // to find the worst link in the path).
+ if (path[i].second == DepType::PRIVATE) {
+ if (result == DepType::PUBLIC)
+ result = DepType::PRIVATE;
+ } else if (path[i].second == DepType::DATA) {
+ result = DepType::DATA;
+ }
+ }
+ return result;
+}
+
+const char* StringForDepType(DepType type) {
+ switch (type) {
+ case DepType::PUBLIC:
+ return "public";
+ case DepType::PRIVATE:
+ return "private";
+ case DepType::DATA:
+ return "data";
+ break;
+ case DepType::NONE:
+ default:
+ return "";
+ }
+}
+
+// Prints the given path. If the implicit_last_dep is not "none", the last
+// dependency will show an elided dependency with the given annotation.
+void PrintPath(const PathVector& path, DepType implicit_last_dep) {
+ if (path.empty())
+ return;
+
+ // Don't print toolchains unless they differ from the first target.
+ const Label& default_toolchain = path[0].first->label().GetToolchainLabel();
+
+ for (size_t i = 0; i < path.size(); i++) {
+ OutputString(path[i].first->label().GetUserVisibleName(default_toolchain));
+
+ // Output dependency type.
+ if (i == path.size() - 1) {
+ // Last one either gets the implicit last dep type or nothing.
+ if (implicit_last_dep != DepType::NONE) {
+ OutputString(std::string(" --> see ") +
+ StringForDepType(implicit_last_dep) +
+ " chain printed above...",
+ DECORATION_DIM);
+ }
+ } else {
+ // Take type from the next entry.
+ OutputString(
+ std::string(" --[") + StringForDepType(path[i + 1].second) + "]-->",
+ DECORATION_DIM);
+ }
+ OutputString("\n");
+ }
+
+ OutputString("\n");
+}
+
+void InsertTargetsIntoFoundPaths(const PathVector& path,
+ DepType implicit_last_dep,
+ Stats* stats) {
+ DepType type = ClassifyPath(path, implicit_last_dep);
+
+ bool inserted = false;
+
+ // Don't try to insert the 0th item in the list which is the "from" target.
+ // The search will be run more than once (for the different path types) and
+ // if the "from" target was in the list, subsequent passes could never run
+ // the starting point is alredy in the list of targets considered).
+ //
+ // One might imagine an alternate implementation where all items are counted
+ // here but the "from" item is erased at the beginning of each search, but
+ // that will mess up the metrics (the private search pass will find the
+ // same public paths as the previous public pass, "inserted" will be true
+ // here since the item wasn't found, and the public path will be
+ // double-counted in the stats.
+ for (size_t i = 1; i < path.size(); i++) {
+ const auto& pair = path[i];
+
+ // Don't overwrite an existing one. The algorithm works by first doing
+ // public, then private, then data, so anything already there is guaranteed
+ // at least as good as our addition.
+ if (stats->found_paths.find(pair.first) == stats->found_paths.end()) {
+ stats->found_paths.insert(std::make_pair(pair.first, type));
+ inserted = true;
+ }
+ }
+
+ if (inserted) {
+ // Only count this path in the stats if any part of it was actually new.
+ if (type == DepType::PUBLIC)
+ stats->public_paths++;
+ else
+ stats->other_paths++;
+ }
+}
+
+void BreadthFirstSearch(const Target* from,
+ const Target* to,
+ PrivateDeps private_deps,
+ DataDeps data_deps,
+ PrintWhat print_what,
+ Stats* stats) {
+ // Seed the initial stack with just the "from" target.
+ PathVector initial_stack;
+ initial_stack.emplace_back(from, DepType::NONE);
+ WorkQueue work_queue;
+ work_queue.push_back(initial_stack);
+
+ // Track checked targets to avoid checking the same once more than once.
+ std::set<const Target*> visited;
+
+ while (!work_queue.empty()) {
+ PathVector current_path = work_queue.front();
+ work_queue.pop_front();
+
+ const Target* current_target = current_path.back().first;
+
+ if (current_target == to) {
+ // Found a new path.
+ if (stats->total_paths() == 0 || print_what == PrintWhat::ALL)
+ PrintPath(current_path, DepType::NONE);
+
+ // Insert all nodes on the path into the found paths list. Since we're
+ // doing search breadth first, we know that the current path is the best
+ // path for all nodes on it.
+ InsertTargetsIntoFoundPaths(current_path, DepType::NONE, stats);
+ } else {
+ // Check for a path that connects to an already known-good one. Printing
+ // this here will mean the results aren't strictly in depth-first order
+ // since there could be many items on the found path this connects to.
+ // Doing this here will mean that the output is sorted by length of items
+ // printed (with the redundant parts of the path omitted) rather than
+ // complete path length.
+ const auto& found_current_target =
+ stats->found_paths.find(current_target);
+ if (found_current_target != stats->found_paths.end()) {
+ if (stats->total_paths() == 0 || print_what == PrintWhat::ALL)
+ PrintPath(current_path, found_current_target->second);
+
+ // Insert all nodes on the path into the found paths list since we know
+ // everything along this path also leads to the destination.
+ InsertTargetsIntoFoundPaths(current_path, found_current_target->second,
+ stats);
+ continue;
+ }
+ }
+
+ // If we've already checked this one, stop. This should be after the above
+ // check for a known-good check, because known-good ones will always have
+ // been previously visited.
+ if (visited.find(current_target) == visited.end())
+ visited.insert(current_target);
+ else
+ continue;
+
+ // Add public deps for this target to the queue.
+ for (const auto& pair : current_target->public_deps()) {
+ work_queue.push_back(current_path);
+ work_queue.back().push_back(TargetDep(pair.ptr, DepType::PUBLIC));
+ }
+
+ if (private_deps == PrivateDeps::INCLUDE) {
+ // Add private deps.
+ for (const auto& pair : current_target->private_deps()) {
+ work_queue.push_back(current_path);
+ work_queue.back().push_back(TargetDep(pair.ptr, DepType::PRIVATE));
+ }
+ }
+
+ if (data_deps == DataDeps::INCLUDE) {
+ // Add data deps.
+ for (const auto& pair : current_target->data_deps()) {
+ work_queue.push_back(current_path);
+ work_queue.back().push_back(TargetDep(pair.ptr, DepType::DATA));
+ }
+ }
+ }
+}
+
+void DoSearch(const Target* from,
+ const Target* to,
+ const Options& options,
+ Stats* stats) {
+ BreadthFirstSearch(from, to, PrivateDeps::EXCLUDE, DataDeps::EXCLUDE,
+ options.print_what, stats);
+ if (!options.public_only) {
+ // Check private deps.
+ BreadthFirstSearch(from, to, PrivateDeps::INCLUDE, DataDeps::EXCLUDE,
+ options.print_what, stats);
+ if (options.with_data) {
+ // Check data deps.
+ BreadthFirstSearch(from, to, PrivateDeps::INCLUDE, DataDeps::INCLUDE,
+ options.print_what, stats);
+ }
+ }
+}
+
+} // namespace
+
+const char kPath[] = "path";
+const char kPath_HelpShort[] = "path: Find paths between two targets.";
+const char kPath_Help[] =
+ R"(gn path <out_dir> <target_one> <target_two>
+
+ Finds paths of dependencies between two targets. Each unique path will be
+ printed in one group, and groups will be separate by newlines. The two
+ targets can appear in either order (paths will be found going in either
+ direction).
+
+ By default, a single path will be printed. If there is a path with only
+ public dependencies, the shortest public path will be printed. Otherwise, the
+ shortest path using either public or private dependencies will be printed. If
+ --with-data is specified, data deps will also be considered. If there are
+ multiple shortest paths, an arbitrary one will be selected.
+
+Interesting paths
+
+ In a large project, there can be 100's of millions of unique paths between a
+ very high level and a common low-level target. To make the output more useful
+ (and terminate in a reasonable time), GN will not revisit sub-paths
+ previously known to lead to the target.
+
+Options
+
+ --all
+ Prints all "interesting" paths found rather than just the first one.
+ Public paths will be printed first in order of increasing length, followed
+ by non-public paths in order of increasing length.
+
+ --public
+ Considers only public paths. Can't be used with --with-data.
+
+ --with-data
+ Additionally follows data deps. Without this flag, only public and private
+ linked deps will be followed. Can't be used with --public.
+
+Example
+
+ gn path out/Default //base //gn
+)";
+
+int RunPath(const std::vector<std::string>& args) {
+ if (args.size() != 3) {
+ Err(Location(), "Unknown command format. See \"gn help path\"",
+ "Usage: \"gn path <out_dir> <target_one> <target_two>\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false))
+ return 1;
+ if (!setup->Run())
+ return 1;
+
+ const Target* target1 = ResolveTargetFromCommandLineString(setup, args[1]);
+ if (!target1)
+ return 1;
+ const Target* target2 = ResolveTargetFromCommandLineString(setup, args[2]);
+ if (!target2)
+ return 1;
+
+ Options options;
+ options.print_what = base::CommandLine::ForCurrentProcess()->HasSwitch("all")
+ ? PrintWhat::ALL
+ : PrintWhat::ONE;
+ options.public_only =
+ base::CommandLine::ForCurrentProcess()->HasSwitch("public");
+ options.with_data =
+ base::CommandLine::ForCurrentProcess()->HasSwitch("with-data");
+ if (options.public_only && options.with_data) {
+ Err(Location(), "Can't use --public with --with-data for 'gn path'.",
+ "Your zealous over-use of arguments has inevitably resulted in an "
+ "invalid\ncombination of flags.")
+ .PrintToStdout();
+ return 1;
+ }
+
+ Stats stats;
+ DoSearch(target1, target2, options, &stats);
+ if (stats.total_paths() == 0) {
+ // If we don't find a path going "forwards", try the reverse direction.
+ // Deps can only go in one direction without having a cycle, which will
+ // have caused a run failure above.
+ DoSearch(target2, target1, options, &stats);
+ }
+
+ // This string is inserted in the results to annotate whether the result
+ // is only public or includes data deps or not.
+ const char* path_annotation = "";
+ if (options.public_only)
+ path_annotation = "public ";
+ else if (!options.with_data)
+ path_annotation = "non-data ";
+
+ if (stats.total_paths() == 0) {
+ // No results.
+ OutputString(
+ base::StringPrintf("No %spaths found between these two targets.\n",
+ path_annotation),
+ DECORATION_YELLOW);
+ } else if (stats.total_paths() == 1) {
+ // Exactly one result.
+ OutputString(base::StringPrintf("1 %spath found.", path_annotation),
+ DECORATION_YELLOW);
+ if (!options.public_only) {
+ if (stats.public_paths)
+ OutputString(" It is public.");
+ else
+ OutputString(" It is not public.");
+ }
+ OutputString("\n");
+ } else {
+ if (options.print_what == PrintWhat::ALL) {
+ // Showing all paths when there are many.
+ OutputString(base::StringPrintf("%d \"interesting\" %spaths found.",
+ stats.total_paths(), path_annotation),
+ DECORATION_YELLOW);
+ if (!options.public_only) {
+ OutputString(
+ base::StringPrintf(" %d of them are public.", stats.public_paths));
+ }
+ OutputString("\n");
+ } else {
+ // Showing one path when there are many.
+ OutputString(
+ base::StringPrintf("Showing one of %d \"interesting\" %spaths.",
+ stats.total_paths(), path_annotation),
+ DECORATION_YELLOW);
+ if (!options.public_only) {
+ OutputString(
+ base::StringPrintf(" %d of them are public.", stats.public_paths));
+ }
+ OutputString("\nUse --all to print all paths.\n");
+ }
+ }
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/command_refs.cc b/gn/src/gn/command_refs.cc
new file mode 100644
index 00000000000..dc27f5660d6
--- /dev/null
+++ b/gn/src/gn/command_refs.cc
@@ -0,0 +1,453 @@
+// 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 <stddef.h>
+
+#include <map>
+#include <set>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "gn/commands.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file.h"
+#include "gn/item.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+
+namespace commands {
+
+namespace {
+
+using TargetSet = std::set<const Target*>;
+using TargetVector = std::vector<const Target*>;
+
+// Maps targets to the list of targets that depend on them.
+using DepMap = std::multimap<const Target*, const Target*>;
+
+// Populates the reverse dependency map for the targets in the Setup.
+void FillDepMap(Setup* setup, DepMap* dep_map) {
+ for (auto* target : setup->builder().GetAllResolvedTargets()) {
+ for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL))
+ dep_map->insert(std::make_pair(dep_pair.ptr, target));
+ }
+}
+
+// Forward declaration for function below.
+size_t RecursivePrintTargetDeps(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* seen_targets,
+ int indent_level);
+
+// Prints the target and its dependencies in tree form. If the set is non-null,
+// new targets encountered will be added to the set, and if a ref is in the set
+// already, it will not be recused into. When the set is null, all refs will be
+// printed.
+//
+// Returns the number of items printed.
+size_t RecursivePrintTarget(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* seen_targets,
+ int indent_level) {
+ std::string indent(indent_level * 2, ' ');
+ size_t count = 1;
+
+ // Only print the toolchain for non-default-toolchain targets.
+ OutputString(indent + target->label().GetUserVisibleName(
+ !target->settings()->is_default()));
+
+ bool print_children = true;
+ if (seen_targets) {
+ if (seen_targets->find(target) == seen_targets->end()) {
+ // New target, mark it visited.
+ seen_targets->insert(target);
+ } else {
+ // Already seen.
+ print_children = false;
+ // Only print "..." if something is actually elided, which means that
+ // the current target has children.
+ if (dep_map.lower_bound(target) != dep_map.upper_bound(target))
+ OutputString("...");
+ }
+ }
+
+ OutputString("\n");
+ if (print_children) {
+ count += RecursivePrintTargetDeps(dep_map, target, seen_targets,
+ indent_level + 1);
+ }
+ return count;
+}
+
+// Prints refs of the given target (not the target itself). See
+// RecursivePrintTarget.
+size_t RecursivePrintTargetDeps(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* seen_targets,
+ int indent_level) {
+ DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
+ DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+ size_t count = 0;
+ for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
+ cur_dep++) {
+ count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets,
+ indent_level);
+ }
+ return count;
+}
+
+void RecursiveCollectChildRefs(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* results);
+
+// Recursively finds all targets that reference the given one, and additionally
+// adds the current one to the list.
+void RecursiveCollectRefs(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* results) {
+ if (results->find(target) != results->end())
+ return; // Already found this target.
+ results->insert(target);
+ RecursiveCollectChildRefs(dep_map, target, results);
+}
+
+// Recursively finds all targets that reference the given one.
+void RecursiveCollectChildRefs(const DepMap& dep_map,
+ const Target* target,
+ TargetSet* results) {
+ DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
+ DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+ for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
+ cur_dep++)
+ RecursiveCollectRefs(dep_map, cur_dep->second, results);
+}
+
+bool TargetReferencesConfig(const Target* target, const Config* config) {
+ for (const LabelConfigPair& cur : target->configs()) {
+ if (cur.ptr == config)
+ return true;
+ }
+ for (const LabelConfigPair& cur : target->public_configs()) {
+ if (cur.ptr == config)
+ return true;
+ }
+ return false;
+}
+
+void GetTargetsReferencingConfig(Setup* setup,
+ const std::vector<const Target*>& all_targets,
+ const Config* config,
+ bool default_toolchain_only,
+ UniqueVector<const Target*>* matches) {
+ Label default_toolchain = setup->loader()->default_toolchain_label();
+ for (auto* target : all_targets) {
+ if (default_toolchain_only) {
+ // Only check targets in the default toolchain.
+ if (target->label().GetToolchainLabel() != default_toolchain)
+ continue;
+ }
+ if (TargetReferencesConfig(target, config))
+ matches->push_back(target);
+ }
+}
+
+// Returns the number of matches printed.
+size_t DoTreeOutput(const DepMap& dep_map,
+ const UniqueVector<const Target*>& implicit_target_matches,
+ const UniqueVector<const Target*>& explicit_target_matches,
+ bool all) {
+ TargetSet seen_targets;
+ size_t count = 0;
+
+ // Implicit targets don't get printed themselves.
+ for (const Target* target : implicit_target_matches) {
+ if (all)
+ count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
+ else
+ count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
+ }
+
+ // Explicit targets appear in the output.
+ for (const Target* target : implicit_target_matches) {
+ if (all)
+ count += RecursivePrintTarget(dep_map, target, nullptr, 0);
+ else
+ count += RecursivePrintTarget(dep_map, target, &seen_targets, 0);
+ }
+
+ return count;
+}
+
+// Returns the number of matches printed.
+size_t DoAllListOutput(
+ const DepMap& dep_map,
+ const UniqueVector<const Target*>& implicit_target_matches,
+ const UniqueVector<const Target*>& explicit_target_matches) {
+ // Output recursive dependencies, uniquified and flattened.
+ TargetSet results;
+
+ for (const Target* target : implicit_target_matches)
+ RecursiveCollectChildRefs(dep_map, target, &results);
+ for (const Target* target : explicit_target_matches) {
+ // Explicit targets also get added to the output themselves.
+ results.insert(target);
+ RecursiveCollectChildRefs(dep_map, target, &results);
+ }
+
+ FilterAndPrintTargetSet(false, results);
+ return results.size();
+}
+
+// Returns the number of matches printed.
+size_t DoDirectListOutput(
+ const DepMap& dep_map,
+ const UniqueVector<const Target*>& implicit_target_matches,
+ const UniqueVector<const Target*>& explicit_target_matches) {
+ TargetSet results;
+
+ // Output everything that refers to the implicit ones.
+ for (const Target* target : implicit_target_matches) {
+ DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
+ DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+ for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
+ cur_dep++)
+ results.insert(cur_dep->second);
+ }
+
+ // And just output the explicit ones directly (these are the target matches
+ // when referring to what references a file or config).
+ for (const Target* target : explicit_target_matches)
+ results.insert(target);
+
+ FilterAndPrintTargetSet(false, results);
+ return results.size();
+}
+
+} // namespace
+
+const char kRefs[] = "refs";
+const char kRefs_HelpShort[] = "refs: Find stuff referencing a target or file.";
+const char kRefs_Help[] =
+ R"(gn refs
+
+ gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all]
+ [--default-toolchain] [--as=...] [--testonly=...] [--type=...]
+
+ Finds reverse dependencies (which targets reference something). The input is
+ a list containing:
+
+ - Target label: The result will be which targets depend on it.
+
+ - Config label: The result will be which targets list the given config in
+ its "configs" or "public_configs" list.
+
+ - Label pattern: The result will be which targets depend on any target
+ matching the given pattern. Patterns will not match configs. These are not
+ general regular expressions, see "gn help label_pattern" for details.
+
+ - File name: The result will be which targets list the given file in its
+ "inputs", "sources", "public", "data", or "outputs". Any input that does
+ not contain wildcards and does not match a target or a config will be
+ treated as a file.
+
+ - Response file: If the input starts with an "@", it will be interpreted as
+ a path to a file containing a list of labels or file names, one per line.
+ This allows us to handle long lists of inputs without worrying about
+ command line limits.
+
+Options
+
+ --all
+ When used without --tree, will recurse and display all unique
+ dependencies of the given targets. For example, if the input is a target,
+ this will output all targets that depend directly or indirectly on the
+ input. If the input is a file, this will output all targets that depend
+ directly or indirectly on that file.
+
+ When used with --tree, turns off eliding to show a complete tree.
+
+)"
+
+ TARGET_PRINTING_MODE_COMMAND_LINE_HELP "\n" DEFAULT_TOOLCHAIN_SWITCH_HELP
+
+ R"(
+ -q
+ Quiet. If nothing matches, don't print any output. Without this option, if
+ there are no matches there will be an informational message printed which
+ might interfere with scripts processing the output.
+
+)"
+
+ TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
+
+ R"(
+ --tree
+ Outputs a reverse dependency tree from the given target. Duplicates will
+ be elided. Combine with --all to see a full dependency tree.
+
+ Tree output can not be used with the filtering or output flags: --as,
+ --type, --testonly.
+
+)"
+
+ TARGET_TYPE_FILTER_COMMAND_LINE_HELP
+
+ R"(
+
+Examples (target input)
+
+ gn refs out/Debug //gn:gn
+ Find all targets depending on the given exact target name.
+
+ gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim
+ Edit all .gn files containing references to //base:i18n
+
+ gn refs out/Debug //base --all
+ List all targets depending directly or indirectly on //base:base.
+
+ gn refs out/Debug "//base/*"
+ List all targets depending directly on any target in //base or
+ its subdirectories.
+
+ gn refs out/Debug "//base:*"
+ List all targets depending directly on any target in
+ //base/BUILD.gn.
+
+ gn refs out/Debug //base --tree
+ Print a reverse dependency tree of //base:base
+
+Examples (file input)
+
+ gn refs out/Debug //base/macros.h
+ Print target(s) listing //base/macros.h as a source.
+
+ gn refs out/Debug //base/macros.h --tree
+ Display a reverse dependency tree to get to the given file. This
+ will show how dependencies will reference that file.
+
+ gn refs out/Debug //base/macros.h //base/at_exit.h --all
+ Display all unique targets with some dependency path to a target
+ containing either of the given files as a source.
+
+ gn refs out/Debug //base/macros.h --testonly=true --type=executable
+ --all --as=output
+ Display the executable file names of all test executables
+ potentially affected by a change to the given file.
+)";
+
+int RunRefs(const std::vector<std::string>& args) {
+ if (args.size() <= 1) {
+ Err(Location(), "Unknown command format. See \"gn help refs\"",
+ "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool tree = cmdline->HasSwitch("tree");
+ bool all = cmdline->HasSwitch("all");
+ bool default_toolchain_only = cmdline->HasSwitch(switches::kDefaultToolchain);
+
+ // Deliberately leaked to avoid expensive process teardown.
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false) || !setup->Run())
+ return 1;
+
+ // The inputs are everything but the first arg (which is the build dir).
+ std::vector<std::string> inputs;
+ for (size_t i = 1; i < args.size(); i++) {
+ if (args[i][0] == '@') {
+ // The argument is as a path to a response file.
+ std::string contents;
+ bool ret =
+ base::ReadFileToString(UTF8ToFilePath(args[i].substr(1)), &contents);
+ if (!ret) {
+ Err(Location(), "Response file " + args[i].substr(1) + " not found.")
+ .PrintToStdout();
+ return 1;
+ }
+ for (const std::string& line : base::SplitString(
+ contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ if (!line.empty())
+ inputs.push_back(line);
+ }
+ } else {
+ // The argument is a label or a path.
+ inputs.push_back(args[i]);
+ }
+ }
+
+ // Get the matches for the command-line input.
+ UniqueVector<const Target*> target_matches;
+ UniqueVector<const Config*> config_matches;
+ UniqueVector<const Toolchain*> toolchain_matches;
+ UniqueVector<SourceFile> file_matches;
+ if (!ResolveFromCommandLineInput(setup, inputs, default_toolchain_only,
+ &target_matches, &config_matches,
+ &toolchain_matches, &file_matches))
+ return 1;
+
+ // When you give a file or config as an input, you want the targets that are
+ // associated with it. We don't want to just append this to the
+ // target_matches, however, since these targets should actually be listed in
+ // the output, while for normal targets you don't want to see the inputs,
+ // only what refers to them.
+ std::vector<const Target*> all_targets =
+ setup->builder().GetAllResolvedTargets();
+ UniqueVector<const Target*> explicit_target_matches;
+ for (const auto& file : file_matches) {
+ std::vector<TargetContainingFile> target_containing;
+ GetTargetsContainingFile(setup, all_targets, file, default_toolchain_only,
+ &target_containing);
+
+ // Extract just the Target*.
+ for (const TargetContainingFile& pair : target_containing)
+ explicit_target_matches.push_back(pair.first);
+ }
+ for (auto* config : config_matches) {
+ GetTargetsReferencingConfig(setup, all_targets, config,
+ default_toolchain_only,
+ &explicit_target_matches);
+ }
+
+ // Tell the user if their input matches no files or labels. We need to check
+ // both that it matched no targets and no configs. File input will already
+ // have been converted to targets at this point. Configs will have been
+ // converted to targets also, but there could be no targets referencing the
+ // config, which is different than no config with that name.
+ bool quiet = cmdline->HasSwitch("q");
+ if (!quiet && config_matches.empty() && explicit_target_matches.empty() &&
+ target_matches.empty()) {
+ OutputString("The input matches no targets, configs, or files.\n",
+ DECORATION_YELLOW);
+ return 1;
+ }
+
+ // Construct the reverse dependency tree.
+ DepMap dep_map;
+ FillDepMap(setup, &dep_map);
+
+ size_t cnt = 0;
+ if (tree)
+ cnt = DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
+ else if (all)
+ cnt = DoAllListOutput(dep_map, target_matches, explicit_target_matches);
+ else
+ cnt = DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
+
+ // If you ask for the references of a valid target, but that target has
+ // nothing referencing it, we'll get here without having printed anything.
+ if (!quiet && cnt == 0)
+ OutputString("Nothing references this.\n", DECORATION_YELLOW);
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/src/gn/commands.cc b/gn/src/gn/commands.cc
new file mode 100644
index 00000000000..da6514b3f7e
--- /dev/null
+++ b/gn/src/gn/commands.cc
@@ -0,0 +1,644 @@
+// 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 "gn/commands.h"
+
+#include <optional>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "gn/builder.h"
+#include "gn/config_values_extractors.h"
+#include "gn/filesystem_utils.h"
+#include "gn/item.h"
+#include "gn/label.h"
+#include "gn/label_pattern.h"
+#include "gn/setup.h"
+#include "gn/standard_out.h"
+#include "gn/target.h"
+#include "util/build_config.h"
+
+namespace commands {
+
+namespace {
+
+// Like above but the input string can be a pattern that matches multiple
+// targets. If the input does not parse as a pattern, prints and error and
+// returns false. If the pattern is valid, fills the vector (which might be
+// empty if there are no matches) and returns true.
+//
+// If default_toolchain_only is true, a pattern with an unspecified toolchain
+// will match the default toolchain only. If true, all toolchains will be
+// matched.
+bool ResolveTargetsFromCommandLinePattern(Setup* setup,
+ const std::string& label_pattern,
+ bool default_toolchain_only,
+ std::vector<const Target*>* matches) {
+ Value pattern_value(nullptr, label_pattern);
+
+ Err err;
+ LabelPattern pattern = LabelPattern::GetPattern(
+ SourceDirForCurrentDirectory(setup->build_settings().root_path()),
+ setup->build_settings().root_path_utf8(), pattern_value, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ if (default_toolchain_only) {
+ // By default a pattern with an empty toolchain will match all toolchains.
+ // If the caller wants to default to the main toolchain only, set it
+ // explicitly.
+ if (pattern.toolchain().is_null()) {
+ // No explicit toolchain set.
+ pattern.set_toolchain(setup->loader()->default_toolchain_label());
+ }
+ }
+
+ std::vector<LabelPattern> pattern_vector;
+ pattern_vector.push_back(pattern);
+ FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(),
+ pattern_vector, matches);
+ return true;
+}
+
+// If there's an error, it will be printed and false will be returned.
+bool ResolveStringFromCommandLineInput(
+ Setup* setup,
+ const SourceDir& current_dir,
+ const std::string& input,
+ bool default_toolchain_only,
+ UniqueVector<const Target*>* target_matches,
+ UniqueVector<const Config*>* config_matches,
+ UniqueVector<const Toolchain*>* toolchain_matches,
+ UniqueVector<SourceFile>* file_matches) {
+ if (LabelPattern::HasWildcard(input)) {
+ // For now, only match patterns against targets. It might be nice in the
+ // future to allow the user to specify which types of things they want to
+ // match, but it should probably only match targets by default.
+ std::vector<const Target*> target_match_vector;
+ if (!ResolveTargetsFromCommandLinePattern(
+ setup, input, default_toolchain_only, &target_match_vector))
+ return false;
+ for (const Target* target : target_match_vector)
+ target_matches->push_back(target);
+ return true;
+ }
+
+ // Try to figure out what this thing is.
+ Err err;
+ Label label = Label::Resolve(
+ current_dir, setup->build_settings().root_path_utf8(),
+ setup->loader()->default_toolchain_label(), Value(nullptr, input), &err);
+ if (err.has_error()) {
+ // Not a valid label, assume this must be a file.
+ err = Err();
+ file_matches->push_back(current_dir.ResolveRelativeFile(
+ Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+ return true;
+ }
+
+ const Item* item = setup->builder().GetItem(label);
+ if (item) {
+ if (const Config* as_config = item->AsConfig())
+ config_matches->push_back(as_config);
+ else if (const Target* as_target = item->AsTarget())
+ target_matches->push_back(as_target);
+ else if (const Toolchain* as_toolchain = item->AsToolchain())
+ toolchain_matches->push_back(as_toolchain);
+ } else {
+ // Not an item, assume this must be a file.
+ file_matches->push_back(current_dir.ResolveRelativeFile(
+ Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+enum TargetPrintingMode {
+ TARGET_PRINT_BUILDFILE,
+ TARGET_PRINT_LABEL,
+ TARGET_PRINT_OUTPUT,
+};
+
+// Retrieves the target printing mode based on the command line flags for the
+// current process. Returns true on success. On error, prints a message to the
+// console and returns false.
+bool GetTargetPrintingMode(TargetPrintingMode* mode) {
+ std::string switch_key = "as";
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+
+ if (!cmdline->HasSwitch(switch_key)) {
+ // Default to labels.
+ *mode = TARGET_PRINT_LABEL;
+ return true;
+ }
+
+ std::string value = cmdline->GetSwitchValueASCII(switch_key);
+ if (value == "buildfile") {
+ *mode = TARGET_PRINT_BUILDFILE;
+ return true;
+ }
+ if (value == "label") {
+ *mode = TARGET_PRINT_LABEL;
+ return true;
+ }
+ if (value == "output") {
+ *mode = TARGET_PRINT_OUTPUT;
+ return true;
+ }
+
+ Err(Location(), "Invalid value for \"--as\".",
+ "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
+ "said \"" +
+ value + "\".")
+ .PrintToStdout();
+ return false;
+}
+
+// Returns the target type filter based on the command line flags for the
+// current process. Returns true on success. On error, prints a message to the
+// console and returns false.
+//
+// Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
+// will never be returned. Code applying the filters should apply Target::ACTION
+// to both ACTION and ACTION_FOREACH.
+bool GetTargetTypeFilter(Target::OutputType* type) {
+ std::string switch_key = "type";
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+
+ if (!cmdline->HasSwitch(switch_key)) {
+ // Default to unknown -> no filtering.
+ *type = Target::UNKNOWN;
+ return true;
+ }
+
+ std::string value = cmdline->GetSwitchValueASCII(switch_key);
+ if (value == "group") {
+ *type = Target::GROUP;
+ return true;
+ }
+ if (value == "executable") {
+ *type = Target::EXECUTABLE;
+ return true;
+ }
+ if (value == "shared_library") {
+ *type = Target::SHARED_LIBRARY;
+ return true;
+ }
+ if (value == "loadable_module") {
+ *type = Target::LOADABLE_MODULE;
+ return true;
+ }
+ if (value == "static_library") {
+ *type = Target::STATIC_LIBRARY;
+ return true;
+ }
+ if (value == "source_set") {
+ *type = Target::SOURCE_SET;
+ return true;
+ }
+ if (value == "copy") {
+ *type = Target::COPY_FILES;
+ return true;
+ }
+ if (value == "action") {
+ *type = Target::ACTION;
+ return true;
+ }
+
+ Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
+ return false;
+}
+
+// Applies any testonly filtering specified on the command line to the given
+// target set. On failure, prints an error and returns false.
+bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ std::string testonly_key = "testonly";
+
+ if (targets->empty() || !cmdline->HasSwitch(testonly_key))
+ return true;
+
+ std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
+ bool testonly = false;
+ if (testonly_value == "true") {
+ testonly = true;
+ } else if (testonly_value != "false") {
+ Err(Location(), "Bad value for --testonly.",
+ "I was expecting --testonly=true or --testonly=false.")
+ .PrintToStdout();
+ return false;
+ }
+
+ // Filter into a copy of the vector, then replace the output.
+ std::vector<const Target*> result;
+ result.reserve(targets->size());
+
+ for (const Target* target : *targets) {
+ if (target->testonly() == testonly)
+ result.push_back(target);
+ }
+
+ *targets = std::move(result);
+ return true;
+}
+
+// Applies any target type filtering specified on the command line to the given
+// target set. On failure, prints an error and returns false.
+bool ApplyTypeFilter(std::vector<const Target*>* targets) {
+ Target::OutputType type = Target::UNKNOWN;
+ if (!GetTargetTypeFilter(&type))
+ return false;
+ if (targets->empty() || type == Target::UNKNOWN)
+ return true; // Nothing to filter out.
+
+ // Filter into a copy of the vector, then replace the output.
+ std::vector<const Target*> result;
+ result.reserve(targets->size());
+
+ for (const Target* target : *targets) {
+ // Make "action" also apply to ACTION_FOREACH.
+ if (target->output_type() == type ||
+ (type == Target::ACTION &&
+ target->output_type() == Target::ACTION_FOREACH))
+ result.push_back(target);
+ }
+
+ *targets = std::move(result);
+ return true;
+}
+
+// Returns the file path generating this item.
+base::FilePath BuildFileForItem(const Item* item) {
+ return item->defined_from()->GetRange().begin().file()->physical_name();
+}
+
+void PrintTargetsAsBuildfiles(const std::vector<const Target*>& targets,
+ base::ListValue* out) {
+ // Output the set of unique source files.
+ std::set<std::string> unique_files;
+ for (const Target* target : targets)
+ unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
+
+ for (const std::string& file : unique_files) {
+ out->AppendString(file);
+ }
+}
+
+void PrintTargetsAsLabels(const std::vector<const Target*>& targets,
+ base::ListValue* out) {
+ // Putting the labels into a set automatically sorts them for us.
+ std::set<Label> unique_labels;
+ for (auto* target : targets)
+ unique_labels.insert(target->label());
+
+ // Grab the label of the default toolchain from the first target.
+ Label default_tc_label = targets[0]->settings()->default_toolchain_label();
+
+ for (const Label& label : unique_labels) {
+ // Print toolchain only for ones not in the default toolchain.
+ out->AppendString(label.GetUserVisibleName(label.GetToolchainLabel() !=
+ default_tc_label));
+ }
+}
+
+void PrintTargetsAsOutputs(const std::vector<const Target*>& targets,
+ base::ListValue* out) {
+ if (targets.empty())
+ return;
+
+ // Grab the build settings from a random target.
+ const BuildSettings* build_settings =
+ targets[0]->settings()->build_settings();
+
+ for (const Target* target : targets) {
+ // Use the link output file if there is one, otherwise fall back to the
+ // dependency output file (for actions, for example).
+ OutputFile output_file = target->link_output_file();
+ if (output_file.value().empty())
+ output_file = target->dependency_output_file();
+
+ SourceFile output_as_source = output_file.AsSourceFile(build_settings);
+ std::string result =
+ RebasePath(output_as_source.value(), build_settings->build_dir(),
+ build_settings->root_path_utf8());
+ out->AppendString(result);
+ }
+}
+
+#if defined(OS_WIN)
+// Git bash will remove the first "/" in "//" paths
+// This also happens for labels assigned to command line parameters, e.g.
+// --filters
+// Fix "//" paths, but not absolute and relative paths
+inline std::string FixGitBashLabelEdit(const std::string& label) {
+ static std::unique_ptr<base::Environment> git_bash_env;
+ if (!git_bash_env)
+ git_bash_env = base::Environment::Create();
+
+ std::string temp_label(label);
+
+ if (git_bash_env->HasVar(
+ "MSYSTEM") && // Only for MinGW based shells like Git Bash
+ temp_label[0] == '/' && // Only fix for //foo paths, not /f:oo paths
+ (temp_label.length() < 2 ||
+ (temp_label[1] != '/' &&
+ (temp_label.length() < 3 || temp_label[1] != ':'))))
+ temp_label.insert(0, "/");
+ return temp_label;
+}
+#else
+// Only repair on Windows
+inline std::string FixGitBashLabelEdit(const std::string& label) {
+ return label;
+}
+#endif
+
+std::optional<HowTargetContainsFile> TargetContainsFile(
+ const Target* target,
+ const SourceFile& file) {
+ for (const auto& cur_file : target->sources()) {
+ if (cur_file == file)
+ return HowTargetContainsFile::kSources;
+ }
+ for (const auto& cur_file : target->public_headers()) {
+ if (cur_file == file)
+ return HowTargetContainsFile::kPublic;
+ }
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ for (const auto& cur_file : iter.cur().inputs()) {
+ if (cur_file == file)
+ return HowTargetContainsFile::kInputs;
+ }
+ }
+ for (const auto& cur_file : target->data()) {
+ if (cur_file == file.value())
+ return HowTargetContainsFile::kData;
+ if (cur_file.back() == '/' &&
+ base::StartsWith(file.value(), cur_file, base::CompareCase::SENSITIVE))
+ return HowTargetContainsFile::kData;
+ }
+
+ if (target->action_values().script().value() == file.value())
+ return HowTargetContainsFile::kScript;
+
+ std::vector<SourceFile> output_sources;
+ target->action_values().GetOutputsAsSourceFiles(target, &output_sources);
+ for (const auto& cur_file : output_sources) {
+ if (cur_file == file)
+ return HowTargetContainsFile::kOutput;
+ }
+
+ for (const auto& cur_file : target->computed_outputs()) {
+ if (cur_file.AsSourceFile(target->settings()->build_settings()) == file)
+ return HowTargetContainsFile::kOutput;
+ }
+ return std::nullopt;
+}
+
+} // namespace
+
+CommandInfo::CommandInfo()
+ : help_short(nullptr), help(nullptr), runner(nullptr) {}
+
+CommandInfo::CommandInfo(const char* in_help_short,
+ const char* in_help,
+ CommandRunner in_runner)
+ : help_short(in_help_short), help(in_help), runner(in_runner) {}
+
+const CommandInfoMap& GetCommands() {
+ static CommandInfoMap info_map;
+ if (info_map.empty()) {
+#define INSERT_COMMAND(cmd) \
+ info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, k##cmd##_Help, &Run##cmd);
+
+ INSERT_COMMAND(Analyze)
+ INSERT_COMMAND(Args)
+ INSERT_COMMAND(Check)
+ INSERT_COMMAND(Clean)
+ INSERT_COMMAND(Desc)
+ INSERT_COMMAND(Gen)
+ INSERT_COMMAND(Format)
+ INSERT_COMMAND(Help)
+ INSERT_COMMAND(Meta)
+ INSERT_COMMAND(Ls)
+ INSERT_COMMAND(Outputs)
+ INSERT_COMMAND(Path)
+ INSERT_COMMAND(Refs)
+ INSERT_COMMAND(CleanStale);
+
+#undef INSERT_COMMAND
+ }
+ return info_map;
+}
+
+const Target* ResolveTargetFromCommandLineString(
+ Setup* setup,
+ const std::string& label_string) {
+ // Need to resolve the label after we know the default toolchain.
+ Label default_toolchain = setup->loader()->default_toolchain_label();
+ Value arg_value(nullptr, FixGitBashLabelEdit(label_string));
+ Err err;
+ Label label = Label::Resolve(
+ SourceDirForCurrentDirectory(setup->build_settings().root_path()),
+ setup->build_settings().root_path_utf8(), default_toolchain, arg_value,
+ &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return nullptr;
+ }
+
+ const Item* item = setup->builder().GetItem(label);
+ if (!item) {
+ Err(Location(), "Label not found.",
+ label.GetUserVisibleName(false) + " not found.")
+ .PrintToStdout();
+ return nullptr;
+ }
+
+ const Target* target = item->AsTarget();
+ if (!target) {
+ Err(Location(), "Not a target.",
+ "The \"" + label.GetUserVisibleName(false) +
+ "\" thing\n"
+ "is not a target. Somebody should probably implement this command "
+ "for "
+ "other\nitem types.")
+ .PrintToStdout();
+ return nullptr;
+ }
+
+ return target;
+}
+
+bool ResolveFromCommandLineInput(
+ Setup* setup,
+ const std::vector<std::string>& input,
+ bool default_toolchain_only,
+ UniqueVector<const Target*>* target_matches,
+ UniqueVector<const Config*>* config_matches,
+ UniqueVector<const Toolchain*>* toolchain_matches,
+ UniqueVector<SourceFile>* file_matches) {
+ if (input.empty()) {
+ Err(Location(), "You need to specify a label, file, or pattern.")
+ .PrintToStdout();
+ return false;
+ }
+
+ SourceDir cur_dir =
+ SourceDirForCurrentDirectory(setup->build_settings().root_path());
+ for (const auto& cur : input) {
+ if (!ResolveStringFromCommandLineInput(
+ setup, cur_dir, cur, default_toolchain_only, target_matches,
+ config_matches, toolchain_matches, file_matches))
+ return false;
+ }
+ return true;
+}
+
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+ const std::vector<LabelPattern>& filter,
+ std::vector<const Target*>* output) {
+ for (auto* target : input) {
+ for (const auto& pattern : filter) {
+ if (pattern.Matches(target->label())) {
+ output->push_back(target);
+ break;
+ }
+ }
+ }
+}
+
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+ const std::vector<LabelPattern>& filter,
+ UniqueVector<const Target*>* output) {
+ for (auto* target : input) {
+ for (const auto& pattern : filter) {
+ if (pattern.Matches(target->label())) {
+ output->push_back(target);
+ break;
+ }
+ }
+ }
+}
+
+void FilterOutTargetsByPatterns(const std::vector<const Target*>& input,
+ const std::vector<LabelPattern>& filter,
+ std::vector<const Target*>* output) {
+ for (auto* target : input) {
+ bool match = false;
+ for (const auto& pattern : filter) {
+ if (pattern.Matches(target->label())) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ output->push_back(target);
+ }
+ }
+}
+
+bool FilterPatternsFromString(const BuildSettings* build_settings,
+ const std::string& label_list_string,
+ std::vector<LabelPattern>* filters,
+ Err* err) {
+ std::vector<std::string> tokens = base::SplitString(
+ label_list_string, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ SourceDir root_dir("//");
+
+ filters->reserve(tokens.size());
+ for (const std::string& token : tokens) {
+ LabelPattern pattern = LabelPattern::GetPattern(
+ root_dir, build_settings->root_path_utf8(),
+ Value(nullptr, FixGitBashLabelEdit(token)), err);
+ if (err->has_error())
+ return false;
+ filters->push_back(pattern);
+ }
+
+ return true;
+}
+
+void FilterAndPrintTargets(std::vector<const Target*>* targets,
+ base::ListValue* out) {
+ if (targets->empty())
+ return;
+
+ if (!ApplyTestonlyFilter(targets))
+ return;
+ if (!ApplyTypeFilter(targets))
+ return;
+
+ TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
+ if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
+ return;
+ switch (printing_mode) {
+ case TARGET_PRINT_BUILDFILE:
+ PrintTargetsAsBuildfiles(*targets, out);
+ break;
+ case TARGET_PRINT_LABEL:
+ PrintTargetsAsLabels(*targets, out);
+ break;
+ case TARGET_PRINT_OUTPUT:
+ PrintTargetsAsOutputs(*targets, out);
+ break;
+ }
+}
+
+void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
+ base::ListValue tmp;
+ FilterAndPrintTargets(targets, &tmp);
+ for (const auto& value : tmp) {
+ std::string string;
+ value.GetAsString(&string);
+ if (indent)
+ OutputString(" ");
+ OutputString(string);
+ OutputString("\n");
+ }
+}
+
+void FilterAndPrintTargetSet(bool indent,
+ const std::set<const Target*>& targets) {
+ std::vector<const Target*> target_vector(targets.begin(), targets.end());
+ FilterAndPrintTargets(indent, &target_vector);
+}
+
+void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
+ base::ListValue* out) {
+ std::vector<const Target*> target_vector(targets.begin(), targets.end());
+ FilterAndPrintTargets(&target_vector, out);
+}
+
+void GetTargetsContainingFile(Setup* setup,
+ const std::vector<const Target*>& all_targets,
+ const SourceFile& file,
+ bool default_toolchain_only,
+ std::vector<TargetContainingFile>* matches) {
+ Label default_toolchain = setup->loader()->default_toolchain_label();
+ for (auto* target : all_targets) {
+ if (default_toolchain_only) {
+ // Only check targets in the default toolchain.
+ if (target->label().GetToolchainLabel() != default_toolchain)
+ continue;
+ }
+ if (auto how = TargetContainsFile(target, file))
+ matches->emplace_back(target, *how);
+ }
+}
+
+} // namespace commands
diff --git a/gn/src/gn/commands.h b/gn/src/gn/commands.h
new file mode 100644
index 00000000000..54a1a9dc8c3
--- /dev/null
+++ b/gn/src/gn/commands.h
@@ -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.
+
+#ifndef TOOLS_GN_COMMANDS_H_
+#define TOOLS_GN_COMMANDS_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "base/values.h"
+#include "gn/target.h"
+#include "gn/unique_vector.h"
+
+class BuildSettings;
+class Config;
+class LabelPattern;
+class Setup;
+class SourceFile;
+class Target;
+class Toolchain;
+
+// Each "Run" command returns the value we should return from main().
+
+namespace commands {
+
+using CommandRunner = int (*)(const std::vector<std::string>&);
+
+extern const char kAnalyze[];
+extern const char kAnalyze_HelpShort[];
+extern const char kAnalyze_Help[];
+int RunAnalyze(const std::vector<std::string>& args);
+
+extern const char kArgs[];
+extern const char kArgs_HelpShort[];
+extern const char kArgs_Help[];
+int RunArgs(const std::vector<std::string>& args);
+
+extern const char kCheck[];
+extern const char kCheck_HelpShort[];
+extern const char kCheck_Help[];
+int RunCheck(const std::vector<std::string>& args);
+
+extern const char kClean[];
+extern const char kClean_HelpShort[];
+extern const char kClean_Help[];
+int RunClean(const std::vector<std::string>& args);
+
+extern const char kDesc[];
+extern const char kDesc_HelpShort[];
+extern const char kDesc_Help[];
+int RunDesc(const std::vector<std::string>& args);
+
+extern const char kGen[];
+extern const char kGen_HelpShort[];
+extern const char kGen_Help[];
+int RunGen(const std::vector<std::string>& args);
+
+extern const char kFormat[];
+extern const char kFormat_HelpShort[];
+extern const char kFormat_Help[];
+int RunFormat(const std::vector<std::string>& args);
+
+extern const char kHelp[];
+extern const char kHelp_HelpShort[];
+extern const char kHelp_Help[];
+int RunHelp(const std::vector<std::string>& args);
+
+extern const char kMeta[];
+extern const char kMeta_HelpShort[];
+extern const char kMeta_Help[];
+int RunMeta(const std::vector<std::string>& args);
+
+extern const char kLs[];
+extern const char kLs_HelpShort[];
+extern const char kLs_Help[];
+int RunLs(const std::vector<std::string>& args);
+
+extern const char kOutputs[];
+extern const char kOutputs_HelpShort[];
+extern const char kOutputs_Help[];
+int RunOutputs(const std::vector<std::string>& args);
+
+extern const char kPath[];
+extern const char kPath_HelpShort[];
+extern const char kPath_Help[];
+int RunPath(const std::vector<std::string>& args);
+
+extern const char kRefs[];
+extern const char kRefs_HelpShort[];
+extern const char kRefs_Help[];
+int RunRefs(const std::vector<std::string>& args);
+
+extern const char kCleanStale[];
+extern const char kCleanStale_HelpShort[];
+extern const char kCleanStale_Help[];
+int RunCleanStale(const std::vector<std::string>& args);
+
+// -----------------------------------------------------------------------------
+
+struct CommandInfo {
+ CommandInfo();
+ CommandInfo(const char* in_help_short,
+ const char* in_help,
+ CommandRunner in_runner);
+
+ const char* help_short;
+ const char* help;
+ CommandRunner runner;
+};
+
+using CommandInfoMap = std::map<std::string_view, CommandInfo>;
+
+const CommandInfoMap& GetCommands();
+
+// Helper functions for some commands ------------------------------------------
+
+// Given a setup that has already been run and some command-line input,
+// resolves that input as a target label and returns the corresponding target.
+// On failure, returns null and prints the error to the standard output.
+const Target* ResolveTargetFromCommandLineString(
+ Setup* setup,
+ const std::string& label_string);
+
+// Resolves a vector of command line inputs and figures out the full set of
+// things they resolve to.
+//
+// On success, returns true and populates the vectors. On failure, prints the
+// error and returns false.
+//
+// Patterns with wildcards will only match targets. The file_matches aren't
+// validated that they are real files or referenced by any targets. They're just
+// the set of things that didn't match anything else.
+bool ResolveFromCommandLineInput(
+ Setup* setup,
+ const std::vector<std::string>& input,
+ bool default_toolchain_only,
+ UniqueVector<const Target*>* target_matches,
+ UniqueVector<const Config*>* config_matches,
+ UniqueVector<const Toolchain*>* toolchain_matches,
+ UniqueVector<SourceFile>* file_matches);
+
+// Runs the header checker. All targets in the build should be given in
+// all_targets, and the specific targets to check should be in to_check.
+//
+// force_check, if true, will override targets opting out of header checking
+// with "check_includes = false" and will check them anyway.
+//
+// Generated files are normally not checked since they do not exist
+// unless a build has been run, but passing true for |check_generated|
+// will attempt to check them anyway, assuming they exist.
+//
+// On success, returns true. If the check fails, the error(s) will be printed
+// to stdout and false will be returned.
+bool CheckPublicHeaders(const BuildSettings* build_settings,
+ const std::vector<const Target*>& all_targets,
+ const std::vector<const Target*>& to_check,
+ bool force_check,
+ bool check_generated,
+ bool check_system);
+
+// Filters the given list of targets by the given pattern list.
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+ const std::vector<LabelPattern>& filter,
+ std::vector<const Target*>* output);
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+ const std::vector<LabelPattern>& filter,
+ UniqueVector<const Target*>* output);
+
+// Removes targets from the input that match the given pattern list.
+void FilterOutTargetsByPatterns(const std::vector<const Target*>& input,
+ const std::vector<LabelPattern>& filter,
+ std::vector<const Target*>* output);
+
+// Builds a list of pattern from a semicolon-separated list of labels.
+bool FilterPatternsFromString(const BuildSettings* build_settings,
+ const std::string& label_list_string,
+ std::vector<LabelPattern>* filters,
+ Err* err);
+
+// These are the documentation strings for the command-line flags used by
+// FilterAndPrintTargets. Commands that call that function should incorporate
+// these into their help.
+#define TARGET_PRINTING_MODE_COMMAND_LINE_HELP \
+ " --as=(buildfile|label|output)\n" \
+ " How to print targets.\n" \
+ "\n" \
+ " buildfile\n" \
+ " Prints the build files where the given target was declared as\n" \
+ " file names.\n" \
+ " label (default)\n" \
+ " Prints the label of the target.\n" \
+ " output\n" \
+ " Prints the first output file for the target relative to the\n" \
+ " root build directory.\n"
+#define TARGET_TYPE_FILTER_COMMAND_LINE_HELP \
+ " --type=(action|copy|executable|group|loadable_module|shared_library|\n" \
+ " source_set|static_library)\n" \
+ " Restrict outputs to targets matching the given type. If\n" \
+ " unspecified, no filtering will be performed.\n"
+#define TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP \
+ " --testonly=(true|false)\n" \
+ " Restrict outputs to targets with the testonly flag set\n" \
+ " accordingly. When unspecified, the target's testonly flags are\n" \
+ " ignored.\n"
+
+// Applies any testonly and type filters specified on the command line,
+// and prints the targets as specified by the --as command line flag.
+//
+// If indent is true, the results will be indented two spaces.
+//
+// The vector will be modified so that only the printed targets will remain.
+void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets);
+void FilterAndPrintTargets(std::vector<const Target*>* targets,
+ base::ListValue* out);
+
+void FilterAndPrintTargetSet(bool indent,
+ const std::set<const Target*>& targets);
+void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
+ base::ListValue* out);
+
+// Computes which targets reference the given file and also stores how the
+// target references the file.
+enum class HowTargetContainsFile {
+ kSources,
+ kPublic,
+ kInputs,
+ kData,
+ kScript,
+ kOutput,
+};
+using TargetContainingFile = std::pair<const Target*, HowTargetContainsFile>;
+void GetTargetsContainingFile(Setup* setup,
+ const std::vector<const Target*>& all_targets,
+ const SourceFile& file,
+ bool default_toolchain_only,
+ std::vector<TargetContainingFile>* matches);
+
+// Extra help from command_check.cc
+extern const char kNoGnCheck_Help[];
+
+} // namespace commands
+
+#endif // TOOLS_GN_COMMANDS_H_
diff --git a/gn/src/gn/commands_unittest.cc b/gn/src/gn/commands_unittest.cc
new file mode 100644
index 00000000000..6d861cdff41
--- /dev/null
+++ b/gn/src/gn/commands_unittest.cc
@@ -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.
+
+#include <stddef.h>
+
+#include "gn/commands.h"
+#include "gn/label_pattern.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(Commands, FilterOutMatch) {
+ TestWithScope setup;
+ SourceDir current_dir("//");
+
+ Target target_afoo(setup.settings(), Label(SourceDir("//a/"), "foo"));
+ Target target_cbar(setup.settings(), Label(SourceDir("//c/"), "bar"));
+ std::vector<const Target*> targets{&target_afoo, &target_cbar};
+
+ Err err;
+ LabelPattern pattern_a = LabelPattern::GetPattern(
+ current_dir, std::string_view(), Value(nullptr, "//a:*"), &err);
+ EXPECT_FALSE(err.has_error());
+ LabelPattern pattern_ef = LabelPattern::GetPattern(
+ current_dir, std::string_view(), Value(nullptr, "//e:f"), &err);
+ EXPECT_FALSE(err.has_error());
+ std::vector<LabelPattern> label_patterns{pattern_a, pattern_ef};
+
+ std::vector<const Target*> output;
+ commands::FilterOutTargetsByPatterns(targets, label_patterns, &output);
+
+ EXPECT_EQ(1, output.size());
+ EXPECT_EQ(&target_cbar, output[0]);
+}
diff --git a/gn/src/gn/compile_commands_writer.cc b/gn/src/gn/compile_commands_writer.cc
new file mode 100644
index 00000000000..9f8ef637f1e
--- /dev/null
+++ b/gn/src/gn/compile_commands_writer.cc
@@ -0,0 +1,365 @@
+// Copyright 2018 The Chromium 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 "gn/compile_commands_writer.h"
+
+#include <sstream>
+
+#include "base/json/string_escape.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "gn/builder.h"
+#include "gn/c_substitution_type.h"
+#include "gn/c_tool.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/escape.h"
+#include "gn/filesystem_utils.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/path_output.h"
+#include "gn/string_output_buffer.h"
+#include "gn/substitution_writer.h"
+
+// Structure of JSON output file
+// [
+// {
+// "directory": "The build directory."
+// "file": "The main source file processed by this compilation step.
+// Must be absolute or relative to the above build directory."
+// "command": "The compile command executed."
+// }
+// ...
+// ]
+
+namespace {
+
+#if defined(OS_WIN)
+const char kPrettyPrintLineEnding[] = "\r\n";
+#else
+const char kPrettyPrintLineEnding[] = "\n";
+#endif
+
+struct CompileFlags {
+ std::string includes;
+ std::string defines;
+ std::string cflags;
+ std::string cflags_c;
+ std::string cflags_cc;
+ std::string cflags_objc;
+ std::string cflags_objcc;
+ std::string framework_dirs;
+ std::string frameworks;
+};
+
+// Helper template function to call RecursiveTargetConfigToStream<std::string>
+// and return the JSON-escaped resulting string.
+//
+// NOTE: The Windows compiler cannot properly deduce the first parameter type
+// so pass it at each call site to ensure proper builds for this platform.
+template <typename T, typename Writer>
+std::string FlagsGetter(const Target* target,
+ const std::vector<T>& (ConfigValues::*getter)() const,
+ const Writer& writer) {
+ std::string result;
+ std::ostringstream out;
+ RecursiveTargetConfigToStream<T>(target, getter, writer, out);
+ base::EscapeJSONString(out.str(), false, &result);
+ return result;
+};
+
+void SetupCompileFlags(const Target* target,
+ PathOutput& path_output,
+ EscapeOptions opts,
+ CompileFlags& flags) {
+ bool has_precompiled_headers =
+ target->config_values().has_precompiled_headers();
+
+ flags.defines =
+ FlagsGetter<std::string>(target, &ConfigValues::defines,
+ DefineWriter(ESCAPE_COMPILATION_DATABASE));
+
+ flags.framework_dirs =
+ FlagsGetter<SourceDir>(target, &ConfigValues::framework_dirs,
+ FrameworkDirsWriter(path_output, "-F"));
+
+ flags.frameworks = FlagsGetter<std::string>(
+ target, &ConfigValues::frameworks,
+ FrameworksWriter(ESCAPE_COMPILATION_DATABASE, "-framework"));
+ flags.frameworks += FlagsGetter<std::string>(
+ target, &ConfigValues::weak_frameworks,
+ FrameworksWriter(ESCAPE_COMPILATION_DATABASE, "-weak_framework"));
+
+ flags.includes = FlagsGetter<SourceDir>(target, &ConfigValues::include_dirs,
+ IncludeWriter(path_output));
+
+ // Helper lambda to call WriteOneFlag() and return the resulting
+ // escaped JSON string.
+ auto one_flag = [&](const Substitution* substitution,
+ bool has_precompiled_headers, const char* tool_name,
+ const std::vector<std::string>& (ConfigValues::*getter)()
+ const) -> std::string {
+ std::string result;
+ std::ostringstream out;
+ WriteOneFlag(target, substitution, has_precompiled_headers, tool_name,
+ getter, opts, path_output, out, /*write_substitution=*/false);
+ base::EscapeJSONString(out.str(), false, &result);
+ return result;
+ };
+
+ flags.cflags = one_flag(&CSubstitutionCFlags, false, Tool::kToolNone,
+ &ConfigValues::cflags);
+
+ flags.cflags_c = one_flag(&CSubstitutionCFlagsC, has_precompiled_headers,
+ CTool::kCToolCc, &ConfigValues::cflags_c);
+
+ flags.cflags_cc = one_flag(&CSubstitutionCFlagsCc, has_precompiled_headers,
+ CTool::kCToolCxx, &ConfigValues::cflags_cc);
+
+ flags.cflags_objc =
+ one_flag(&CSubstitutionCFlagsObjC, has_precompiled_headers,
+ CTool::kCToolObjC, &ConfigValues::cflags_objc);
+
+ flags.cflags_objcc =
+ one_flag(&CSubstitutionCFlagsObjCc, has_precompiled_headers,
+ CTool::kCToolObjCxx, &ConfigValues::cflags_objcc);
+}
+
+void WriteFile(const SourceFile& source,
+ PathOutput& path_output,
+ std::ostream& out) {
+ std::ostringstream rel_source_path;
+ out << " \"file\": \"";
+ path_output.WriteFile(out, source);
+}
+
+void WriteDirectory(std::string build_dir, std::ostream& out) {
+ out << "\",";
+ out << kPrettyPrintLineEnding;
+ out << " \"directory\": \"";
+ out << build_dir;
+ out << "\",";
+}
+
+void WriteCommand(const Target* target,
+ const SourceFile& source,
+ const CompileFlags& flags,
+ std::vector<OutputFile>& tool_outputs,
+ PathOutput& path_output,
+ SourceFile::Type source_type,
+ const char* tool_name,
+ EscapeOptions opts,
+ std::ostream& out) {
+ EscapeOptions no_quoting(opts);
+ no_quoting.inhibit_quoting = true;
+ const Tool* tool = target->toolchain()->GetTool(tool_name);
+
+ out << kPrettyPrintLineEnding;
+ out << " \"command\": \"";
+
+ for (const auto& range : tool->command().ranges()) {
+ // TODO: this is emitting a bonus space prior to each substitution.
+ if (range.type == &SubstitutionLiteral) {
+ EscapeJSONStringToStream(out, range.literal, no_quoting);
+ } else if (range.type == &SubstitutionOutput) {
+ path_output.WriteFiles(out, tool_outputs);
+ } else if (range.type == &CSubstitutionDefines) {
+ out << flags.defines;
+ } else if (range.type == &CSubstitutionFrameworkDirs) {
+ out << flags.framework_dirs;
+ } else if (range.type == &CSubstitutionFrameworks) {
+ out << flags.frameworks;
+ } else if (range.type == &CSubstitutionIncludeDirs) {
+ out << flags.includes;
+ } else if (range.type == &CSubstitutionCFlags) {
+ out << flags.cflags;
+ } else if (range.type == &CSubstitutionCFlagsC) {
+ if (source_type == SourceFile::SOURCE_C)
+ out << flags.cflags_c;
+ } else if (range.type == &CSubstitutionCFlagsCc) {
+ if (source_type == SourceFile::SOURCE_CPP)
+ out << flags.cflags_cc;
+ } else if (range.type == &CSubstitutionCFlagsObjC) {
+ if (source_type == SourceFile::SOURCE_M)
+ out << flags.cflags_objc;
+ } else if (range.type == &CSubstitutionCFlagsObjCc) {
+ if (source_type == SourceFile::SOURCE_MM)
+ out << flags.cflags_objcc;
+ } else if (range.type == &SubstitutionLabel ||
+ range.type == &SubstitutionLabelName ||
+ range.type == &SubstitutionLabelNoToolchain ||
+ range.type == &SubstitutionRootGenDir ||
+ range.type == &SubstitutionRootOutDir ||
+ range.type == &SubstitutionTargetGenDir ||
+ range.type == &SubstitutionTargetOutDir ||
+ range.type == &SubstitutionTargetOutputName ||
+ range.type == &SubstitutionSource ||
+ range.type == &SubstitutionSourceNamePart ||
+ range.type == &SubstitutionSourceFilePart ||
+ range.type == &SubstitutionSourceDir ||
+ range.type == &SubstitutionSourceRootRelativeDir ||
+ range.type == &SubstitutionSourceGenDir ||
+ range.type == &SubstitutionSourceOutDir ||
+ range.type == &SubstitutionSourceTargetRelative) {
+ EscapeStringToStream(out,
+ SubstitutionWriter::GetCompilerSubstitution(
+ target, source, range.type),
+ opts);
+ } else {
+ // Other flags shouldn't be relevant to compiling C/C++/ObjC/ObjC++
+ // source files.
+ NOTREACHED() << "Unsupported substitution for this type of target : "
+ << range.type->name;
+ continue;
+ }
+ }
+}
+
+void OutputJSON(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets,
+ std::ostream& out) {
+ out << '[';
+ out << kPrettyPrintLineEnding;
+ bool first = true;
+ auto build_dir = build_settings->GetFullPath(build_settings->build_dir())
+ .StripTrailingSeparators();
+ std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
+
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
+
+ for (const auto* target : all_targets) {
+ if (!target->IsBinary())
+ continue;
+
+ // Precompute values that are the same for all sources in a target to avoid
+ // computing for every source.
+
+ PathOutput path_output(
+ target->settings()->build_settings()->build_dir(),
+ target->settings()->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA_COMMAND);
+
+ CompileFlags flags;
+ SetupCompileFlags(target, path_output, opts, flags);
+
+ for (const auto& source : target->sources()) {
+ // If this source is not a C/C++/ObjC/ObjC++ source (not header) file,
+ // continue as it does not belong in the compilation database.
+ SourceFile::Type source_type = source.type();
+ if (source_type != SourceFile::SOURCE_CPP &&
+ source_type != SourceFile::SOURCE_C &&
+ source_type != SourceFile::SOURCE_M &&
+ source_type != SourceFile::SOURCE_MM)
+ continue;
+
+ const char* tool_name = Tool::kToolNone;
+ if (!target->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
+ continue;
+
+ if (!first) {
+ out << ',';
+ out << kPrettyPrintLineEnding;
+ }
+ first = false;
+ out << " {";
+ out << kPrettyPrintLineEnding;
+
+ WriteFile(source, path_output, out);
+ WriteDirectory(base::StringPrintf("%" PRIsFP, PATH_CSTR(build_dir)), out);
+ WriteCommand(target, source, flags, tool_outputs, path_output,
+ source_type, tool_name, opts, out);
+ out << "\"";
+ out << kPrettyPrintLineEnding;
+ out << " }";
+ }
+ }
+
+ out << kPrettyPrintLineEnding;
+ out << "]";
+ out << kPrettyPrintLineEnding;
+}
+
+} // namespace
+
+std::string CompileCommandsWriter::RenderJSON(
+ const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets) {
+ StringOutputBuffer json;
+ std::ostream out(&json);
+ OutputJSON(build_settings, all_targets, out);
+ return json.str();
+}
+
+bool CompileCommandsWriter::RunAndWriteFiles(
+ const BuildSettings* build_settings,
+ const Builder& builder,
+ const std::string& file_name,
+ const std::string& target_filters,
+ bool quiet,
+ Err* err) {
+ SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
+ Value(nullptr, file_name), err);
+ if (output_file.is_null())
+ return false;
+
+ base::FilePath output_path = build_settings->GetFullPath(output_file);
+
+ std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
+
+ std::set<std::string> target_filters_set;
+ for (auto& target :
+ base::SplitString(target_filters, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ target_filters_set.insert(target);
+ }
+
+ StringOutputBuffer json;
+ std::ostream output_to_json(&json);
+ if (target_filters_set.empty()) {
+ OutputJSON(build_settings, all_targets, output_to_json);
+ } else {
+ std::vector<const Target*> preserved_targets =
+ FilterTargets(all_targets, target_filters_set);
+ OutputJSON(build_settings, preserved_targets, output_to_json);
+ }
+
+ if (!json.ContentsEqual(output_path)) {
+ if (!json.WriteToFile(output_path, err))
+ return false;
+ }
+ return true;
+}
+
+std::vector<const Target*> CompileCommandsWriter::FilterTargets(
+ const std::vector<const Target*>& all_targets,
+ const std::set<std::string>& target_filters_set) {
+ std::vector<const Target*> preserved_targets;
+
+ std::set<const Target*> visited;
+ for (auto& target : all_targets) {
+ if (target_filters_set.count(target->label().name())) {
+ VisitDeps(target, &visited);
+ }
+ }
+
+ preserved_targets.reserve(visited.size());
+ // Preserve the original ordering of all_targets
+ // to allow easier debugging and testing.
+ for (auto& target : all_targets) {
+ if (visited.count(target)) {
+ preserved_targets.push_back(target);
+ }
+ }
+ return preserved_targets;
+}
+
+void CompileCommandsWriter::VisitDeps(const Target* target,
+ std::set<const Target*>* visited) {
+ if (!visited->count(target)) {
+ visited->insert(target);
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
+ VisitDeps(pair.ptr, visited);
+ }
+ }
+}
diff --git a/gn/src/gn/compile_commands_writer.h b/gn/src/gn/compile_commands_writer.h
new file mode 100644
index 00000000000..be4e90bc9a0
--- /dev/null
+++ b/gn/src/gn/compile_commands_writer.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
+#define TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
+
+#include "gn/err.h"
+#include "gn/target.h"
+
+class Builder;
+class BuildSettings;
+
+class CompileCommandsWriter {
+ public:
+ // Write compile commands into a json file located by parameter file_name.
+ //
+ // Parameter target_filters should be in "target_name1,target_name2..."
+ // format. If it is not empty, only targets that are reachable from targets
+ // in target_filters are used to generate compile commands.
+ //
+ // Parameter quiet is not used.
+ static bool RunAndWriteFiles(const BuildSettings* build_setting,
+ const Builder& builder,
+ const std::string& file_name,
+ const std::string& target_filters,
+ bool quiet,
+ Err* err);
+
+ static std::string RenderJSON(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets);
+
+ static std::vector<const Target*> FilterTargets(
+ const std::vector<const Target*>& all_targets,
+ const std::set<std::string>& target_filters_set);
+
+ private:
+ // This fuction visits the deps graph of a target in a DFS fashion.
+ static void VisitDeps(const Target* target, std::set<const Target*>* visited);
+};
+
+#endif // TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
diff --git a/gn/src/gn/compile_commands_writer_unittest.cc b/gn/src/gn/compile_commands_writer_unittest.cc
new file mode 100644
index 00000000000..29e6a573699
--- /dev/null
+++ b/gn/src/gn/compile_commands_writer_unittest.cc
@@ -0,0 +1,640 @@
+// Copyright 2018 The Chromium 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 "gn/compile_commands_writer.h"
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "gn/config.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/scheduler.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+namespace {
+
+// InputConversion needs a global scheduler object.
+class CompileCommandsTest : public TestWithScheduler {
+ public:
+ CompileCommandsTest() = default;
+
+ const BuildSettings* build_settings() { return setup_.build_settings(); }
+ const Settings* settings() { return setup_.settings(); }
+ const TestWithScope& setup() { return setup_; }
+ const Toolchain* toolchain() { return setup_.toolchain(); }
+
+ private:
+ TestWithScope setup_;
+};
+
+} // namespace
+
+TEST_F(CompileCommandsTest, SourceSet) {
+ Err err;
+
+ std::vector<const Target*> targets;
+ Target target(settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ // Also test object files, which should be just passed through to the
+ // dependents to link.
+ target.sources().push_back(SourceFile("//foo/input3.o"));
+ target.sources().push_back(SourceFile("//foo/input4.obj"));
+ target.SetToolchain(toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+ targets.push_back(&target);
+
+ // Source set itself.
+ {
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "obj/foo/bar.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input2.cc -o "
+ "obj/foo/bar.input2.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "obj/foo/bar.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input2.cc -o "
+ "obj/foo/bar.input2.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(expected, out);
+ }
+
+ // A shared library that depends on the source set.
+ Target shlib_target(settings(), Label(SourceDir("//foo/"), "shlib"));
+ shlib_target.sources().push_back(SourceFile("//foo/input3.cc"));
+ shlib_target.set_output_type(Target::SHARED_LIBRARY);
+ shlib_target.public_deps().push_back(LabelTargetPair(&target));
+ shlib_target.SetToolchain(toolchain());
+ ASSERT_TRUE(shlib_target.OnResolved(&err));
+ targets.push_back(&shlib_target);
+
+ {
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "obj/foo/bar.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input2.cc -o "
+ "obj/foo/bar.input2.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input3.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input3.cc -o "
+ "obj/foo/libshlib.input3.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "obj/foo/bar.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input2.cc -o "
+ "obj/foo/bar.input2.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input3.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input3.cc -o "
+ "obj/foo/libshlib.input3.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(expected, out);
+ }
+
+ // A static library that depends on the source set (should not link it).
+ Target stlib_target(settings(), Label(SourceDir("//foo/"), "stlib"));
+ stlib_target.sources().push_back(SourceFile("//foo/input4.cc"));
+ stlib_target.set_output_type(Target::STATIC_LIBRARY);
+ stlib_target.public_deps().push_back(LabelTargetPair(&target));
+ stlib_target.SetToolchain(toolchain());
+ ASSERT_TRUE(stlib_target.OnResolved(&err));
+ targets.push_back(&stlib_target);
+
+ {
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "obj/foo/bar.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input2.cc -o "
+ "obj/foo/bar.input2.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input3.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input3.cc -o "
+ "obj/foo/libshlib.input3.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input4.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input4.cc -o "
+ "obj/foo/libstlib.input4.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "obj/foo/bar.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input2.cc -o "
+ "obj/foo/bar.input2.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input3.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input3.cc -o "
+ "obj/foo/libshlib.input3.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input4.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input4.cc -o "
+ "obj/foo/libstlib.input4.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(expected, out);
+ }
+}
+
+TEST_F(CompileCommandsTest, EscapeDefines) {
+ Err err;
+
+ std::vector<const Target*> targets;
+ TestTarget target(setup(), "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input.cc"));
+ target.config_values().defines().push_back("BOOL_DEF");
+ target.config_values().defines().push_back("INT_DEF=123");
+ target.config_values().defines().push_back("STR_DEF=\"ABCD 1\"");
+ target.config_values().defines().push_back("INCLUDE=<header.h>");
+ ASSERT_TRUE(target.OnResolved(&err));
+ targets.push_back(&target);
+
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+ const char expected[] =
+ "-DBOOL_DEF -DINT_DEF=123 \\\"-DSTR_DEF=\\\\\\\"ABCD 1\\\\\\\"\\\" "
+ "\\\"-DINCLUDE=\\u003Cheader.h>\\\"";
+ EXPECT_TRUE(out.find(expected) != std::string::npos);
+}
+
+TEST_F(CompileCommandsTest, WinPrecompiledHeaders) {
+ Err err;
+
+ // This setup's toolchain does not have precompiled headers defined.
+ // A precompiled header toolchain.
+ Settings pch_settings(build_settings(), "withpch/");
+ Toolchain pch_toolchain(&pch_settings,
+ Label(SourceDir("//toolchain/"), "withpch"));
+ pch_settings.set_toolchain_label(pch_toolchain.label());
+ pch_settings.set_default_toolchain_label(toolchain()->label());
+
+ // Declare a C++ compiler that supports PCH.
+ std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cxx));
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
+ TestWithScope::SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool);
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cc));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // This target doesn't specify precompiled headers.
+ {
+ std::vector<const Target*> targets;
+ Target no_pch_target(&pch_settings,
+ Label(SourceDir("//foo/"), "no_pch_target"));
+ no_pch_target.set_output_type(Target::SOURCE_SET);
+ no_pch_target.visibility().SetPublic();
+ no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ no_pch_target.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+ targets.push_back(&no_pch_target);
+
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char no_pch_expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.c\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
+ "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char no_pch_expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "withpch/obj/foo/no_pch_target.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.c\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
+ "withpch/obj/foo/no_pch_target.input2.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(no_pch_expected, out);
+ }
+
+ // This target specifies PCH.
+ {
+ std::vector<const Target*> targets;
+ Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
+ pch_target.config_values().set_precompiled_header("build/precompile.h");
+ pch_target.config_values().set_precompiled_source(
+ SourceFile("//build/precompile.cc"));
+ pch_target.set_output_type(Target::SOURCE_SET);
+ pch_target.visibility().SetPublic();
+ pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+ targets.push_back(&pch_target);
+
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char pch_win_expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc "
+ "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h -o "
+ "withpch/obj/foo/pch_target.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.c\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"cc ../../foo/input2.c "
+ "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h -o "
+ "withpch/obj/foo/pch_target.input2.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char pch_win_expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc "
+ "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h -o "
+ "withpch/obj/foo/pch_target.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.c\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"cc ../../foo/input2.c "
+ "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h -o "
+ "withpch/obj/foo/pch_target.input2.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(pch_win_expected, out);
+ }
+}
+
+TEST_F(CompileCommandsTest, GCCPrecompiledHeaders) {
+ Err err;
+
+ // This setup's toolchain does not have precompiled headers defined.
+ // A precompiled header toolchain.
+ Settings pch_settings(build_settings(), "withpch/");
+ Toolchain pch_toolchain(&pch_settings,
+ Label(SourceDir("//toolchain/"), "withpch"));
+ pch_settings.set_toolchain_label(pch_toolchain.label());
+ pch_settings.set_default_toolchain_label(toolchain()->label());
+
+ // Declare a C++ compiler that supports PCH.
+ std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cxx));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
+ TestWithScope::SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool);
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cc));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // This target doesn't specify precompiled headers.
+ {
+ std::vector<const Target*> targets;
+ Target no_pch_target(&pch_settings,
+ Label(SourceDir("//foo/"), "no_pch_target"));
+ no_pch_target.set_output_type(Target::SOURCE_SET);
+ no_pch_target.visibility().SetPublic();
+ no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ no_pch_target.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+ targets.push_back(&no_pch_target);
+
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char no_pch_expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.c\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
+ "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char no_pch_expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc -o "
+ "withpch/obj/foo/no_pch_target.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.c\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
+ "withpch/obj/foo/no_pch_target.input2.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(no_pch_expected, out);
+ }
+
+ // This target specifies PCH.
+ {
+ std::vector<const Target*> targets;
+ Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
+ pch_target.config_values().set_precompiled_source(
+ SourceFile("//build/precompile.h"));
+ pch_target.config_values().cflags_c().push_back("-std=c99");
+ pch_target.set_output_type(Target::SOURCE_SET);
+ pch_target.visibility().SetPublic();
+ pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+ targets.push_back(&pch_target);
+
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char pch_gcc_expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.cc\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"c++ ../../foo/input1.cc -include "
+ "withpch/obj/build/pch_target.precompile.h-cc -o "
+ "withpch/obj/foo/pch_target.input1.o\"\r\n"
+ " },\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input2.c\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"cc ../../foo/input2.c -std=c99 -include "
+ "withpch/obj/build/pch_target.precompile.h-c -o "
+ "withpch/obj/foo/pch_target.input2.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char pch_gcc_expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.cc\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"c++ ../../foo/input1.cc -include "
+ "withpch/obj/build/pch_target.precompile.h-cc -o "
+ "withpch/obj/foo/pch_target.input1.o\"\n"
+ " },\n"
+ " {\n"
+ " \"file\": \"../../foo/input2.c\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"cc ../../foo/input2.c -std=c99 -include "
+ "withpch/obj/build/pch_target.precompile.h-c -o "
+ "withpch/obj/foo/pch_target.input2.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(pch_gcc_expected, out);
+ }
+}
+
+TEST_F(CompileCommandsTest, EscapedFlags) {
+ Err err;
+
+ std::vector<const Target*> targets;
+ Target target(settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.sources().push_back(SourceFile("//foo/input1.c"));
+ target.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
+ target.SetToolchain(toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+ targets.push_back(&target);
+
+ CompileCommandsWriter writer;
+ std::string out = writer.RenderJSON(build_settings(), targets);
+
+#if defined(OS_WIN)
+ const char expected[] =
+ "[\r\n"
+ " {\r\n"
+ " \"file\": \"../../foo/input1.c\",\r\n"
+ " \"directory\": \"out/Debug\",\r\n"
+ " \"command\": \"cc ../../foo/input1.c -DCONFIG=\\\"/config\\\" "
+ "-o obj/foo/bar.input1.o\"\r\n"
+ " }\r\n"
+ "]\r\n";
+#else
+ const char expected[] =
+ "[\n"
+ " {\n"
+ " \"file\": \"../../foo/input1.c\",\n"
+ " \"directory\": \"out/Debug\",\n"
+ " \"command\": \"cc ../../foo/input1.c -DCONFIG=\\\"/config\\\" "
+ "-o obj/foo/bar.input1.o\"\n"
+ " }\n"
+ "]\n";
+#endif
+ EXPECT_EQ(expected, out);
+}
+
+TEST_F(CompileCommandsTest, CompDBFilter) {
+ Err err;
+
+ std::vector<const Target*> targets;
+ Target target1(settings(), Label(SourceDir("//foo/"), "bar1"));
+ target1.set_output_type(Target::SOURCE_SET);
+ target1.sources().push_back(SourceFile("//foo/input1.c"));
+ target1.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
+ target1.SetToolchain(toolchain());
+ ASSERT_TRUE(target1.OnResolved(&err));
+ targets.push_back(&target1);
+
+ Target target2(settings(), Label(SourceDir("//foo/"), "bar2"));
+ target2.set_output_type(Target::SOURCE_SET);
+ target2.sources().push_back(SourceFile("//foo/input2.c"));
+ target2.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
+ target2.SetToolchain(toolchain());
+ ASSERT_TRUE(target2.OnResolved(&err));
+ targets.push_back(&target2);
+
+ Target target3(settings(), Label(SourceDir("//foo/"), "bar3"));
+ target3.set_output_type(Target::SOURCE_SET);
+ target3.sources().push_back(SourceFile("//foo/input3.c"));
+ target3.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
+ target3.SetToolchain(toolchain());
+ ASSERT_TRUE(target3.OnResolved(&err));
+ targets.push_back(&target3);
+
+ target1.private_deps().push_back(LabelTargetPair(&target2));
+ target1.private_deps().push_back(LabelTargetPair(&target3));
+
+ CompileCommandsWriter writer;
+
+ std::set<std::string> filter1;
+ std::vector<const Target*> test_results1 =
+ writer.FilterTargets(targets, filter1);
+ ASSERT_TRUE(test_results1.empty());
+
+ std::set<std::string> filter2;
+ filter2.insert(target1.label().name());
+ std::vector<const Target*> test_results2 =
+ writer.FilterTargets(targets, filter2);
+ ASSERT_EQ(test_results2, targets);
+
+ std::set<std::string> filter3;
+ filter3.insert(target2.label().name());
+ std::vector<const Target*> test_result3 =
+ writer.FilterTargets(targets, filter3);
+ std::vector<const Target*> expected_results3;
+ expected_results3.push_back(&target2);
+ ASSERT_EQ(test_result3, expected_results3);
+}
diff --git a/gn/src/gn/config.cc b/gn/src/gn/config.cc
new file mode 100644
index 00000000000..c0a51666510
--- /dev/null
+++ b/gn/src/gn/config.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 "gn/config.h"
+
+#include "gn/err.h"
+#include "gn/input_file_manager.h"
+#include "gn/scheduler.h"
+
+Config::Config(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files)
+ : Item(settings, label, build_dependency_files) {}
+
+Config::~Config() = default;
+
+Config* Config::AsConfig() {
+ return this;
+}
+
+const Config* Config::AsConfig() const {
+ return this;
+}
+
+bool Config::OnResolved(Err* err) {
+ DCHECK(!resolved_);
+ resolved_ = true;
+
+ if (!configs_.empty()) {
+ // Subconfigs, flatten.
+ //
+ // Implementation note for the future: Flattening these here means we
+ // lose the ability to de-dupe subconfigs. If a subconfig is listed as
+ // a separate config or a subconfig that also applies to the target, the
+ // subconfig's flags will be duplicated.
+ //
+ // If we want to be able to de-dupe these, here's one idea. As a config is
+ // resolved, inline any sub-sub configs so the configs_ vector is a flat
+ // list, much the same way that libs and lib_dirs are pushed through
+ // targets. Do the same for Target.configs_ when a target is resolved. This
+ // will naturally de-dupe and also prevents recursive config walking to
+ // compute every possible flag, although it will expand the configs list on
+ // a target nontrivially (depending on build configuration).
+ composite_values_ = own_values_;
+ for (const auto& pair : configs_)
+ composite_values_.AppendValues(pair.ptr->resolved_values());
+ }
+ return true;
+}
diff --git a/gn/src/gn/config.h b/gn/src/gn/config.h
new file mode 100644
index 00000000000..cb208d1b66d
--- /dev/null
+++ b/gn/src/gn/config.h
@@ -0,0 +1,69 @@
+// 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 TOOLS_GN_CONFIG_H_
+#define TOOLS_GN_CONFIG_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/config_values.h"
+#include "gn/item.h"
+#include "gn/label_ptr.h"
+#include "gn/unique_vector.h"
+
+// Represents a named config in the dependency graph.
+//
+// A config can list other configs. We track both the data assigned directly
+// on the config, this list of sub-configs, and (when the config is resolved)
+// the resulting values of everything merged together. The flatten step
+// means we can avoid doing a recursive config walk for every target to compute
+// flags.
+class Config : public Item {
+ public:
+ // We track the set of build files that may affect this config, please refer
+ // to Scope for how this is determined.
+ Config(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files = {});
+ ~Config() override;
+
+ // Item implementation.
+ Config* AsConfig() override;
+ const Config* AsConfig() const override;
+ bool OnResolved(Err* err) override;
+
+ // The values set directly on this config. This will not contain data from
+ // sub-configs.
+ ConfigValues& own_values() { return own_values_; }
+ const ConfigValues& own_values() const { return own_values_; }
+
+ // The values that represent this config and all sub-configs combined into
+ // one. This is only valid after the config is resolved (when we know the
+ // contents of the sub-configs).
+ const ConfigValues& resolved_values() const {
+ DCHECK(resolved_);
+ if (configs_.empty()) // No sub configs, just use the regular values.
+ return own_values_;
+ return composite_values_;
+ }
+
+ // List of sub-configs.
+ const UniqueVector<LabelConfigPair>& configs() const { return configs_; }
+ UniqueVector<LabelConfigPair>& configs() { return configs_; }
+
+ private:
+ ConfigValues own_values_;
+
+ // Contains the own_values combined with sub-configs. Most configs don't have
+ // sub-configs. So as an optimization, this is not populated if there are no
+ // items in configs_. The resolved_values() getter handles this.
+ bool resolved_ = false;
+ ConfigValues composite_values_;
+
+ UniqueVector<LabelConfigPair> configs_;
+
+ DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+#endif // TOOLS_GN_CONFIG_H_
diff --git a/gn/src/gn/config_unittest.cc b/gn/src/gn/config_unittest.cc
new file mode 100644
index 00000000000..ec7ef3caa34
--- /dev/null
+++ b/gn/src/gn/config_unittest.cc
@@ -0,0 +1,85 @@
+// 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 "gn/config.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+// Tests that the "resolved" values are the same as "own" values when there
+// are no subconfigs.
+TEST(Config, ResolvedNoSub) {
+ TestWithScope setup;
+ Err err;
+
+ Config config(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ config.own_values().defines().push_back("FOO");
+ ASSERT_TRUE(config.OnResolved(&err));
+
+ // The resolved values should be the same as the value we put in to
+ // own_values().
+ ASSERT_EQ(1u, config.resolved_values().defines().size());
+ EXPECT_EQ("FOO", config.resolved_values().defines()[0]);
+
+ // As an optimization, the string should actually refer to the original. This
+ // isn't required to pass for semantic correctness, though.
+ EXPECT_TRUE(&config.own_values() == &config.resolved_values());
+}
+
+// Tests that subconfigs are resolved in the correct order.
+TEST(Config, ResolvedSub) {
+ TestWithScope setup;
+ Err err;
+
+ Config sub1(setup.settings(), Label(SourceDir("//foo/"), "1"));
+ sub1.own_values().defines().push_back("ONE");
+ ASSERT_TRUE(sub1.OnResolved(&err));
+
+ Config sub2(setup.settings(), Label(SourceDir("//foo/"), "2"));
+ sub2.own_values().defines().push_back("TWO");
+ ASSERT_TRUE(sub2.OnResolved(&err));
+
+ Config config(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ config.own_values().defines().push_back("FOO");
+ config.configs().push_back(LabelConfigPair(&sub1));
+ config.configs().push_back(LabelConfigPair(&sub2));
+ ASSERT_TRUE(config.OnResolved(&err));
+
+ // The resolved values should be the same as the value we put in to
+ // own_values().
+ ASSERT_EQ(3u, config.resolved_values().defines().size());
+ EXPECT_EQ("FOO", config.resolved_values().defines()[0]);
+ EXPECT_EQ("ONE", config.resolved_values().defines()[1]);
+ EXPECT_EQ("TWO", config.resolved_values().defines()[2]);
+
+ // The "own" values should be unchanged.
+ ASSERT_EQ(1u, config.own_values().defines().size());
+ EXPECT_EQ("FOO", config.own_values().defines()[0]);
+}
+
+// Tests that subconfigs of subconfigs are resolved properly.
+TEST(Config, SubSub) {
+ TestWithScope setup;
+ Err err;
+
+ // Set up first -> middle -> last configs.
+ Config last(setup.settings(), Label(SourceDir("//foo/"), "last"));
+ last.own_values().defines().push_back("LAST");
+ ASSERT_TRUE(last.OnResolved(&err));
+
+ Config middle(setup.settings(), Label(SourceDir("//foo/"), "middle"));
+ middle.own_values().defines().push_back("MIDDLE");
+ middle.configs().push_back(LabelConfigPair(&last));
+ ASSERT_TRUE(middle.OnResolved(&err));
+
+ Config first(setup.settings(), Label(SourceDir("//foo/"), "first"));
+ first.own_values().defines().push_back("FIRST");
+ first.configs().push_back(LabelConfigPair(&middle));
+ ASSERT_TRUE(first.OnResolved(&err));
+
+ // Check final resolved defines on "first".
+ ASSERT_EQ(3u, first.resolved_values().defines().size());
+ EXPECT_EQ("FIRST", first.resolved_values().defines()[0]);
+ EXPECT_EQ("MIDDLE", first.resolved_values().defines()[1]);
+ EXPECT_EQ("LAST", first.resolved_values().defines()[2]);
+}
diff --git a/gn/src/gn/config_values.cc b/gn/src/gn/config_values.cc
new file mode 100644
index 00000000000..9bddf993765
--- /dev/null
+++ b/gn/src/gn/config_values.cc
@@ -0,0 +1,52 @@
+// 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 "gn/config_values.h"
+
+namespace {
+
+template <typename T>
+void VectorAppend(std::vector<T>* append_to,
+ const std::vector<T>& append_this) {
+ if (append_this.empty())
+ return;
+ append_to->insert(append_to->end(), append_this.begin(), append_this.end());
+}
+
+} // namespace
+
+ConfigValues::ConfigValues() = default;
+
+ConfigValues::~ConfigValues() = default;
+
+void ConfigValues::AppendValues(const ConfigValues& append) {
+ VectorAppend(&asmflags_, append.asmflags_);
+ VectorAppend(&arflags_, append.arflags_);
+ VectorAppend(&cflags_, append.cflags_);
+ VectorAppend(&cflags_c_, append.cflags_c_);
+ VectorAppend(&cflags_cc_, append.cflags_cc_);
+ VectorAppend(&cflags_objc_, append.cflags_objc_);
+ VectorAppend(&cflags_objcc_, append.cflags_objcc_);
+ VectorAppend(&defines_, append.defines_);
+ VectorAppend(&frameworks_, append.frameworks_);
+ VectorAppend(&weak_frameworks_, append.weak_frameworks_);
+ VectorAppend(&framework_dirs_, append.framework_dirs_);
+ VectorAppend(&include_dirs_, append.include_dirs_);
+ VectorAppend(&inputs_, append.inputs_);
+ VectorAppend(&ldflags_, append.ldflags_);
+ VectorAppend(&lib_dirs_, append.lib_dirs_);
+ VectorAppend(&libs_, append.libs_);
+ VectorAppend(&rustflags_, append.rustflags_);
+ VectorAppend(&rustenv_, append.rustenv_);
+ VectorAppend(&swiftflags_, append.swiftflags_);
+
+ // Only append precompiled header if there isn't one. It might be nice to
+ // throw an error if there are conflicting precompiled headers, but that
+ // requires piping through some context of the actual configs involved, and
+ // conflicts here should be very unusual. Instead, use the first value.
+ if (!append.precompiled_header_.empty() && !precompiled_header_.empty())
+ precompiled_header_ = append.precompiled_header_;
+ if (!append.precompiled_source_.is_null() && !precompiled_source_.is_null())
+ precompiled_source_ = append.precompiled_source_;
+}
diff --git a/gn/src/gn/config_values.h b/gn/src/gn/config_values.h
new file mode 100644
index 00000000000..6361e78e965
--- /dev/null
+++ b/gn/src/gn/config_values.h
@@ -0,0 +1,108 @@
+// 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 TOOLS_GN_CONFIG_VALUES_H_
+#define TOOLS_GN_CONFIG_VALUES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/lib_file.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+
+// Holds the values (include_dirs, defines, compiler flags, etc.) for a given
+// config or target.
+class ConfigValues {
+ public:
+ ConfigValues();
+ ~ConfigValues();
+
+ // Appends the values from the given config to this one.
+ void AppendValues(const ConfigValues& append);
+
+#define STRING_VALUES_ACCESSOR(name) \
+ const std::vector<std::string>& name() const { return name##_; } \
+ std::vector<std::string>& name() { return name##_; }
+#define DIR_VALUES_ACCESSOR(name) \
+ const std::vector<SourceDir>& name() const { return name##_; } \
+ std::vector<SourceDir>& name() { return name##_; }
+
+ // =================================================================
+ // IMPORTANT: If you add a new one, be sure to update AppendValues()
+ // and command_desc.cc.
+ // =================================================================
+ STRING_VALUES_ACCESSOR(arflags)
+ STRING_VALUES_ACCESSOR(asmflags)
+ STRING_VALUES_ACCESSOR(cflags)
+ STRING_VALUES_ACCESSOR(cflags_c)
+ STRING_VALUES_ACCESSOR(cflags_cc)
+ STRING_VALUES_ACCESSOR(cflags_objc)
+ STRING_VALUES_ACCESSOR(cflags_objcc)
+ STRING_VALUES_ACCESSOR(defines)
+ DIR_VALUES_ACCESSOR(framework_dirs)
+ STRING_VALUES_ACCESSOR(frameworks)
+ STRING_VALUES_ACCESSOR(weak_frameworks)
+ DIR_VALUES_ACCESSOR(include_dirs)
+ STRING_VALUES_ACCESSOR(ldflags)
+ DIR_VALUES_ACCESSOR(lib_dirs)
+ STRING_VALUES_ACCESSOR(rustflags)
+ STRING_VALUES_ACCESSOR(rustenv)
+ STRING_VALUES_ACCESSOR(swiftflags)
+ // =================================================================
+ // IMPORTANT: If you add a new one, be sure to update AppendValues()
+ // and command_desc.cc.
+ // =================================================================
+
+#undef STRING_VALUES_ACCESSOR
+#undef DIR_VALUES_ACCESSOR
+
+ const std::vector<SourceFile>& inputs() const { return inputs_; }
+ std::vector<SourceFile>& inputs() { return inputs_; }
+
+ const std::vector<LibFile>& libs() const { return libs_; }
+ std::vector<LibFile>& libs() { return libs_; }
+
+ const std::vector<std::pair<std::string, LibFile>>& externs() const {
+ return externs_;
+ }
+ std::vector<std::pair<std::string, LibFile>>& externs() { return externs_; }
+
+ bool has_precompiled_headers() const {
+ return !precompiled_header_.empty() || !precompiled_source_.is_null();
+ }
+ const std::string& precompiled_header() const { return precompiled_header_; }
+ void set_precompiled_header(const std::string& f) { precompiled_header_ = f; }
+ const SourceFile& precompiled_source() const { return precompiled_source_; }
+ void set_precompiled_source(const SourceFile& f) { precompiled_source_ = f; }
+
+ private:
+ std::vector<std::string> arflags_;
+ std::vector<std::string> asmflags_;
+ std::vector<std::string> cflags_;
+ std::vector<std::string> cflags_c_;
+ std::vector<std::string> cflags_cc_;
+ std::vector<std::string> cflags_objc_;
+ std::vector<std::string> cflags_objcc_;
+ std::vector<std::string> defines_;
+ std::vector<SourceDir> include_dirs_;
+ std::vector<SourceDir> framework_dirs_;
+ std::vector<std::string> frameworks_;
+ std::vector<std::string> weak_frameworks_;
+ std::vector<SourceFile> inputs_;
+ std::vector<std::string> ldflags_;
+ std::vector<SourceDir> lib_dirs_;
+ std::vector<LibFile> libs_;
+ std::vector<std::string> rustflags_;
+ std::vector<std::string> rustenv_;
+ std::vector<std::string> swiftflags_;
+ std::vector<std::pair<std::string, LibFile>> externs_;
+ // If you add a new one, be sure to update AppendValues().
+
+ std::string precompiled_header_;
+ SourceFile precompiled_source_;
+};
+
+#endif // TOOLS_GN_CONFIG_VALUES_H_
diff --git a/gn/src/gn/config_values_extractors.cc b/gn/src/gn/config_values_extractors.cc
new file mode 100644
index 00000000000..409e11d3a41
--- /dev/null
+++ b/gn/src/gn/config_values_extractors.cc
@@ -0,0 +1,34 @@
+// 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 "gn/config_values_extractors.h"
+
+#include "gn/escape.h"
+
+namespace {
+
+class EscapedStringWriter {
+ public:
+ explicit EscapedStringWriter(const EscapeOptions& escape_options)
+ : escape_options_(escape_options) {}
+
+ void operator()(const std::string& s, std::ostream& out) const {
+ out << " ";
+ EscapeStringToStream(out, s, escape_options_);
+ }
+
+ private:
+ const EscapeOptions& escape_options_;
+};
+
+} // namespace
+
+void RecursiveTargetConfigStringsToStream(
+ const Target* target,
+ const std::vector<std::string>& (ConfigValues::*getter)() const,
+ const EscapeOptions& escape_options,
+ std::ostream& out) {
+ RecursiveTargetConfigToStream(target, getter,
+ EscapedStringWriter(escape_options), out);
+}
diff --git a/gn/src/gn/config_values_extractors.h b/gn/src/gn/config_values_extractors.h
new file mode 100644
index 00000000000..ae517610350
--- /dev/null
+++ b/gn/src/gn/config_values_extractors.h
@@ -0,0 +1,102 @@
+// 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 TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
+#define TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "gn/config.h"
+#include "gn/config_values.h"
+#include "gn/target.h"
+
+struct EscapeOptions;
+
+// Provides a way to iterate through all ConfigValues applying to a given
+// target. This is more complicated than normal because the target has a list
+// of configs applying to it, and also config values on the target itself.
+//
+// This iterator allows one to iterate through all of these in a defined order
+// in one convenient loop. The order is defined to be the ConfigValues on the
+// target itself first, then the applying configs, in order.
+//
+// Example:
+// for (ConfigValueIterator iter(target); !iter.done(); iter.Next())
+// DoSomething(iter.cur());
+class ConfigValuesIterator {
+ public:
+ explicit ConfigValuesIterator(const Target* target) : target_(target) {}
+
+ bool done() const {
+ return cur_index_ >= static_cast<int>(target_->configs().size());
+ }
+
+ const ConfigValues& cur() const {
+ if (cur_index_ == -1)
+ return target_->config_values();
+ return target_->configs()[cur_index_].ptr->resolved_values();
+ }
+
+ // Returns the origin of who added this config, if any. This will always be
+ // null for the config values of a target itself.
+ const ParseNode* origin() const {
+ if (cur_index_ == -1)
+ return nullptr;
+ return target_->configs()[cur_index_].origin;
+ }
+
+ void Next() { cur_index_++; }
+
+ // Returns the config holding the current config values, or NULL for those
+ // config values associated with the target itself.
+ const Config* GetCurrentConfig() const {
+ if (cur_index_ == -1)
+ return nullptr;
+ return target_->configs()[cur_index_].ptr;
+ }
+
+ private:
+ const Target* target_;
+
+ // Represents an index into the target_'s configs() or, when -1, the config
+ // values on the target itself.
+ int cur_index_ = -1;
+};
+
+template <typename T, class Writer>
+inline void ConfigValuesToStream(const ConfigValues& values,
+ const std::vector<T>& (ConfigValues::*getter)()
+ const,
+ const Writer& writer,
+ std::ostream& out) {
+ const std::vector<T>& v = (values.*getter)();
+ for (size_t i = 0; i < v.size(); i++)
+ writer(v[i], out);
+}
+
+// Writes a given config value that applies to a given target. This collects
+// all values from the target itself and all configs that apply, and writes
+// then in order.
+template <typename T, class Writer>
+inline void RecursiveTargetConfigToStream(
+ const Target* target,
+ const std::vector<T>& (ConfigValues::*getter)() const,
+ const Writer& writer,
+ std::ostream& out) {
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next())
+ ConfigValuesToStream(iter.cur(), getter, writer, out);
+}
+
+// Writes the values out as strings with no transformation.
+void RecursiveTargetConfigStringsToStream(
+ const Target* target,
+ const std::vector<std::string>& (ConfigValues::*getter)() const,
+ const EscapeOptions& escape_options,
+ std::ostream& out);
+
+#endif // TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
diff --git a/gn/src/gn/config_values_extractors_unittest.cc b/gn/src/gn/config_values_extractors_unittest.cc
new file mode 100644
index 00000000000..7c520ad5566
--- /dev/null
+++ b/gn/src/gn/config_values_extractors_unittest.cc
@@ -0,0 +1,140 @@
+// 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 <sstream>
+
+#include "gn/config.h"
+#include "gn/config_values_extractors.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+struct FlagWriter {
+ void operator()(const std::string& dir, std::ostream& out) const {
+ out << dir << " ";
+ }
+};
+
+struct IncludeWriter {
+ void operator()(const SourceDir& dir, std::ostream& out) const {
+ out << dir.value() << " ";
+ }
+};
+
+} // namespace
+
+TEST(ConfigValuesExtractors, IncludeOrdering) {
+ TestWithScope setup;
+ Err err;
+
+ // Construct a chain of dependencies: target -> dep1 -> dep2
+ // Add representative values: cflags (opaque, always copied) and include_dirs
+ // (uniquified) to each one so we can check what comes out the other end.
+
+ // Set up dep2, direct and all dependent configs.
+ Config dep2_all(setup.settings(), Label(SourceDir("//dep2/"), "all"));
+ dep2_all.own_values().cflags().push_back("--dep2-all");
+ dep2_all.own_values().include_dirs().push_back(SourceDir("//dep2/all/"));
+ ASSERT_TRUE(dep2_all.OnResolved(&err));
+
+ Config dep2_direct(setup.settings(), Label(SourceDir("//dep2/"), "direct"));
+ dep2_direct.own_values().cflags().push_back("--dep2-direct");
+ dep2_direct.own_values().include_dirs().push_back(
+ SourceDir("//dep2/direct/"));
+ ASSERT_TRUE(dep2_direct.OnResolved(&err));
+
+ Target dep2(setup.settings(), Label(SourceDir("//dep2/"), "dep2"));
+ dep2.set_output_type(Target::SOURCE_SET);
+ dep2.visibility().SetPublic();
+ dep2.SetToolchain(setup.toolchain());
+ dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all));
+ dep2.public_configs().push_back(LabelConfigPair(&dep2_direct));
+
+ // Set up dep1, direct and all dependent configs. Also set up a subconfig
+ // on "dep1_all" to test sub configs.
+ Config dep1_all_sub(setup.settings(), Label(SourceDir("//dep1"), "allch"));
+ dep1_all_sub.own_values().cflags().push_back("--dep1-all-sub");
+ ASSERT_TRUE(dep1_all_sub.OnResolved(&err));
+
+ Config dep1_all(setup.settings(), Label(SourceDir("//dep1/"), "all"));
+ dep1_all.own_values().cflags().push_back("--dep1-all");
+ dep1_all.own_values().include_dirs().push_back(SourceDir("//dep1/all/"));
+ dep1_all.configs().push_back(LabelConfigPair(&dep1_all_sub));
+ ASSERT_TRUE(dep1_all.OnResolved(&err));
+
+ Config dep1_direct(setup.settings(), Label(SourceDir("//dep1/"), "direct"));
+ dep1_direct.own_values().cflags().push_back("--dep1-direct");
+ dep1_direct.own_values().include_dirs().push_back(
+ SourceDir("//dep1/direct/"));
+ ASSERT_TRUE(dep1_direct.OnResolved(&err));
+
+ Target dep1(setup.settings(), Label(SourceDir("//dep1/"), "dep1"));
+ dep1.set_output_type(Target::SOURCE_SET);
+ dep1.visibility().SetPublic();
+ dep1.SetToolchain(setup.toolchain());
+ dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all));
+ dep1.public_configs().push_back(LabelConfigPair(&dep1_direct));
+ dep1.private_deps().push_back(LabelTargetPair(&dep2));
+
+ // Set up target, direct and all dependent configs.
+ Config target_all(setup.settings(), Label(SourceDir("//target/"), "all"));
+ target_all.visibility().SetPublic();
+ target_all.own_values().cflags().push_back("--target-all");
+ target_all.own_values().include_dirs().push_back(SourceDir("//target/all/"));
+ ASSERT_TRUE(target_all.OnResolved(&err));
+
+ Config target_direct(setup.settings(),
+ Label(SourceDir("//target/"), "direct"));
+ target_direct.visibility().SetPublic();
+ target_direct.own_values().cflags().push_back("--target-direct");
+ target_direct.own_values().include_dirs().push_back(
+ SourceDir("//target/direct/"));
+ ASSERT_TRUE(target_direct.OnResolved(&err));
+
+ // This config is applied directly to target.
+ Config target_config(setup.settings(),
+ Label(SourceDir("//target/"), "config"));
+ target_config.visibility().SetPublic();
+ target_config.own_values().cflags().push_back("--target-config");
+ target_config.own_values().include_dirs().push_back(
+ SourceDir("//target/config/"));
+ ASSERT_TRUE(target_config.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//target/"), "target"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.SetToolchain(setup.toolchain());
+ target.all_dependent_configs().push_back(LabelConfigPair(&target_all));
+ target.public_configs().push_back(LabelConfigPair(&target_direct));
+ target.configs().push_back(LabelConfigPair(&target_config));
+ target.private_deps().push_back(LabelTargetPair(&dep1));
+
+ // Additionally add some values directly on "target".
+ target.config_values().cflags().push_back("--target");
+ target.config_values().include_dirs().push_back(SourceDir("//target/"));
+
+ // Mark targets resolved. This should push dependent configs.
+ ASSERT_TRUE(dep2.OnResolved(&err));
+ ASSERT_TRUE(dep1.OnResolved(&err));
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Verify cflags by serializing.
+ std::ostringstream flag_out;
+ FlagWriter flag_writer;
+ RecursiveTargetConfigToStream<std::string, FlagWriter>(
+ &target, &ConfigValues::cflags, flag_writer, flag_out);
+ EXPECT_EQ(flag_out.str(),
+ "--target --target-config --target-all --target-direct "
+ "--dep1-all --dep1-all-sub --dep2-all --dep1-direct ");
+
+ // Verify include dirs by serializing.
+ std::ostringstream include_out;
+ IncludeWriter include_writer;
+ RecursiveTargetConfigToStream<SourceDir, IncludeWriter>(
+ &target, &ConfigValues::include_dirs, include_writer, include_out);
+ EXPECT_EQ(include_out.str(),
+ "//target/ //target/config/ //target/all/ //target/direct/ "
+ "//dep1/all/ //dep2/all/ //dep1/direct/ ");
+}
diff --git a/gn/src/gn/config_values_generator.cc b/gn/src/gn/config_values_generator.cc
new file mode 100644
index 00000000000..fac03d774d6
--- /dev/null
+++ b/gn/src/gn/config_values_generator.cc
@@ -0,0 +1,170 @@
+// 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 "gn/config_values_generator.h"
+
+#include "base/strings/string_util.h"
+#include "gn/build_settings.h"
+#include "gn/config_values.h"
+#include "gn/frameworks_utils.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/value.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+namespace {
+
+void GetStringList(Scope* scope,
+ const char* var_name,
+ ConfigValues* config_values,
+ std::vector<std::string>& (ConfigValues::*accessor)(),
+ Err* err) {
+ const Value* value = scope->GetValue(var_name, true);
+ if (!value)
+ return; // No value, empty input and succeed.
+
+ ExtractListOfStringValues(*value, &(config_values->*accessor)(), err);
+}
+
+void GetDirList(Scope* scope,
+ const char* var_name,
+ ConfigValues* config_values,
+ const SourceDir input_dir,
+ std::vector<SourceDir>& (ConfigValues::*accessor)(),
+ Err* err) {
+ const Value* value = scope->GetValue(var_name, true);
+ if (!value)
+ return; // No value, empty input and succeed.
+
+ std::vector<SourceDir> result;
+ ExtractListOfRelativeDirs(scope->settings()->build_settings(), *value,
+ input_dir, &result, err);
+ (config_values->*accessor)().swap(result);
+}
+
+void GetFrameworksList(Scope* scope,
+ const char* var_name,
+ ConfigValues* config_values,
+ std::vector<std::string>& (ConfigValues::*accessor)(),
+ Err* err) {
+ const Value* value = scope->GetValue(var_name, true);
+ if (!value)
+ return;
+
+ std::vector<std::string> frameworks;
+ if (!ExtractListOfStringValues(*value, &frameworks, err))
+ return;
+
+ // All strings must end with ".frameworks".
+ for (const std::string& framework : frameworks) {
+ std::string_view framework_name = GetFrameworkName(framework);
+ if (framework_name.empty()) {
+ *err = Err(*value,
+ "This frameworks value is wrong."
+ "All listed frameworks names must not include any\n"
+ "path component and have \".framework\" extension.");
+ return;
+ }
+ }
+
+ (config_values->*accessor)().swap(frameworks);
+}
+
+} // namespace
+
+ConfigValuesGenerator::ConfigValuesGenerator(ConfigValues* dest_values,
+ Scope* scope,
+ const SourceDir& input_dir,
+ Err* err)
+ : config_values_(dest_values),
+ scope_(scope),
+ input_dir_(input_dir),
+ err_(err) {}
+
+ConfigValuesGenerator::~ConfigValuesGenerator() = default;
+
+void ConfigValuesGenerator::Run() {
+#define FILL_STRING_CONFIG_VALUE(name) \
+ GetStringList(scope_, #name, config_values_, &ConfigValues::name, err_);
+#define FILL_DIR_CONFIG_VALUE(name) \
+ GetDirList(scope_, #name, config_values_, input_dir_, &ConfigValues::name, \
+ err_);
+
+ FILL_STRING_CONFIG_VALUE(arflags)
+ FILL_STRING_CONFIG_VALUE(asmflags)
+ FILL_STRING_CONFIG_VALUE(cflags)
+ FILL_STRING_CONFIG_VALUE(cflags_c)
+ FILL_STRING_CONFIG_VALUE(cflags_cc)
+ FILL_STRING_CONFIG_VALUE(cflags_objc)
+ FILL_STRING_CONFIG_VALUE(cflags_objcc)
+ FILL_STRING_CONFIG_VALUE(defines)
+ FILL_DIR_CONFIG_VALUE(framework_dirs)
+ FILL_DIR_CONFIG_VALUE(include_dirs)
+ FILL_STRING_CONFIG_VALUE(ldflags)
+ FILL_DIR_CONFIG_VALUE(lib_dirs)
+ FILL_STRING_CONFIG_VALUE(rustflags)
+ FILL_STRING_CONFIG_VALUE(rustenv)
+ FILL_STRING_CONFIG_VALUE(swiftflags)
+
+#undef FILL_STRING_CONFIG_VALUE
+#undef FILL_DIR_CONFIG_VALUE
+
+ // Inputs
+ const Value* inputs_value = scope_->GetValue(variables::kInputs, true);
+ if (inputs_value) {
+ ExtractListOfRelativeFiles(scope_->settings()->build_settings(),
+ *inputs_value, input_dir_,
+ &config_values_->inputs(), err_);
+ }
+
+ // Libs
+ const Value* libs_value = scope_->GetValue(variables::kLibs, true);
+ if (libs_value) {
+ ExtractListOfLibs(scope_->settings()->build_settings(), *libs_value,
+ input_dir_, &config_values_->libs(), err_);
+ }
+
+ // Externs
+ const Value* externs_value = scope_->GetValue(variables::kExterns, true);
+ if (externs_value) {
+ ExtractListOfExterns(scope_->settings()->build_settings(), *externs_value,
+ input_dir_, &config_values_->externs(), err_);
+ }
+
+ // Frameworks
+ GetFrameworksList(scope_, variables::kFrameworks, config_values_,
+ &ConfigValues::frameworks, err_);
+ GetFrameworksList(scope_, variables::kWeakFrameworks, config_values_,
+ &ConfigValues::weak_frameworks, err_);
+
+ // Precompiled headers.
+ const Value* precompiled_header_value =
+ scope_->GetValue(variables::kPrecompiledHeader, true);
+ if (precompiled_header_value) {
+ if (!precompiled_header_value->VerifyTypeIs(Value::STRING, err_))
+ return;
+
+ // Check for common errors. This is a string and not a file.
+ const std::string& pch_string = precompiled_header_value->string_value();
+ if (base::StartsWith(pch_string, "//", base::CompareCase::SENSITIVE)) {
+ *err_ = Err(
+ *precompiled_header_value, "This precompiled_header value is wrong.",
+ "You need to specify a string that the compiler will match against\n"
+ "the #include lines rather than a GN-style file name.\n");
+ return;
+ }
+ config_values_->set_precompiled_header(pch_string);
+ }
+
+ const Value* precompiled_source_value =
+ scope_->GetValue(variables::kPrecompiledSource, true);
+ if (precompiled_source_value) {
+ config_values_->set_precompiled_source(input_dir_.ResolveRelativeFile(
+ *precompiled_source_value, err_,
+ scope_->settings()->build_settings()->root_path_utf8()));
+ if (err_->has_error())
+ return;
+ }
+}
diff --git a/gn/src/gn/config_values_generator.h b/gn/src/gn/config_values_generator.h
new file mode 100644
index 00000000000..b7b008ca442
--- /dev/null
+++ b/gn/src/gn/config_values_generator.h
@@ -0,0 +1,46 @@
+// 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 TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
+#define TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/source_dir.h"
+
+class ConfigValues;
+class Err;
+class Scope;
+
+// This class fills in the config values from a given scope. It's shared
+// between the "config" function call and all the different binary target types
+// (shared library, static library, etc.) since all of these support the
+// various flags stored in the ConfigValues class.
+class ConfigValuesGenerator {
+ public:
+ ConfigValuesGenerator(ConfigValues* dest_values,
+ Scope* scope,
+ const SourceDir& input_dir,
+ Err* err);
+ ~ConfigValuesGenerator();
+
+ // Sets the error passed to the constructor on failure.
+ void Run();
+
+ private:
+ ConfigValues* config_values_;
+ Scope* scope_;
+ const SourceDir input_dir_;
+ Err* err_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConfigValuesGenerator);
+};
+
+// For using in documentation for functions which use this.
+#define CONFIG_VALUES_VARS_HELP \
+ " Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,\n" \
+ " asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,\n" \
+ " libs, precompiled_header, precompiled_source, rustflags,\n" \
+ " rustenv, swiftflags\n"
+
+#endif // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/gn/src/gn/copy_target_generator.cc b/gn/src/gn/copy_target_generator.cc
new file mode 100644
index 00000000000..a0dc9242c96
--- /dev/null
+++ b/gn/src/gn/copy_target_generator.cc
@@ -0,0 +1,44 @@
+// 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 "gn/copy_target_generator.h"
+
+#include "gn/build_settings.h"
+#include "gn/filesystem_utils.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+
+CopyTargetGenerator::CopyTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err) {}
+
+CopyTargetGenerator::~CopyTargetGenerator() = default;
+
+void CopyTargetGenerator::DoRun() {
+ target_->set_output_type(Target::COPY_FILES);
+
+ if (!FillSources())
+ return;
+ if (!FillOutputs(true))
+ return;
+
+ if (target_->sources().empty()) {
+ *err_ = Err(
+ function_call_, "Empty sources for copy command.",
+ "You have to specify at least one file to copy in the \"sources\".");
+ return;
+ }
+ if (target_->action_values().outputs().list().size() != 1) {
+ *err_ = Err(
+ function_call_, "Copy command must have exactly one output.",
+ "You must specify exactly one value in the \"outputs\" array for the "
+ "destination of the copy\n(see \"gn help copy\"). If there are "
+ "multiple sources to copy, use source expansion\n(see \"gn help "
+ "source_expansion\").");
+ return;
+ }
+}
diff --git a/gn/src/gn/copy_target_generator.h b/gn/src/gn/copy_target_generator.h
new file mode 100644
index 00000000000..bec05e35726
--- /dev/null
+++ b/gn/src/gn/copy_target_generator.h
@@ -0,0 +1,27 @@
+// 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 TOOLS_GN_COPY_TARGET_GENERATOR_H_
+#define TOOLS_GN_COPY_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target_generator.h"
+
+// Populates a Target with the values from a copy rule.
+class CopyTargetGenerator : public TargetGenerator {
+ public:
+ CopyTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ ~CopyTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CopyTargetGenerator);
+};
+
+#endif // TOOLS_GN_COPY_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/create_bundle_target_generator.cc b/gn/src/gn/create_bundle_target_generator.cc
new file mode 100644
index 00000000000..b614a9ca648
--- /dev/null
+++ b/gn/src/gn/create_bundle_target_generator.cc
@@ -0,0 +1,318 @@
+// Copyright 2016 The Chromium 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 "gn/create_bundle_target_generator.h"
+
+#include <map>
+
+#include "base/logging.h"
+#include "gn/filesystem_utils.h"
+#include "gn/label_pattern.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/substitution_type.h"
+#include "gn/target.h"
+#include "gn/value.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+CreateBundleTargetGenerator::CreateBundleTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err) {}
+
+CreateBundleTargetGenerator::~CreateBundleTargetGenerator() = default;
+
+void CreateBundleTargetGenerator::DoRun() {
+ target_->set_output_type(Target::CREATE_BUNDLE);
+
+ BundleData& bundle_data = target_->bundle_data();
+ if (!FillBundleDir(SourceDir(), variables::kBundleRootDir,
+ &bundle_data.root_dir()))
+ return;
+ if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleContentsDir,
+ &bundle_data.contents_dir()))
+ return;
+ if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleResourcesDir,
+ &bundle_data.resources_dir()))
+ return;
+ if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleExecutableDir,
+ &bundle_data.executable_dir()))
+ return;
+
+ if (!FillXcodeExtraAttributes())
+ return;
+
+ if (!FillProductType())
+ return;
+
+ if (!FillPartialInfoPlist())
+ return;
+
+ if (!FillXcodeTestApplicationName())
+ return;
+
+ if (!FillCodeSigningScript())
+ return;
+
+ if (!FillCodeSigningSources())
+ return;
+
+ if (!FillCodeSigningOutputs())
+ return;
+
+ if (!FillCodeSigningArgs())
+ return;
+
+ if (!FillBundleDepsFilter())
+ return;
+
+ if (!FillXcassetCompilerFlags())
+ return;
+}
+
+bool CreateBundleTargetGenerator::FillBundleDir(
+ const SourceDir& bundle_root_dir,
+ const std::string_view& name,
+ SourceDir* bundle_dir) {
+ // All bundle_foo_dir properties are optional. They are only required if they
+ // are used in an expansion. The check is performed there.
+ const Value* value = scope_->GetValue(name, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+ std::string str = value->string_value();
+ if (!str.empty() && str[str.size() - 1] != '/')
+ str.push_back('/');
+ if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), str,
+ value->origin(), err_))
+ return false;
+ if (str != bundle_root_dir.value() &&
+ !IsStringInOutputDir(bundle_root_dir, str)) {
+ *err_ = Err(
+ value->origin(), "Path is not in bundle root dir.",
+ "The given file should be in the bundle root directory or below.\n"
+ "Normally you would do \"$bundle_root_dir/foo\". I interpreted this\n"
+ "as \"" +
+ str + "\".");
+ return false;
+ }
+ *bundle_dir = SourceDir(std::move(str));
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() {
+ // Need to get a mutable value to mark all values in the scope as used. This
+ // cannot be done on a const Scope.
+ Value* value = scope_->GetMutableValue(variables::kXcodeExtraAttributes,
+ Scope::SEARCH_CURRENT, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::SCOPE, err_))
+ return false;
+
+ Scope* scope_value = value->scope_value();
+
+ Scope::KeyValueMap value_map;
+ scope_value->GetCurrentScopeValues(&value_map);
+ scope_value->MarkAllUsed();
+
+ std::map<std::string, std::string> xcode_extra_attributes;
+ for (const auto& iter : value_map) {
+ if (!iter.second.VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ xcode_extra_attributes.insert(
+ std::make_pair(std::string(iter.first), iter.second.string_value()));
+ }
+
+ target_->bundle_data().xcode_extra_attributes() =
+ std::move(xcode_extra_attributes);
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillProductType() {
+ const Value* value = scope_->GetValue(variables::kProductType, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ target_->bundle_data().product_type().assign(value->string_value());
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillPartialInfoPlist() {
+ const Value* value = scope_->GetValue(variables::kPartialInfoPlist, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ const BuildSettings* build_settings = scope_->settings()->build_settings();
+ SourceFile path = scope_->GetSourceDir().ResolveRelativeFile(
+ *value, err_, build_settings->root_path_utf8());
+
+ if (err_->has_error())
+ return false;
+
+ if (!EnsureStringIsInOutputDir(build_settings->build_dir(), path.value(),
+ value->origin(), err_))
+ return false;
+
+ target_->bundle_data().set_partial_info_plist(path);
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillXcodeTestApplicationName() {
+ const Value* value =
+ scope_->GetValue(variables::kXcodeTestApplicationName, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ target_->bundle_data().xcode_test_application_name().assign(
+ value->string_value());
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillCodeSigningScript() {
+ const Value* value = scope_->GetValue(variables::kCodeSigningScript, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
+ *value, err_, scope_->settings()->build_settings()->root_path_utf8());
+ if (err_->has_error())
+ return false;
+
+ target_->bundle_data().set_code_signing_script(script_file);
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillCodeSigningSources() {
+ const Value* value = scope_->GetValue(variables::kCodeSigningSources, true);
+ if (!value)
+ return true;
+
+ if (target_->bundle_data().code_signing_script().is_null()) {
+ *err_ = Err(
+ function_call_,
+ "No code signing script."
+ "You must define code_signing_script if you use code_signing_sources.");
+ return false;
+ }
+
+ Target::FileList script_sources;
+ if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &script_sources,
+ err_))
+ return false;
+
+ target_->bundle_data().code_signing_sources() = std::move(script_sources);
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillCodeSigningOutputs() {
+ const Value* value = scope_->GetValue(variables::kCodeSigningOutputs, true);
+ if (!value)
+ return true;
+
+ if (target_->bundle_data().code_signing_script().is_null()) {
+ *err_ = Err(
+ function_call_,
+ "No code signing script."
+ "You must define code_signing_script if you use code_signing_outputs.");
+ return false;
+ }
+
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ SubstitutionList& outputs = target_->bundle_data().code_signing_outputs();
+ if (!outputs.Parse(*value, err_))
+ return false;
+
+ if (outputs.list().empty()) {
+ *err_ =
+ Err(function_call_,
+ "Code signing script has no output."
+ "If you have no outputs, the build system can not tell when your\n"
+ "code signing script needs to be run.");
+ return false;
+ }
+
+ // Validate that outputs are in the output dir.
+ CHECK_EQ(value->list_value().size(), outputs.list().size());
+ for (size_t i = 0; i < value->list_value().size(); ++i) {
+ if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
+ value->list_value()[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillCodeSigningArgs() {
+ const Value* value = scope_->GetValue(variables::kCodeSigningArgs, true);
+ if (!value)
+ return true;
+
+ if (target_->bundle_data().code_signing_script().is_null()) {
+ *err_ = Err(
+ function_call_,
+ "No code signing script."
+ "You must define code_signing_script if you use code_signing_args.");
+ return false;
+ }
+
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ return target_->bundle_data().code_signing_args().Parse(*value, err_);
+}
+
+bool CreateBundleTargetGenerator::FillBundleDepsFilter() {
+ const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ const SourceDir& current_dir = scope_->GetSourceDir();
+ std::vector<LabelPattern>& bundle_deps_filter =
+ target_->bundle_data().bundle_deps_filter();
+ for (const auto& item : value->list_value()) {
+ bundle_deps_filter.push_back(LabelPattern::GetPattern(
+ current_dir, scope_->settings()->build_settings()->root_path_utf8(),
+ item, err_));
+ if (err_->has_error())
+ return false;
+ }
+
+ return true;
+}
+
+bool CreateBundleTargetGenerator::FillXcassetCompilerFlags() {
+ const Value* value = scope_->GetValue(variables::kXcassetCompilerFlags, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ return target_->bundle_data().xcasset_compiler_flags().Parse(*value, err_);
+}
diff --git a/gn/src/gn/create_bundle_target_generator.h b/gn/src/gn/create_bundle_target_generator.h
new file mode 100644
index 00000000000..6f13fb45b5c
--- /dev/null
+++ b/gn/src/gn/create_bundle_target_generator.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_CREATE_BUNDLE_TARGET_GENERATOR_H_
+#define TOOLS_GN_CREATE_BUNDLE_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target_generator.h"
+
+class SourceDir;
+
+// Populates a Target with the values from a create_bundle rule.
+class CreateBundleTargetGenerator : public TargetGenerator {
+ public:
+ CreateBundleTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ ~CreateBundleTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ bool FillBundleDir(const SourceDir& bundle_root_dir,
+ const std::string_view& name,
+ SourceDir* bundle_dir);
+
+ bool FillXcodeExtraAttributes();
+
+ bool FillProductType();
+ bool FillPartialInfoPlist();
+ bool FillXcodeTestApplicationName();
+
+ bool FillCodeSigningScript();
+ bool FillCodeSigningSources();
+ bool FillCodeSigningOutputs();
+ bool FillCodeSigningArgs();
+ bool FillBundleDepsFilter();
+ bool FillXcassetCompilerFlags();
+
+ DISALLOW_COPY_AND_ASSIGN(CreateBundleTargetGenerator);
+};
+
+#endif // TOOLS_GN_CREATE_BUNDLE_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/deps_iterator.cc b/gn/src/gn/deps_iterator.cc
new file mode 100644
index 00000000000..33802fa773a
--- /dev/null
+++ b/gn/src/gn/deps_iterator.cc
@@ -0,0 +1,53 @@
+// 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 "gn/deps_iterator.h"
+
+#include "gn/target.h"
+
+DepsIterator::DepsIterator() : current_index_(0) {
+ vect_stack_[0] = nullptr;
+ vect_stack_[1] = nullptr;
+ vect_stack_[2] = nullptr;
+}
+
+DepsIterator::DepsIterator(const LabelTargetVector* a,
+ const LabelTargetVector* b,
+ const LabelTargetVector* c)
+ : current_index_(0) {
+ vect_stack_[0] = a;
+ vect_stack_[1] = b;
+ vect_stack_[2] = c;
+
+ if (vect_stack_[0] && vect_stack_[0]->empty())
+ operator++();
+}
+
+// Advance to the next position. This assumes there are more vectors.
+//
+// For internal use, this function tolerates an initial index equal to the
+// length of the current vector. In this case, it will advance to the next
+// one.
+DepsIterator& DepsIterator::operator++() {
+ DCHECK(vect_stack_[0]);
+
+ current_index_++;
+ if (current_index_ >= vect_stack_[0]->size()) {
+ // Advance to next vect. Shift the elements left by one.
+ vect_stack_[0] = vect_stack_[1];
+ vect_stack_[1] = vect_stack_[2];
+ vect_stack_[2] = nullptr;
+
+ current_index_ = 0;
+
+ if (vect_stack_[0] && vect_stack_[0]->empty())
+ operator++();
+ }
+ return *this;
+}
+
+DepsIteratorRange::DepsIteratorRange(const DepsIterator& b)
+ : begin_(b), end_() {}
+
+DepsIteratorRange::~DepsIteratorRange() = default;
diff --git a/gn/src/gn/deps_iterator.h b/gn/src/gn/deps_iterator.h
new file mode 100644
index 00000000000..f8b25bb691c
--- /dev/null
+++ b/gn/src/gn/deps_iterator.h
@@ -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.
+
+#ifndef TOOLS_GN_DEPS_ITERATOR_H_
+#define TOOLS_GN_DEPS_ITERATOR_H_
+
+#include <stddef.h>
+
+#include "gn/label_ptr.h"
+
+// Provides an iterator for iterating over multiple LabelTargetVectors to
+// make it convenient to iterate over all deps of a target.
+//
+// This works by maintaining a simple stack of vectors (since we have a fixed
+// number of deps types). When the stack is empty, we've reached the end. This
+// means that the default-constructed iterator == end() for any sequence.
+class DepsIterator {
+ public:
+ // Creates an empty iterator.
+ DepsIterator();
+
+ // Iterate over the deps in the given vectors. If passing less than three,
+ // pad with nulls.
+ DepsIterator(const LabelTargetVector* a,
+ const LabelTargetVector* b,
+ const LabelTargetVector* c);
+
+ // Prefix increment operator. This assumes there are more items (i.e.
+ // *this != DepsIterator()).
+ //
+ // For internal use, this function tolerates an initial index equal to the
+ // length of the current vector. In this case, it will advance to the next
+ // one.
+ DepsIterator& operator++();
+
+ // Comparison for STL-based loops.
+ bool operator!=(const DepsIterator& other) const {
+ return current_index_ != other.current_index_ ||
+ vect_stack_[0] != other.vect_stack_[0] ||
+ vect_stack_[1] != other.vect_stack_[1] ||
+ vect_stack_[2] != other.vect_stack_[2];
+ }
+
+ // Dereference operator for STL-compatible iterators.
+ const LabelTargetPair& operator*() const {
+ DCHECK_LT(current_index_, vect_stack_[0]->size());
+ return (*vect_stack_[0])[current_index_];
+ }
+
+ private:
+ const LabelTargetVector* vect_stack_[3];
+
+ size_t current_index_;
+};
+
+// Provides a virtual container implementing begin() and end() for a
+// sequence of deps. This can then be used in range-based for loops.
+class DepsIteratorRange {
+ public:
+ explicit DepsIteratorRange(const DepsIterator& b);
+ ~DepsIteratorRange();
+
+ const DepsIterator& begin() const { return begin_; }
+ const DepsIterator& end() const { return end_; }
+
+ private:
+ DepsIterator begin_;
+ DepsIterator end_;
+};
+
+#endif // TOOLS_GN_DEPS_ITERATOR_H_
diff --git a/gn/src/gn/desc_builder.cc b/gn/src/gn/desc_builder.cc
new file mode 100644
index 00000000000..9dd9d5f3223
--- /dev/null
+++ b/gn/src/gn/desc_builder.cc
@@ -0,0 +1,891 @@
+// Copyright (c) 2016 The Chromium 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 <memory>
+#include <set>
+
+#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
+#include "gn/commands.h"
+#include "gn/config.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/desc_builder.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/runtime_deps.h"
+#include "gn/rust_variables.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/standard_out.h"
+#include "gn/substitution_writer.h"
+#include "gn/swift_variables.h"
+#include "gn/variables.h"
+
+// Example structure of Value for single target
+// (not applicable or empty fields will be ommitted depending on target type)
+//
+// target_properties = {
+// "type" : "output_type", // matching Target::GetStringForOutputType
+// "toolchain" : "toolchain_name",
+// "visibility" : [ list of visibility pattern descriptions ],
+// "test_only" : true or false,
+// "check_includes": true or false,
+// "allow_circular_includes_from": [ list of target names ],
+// "sources" : [ list of source files ],
+// "public" : either "*" or [ list of public headers],
+// "inputs" : [ list of inputs for target ],
+// "configs" : [ list of configs for this target ],
+// "public_configs" : [ list of public configs for this taget],
+// "all_dependent_configs", [ list of all dependent configs for this target],
+// "script" : "script for action targets",
+// "args" : [ argument list for action targets ],
+// "depfile : "file name for action input dependencies",
+// "outputs" : [ list of target outputs ],
+// "arflags", "asmflags", "cflags", "cflags_c",
+// "clfags_cc", "cflags_objc", "clfags_objcc" : [ list of flags],
+// "defines" : [ list of preprocessor definitions ],
+// "include_dirs" : [ list of include directories ],
+// "precompiled_header" : "name of precompiled header file",
+// "precompiled_source" : "path to precompiled source",
+// "deps : [ list of target dependencies ],
+// "libs" : [ list of libraries ],
+// "lib_dirs" : [ list of library directories ]
+// "metadata" : [ dictionary of target metadata values ]
+// "data_keys" : [ list of target data keys ]
+// "walk_keys" : [ list of target walk keys ]
+// "crate_root" : "root file of a Rust target"
+// "crate_name" : "name of a Rust target"
+// "rebase" : true or false
+// "output_conversion" : "string for output conversion"
+// "response_file_contents": [ list of response file contents entries ]
+// }
+//
+// Optionally, if "what" is specified while generating description, two other
+// properties can be requested that are not included by default. First the
+// runtime dependendencies (see "gn help runtime_deps"):
+//
+// "runtime_deps" : [list of computed runtime dependencies]
+//
+// Second, for targets whose sources map to outputs (binary targets,
+// action_foreach, and copies with non-constant outputs), the "source_outputs"
+// indicates the mapping from source to output file(s):
+//
+// "source_outputs" : {
+// "source_file x" : [ list of outputs for source file x ]
+// "source_file y" : [ list of outputs for source file y ]
+// ...
+// }
+
+namespace {
+
+std::string FormatSourceDir(const SourceDir& dir) {
+#if defined(OS_WIN)
+ // On Windows we fix up system absolute paths to look like native ones.
+ // Internally, they'll look like "/C:\foo\bar/"
+ if (dir.is_system_absolute()) {
+ std::string buf = dir.value();
+ if (buf.size() > 3 && buf[2] == ':') {
+ buf.erase(buf.begin()); // Erase beginning slash.
+ return buf;
+ }
+ }
+#endif
+ return dir.value();
+}
+
+void RecursiveCollectChildDeps(const Target* target,
+ std::set<const Target*>* result);
+
+void RecursiveCollectDeps(const Target* target,
+ std::set<const Target*>* result) {
+ if (result->find(target) != result->end())
+ return; // Already did this target.
+ result->insert(target);
+
+ RecursiveCollectChildDeps(target, result);
+}
+
+void RecursiveCollectChildDeps(const Target* target,
+ std::set<const Target*>* result) {
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
+ RecursiveCollectDeps(pair.ptr, result);
+}
+
+// Common functionality for target and config description builder
+class BaseDescBuilder {
+ public:
+ using ValuePtr = std::unique_ptr<base::Value>;
+
+ BaseDescBuilder(const std::set<std::string>& what,
+ bool all,
+ bool tree,
+ bool blame)
+ : what_(what), all_(all), tree_(tree), blame_(blame) {}
+
+ protected:
+ virtual Label GetToolchainLabel() const = 0;
+
+ bool what(const std::string& w) const {
+ return what_.empty() || what_.find(w) != what_.end();
+ }
+
+ template <typename T>
+ ValuePtr RenderValue(const std::vector<T>& vector) {
+ auto res = std::make_unique<base::ListValue>();
+ for (const auto& v : vector)
+ res->Append(RenderValue(v));
+
+ return std::move(res);
+ }
+
+ ValuePtr RenderValue(const std::string& s, bool optional = false) {
+ return (s.empty() && optional) ? std::make_unique<base::Value>()
+ : ValuePtr(new base::Value(s));
+ }
+
+ ValuePtr RenderValue(const SourceDir& d) {
+ return d.is_null() ? std::make_unique<base::Value>()
+ : ValuePtr(new base::Value(FormatSourceDir(d)));
+ }
+
+ ValuePtr RenderValue(const SourceFile& f) {
+ return f.is_null() ? std::make_unique<base::Value>()
+ : ValuePtr(new base::Value(f.value()));
+ }
+
+ ValuePtr RenderValue(const SourceFile* f) { return RenderValue(*f); }
+
+ ValuePtr RenderValue(const LibFile& lib) {
+ if (lib.is_source_file())
+ return RenderValue(lib.source_file());
+ return RenderValue(lib.value());
+ }
+
+ template <typename T>
+ base::Value ToBaseValue(const std::vector<T>& vector) {
+ base::ListValue res;
+ for (const auto& v : vector)
+ res.GetList().emplace_back(ToBaseValue(v));
+ return std::move(res);
+ }
+
+ base::Value ToBaseValue(const Scope* scope) {
+ base::DictionaryValue res;
+ Scope::KeyValueMap map;
+ scope->GetCurrentScopeValues(&map);
+ for (const auto& v : map)
+ res.SetKey(v.first, ToBaseValue(v.second));
+ return std::move(res);
+ }
+
+ base::Value ToBaseValue(const Value& val) {
+ switch (val.type()) {
+ case Value::STRING:
+ return base::Value(val.string_value());
+ case Value::INTEGER:
+ return base::Value(int(val.int_value()));
+ case Value::BOOLEAN:
+ return base::Value(val.boolean_value());
+ case Value::SCOPE:
+ return ToBaseValue(val.scope_value());
+ case Value::LIST:
+ return ToBaseValue(val.list_value());
+ case Value::NONE:
+ return base::Value();
+ }
+ NOTREACHED();
+ return base::Value();
+ }
+
+ template <class VectorType>
+ void FillInConfigVector(base::ListValue* out,
+ const VectorType& configs,
+ int indent = 0) {
+ for (const auto& config : configs) {
+ std::string name(indent * 2, ' ');
+ name.append(config.label.GetUserVisibleName(GetToolchainLabel()));
+ out->AppendString(name);
+ if (tree_)
+ FillInConfigVector(out, config.ptr->configs(), indent + 1);
+ }
+ }
+
+ void FillInPrecompiledHeader(base::DictionaryValue* out,
+ const ConfigValues& values) {
+ if (what(variables::kPrecompiledHeader) &&
+ !values.precompiled_header().empty()) {
+ out->SetWithoutPathExpansion(
+ variables::kPrecompiledHeader,
+ RenderValue(values.precompiled_header(), true));
+ }
+ if (what(variables::kPrecompiledSource) &&
+ !values.precompiled_source().is_null()) {
+ out->SetWithoutPathExpansion(variables::kPrecompiledSource,
+ RenderValue(values.precompiled_source()));
+ }
+ }
+
+ std::set<std::string> what_;
+ bool all_;
+ bool tree_;
+ bool blame_;
+};
+
+class ConfigDescBuilder : public BaseDescBuilder {
+ public:
+ ConfigDescBuilder(const Config* config, const std::set<std::string>& what)
+ : BaseDescBuilder(what, false, false, false), config_(config) {}
+
+ std::unique_ptr<base::DictionaryValue> BuildDescription() {
+ auto res = std::make_unique<base::DictionaryValue>();
+ const ConfigValues& values = config_->resolved_values();
+
+ if (what_.empty())
+ res->SetKey(
+ "toolchain",
+ base::Value(
+ config_->label().GetToolchainLabel().GetUserVisibleName(false)));
+
+ if (what(variables::kConfigs) && !config_->configs().empty()) {
+ auto configs = std::make_unique<base::ListValue>();
+ FillInConfigVector(configs.get(), config_->configs().vector());
+ res->SetWithoutPathExpansion(variables::kConfigs, std::move(configs));
+ }
+
+ if (what(variables::kVisibility)) {
+ res->SetWithoutPathExpansion(variables::kVisibility,
+ config_->visibility().AsValue());
+ }
+
+#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
+ if (what(#name)) { \
+ ValuePtr ptr = \
+ render_config_value_array<type>(values, &ConfigValues::name); \
+ if (ptr) { \
+ res->SetWithoutPathExpansion(#name, std::move(ptr)); \
+ } \
+ }
+ CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(defines, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(frameworks, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(framework_dirs, SourceDir)
+ CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir)
+ CONFIG_VALUE_ARRAY_HANDLER(inputs, SourceFile)
+ CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(lib_dirs, SourceDir)
+ CONFIG_VALUE_ARRAY_HANDLER(libs, LibFile)
+ CONFIG_VALUE_ARRAY_HANDLER(swiftflags, std::string)
+
+#undef CONFIG_VALUE_ARRAY_HANDLER
+
+ FillInPrecompiledHeader(res.get(), values);
+
+ return res;
+ }
+
+ protected:
+ Label GetToolchainLabel() const override {
+ return config_->label().GetToolchainLabel();
+ }
+
+ private:
+ template <typename T>
+ ValuePtr render_config_value_array(
+ const ConfigValues& values,
+ const std::vector<T>& (ConfigValues::*getter)() const) {
+ auto res = std::make_unique<base::ListValue>();
+
+ for (const T& cur : (values.*getter)())
+ res->Append(RenderValue(cur));
+
+ return res->empty() ? nullptr : std::move(res);
+ }
+
+ const Config* config_;
+};
+
+class TargetDescBuilder : public BaseDescBuilder {
+ public:
+ TargetDescBuilder(const Target* target,
+ const std::set<std::string>& what,
+ bool all,
+ bool tree,
+ bool blame)
+ : BaseDescBuilder(what, all, tree, blame), target_(target) {}
+
+ std::unique_ptr<base::DictionaryValue> BuildDescription() {
+ auto res = std::make_unique<base::DictionaryValue>();
+ bool is_binary_output = target_->IsBinary();
+
+ if (what_.empty()) {
+ res->SetKey(
+ "type",
+ base::Value(Target::GetStringForOutputType(target_->output_type())));
+ res->SetKey(
+ "toolchain",
+ base::Value(
+ target_->label().GetToolchainLabel().GetUserVisibleName(false)));
+ }
+
+ if (target_->source_types_used().RustSourceUsed()) {
+ if (what(variables::kRustCrateRoot)) {
+ res->SetWithoutPathExpansion(
+ variables::kRustCrateRoot,
+ RenderValue(target_->rust_values().crate_root()));
+ }
+ if (what(variables::kRustCrateName)) {
+ res->SetKey(variables::kRustCrateName,
+ base::Value(target_->rust_values().crate_name()));
+ }
+ }
+
+ if (target_->source_types_used().SwiftSourceUsed()) {
+ if (what(variables::kSwiftBridgeHeader)) {
+ res->SetWithoutPathExpansion(
+ variables::kSwiftBridgeHeader,
+ RenderValue(target_->swift_values().bridge_header()));
+ }
+ if (what(variables::kSwiftModuleName)) {
+ res->SetKey(variables::kSwiftModuleName,
+ base::Value(target_->swift_values().module_name()));
+ }
+ }
+
+ // General target meta variables.
+
+ if (what(variables::kMetadata)) {
+ base::DictionaryValue metadata;
+ for (const auto& v : target_->metadata().contents())
+ metadata.SetKey(v.first, ToBaseValue(v.second));
+ res->SetKey(variables::kMetadata, std::move(metadata));
+ }
+
+ if (what(variables::kVisibility))
+ res->SetWithoutPathExpansion(variables::kVisibility,
+ target_->visibility().AsValue());
+
+ if (what(variables::kTestonly))
+ res->SetKey(variables::kTestonly, base::Value(target_->testonly()));
+
+ if (is_binary_output) {
+ if (what(variables::kCheckIncludes))
+ res->SetKey(variables::kCheckIncludes,
+ base::Value(target_->check_includes()));
+
+ if (what(variables::kAllowCircularIncludesFrom)) {
+ auto labels = std::make_unique<base::ListValue>();
+ for (const auto& cur : target_->allow_circular_includes_from())
+ labels->AppendString(cur.GetUserVisibleName(GetToolchainLabel()));
+
+ res->SetWithoutPathExpansion(variables::kAllowCircularIncludesFrom,
+ std::move(labels));
+ }
+ }
+
+ if (what(variables::kSources) && !target_->sources().empty())
+ res->SetWithoutPathExpansion(variables::kSources,
+ RenderValue(target_->sources()));
+
+ if (what(variables::kOutputName) && !target_->output_name().empty())
+ res->SetKey(variables::kOutputName, base::Value(target_->output_name()));
+
+ if (what(variables::kOutputDir) && !target_->output_dir().is_null())
+ res->SetWithoutPathExpansion(variables::kOutputDir,
+ RenderValue(target_->output_dir()));
+
+ if (what(variables::kOutputExtension) && target_->output_extension_set())
+ res->SetKey(variables::kOutputExtension,
+ base::Value(target_->output_extension()));
+
+ if (what(variables::kPublic)) {
+ if (target_->all_headers_public())
+ res->SetKey(variables::kPublic, base::Value("*"));
+ else
+ res->SetWithoutPathExpansion(variables::kPublic,
+ RenderValue(target_->public_headers()));
+ }
+
+ if (what(variables::kInputs)) {
+ std::vector<const SourceFile*> inputs;
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ for (const auto& input : iter.cur().inputs())
+ inputs.push_back(&input);
+ }
+ if (!inputs.empty())
+ res->SetWithoutPathExpansion(variables::kInputs, RenderValue(inputs));
+ }
+
+ if (is_binary_output && what(variables::kConfigs) &&
+ !target_->configs().empty()) {
+ auto configs = std::make_unique<base::ListValue>();
+ FillInConfigVector(configs.get(), target_->configs().vector());
+ res->SetWithoutPathExpansion(variables::kConfigs, std::move(configs));
+ }
+
+ if (what(variables::kPublicConfigs) && !target_->public_configs().empty()) {
+ auto configs = std::make_unique<base::ListValue>();
+ FillInConfigVector(configs.get(), target_->public_configs());
+ res->SetWithoutPathExpansion(variables::kPublicConfigs,
+ std::move(configs));
+ }
+
+ if (what(variables::kAllDependentConfigs) &&
+ !target_->all_dependent_configs().empty()) {
+ auto configs = std::make_unique<base::ListValue>();
+ FillInConfigVector(configs.get(), target_->all_dependent_configs());
+ res->SetWithoutPathExpansion(variables::kAllDependentConfigs,
+ std::move(configs));
+ }
+
+ // Action
+ if (target_->output_type() == Target::ACTION ||
+ target_->output_type() == Target::ACTION_FOREACH) {
+ if (what(variables::kScript))
+ res->SetKey(variables::kScript,
+ base::Value(target_->action_values().script().value()));
+
+ if (what(variables::kArgs)) {
+ auto args = std::make_unique<base::ListValue>();
+ for (const auto& elem : target_->action_values().args().list())
+ args->AppendString(elem.AsString());
+
+ res->SetWithoutPathExpansion(variables::kArgs, std::move(args));
+ }
+ if (what(variables::kResponseFileContents) &&
+ !target_->action_values().rsp_file_contents().list().empty()) {
+ auto rsp_file_contents = std::make_unique<base::ListValue>();
+ for (const auto& elem :
+ target_->action_values().rsp_file_contents().list())
+ rsp_file_contents->AppendString(elem.AsString());
+
+ res->SetWithoutPathExpansion(variables::kResponseFileContents,
+ std::move(rsp_file_contents));
+ }
+ if (what(variables::kDepfile) &&
+ !target_->action_values().depfile().empty()) {
+ res->SetKey(variables::kDepfile,
+ base::Value(target_->action_values().depfile().AsString()));
+ }
+ }
+
+ if (target_->output_type() != Target::SOURCE_SET &&
+ target_->output_type() != Target::GROUP &&
+ target_->output_type() != Target::BUNDLE_DATA) {
+ if (what(variables::kOutputs))
+ FillInOutputs(res.get());
+ }
+
+ // Source outputs are only included when specifically asked for it
+ if (what_.find("source_outputs") != what_.end())
+ FillInSourceOutputs(res.get());
+
+ if (target_->output_type() == Target::CREATE_BUNDLE && what("bundle_data"))
+ FillInBundle(res.get());
+
+ if (is_binary_output) {
+#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
+ if (what(#name)) { \
+ ValuePtr ptr = RenderConfigValues<type>(&ConfigValues::name); \
+ if (ptr) { \
+ res->SetWithoutPathExpansion(#name, std::move(ptr)); \
+ } \
+ }
+ CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(rustflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(defines, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir)
+ CONFIG_VALUE_ARRAY_HANDLER(inputs, SourceFile)
+ CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string)
+ CONFIG_VALUE_ARRAY_HANDLER(swiftflags, std::string)
+#undef CONFIG_VALUE_ARRAY_HANDLER
+
+ // Libs and lib_dirs are handled specially below.
+
+ if (what(variables::kExterns)) {
+ base::DictionaryValue externs;
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ const ConfigValues& cur = iter.cur();
+ for (const auto& e : cur.externs()) {
+ externs.SetKey(e.first, base::Value(e.second.value()));
+ }
+ }
+ res->SetKey(variables::kExterns, std::move(externs));
+ }
+
+ FillInPrecompiledHeader(res.get(), target_->config_values());
+ }
+
+ // GeneratedFile vars.
+ if (target_->output_type() == Target::GENERATED_FILE) {
+ if (what(variables::kWriteOutputConversion)) {
+ res->SetKey(variables::kWriteOutputConversion,
+ ToBaseValue(target_->output_conversion()));
+ }
+ if (what(variables::kDataKeys)) {
+ base::ListValue keys;
+ for (const auto& k : target_->data_keys())
+ keys.GetList().push_back(base::Value(k));
+ res->SetKey(variables::kDataKeys, std::move(keys));
+ }
+ if (what(variables::kRebase)) {
+ res->SetWithoutPathExpansion(variables::kRebase,
+ RenderValue(target_->rebase()));
+ }
+ if (what(variables::kWalkKeys)) {
+ base::ListValue keys;
+ for (const auto& k : target_->walk_keys())
+ keys.GetList().push_back(base::Value(k));
+ res->SetKey(variables::kWalkKeys, std::move(keys));
+ }
+ }
+
+ if (what(variables::kDeps))
+ res->SetWithoutPathExpansion(variables::kDeps, RenderDeps());
+
+ // Runtime deps are special, print only when explicitly asked for and not in
+ // overview mode.
+ if (what_.find("runtime_deps") != what_.end())
+ res->SetWithoutPathExpansion("runtime_deps", RenderRuntimeDeps());
+
+ // libs and lib_dirs are special in that they're inherited. We don't
+ // currently implement a blame feature for this since the bottom-up
+ // inheritance makes this difficult.
+
+ // Libs can be part of any target and get recursively pushed up the chain,
+ // so display them regardless of target type.
+ if (what(variables::kLibs)) {
+ const OrderedSet<LibFile>& all_libs = target_->all_libs();
+ if (!all_libs.empty()) {
+ auto libs = std::make_unique<base::ListValue>();
+ for (size_t i = 0; i < all_libs.size(); i++)
+ libs->AppendString(all_libs[i].value());
+ res->SetWithoutPathExpansion(variables::kLibs, std::move(libs));
+ }
+ }
+
+ if (what(variables::kLibDirs)) {
+ const OrderedSet<SourceDir>& all_lib_dirs = target_->all_lib_dirs();
+ if (!all_lib_dirs.empty()) {
+ auto lib_dirs = std::make_unique<base::ListValue>();
+ for (size_t i = 0; i < all_lib_dirs.size(); i++)
+ lib_dirs->AppendString(FormatSourceDir(all_lib_dirs[i]));
+ res->SetWithoutPathExpansion(variables::kLibDirs, std::move(lib_dirs));
+ }
+ }
+
+ if (what(variables::kFrameworks)) {
+ const auto& all_frameworks = target_->all_frameworks();
+ if (!all_frameworks.empty()) {
+ auto frameworks = std::make_unique<base::ListValue>();
+ for (size_t i = 0; i < all_frameworks.size(); i++)
+ frameworks->AppendString(all_frameworks[i]);
+ res->SetWithoutPathExpansion(variables::kFrameworks,
+ std::move(frameworks));
+ }
+ }
+ if (what(variables::kWeakFrameworks)) {
+ const auto& weak_frameworks = target_->all_weak_frameworks();
+ if (!weak_frameworks.empty()) {
+ auto frameworks = std::make_unique<base::ListValue>();
+ for (size_t i = 0; i < weak_frameworks.size(); i++)
+ frameworks->AppendString(weak_frameworks[i]);
+ res->SetWithoutPathExpansion(variables::kWeakFrameworks,
+ std::move(frameworks));
+ }
+ }
+
+ if (what(variables::kFrameworkDirs)) {
+ const auto& all_framework_dirs = target_->all_framework_dirs();
+ if (!all_framework_dirs.empty()) {
+ auto framework_dirs = std::make_unique<base::ListValue>();
+ for (size_t i = 0; i < all_framework_dirs.size(); i++)
+ framework_dirs->AppendString(all_framework_dirs[i].value());
+ res->SetWithoutPathExpansion(variables::kFrameworkDirs,
+ std::move(framework_dirs));
+ }
+ }
+
+ return res;
+ }
+
+ private:
+ // Prints dependencies of the given target (not the target itself). If the
+ // set is non-null, new targets encountered will be added to the set, and if
+ // a dependency is in the set already, it will not be recused into. When the
+ // set is null, all dependencies will be printed.
+ void RecursivePrintDeps(base::ListValue* out,
+ const Target* target,
+ std::set<const Target*>* seen_targets,
+ int indent_level) {
+ // Combine all deps into one sorted list.
+ std::vector<LabelTargetPair> sorted_deps;
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
+ sorted_deps.push_back(pair);
+ std::sort(sorted_deps.begin(), sorted_deps.end());
+
+ std::string indent(indent_level * 2, ' ');
+
+ for (const auto& pair : sorted_deps) {
+ const Target* cur_dep = pair.ptr;
+ std::string str =
+ indent + cur_dep->label().GetUserVisibleName(GetToolchainLabel());
+
+ bool print_children = true;
+ if (seen_targets) {
+ if (seen_targets->find(cur_dep) == seen_targets->end()) {
+ // New target, mark it visited.
+ seen_targets->insert(cur_dep);
+ } else {
+ // Already seen.
+ print_children = false;
+ // Only print "..." if something is actually elided, which means that
+ // the current target has children.
+ if (!cur_dep->public_deps().empty() ||
+ !cur_dep->private_deps().empty() || !cur_dep->data_deps().empty())
+ str += "...";
+ }
+ }
+
+ out->AppendString(str);
+
+ if (print_children)
+ RecursivePrintDeps(out, cur_dep, seen_targets, indent_level + 1);
+ }
+ }
+
+ ValuePtr RenderDeps() {
+ auto res = std::make_unique<base::ListValue>();
+
+ // Tree mode is separate.
+ if (tree_) {
+ if (all_) {
+ // Show all tree deps with no eliding.
+ RecursivePrintDeps(res.get(), target_, nullptr, 0);
+ } else {
+ // Don't recurse into duplicates.
+ std::set<const Target*> seen_targets;
+ RecursivePrintDeps(res.get(), target_, &seen_targets, 0);
+ }
+ } else { // not tree
+
+ // Collect the deps to display.
+ if (all_) {
+ // Show all dependencies.
+ std::set<const Target*> all_deps;
+ RecursiveCollectChildDeps(target_, &all_deps);
+ commands::FilterAndPrintTargetSet(all_deps, res.get());
+ } else {
+ // Show direct dependencies only.
+ std::vector<const Target*> deps;
+ for (const auto& pair : target_->GetDeps(Target::DEPS_ALL))
+ deps.push_back(pair.ptr);
+ std::sort(deps.begin(), deps.end());
+ commands::FilterAndPrintTargets(&deps, res.get());
+ }
+ }
+
+ return std::move(res);
+ }
+
+ ValuePtr RenderRuntimeDeps() {
+ auto res = std::make_unique<base::ListValue>();
+
+ const Target* previous_from = NULL;
+ for (const auto& pair : ComputeRuntimeDeps(target_)) {
+ std::string str;
+ if (blame_) {
+ // Generally a target's runtime deps will be listed sequentially, so
+ // group them and don't duplicate the "from" label for two in a row.
+ if (previous_from == pair.second) {
+ str = " ";
+ } else {
+ previous_from = pair.second;
+ res->AppendString(
+ str + "From " +
+ pair.second->label().GetUserVisibleName(GetToolchainLabel()));
+ str = " ";
+ }
+ }
+
+ res->AppendString(str + pair.first.value());
+ }
+
+ return std::move(res);
+ }
+
+ void FillInSourceOutputs(base::DictionaryValue* res) {
+ // Only include "source outputs" if there are sources that map to outputs.
+ // Things like actions have constant per-target outputs that don't depend on
+ // the list of sources. These don't need source outputs.
+ if (target_->output_type() != Target::ACTION_FOREACH &&
+ target_->output_type() != Target::COPY_FILES && !target_->IsBinary())
+ return; // Everything else has constant outputs.
+
+ // "copy" targets may have patterns or not. If there's only one file, the
+ // user can specify a constant output name.
+ if (target_->output_type() == Target::COPY_FILES &&
+ target_->action_values().outputs().required_types().empty())
+ return; // Constant output.
+
+ auto dict = std::make_unique<base::DictionaryValue>();
+ for (const auto& source : target_->sources()) {
+ std::vector<OutputFile> outputs;
+ const char* tool_name = Tool::kToolNone;
+ if (target_->GetOutputFilesForSource(source, &tool_name, &outputs)) {
+ auto list = std::make_unique<base::ListValue>();
+ for (const auto& output : outputs)
+ list->AppendString(output.value());
+
+ dict->SetWithoutPathExpansion(source.value(), std::move(list));
+ }
+ }
+ res->SetWithoutPathExpansion("source_outputs", std::move(dict));
+ }
+
+ void FillInBundle(base::DictionaryValue* res) {
+ auto data = std::make_unique<base::DictionaryValue>();
+ const BundleData& bundle_data = target_->bundle_data();
+ const Settings* settings = target_->settings();
+ BundleData::SourceFiles sources;
+ bundle_data.GetSourceFiles(&sources);
+ data->SetWithoutPathExpansion("source_files", RenderValue(sources));
+ data->SetKey(
+ "root_dir_output",
+ base::Value(bundle_data.GetBundleRootDirOutput(settings).value()));
+ data->SetWithoutPathExpansion("root_dir",
+ RenderValue(bundle_data.root_dir()));
+ data->SetWithoutPathExpansion("resources_dir",
+ RenderValue(bundle_data.resources_dir()));
+ data->SetWithoutPathExpansion("executable_dir",
+ RenderValue(bundle_data.executable_dir()));
+ data->SetKey("product_type", base::Value(bundle_data.product_type()));
+ data->SetWithoutPathExpansion(
+ "partial_info_plist", RenderValue(bundle_data.partial_info_plist()));
+
+ auto deps = std::make_unique<base::ListValue>();
+ for (const auto* dep : bundle_data.bundle_deps())
+ deps->AppendString(dep->label().GetUserVisibleName(GetToolchainLabel()));
+
+ data->SetWithoutPathExpansion("deps", std::move(deps));
+ res->SetWithoutPathExpansion("bundle_data", std::move(data));
+ }
+
+ void FillInOutputs(base::DictionaryValue* res) {
+ std::vector<SourceFile> output_files;
+ Err err;
+ if (!target_->GetOutputsAsSourceFiles(LocationRange(), true, &output_files,
+ &err)) {
+ err.PrintToStdout();
+ return;
+ }
+ res->SetWithoutPathExpansion(variables::kOutputs,
+ RenderValue(output_files));
+
+ // Write some extra data for certain output types.
+ if (target_->output_type() == Target::ACTION_FOREACH ||
+ target_->output_type() == Target::COPY_FILES) {
+ const SubstitutionList& outputs = target_->action_values().outputs();
+ if (!outputs.required_types().empty()) {
+ // Write out the output patterns if there are any.
+ auto patterns = std::make_unique<base::ListValue>();
+ for (const auto& elem : outputs.list())
+ patterns->AppendString(elem.AsString());
+
+ res->SetWithoutPathExpansion("output_patterns", std::move(patterns));
+ }
+ }
+ }
+
+ // Writes a given config value type to the string, optionally with
+ // attribution.
+ // This should match RecursiveTargetConfigToStream in the order it traverses.
+ template <class T>
+ ValuePtr RenderConfigValues(const std::vector<T>& (ConfigValues::*getter)()
+ const) {
+ auto res = std::make_unique<base::ListValue>();
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ const std::vector<T>& vec = (iter.cur().*getter)();
+
+ if (vec.empty())
+ continue;
+
+ if (blame_) {
+ const Config* config = iter.GetCurrentConfig();
+ if (config) {
+ // Source of this value is a config.
+ std::string from =
+ "From " + config->label().GetUserVisibleName(false);
+ res->AppendString(from);
+ if (iter.origin()) {
+ Location location = iter.origin()->GetRange().begin();
+ from = " (Added by " + location.file()->name().value() + ":" +
+ base::IntToString(location.line_number()) + ")";
+ res->AppendString(from);
+ }
+ } else {
+ // Source of this value is the target itself.
+ std::string from =
+ "From " + target_->label().GetUserVisibleName(false);
+ res->AppendString(from);
+ }
+ }
+
+ for (const T& val : vec) {
+ ValuePtr rendered = RenderValue(val);
+ std::string str;
+ // Indent string values in blame mode
+ if (blame_ && rendered->GetAsString(&str)) {
+ str = " " + str;
+ rendered = std::make_unique<base::Value>(str);
+ }
+ res->Append(std::move(rendered));
+ }
+ }
+ return res->empty() ? nullptr : std::move(res);
+ }
+
+ Label GetToolchainLabel() const override {
+ return target_->label().GetToolchainLabel();
+ }
+
+ const Target* target_;
+};
+
+} // namespace
+
+std::unique_ptr<base::DictionaryValue> DescBuilder::DescriptionForTarget(
+ const Target* target,
+ const std::string& what,
+ bool all,
+ bool tree,
+ bool blame) {
+ std::set<std::string> w;
+ if (!what.empty())
+ w.insert(what);
+ TargetDescBuilder b(target, w, all, tree, blame);
+ return b.BuildDescription();
+}
+
+std::unique_ptr<base::DictionaryValue> DescBuilder::DescriptionForConfig(
+ const Config* config,
+ const std::string& what) {
+ std::set<std::string> w;
+ if (!what.empty())
+ w.insert(what);
+ ConfigDescBuilder b(config, w);
+ return b.BuildDescription();
+}
diff --git a/gn/src/gn/desc_builder.h b/gn/src/gn/desc_builder.h
new file mode 100644
index 00000000000..e0e95a32172
--- /dev/null
+++ b/gn/src/gn/desc_builder.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 The Chromium 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 TOOLS_GN_DESC_BUILDER_H_
+#define TOOLS_GN_DESC_BUILDER_H_
+
+#include "base/values.h"
+#include "gn/target.h"
+
+class DescBuilder {
+ public:
+ // Creates Dictionary representation for given target
+ static std::unique_ptr<base::DictionaryValue> DescriptionForTarget(
+ const Target* target,
+ const std::string& what,
+ bool all,
+ bool tree,
+ bool blame);
+
+ // Creates Dictionary representation for given config
+ static std::unique_ptr<base::DictionaryValue> DescriptionForConfig(
+ const Config* config,
+ const std::string& what);
+};
+
+#endif
diff --git a/gn/src/gn/eclipse_writer.cc b/gn/src/gn/eclipse_writer.cc
new file mode 100644
index 00000000000..0067f6d4123
--- /dev/null
+++ b/gn/src/gn/eclipse_writer.cc
@@ -0,0 +1,171 @@
+// Copyright 2016 The Chromium 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 "gn/eclipse_writer.h"
+
+#include <fstream>
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "gn/builder.h"
+#include "gn/config_values_extractors.h"
+#include "gn/filesystem_utils.h"
+#include "gn/loader.h"
+#include "gn/xml_element_writer.h"
+
+namespace {
+
+// Escapes |unescaped| for use in XML element content.
+std::string EscapeForXML(const std::string& unescaped) {
+ std::string result;
+ result.reserve(unescaped.length());
+ for (const char c : unescaped) {
+ if (c == '<')
+ result += "&lt;";
+ else if (c == '>')
+ result += "&gt;";
+ else if (c == '&')
+ result += "&amp;";
+ else
+ result.push_back(c);
+ }
+ return result;
+}
+
+} // namespace
+
+EclipseWriter::EclipseWriter(const BuildSettings* build_settings,
+ const Builder& builder,
+ std::ostream& out)
+ : build_settings_(build_settings), builder_(builder), out_(out) {
+ languages_.push_back("C++ Source File");
+ languages_.push_back("C Source File");
+ languages_.push_back("Assembly Source File");
+ languages_.push_back("GNU C++");
+ languages_.push_back("GNU C");
+ languages_.push_back("Assembly");
+}
+
+EclipseWriter::~EclipseWriter() = default;
+
+// static
+bool EclipseWriter::RunAndWriteFile(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err) {
+ base::FilePath file = build_settings->GetFullPath(build_settings->build_dir())
+ .AppendASCII("eclipse-cdt-settings.xml");
+ std::ofstream file_out;
+ file_out.open(FilePathToUTF8(file).c_str(),
+ std::ios_base::out | std::ios_base::binary);
+ if (file_out.fail()) {
+ *err =
+ Err(Location(), "Couldn't open eclipse-cdt-settings.xml for writing");
+ return false;
+ }
+
+ EclipseWriter gen(build_settings, builder, file_out);
+ gen.Run();
+ return true;
+}
+
+void EclipseWriter::Run() {
+ GetAllIncludeDirs();
+ GetAllDefines();
+ WriteCDTSettings();
+}
+
+void EclipseWriter::GetAllIncludeDirs() {
+ std::vector<const Target*> targets = builder_.GetAllResolvedTargets();
+ for (const Target* target : targets) {
+ if (!UsesDefaultToolchain(target))
+ continue;
+
+ for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
+ for (const SourceDir& include_dir : it.cur().include_dirs()) {
+ include_dirs_.insert(
+ FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
+ }
+ }
+ }
+}
+
+void EclipseWriter::GetAllDefines() {
+ std::vector<const Target*> targets = builder_.GetAllResolvedTargets();
+ for (const Target* target : targets) {
+ if (!UsesDefaultToolchain(target))
+ continue;
+
+ for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
+ for (const std::string& define : it.cur().defines()) {
+ size_t equal_pos = define.find('=');
+ std::string define_key;
+ std::string define_value;
+ if (equal_pos == std::string::npos) {
+ define_key = define;
+ } else {
+ define_key = define.substr(0, equal_pos);
+ define_value = define.substr(equal_pos + 1);
+ }
+ defines_[define_key] = define_value;
+ }
+ }
+ }
+}
+
+bool EclipseWriter::UsesDefaultToolchain(const Target* target) const {
+ return target->toolchain()->label() ==
+ builder_.loader()->GetDefaultToolchain();
+}
+
+void EclipseWriter::WriteCDTSettings() {
+ out_ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
+ XmlElementWriter cdt_properties_element(out_, "cdtprojectproperties",
+ XmlAttributes());
+
+ {
+ const char* kIncludesSectionName =
+ "org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths";
+ std::unique_ptr<XmlElementWriter> section_element =
+ cdt_properties_element.SubElement(
+ "section", XmlAttributes("name", kIncludesSectionName));
+
+ section_element->SubElement(
+ "language", XmlAttributes("name", "holder for library settings"));
+
+ for (const std::string& language : languages_) {
+ std::unique_ptr<XmlElementWriter> language_element =
+ section_element->SubElement("language",
+ XmlAttributes("name", language));
+ for (const std::string& include_dir : include_dirs_) {
+ language_element
+ ->SubElement("includepath",
+ XmlAttributes("workspace_path", "false"))
+ ->Text(EscapeForXML(include_dir));
+ }
+ }
+ }
+
+ {
+ const char* kMacrosSectionName =
+ "org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros";
+ std::unique_ptr<XmlElementWriter> section_element =
+ cdt_properties_element.SubElement(
+ "section", XmlAttributes("name", kMacrosSectionName));
+
+ section_element->SubElement(
+ "language", XmlAttributes("name", "holder for library settings"));
+
+ for (const std::string& language : languages_) {
+ std::unique_ptr<XmlElementWriter> language_element =
+ section_element->SubElement("language",
+ XmlAttributes("name", language));
+ for (const auto& key_val : defines_) {
+ std::unique_ptr<XmlElementWriter> macro_element =
+ language_element->SubElement("macro");
+ macro_element->SubElement("name")->Text(EscapeForXML(key_val.first));
+ macro_element->SubElement("value")->Text(EscapeForXML(key_val.second));
+ }
+ }
+ }
+}
diff --git a/gn/tools/gn/eclipse_writer.h b/gn/src/gn/eclipse_writer.h
index e15254fbadb..e15254fbadb 100644
--- a/gn/tools/gn/eclipse_writer.h
+++ b/gn/src/gn/eclipse_writer.h
diff --git a/gn/src/gn/err.cc b/gn/src/gn/err.cc
new file mode 100644
index 00000000000..301ee8d6ed5
--- /dev/null
+++ b/gn/src/gn/err.cc
@@ -0,0 +1,200 @@
+// 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 "gn/err.h"
+
+#include <stddef.h>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/standard_out.h"
+#include "gn/tokenizer.h"
+#include "gn/value.h"
+
+namespace {
+
+std::string GetNthLine(const std::string_view& data, int n) {
+ size_t line_off = Tokenizer::ByteOffsetOfNthLine(data, n);
+ size_t end = line_off + 1;
+ while (end < data.size() && !Tokenizer::IsNewline(data, end))
+ end++;
+ return std::string(data.substr(line_off, end - line_off));
+}
+
+void FillRangeOnLine(const LocationRange& range,
+ int line_number,
+ std::string* line) {
+ // Only bother if the range's begin or end overlaps the line. If the entire
+ // line is highlighted as a result of this range, it's not very helpful.
+ if (range.begin().line_number() != line_number &&
+ range.end().line_number() != line_number)
+ return;
+
+ // Watch out, the char offsets in the location are 1-based, so we have to
+ // subtract 1.
+ int begin_char;
+ if (range.begin().line_number() < line_number)
+ begin_char = 0;
+ else
+ begin_char = range.begin().column_number() - 1;
+
+ int end_char;
+ if (range.end().line_number() > line_number)
+ end_char = static_cast<int>(line->size()); // Ending is non-inclusive.
+ else
+ end_char = range.end().column_number() - 1;
+
+ CHECK(end_char >= begin_char);
+ CHECK(begin_char >= 0 && begin_char <= static_cast<int>(line->size()));
+ CHECK(end_char >= 0 && end_char <= static_cast<int>(line->size()));
+ for (int i = begin_char; i < end_char; i++)
+ line->at(i) = '-';
+}
+
+// The line length is used to clip the maximum length of the markers we'll
+// make if the error spans more than one line (like unterminated literals).
+void OutputHighlighedPosition(const Location& location,
+ const Err::RangeList& ranges,
+ size_t line_length) {
+ // Make a buffer of the line in spaces.
+ std::string highlight;
+ highlight.resize(line_length);
+ for (size_t i = 0; i < line_length; i++)
+ highlight[i] = ' ';
+
+ // Highlight all the ranges on the line.
+ for (const auto& range : ranges)
+ FillRangeOnLine(range, location.line_number(), &highlight);
+
+ // Allow the marker to be one past the end of the line for marking the end.
+ highlight.push_back(' ');
+ CHECK(location.column_number() - 1 >= 0 &&
+ location.column_number() - 1 < static_cast<int>(highlight.size()));
+ highlight[location.column_number() - 1] = '^';
+
+ // Trim unused spaces from end of line.
+ while (!highlight.empty() && highlight[highlight.size() - 1] == ' ')
+ highlight.resize(highlight.size() - 1);
+
+ highlight += "\n";
+ OutputString(highlight, DECORATION_BLUE);
+}
+
+} // namespace
+
+Err::Err() : has_error_(false) {}
+
+Err::Err(const Location& location,
+ const std::string& msg,
+ const std::string& help)
+ : has_error_(true), location_(location), message_(msg), help_text_(help) {}
+
+Err::Err(const LocationRange& range,
+ const std::string& msg,
+ const std::string& help)
+ : has_error_(true),
+ location_(range.begin()),
+ message_(msg),
+ help_text_(help) {
+ ranges_.push_back(range);
+}
+
+Err::Err(const Token& token, const std::string& msg, const std::string& help)
+ : has_error_(true),
+ location_(token.location()),
+ message_(msg),
+ help_text_(help) {
+ ranges_.push_back(token.range());
+}
+
+Err::Err(const ParseNode* node,
+ const std::string& msg,
+ const std::string& help_text)
+ : has_error_(true), message_(msg), help_text_(help_text) {
+ // Node will be null in certain tests.
+ if (node) {
+ LocationRange range = node->GetRange();
+ location_ = range.begin();
+ ranges_.push_back(range);
+ }
+}
+
+Err::Err(const Value& value,
+ const std::string msg,
+ const std::string& help_text)
+ : has_error_(true), message_(msg), help_text_(help_text) {
+ if (value.origin()) {
+ LocationRange range = value.origin()->GetRange();
+ location_ = range.begin();
+ ranges_.push_back(range);
+ }
+}
+
+Err::Err(const Err& other) = default;
+
+Err::~Err() = default;
+
+void Err::PrintToStdout() const {
+ InternalPrintToStdout(false, true);
+}
+
+void Err::PrintNonfatalToStdout() const {
+ InternalPrintToStdout(false, false);
+}
+
+void Err::AppendSubErr(const Err& err) {
+ sub_errs_.push_back(err);
+}
+
+void Err::InternalPrintToStdout(bool is_sub_err, bool is_fatal) const {
+ DCHECK(has_error_);
+
+ if (!is_sub_err) {
+ if (is_fatal)
+ OutputString("ERROR ", DECORATION_RED);
+ else
+ OutputString("WARNING ", DECORATION_RED);
+ }
+
+ // File name and location.
+ const InputFile* input_file = location_.file();
+ std::string loc_str = location_.Describe(true);
+ if (!loc_str.empty()) {
+ if (is_sub_err)
+ loc_str.insert(0, "See ");
+ else
+ loc_str.insert(0, "at ");
+ if (!toolchain_label_.is_null())
+ loc_str += " ";
+ }
+ std::string toolchain_str;
+ if (!toolchain_label_.is_null()) {
+ toolchain_str += "(" + toolchain_label_.GetUserVisibleName(false) + ")";
+ }
+ std::string colon;
+ if (!loc_str.empty() || !toolchain_str.empty())
+ colon = ": ";
+ OutputString(loc_str + toolchain_str + colon + message_ + "\n");
+
+ // Quoted line.
+ if (input_file) {
+ std::string line =
+ GetNthLine(input_file->contents(), location_.line_number());
+ if (!base::ContainsOnlyChars(line, base::kWhitespaceASCII)) {
+ OutputString(line + "\n", DECORATION_DIM);
+ OutputHighlighedPosition(location_, ranges_, line.size());
+ }
+ }
+
+ // Optional help text.
+ if (!help_text_.empty())
+ OutputString(help_text_ + "\n");
+
+ // Sub errors.
+ for (const auto& sub_err : sub_errs_)
+ sub_err.InternalPrintToStdout(true, is_fatal);
+}
diff --git a/gn/src/gn/err.h b/gn/src/gn/err.h
new file mode 100644
index 00000000000..6bd2f02b299
--- /dev/null
+++ b/gn/src/gn/err.h
@@ -0,0 +1,105 @@
+// 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 TOOLS_GN_ERR_H_
+#define TOOLS_GN_ERR_H_
+
+#include <string>
+#include <vector>
+
+#include "gn/label.h"
+#include "gn/location.h"
+#include "gn/token.h"
+
+class ParseNode;
+class Value;
+
+// Result of doing some operation. Check has_error() to see if an error
+// occurred.
+//
+// An error has a location and a message. Below that, is some optional help
+// text to go with the annotation of the location.
+//
+// An error can also have sub-errors which are additionally printed out
+// below. They can provide additional context.
+class Err {
+ public:
+ using RangeList = std::vector<LocationRange>;
+
+ // Indicates no error.
+ Err();
+
+ // Error at a single point.
+ Err(const Location& location,
+ const std::string& msg,
+ const std::string& help = std::string());
+
+ // Error at a given range.
+ Err(const LocationRange& range,
+ const std::string& msg,
+ const std::string& help = std::string());
+
+ // Error at a given token.
+ Err(const Token& token,
+ const std::string& msg,
+ const std::string& help_text = std::string());
+
+ // Error at a given node.
+ Err(const ParseNode* node,
+ const std::string& msg,
+ const std::string& help_text = std::string());
+
+ // Error at a given value.
+ Err(const Value& value,
+ const std::string msg,
+ const std::string& help_text = std::string());
+
+ Err(const Err& other);
+
+ ~Err();
+
+ bool has_error() const { return has_error_; }
+ const Location& location() const { return location_; }
+ const std::string& message() const { return message_; }
+ const std::string& help_text() const { return help_text_; }
+
+ void AppendRange(const LocationRange& range) { ranges_.push_back(range); }
+ const RangeList& ranges() const { return ranges_; }
+
+ void set_toolchain_label(const Label& toolchain_label) {
+ toolchain_label_ = toolchain_label;
+ }
+
+ void AppendSubErr(const Err& err);
+
+ void PrintToStdout() const;
+
+ // Prints to standard out but uses a "WARNING" messaging instead of the
+ // normal "ERROR" messaging. This is a property of the printing system rather
+ // than of the Err class because there is no expectation that code calling a
+ // function that take an Err check that the error is nonfatal and continue.
+ // Generally all Err objects with has_error() set are fatal.
+ //
+ // In some very specific cases code will detect a condition and print a
+ // nonfatal error to the screen instead of returning it. In these cases, that
+ // code can decide at printing time whether it will continue (and use this
+ // method) or not (and use PrintToStdout()).
+ void PrintNonfatalToStdout() const;
+
+ private:
+ void InternalPrintToStdout(bool is_sub_err, bool is_fatal) const;
+
+ bool has_error_;
+ Location location_;
+ Label toolchain_label_;
+
+ std::vector<LocationRange> ranges_;
+
+ std::string message_;
+ std::string help_text_;
+
+ std::vector<Err> sub_errs_;
+};
+
+#endif // TOOLS_GN_ERR_H_
diff --git a/gn/src/gn/escape.cc b/gn/src/gn/escape.cc
new file mode 100644
index 00000000000..3c8dc3f498a
--- /dev/null
+++ b/gn/src/gn/escape.cc
@@ -0,0 +1,312 @@
+// 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 "gn/escape.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "util/build_config.h"
+
+namespace {
+
+constexpr size_t kStackStringBufferSize = 1024;
+#if defined(OS_WIN)
+constexpr size_t kMaxEscapedCharsPerChar = 2;
+#else
+constexpr size_t kMaxEscapedCharsPerChar = 3;
+#endif
+
+// A "1" in this lookup table means that char is valid in the Posix shell.
+// clang-format off
+const char kShellValid[0x80] = {
+// 00-1f: all are invalid
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// ' ' ! " # $ % & ' ( ) * + , - . /
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
+// @ A B C D E F G H I J K L M N O
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// P Q R S T U V W X Y Z [ \ ] ^ _
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+// ` a b c d e f g h i j k l m n o
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// p q r s t u v w x y z { | } ~
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
+// clang-format on
+
+size_t EscapeStringToString_Space(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ size_t i = 0;
+ for (const auto& elem : str) {
+ if (elem == ' ')
+ dest[i++] = '\\';
+ dest[i++] = elem;
+ }
+ return i;
+}
+
+// Uses the stack if the space needed is small and the heap otherwise.
+class StackOrHeapBuffer {
+ public:
+ explicit StackOrHeapBuffer(size_t buf_size) {
+ if (UNLIKELY(buf_size > kStackStringBufferSize))
+ heap_buf.reset(new char[buf_size]);
+ }
+ operator char*() { return heap_buf ? heap_buf.get() : stack_buf; }
+
+ private:
+ char stack_buf[kStackStringBufferSize];
+ std::unique_ptr<char[]> heap_buf;
+};
+
+// Ninja's escaping rules are very simple. We always escape colons even
+// though they're OK in many places, in case the resulting string is used on
+// the left-hand-side of a rule.
+inline bool ShouldEscapeCharForNinja(char ch) {
+ return ch == '$' || ch == ' ' || ch == ':';
+}
+
+size_t EscapeStringToString_Ninja(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ size_t i = 0;
+ for (const auto& elem : str) {
+ if (ShouldEscapeCharForNinja(elem))
+ dest[i++] = '$';
+ dest[i++] = elem;
+ }
+ return i;
+}
+
+inline bool ShouldEscapeCharForCompilationDatabase(char ch) {
+ return ch == '\\' || ch == '"';
+}
+
+size_t EscapeStringToString_CompilationDatabase(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ size_t i = 0;
+ bool quote = false;
+ for (const auto& elem : str) {
+ if (static_cast<unsigned>(elem) >= 0x80 ||
+ !kShellValid[static_cast<int>(elem)]) {
+ quote = true;
+ break;
+ }
+ }
+ if (quote)
+ dest[i++] = '"';
+
+ for (const auto& elem : str) {
+ if (ShouldEscapeCharForCompilationDatabase(elem))
+ dest[i++] = '\\';
+ dest[i++] = elem;
+ }
+ if (quote)
+ dest[i++] = '"';
+ return i;
+}
+
+size_t EscapeStringToString_Depfile(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ size_t i = 0;
+ for (const auto& elem : str) {
+ // Escape all characters that ninja depfile parser can recognize as escaped,
+ // even if some of them can work without escaping.
+ if (elem == ' ' || elem == '\\' || elem == '#' || elem == '*' ||
+ elem == '[' || elem == '|' || elem == ']')
+ dest[i++] = '\\';
+ else if (elem == '$') // Extra rule for $$
+ dest[i++] = '$';
+ dest[i++] = elem;
+ }
+ return i;
+}
+
+size_t EscapeStringToString_NinjaPreformatted(const std::string_view& str,
+ char* dest) {
+ // Only Ninja-escape $.
+ size_t i = 0;
+ for (const auto& elem : str) {
+ if (elem == '$')
+ dest[i++] = '$';
+ dest[i++] = elem;
+ }
+ return i;
+}
+
+// Escape for CommandLineToArgvW and additionally escape Ninja characters.
+//
+// The basic algorithm is if the string doesn't contain any parse-affecting
+// characters, don't do anything (other than the Ninja processing). If it does,
+// quote the string, and backslash-escape all quotes and backslashes.
+// See:
+// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
+// http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
+size_t EscapeStringToString_WindowsNinjaFork(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ // We assume we don't have any whitespace chars that aren't spaces.
+ DCHECK(str.find_first_of("\r\n\v\t") == std::string::npos);
+
+ size_t i = 0;
+ if (str.find_first_of(" \"") == std::string::npos) {
+ // Simple case, don't quote.
+ return EscapeStringToString_Ninja(str, options, dest, needed_quoting);
+ } else {
+ if (!options.inhibit_quoting)
+ dest[i++] = '"';
+
+ for (size_t j = 0; j < str.size(); j++) {
+ // Count backslashes in case they're followed by a quote.
+ size_t backslash_count = 0;
+ while (j < str.size() && str[j] == '\\') {
+ j++;
+ backslash_count++;
+ }
+ if (j == str.size()) {
+ // Backslashes at end of string. Backslash-escape all of them since
+ // they'll be followed by a quote.
+ memset(dest + i, '\\', backslash_count * 2);
+ i += backslash_count * 2;
+ } else if (str[j] == '"') {
+ // 0 or more backslashes followed by a quote. Backslash-escape the
+ // backslashes, then backslash-escape the quote.
+ memset(dest + i, '\\', backslash_count * 2 + 1);
+ i += backslash_count * 2 + 1;
+ dest[i++] = '"';
+ } else {
+ // Non-special Windows character, just escape for Ninja. Also, add any
+ // backslashes we read previously, these are literals.
+ memset(dest + i, '\\', backslash_count);
+ i += backslash_count;
+ if (ShouldEscapeCharForNinja(str[j]))
+ dest[i++] = '$';
+ dest[i++] = str[j];
+ }
+ }
+
+ if (!options.inhibit_quoting)
+ dest[i++] = '"';
+ if (needed_quoting)
+ *needed_quoting = true;
+ }
+ return i;
+}
+
+size_t EscapeStringToString_PosixNinjaFork(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ size_t i = 0;
+ for (const auto& elem : str) {
+ if (elem == '$' || elem == ' ') {
+ // Space and $ are special to both Ninja and the shell. '$' escape for
+ // Ninja, then backslash-escape for the shell.
+ dest[i++] = '\\';
+ dest[i++] = '$';
+ dest[i++] = elem;
+ } else if (elem == ':') {
+ // Colon is the only other Ninja special char, which is not special to
+ // the shell.
+ dest[i++] = '$';
+ dest[i++] = ':';
+ } else if (static_cast<unsigned>(elem) >= 0x80 ||
+ !kShellValid[static_cast<int>(elem)]) {
+ // All other invalid shell chars get backslash-escaped.
+ dest[i++] = '\\';
+ dest[i++] = elem;
+ } else {
+ // Everything else is a literal.
+ dest[i++] = elem;
+ }
+ }
+ return i;
+}
+
+// Escapes |str| into |dest| and returns the number of characters written.
+size_t EscapeStringToString(const std::string_view& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ switch (options.mode) {
+ case ESCAPE_NONE:
+ strncpy(dest, str.data(), str.size());
+ return str.size();
+ case ESCAPE_SPACE:
+ return EscapeStringToString_Space(str, options, dest, needed_quoting);
+ case ESCAPE_NINJA:
+ return EscapeStringToString_Ninja(str, options, dest, needed_quoting);
+ case ESCAPE_DEPFILE:
+ return EscapeStringToString_Depfile(str, options, dest, needed_quoting);
+ case ESCAPE_COMPILATION_DATABASE:
+ return EscapeStringToString_CompilationDatabase(str, options, dest,
+ needed_quoting);
+ case ESCAPE_NINJA_COMMAND:
+ switch (options.platform) {
+ case ESCAPE_PLATFORM_CURRENT:
+#if defined(OS_WIN)
+ return EscapeStringToString_WindowsNinjaFork(str, options, dest,
+ needed_quoting);
+#else
+ return EscapeStringToString_PosixNinjaFork(str, options, dest,
+ needed_quoting);
+#endif
+ case ESCAPE_PLATFORM_WIN:
+ return EscapeStringToString_WindowsNinjaFork(str, options, dest,
+ needed_quoting);
+ case ESCAPE_PLATFORM_POSIX:
+ return EscapeStringToString_PosixNinjaFork(str, options, dest,
+ needed_quoting);
+ default:
+ NOTREACHED();
+ }
+ case ESCAPE_NINJA_PREFORMATTED_COMMAND:
+ return EscapeStringToString_NinjaPreformatted(str, dest);
+ default:
+ NOTREACHED();
+ }
+ return 0;
+}
+
+} // namespace
+
+std::string EscapeString(const std::string_view& str,
+ const EscapeOptions& options,
+ bool* needed_quoting) {
+ StackOrHeapBuffer dest(str.size() * kMaxEscapedCharsPerChar);
+ return std::string(dest,
+ EscapeStringToString(str, options, dest, needed_quoting));
+}
+
+void EscapeStringToStream(std::ostream& out,
+ const std::string_view& str,
+ const EscapeOptions& options) {
+ StackOrHeapBuffer dest(str.size() * kMaxEscapedCharsPerChar);
+ out.write(dest, EscapeStringToString(str, options, dest, nullptr));
+}
+
+void EscapeJSONStringToStream(std::ostream& out,
+ const std::string_view& str,
+ const EscapeOptions& options) {
+ std::string dest;
+ bool needed_quoting = !options.inhibit_quoting;
+ base::EscapeJSONString(str, needed_quoting, &dest);
+
+ EscapeStringToStream(out, dest, options);
+}
diff --git a/gn/src/gn/escape.h b/gn/src/gn/escape.h
new file mode 100644
index 00000000000..31f972edb4d
--- /dev/null
+++ b/gn/src/gn/escape.h
@@ -0,0 +1,90 @@
+// 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 TOOLS_GN_ESCAPE_H_
+#define TOOLS_GN_ESCAPE_H_
+
+#include <iosfwd>
+#include <string_view>
+
+enum EscapingMode {
+ // No escaping.
+ ESCAPE_NONE,
+
+ // Space only.
+ ESCAPE_SPACE,
+
+ // Ninja string escaping.
+ ESCAPE_NINJA,
+
+ // Ninja/makefile depfile string escaping.
+ ESCAPE_DEPFILE,
+
+ // For writing commands to ninja files. This assumes the output is "one
+ // thing" like a filename, so will escape or quote spaces as necessary for
+ // both Ninja and the shell to keep that thing together.
+ ESCAPE_NINJA_COMMAND,
+
+ // For writing preformatted shell commands to Ninja files. This assumes the
+ // output already has the proper quoting and may include special shell
+ // characters which we want to pass to the shell (like when writing tool
+ // commands). Only Ninja "$" are escaped.
+ ESCAPE_NINJA_PREFORMATTED_COMMAND,
+
+ // Shell escaping as described by JSON Compilation Database spec:
+ // Parameters use shell quoting and shell escaping of quotes, with ‘"’ and ‘\’
+ // being the only special characters.
+ ESCAPE_COMPILATION_DATABASE,
+};
+
+enum EscapingPlatform {
+ // Do escaping for the current platform.
+ ESCAPE_PLATFORM_CURRENT,
+
+ // Force escaping for the given platform.
+ ESCAPE_PLATFORM_POSIX,
+ ESCAPE_PLATFORM_WIN,
+};
+
+struct EscapeOptions {
+ EscapingMode mode = ESCAPE_NONE;
+
+ // Controls how "fork" escaping is done. You will generally want to keep the
+ // default "current" platform.
+ EscapingPlatform platform = ESCAPE_PLATFORM_CURRENT;
+
+ // When the escaping mode is ESCAPE_SHELL, the escaper will normally put
+ // quotes around things with spaces. If this value is set to true, we'll
+ // disable the quoting feature and just add the spaces.
+ //
+ // This mode is for when quoting is done at some higher-level. Defaults to
+ // false. Note that Windows has strange behavior where the meaning of the
+ // backslashes changes according to if it is followed by a quote. The
+ // escaping rules assume that a double-quote will be appended to the result.
+ bool inhibit_quoting = false;
+};
+
+// Escapes the given input, returnining the result.
+//
+// If needed_quoting is non-null, whether the string was or should have been
+// (if inhibit_quoting was set) quoted will be written to it. This value should
+// be initialized to false by the caller and will be written to only if it's
+// true (the common use-case is for chaining calls).
+std::string EscapeString(const std::string_view& str,
+ const EscapeOptions& options,
+ bool* needed_quoting);
+
+// Same as EscapeString but writes the results to the given stream, saving a
+// copy.
+void EscapeStringToStream(std::ostream& out,
+ const std::string_view& str,
+ const EscapeOptions& options);
+
+// Same as EscapeString but escape JSON string and writes the results to the
+// given stream, saving a copy.
+void EscapeJSONStringToStream(std::ostream& out,
+ const std::string_view& str,
+ const EscapeOptions& options);
+
+#endif // TOOLS_GN_ESCAPE_H_
diff --git a/gn/src/gn/escape_unittest.cc b/gn/src/gn/escape_unittest.cc
new file mode 100644
index 00000000000..004498e631e
--- /dev/null
+++ b/gn/src/gn/escape_unittest.cc
@@ -0,0 +1,112 @@
+// 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 "gn/escape.h"
+#include "gn/string_output_buffer.h"
+#include "util/test/test.h"
+
+TEST(Escape, Ninja) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA;
+ std::string result = EscapeString("asdf: \"$\\bar", opts, nullptr);
+ EXPECT_EQ("asdf$:$ \"$$\\bar", result);
+}
+
+TEST(Escape, Depfile) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_DEPFILE;
+ std::string result = EscapeString("asdf:$ \\#*[|]bar", opts, nullptr);
+ EXPECT_EQ("asdf:$$\\ \\\\\\#\\*\\[\\|\\]bar", result);
+}
+
+TEST(Escape, WindowsCommand) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ opts.platform = ESCAPE_PLATFORM_WIN;
+
+ // Regular string is passed, even if it has backslashes.
+ EXPECT_EQ("foo\\bar", EscapeString("foo\\bar", opts, nullptr));
+
+ // Spaces means the string is quoted, normal backslahes untouched.
+ bool needs_quoting = false;
+ EXPECT_EQ("\"foo\\$ bar\"", EscapeString("foo\\ bar", opts, &needs_quoting));
+ EXPECT_TRUE(needs_quoting);
+
+ // Inhibit quoting.
+ needs_quoting = false;
+ opts.inhibit_quoting = true;
+ EXPECT_EQ("foo\\$ bar", EscapeString("foo\\ bar", opts, &needs_quoting));
+ EXPECT_TRUE(needs_quoting);
+ opts.inhibit_quoting = false;
+
+ // Backslashes at the end of the string get escaped.
+ EXPECT_EQ("\"foo$ bar\\\\\\\\\"", EscapeString("foo bar\\\\", opts, nullptr));
+
+ // Backslashes preceding quotes are escaped, and the quote is escaped.
+ EXPECT_EQ("\"foo\\\\\\\"$ bar\"", EscapeString("foo\\\" bar", opts, nullptr));
+}
+
+TEST(Escape, PosixCommand) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ opts.platform = ESCAPE_PLATFORM_POSIX;
+
+ // : and $ ninja escaped with $. Then Shell-escape backslashes and quotes.
+ EXPECT_EQ("a$:\\$ \\\"\\$$\\\\b", EscapeString("a: \"$\\b", opts, nullptr));
+
+ // Some more generic shell chars.
+ EXPECT_EQ("a_\\;\\<\\*b", EscapeString("a_;<*b", opts, nullptr));
+
+ // Curly braces must be escaped to avoid brace expansion on systems using
+ // bash as default shell..
+ EXPECT_EQ("\\{a,b\\}\\{c,d\\}", EscapeString("{a,b}{c,d}", opts, nullptr));
+}
+
+TEST(Escape, NinjaPreformatted) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
+
+ // Only $ is escaped.
+ EXPECT_EQ("a: \"$$\\b<;", EscapeString("a: \"$\\b<;", opts, nullptr));
+}
+
+TEST(Escape, Space) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_SPACE;
+
+ // ' ' is escaped.
+ EXPECT_EQ("-VERSION=\"libsrtp2\\ 2.1.0-pre\"",
+ EscapeString("-VERSION=\"libsrtp2 2.1.0-pre\"", opts, nullptr));
+}
+
+TEST(EscapeJSONString, NinjaPreformatted) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
+ opts.inhibit_quoting = true;
+
+ StringOutputBuffer buffer;
+ std::ostream out(&buffer);
+
+ EscapeJSONStringToStream(out, "foo\\\" bar", opts);
+ EXPECT_EQ("foo\\\\\\\" bar", buffer.str());
+
+ StringOutputBuffer buffer1;
+ std::ostream out1(&buffer1);
+ EscapeJSONStringToStream(out1, "foo bar\\\\", opts);
+ EXPECT_EQ("foo bar\\\\\\\\", buffer1.str());
+
+ StringOutputBuffer buffer2;
+ std::ostream out2(&buffer2);
+ EscapeJSONStringToStream(out2, "a: \"$\\b", opts);
+ EXPECT_EQ("a: \\\"$$\\\\b", buffer2.str());
+}
+
+TEST(Escape, CompilationDatabase) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_COMPILATION_DATABASE;
+
+ // The only special characters are '"' and '\'.
+ std::string result = EscapeString("asdf:$ \\#*[|]bar", opts, nullptr);
+ EXPECT_EQ("\"asdf:$ \\\\#*[|]bar\"", result);
+}
diff --git a/gn/src/gn/exec_process.cc b/gn/src/gn/exec_process.cc
new file mode 100644
index 00000000000..2f001bfeb9f
--- /dev/null
+++ b/gn/src/gn/exec_process.cc
@@ -0,0 +1,289 @@
+// 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 "gn/exec_process.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/win_util.h"
+#else
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/file_descriptor_shuffle.h"
+#endif
+
+namespace internal {
+
+#if defined(OS_WIN)
+bool ExecProcess(const base::CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ return ExecProcess(cmdline.GetCommandLineString(), startup_dir, std_out,
+ std_err, exit_code);
+}
+
+bool ExecProcess(const std::u16string& cmdline_str,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ SECURITY_ATTRIBUTES sa_attr;
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa_attr.bInheritHandle = TRUE;
+ sa_attr.lpSecurityDescriptor = nullptr;
+
+ // Create the pipe for the child process's STDOUT.
+ HANDLE out_read = nullptr;
+ HANDLE out_write = nullptr;
+ if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+ base::win::ScopedHandle scoped_out_read(out_read);
+ base::win::ScopedHandle scoped_out_write(out_write);
+
+ // Create the pipe for the child process's STDERR.
+ HANDLE err_read = nullptr;
+ HANDLE err_write = nullptr;
+ if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+ base::win::ScopedHandle scoped_err_read(err_read);
+ base::win::ScopedHandle scoped_err_write(err_write);
+
+ // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
+ if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disable pipe inheritance";
+ return false;
+ }
+ if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disable pipe inheritance";
+ return false;
+ }
+
+ STARTUPINFO start_info = {};
+
+ start_info.cb = sizeof(STARTUPINFO);
+ start_info.hStdOutput = out_write;
+ // Keep the normal stdin.
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ // FIXME(brettw) set stderr here when we actually read it below.
+ // start_info.hStdError = err_write;
+ start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ std::u16string cmdline_writable = cmdline_str;
+
+ // Create the child process.
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!CreateProcess(
+ nullptr, base::ToWCharT(&cmdline_writable[0]), nullptr, nullptr,
+ TRUE, // Handles are inherited.
+ NORMAL_PRIORITY_CLASS, nullptr, base::ToWCharT(&startup_dir.value()),
+ &start_info, &temp_process_info)) {
+ return false;
+ }
+ base::win::ScopedProcessInformation proc_info(temp_process_info);
+
+ // Close our writing end of pipes now. Otherwise later read would not be
+ // able to detect end of child's output.
+ scoped_out_write.Close();
+ scoped_err_write.Close();
+
+ // Read output from the child process's pipe for STDOUT
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+
+ // FIXME(brettw) read from stderr here! This is complicated because we want
+ // to read both of them at the same time, probably need overlapped I/O.
+ // Also uncomment start_info code above.
+ for (;;) {
+ DWORD bytes_read = 0;
+ BOOL success =
+ ReadFile(out_read, buffer, kBufferSize, &bytes_read, nullptr);
+ if (!success || bytes_read == 0)
+ break;
+ std_out->append(buffer, bytes_read);
+ }
+
+ // Let's wait for the process to finish.
+ WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+ DWORD dw_exit_code;
+ GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
+ *exit_code = static_cast<int>(dw_exit_code);
+
+ return true;
+}
+#else
+// Reads from the provided file descriptor and appends to output. Returns false
+// if the fd is closed or there is an unexpected error (not
+// EINTR/EAGAIN/EWOULDBLOCK).
+bool ReadFromPipe(int fd, std::string* output) {
+ char buffer[256];
+ int bytes_read = HANDLE_EINTR(read(fd, buffer, sizeof(buffer)));
+ if (bytes_read == -1) {
+ return errno == EAGAIN || errno == EWOULDBLOCK;
+ } else if (bytes_read <= 0) {
+ return false;
+ }
+ output->append(buffer, bytes_read);
+ return true;
+}
+
+bool WaitForExit(int pid, int* exit_code) {
+ int status;
+ if (waitpid(pid, &status, 0) < 0) {
+ PLOG(ERROR) << "waitpid";
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ } else if (WIFSIGNALED(status)) {
+ if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM ||
+ WTERMSIG(status) == SIGHUP)
+ return false;
+ }
+ return false;
+}
+
+bool ExecProcess(const base::CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ *exit_code = EXIT_FAILURE;
+
+ std::vector<std::string> argv = cmdline.argv();
+
+ int out_fd[2], err_fd[2];
+ pid_t pid;
+ base::InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ std::unique_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
+
+ fd_shuffle1.reserve(3);
+ fd_shuffle2.reserve(3);
+
+ if (pipe(out_fd) < 0)
+ return false;
+ base::ScopedFD out_read(out_fd[0]), out_write(out_fd[1]);
+
+ if (pipe(err_fd) < 0)
+ return false;
+ base::ScopedFD err_read(err_fd[0]), err_write(err_fd[1]);
+
+ if (out_read.get() >= FD_SETSIZE || err_read.get() >= FD_SETSIZE)
+ return false;
+
+ switch (pid = fork()) {
+ case -1: // error
+ return false;
+ case 0: // child
+ {
+#if defined(OS_MAC)
+ // When debugging the app under Xcode, the child will receive a SIGTRAP
+ // signal which will terminate the child process. Ignore the signal to
+ // allow debugging under macOS.
+ sigignore(SIGTRAP);
+#endif
+
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+ //
+ // STL iterators are also not allowed (including those implied
+ // by range-based for loops), since debug iterators use locks.
+
+ // Obscure fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
+ int dev_null = open("/dev/null", O_WRONLY);
+ if (dev_null < 0)
+ _exit(127);
+
+ fd_shuffle1.push_back(
+ base::InjectionArc(out_write.get(), STDOUT_FILENO, true));
+ fd_shuffle1.push_back(
+ base::InjectionArc(err_write.get(), STDERR_FILENO, true));
+ fd_shuffle1.push_back(base::InjectionArc(dev_null, STDIN_FILENO, true));
+ // Adding another element here? Remeber to increase the argument to
+ // reserve(), above.
+
+ // DANGER: Do NOT convert to range-based for loop!
+ for (size_t i = 0; i < fd_shuffle1.size(); ++i)
+ fd_shuffle2.push_back(fd_shuffle1[i]);
+
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ base::SetCurrentDirectory(startup_dir);
+
+ // TODO(brettw) the base version GetAppOutput does a
+ // CloseSuperfluousFds call here. Do we need this?
+
+ // DANGER: Do NOT convert to range-based for loop!
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = nullptr;
+ execvp(argv_cstr[0], argv_cstr.get());
+ _exit(127);
+ }
+ default: // parent
+ {
+ // Close our writing end of pipe now. Otherwise later read would not
+ // be able to detect end of child's output (in theory we could still
+ // write to the pipe).
+ out_write.reset();
+ err_write.reset();
+
+ bool out_open = true, err_open = true;
+ while (out_open || err_open) {
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ FD_SET(out_read.get(), &read_fds);
+ FD_SET(err_read.get(), &read_fds);
+ int res =
+ HANDLE_EINTR(select(std::max(out_read.get(), err_read.get()) + 1,
+ &read_fds, nullptr, nullptr, nullptr));
+ if (res <= 0)
+ break;
+ if (FD_ISSET(out_read.get(), &read_fds))
+ out_open = ReadFromPipe(out_read.get(), std_out);
+ if (FD_ISSET(err_read.get(), &read_fds))
+ err_open = ReadFromPipe(err_read.get(), std_err);
+ }
+
+ return WaitForExit(pid, exit_code);
+ }
+ }
+
+ return false;
+}
+#endif
+
+} // namespace internal
diff --git a/gn/src/gn/exec_process.h b/gn/src/gn/exec_process.h
new file mode 100644
index 00000000000..91ee876fd1e
--- /dev/null
+++ b/gn/src/gn/exec_process.h
@@ -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.
+
+#ifndef TOOLS_GN_EXEC_PROCESS_H_
+#define TOOLS_GN_EXEC_PROCESS_H_
+
+#include <string>
+
+#include "util/build_config.h"
+
+namespace base {
+class CommandLine;
+class FilePath;
+} // namespace base
+
+namespace internal {
+
+bool ExecProcess(const base::CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code);
+
+#if defined(OS_WIN)
+bool ExecProcess(const std::u16string& cmdline_str,
+ const base::FilePath& startup_dir,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code);
+#endif // OS_WIN
+
+} // namespace internal
+
+#endif // TOOLS_GN_EXEC_PROCESS_H_
diff --git a/gn/src/gn/exec_process_unittest.cc b/gn/src/gn/exec_process_unittest.cc
new file mode 100644
index 00000000000..599892d3867
--- /dev/null
+++ b/gn/src/gn/exec_process_unittest.cc
@@ -0,0 +1,123 @@
+// 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 "gn/exec_process.h"
+
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_util.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+#if defined(OS_WIN)
+#include "base/strings/utf_string_conversions.h"
+#endif
+
+namespace internal {
+
+// TODO(cjhopman): Enable these tests when windows ExecProcess handles stderr.
+// 'python' is not runnable on Windows. Adding ["cmd", "/c"] fails because
+// CommandLine does unusual reordering of args.
+#if !defined(OS_WIN)
+namespace {
+bool ExecPython(const std::string& command,
+ std::string* std_out,
+ std::string* std_err,
+ int* exit_code) {
+ base::ScopedTempDir temp_dir;
+ CHECK(temp_dir.CreateUniqueTempDir());
+ base::CommandLine::StringVector args;
+#if defined(OS_WIN)
+ args.push_back(L"python");
+ args.push_back(L"-c");
+ args.push_back(base::UTF8ToUTF16(command));
+#else
+ args.push_back("python3");
+ args.push_back("-c");
+ args.push_back(command);
+#endif
+ return ExecProcess(base::CommandLine(args), temp_dir.GetPath(), std_out,
+ std_err, exit_code);
+}
+} // namespace
+
+TEST(ExecProcessTest, TestExitCode) {
+ std::string std_out, std_err;
+ int exit_code;
+
+ ASSERT_TRUE(
+ ExecPython("import sys; sys.exit(0)", &std_out, &std_err, &exit_code));
+ EXPECT_EQ(0, exit_code);
+
+ ASSERT_TRUE(
+ ExecPython("import sys; sys.exit(1)", &std_out, &std_err, &exit_code));
+ EXPECT_EQ(1, exit_code);
+
+ ASSERT_TRUE(
+ ExecPython("import sys; sys.exit(253)", &std_out, &std_err, &exit_code));
+ EXPECT_EQ(253, exit_code);
+
+ ASSERT_TRUE(ExecPython("throw Exception()", &std_out, &std_err, &exit_code));
+ EXPECT_EQ(1, exit_code);
+}
+
+// Test that large output is handled correctly. There are various ways that this
+// could potentially fail. For example, non-blocking Linux pipes have a 65536
+// byte buffer and, if stdout is non-blocking, python will throw an IOError when
+// a write exceeds the buffer size.
+TEST(ExecProcessTest, TestLargeOutput) {
+ std::string std_out, std_err;
+ int exit_code;
+
+ ASSERT_TRUE(ExecPython("import sys; print('o' * 1000000)", &std_out, &std_err,
+ &exit_code));
+ EXPECT_EQ(0, exit_code);
+ EXPECT_EQ(1000001u, std_out.size());
+}
+
+TEST(ExecProcessTest, TestStdoutAndStderrOutput) {
+ std::string std_out, std_err;
+ int exit_code;
+
+ ASSERT_TRUE(
+ ExecPython("from __future__ import print_function; import sys; print('o' "
+ "* 10000); print('e' * 10000, file=sys.stderr)",
+ &std_out, &std_err, &exit_code));
+ EXPECT_EQ(0, exit_code);
+ EXPECT_EQ(10001u, std_out.size());
+ EXPECT_EQ(10001u, std_err.size());
+
+ std_out.clear();
+ std_err.clear();
+ ASSERT_TRUE(
+ ExecPython("from __future__ import print_function; import sys; print('e' "
+ "* 10000, file=sys.stderr); print('o' * 10000)",
+ &std_out, &std_err, &exit_code));
+ EXPECT_EQ(0, exit_code);
+ EXPECT_EQ(10001u, std_out.size());
+ EXPECT_EQ(10001u, std_err.size());
+}
+
+TEST(ExecProcessTest, TestOneOutputClosed) {
+ std::string std_out, std_err;
+ int exit_code;
+
+ ASSERT_TRUE(ExecPython("import sys; sys.stderr.close(); print('o' * 10000)",
+ &std_out, &std_err, &exit_code));
+ EXPECT_EQ(0, exit_code);
+ EXPECT_EQ(10001u, std_out.size());
+ EXPECT_EQ(std_err.size(), 0u);
+
+ std_out.clear();
+ std_err.clear();
+ ASSERT_TRUE(
+ ExecPython("from __future__ import print_function; import sys; "
+ "sys.stdout.close(); print('e' * 10000, file=sys.stderr)",
+ &std_out, &std_err, &exit_code));
+ EXPECT_EQ(0, exit_code);
+ EXPECT_EQ(0u, std_out.size());
+ EXPECT_EQ(10001u, std_err.size());
+}
+#endif
+} // namespace internal
diff --git a/gn/src/gn/file_writer.cc b/gn/src/gn/file_writer.cc
new file mode 100644
index 00000000000..a4bb5bee436
--- /dev/null
+++ b/gn/src/gn/file_writer.cc
@@ -0,0 +1,108 @@
+// Copyright 2020 The Chromium 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 "gn/file_writer.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "gn/filesystem_utils.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "base/strings/utf_string_conversions.h"
+#else
+#include <fcntl.h>
+#include <unistd.h>
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+FileWriter::~FileWriter() = default;
+
+#if defined(OS_WIN)
+
+bool FileWriter::Create(const base::FilePath& file_path) {
+ // On Windows, provide a custom implementation of base::WriteFile. Sometimes
+ // the base version fails, especially on the bots. The guess is that Windows
+ // Defender or other antivirus programs still have the file open (after
+ // checking for the read) when the write happens immediately after. This
+ // version opens with FILE_SHARE_READ (normally not what you want when
+ // replacing the entire contents of the file) which lets us continue even if
+ // another program has the file open for reading. See
+ // http://crbug.com/468437
+ file_path_ = base::UTF16ToUTF8(file_path.value());
+ file_ = base::win::ScopedHandle(::CreateFile(
+ reinterpret_cast<LPCWSTR>(file_path.value().c_str()), GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL));
+
+ valid_ = file_.IsValid();
+ if (!valid_) {
+ PLOG(ERROR) << "CreateFile failed for path " << file_path_;
+ }
+ return valid_;
+}
+
+bool FileWriter::Write(std::string_view str) {
+ if (!valid_)
+ return false;
+
+ DWORD written;
+ BOOL result =
+ ::WriteFile(file_.Get(), str.data(), str.size(), &written, nullptr);
+ if (!result) {
+ PLOG(ERROR) << "writing file " << file_path_ << " failed";
+ valid_ = false;
+ return false;
+ }
+ if (static_cast<size_t>(written) != str.size()) {
+ PLOG(ERROR) << "wrote " << written << " bytes to "
+ << file_path_ << " expected " << str.size();
+ valid_ = false;
+ return false;
+ }
+ return true;
+}
+
+bool FileWriter::Close() {
+ // NOTE: file_.Close() is not used here because it cannot return an error.
+ HANDLE handle = file_.Take();
+ if (handle && !::CloseHandle(handle))
+ return false;
+
+ return valid_;
+}
+
+#else // !OS_WIN
+
+bool FileWriter::Create(const base::FilePath& file_path) {
+ fd_.reset(HANDLE_EINTR(::creat(file_path.value().c_str(), 0666)));
+ valid_ = fd_.is_valid();
+ if (!valid_) {
+ PLOG(ERROR) << "creat() failed for path " << file_path.value();
+ }
+ return valid_;
+}
+
+bool FileWriter::Write(std::string_view str) {
+ if (!valid_)
+ return false;
+
+ while (!str.empty()) {
+ ssize_t written = HANDLE_EINTR(::write(fd_.get(), str.data(), str.size()));
+ if (written <= 0) {
+ valid_ = false;
+ return false;
+ }
+ str.remove_prefix(static_cast<size_t>(written));
+ }
+ return true;
+}
+
+bool FileWriter::Close() {
+ // The ScopedFD reset() method will crash on EBADF and ignore other errors
+ // intentionally, so no need to check anything here.
+ fd_.reset();
+ return valid_;
+}
+
+#endif // !OS_WIN
diff --git a/gn/src/gn/file_writer.h b/gn/src/gn/file_writer.h
new file mode 100644
index 00000000000..0ae50619e59
--- /dev/null
+++ b/gn/src/gn/file_writer.h
@@ -0,0 +1,74 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_FILE_WRITER_H_
+#define TOOLS_GN_FILE_WRITER_H_
+
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#else
+#include "base/files/scoped_file.h"
+#endif
+
+#include <string>
+
+namespace base {
+class FilePath;
+}
+
+// Convenience class to write data to a file. This is used to work around two
+// limitations of base::WriteFile, i.e.:
+//
+// - base::WriteFile() doesn't allow multiple writes to the target file.
+//
+//
+// - Windows-specific issues created by anti-virus programs required opening
+// the file differently (see http://crbug.com/468437).
+//
+// Usage is:
+// 1) Create instance.
+// 2) Call Create() to create the file.
+// 3) Call Write() one or more times to write data to it.
+// 4) Call Close(), or the destructor, to close the file.
+//
+// As soon as one method fails, all other calls will return false, this allows
+// simplified error checking as in:
+//
+// FileWriter writer;
+// writer.Create(<some_path>);
+// writer.Write(<some_data>);
+// writer.Write(<some_more_data>);
+// if (!writer.Close()) {
+// ... error happened in one of the above calls.
+// }
+//
+class FileWriter {
+ public:
+ FileWriter() = default;
+ ~FileWriter();
+
+ // Create output file. Return true on success, false otherwise.
+ bool Create(const base::FilePath& file_path);
+
+ // Append |data| to the output file. Return true on success, or false on
+ // failure or if any previous Create() or Write() call failed.
+ bool Write(std::string_view data);
+
+ // Close the file. Return true on success, or false on failure or if
+ // any previous Create() or Write() call failed.
+ bool Close();
+
+ private:
+#if defined(OS_WIN)
+ base::win::ScopedHandle file_;
+ std::string file_path_;
+#else
+ base::ScopedFD fd_;
+#endif
+ bool valid_ = true;
+};
+
+#endif // TOOLS_GN_FILE_WRITER_H_
diff --git a/gn/src/gn/file_writer_unittest.cc b/gn/src/gn/file_writer_unittest.cc
new file mode 100644
index 00000000000..49f533a5071
--- /dev/null
+++ b/gn/src/gn/file_writer_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium 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 "gn/file_writer.h"
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "gn/filesystem_utils.h"
+
+#include "util/test/test.h"
+
+TEST(FileWriter, SingleWrite) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ std::string data = "foo";
+
+ base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt");
+
+ FileWriter writer;
+ EXPECT_TRUE(writer.Create(file_path));
+ EXPECT_TRUE(writer.Write(data));
+ EXPECT_TRUE(writer.Close());
+
+ EXPECT_TRUE(ContentsEqual(file_path, data));
+}
+
+TEST(FileWriter, MultipleWrites) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ std::string data = "Hello World!";
+
+ base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt");
+
+ FileWriter writer;
+ EXPECT_TRUE(writer.Create(file_path));
+ EXPECT_TRUE(writer.Write("Hello "));
+ EXPECT_TRUE(writer.Write("World!"));
+ EXPECT_TRUE(writer.Close());
+
+ EXPECT_TRUE(ContentsEqual(file_path, data));
+}
diff --git a/gn/src/gn/filesystem_utils.cc b/gn/src/gn/filesystem_utils.cc
new file mode 100644
index 00000000000..ae6d4d97ee9
--- /dev/null
+++ b/gn/src/gn/filesystem_utils.cc
@@ -0,0 +1,1077 @@
+// 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 "gn/filesystem_utils.h"
+
+#include <algorithm>
+
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/file_writer.h"
+#include "gn/location.h"
+#include "gn/settings.h"
+#include "gn/source_dir.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+enum DotDisposition {
+ // The given dot is just part of a filename and is not special.
+ NOT_A_DIRECTORY,
+
+ // The given dot is the current directory.
+ DIRECTORY_CUR,
+
+ // The given dot is the first of a double dot that should take us up one.
+ DIRECTORY_UP
+};
+
+// When we find a dot, this function is called with the character following
+// that dot to see what it is. The return value indicates what type this dot is
+// (see above). This code handles the case where the dot is at the end of the
+// input.
+//
+// |*consumed_len| will contain the number of characters in the input that
+// express what we found.
+DotDisposition ClassifyAfterDot(const std::string& path,
+ size_t after_dot,
+ size_t* consumed_len) {
+ if (after_dot == path.size()) {
+ // Single dot at the end.
+ *consumed_len = 1;
+ return DIRECTORY_CUR;
+ }
+ if (IsSlash(path[after_dot])) {
+ // Single dot followed by a slash.
+ *consumed_len = 2; // Consume the slash
+ return DIRECTORY_CUR;
+ }
+
+ if (path[after_dot] == '.') {
+ // Two dots.
+ if (after_dot + 1 == path.size()) {
+ // Double dot at the end.
+ *consumed_len = 2;
+ return DIRECTORY_UP;
+ }
+ if (IsSlash(path[after_dot + 1])) {
+ // Double dot folowed by a slash.
+ *consumed_len = 3;
+ return DIRECTORY_UP;
+ }
+ }
+
+ // The dots are followed by something else, not a directory.
+ *consumed_len = 1;
+ return NOT_A_DIRECTORY;
+}
+
+#if defined(OS_WIN)
+inline char NormalizeWindowsPathChar(char c) {
+ if (c == '/')
+ return '\\';
+ return base::ToLowerASCII(c);
+}
+
+// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
+// paths.
+bool AreAbsoluteWindowsPathsEqual(const std::string_view& a,
+ const std::string_view& b) {
+ if (a.size() != b.size())
+ return false;
+
+ // For now, just do a case-insensitive ASCII comparison. We could convert to
+ // UTF-16 and use ICU if necessary.
+ for (size_t i = 0; i < a.size(); i++) {
+ if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
+ return false;
+ }
+ return true;
+}
+
+bool DoesBeginWindowsDriveLetter(const std::string_view& path) {
+ if (path.size() < 3)
+ return false;
+
+ // Check colon first, this will generally fail fastest.
+ if (path[1] != ':')
+ return false;
+
+ // Check drive letter.
+ if (!base::IsAsciiAlpha(path[0]))
+ return false;
+
+ if (!IsSlash(path[2]))
+ return false;
+ return true;
+}
+#endif
+
+// A wrapper around FilePath.GetComponents that works the way we need. This is
+// not super efficient since it does some O(n) transformations on the path. If
+// this is called a lot, we might want to optimize.
+std::vector<base::FilePath::StringType> GetPathComponents(
+ const base::FilePath& path) {
+ std::vector<base::FilePath::StringType> result;
+ path.GetComponents(&result);
+
+ if (result.empty())
+ return result;
+
+ // GetComponents will preserve the "/" at the beginning, which confuses us.
+ // We don't expect to have relative paths in this function.
+ // Don't use IsSeparator since we always want to allow backslashes.
+ if (result[0] == FILE_PATH_LITERAL("/") ||
+ result[0] == FILE_PATH_LITERAL("\\"))
+ result.erase(result.begin());
+
+#if defined(OS_WIN)
+ // On Windows, GetComponents will give us [ "C:", "/", "foo" ], and we
+ // don't want the slash in there. This doesn't support input like "C:foo"
+ // which means foo relative to the current directory of the C drive but
+ // that's basically legacy DOS behavior we don't need to support.
+ if (result.size() >= 2 && result[1].size() == 1 &&
+ IsSlash(static_cast<char>(result[1][0])))
+ result.erase(result.begin() + 1);
+#endif
+
+ return result;
+}
+
+// Provides the equivalent of == for filesystem strings, trying to do
+// approximately the right thing with case.
+bool FilesystemStringsEqual(const base::FilePath::StringType& a,
+ const base::FilePath::StringType& b) {
+#if defined(OS_WIN)
+ // Assume case-insensitive filesystems on Windows. We use the CompareString
+ // function to do a case-insensitive comparison based on the current locale
+ // (we don't want GN to depend on ICU which is large and requires data
+ // files). This isn't perfect, but getting this perfectly right is very
+ // difficult and requires I/O, and this comparison should cover 99.9999% of
+ // all cases.
+ //
+ // Note: The documentation for CompareString says it runs fastest on
+ // null-terminated strings with -1 passed for the length, so we do that here.
+ // There should not be embedded nulls in filesystem strings.
+ return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
+ reinterpret_cast<LPCWSTR>(a.c_str()), -1,
+ reinterpret_cast<LPCWSTR>(b.c_str()),
+ -1) == CSTR_EQUAL;
+#else
+ // Assume case-sensitive filesystems on non-Windows.
+ return a == b;
+#endif
+}
+
+// Helper function for computing subdirectories in the build directory
+// corresponding to absolute paths. This will try to resolve the absolute
+// path as a source-relative path first, and otherwise it creates a
+// special subdirectory for absolute paths to keep them from colliding with
+// other generated sources and outputs.
+void AppendFixedAbsolutePathSuffix(const BuildSettings* build_settings,
+ const SourceDir& source_dir,
+ OutputFile* result) {
+ const std::string& build_dir = build_settings->build_dir().value();
+
+ if (base::StartsWith(source_dir.value(), build_dir,
+ base::CompareCase::SENSITIVE)) {
+ size_t build_dir_size = build_dir.size();
+ result->value().append(&source_dir.value()[build_dir_size],
+ source_dir.value().size() - build_dir_size);
+ } else {
+ result->value().append("ABS_PATH");
+#if defined(OS_WIN)
+ // Windows absolute path contains ':' after drive letter. Remove it to
+ // avoid inserting ':' in the middle of path (eg. "ABS_PATH/C:/").
+ std::string src_dir_value = source_dir.value();
+ const auto colon_pos = src_dir_value.find(':');
+ if (colon_pos != std::string::npos)
+ src_dir_value.erase(src_dir_value.begin() + colon_pos);
+#else
+ const std::string& src_dir_value = source_dir.value();
+#endif
+ result->value().append(src_dir_value);
+ }
+}
+
+size_t AbsPathLenWithNoTrailingSlash(const std::string_view& path) {
+ size_t len = path.size();
+#if defined(OS_WIN)
+ size_t min_len = 3;
+#else
+ // On posix system. The minimal abs path is "/".
+ size_t min_len = 1;
+#endif
+ for (; len > min_len && IsSlash(path[len - 1]); len--)
+ ;
+ return len;
+}
+} // namespace
+
+std::string FilePathToUTF8(const base::FilePath::StringType& str) {
+#if defined(OS_WIN)
+ return base::UTF16ToUTF8(str);
+#else
+ return str;
+#endif
+}
+
+base::FilePath UTF8ToFilePath(const std::string_view& sp) {
+#if defined(OS_WIN)
+ return base::FilePath(base::UTF8ToUTF16(sp));
+#else
+ return base::FilePath(sp);
+#endif
+}
+
+size_t FindExtensionOffset(const std::string& path) {
+ for (int i = static_cast<int>(path.size()); i >= 0; i--) {
+ if (IsSlash(path[i]))
+ break;
+ if (path[i] == '.')
+ return i + 1;
+ }
+ return std::string::npos;
+}
+
+std::string_view FindExtension(const std::string* path) {
+ size_t extension_offset = FindExtensionOffset(*path);
+ if (extension_offset == std::string::npos)
+ return std::string_view();
+ return std::string_view(&path->data()[extension_offset],
+ path->size() - extension_offset);
+}
+
+size_t FindFilenameOffset(const std::string& path) {
+ for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
+ if (IsSlash(path[i]))
+ return i + 1;
+ }
+ return 0; // No filename found means everything was the filename.
+}
+
+std::string_view FindFilename(const std::string* path) {
+ size_t filename_offset = FindFilenameOffset(*path);
+ if (filename_offset == 0)
+ return std::string_view(*path); // Everything is the file name.
+ return std::string_view(&(*path).data()[filename_offset],
+ path->size() - filename_offset);
+}
+
+std::string_view FindFilenameNoExtension(const std::string* path) {
+ if (path->empty())
+ return std::string_view();
+ size_t filename_offset = FindFilenameOffset(*path);
+ size_t extension_offset = FindExtensionOffset(*path);
+
+ size_t name_len;
+ if (extension_offset == std::string::npos)
+ name_len = path->size() - filename_offset;
+ else
+ name_len = extension_offset - filename_offset - 1;
+
+ return std::string_view(&(*path).data()[filename_offset], name_len);
+}
+
+void RemoveFilename(std::string* path) {
+ path->resize(FindFilenameOffset(*path));
+}
+
+bool EndsWithSlash(const std::string_view s) {
+ return !s.empty() && IsSlash(s[s.size() - 1]);
+}
+
+std::string_view FindDir(const std::string* path) {
+ size_t filename_offset = FindFilenameOffset(*path);
+ if (filename_offset == 0u)
+ return std::string_view();
+ return std::string_view(path->data(), filename_offset);
+}
+
+std::string_view FindLastDirComponent(const SourceDir& dir) {
+ const std::string& dir_string = dir.value();
+
+ if (dir_string.empty())
+ return std::string_view();
+ int cur = static_cast<int>(dir_string.size()) - 1;
+ DCHECK(dir_string[cur] == '/');
+ int end = cur;
+ cur--; // Skip before the last slash.
+
+ for (; cur >= 0; cur--) {
+ if (dir_string[cur] == '/')
+ return std::string_view(&dir_string[cur + 1], end - cur - 1);
+ }
+ return std::string_view(&dir_string[0], end);
+}
+
+bool IsStringInOutputDir(const SourceDir& output_dir, const std::string& str) {
+ // This check will be wrong for all proper prefixes "e.g. "/output" will
+ // match "/out" but we don't really care since this is just a sanity check.
+ const std::string& dir_str = output_dir.value();
+ return str.compare(0, dir_str.length(), dir_str) == 0;
+}
+
+bool EnsureStringIsInOutputDir(const SourceDir& output_dir,
+ const std::string& str,
+ const ParseNode* origin,
+ Err* err) {
+ if (IsStringInOutputDir(output_dir, str))
+ return true; // Output directory is hardcoded.
+
+ *err = Err(
+ origin, "File is not inside output directory.",
+ "The given file should be in the output directory. Normally you would "
+ "specify\n\"$target_out_dir/foo\" or "
+ "\"$target_gen_dir/foo\". I interpreted this as\n\"" +
+ str + "\".");
+ return false;
+}
+
+bool IsPathAbsolute(const std::string_view& path) {
+ if (path.empty())
+ return false;
+
+ if (!IsSlash(path[0])) {
+#if defined(OS_WIN)
+ // Check for Windows system paths like "C:\foo".
+ if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
+ return true;
+#endif
+ return false; // Doesn't begin with a slash, is relative.
+ }
+
+ // Double forward slash at the beginning means source-relative (we don't
+ // allow backslashes for denoting this).
+ if (path.size() > 1 && path[1] == '/')
+ return false;
+
+ return true;
+}
+
+bool IsPathSourceAbsolute(const std::string_view& path) {
+ return (path.size() >= 2 && path[0] == '/' && path[1] == '/');
+}
+
+bool MakeAbsolutePathRelativeIfPossible(const std::string_view& source_root,
+ const std::string_view& path,
+ std::string* dest) {
+ DCHECK(IsPathAbsolute(source_root));
+ DCHECK(IsPathAbsolute(path));
+
+ dest->clear();
+
+ // There is no specification of how many slashes may be at the end of
+ // source_root or path. Trim them off for easier string manipulation.
+ size_t path_len = AbsPathLenWithNoTrailingSlash(path);
+ size_t source_root_len = AbsPathLenWithNoTrailingSlash(source_root);
+
+ if (source_root_len > path_len)
+ return false; // The source root is longer: the path can never be inside.
+#if defined(OS_WIN)
+ // Source root should be canonical on Windows. Note that the initial slash
+ // must be forward slash, but that the other ones can be either forward or
+ // backward.
+ DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
+ source_root[1] == ':' && IsSlash(source_root[2]));
+
+ size_t after_common_index = std::string::npos;
+ if (DoesBeginWindowsDriveLetter(path)) {
+ // Handle "C:\foo"
+ if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
+ path.substr(0, source_root_len))) {
+ after_common_index = source_root_len;
+ if (path_len == source_root_len) {
+ *dest = "//";
+ return true;
+ }
+ } else {
+ return false;
+ }
+ } else if (path[0] == '/' && source_root_len <= path_len - 1 &&
+ DoesBeginWindowsDriveLetter(path.substr(1))) {
+ // Handle "/C:/foo"
+ if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
+ path.substr(1, source_root_len))) {
+ after_common_index = source_root_len + 1;
+ if (path_len + 1 == source_root_len) {
+ *dest = "//";
+ return true;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ // If we get here, there's a match and after_common_index identifies the
+ // part after it.
+
+ if (!IsSlash(path[after_common_index])) {
+ // path is ${source-root}SUFFIX/...
+ return false;
+ }
+ // A source-root relative path, The input may have an unknown number of
+ // slashes after the previous match. Skip over them.
+ size_t first_after_slash = after_common_index + 1;
+ while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
+ first_after_slash++;
+ dest->assign("//"); // Result is source root relative.
+ dest->append(&path.data()[first_after_slash],
+ path.size() - first_after_slash);
+ return true;
+
+#else
+
+ // On non-Windows this is easy. Since we know both are absolute, just do a
+ // prefix check.
+
+ if (path.substr(0, source_root_len) ==
+ source_root.substr(0, source_root_len)) {
+ if (path_len == source_root_len) {
+ // path is equivalent to source_root.
+ *dest = "//";
+ return true;
+ } else if (!IsSlash(path[source_root_len])) {
+ // path is ${source-root}SUFFIX/...
+ return false;
+ }
+ // A source-root relative path, The input may have an unknown number of
+ // slashes after the previous match. Skip over them.
+ size_t first_after_slash = source_root_len + 1;
+ while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
+ first_after_slash++;
+
+ dest->assign("//"); // Result is source root relative.
+ dest->append(&path.data()[first_after_slash],
+ path.size() - first_after_slash);
+ return true;
+ }
+ return false;
+#endif
+}
+
+base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
+ const base::FilePath& base,
+ const base::FilePath& target) {
+ DCHECK(base.IsAbsolute());
+ DCHECK(target.IsAbsolute());
+ std::vector<base::FilePath::StringType> base_components;
+ std::vector<base::FilePath::StringType> target_components;
+ base.GetComponents(&base_components);
+ target.GetComponents(&target_components);
+#if defined(OS_WIN)
+ // On Windows, it's impossible to have a relative path from C:\foo to D:\bar,
+ // so return the target as an absolute path instead.
+ if (base_components[0] != target_components[0])
+ return target;
+
+ // GetComponents() returns the first slash after the root. Set it to the
+ // same value in both component lists so that relative paths between
+ // "C:/foo/..." and "C:\foo\..." are computed correctly.
+ target_components[1] = base_components[1];
+#endif
+ size_t i;
+ for (i = 0; i < base_components.size() && i < target_components.size(); i++) {
+ if (base_components[i] != target_components[i])
+ break;
+ }
+ std::vector<base::FilePath::StringType> relative_components;
+ for (size_t j = i; j < base_components.size(); j++)
+ relative_components.push_back(base::FilePath::kParentDirectory);
+ for (size_t j = i; j < target_components.size(); j++)
+ relative_components.push_back(target_components[j]);
+ if (relative_components.size() <= 1) {
+ // In case the file pointed-to is an executable, prepend the current
+ // directory to the path -- if the path was "gn", use "./gn" instead. If
+ // the file path is used in a command, this prevents issues where "gn" might
+ // not be in the PATH (or it is in the path, and the wrong gn is used).
+ relative_components.insert(relative_components.begin(),
+ base::FilePath::kCurrentDirectory);
+ }
+ // base::FilePath::Append(component) replaces the file path with |component|
+ // if the path is base::Filepath::kCurrentDirectory. We want to preserve the
+ // leading "./", so we build the path ourselves and use that to construct the
+ // base::FilePath.
+ base::FilePath::StringType separator(&base::FilePath::kSeparators[0], 1);
+ return base::FilePath(base::JoinString(relative_components, separator));
+}
+
+void NormalizePath(std::string* path, const std::string_view& source_root) {
+ char* pathbuf = path->empty() ? nullptr : &(*path)[0];
+
+ // top_index is the first character we can modify in the path. Anything
+ // before this indicates where the path is relative to.
+ size_t top_index = 0;
+ bool is_relative = true;
+ if (!path->empty() && pathbuf[0] == '/') {
+ is_relative = false;
+
+ if (path->size() > 1 && pathbuf[1] == '/') {
+ // Two leading slashes, this is a path into the source dir.
+ top_index = 2;
+ } else {
+ // One leading slash, this is a system-absolute path.
+ top_index = 1;
+ }
+ }
+
+ size_t dest_i = top_index;
+ for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
+ if (pathbuf[src_i] == '.') {
+ if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
+ // Slash followed by a dot, see if it's something special.
+ size_t consumed_len;
+ switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
+ case NOT_A_DIRECTORY:
+ // Copy the dot to the output, it means nothing special.
+ pathbuf[dest_i++] = pathbuf[src_i++];
+ break;
+ case DIRECTORY_CUR:
+ // Current directory, just skip the input.
+ src_i += consumed_len;
+ break;
+ case DIRECTORY_UP:
+ // Back up over previous directory component. If we're already
+ // at the top, preserve the "..".
+ if (dest_i > top_index) {
+ // The previous char was a slash, remove it.
+ dest_i--;
+ }
+
+ if (dest_i == top_index) {
+ if (is_relative) {
+ // We're already at the beginning of a relative input, copy the
+ // ".." and continue. We need the trailing slash if there was
+ // one before (otherwise we're at the end of the input).
+ pathbuf[dest_i++] = '.';
+ pathbuf[dest_i++] = '.';
+ if (consumed_len == 3)
+ pathbuf[dest_i++] = '/';
+
+ // This also makes a new "root" that we can't delete by going
+ // up more levels. Otherwise "../.." would collapse to
+ // nothing.
+ top_index = dest_i;
+ } else if (top_index == 2 && !source_root.empty()) {
+ // |path| was passed in as a source-absolute path. Prepend
+ // |source_root| to make |path| absolute. |source_root| must not
+ // end with a slash unless we are at root.
+ DCHECK(source_root.size() == 1u ||
+ !IsSlash(source_root[source_root.size() - 1u]));
+ size_t source_root_len = source_root.size();
+
+#if defined(OS_WIN)
+ // On Windows, if the source_root does not start with a slash,
+ // append one here for consistency.
+ if (!IsSlash(source_root[0])) {
+ path->insert(0, "/" + std::string(source_root));
+ source_root_len++;
+ } else {
+ path->insert(0, source_root.data(), source_root_len);
+ }
+
+ // Normalize slashes in source root portion.
+ for (size_t i = 0; i < source_root_len; ++i) {
+ if ((*path)[i] == '\\')
+ (*path)[i] = '/';
+ }
+#else
+ path->insert(0, source_root.data(), source_root_len);
+#endif
+
+ // |path| is now absolute, so |top_index| is 1. |dest_i| and
+ // |src_i| should be incremented to keep the same relative
+ // position. Comsume the leading "//" by decrementing |dest_i|.
+ top_index = 1;
+ pathbuf = &(*path)[0];
+ dest_i += source_root_len - 2;
+ src_i += source_root_len;
+
+ // Just find the previous slash or the beginning of input.
+ while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
+ dest_i--;
+ }
+ // Otherwise we're at the beginning of a system-absolute path, or
+ // a source-absolute path for which we don't know the absolute
+ // path. Don't allow ".." to go up another level, and just eat it.
+ } else {
+ // Just find the previous slash or the beginning of input.
+ while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
+ dest_i--;
+ }
+ src_i += consumed_len;
+ }
+ } else {
+ // Dot not preceeded by a slash, copy it literally.
+ pathbuf[dest_i++] = pathbuf[src_i++];
+ }
+ } else if (IsSlash(pathbuf[src_i])) {
+ if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
+ // Two slashes in a row, skip over it.
+ src_i++;
+ } else {
+ // Just one slash, copy it, normalizing to foward slash.
+ pathbuf[dest_i] = '/';
+ dest_i++;
+ src_i++;
+ }
+ } else {
+ // Input nothing special, just copy it.
+ pathbuf[dest_i++] = pathbuf[src_i++];
+ }
+ }
+ path->resize(dest_i);
+}
+
+void ConvertPathToSystem(std::string* path) {
+#if defined(OS_WIN)
+ for (size_t i = 0; i < path->size(); i++) {
+ if ((*path)[i] == '/')
+ (*path)[i] = '\\';
+ }
+#endif
+}
+
+std::string MakeRelativePath(const std::string& input,
+ const std::string& dest) {
+#if defined(OS_WIN)
+ // Make sure that absolute |input| path starts with a slash if |dest| path
+ // does. Otherwise skipping common prefixes won't work properly. Ensure the
+ // same for |dest| path too.
+ if (IsPathAbsolute(input) && !IsSlash(input[0]) && IsSlash(dest[0])) {
+ std::string corrected_input(1, dest[0]);
+ corrected_input.append(input);
+ return MakeRelativePath(corrected_input, dest);
+ }
+ if (IsPathAbsolute(dest) && !IsSlash(dest[0]) && IsSlash(input[0])) {
+ std::string corrected_dest(1, input[0]);
+ corrected_dest.append(dest);
+ return MakeRelativePath(input, corrected_dest);
+ }
+
+ // Make sure that both absolute paths use the same drive letter case.
+ if (IsPathAbsolute(input) && IsPathAbsolute(dest) && input.size() > 1 &&
+ dest.size() > 1) {
+ int letter_pos = base::IsAsciiAlpha(input[0]) ? 0 : 1;
+ if (input[letter_pos] != dest[letter_pos] &&
+ base::ToUpperASCII(input[letter_pos]) ==
+ base::ToUpperASCII(dest[letter_pos])) {
+ std::string corrected_input = input;
+ corrected_input[letter_pos] = dest[letter_pos];
+ return MakeRelativePath(corrected_input, dest);
+ }
+ }
+#endif
+
+ DCHECK(EndsWithSlash(dest));
+ std::string ret;
+
+ // Skip the common prefixes of the source and dest as long as they end in
+ // a [back]slash or end the string. dest always ends with a (back)slash in
+ // this function, so checking dest for just that is sufficient.
+ size_t common_prefix_len = 0;
+ size_t max_common_length = std::min(input.size(), dest.size());
+ for (size_t i = common_prefix_len; i <= max_common_length; i++) {
+ if ((IsSlash(input[i]) || input[i] == '\0') && IsSlash(dest[i]))
+ common_prefix_len = i + 1;
+ else if (input[i] != dest[i])
+ break;
+ }
+
+ // Invert the dest dir starting from the end of the common prefix.
+ for (size_t i = common_prefix_len; i < dest.size(); i++) {
+ if (IsSlash(dest[i]))
+ ret.append("../");
+ }
+
+ // Append any remaining unique input.
+ if (common_prefix_len <= input.size())
+ ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
+ else if (input.back() != '/' && !ret.empty())
+ ret.pop_back();
+
+ // If the result is still empty, the paths are the same.
+ if (ret.empty())
+ ret.push_back('.');
+
+ return ret;
+}
+
+std::string RebasePath(const std::string& input,
+ const SourceDir& dest_dir,
+ const std::string_view& source_root) {
+ std::string ret;
+ DCHECK(source_root.empty() ||
+ !base::EndsWith(source_root, "/", base::CompareCase::SENSITIVE));
+
+ bool input_is_source_path =
+ (input.size() >= 2 && input[0] == '/' && input[1] == '/');
+
+ if (!source_root.empty() &&
+ (!input_is_source_path || !dest_dir.is_source_absolute())) {
+ std::string input_full;
+ std::string dest_full;
+ if (input_is_source_path) {
+ input_full.append(source_root);
+ input_full.push_back('/');
+ input_full.append(input, 2, std::string::npos);
+ } else {
+ input_full.append(input);
+ }
+ if (dest_dir.is_source_absolute()) {
+ dest_full.append(source_root);
+ dest_full.push_back('/');
+ dest_full.append(dest_dir.value(), 2, std::string::npos);
+ } else {
+#if defined(OS_WIN)
+ // On Windows, SourceDir system-absolute paths start
+ // with /, e.g. "/C:/foo/bar".
+ const std::string& value = dest_dir.value();
+ if (value.size() > 2 && value[2] == ':')
+ dest_full.append(dest_dir.value().substr(1));
+ else
+ dest_full.append(dest_dir.value());
+#else
+ dest_full.append(dest_dir.value());
+#endif
+ }
+ bool remove_slash = false;
+ if (!EndsWithSlash(input_full)) {
+ input_full.push_back('/');
+ remove_slash = true;
+ }
+ ret = MakeRelativePath(input_full, dest_full);
+ if (remove_slash && ret.size() > 1)
+ ret.pop_back();
+ return ret;
+ }
+
+ ret = MakeRelativePath(input, dest_dir.value());
+ return ret;
+}
+
+base::FilePath ResolvePath(const std::string& value,
+ bool as_file,
+ const base::FilePath& source_root) {
+ if (value.empty())
+ return base::FilePath();
+
+ std::string converted;
+ if (!IsPathSourceAbsolute(value)) {
+ if (value.size() > 2 && value[2] == ':') {
+ // Windows path, strip the leading slash.
+ converted.assign(&value[1], value.size() - 1);
+ } else {
+ converted.assign(value);
+ }
+ return base::FilePath(UTF8ToFilePath(converted));
+ }
+
+ // String the double-leading slash for source-relative paths.
+ converted.assign(&value[2], value.size() - 2);
+
+ if (as_file && source_root.empty())
+ return UTF8ToFilePath(converted).NormalizePathSeparatorsTo('/');
+
+ return source_root.Append(UTF8ToFilePath(converted))
+ .NormalizePathSeparatorsTo('/');
+}
+
+template <typename StringType>
+std::string ResolveRelative(const StringType& input,
+ const std::string& value,
+ bool as_file,
+ const std::string_view& source_root) {
+ std::string result;
+
+ if (input.size() >= 2 && input[0] == '/' && input[1] == '/') {
+ // Source-relative.
+ result.assign(input.data(), input.size());
+ if (!as_file) {
+ if (!EndsWithSlash(result))
+ result.push_back('/');
+ }
+ NormalizePath(&result, source_root);
+ return result;
+ } else if (IsPathAbsolute(input)) {
+ if (source_root.empty() ||
+ !MakeAbsolutePathRelativeIfPossible(source_root, input, &result)) {
+#if defined(OS_WIN)
+ if (input[0] != '/') // See the file case for why we do this check.
+ result = "/";
+#endif
+ result.append(input.data(), input.size());
+ }
+ NormalizePath(&result);
+ if (!as_file) {
+ if (!EndsWithSlash(result))
+ result.push_back('/');
+ }
+ return result;
+ }
+
+ if (!source_root.empty()) {
+ std::string absolute =
+ FilePathToUTF8(ResolvePath(value, as_file, UTF8ToFilePath(source_root))
+ .AppendASCII(input)
+ .value());
+ NormalizePath(&absolute);
+ if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute, &result)) {
+#if defined(OS_WIN)
+ if (absolute[0] != '/') // See the file case for why we do this check.
+ result = "/";
+#endif
+ result.append(absolute.data(), absolute.size());
+ }
+ if (!as_file && !EndsWithSlash(result))
+ result.push_back('/');
+ return result;
+ }
+
+ // With no source_root, there's nothing we can do about
+ // e.g. input=../../../path/to/file and value=//source and we'll
+ // errornously return //file.
+ result.reserve(value.size() + input.size());
+ result.assign(value);
+ result.append(input.data(), input.size());
+
+ NormalizePath(&result);
+ if (!as_file && !EndsWithSlash(result))
+ result.push_back('/');
+
+ return result;
+}
+
+// Explicit template instantiation
+template std::string ResolveRelative(const std::string_view& input,
+ const std::string& value,
+ bool as_file,
+ const std::string_view& source_root);
+
+template std::string ResolveRelative(const std::string& input,
+ const std::string& value,
+ bool as_file,
+ const std::string_view& source_root);
+
+std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
+ std::string ret;
+
+ if (dir.value().empty()) {
+ // Just keep input the same.
+ } else if (dir.value() == "/") {
+ ret.assign("/.");
+ } else if (dir.value() == "//") {
+ ret.assign("//.");
+ } else {
+ ret.assign(dir.value());
+ ret.resize(ret.size() - 1);
+ }
+ return ret;
+}
+
+SourceDir SourceDirForPath(const base::FilePath& source_root,
+ const base::FilePath& path) {
+ std::vector<base::FilePath::StringType> source_comp =
+ GetPathComponents(source_root);
+ std::vector<base::FilePath::StringType> path_comp = GetPathComponents(path);
+
+ // See if path is inside the source root by looking for each of source root's
+ // components at the beginning of path.
+ bool is_inside_source;
+ if (path_comp.size() < source_comp.size() || source_root.empty()) {
+ // Too small to fit.
+ is_inside_source = false;
+ } else {
+ is_inside_source = true;
+ for (size_t i = 0; i < source_comp.size(); i++) {
+ if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
+ is_inside_source = false;
+ break;
+ }
+ }
+ }
+
+ std::string result_str;
+ size_t initial_path_comp_to_use;
+ if (is_inside_source) {
+ // Construct a source-relative path beginning in // and skip all of the
+ // shared directories.
+ result_str = "//";
+ initial_path_comp_to_use = source_comp.size();
+ } else {
+ // Not inside source code, construct a system-absolute path.
+ result_str = "/";
+ initial_path_comp_to_use = 0;
+ }
+
+ for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
+ result_str.append(FilePathToUTF8(path_comp[i]));
+ result_str.push_back('/');
+ }
+ return SourceDir(std::move(result_str));
+}
+
+SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
+ base::FilePath cd;
+ base::GetCurrentDirectory(&cd);
+ return SourceDirForPath(source_root, cd);
+}
+
+std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
+ // The default toolchain has no subdir.
+ if (is_default)
+ return std::string();
+
+ // For now just assume the toolchain name is always a valid dir name. We may
+ // want to clean up the in the future.
+ return toolchain_label.name() + "/";
+}
+
+bool ContentsEqual(const base::FilePath& file_path, const std::string& data) {
+ // Compare file and stream sizes first. Quick and will save us some time if
+ // they are different sizes.
+ int64_t file_size;
+ if (!base::GetFileSize(file_path, &file_size) ||
+ static_cast<size_t>(file_size) != data.size()) {
+ return false;
+ }
+
+ std::string file_data;
+ file_data.resize(file_size);
+ if (!base::ReadFileToString(file_path, &file_data))
+ return false;
+
+ return file_data == data;
+}
+
+bool WriteFileIfChanged(const base::FilePath& file_path,
+ const std::string& data,
+ Err* err) {
+ if (ContentsEqual(file_path, data))
+ return true;
+
+ return WriteFile(file_path, data, err);
+}
+
+bool WriteFile(const base::FilePath& file_path,
+ const std::string& data,
+ Err* err) {
+ // Create the directory if necessary.
+ if (!base::CreateDirectory(file_path.DirName())) {
+ if (err) {
+ *err =
+ Err(Location(), "Unable to create directory.",
+ "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
+ }
+ return false;
+ }
+
+ FileWriter writer;
+ writer.Create(file_path);
+ writer.Write(data);
+ bool write_success = writer.Close();
+
+ if (!write_success && err) {
+ *err = Err(Location(), "Unable to write file.",
+ "I was writing \"" + FilePathToUTF8(file_path) + "\".");
+ }
+
+ return write_success;
+}
+
+BuildDirContext::BuildDirContext(const Target* target)
+ : BuildDirContext(target->settings()) {}
+
+BuildDirContext::BuildDirContext(const Settings* settings)
+ : BuildDirContext(settings->build_settings(),
+ settings->toolchain_label(),
+ settings->is_default()) {}
+
+BuildDirContext::BuildDirContext(const Scope* execution_scope)
+ : BuildDirContext(execution_scope->settings()) {}
+
+BuildDirContext::BuildDirContext(const Scope* execution_scope,
+ const Label& toolchain_label)
+ : BuildDirContext(execution_scope->settings()->build_settings(),
+ toolchain_label,
+ execution_scope->settings()->default_toolchain_label() ==
+ toolchain_label) {}
+
+BuildDirContext::BuildDirContext(const BuildSettings* in_build_settings,
+ const Label& in_toolchain_label,
+ bool in_is_default_toolchain)
+ : build_settings(in_build_settings),
+ toolchain_label(in_toolchain_label),
+ is_default_toolchain(in_is_default_toolchain) {}
+
+SourceDir GetBuildDirAsSourceDir(const BuildDirContext& context,
+ BuildDirType type) {
+ return GetBuildDirAsOutputFile(context, type)
+ .AsSourceDir(context.build_settings);
+}
+
+OutputFile GetBuildDirAsOutputFile(const BuildDirContext& context,
+ BuildDirType type) {
+ OutputFile result(GetOutputSubdirName(context.toolchain_label,
+ context.is_default_toolchain));
+ DCHECK(result.value().empty() || result.value().back() == '/');
+
+ if (type == BuildDirType::GEN)
+ result.value().append("gen/");
+ else if (type == BuildDirType::OBJ)
+ result.value().append("obj/");
+ return result;
+}
+
+SourceDir GetSubBuildDirAsSourceDir(const BuildDirContext& context,
+ const SourceDir& source_dir,
+ BuildDirType type) {
+ return GetSubBuildDirAsOutputFile(context, source_dir, type)
+ .AsSourceDir(context.build_settings);
+}
+
+OutputFile GetSubBuildDirAsOutputFile(const BuildDirContext& context,
+ const SourceDir& source_dir,
+ BuildDirType type) {
+ DCHECK(type != BuildDirType::TOOLCHAIN_ROOT);
+ OutputFile result = GetBuildDirAsOutputFile(context, type);
+
+ if (source_dir.is_source_absolute()) {
+ // The source dir is source-absolute, so we trim off the two leading
+ // slashes to append to the toolchain object directory.
+ result.value().append(&source_dir.value()[2],
+ source_dir.value().size() - 2);
+ } else {
+ // System-absolute.
+ AppendFixedAbsolutePathSuffix(context.build_settings, source_dir, &result);
+ }
+ return result;
+}
+
+SourceDir GetBuildDirForTargetAsSourceDir(const Target* target,
+ BuildDirType type) {
+ return GetSubBuildDirAsSourceDir(BuildDirContext(target),
+ target->label().dir(), type);
+}
+
+OutputFile GetBuildDirForTargetAsOutputFile(const Target* target,
+ BuildDirType type) {
+ return GetSubBuildDirAsOutputFile(BuildDirContext(target),
+ target->label().dir(), type);
+}
+
+SourceDir GetScopeCurrentBuildDirAsSourceDir(const Scope* scope,
+ BuildDirType type) {
+ if (type == BuildDirType::TOOLCHAIN_ROOT)
+ return GetBuildDirAsSourceDir(BuildDirContext(scope), type);
+ return GetSubBuildDirAsSourceDir(BuildDirContext(scope),
+ scope->GetSourceDir(), type);
+}
diff --git a/gn/src/gn/filesystem_utils.h b/gn/src/gn/filesystem_utils.h
new file mode 100644
index 00000000000..830478a5fcd
--- /dev/null
+++ b/gn/src/gn/filesystem_utils.h
@@ -0,0 +1,305 @@
+// 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 TOOLS_GN_FILESYSTEM_UTILS_H_
+#define TOOLS_GN_FILESYSTEM_UTILS_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <string_view>
+
+#include "base/files/file_path.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+
+class Err;
+
+std::string FilePathToUTF8(const base::FilePath::StringType& str);
+inline std::string FilePathToUTF8(const base::FilePath& path) {
+ return FilePathToUTF8(path.value());
+}
+base::FilePath UTF8ToFilePath(const std::string_view& sp);
+
+// Extensions -----------------------------------------------------------------
+
+// Returns the index of the extension (character after the last dot not after a
+// slash). Returns std::string::npos if not found. Returns path.size() if the
+// file ends with a dot.
+size_t FindExtensionOffset(const std::string& path);
+
+// Returns a string piece pointing into the input string identifying the
+// extension. Note that the input pointer must outlive the output.
+std::string_view FindExtension(const std::string* path);
+
+// Filename parts -------------------------------------------------------------
+
+// Returns the offset of the character following the last slash, or
+// 0 if no slash was found. Returns path.size() if the path ends with a slash.
+// Note that the input pointer must outlive the output.
+size_t FindFilenameOffset(const std::string& path);
+
+// Returns a string piece pointing into the input string identifying the
+// file name (following the last slash, including the extension). Note that the
+// input pointer must outlive the output.
+std::string_view FindFilename(const std::string* path);
+
+// Like FindFilename but does not include the extension.
+std::string_view FindFilenameNoExtension(const std::string* path);
+
+// Removes everything after the last slash. The last slash, if any, will be
+// preserved.
+void RemoveFilename(std::string* path);
+
+// Returns if the given character is a slash. This allows both slashes and
+// backslashes for consistency between Posix and Windows (as opposed to
+// FilePath::IsSeparator which is based on the current platform).
+inline bool IsSlash(const char ch) {
+ return ch == '/' || ch == '\\';
+}
+
+// Returns true if the given path ends with a slash.
+bool EndsWithSlash(const std::string_view s);
+
+// Path parts -----------------------------------------------------------------
+
+// Returns a string piece pointing into the input string identifying the
+// directory name of the given path, including the last slash. Note that the
+// input pointer must outlive the output.
+std::string_view FindDir(const std::string* path);
+
+// Returns the substring identifying the last component of the dir, or the
+// empty substring if none. For example "//foo/bar/" -> "bar".
+std::string_view FindLastDirComponent(const SourceDir& dir);
+
+// Returns true if the given string is in the given output dir. This is pretty
+// stupid and doesn't handle "." and "..", etc., it is designed for a sanity
+// check to keep people from writing output files to the source directory
+// accidentally.
+bool IsStringInOutputDir(const SourceDir& output_dir, const std::string& str);
+
+// Verifies that the given string references a file inside of the given
+// directory. This just uses IsStringInOutputDir above.
+//
+// The origin will be blamed in the error.
+//
+// If the file isn't in the dir, returns false and sets the error. Otherwise
+// returns true and leaves the error untouched.
+bool EnsureStringIsInOutputDir(const SourceDir& output_dir,
+ const std::string& str,
+ const ParseNode* origin,
+ Err* err);
+
+// ----------------------------------------------------------------------------
+
+// Returns true if the input string is absolute. Double-slashes at the
+// beginning are treated as source-relative paths. On Windows, this handles
+// paths of both the native format: "C:/foo" and ours "/C:/foo"
+bool IsPathAbsolute(const std::string_view& path);
+
+// Returns true if the input string is source-absolute. Source-absolute
+// paths begin with two forward slashes and resolve as if they are
+// relative to the source root.
+bool IsPathSourceAbsolute(const std::string_view& path);
+
+// Given an absolute path, checks to see if is it is inside the source root.
+// If it is, fills a source-absolute path into the given output and returns
+// true. If it isn't, clears the dest and returns false.
+//
+// The source_root should be a base::FilePath converted to UTF-8. On Windows,
+// it should begin with a "C:/" rather than being our SourceFile's style
+// ("/C:/"). The source root can end with a slash or not.
+//
+// Note that this does not attempt to normalize slashes in the output.
+bool MakeAbsolutePathRelativeIfPossible(const std::string_view& source_root,
+ const std::string_view& path,
+ std::string* dest);
+
+// Given two absolute paths |base| and |target|, returns a relative path to
+// |target| as if the current directory was |base|. The relative path returned
+// is minimal. For example, if "../../a/b/" and "../b" are both valid, then the
+// latter will be returned. On Windows, it's impossible to have a relative path
+// from C:\foo to D:\bar, so the absolute path |target| is returned instead for
+// this case.
+base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
+ const base::FilePath& base,
+ const base::FilePath& target);
+
+// Collapses "." and sequential "/"s and evaluates "..". |path| may be
+// system-absolute, source-absolute, or relative. If |path| is source-absolute
+// and |source_root| is non-empty, |path| may be system absolute after this
+// function returns, if |path| references the filesystem outside of
+// |source_root| (ex. path = "//.."). In this case on Windows, |path| will have
+// a leading slash. Otherwise, |path| will retain its relativity. |source_root|
+// must not end with a slash.
+void NormalizePath(std::string* path,
+ const std::string_view& source_root = std::string_view());
+
+// Converts slashes to backslashes for Windows. Keeps the string unchanged
+// for other systems.
+void ConvertPathToSystem(std::string* path);
+
+// Takes a path, |input|, and makes it relative to the given directory
+// |dest_dir|. Both inputs may be source-relative (e.g. begins with
+// with "//") or may be absolute.
+//
+// If supplied, the |source_root| parameter is the absolute path to
+// the source root and not end in a slash. Unless you know that the
+// inputs are always source relative, this should be supplied.
+std::string RebasePath(
+ const std::string& input,
+ const SourceDir& dest_dir,
+ const std::string_view& source_root = std::string_view());
+
+// Resolves a file or dir name (parameter input) relative to
+// value directory. Will return an empty SourceDir/File on error
+// and set the give *err pointer (required). Empty input is always an error.
+// Returned value can be used to set value in either SourceFile or SourceDir
+// (based on as_file parameter).
+//
+// Parameter as_file defines whether result path will look like a file path
+// or it should be treated as a directory (contains "/" and the end
+// of the string).
+//
+// If source_root is supplied, these functions will additionally handle the
+// case where the input is a system-absolute but still inside the source
+// tree. This is the case for some external tools.
+template <typename StringType>
+std::string ResolveRelative(const StringType& input,
+ const std::string& value,
+ bool as_file,
+ const std::string_view& source_root);
+
+// Resolves source file or directory relative to some given source root. Returns
+// an empty file path on error.
+base::FilePath ResolvePath(const std::string& value,
+ bool as_file,
+ const base::FilePath& source_root);
+
+// Returns the given directory with no terminating slash at the end, such that
+// appending a slash and more stuff will produce a valid path.
+//
+// If the directory refers to either the source or system root, we'll append
+// a "." so this remains valid.
+std::string DirectoryWithNoLastSlash(const SourceDir& dir);
+
+// Returns the "best" SourceDir representing the given path. If it's inside the
+// given source_root, a source-relative directory will be returned (e.g.
+// "//foo/bar.cc". If it's outside of the source root or the source root is
+// empty, a system-absolute directory will be returned.
+SourceDir SourceDirForPath(const base::FilePath& source_root,
+ const base::FilePath& path);
+
+// Like SourceDirForPath but returns the SourceDir representing the current
+// directory.
+SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root);
+
+// Given the label of a toolchain and whether that toolchain is the default
+// toolchain, returns the name of the subdirectory for that toolchain's
+// output. This will be the empty string to indicate that the toolchain outputs
+// go in the root build directory. Otherwise, the result will end in a slash.
+std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default);
+
+// Returns true if the contents of the file and stream given are equal, false
+// otherwise.
+bool ContentsEqual(const base::FilePath& file_path, const std::string& data);
+
+// Writes given stream contents to the given file if it differs from existing
+// file contents. Returns true if new contents was successfully written or
+// existing file contents doesn't need updating, false on write error. |err| is
+// set on write error if not nullptr.
+bool WriteFileIfChanged(const base::FilePath& file_path,
+ const std::string& data,
+ Err* err);
+
+// Writes given stream contents to the given file. Returns true if data was
+// successfully written, false otherwise. |err| is set on error if not nullptr.
+bool WriteFile(const base::FilePath& file_path,
+ const std::string& data,
+ Err* err);
+
+// -----------------------------------------------------------------------------
+
+enum class BuildDirType {
+ // Returns the root toolchain dir rather than the generated or output
+ // subdirectories. This is valid only for the toolchain directory getters.
+ // Asking for this for a target or source dir makes no sense.
+ TOOLCHAIN_ROOT,
+
+ // Generated file directory.
+ GEN,
+
+ // Output file directory.
+ OBJ,
+};
+
+// In different contexts, different information is known about the toolchain in
+// question. If you have a Target or settings object, everything can be
+// extracted from there. But when querying label information on something in
+// another toolchain, for example, the only thing known (it may not even exist)
+// is the toolchain label string and whether it matches the default toolchain.
+//
+// This object extracts the relevant information from a variety of input
+// types for the convenience of the caller.
+class BuildDirContext {
+ public:
+ // Extracts toolchain information associated with the given target.
+ explicit BuildDirContext(const Target* target);
+
+ // Extracts toolchain information associated with the given settings object.
+ explicit BuildDirContext(const Settings* settings);
+
+ // Extrats toolchain information from the current toolchain of the scope.
+ explicit BuildDirContext(const Scope* execution_scope);
+
+ // Extracts the default toolchain information from the given execution
+ // scope. The toolchain you want to query must be passed in. This doesn't
+ // use the settings object from the Scope so one can query other toolchains.
+ // If you want to use the scope's current toolchain, use the version above.
+ BuildDirContext(const Scope* execution_scope, const Label& toolchain_label);
+
+ // Specify all information manually.
+ BuildDirContext(const BuildSettings* build_settings,
+ const Label& toolchain_label,
+ bool is_default_toolchain);
+
+ const BuildSettings* build_settings;
+ const Label& toolchain_label;
+ bool is_default_toolchain;
+};
+
+// Returns the root, object, or generated file directory for the toolchain.
+//
+// The toolchain object file root is never exposed in GN (there is no
+// root_obj_dir variable) so BuildDirType::OBJ would normally never be passed
+// to this function except when it's called by one of the variants below that
+// append paths to it.
+SourceDir GetBuildDirAsSourceDir(const BuildDirContext& context,
+ BuildDirType type);
+OutputFile GetBuildDirAsOutputFile(const BuildDirContext& context,
+ BuildDirType type);
+
+// Returns the output or generated file directory corresponding to the given
+// source directory.
+SourceDir GetSubBuildDirAsSourceDir(const BuildDirContext& context,
+ const SourceDir& source_dir,
+ BuildDirType type);
+OutputFile GetSubBuildDirAsOutputFile(const BuildDirContext& context,
+ const SourceDir& source_dir,
+ BuildDirType type);
+
+// Returns the output or generated file directory corresponding to the given
+// target.
+SourceDir GetBuildDirForTargetAsSourceDir(const Target* target,
+ BuildDirType type);
+OutputFile GetBuildDirForTargetAsOutputFile(const Target* target,
+ BuildDirType type);
+
+// Returns the scope's current directory.
+SourceDir GetScopeCurrentBuildDirAsSourceDir(const Scope* scope,
+ BuildDirType type);
+// Lack of OutputDir version is due only to it not currently being needed,
+// please add one if you need it.
+
+#endif // TOOLS_GN_FILESYSTEM_UTILS_H_
diff --git a/gn/src/gn/filesystem_utils_unittest.cc b/gn/src/gn/filesystem_utils_unittest.cc
new file mode 100644
index 00000000000..347d0fec097
--- /dev/null
+++ b/gn/src/gn/filesystem_utils_unittest.cc
@@ -0,0 +1,868 @@
+// 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 "gn/filesystem_utils.h"
+
+#include <thread>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/target.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(FilesystemUtils, FileExtensionOffset) {
+ EXPECT_EQ(std::string::npos, FindExtensionOffset(""));
+ EXPECT_EQ(std::string::npos, FindExtensionOffset("foo/bar/baz"));
+ EXPECT_EQ(4u, FindExtensionOffset("foo."));
+ EXPECT_EQ(4u, FindExtensionOffset("f.o.bar"));
+ EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/"));
+ EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/baz"));
+}
+
+TEST(FilesystemUtils, FindExtension) {
+ std::string input;
+ EXPECT_EQ("", FindExtension(&input));
+ input = "foo/bar/baz";
+ EXPECT_EQ("", FindExtension(&input));
+ input = "foo.";
+ EXPECT_EQ("", FindExtension(&input));
+ input = "f.o.bar";
+ EXPECT_EQ("bar", FindExtension(&input));
+ input = "foo.bar/";
+ EXPECT_EQ("", FindExtension(&input));
+ input = "foo.bar/baz";
+ EXPECT_EQ("", FindExtension(&input));
+}
+
+TEST(FilesystemUtils, FindFilenameOffset) {
+ EXPECT_EQ(0u, FindFilenameOffset(""));
+ EXPECT_EQ(0u, FindFilenameOffset("foo"));
+ EXPECT_EQ(4u, FindFilenameOffset("foo/"));
+ EXPECT_EQ(4u, FindFilenameOffset("foo/bar"));
+}
+
+TEST(FilesystemUtils, RemoveFilename) {
+ std::string s;
+
+ RemoveFilename(&s);
+ EXPECT_STREQ("", s.c_str());
+
+ s = "foo";
+ RemoveFilename(&s);
+ EXPECT_STREQ("", s.c_str());
+
+ s = "/";
+ RemoveFilename(&s);
+ EXPECT_STREQ("/", s.c_str());
+
+ s = "foo/bar";
+ RemoveFilename(&s);
+ EXPECT_STREQ("foo/", s.c_str());
+
+ s = "foo/bar/baz.cc";
+ RemoveFilename(&s);
+ EXPECT_STREQ("foo/bar/", s.c_str());
+}
+
+TEST(FilesystemUtils, FindDir) {
+ std::string input;
+ EXPECT_EQ("", FindDir(&input));
+ input = "/";
+ EXPECT_EQ("/", FindDir(&input));
+ input = "foo/";
+ EXPECT_EQ("foo/", FindDir(&input));
+ input = "foo/bar/baz";
+ EXPECT_EQ("foo/bar/", FindDir(&input));
+}
+
+TEST(FilesystemUtils, FindLastDirComponent) {
+ SourceDir empty;
+ EXPECT_EQ("", FindLastDirComponent(empty));
+
+ SourceDir root("/");
+ EXPECT_EQ("", FindLastDirComponent(root));
+
+ SourceDir srcroot("//");
+ EXPECT_EQ("", FindLastDirComponent(srcroot));
+
+ SourceDir regular1("//foo/");
+ EXPECT_EQ("foo", FindLastDirComponent(regular1));
+
+ SourceDir regular2("//foo/bar/");
+ EXPECT_EQ("bar", FindLastDirComponent(regular2));
+}
+
+TEST(FilesystemUtils, EnsureStringIsInOutputDir) {
+ SourceDir output_dir("//out/Debug/");
+
+ // Some outside.
+ Err err;
+ EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "//foo", nullptr, &err));
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+ EXPECT_FALSE(
+ EnsureStringIsInOutputDir(output_dir, "//out/Debugit", nullptr, &err));
+ EXPECT_TRUE(err.has_error());
+
+ // Some inside.
+ err = Err();
+ EXPECT_TRUE(
+ EnsureStringIsInOutputDir(output_dir, "//out/Debug/", nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(
+ EnsureStringIsInOutputDir(output_dir, "//out/Debug/foo", nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+
+ // Pattern but no template expansions are allowed.
+ EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "{{source_gen_dir}}",
+ nullptr, &err));
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilesystemUtils, IsPathAbsolute) {
+ EXPECT_TRUE(IsPathAbsolute("/foo/bar"));
+ EXPECT_TRUE(IsPathAbsolute("/"));
+ EXPECT_FALSE(IsPathAbsolute(""));
+ EXPECT_FALSE(IsPathAbsolute("//"));
+ EXPECT_FALSE(IsPathAbsolute("//foo/bar"));
+
+#if defined(OS_WIN)
+ EXPECT_TRUE(IsPathAbsolute("C:/foo"));
+ EXPECT_TRUE(IsPathAbsolute("C:/"));
+ EXPECT_TRUE(IsPathAbsolute("C:\\foo"));
+ EXPECT_TRUE(IsPathAbsolute("C:\\"));
+ EXPECT_TRUE(IsPathAbsolute("/C:/foo"));
+ EXPECT_TRUE(IsPathAbsolute("/C:\\foo"));
+#endif
+}
+
+TEST(FilesystemUtils, MakeAbsolutePathRelativeIfPossible) {
+ std::string dest;
+
+#if defined(OS_WIN)
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("C:\\base", "C:\\base\\foo", &dest));
+ EXPECT_EQ("//foo", dest);
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("C:\\base", "/C:/base/foo", &dest));
+ EXPECT_EQ("//foo", dest);
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("c:\\base", "C:\\base\\foo\\", &dest));
+ EXPECT_EQ("//foo\\", dest);
+
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("C:\\base", "C:\\ba", &dest));
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("C:\\base",
+ "C:\\/notbase/foo", &dest));
+#else
+
+ EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("/base", "/base/foo/", &dest));
+ EXPECT_EQ("//foo/", dest);
+ EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("/base", "/base/foo", &dest));
+ EXPECT_EQ("//foo", dest);
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("/base/", "/base/foo/", &dest));
+ EXPECT_EQ("//foo/", dest);
+
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("/base", "/ba", &dest));
+ EXPECT_FALSE(
+ MakeAbsolutePathRelativeIfPossible("/base", "/notbase/foo", &dest));
+#endif
+}
+
+TEST(FilesystemUtils, MakeAbsoluteFilePathRelativeIfPossible) {
+#if defined(OS_WIN)
+ EXPECT_EQ(
+ base::FilePath(u"out\\Debug"),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath(u"C:\\src"), base::FilePath(u"C:\\src\\out\\Debug")));
+ EXPECT_EQ(base::FilePath(u".\\gn"),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath(u"C:\\src\\out\\Debug"),
+ base::FilePath(u"C:\\src\\out\\Debug\\gn")));
+ EXPECT_EQ(
+ base::FilePath(u"..\\.."),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath(u"C:\\src\\out\\Debug"), base::FilePath(u"C:\\src")));
+ EXPECT_EQ(
+ base::FilePath(u"..\\.."),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath(u"C:\\src\\out\\Debug"), base::FilePath(u"C:/src")));
+ EXPECT_EQ(base::FilePath(u"."),
+ MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(u"C:\\src"),
+ base::FilePath(u"C:\\src")));
+ EXPECT_EQ(base::FilePath(u"..\\..\\..\\u\\v\\w"),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath(u"C:\\a\\b\\c\\x\\y\\z"),
+ base::FilePath(u"C:\\a\\b\\c\\u\\v\\w")));
+ EXPECT_EQ(base::FilePath(u"D:\\bar"),
+ MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(u"C:\\foo"),
+ base::FilePath(u"D:\\bar")));
+#else
+ EXPECT_EQ(base::FilePath("out/Debug"),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath("/src"), base::FilePath("/src/out/Debug")));
+ EXPECT_EQ(base::FilePath("./gn"), MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath("/src/out/Debug"),
+ base::FilePath("/src/out/Debug/gn")));
+ EXPECT_EQ(base::FilePath("../.."),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath("/src/out/Debug"), base::FilePath("/src")));
+ EXPECT_EQ(base::FilePath("."),
+ MakeAbsoluteFilePathRelativeIfPossible(base::FilePath("/src"),
+ base::FilePath("/src")));
+ EXPECT_EQ(
+ base::FilePath("../../../u/v/w"),
+ MakeAbsoluteFilePathRelativeIfPossible(base::FilePath("/a/b/c/x/y/z"),
+ base::FilePath("/a/b/c/u/v/w")));
+#endif
+}
+
+TEST(FilesystemUtils, NormalizePath) {
+ std::string input;
+
+ NormalizePath(&input);
+ EXPECT_EQ("", input);
+
+ input = "foo/bar.txt";
+ NormalizePath(&input);
+ EXPECT_EQ("foo/bar.txt", input);
+
+ input = ".";
+ NormalizePath(&input);
+ EXPECT_EQ("", input);
+
+ input = "..";
+ NormalizePath(&input);
+ EXPECT_EQ("..", input);
+
+ input = "foo//bar";
+ NormalizePath(&input);
+ EXPECT_EQ("foo/bar", input);
+
+ input = "//foo";
+ NormalizePath(&input);
+ EXPECT_EQ("//foo", input);
+
+ input = "foo/..//bar";
+ NormalizePath(&input);
+ EXPECT_EQ("bar", input);
+
+ input = "foo/../../bar";
+ NormalizePath(&input);
+ EXPECT_EQ("../bar", input);
+
+ input = "/../foo"; // Don't go above the root dir.
+ NormalizePath(&input);
+ EXPECT_EQ("/foo", input);
+
+ input = "//../foo"; // Don't go above the root dir.
+ NormalizePath(&input);
+ EXPECT_EQ("//foo", input);
+
+ input = "../foo";
+ NormalizePath(&input);
+ EXPECT_EQ("../foo", input);
+
+ input = "..";
+ NormalizePath(&input);
+ EXPECT_EQ("..", input);
+
+ input = "./././.";
+ NormalizePath(&input);
+ EXPECT_EQ("", input);
+
+ input = "../../..";
+ NormalizePath(&input);
+ EXPECT_EQ("../../..", input);
+
+ input = "../";
+ NormalizePath(&input);
+ EXPECT_EQ("../", input);
+
+ // Backslash normalization.
+ input = "foo\\..\\..\\bar";
+ NormalizePath(&input);
+ EXPECT_EQ("../bar", input);
+
+ // Trailing slashes should get preserved.
+ input = "//foo/bar/";
+ NormalizePath(&input);
+ EXPECT_EQ("//foo/bar/", input);
+
+#if defined(OS_WIN)
+ // Go above and outside of the source root.
+ input = "//../foo";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/C:/source/foo", input);
+
+ input = "//../foo";
+ NormalizePath(&input, "C:\\source\\root");
+ EXPECT_EQ("/C:/source/foo", input);
+
+ input = "//../";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/C:/source/", input);
+
+ input = "//../foo.txt";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/C:/source/foo.txt", input);
+
+ input = "//../foo/bar/";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/C:/source/foo/bar/", input);
+
+ // Go above and back into the source root. This should return a system-
+ // absolute path. We could arguably return this as a source-absolute path,
+ // but that would require additional handling to account for a rare edge
+ // case.
+ input = "//../root/foo";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/C:/source/root/foo", input);
+
+ input = "//../root/foo/bar/";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/C:/source/root/foo/bar/", input);
+
+ // Stay inside the source root
+ input = "//foo/bar";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("//foo/bar", input);
+
+ input = "//foo/bar/";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("//foo/bar/", input);
+
+ // The path should not go above the system root. Note that on Windows, this
+ // will consume the drive (C:).
+ input = "//../../../../../foo/bar";
+ NormalizePath(&input, "/C:/source/root");
+ EXPECT_EQ("/foo/bar", input);
+
+ // Test when the source root is the letter drive.
+ input = "//../foo";
+ NormalizePath(&input, "/C:");
+ EXPECT_EQ("/foo", input);
+
+ input = "//../foo";
+ NormalizePath(&input, "C:");
+ EXPECT_EQ("/foo", input);
+
+ input = "//../foo";
+ NormalizePath(&input, "/");
+ EXPECT_EQ("/foo", input);
+
+ input = "//../";
+ NormalizePath(&input, "\\C:");
+ EXPECT_EQ("/", input);
+
+ input = "//../foo.txt";
+ NormalizePath(&input, "/C:");
+ EXPECT_EQ("/foo.txt", input);
+#else
+ // Go above and outside of the source root.
+ input = "//../foo";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/source/foo", input);
+
+ input = "//../";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/source/", input);
+
+ input = "//../foo.txt";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/source/foo.txt", input);
+
+ input = "//../foo/bar/";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/source/foo/bar/", input);
+
+ // Go above and back into the source root. This should return a system-
+ // absolute path. We could arguably return this as a source-absolute path,
+ // but that would require additional handling to account for a rare edge
+ // case.
+ input = "//../root/foo";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/source/root/foo", input);
+
+ input = "//../root/foo/bar/";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/source/root/foo/bar/", input);
+
+ // Stay inside the source root
+ input = "//foo/bar";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("//foo/bar", input);
+
+ input = "//foo/bar/";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("//foo/bar/", input);
+
+ // The path should not go above the system root.
+ input = "//../../../../../foo/bar";
+ NormalizePath(&input, "/source/root");
+ EXPECT_EQ("/foo/bar", input);
+
+ // Test when the source root is the system root.
+ input = "//../foo/bar/";
+ NormalizePath(&input, "/");
+ EXPECT_EQ("/foo/bar/", input);
+
+ input = "//../";
+ NormalizePath(&input, "/");
+ EXPECT_EQ("/", input);
+
+ input = "//../foo.txt";
+ NormalizePath(&input, "/");
+ EXPECT_EQ("/foo.txt", input);
+
+#endif
+}
+
+TEST(FilesystemUtils, RebasePath) {
+ std::string_view source_root("/source/root");
+
+ // Degenerate case.
+ EXPECT_EQ(".", RebasePath("//", SourceDir("//"), source_root));
+ EXPECT_EQ(".",
+ RebasePath("//foo/bar/", SourceDir("//foo/bar/"), source_root));
+
+ // Going up the tree.
+ EXPECT_EQ("../foo", RebasePath("//foo", SourceDir("//bar/"), source_root));
+ EXPECT_EQ("../foo/", RebasePath("//foo/", SourceDir("//bar/"), source_root));
+ EXPECT_EQ("../../foo",
+ RebasePath("//foo", SourceDir("//bar/moo"), source_root));
+ EXPECT_EQ("../../foo/",
+ RebasePath("//foo/", SourceDir("//bar/moo"), source_root));
+
+ // Going down the tree.
+ EXPECT_EQ("foo/bar", RebasePath("//foo/bar", SourceDir("//"), source_root));
+ EXPECT_EQ("foo/bar/", RebasePath("//foo/bar/", SourceDir("//"), source_root));
+
+ // Going up and down the tree.
+ EXPECT_EQ("../../foo/bar",
+ RebasePath("//foo/bar", SourceDir("//a/b/"), source_root));
+ EXPECT_EQ("../../foo/bar/",
+ RebasePath("//foo/bar/", SourceDir("//a/b/"), source_root));
+
+ // Sharing prefix.
+ EXPECT_EQ("foo", RebasePath("//a/foo", SourceDir("//a/"), source_root));
+ EXPECT_EQ("foo", RebasePath("//a/foo", SourceDir("//a"), source_root));
+ EXPECT_EQ("foo/", RebasePath("//a/foo/", SourceDir("//a/"), source_root));
+ EXPECT_EQ("foo", RebasePath("//a/b/foo", SourceDir("//a/b/"), source_root));
+ EXPECT_EQ("foo/", RebasePath("//a/b/foo/", SourceDir("//a/b/"), source_root));
+ EXPECT_EQ("foo/bar",
+ RebasePath("//a/b/foo/bar", SourceDir("//a/b/"), source_root));
+ EXPECT_EQ("foo/bar/",
+ RebasePath("//a/b/foo/bar/", SourceDir("//a/b/"), source_root));
+ EXPECT_EQ(".",
+ RebasePath("//foo/bar", SourceDir("//foo/bar/"), source_root));
+ EXPECT_EQ("..",
+ RebasePath("//foo", SourceDir("//foo/bar/"), source_root));
+ EXPECT_EQ("../",
+ RebasePath("//foo/", SourceDir("//foo/bar/"), source_root));
+
+ // Check when only |input| is system-absolute
+ EXPECT_EQ("foo", RebasePath("/source/root/foo", SourceDir("//"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("foo/", RebasePath("/source/root/foo/", SourceDir("//"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("//"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("//"),
+ std::string_view("/source/root/foo")));
+ EXPECT_EQ("../../../builddir/Out/Debug/",
+ RebasePath("/builddir/Out/Debug/", SourceDir("//"),
+ std::string_view("/source/root/foo")));
+ EXPECT_EQ("../../path/to/foo", RebasePath("/path/to/foo", SourceDir("//"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("//a"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("//a/b"),
+ std::string_view("/source/root")));
+
+ // Check when only |dest_dir| is system-absolute.
+ EXPECT_EQ(".", RebasePath("//", SourceDir("/source/root"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("foo", RebasePath("//foo", SourceDir("/source/root"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../foo", RebasePath("//foo", SourceDir("/source/root/bar"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../../../source/root/foo",
+ RebasePath("//foo", SourceDir("/other/source/root"),
+ std::string_view("/source/root")));
+ EXPECT_EQ("../../../../source/root/foo",
+ RebasePath("//foo", SourceDir("/other/source/root/bar"),
+ std::string_view("/source/root")));
+
+ // Check when |input| and |dest_dir| are both system-absolute. Also,
+ // in this case |source_root| is never used so set it to a dummy
+ // value.
+ EXPECT_EQ("foo", RebasePath("/source/root/foo", SourceDir("/source/root"),
+ std::string_view("/x/y/z")));
+ EXPECT_EQ("foo/", RebasePath("/source/root/foo/", SourceDir("/source/root"),
+ std::string_view("/x/y/z")));
+ EXPECT_EQ("../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("/source/root"),
+ std::string_view("/x/y/z")));
+ EXPECT_EQ("../../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("/source/root/foo"),
+ std::string_view("/source/root/foo")));
+ EXPECT_EQ("../../../builddir/Out/Debug/",
+ RebasePath("/builddir/Out/Debug/", SourceDir("/source/root/foo"),
+ std::string_view("/source/root/foo")));
+ EXPECT_EQ("../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("/source/root"),
+ std::string_view("/x/y/z")));
+ EXPECT_EQ("../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("/source/root/a"),
+ std::string_view("/x/y/z")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("/source/root/a/b"),
+ std::string_view("/x/y/z")));
+
+#if defined(OS_WIN)
+ // Test corrections while rebasing Windows-style absolute paths.
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("C:/path/to/foo", SourceDir("//a/b"),
+ std::string_view("/C:/source/root")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/C:/path/to/foo", SourceDir("//a/b"),
+ std::string_view("C:/source/root")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/C:/path/to/foo", SourceDir("//a/b"),
+ std::string_view("/c:/source/root")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/c:/path/to/foo", SourceDir("//a/b"),
+ std::string_view("c:/source/root")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/c:/path/to/foo", SourceDir("//a/b"),
+ std::string_view("C:/source/root")));
+#endif
+}
+
+TEST(FilesystemUtils, DirectoryWithNoLastSlash) {
+ EXPECT_EQ("", DirectoryWithNoLastSlash(SourceDir()));
+ EXPECT_EQ("/.", DirectoryWithNoLastSlash(SourceDir("/")));
+ EXPECT_EQ("//.", DirectoryWithNoLastSlash(SourceDir("//")));
+ EXPECT_EQ("//foo", DirectoryWithNoLastSlash(SourceDir("//foo/")));
+ EXPECT_EQ("/bar", DirectoryWithNoLastSlash(SourceDir("/bar/")));
+}
+
+TEST(FilesystemUtils, SourceDirForPath) {
+#if defined(OS_WIN)
+ base::FilePath root(u"C:\\source\\foo\\");
+ EXPECT_EQ("/C:/foo/bar/",
+ SourceDirForPath(root, base::FilePath(u"C:\\foo\\bar")).value());
+ EXPECT_EQ("/", SourceDirForPath(root, base::FilePath(u"/")).value());
+ EXPECT_EQ("//",
+ SourceDirForPath(root, base::FilePath(u"C:\\source\\foo")).value());
+ EXPECT_EQ("//bar/",
+ SourceDirForPath(root, base::FilePath(u"C:\\source\\foo\\bar\\"))
+ .value());
+ EXPECT_EQ("//bar/baz/",
+ SourceDirForPath(root, base::FilePath(u"C:\\source\\foo\\bar\\baz"))
+ .value());
+
+ // Should be case-and-slash-insensitive.
+ EXPECT_EQ(
+ "//baR/",
+ SourceDirForPath(root, base::FilePath(u"c:/SOURCE\\Foo/baR/")).value());
+
+ // Some "weird" Windows paths.
+ EXPECT_EQ("/foo/bar/",
+ SourceDirForPath(root, base::FilePath(u"/foo/bar/")).value());
+ EXPECT_EQ("/C:/foo/bar/",
+ SourceDirForPath(root, base::FilePath(u"C:foo/bar/")).value());
+
+ // Also allow absolute GN-style Windows paths.
+ EXPECT_EQ("/C:/foo/bar/",
+ SourceDirForPath(root, base::FilePath(u"/C:/foo/bar")).value());
+ EXPECT_EQ(
+ "//bar/",
+ SourceDirForPath(root, base::FilePath(u"/C:/source/foo/bar")).value());
+
+ // Empty source dir.
+ base::FilePath empty;
+ EXPECT_EQ(
+ "/C:/source/foo/",
+ SourceDirForPath(empty, base::FilePath(u"C:\\source\\foo")).value());
+#else
+ base::FilePath root("/source/foo/");
+ EXPECT_EQ("/foo/bar/",
+ SourceDirForPath(root, base::FilePath("/foo/bar/")).value());
+ EXPECT_EQ("/", SourceDirForPath(root, base::FilePath("/")).value());
+ EXPECT_EQ("//",
+ SourceDirForPath(root, base::FilePath("/source/foo")).value());
+ EXPECT_EQ("//bar/",
+ SourceDirForPath(root, base::FilePath("/source/foo/bar/")).value());
+ EXPECT_EQ(
+ "//bar/baz/",
+ SourceDirForPath(root, base::FilePath("/source/foo/bar/baz/")).value());
+
+ // Should be case-sensitive.
+ EXPECT_EQ("/SOURCE/foo/bar/",
+ SourceDirForPath(root, base::FilePath("/SOURCE/foo/bar/")).value());
+
+ // Empty source dir.
+ base::FilePath empty;
+ EXPECT_EQ("/source/foo/",
+ SourceDirForPath(empty, base::FilePath("/source/foo")).value());
+#endif
+}
+
+TEST(FilesystemUtils, ContentsEqual) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ std::string data = "foo";
+
+ base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt");
+ base::WriteFile(file_path, data.c_str(), static_cast<int>(data.size()));
+
+ EXPECT_TRUE(ContentsEqual(file_path, data));
+
+ // Different length and contents.
+ data += "bar";
+ EXPECT_FALSE(ContentsEqual(file_path, data));
+
+ // The same length, different contents.
+ EXPECT_FALSE(ContentsEqual(file_path, "bar"));
+}
+
+TEST(FilesystemUtils, WriteFileIfChanged) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ std::string data = "foo";
+
+ // Write if file doesn't exist. Create also directory.
+ base::FilePath file_path =
+ temp_dir.GetPath().AppendASCII("bar").AppendASCII("foo.txt");
+ EXPECT_TRUE(WriteFileIfChanged(file_path, data, nullptr));
+
+ base::File::Info file_info;
+ ASSERT_TRUE(base::GetFileInfo(file_path, &file_info));
+ Ticks last_modified = file_info.last_modified;
+
+ {
+ using namespace std::chrono_literals;
+#if defined(OS_MACOSX)
+ // Modification times are in seconds in HFS on Mac.
+ std::this_thread::sleep_for(1s);
+#else
+ std::this_thread::sleep_for(1ms);
+#endif
+ }
+
+ // Don't write if contents is the same.
+ EXPECT_TRUE(WriteFileIfChanged(file_path, data, nullptr));
+ ASSERT_TRUE(base::GetFileInfo(file_path, &file_info));
+ EXPECT_EQ(last_modified, file_info.last_modified);
+
+ // Write if contents changed.
+ EXPECT_TRUE(WriteFileIfChanged(file_path, "bar", nullptr));
+ std::string file_data;
+ ASSERT_TRUE(base::ReadFileToString(file_path, &file_data));
+ EXPECT_EQ("bar", file_data);
+}
+
+TEST(FilesystemUtils, GetToolchainDirs) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+
+ // The default toolchain.
+ Settings default_settings(&build_settings, "");
+ Label default_toolchain_label(SourceDir("//toolchain/"), "default");
+ default_settings.set_toolchain_label(default_toolchain_label);
+ default_settings.set_default_toolchain_label(default_toolchain_label);
+ BuildDirContext default_context(&default_settings);
+
+ // Default toolchain out dir as source dirs.
+ EXPECT_EQ("//out/Debug/", GetBuildDirAsSourceDir(default_context,
+ BuildDirType::TOOLCHAIN_ROOT)
+ .value());
+ EXPECT_EQ("//out/Debug/obj/",
+ GetBuildDirAsSourceDir(default_context, BuildDirType::OBJ).value());
+ EXPECT_EQ("//out/Debug/gen/",
+ GetBuildDirAsSourceDir(default_context, BuildDirType::GEN).value());
+
+ // Default toolchain our dir as output files.
+ EXPECT_EQ(
+ "", GetBuildDirAsOutputFile(default_context, BuildDirType::TOOLCHAIN_ROOT)
+ .value());
+ EXPECT_EQ(
+ "obj/",
+ GetBuildDirAsOutputFile(default_context, BuildDirType::OBJ).value());
+ EXPECT_EQ(
+ "gen/",
+ GetBuildDirAsOutputFile(default_context, BuildDirType::GEN).value());
+
+ // Check a secondary toolchain.
+ Settings other_settings(&build_settings, "two/");
+ Label other_toolchain_label(SourceDir("//toolchain/"), "two");
+ other_settings.set_toolchain_label(other_toolchain_label);
+ other_settings.set_default_toolchain_label(default_toolchain_label);
+ BuildDirContext other_context(&other_settings);
+
+ // Secondary toolchain out dir as source dirs.
+ EXPECT_EQ("//out/Debug/two/",
+ GetBuildDirAsSourceDir(other_context, BuildDirType::TOOLCHAIN_ROOT)
+ .value());
+ EXPECT_EQ("//out/Debug/two/obj/",
+ GetBuildDirAsSourceDir(other_context, BuildDirType::OBJ).value());
+ EXPECT_EQ("//out/Debug/two/gen/",
+ GetBuildDirAsSourceDir(other_context, BuildDirType::GEN).value());
+
+ // Secondary toolchain out dir as output files.
+ EXPECT_EQ("two/",
+ GetBuildDirAsOutputFile(other_context, BuildDirType::TOOLCHAIN_ROOT)
+ .value());
+ EXPECT_EQ("two/obj/",
+ GetBuildDirAsOutputFile(other_context, BuildDirType::OBJ).value());
+ EXPECT_EQ("two/gen/",
+ GetBuildDirAsOutputFile(other_context, BuildDirType::GEN).value());
+}
+
+TEST(FilesystemUtils, GetSubBuildDir) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+
+ // Test the default toolchain.
+ Label default_toolchain_label(SourceDir("//toolchain/"), "default");
+ Settings default_settings(&build_settings, "");
+ default_settings.set_toolchain_label(default_toolchain_label);
+ default_settings.set_default_toolchain_label(default_toolchain_label);
+ BuildDirContext default_context(&default_settings);
+
+ // Target in the root.
+ EXPECT_EQ("//out/Debug/obj/",
+ GetSubBuildDirAsSourceDir(default_context, SourceDir("//"),
+ BuildDirType::OBJ)
+ .value());
+ EXPECT_EQ("gen/", GetSubBuildDirAsOutputFile(default_context, SourceDir("//"),
+ BuildDirType::GEN)
+ .value());
+
+ // Target in another directory.
+ EXPECT_EQ("//out/Debug/obj/foo/bar/",
+ GetSubBuildDirAsSourceDir(default_context, SourceDir("//foo/bar/"),
+ BuildDirType::OBJ)
+ .value());
+ EXPECT_EQ("gen/foo/bar/",
+ GetSubBuildDirAsOutputFile(default_context, SourceDir("//foo/bar/"),
+ BuildDirType::GEN)
+ .value());
+
+ // Secondary toolchain.
+ Settings other_settings(&build_settings, "two/");
+ other_settings.set_toolchain_label(Label(SourceDir("//toolchain/"), "two"));
+ other_settings.set_default_toolchain_label(default_toolchain_label);
+ BuildDirContext other_context(&other_settings);
+
+ // Target in the root.
+ EXPECT_EQ("//out/Debug/two/obj/",
+ GetSubBuildDirAsSourceDir(other_context, SourceDir("//"),
+ BuildDirType::OBJ)
+ .value());
+ EXPECT_EQ("two/gen/", GetSubBuildDirAsOutputFile(
+ other_context, SourceDir("//"), BuildDirType::GEN)
+ .value());
+
+ // Target in another directory.
+ EXPECT_EQ("//out/Debug/two/obj/foo/bar/",
+ GetSubBuildDirAsSourceDir(other_context, SourceDir("//foo/bar/"),
+ BuildDirType::OBJ)
+ .value());
+ EXPECT_EQ("two/gen/foo/bar/",
+ GetSubBuildDirAsOutputFile(other_context, SourceDir("//foo/bar/"),
+ BuildDirType::GEN)
+ .value());
+
+ // Absolute source path
+ EXPECT_EQ("//out/Debug/obj/ABS_PATH/abs/",
+ GetSubBuildDirAsSourceDir(default_context, SourceDir("/abs"),
+ BuildDirType::OBJ)
+ .value());
+ EXPECT_EQ("gen/ABS_PATH/abs/",
+ GetSubBuildDirAsOutputFile(default_context, SourceDir("/abs"),
+ BuildDirType::GEN)
+ .value());
+#if defined(OS_WIN)
+ EXPECT_EQ("//out/Debug/obj/ABS_PATH/C/abs/",
+ GetSubBuildDirAsSourceDir(default_context, SourceDir("/C:/abs"),
+ BuildDirType::OBJ)
+ .value());
+ EXPECT_EQ("gen/ABS_PATH/C/abs/",
+ GetSubBuildDirAsOutputFile(default_context, SourceDir("/C:/abs"),
+ BuildDirType::GEN)
+ .value());
+#endif
+}
+
+TEST(FilesystemUtils, GetBuildDirForTarget) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+ Settings settings(&build_settings, "");
+
+ Target a(&settings, Label(SourceDir("//foo/bar/"), "baz"));
+ EXPECT_EQ("//out/Debug/obj/foo/bar/",
+ GetBuildDirForTargetAsSourceDir(&a, BuildDirType::OBJ).value());
+ EXPECT_EQ("obj/foo/bar/",
+ GetBuildDirForTargetAsOutputFile(&a, BuildDirType::OBJ).value());
+ EXPECT_EQ("//out/Debug/gen/foo/bar/",
+ GetBuildDirForTargetAsSourceDir(&a, BuildDirType::GEN).value());
+ EXPECT_EQ("gen/foo/bar/",
+ GetBuildDirForTargetAsOutputFile(&a, BuildDirType::GEN).value());
+}
+
+// Tests handling of output dirs when build dir is the same as the root.
+TEST(FilesystemUtils, GetDirForEmptyBuildDir) {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//"));
+ Settings settings(&build_settings, "");
+
+ BuildDirContext context(&settings);
+
+ EXPECT_EQ(
+ "//",
+ GetBuildDirAsSourceDir(context, BuildDirType::TOOLCHAIN_ROOT).value());
+ EXPECT_EQ("//gen/",
+ GetBuildDirAsSourceDir(context, BuildDirType::GEN).value());
+ EXPECT_EQ("//obj/",
+ GetBuildDirAsSourceDir(context, BuildDirType::OBJ).value());
+
+ EXPECT_EQ(
+ "",
+ GetBuildDirAsOutputFile(context, BuildDirType::TOOLCHAIN_ROOT).value());
+ EXPECT_EQ("gen/",
+ GetBuildDirAsOutputFile(context, BuildDirType::GEN).value());
+ EXPECT_EQ("obj/",
+ GetBuildDirAsOutputFile(context, BuildDirType::OBJ).value());
+}
+
+TEST(FilesystemUtils, ResolveRelativeTest) {
+ std::string result;
+#ifndef OS_WIN
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("/some/dir", "/some/dir/a", &result));
+ EXPECT_EQ(result, "//a");
+
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
+ "/some/dir", "/some/dir-sufix/a", &result));
+#else
+ EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("C:/some/dir",
+ "/C:/some/dir/a", &result));
+ EXPECT_EQ(result, "//a");
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
+ "C:/some/dir", "C:/some/dir-sufix/a", &result));
+#endif
+}
diff --git a/gn/tools/gn/format_test_data/001.gn b/gn/src/gn/format_test_data/001.gn
index c35c27951fd..c35c27951fd 100644
--- a/gn/tools/gn/format_test_data/001.gn
+++ b/gn/src/gn/format_test_data/001.gn
diff --git a/gn/tools/gn/format_test_data/001.golden b/gn/src/gn/format_test_data/001.golden
index 31c9069d2b2..31c9069d2b2 100644
--- a/gn/tools/gn/format_test_data/001.golden
+++ b/gn/src/gn/format_test_data/001.golden
diff --git a/gn/tools/gn/format_test_data/002.gn b/gn/src/gn/format_test_data/002.gn
index 34043b5ed30..34043b5ed30 100644
--- a/gn/tools/gn/format_test_data/002.gn
+++ b/gn/src/gn/format_test_data/002.gn
diff --git a/gn/tools/gn/format_test_data/002.golden b/gn/src/gn/format_test_data/002.golden
index cd8f38810c4..cd8f38810c4 100644
--- a/gn/tools/gn/format_test_data/002.golden
+++ b/gn/src/gn/format_test_data/002.golden
diff --git a/gn/tools/gn/format_test_data/003.gn b/gn/src/gn/format_test_data/003.gn
index 429165a285c..429165a285c 100644
--- a/gn/tools/gn/format_test_data/003.gn
+++ b/gn/src/gn/format_test_data/003.gn
diff --git a/gn/src/gn/format_test_data/003.golden b/gn/src/gn/format_test_data/003.golden
new file mode 100644
index 00000000000..a5bd5d368a4
--- /dev/null
+++ b/gn/src/gn/format_test_data/003.golden
@@ -0,0 +1,8 @@
+executable("test") {
+ sources = [
+ "stuff.cc",
+ "things.cc",
+ ]
+
+ deps = [ "//base" ]
+}
diff --git a/gn/tools/gn/format_test_data/004.gn b/gn/src/gn/format_test_data/004.gn
index f36d039cf4c..f36d039cf4c 100644
--- a/gn/tools/gn/format_test_data/004.gn
+++ b/gn/src/gn/format_test_data/004.gn
diff --git a/gn/src/gn/format_test_data/004.golden b/gn/src/gn/format_test_data/004.golden
new file mode 100644
index 00000000000..1d000c628b6
--- /dev/null
+++ b/gn/src/gn/format_test_data/004.golden
@@ -0,0 +1,11 @@
+# This is a block comment that goes at the top of the file and is attached to
+# the top level target.
+executable("test") {
+ sources = [
+ "stuff.cc", # Comment attached to list item.
+ "things.cc",
+ ]
+
+ # Comment attached to statement.
+ deps = [ "//base" ]
+}
diff --git a/gn/tools/gn/format_test_data/005.gn b/gn/src/gn/format_test_data/005.gn
index 07b4ef372c0..07b4ef372c0 100644
--- a/gn/tools/gn/format_test_data/005.gn
+++ b/gn/src/gn/format_test_data/005.gn
diff --git a/gn/tools/gn/format_test_data/005.golden b/gn/src/gn/format_test_data/005.golden
index 07b4ef372c0..07b4ef372c0 100644
--- a/gn/tools/gn/format_test_data/005.golden
+++ b/gn/src/gn/format_test_data/005.golden
diff --git a/gn/tools/gn/format_test_data/006.gn b/gn/src/gn/format_test_data/006.gn
index 737fceaae0e..737fceaae0e 100644
--- a/gn/tools/gn/format_test_data/006.gn
+++ b/gn/src/gn/format_test_data/006.gn
diff --git a/gn/tools/gn/format_test_data/006.golden b/gn/src/gn/format_test_data/006.golden
index 07b4ef372c0..07b4ef372c0 100644
--- a/gn/tools/gn/format_test_data/006.golden
+++ b/gn/src/gn/format_test_data/006.golden
diff --git a/gn/tools/gn/format_test_data/007.gn b/gn/src/gn/format_test_data/007.gn
index 3cd002d8606..3cd002d8606 100644
--- a/gn/tools/gn/format_test_data/007.gn
+++ b/gn/src/gn/format_test_data/007.gn
diff --git a/gn/src/gn/format_test_data/007.golden b/gn/src/gn/format_test_data/007.golden
new file mode 100644
index 00000000000..2d2b4c1fc5c
--- /dev/null
+++ b/gn/src/gn/format_test_data/007.golden
@@ -0,0 +1,9 @@
+executable("test") {
+ sources = [ "a.cc" ]
+
+ # This is an unusual comment that's a header for the stuff that comes after
+ # it. We want to make sure that it's not connected to the next element in
+ # the file.
+
+ cflags = [ "-Wee" ]
+}
diff --git a/gn/tools/gn/format_test_data/008.gn b/gn/src/gn/format_test_data/008.gn
index 16b4a8e32fb..16b4a8e32fb 100644
--- a/gn/tools/gn/format_test_data/008.gn
+++ b/gn/src/gn/format_test_data/008.gn
diff --git a/gn/src/gn/format_test_data/008.golden b/gn/src/gn/format_test_data/008.golden
new file mode 100644
index 00000000000..8e4c5f1bbf2
--- /dev/null
+++ b/gn/src/gn/format_test_data/008.golden
@@ -0,0 +1,3 @@
+if (is_win) {
+ sources = [ "win.cc" ]
+}
diff --git a/gn/tools/gn/format_test_data/009.gn b/gn/src/gn/format_test_data/009.gn
index e47b62175ae..e47b62175ae 100644
--- a/gn/tools/gn/format_test_data/009.gn
+++ b/gn/src/gn/format_test_data/009.gn
diff --git a/gn/src/gn/format_test_data/009.golden b/gn/src/gn/format_test_data/009.golden
new file mode 100644
index 00000000000..20663f860c2
--- /dev/null
+++ b/gn/src/gn/format_test_data/009.golden
@@ -0,0 +1,5 @@
+if (is_win) {
+ sources = [ "win.cc" ]
+} else {
+ sources = [ "linux.cc" ]
+}
diff --git a/gn/tools/gn/format_test_data/010.gn b/gn/src/gn/format_test_data/010.gn
index 70004a75158..70004a75158 100644
--- a/gn/tools/gn/format_test_data/010.gn
+++ b/gn/src/gn/format_test_data/010.gn
diff --git a/gn/src/gn/format_test_data/010.golden b/gn/src/gn/format_test_data/010.golden
new file mode 100644
index 00000000000..9512a2e71b2
--- /dev/null
+++ b/gn/src/gn/format_test_data/010.golden
@@ -0,0 +1,5 @@
+if (is_win) {
+ sources = [ "win.cc" ]
+} else if (is_linux) {
+ sources = [ "linux.cc" ]
+}
diff --git a/gn/tools/gn/format_test_data/011.gn b/gn/src/gn/format_test_data/011.gn
index c2c5bf755ea..c2c5bf755ea 100644
--- a/gn/tools/gn/format_test_data/011.gn
+++ b/gn/src/gn/format_test_data/011.gn
diff --git a/gn/src/gn/format_test_data/011.golden b/gn/src/gn/format_test_data/011.golden
new file mode 100644
index 00000000000..43d70c59e2e
--- /dev/null
+++ b/gn/src/gn/format_test_data/011.golden
@@ -0,0 +1,7 @@
+if (is_win) {
+ sources = [ "win.cc" ]
+} else if (is_linux) {
+ sources = [ "linux.cc" ]
+} else {
+ sources = [ "wha.cc" ]
+}
diff --git a/gn/tools/gn/format_test_data/012.gn b/gn/src/gn/format_test_data/012.gn
index 1da385c00e8..1da385c00e8 100644
--- a/gn/tools/gn/format_test_data/012.gn
+++ b/gn/src/gn/format_test_data/012.gn
diff --git a/gn/src/gn/format_test_data/012.golden b/gn/src/gn/format_test_data/012.golden
new file mode 100644
index 00000000000..7e3fc989e02
--- /dev/null
+++ b/gn/src/gn/format_test_data/012.golden
@@ -0,0 +1,16 @@
+# (A sample top level block comment)
+# 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_win) {
+ # This is some special stuff for Windows
+ sources = [ "win.cc" ]
+} else if (is_linux) {
+ # This is a block comment inside the linux block, but not attached.
+
+ sources = [ "linux.cc" ]
+} else {
+ # A comment with trailing spaces
+ sources = [ "wha.cc" ]
+}
diff --git a/gn/tools/gn/format_test_data/013.gn b/gn/src/gn/format_test_data/013.gn
index 06183a82d48..06183a82d48 100644
--- a/gn/tools/gn/format_test_data/013.gn
+++ b/gn/src/gn/format_test_data/013.gn
diff --git a/gn/tools/gn/format_test_data/013.golden b/gn/src/gn/format_test_data/013.golden
index d84b7d20f8e..d84b7d20f8e 100644
--- a/gn/tools/gn/format_test_data/013.golden
+++ b/gn/src/gn/format_test_data/013.golden
diff --git a/gn/tools/gn/format_test_data/014.gn b/gn/src/gn/format_test_data/014.gn
index 2d0170d862a..2d0170d862a 100644
--- a/gn/tools/gn/format_test_data/014.gn
+++ b/gn/src/gn/format_test_data/014.gn
diff --git a/gn/tools/gn/format_test_data/014.golden b/gn/src/gn/format_test_data/014.golden
index c0ba5bb3981..c0ba5bb3981 100644
--- a/gn/tools/gn/format_test_data/014.golden
+++ b/gn/src/gn/format_test_data/014.golden
diff --git a/gn/tools/gn/format_test_data/015.gn b/gn/src/gn/format_test_data/015.gn
index f065095f656..f065095f656 100644
--- a/gn/tools/gn/format_test_data/015.gn
+++ b/gn/src/gn/format_test_data/015.gn
diff --git a/gn/src/gn/format_test_data/015.golden b/gn/src/gn/format_test_data/015.golden
new file mode 100644
index 00000000000..8e126887a14
--- /dev/null
+++ b/gn/src/gn/format_test_data/015.golden
@@ -0,0 +1,4 @@
+if (is_win) {
+ sources = [ "a.cc" ]
+ # Some comment at end.
+}
diff --git a/gn/tools/gn/format_test_data/016.gn b/gn/src/gn/format_test_data/016.gn
index 00a79922828..00a79922828 100644
--- a/gn/tools/gn/format_test_data/016.gn
+++ b/gn/src/gn/format_test_data/016.gn
diff --git a/gn/tools/gn/format_test_data/016.golden b/gn/src/gn/format_test_data/016.golden
index 3f4f15bc507..3f4f15bc507 100644
--- a/gn/tools/gn/format_test_data/016.golden
+++ b/gn/src/gn/format_test_data/016.golden
diff --git a/gn/tools/gn/format_test_data/017.gn b/gn/src/gn/format_test_data/017.gn
index 225ae2fe30c..225ae2fe30c 100644
--- a/gn/tools/gn/format_test_data/017.gn
+++ b/gn/src/gn/format_test_data/017.gn
diff --git a/gn/tools/gn/format_test_data/017.golden b/gn/src/gn/format_test_data/017.golden
index d9d7ad9a472..d9d7ad9a472 100644
--- a/gn/tools/gn/format_test_data/017.golden
+++ b/gn/src/gn/format_test_data/017.golden
diff --git a/gn/tools/gn/format_test_data/018.gn b/gn/src/gn/format_test_data/018.gn
index e63ef9def15..e63ef9def15 100644
--- a/gn/tools/gn/format_test_data/018.gn
+++ b/gn/src/gn/format_test_data/018.gn
diff --git a/gn/tools/gn/format_test_data/018.golden b/gn/src/gn/format_test_data/018.golden
index e63ef9def15..e63ef9def15 100644
--- a/gn/tools/gn/format_test_data/018.golden
+++ b/gn/src/gn/format_test_data/018.golden
diff --git a/gn/src/gn/format_test_data/019.gn b/gn/src/gn/format_test_data/019.gn
new file mode 100644
index 00000000000..125480cd64f
--- /dev/null
+++ b/gn/src/gn/format_test_data/019.gn
@@ -0,0 +1,23 @@
+# Make sure blank lines are maintained before comments in lists.
+
+deps = [
+ "//pdf", # Not compiled on Android in GYP yet, either.
+ "//ppapi:ppapi_c",
+ "//third_party/libusb",
+ "//ui/keyboard", # Blocked on content.
+
+ # Seems to not be compiled on Android. Otherwise it will need a config.h.
+ "//third_party/libxslt",
+
+ # Not relevant to Android.
+ "//ash",
+ "//gn",
+
+ # Multiple line
+ # comment
+ # here.
+ "//ui/aura",
+ "//ui/display",
+ "//ui/views",
+ "//ui/views/controls/webview",
+]
diff --git a/gn/src/gn/format_test_data/019.golden b/gn/src/gn/format_test_data/019.golden
new file mode 100644
index 00000000000..256c1712575
--- /dev/null
+++ b/gn/src/gn/format_test_data/019.golden
@@ -0,0 +1,23 @@
+# Make sure blank lines are maintained before comments in lists.
+
+deps = [
+ "//pdf", # Not compiled on Android in GYP yet, either.
+ "//ppapi:ppapi_c",
+ "//third_party/libusb",
+ "//ui/keyboard", # Blocked on content.
+
+ # Seems to not be compiled on Android. Otherwise it will need a config.h.
+ "//third_party/libxslt",
+
+ # Not relevant to Android.
+ "//ash",
+ "//gn",
+
+ # Multiple line
+ # comment
+ # here.
+ "//ui/aura",
+ "//ui/display",
+ "//ui/views",
+ "//ui/views/controls/webview",
+]
diff --git a/gn/tools/gn/format_test_data/020.gn b/gn/src/gn/format_test_data/020.gn
index 96de16448bc..96de16448bc 100644
--- a/gn/tools/gn/format_test_data/020.gn
+++ b/gn/src/gn/format_test_data/020.gn
diff --git a/gn/tools/gn/format_test_data/020.golden b/gn/src/gn/format_test_data/020.golden
index 96de16448bc..96de16448bc 100644
--- a/gn/tools/gn/format_test_data/020.golden
+++ b/gn/src/gn/format_test_data/020.golden
diff --git a/gn/tools/gn/format_test_data/021.gn b/gn/src/gn/format_test_data/021.gn
index 355735db135..355735db135 100644
--- a/gn/tools/gn/format_test_data/021.gn
+++ b/gn/src/gn/format_test_data/021.gn
diff --git a/gn/tools/gn/format_test_data/021.golden b/gn/src/gn/format_test_data/021.golden
index f17c5fd83c6..f17c5fd83c6 100644
--- a/gn/tools/gn/format_test_data/021.golden
+++ b/gn/src/gn/format_test_data/021.golden
diff --git a/gn/tools/gn/format_test_data/022.gn b/gn/src/gn/format_test_data/022.gn
index a67ed24d6bb..a67ed24d6bb 100644
--- a/gn/tools/gn/format_test_data/022.gn
+++ b/gn/src/gn/format_test_data/022.gn
diff --git a/gn/tools/gn/format_test_data/022.golden b/gn/src/gn/format_test_data/022.golden
index a67ed24d6bb..a67ed24d6bb 100644
--- a/gn/tools/gn/format_test_data/022.golden
+++ b/gn/src/gn/format_test_data/022.golden
diff --git a/gn/tools/gn/format_test_data/023.gn b/gn/src/gn/format_test_data/023.gn
index 8a1b51926b7..8a1b51926b7 100644
--- a/gn/tools/gn/format_test_data/023.gn
+++ b/gn/src/gn/format_test_data/023.gn
diff --git a/gn/tools/gn/format_test_data/023.golden b/gn/src/gn/format_test_data/023.golden
index bab6e45eda3..bab6e45eda3 100644
--- a/gn/tools/gn/format_test_data/023.golden
+++ b/gn/src/gn/format_test_data/023.golden
diff --git a/gn/tools/gn/format_test_data/024.gn b/gn/src/gn/format_test_data/024.gn
index 5034cdc84e7..5034cdc84e7 100644
--- a/gn/tools/gn/format_test_data/024.gn
+++ b/gn/src/gn/format_test_data/024.gn
diff --git a/gn/tools/gn/format_test_data/024.golden b/gn/src/gn/format_test_data/024.golden
index f2c755dcb8b..f2c755dcb8b 100644
--- a/gn/tools/gn/format_test_data/024.golden
+++ b/gn/src/gn/format_test_data/024.golden
diff --git a/gn/tools/gn/format_test_data/025.gn b/gn/src/gn/format_test_data/025.gn
index 959ec8a3c52..959ec8a3c52 100644
--- a/gn/tools/gn/format_test_data/025.gn
+++ b/gn/src/gn/format_test_data/025.gn
diff --git a/gn/tools/gn/format_test_data/025.golden b/gn/src/gn/format_test_data/025.golden
index e6d08692a4f..e6d08692a4f 100644
--- a/gn/tools/gn/format_test_data/025.golden
+++ b/gn/src/gn/format_test_data/025.golden
diff --git a/gn/tools/gn/format_test_data/026.gn b/gn/src/gn/format_test_data/026.gn
index 8cf2028a72a..8cf2028a72a 100644
--- a/gn/tools/gn/format_test_data/026.gn
+++ b/gn/src/gn/format_test_data/026.gn
diff --git a/gn/tools/gn/format_test_data/026.golden b/gn/src/gn/format_test_data/026.golden
index a1d1d3f936d..a1d1d3f936d 100644
--- a/gn/tools/gn/format_test_data/026.golden
+++ b/gn/src/gn/format_test_data/026.golden
diff --git a/gn/tools/gn/format_test_data/027.gn b/gn/src/gn/format_test_data/027.gn
index cc5fe5fa942..cc5fe5fa942 100644
--- a/gn/tools/gn/format_test_data/027.gn
+++ b/gn/src/gn/format_test_data/027.gn
diff --git a/gn/tools/gn/format_test_data/027.golden b/gn/src/gn/format_test_data/027.golden
index 05f0eb57931..05f0eb57931 100644
--- a/gn/tools/gn/format_test_data/027.golden
+++ b/gn/src/gn/format_test_data/027.golden
diff --git a/gn/tools/gn/format_test_data/028.gn b/gn/src/gn/format_test_data/028.gn
index d84e1f8c957..d84e1f8c957 100644
--- a/gn/tools/gn/format_test_data/028.gn
+++ b/gn/src/gn/format_test_data/028.gn
diff --git a/gn/tools/gn/format_test_data/028.golden b/gn/src/gn/format_test_data/028.golden
index 95068963296..95068963296 100644
--- a/gn/tools/gn/format_test_data/028.golden
+++ b/gn/src/gn/format_test_data/028.golden
diff --git a/gn/tools/gn/format_test_data/029.gn b/gn/src/gn/format_test_data/029.gn
index ac67830164e..ac67830164e 100644
--- a/gn/tools/gn/format_test_data/029.gn
+++ b/gn/src/gn/format_test_data/029.gn
diff --git a/gn/tools/gn/format_test_data/029.golden b/gn/src/gn/format_test_data/029.golden
index ac67830164e..ac67830164e 100644
--- a/gn/tools/gn/format_test_data/029.golden
+++ b/gn/src/gn/format_test_data/029.golden
diff --git a/gn/tools/gn/format_test_data/030.gn b/gn/src/gn/format_test_data/030.gn
index adac9a82416..adac9a82416 100644
--- a/gn/tools/gn/format_test_data/030.gn
+++ b/gn/src/gn/format_test_data/030.gn
diff --git a/gn/tools/gn/format_test_data/030.golden b/gn/src/gn/format_test_data/030.golden
index adac9a82416..adac9a82416 100644
--- a/gn/tools/gn/format_test_data/030.golden
+++ b/gn/src/gn/format_test_data/030.golden
diff --git a/gn/tools/gn/format_test_data/031.gn b/gn/src/gn/format_test_data/031.gn
index d83d4234a70..d83d4234a70 100644
--- a/gn/tools/gn/format_test_data/031.gn
+++ b/gn/src/gn/format_test_data/031.gn
diff --git a/gn/tools/gn/format_test_data/031.golden b/gn/src/gn/format_test_data/031.golden
index 6653951f16a..6653951f16a 100644
--- a/gn/tools/gn/format_test_data/031.golden
+++ b/gn/src/gn/format_test_data/031.golden
diff --git a/gn/tools/gn/format_test_data/032.gn b/gn/src/gn/format_test_data/032.gn
index d7ea7e5f8f4..d7ea7e5f8f4 100644
--- a/gn/tools/gn/format_test_data/032.gn
+++ b/gn/src/gn/format_test_data/032.gn
diff --git a/gn/tools/gn/format_test_data/032.golden b/gn/src/gn/format_test_data/032.golden
index aeca8963933..aeca8963933 100644
--- a/gn/tools/gn/format_test_data/032.golden
+++ b/gn/src/gn/format_test_data/032.golden
diff --git a/gn/tools/gn/format_test_data/033.gn b/gn/src/gn/format_test_data/033.gn
index 6767acd6b93..6767acd6b93 100644
--- a/gn/tools/gn/format_test_data/033.gn
+++ b/gn/src/gn/format_test_data/033.gn
diff --git a/gn/tools/gn/format_test_data/033.golden b/gn/src/gn/format_test_data/033.golden
index 6767acd6b93..6767acd6b93 100644
--- a/gn/tools/gn/format_test_data/033.golden
+++ b/gn/src/gn/format_test_data/033.golden
diff --git a/gn/tools/gn/format_test_data/034.gn b/gn/src/gn/format_test_data/034.gn
index 33f5eadceb5..33f5eadceb5 100644
--- a/gn/tools/gn/format_test_data/034.gn
+++ b/gn/src/gn/format_test_data/034.gn
diff --git a/gn/tools/gn/format_test_data/035.gn b/gn/src/gn/format_test_data/035.gn
index 70bc1a9eca0..70bc1a9eca0 100644
--- a/gn/tools/gn/format_test_data/035.gn
+++ b/gn/src/gn/format_test_data/035.gn
diff --git a/gn/tools/gn/format_test_data/035.golden b/gn/src/gn/format_test_data/035.golden
index 70bc1a9eca0..70bc1a9eca0 100644
--- a/gn/tools/gn/format_test_data/035.golden
+++ b/gn/src/gn/format_test_data/035.golden
diff --git a/gn/tools/gn/format_test_data/036.gn b/gn/src/gn/format_test_data/036.gn
index 5a5eca8455c..5a5eca8455c 100644
--- a/gn/tools/gn/format_test_data/036.gn
+++ b/gn/src/gn/format_test_data/036.gn
diff --git a/gn/tools/gn/format_test_data/036.golden b/gn/src/gn/format_test_data/036.golden
index 5a5eca8455c..5a5eca8455c 100644
--- a/gn/tools/gn/format_test_data/036.golden
+++ b/gn/src/gn/format_test_data/036.golden
diff --git a/gn/tools/gn/format_test_data/037.gn b/gn/src/gn/format_test_data/037.gn
index ebbf0f8fa9a..ebbf0f8fa9a 100644
--- a/gn/tools/gn/format_test_data/037.gn
+++ b/gn/src/gn/format_test_data/037.gn
diff --git a/gn/tools/gn/format_test_data/037.golden b/gn/src/gn/format_test_data/037.golden
index 71e95a4b581..71e95a4b581 100644
--- a/gn/tools/gn/format_test_data/037.golden
+++ b/gn/src/gn/format_test_data/037.golden
diff --git a/gn/tools/gn/format_test_data/038.gn b/gn/src/gn/format_test_data/038.gn
index ea06d93da70..ea06d93da70 100644
--- a/gn/tools/gn/format_test_data/038.gn
+++ b/gn/src/gn/format_test_data/038.gn
diff --git a/gn/tools/gn/format_test_data/038.golden b/gn/src/gn/format_test_data/038.golden
index f8c9a1b92b0..f8c9a1b92b0 100644
--- a/gn/tools/gn/format_test_data/038.golden
+++ b/gn/src/gn/format_test_data/038.golden
diff --git a/gn/tools/gn/format_test_data/039.gn b/gn/src/gn/format_test_data/039.gn
index 662b8cdb108..662b8cdb108 100644
--- a/gn/tools/gn/format_test_data/039.gn
+++ b/gn/src/gn/format_test_data/039.gn
diff --git a/gn/tools/gn/format_test_data/039.golden b/gn/src/gn/format_test_data/039.golden
index b92b1ae838b..b92b1ae838b 100644
--- a/gn/tools/gn/format_test_data/039.golden
+++ b/gn/src/gn/format_test_data/039.golden
diff --git a/gn/src/gn/format_test_data/040.gn b/gn/src/gn/format_test_data/040.gn
new file mode 100644
index 00000000000..63e5b07cf37
--- /dev/null
+++ b/gn/src/gn/format_test_data/040.gn
@@ -0,0 +1,9 @@
+# Dewrapping shouldn't cause 80 col to be exceeded.
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ if (is_win) {
+ cflags = [
+ "/wd4267", # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ ]
+ }
+}
diff --git a/gn/src/gn/format_test_data/040.golden b/gn/src/gn/format_test_data/040.golden
new file mode 100644
index 00000000000..89fdacf1576
--- /dev/null
+++ b/gn/src/gn/format_test_data/040.golden
@@ -0,0 +1,10 @@
+# Dewrapping shouldn't cause 80 col to be exceeded.
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ if (is_win) {
+ cflags = [
+ "/wd4267", # TODO(jschuh): crbug.com/167187 fix size_t to int
+ # truncations.
+ ]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/041.gn b/gn/src/gn/format_test_data/041.gn
index fa39b4923ff..fa39b4923ff 100644
--- a/gn/tools/gn/format_test_data/041.gn
+++ b/gn/src/gn/format_test_data/041.gn
diff --git a/gn/tools/gn/format_test_data/041.golden b/gn/src/gn/format_test_data/041.golden
index fa39b4923ff..fa39b4923ff 100644
--- a/gn/tools/gn/format_test_data/041.golden
+++ b/gn/src/gn/format_test_data/041.golden
diff --git a/gn/tools/gn/format_test_data/042.gn b/gn/src/gn/format_test_data/042.gn
index b827f29e070..b827f29e070 100644
--- a/gn/tools/gn/format_test_data/042.gn
+++ b/gn/src/gn/format_test_data/042.gn
diff --git a/gn/src/gn/format_test_data/042.golden b/gn/src/gn/format_test_data/042.golden
new file mode 100644
index 00000000000..2baca562690
--- /dev/null
+++ b/gn/src/gn/format_test_data/042.golden
@@ -0,0 +1,96 @@
+# Test zero, one, and multiple element for specifically named LHSs.
+if (true) {
+ cflags = []
+ cflags_c = []
+ cflags_cc = []
+ data = []
+ datadeps = []
+ defines = []
+ deps = []
+ include_dirs = []
+ inputs = []
+ ldflags = []
+ outputs = []
+ public_deps = []
+ sources = []
+} else if (true) {
+ cflags = [ "x" ]
+ cflags_c = [ "x" ]
+ cflags_cc = [ "x" ]
+ data = [ "x" ]
+ datadeps = [ "x" ]
+ defines = [ "x" ]
+ deps = [ "x" ]
+ include_dirs = [ "x" ]
+ inputs = [ "x" ]
+ ldflags = [ "x" ]
+ outputs = [ "x" ]
+ public_deps = [ "x" ]
+ sources = [ "x" ]
+} else {
+ cflags = [
+ "x",
+ "y",
+ "z",
+ ]
+ cflags_c = [
+ "x",
+ "y",
+ "z",
+ ]
+ cflags_cc = [
+ "x",
+ "y",
+ "z",
+ ]
+ data = [
+ "x",
+ "y",
+ "z",
+ ]
+ datadeps = [
+ "x",
+ "y",
+ "z",
+ ]
+ defines = [
+ "x",
+ "y",
+ "z",
+ ]
+ deps = [
+ "x",
+ "y",
+ "z",
+ ]
+ include_dirs = [
+ "x",
+ "y",
+ "z",
+ ]
+ inputs = [
+ "x",
+ "y",
+ "z",
+ ]
+ ldflags = [
+ "x",
+ "y",
+ "z",
+ ]
+ outputs = [
+ "x",
+ "y",
+ "z",
+ ]
+ public_deps = [
+ "x",
+ "y",
+ "z",
+ ]
+ sources = [
+ "x",
+ "y",
+ "z",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/043.gn b/gn/src/gn/format_test_data/043.gn
index b95c6a52593..b95c6a52593 100644
--- a/gn/tools/gn/format_test_data/043.gn
+++ b/gn/src/gn/format_test_data/043.gn
diff --git a/gn/tools/gn/format_test_data/043.golden b/gn/src/gn/format_test_data/043.golden
index 336ec2fff1e..336ec2fff1e 100644
--- a/gn/tools/gn/format_test_data/043.golden
+++ b/gn/src/gn/format_test_data/043.golden
diff --git a/gn/tools/gn/format_test_data/044.golden b/gn/src/gn/format_test_data/044.gn
index 030c5dd5dc7..030c5dd5dc7 100644
--- a/gn/tools/gn/format_test_data/044.golden
+++ b/gn/src/gn/format_test_data/044.gn
diff --git a/gn/src/gn/format_test_data/044.golden b/gn/src/gn/format_test_data/044.golden
new file mode 100644
index 00000000000..030c5dd5dc7
--- /dev/null
+++ b/gn/src/gn/format_test_data/044.golden
@@ -0,0 +1,11 @@
+# 80 ---------------------------------------------------------------------------
+config("compiler") {
+ if (is_win) {
+ if (is_component_build) {
+ cflags += [
+ "/EHsc", # Assume C functions can't throw exceptions and don't catch
+ # structured exceptions (only C++ ones).
+ ]
+ }
+ }
+}
diff --git a/gn/tools/gn/format_test_data/045.gn b/gn/src/gn/format_test_data/045.gn
index 28a7280d3ba..28a7280d3ba 100644
--- a/gn/tools/gn/format_test_data/045.gn
+++ b/gn/src/gn/format_test_data/045.gn
diff --git a/gn/tools/gn/format_test_data/045.golden b/gn/src/gn/format_test_data/045.golden
index 21c560a80f4..21c560a80f4 100644
--- a/gn/tools/gn/format_test_data/045.golden
+++ b/gn/src/gn/format_test_data/045.golden
diff --git a/gn/tools/gn/format_test_data/046.gn b/gn/src/gn/format_test_data/046.gn
index 28df74be66c..28df74be66c 100644
--- a/gn/tools/gn/format_test_data/046.gn
+++ b/gn/src/gn/format_test_data/046.gn
diff --git a/gn/tools/gn/format_test_data/046.golden b/gn/src/gn/format_test_data/046.golden
index 7d2a679c18d..7d2a679c18d 100644
--- a/gn/tools/gn/format_test_data/046.golden
+++ b/gn/src/gn/format_test_data/046.golden
diff --git a/gn/tools/gn/format_test_data/047.gn b/gn/src/gn/format_test_data/047.gn
index f1fdbeca8b5..f1fdbeca8b5 100644
--- a/gn/tools/gn/format_test_data/047.gn
+++ b/gn/src/gn/format_test_data/047.gn
diff --git a/gn/tools/gn/format_test_data/047.golden b/gn/src/gn/format_test_data/047.golden
index 5217e762580..5217e762580 100644
--- a/gn/tools/gn/format_test_data/047.golden
+++ b/gn/src/gn/format_test_data/047.golden
diff --git a/gn/src/gn/format_test_data/048.gn b/gn/src/gn/format_test_data/048.gn
new file mode 100644
index 00000000000..2584b757c57
--- /dev/null
+++ b/gn/src/gn/format_test_data/048.gn
@@ -0,0 +1,19 @@
+# No blank inserted after libs (caused by trailing comment on 'else').
+component("google_toolbox_for_mac") {
+ if (!is_ios) {
+ sources += [
+ "src/AddressBook/GTMABAddressBook.h",
+ "src/AddressBook/GTMABAddressBook.m",
+ ]
+
+ frameworks = [
+ "AddressBook.framework",
+ "AppKit.framework",
+ ]
+ } else { # is_ios
+ sources += [
+ "src/iPhone/GTMFadeTruncatingLabel.h",
+ "src/iPhone/GTMFadeTruncatingLabel.m",
+ ]
+ }
+}
diff --git a/gn/src/gn/format_test_data/048.golden b/gn/src/gn/format_test_data/048.golden
new file mode 100644
index 00000000000..2584b757c57
--- /dev/null
+++ b/gn/src/gn/format_test_data/048.golden
@@ -0,0 +1,19 @@
+# No blank inserted after libs (caused by trailing comment on 'else').
+component("google_toolbox_for_mac") {
+ if (!is_ios) {
+ sources += [
+ "src/AddressBook/GTMABAddressBook.h",
+ "src/AddressBook/GTMABAddressBook.m",
+ ]
+
+ frameworks = [
+ "AddressBook.framework",
+ "AppKit.framework",
+ ]
+ } else { # is_ios
+ sources += [
+ "src/iPhone/GTMFadeTruncatingLabel.h",
+ "src/iPhone/GTMFadeTruncatingLabel.m",
+ ]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/049.gn b/gn/src/gn/format_test_data/049.gn
index fe793d2898a..fe793d2898a 100644
--- a/gn/tools/gn/format_test_data/049.gn
+++ b/gn/src/gn/format_test_data/049.gn
diff --git a/gn/tools/gn/format_test_data/050.gn b/gn/src/gn/format_test_data/050.gn
index 92a6d6d7d4b..92a6d6d7d4b 100644
--- a/gn/tools/gn/format_test_data/050.gn
+++ b/gn/src/gn/format_test_data/050.gn
diff --git a/gn/tools/gn/format_test_data/050.golden b/gn/src/gn/format_test_data/050.golden
index 2645600c738..2645600c738 100644
--- a/gn/tools/gn/format_test_data/050.golden
+++ b/gn/src/gn/format_test_data/050.golden
diff --git a/gn/tools/gn/format_test_data/051.gn b/gn/src/gn/format_test_data/051.gn
index 8076ec696d4..8076ec696d4 100644
--- a/gn/tools/gn/format_test_data/051.gn
+++ b/gn/src/gn/format_test_data/051.gn
diff --git a/gn/tools/gn/format_test_data/051.golden b/gn/src/gn/format_test_data/051.golden
index 37a12124013..37a12124013 100644
--- a/gn/tools/gn/format_test_data/051.golden
+++ b/gn/src/gn/format_test_data/051.golden
diff --git a/gn/tools/gn/format_test_data/052.gn b/gn/src/gn/format_test_data/052.gn
index 1f6a10f5159..1f6a10f5159 100644
--- a/gn/tools/gn/format_test_data/052.gn
+++ b/gn/src/gn/format_test_data/052.gn
diff --git a/gn/tools/gn/format_test_data/052.golden b/gn/src/gn/format_test_data/052.golden
index 880c5639f2c..880c5639f2c 100644
--- a/gn/tools/gn/format_test_data/052.golden
+++ b/gn/src/gn/format_test_data/052.golden
diff --git a/gn/tools/gn/format_test_data/053.gn b/gn/src/gn/format_test_data/053.gn
index 42a3e0899ba..42a3e0899ba 100644
--- a/gn/tools/gn/format_test_data/053.gn
+++ b/gn/src/gn/format_test_data/053.gn
diff --git a/gn/tools/gn/format_test_data/053.golden b/gn/src/gn/format_test_data/053.golden
index 76179cc1953..76179cc1953 100644
--- a/gn/tools/gn/format_test_data/053.golden
+++ b/gn/src/gn/format_test_data/053.golden
diff --git a/gn/tools/gn/format_test_data/054.gn b/gn/src/gn/format_test_data/054.gn
index dca2ace3b32..dca2ace3b32 100644
--- a/gn/tools/gn/format_test_data/054.gn
+++ b/gn/src/gn/format_test_data/054.gn
diff --git a/gn/tools/gn/format_test_data/054.golden b/gn/src/gn/format_test_data/054.golden
index 7dfe5be7f26..7dfe5be7f26 100644
--- a/gn/tools/gn/format_test_data/054.golden
+++ b/gn/src/gn/format_test_data/054.golden
diff --git a/gn/tools/gn/format_test_data/055.gn b/gn/src/gn/format_test_data/055.gn
index 7e467247694..7e467247694 100644
--- a/gn/tools/gn/format_test_data/055.gn
+++ b/gn/src/gn/format_test_data/055.gn
diff --git a/gn/tools/gn/format_test_data/055.golden b/gn/src/gn/format_test_data/055.golden
index b2bfdf28654..b2bfdf28654 100644
--- a/gn/tools/gn/format_test_data/055.golden
+++ b/gn/src/gn/format_test_data/055.golden
diff --git a/gn/tools/gn/format_test_data/056.gn b/gn/src/gn/format_test_data/056.gn
index 2082a770d87..2082a770d87 100644
--- a/gn/tools/gn/format_test_data/056.gn
+++ b/gn/src/gn/format_test_data/056.gn
diff --git a/gn/tools/gn/format_test_data/056.golden b/gn/src/gn/format_test_data/056.golden
index df088fc06b5..df088fc06b5 100644
--- a/gn/tools/gn/format_test_data/056.golden
+++ b/gn/src/gn/format_test_data/056.golden
diff --git a/gn/tools/gn/format_test_data/057.gn b/gn/src/gn/format_test_data/057.gn
index 858e3115b11..858e3115b11 100644
--- a/gn/tools/gn/format_test_data/057.gn
+++ b/gn/src/gn/format_test_data/057.gn
diff --git a/gn/tools/gn/format_test_data/057.golden b/gn/src/gn/format_test_data/057.golden
index d0daa6c8e72..d0daa6c8e72 100644
--- a/gn/tools/gn/format_test_data/057.golden
+++ b/gn/src/gn/format_test_data/057.golden
diff --git a/gn/tools/gn/format_test_data/058.gn b/gn/src/gn/format_test_data/058.gn
index 568074a634b..568074a634b 100644
--- a/gn/tools/gn/format_test_data/058.gn
+++ b/gn/src/gn/format_test_data/058.gn
diff --git a/gn/tools/gn/format_test_data/058.golden b/gn/src/gn/format_test_data/058.golden
index 568074a634b..568074a634b 100644
--- a/gn/tools/gn/format_test_data/058.golden
+++ b/gn/src/gn/format_test_data/058.golden
diff --git a/gn/tools/gn/format_test_data/059.gn b/gn/src/gn/format_test_data/059.gn
index ea6fb8e3dc8..ea6fb8e3dc8 100644
--- a/gn/tools/gn/format_test_data/059.gn
+++ b/gn/src/gn/format_test_data/059.gn
diff --git a/gn/tools/gn/format_test_data/059.golden b/gn/src/gn/format_test_data/059.golden
index 423e88882e1..423e88882e1 100644
--- a/gn/tools/gn/format_test_data/059.golden
+++ b/gn/src/gn/format_test_data/059.golden
diff --git a/gn/tools/gn/format_test_data/060.gn b/gn/src/gn/format_test_data/060.gn
index 2b0da79f7cb..2b0da79f7cb 100644
--- a/gn/tools/gn/format_test_data/060.gn
+++ b/gn/src/gn/format_test_data/060.gn
diff --git a/gn/tools/gn/format_test_data/060.golden b/gn/src/gn/format_test_data/060.golden
index 2b0da79f7cb..2b0da79f7cb 100644
--- a/gn/tools/gn/format_test_data/060.golden
+++ b/gn/src/gn/format_test_data/060.golden
diff --git a/gn/tools/gn/format_test_data/061.gn b/gn/src/gn/format_test_data/061.gn
index 5948037fe56..5948037fe56 100644
--- a/gn/tools/gn/format_test_data/061.gn
+++ b/gn/src/gn/format_test_data/061.gn
diff --git a/gn/tools/gn/format_test_data/061.golden b/gn/src/gn/format_test_data/061.golden
index edbf43dc6ad..edbf43dc6ad 100644
--- a/gn/tools/gn/format_test_data/061.golden
+++ b/gn/src/gn/format_test_data/061.golden
diff --git a/gn/src/gn/format_test_data/062.gn b/gn/src/gn/format_test_data/062.gn
new file mode 100644
index 00000000000..c4005914683
--- /dev/null
+++ b/gn/src/gn/format_test_data/062.gn
@@ -0,0 +1,128 @@
+# Sorting, making sure we don't detach comments.
+
+sources = []
+
+sources = ["x.cc"]
+
+sources = [
+ "/a",
+ "/b",
+ "/c",
+ # End of block.
+]
+
+sources += [
+ # Start of block, separate.
+
+ "c",
+ "a",
+ "b",
+]
+
+sources += [
+ "z",
+ "z2",
+ # Attached comment.
+ "y.h",
+ "y.cc",
+ "y.mm",
+ "y.rc",
+ "a"
+]
+
+sources += [
+ "z",
+ "z2",
+
+ # Block comment.
+
+ "y.h",
+ "y.cc",
+ "y.mm",
+ "y.rc",
+ "a"
+]
+
+sources += [
+ "z",
+ "z2",
+
+ #
+ # Multiline block comment.
+ #
+
+ "y.h",
+ "y.cc",
+ "y.mm",
+ "y.rc",
+ "a"
+]
+
+# With identifiers.
+sources += [
+ "a",
+ "b",
+ "c",
+ some_other_thing,
+ abcd,
+]
+
+# With accessors.
+sources += [
+ "a",
+ wee[0],
+ "b",
+ invoker.stuff,
+ "c",
+]
+
+# Various separated blocks.
+sources -= [
+ # Fix this test to build on Windows.
+ "focus_cycler_unittest.cc",
+
+ # All tests for multiple displays: not supported on Windows Ash.
+ "wm/drag_window_resizer_unittest.cc",
+
+ # Accelerometer is only available on Chrome OS.
+ "wm/maximize_mode/maximize_mode_controller_unittest.cc",
+
+ # Can't resize on Windows Ash. http://crbug.com/165962
+ "autoclick/autoclick_unittest.cc",
+ "magnifier/magnification_controller_unittest.cc",
+ # Attached 1.
+ # Attached 2.
+ "wm/workspace/workspace_window_resizer_unittest.cc",
+ "sticky_keys/sticky_keys_overlay_unittest.cc",
+ "system/tray/media_security/multi_profile_media_tray_item_unittest.cc",
+ "virtual_keyboard_controller_unittest.cc",
+
+ # Separated at end.
+ "zzzzzzzzzzzzzz.cc",
+]
+
+sources += [
+ "srtp/crypto/include/xfm.h",
+
+ # sources
+ "srtp/srtp/ekt.c",
+ "srtp/srtp/srtp.c",
+ "srtp/crypto/rng/prng.c",
+ "srtp/crypto/rng/rand_source.c",
+]
+
+# Try "public" too. It should be treated the same.
+public = [
+ # Let's sort
+ "this", "into", "word", "salad",
+
+ # But leave
+ "these", "two"
+ # alone!
+]
+
+# Lists with "sources" suffix should also be sorted.
+foo_sources = [
+ "z",
+ "a",
+]
diff --git a/gn/src/gn/format_test_data/062.golden b/gn/src/gn/format_test_data/062.golden
new file mode 100644
index 00000000000..3d653cebb59
--- /dev/null
+++ b/gn/src/gn/format_test_data/062.golden
@@ -0,0 +1,136 @@
+# Sorting, making sure we don't detach comments.
+
+sources = []
+
+sources = [ "x.cc" ]
+
+sources = [
+ "/a",
+ "/b",
+ "/c",
+
+ # End of block.
+]
+
+sources += [
+ # Start of block, separate.
+
+ "a",
+ "b",
+ "c",
+]
+
+sources += [
+ "a",
+ "y.cc",
+
+ # Attached comment.
+ "y.h",
+ "y.mm",
+ "y.rc",
+ "z",
+ "z2",
+]
+
+sources += [
+ "z",
+ "z2",
+
+ # Block comment.
+
+ "a",
+ "y.cc",
+ "y.h",
+ "y.mm",
+ "y.rc",
+]
+
+sources += [
+ "z",
+ "z2",
+
+ #
+ # Multiline block comment.
+ #
+
+ "a",
+ "y.cc",
+ "y.h",
+ "y.mm",
+ "y.rc",
+]
+
+# With identifiers.
+sources += [
+ "a",
+ "b",
+ "c",
+ abcd,
+ some_other_thing,
+]
+
+# With accessors.
+sources += [
+ "a",
+ "b",
+ "c",
+ invoker.stuff,
+ wee[0],
+]
+
+# Various separated blocks.
+sources -= [
+ # Fix this test to build on Windows.
+ "focus_cycler_unittest.cc",
+
+ # All tests for multiple displays: not supported on Windows Ash.
+ "wm/drag_window_resizer_unittest.cc",
+
+ # Accelerometer is only available on Chrome OS.
+ "wm/maximize_mode/maximize_mode_controller_unittest.cc",
+
+ # Can't resize on Windows Ash. http://crbug.com/165962
+ "autoclick/autoclick_unittest.cc",
+ "magnifier/magnification_controller_unittest.cc",
+ "sticky_keys/sticky_keys_overlay_unittest.cc",
+ "system/tray/media_security/multi_profile_media_tray_item_unittest.cc",
+ "virtual_keyboard_controller_unittest.cc",
+
+ # Attached 1.
+ # Attached 2.
+ "wm/workspace/workspace_window_resizer_unittest.cc",
+
+ # Separated at end.
+ "zzzzzzzzzzzzzz.cc",
+]
+
+sources += [
+ "srtp/crypto/include/xfm.h",
+
+ # sources
+ "srtp/crypto/rng/prng.c",
+ "srtp/crypto/rng/rand_source.c",
+ "srtp/srtp/ekt.c",
+ "srtp/srtp/srtp.c",
+]
+
+# Try "public" too. It should be treated the same.
+public = [
+ # Let's sort
+ "into",
+ "salad",
+ "this",
+ "word",
+
+ # But leave
+ "these",
+ "two",
+
+ # alone!
+]
+
+# Lists with "sources" suffix should also be sorted.
+foo_sources = [
+ "a",
+ "z",
+]
diff --git a/gn/src/gn/format_test_data/063.gn b/gn/src/gn/format_test_data/063.gn
new file mode 100644
index 00000000000..de5002ed4d6
--- /dev/null
+++ b/gn/src/gn/format_test_data/063.gn
@@ -0,0 +1,50 @@
+source_set("test") {
+ a = "a"
+ b = "b"
+ deps = [
+ "//a",
+ "//a/a",
+ "//a/b",
+ "//a:a",
+ "//a:b",
+ "//b",
+ ":a",
+ ":b",
+ "a",
+ "a/a",
+ "a/b",
+ "a:a",
+ "a:b",
+ "b",
+ a,
+ b,
+ ]
+
+ public_deps = []
+ if (condition) {
+ public_deps += [
+ "//a",
+ "//a/a",
+ "//a:a",
+ ":a",
+ "a",
+ "a/a",
+ "a:a",
+ a,
+ ]
+ }
+
+ # Sort lists with "deps" suffix as "deps".
+ foo_deps = [
+ "//a",
+ ":z",
+ ]
+
+ # Likewise for visibility
+ visibility = [
+ "//b:*",
+ "//a",
+ "//b/*",
+ ":z",
+ ]
+}
diff --git a/gn/src/gn/format_test_data/063.golden b/gn/src/gn/format_test_data/063.golden
new file mode 100644
index 00000000000..8972886ca6a
--- /dev/null
+++ b/gn/src/gn/format_test_data/063.golden
@@ -0,0 +1,50 @@
+source_set("test") {
+ a = "a"
+ b = "b"
+ deps = [
+ ":a",
+ ":b",
+ "a",
+ "a:a",
+ "a:b",
+ "a/a",
+ "a/b",
+ "b",
+ "//a",
+ "//a:a",
+ "//a:b",
+ "//a/a",
+ "//a/b",
+ "//b",
+ a,
+ b,
+ ]
+
+ public_deps = []
+ if (condition) {
+ public_deps += [
+ ":a",
+ "a",
+ "a:a",
+ "a/a",
+ "//a",
+ "//a:a",
+ "//a/a",
+ a,
+ ]
+ }
+
+ # Sort lists with "deps" suffix as "deps".
+ foo_deps = [
+ ":z",
+ "//a",
+ ]
+
+ # Likewise for visibility
+ visibility = [
+ ":z",
+ "//a",
+ "//b:*",
+ "//b/*",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/064.gn b/gn/src/gn/format_test_data/064.gn
index c1867250fe5..c1867250fe5 100644
--- a/gn/tools/gn/format_test_data/064.gn
+++ b/gn/src/gn/format_test_data/064.gn
diff --git a/gn/src/gn/format_test_data/064.golden b/gn/src/gn/format_test_data/064.golden
new file mode 100644
index 00000000000..c1867250fe5
--- /dev/null
+++ b/gn/src/gn/format_test_data/064.golden
@@ -0,0 +1,3 @@
+source_set("test") {
+ deps = [ rebase_path(sdk_dep, ".", mojo_root) ]
+}
diff --git a/gn/tools/gn/format_test_data/065.gn b/gn/src/gn/format_test_data/065.gn
index a4489092232..a4489092232 100644
--- a/gn/tools/gn/format_test_data/065.gn
+++ b/gn/src/gn/format_test_data/065.gn
diff --git a/gn/tools/gn/format_test_data/065.golden b/gn/src/gn/format_test_data/065.golden
index 5df85fda4df..5df85fda4df 100644
--- a/gn/tools/gn/format_test_data/065.golden
+++ b/gn/src/gn/format_test_data/065.golden
diff --git a/gn/tools/gn/format_test_data/066.gn b/gn/src/gn/format_test_data/066.gn
index 0a79d0cd738..0a79d0cd738 100644
--- a/gn/tools/gn/format_test_data/066.gn
+++ b/gn/src/gn/format_test_data/066.gn
diff --git a/gn/src/gn/format_test_data/066.golden b/gn/src/gn/format_test_data/066.golden
new file mode 100644
index 00000000000..bdcf055bfb9
--- /dev/null
+++ b/gn/src/gn/format_test_data/066.golden
@@ -0,0 +1,28 @@
+# Suppress sorting based on comment.
+
+# NOSORT
+sources = []
+
+# NOSORT
+sources = [ "a" ]
+
+# NOSORT
+sources += [ "a" ]
+
+# NOSORT
+# This is NOSORT because reason.
+sources = [
+ "z",
+ "z2",
+ "a",
+ "y.cc",
+]
+
+# This is NOSORT because reason:
+# NOSORT
+sources += [
+ "z",
+ "z2",
+ "a",
+ "y.cc",
+]
diff --git a/gn/tools/gn/format_test_data/067.gn b/gn/src/gn/format_test_data/067.gn
index b3d5eaa6ce0..b3d5eaa6ce0 100644
--- a/gn/tools/gn/format_test_data/067.gn
+++ b/gn/src/gn/format_test_data/067.gn
diff --git a/gn/tools/gn/format_test_data/067.golden b/gn/src/gn/format_test_data/067.golden
index 9ce2fa77276..9ce2fa77276 100644
--- a/gn/tools/gn/format_test_data/067.golden
+++ b/gn/src/gn/format_test_data/067.golden
diff --git a/gn/tools/gn/format_test_data/068.gn b/gn/src/gn/format_test_data/068.gn
index 7fb8e1b00d4..7fb8e1b00d4 100644
--- a/gn/tools/gn/format_test_data/068.gn
+++ b/gn/src/gn/format_test_data/068.gn
diff --git a/gn/tools/gn/format_test_data/068.golden b/gn/src/gn/format_test_data/068.golden
index 7fb8e1b00d4..7fb8e1b00d4 100644
--- a/gn/tools/gn/format_test_data/068.golden
+++ b/gn/src/gn/format_test_data/068.golden
diff --git a/gn/tools/gn/format_test_data/069.gn b/gn/src/gn/format_test_data/069.gn
index 200b3131272..200b3131272 100644
--- a/gn/tools/gn/format_test_data/069.gn
+++ b/gn/src/gn/format_test_data/069.gn
diff --git a/gn/tools/gn/format_test_data/069.golden b/gn/src/gn/format_test_data/069.golden
index 64d4acce040..64d4acce040 100644
--- a/gn/tools/gn/format_test_data/069.golden
+++ b/gn/src/gn/format_test_data/069.golden
diff --git a/gn/tools/gn/format_test_data/070.gn b/gn/src/gn/format_test_data/070.gn
index 09d80d2b02e..09d80d2b02e 100644
--- a/gn/tools/gn/format_test_data/070.gn
+++ b/gn/src/gn/format_test_data/070.gn
diff --git a/gn/tools/gn/format_test_data/070.golden b/gn/src/gn/format_test_data/070.golden
index 0997aa47743..0997aa47743 100644
--- a/gn/tools/gn/format_test_data/070.golden
+++ b/gn/src/gn/format_test_data/070.golden
diff --git a/gn/tools/gn/format_test_data/071.gn b/gn/src/gn/format_test_data/071.gn
index 665d099558d..665d099558d 100644
--- a/gn/tools/gn/format_test_data/071.gn
+++ b/gn/src/gn/format_test_data/071.gn
diff --git a/gn/tools/gn/format_test_data/071.golden b/gn/src/gn/format_test_data/071.golden
index 6d8f98f296b..6d8f98f296b 100644
--- a/gn/tools/gn/format_test_data/071.golden
+++ b/gn/src/gn/format_test_data/071.golden
diff --git a/gn/tools/gn/format_test_data/072.gn b/gn/src/gn/format_test_data/072.gn
index 10b5a815140..10b5a815140 100644
--- a/gn/tools/gn/format_test_data/072.gn
+++ b/gn/src/gn/format_test_data/072.gn
diff --git a/gn/tools/gn/format_test_data/072.golden b/gn/src/gn/format_test_data/072.golden
index b4e5a4b4151..b4e5a4b4151 100644
--- a/gn/tools/gn/format_test_data/072.golden
+++ b/gn/src/gn/format_test_data/072.golden
diff --git a/gn/tools/gn/format_test_data/073.gn b/gn/src/gn/format_test_data/073.gn
index e3a65614430..e3a65614430 100644
--- a/gn/tools/gn/format_test_data/073.gn
+++ b/gn/src/gn/format_test_data/073.gn
diff --git a/gn/tools/gn/format_test_data/073.golden b/gn/src/gn/format_test_data/073.golden
index e32977b2d30..e32977b2d30 100644
--- a/gn/tools/gn/format_test_data/073.golden
+++ b/gn/src/gn/format_test_data/073.golden
diff --git a/gn/tools/gn/format_test_data/074.gn b/gn/src/gn/format_test_data/074.gn
index d5e5101ff59..d5e5101ff59 100644
--- a/gn/tools/gn/format_test_data/074.gn
+++ b/gn/src/gn/format_test_data/074.gn
diff --git a/gn/tools/gn/format_test_data/074.golden b/gn/src/gn/format_test_data/074.golden
index d5e5101ff59..d5e5101ff59 100644
--- a/gn/tools/gn/format_test_data/074.golden
+++ b/gn/src/gn/format_test_data/074.golden
diff --git a/gn/tools/gn/format_test_data/075.gn b/gn/src/gn/format_test_data/075.gn
index 8a3c3275298..8a3c3275298 100644
--- a/gn/tools/gn/format_test_data/075.gn
+++ b/gn/src/gn/format_test_data/075.gn
diff --git a/gn/tools/gn/format_test_data/075.golden b/gn/src/gn/format_test_data/075.golden
index d181bd689cc..d181bd689cc 100644
--- a/gn/tools/gn/format_test_data/075.golden
+++ b/gn/src/gn/format_test_data/075.golden
diff --git a/gn/src/gn/format_test_data/076.gn b/gn/src/gn/format_test_data/076.gn
new file mode 100644
index 00000000000..ea617dc90b9
--- /dev/null
+++ b/gn/src/gn/format_test_data/076.gn
@@ -0,0 +1,12 @@
+# Intentionally contains tabs which the formatter should convert to spaces, but
+# only where it's not relevant to the parse.
+
+if (true) {
+ sources = [
+ "a.c", "a.h", "main.c"
+ ]
+}
+
+if (false) {
+ embedded = "a tab to leave alone"
+}
diff --git a/gn/src/gn/format_test_data/076.golden b/gn/src/gn/format_test_data/076.golden
new file mode 100644
index 00000000000..b682482b482
--- /dev/null
+++ b/gn/src/gn/format_test_data/076.golden
@@ -0,0 +1,14 @@
+# Intentionally contains tabs which the formatter should convert to spaces, but
+# only where it's not relevant to the parse.
+
+if (true) {
+ sources = [
+ "a.c",
+ "a.h",
+ "main.c",
+ ]
+}
+
+if (false) {
+ embedded = "a tab to leave alone"
+}
diff --git a/gn/src/gn/format_test_data/077.gn b/gn/src/gn/format_test_data/077.gn
new file mode 100644
index 00000000000..665d420e627
--- /dev/null
+++ b/gn/src/gn/format_test_data/077.gn
@@ -0,0 +1,6 @@
+# Regression test for https://crbug.com/gn/138. 80 col -------------------------
+foo("bar") {
+ blah = [
+ "$target_gen_dir/run-lit", # Non-existing, so that ninja runs it each time.
+ ]
+}
diff --git a/gn/src/gn/format_test_data/077.golden b/gn/src/gn/format_test_data/077.golden
new file mode 100644
index 00000000000..665d420e627
--- /dev/null
+++ b/gn/src/gn/format_test_data/077.golden
@@ -0,0 +1,6 @@
+# Regression test for https://crbug.com/gn/138. 80 col -------------------------
+foo("bar") {
+ blah = [
+ "$target_gen_dir/run-lit", # Non-existing, so that ninja runs it each time.
+ ]
+}
diff --git a/gn/src/gn/format_test_data/078.gn b/gn/src/gn/format_test_data/078.gn
new file mode 100644
index 00000000000..9b1369a6cb5
--- /dev/null
+++ b/gn/src/gn/format_test_data/078.gn
@@ -0,0 +1,15 @@
+# 80 ---------------------------------------------------------------------------
+# Long suffix comments, and including trailing spaces.
+config("compiler") {
+ if (is_win) {
+ if (is_component_build) {
+ cflags += [
+ "/EHsc", # These are some very long suffix comment words aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ # bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+ # cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+ # dddddddddddddddd ddddddddddd dddddddd ddddddddddddd ddddddddddddddd
+ ]
+ }
+ }
+}
+
diff --git a/gn/src/gn/format_test_data/078.golden b/gn/src/gn/format_test_data/078.golden
new file mode 100644
index 00000000000..ea9658014a3
--- /dev/null
+++ b/gn/src/gn/format_test_data/078.golden
@@ -0,0 +1,16 @@
+# 80 ---------------------------------------------------------------------------
+# Long suffix comments, and including trailing spaces.
+config("compiler") {
+ if (is_win) {
+ if (is_component_build) {
+ cflags += [
+ "/EHsc", # These are some very long suffix comment words
+ # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ # bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+ # cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+ # dddddddddddddddd ddddddddddd dddddddd ddddddddddddd
+ # ddddddddddddddd
+ ]
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/079.gn b/gn/src/gn/format_test_data/079.gn
new file mode 100644
index 00000000000..9218757aaec
--- /dev/null
+++ b/gn/src/gn/format_test_data/079.gn
@@ -0,0 +1,14 @@
+# 80 ---------------------------------------------------------------------------
+# Somewhat more intelligent suffix comments.
+
+core_generated_interface_idl_files = generated_webcore_testing_idl_files # interfaces
+
+core_generated_interface_idl_files = generated_webcore_testing_idl_files_and_some_more_longer # stuff
+
+if (true) {
+ if (true) {
+ if (true) {
+ dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}" # e.g. foo.dll
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/079.golden b/gn/src/gn/format_test_data/079.golden
new file mode 100644
index 00000000000..c4617b55c5b
--- /dev/null
+++ b/gn/src/gn/format_test_data/079.golden
@@ -0,0 +1,18 @@
+# 80 ---------------------------------------------------------------------------
+# Somewhat more intelligent suffix comments.
+
+core_generated_interface_idl_files =
+ generated_webcore_testing_idl_files # interfaces
+
+core_generated_interface_idl_files =
+ generated_webcore_testing_idl_files_and_some_more_longer # stuff
+
+if (true) {
+ if (true) {
+ if (true) {
+ dllname =
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}" # e.g.
+ # foo.dll
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/080.gn b/gn/src/gn/format_test_data/080.gn
new file mode 100644
index 00000000000..3e3fdba6228
--- /dev/null
+++ b/gn/src/gn/format_test_data/080.gn
@@ -0,0 +1,33 @@
+# https://crbug.com/gn/141. 80 col ---------------------------------------------
+
+a =
+ [ "b" ] # comment1
+
+a =
+ [ "b", "c" ] # comment1b
+
+a =
+ b # comment2
+
+a =
+ { b = 3 } # comment3
+
+a =
+ { # comment4
+
+ b = 3 } # comment5
+
+a =
+ { b = 3 # comment6
+ }
+
+if (true) {
+ if (true) {
+ if (true) {
+ something_longer_on_the_lhs =
+ [ "something that will exceed 80 col if dewrapped" ] # comment7
+
+ something_longer_on_the_lhs = [ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] # comment8
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/080.golden b/gn/src/gn/format_test_data/080.golden
new file mode 100644
index 00000000000..9f18a5e6fa9
--- /dev/null
+++ b/gn/src/gn/format_test_data/080.golden
@@ -0,0 +1,34 @@
+# https://crbug.com/gn/141. 80 col ---------------------------------------------
+
+a = [ "b" ] # comment1
+
+a = [
+ "b",
+ "c",
+] # comment1b
+
+a = b # comment2
+
+a = {
+ b = 3
+} # comment3
+
+a = { # comment4
+ b = 3
+} # comment5
+
+a = {
+ b = 3 # comment6
+}
+
+if (true) {
+ if (true) {
+ if (true) {
+ something_longer_on_the_lhs =
+ [ "something that will exceed 80 col if dewrapped" ] # comment7
+
+ something_longer_on_the_lhs =
+ [ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] # comment8
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/081.gn b/gn/src/gn/format_test_data/081.gn
new file mode 100644
index 00000000000..d9d494e6c16
--- /dev/null
+++ b/gn/src/gn/format_test_data/081.gn
@@ -0,0 +1,18 @@
+inputs = idl_lexer_parser_files + idl_compiler_files # to be explicit (covered by parsetab)
+inputs += "hi"
+
+if (true) {
+ if (true) {
+ inputs = idl_lexer_parser_files + idl_compiler_files # to be explicit (covered by parsetab)
+ inputs += "hi"
+ }
+}
+
+if (true) {
+ if (something) {
+ a = b
+ } else { # !is_chromeos
+ os_category = current_os
+ }
+ no_blank_here = true
+}
diff --git a/gn/src/gn/format_test_data/081.golden b/gn/src/gn/format_test_data/081.golden
new file mode 100644
index 00000000000..c4ee25f4547
--- /dev/null
+++ b/gn/src/gn/format_test_data/081.golden
@@ -0,0 +1,21 @@
+inputs = idl_lexer_parser_files + idl_compiler_files # to be explicit (covered
+ # by parsetab)
+inputs += "hi"
+
+if (true) {
+ if (true) {
+ inputs =
+ idl_lexer_parser_files + idl_compiler_files # to be explicit (covered
+ # by parsetab)
+ inputs += "hi"
+ }
+}
+
+if (true) {
+ if (something) {
+ a = b
+ } else { # !is_chromeos
+ os_category = current_os
+ }
+ no_blank_here = true
+}
diff --git a/gn/src/gn/format_test_data/082.gn b/gn/src/gn/format_test_data/082.gn
new file mode 100644
index 00000000000..2ced597c22a
--- /dev/null
+++ b/gn/src/gn/format_test_data/082.gn
@@ -0,0 +1,34 @@
+# https://crbug.com/gn/156 -----------------------------------------------------
+if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+ (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ||
+ cccccccccccccccccccccc)) {
+}
+
+if (!is_nacl && !use_libfzzzzuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzer &&
+ (target_os == "android" || target_os == "chromeos" ||
+ target_os == "fuchsia" || target_os == "linux" ||
+ target_os == "winnnnn")) {
+ print("hi")
+}
+
+foo("bar") {
+ if (foo) {
+ if (!is_nacl && !use_libfuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzer &&
+ (target_os == "android" || target_os == "chromeos" ||
+ target_os == "fuchsia" || target_os == "linux" ||
+ target_os == "win")) {
+ cflags += [ "-Wunreachable-code" ]
+ }
+ }
+}
+
+foo("bar") {
+ if (foo) {
+ if (!is_nacl && !use_libfuzzer &&
+ (target_os == "android" || target_os == "chromeos" ||
+ target_os == "fuchsia" || target_os == "linux" ||
+ target_os == "win")) {
+ cflags += [ "-Wunreachable-code" ]
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/082.golden b/gn/src/gn/format_test_data/082.golden
new file mode 100644
index 00000000000..2ced597c22a
--- /dev/null
+++ b/gn/src/gn/format_test_data/082.golden
@@ -0,0 +1,34 @@
+# https://crbug.com/gn/156 -----------------------------------------------------
+if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+ (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ||
+ cccccccccccccccccccccc)) {
+}
+
+if (!is_nacl && !use_libfzzzzuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzer &&
+ (target_os == "android" || target_os == "chromeos" ||
+ target_os == "fuchsia" || target_os == "linux" ||
+ target_os == "winnnnn")) {
+ print("hi")
+}
+
+foo("bar") {
+ if (foo) {
+ if (!is_nacl && !use_libfuzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzer &&
+ (target_os == "android" || target_os == "chromeos" ||
+ target_os == "fuchsia" || target_os == "linux" ||
+ target_os == "win")) {
+ cflags += [ "-Wunreachable-code" ]
+ }
+ }
+}
+
+foo("bar") {
+ if (foo) {
+ if (!is_nacl && !use_libfuzzer &&
+ (target_os == "android" || target_os == "chromeos" ||
+ target_os == "fuchsia" || target_os == "linux" ||
+ target_os == "win")) {
+ cflags += [ "-Wunreachable-code" ]
+ }
+ }
+}
diff --git a/gn/src/gn/format_test_data/083.gn b/gn/src/gn/format_test_data/083.gn
new file mode 100644
index 00000000000..6936bf57f53
--- /dev/null
+++ b/gn/src/gn/format_test_data/083.gn
@@ -0,0 +1,7 @@
+# Crash found by afl-fuzz (non-literal import).
+import("x")
+import(sources)
+import(zip)
+import(zap)
+import(a+b+c+d)
+import(zzz+yyy)
diff --git a/gn/src/gn/format_test_data/083.golden b/gn/src/gn/format_test_data/083.golden
new file mode 100644
index 00000000000..445534d2686
--- /dev/null
+++ b/gn/src/gn/format_test_data/083.golden
@@ -0,0 +1,7 @@
+# Crash found by afl-fuzz (non-literal import).
+import(sources)
+import(zip)
+import(zap)
+import(a + b + c + d)
+import(zzz + yyy)
+import("x")
diff --git a/gn/src/gn/frameworks_utils.cc b/gn/src/gn/frameworks_utils.cc
new file mode 100644
index 00000000000..4aba827432b
--- /dev/null
+++ b/gn/src/gn/frameworks_utils.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium 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 "gn/frameworks_utils.h"
+
+#include "gn/filesystem_utils.h"
+
+namespace {
+
+// Name of the extension of frameworks.
+const char kFrameworkExtension[] = "framework";
+
+} // anonymous namespace
+
+std::string_view GetFrameworkName(const std::string& file) {
+ if (FindFilenameOffset(file) != 0)
+ return std::string_view();
+
+ std::string_view extension = FindExtension(&file);
+ if (extension != kFrameworkExtension)
+ return std::string_view();
+
+ return FindFilenameNoExtension(&file);
+}
diff --git a/gn/src/gn/frameworks_utils.h b/gn/src/gn/frameworks_utils.h
new file mode 100644
index 00000000000..b9df3e77c8d
--- /dev/null
+++ b/gn/src/gn/frameworks_utils.h
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_FRAMEWORKS_UTILS_H_
+#define TOOLS_GN_FRAMEWORKS_UTILS_H_
+
+#include <string>
+#include <string_view>
+
+// Returns the name of the framework from a file name. This returns an empty
+// string_view if the name is incorrect (does not ends in ".framework", ...).
+std::string_view GetFrameworkName(const std::string& file);
+
+#endif // TOOLS_GN_FRAMEWORKS_UTILS_H_
diff --git a/gn/src/gn/frameworks_utils_unittest.cc b/gn/src/gn/frameworks_utils_unittest.cc
new file mode 100644
index 00000000000..3e208230520
--- /dev/null
+++ b/gn/src/gn/frameworks_utils_unittest.cc
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium 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 "gn/frameworks_utils.h"
+
+#include "util/test/test.h"
+
+TEST(FrameworksUtils, GetFrameworkName) {
+ const std::string kFramework = "Foundation.framework";
+ const std::string kFrameworkNoExtension = "Foundation";
+ const std::string kFrameworkPath = "Foo/Foo.framework";
+
+ EXPECT_EQ("Foundation", GetFrameworkName(kFramework));
+ EXPECT_EQ("", GetFrameworkName(kFrameworkNoExtension));
+ EXPECT_EQ("", GetFrameworkName(kFrameworkPath));
+}
diff --git a/gn/src/gn/function_exec_script.cc b/gn/src/gn/function_exec_script.cc
new file mode 100644
index 00000000000..f5f24aa596c
--- /dev/null
+++ b/gn/src/gn/function_exec_script.cc
@@ -0,0 +1,278 @@
+// 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 "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/err.h"
+#include "gn/exec_process.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/input_conversion.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "gn/trace.h"
+#include "gn/value.h"
+#include "util/build_config.h"
+#include "util/ticks.h"
+
+namespace functions {
+
+namespace {
+
+bool CheckExecScriptPermissions(const BuildSettings* build_settings,
+ const FunctionCallNode* function,
+ Err* err) {
+ const SourceFileSet* whitelist = build_settings->exec_script_whitelist();
+ if (!whitelist)
+ return true; // No whitelist specified, don't check.
+
+ LocationRange function_range = function->GetRange();
+ if (!function_range.begin().file())
+ return true; // No file, might be some internal thing, implicitly pass.
+
+ if (whitelist->find(function_range.begin().file()->name()) !=
+ whitelist->end())
+ return true; // Whitelisted, this is OK.
+
+ // Disallowed case.
+ *err = Err(
+ function, "Disallowed exec_script call.",
+ "The use of exec_script use is restricted in this build. exec_script\n"
+ "is discouraged because it can slow down the GN run and is easily\n"
+ "abused.\n"
+ "\n"
+ "Generally nontrivial work should be done as build steps rather than\n"
+ "when GN is run. For example, if you need to compute a nontrivial\n"
+ "preprocessor define, it will be better to have an action target\n"
+ "generate a header containing the define rather than blocking the GN\n"
+ "run to compute the value.\n"
+ "\n"
+ "The allowed callers of exec_script is maintained in the \"//.gn\" file\n"
+ "if you need to modify the whitelist.");
+ return false;
+}
+
+} // namespace
+
+const char kExecScript[] = "exec_script";
+const char kExecScript_HelpShort[] =
+ "exec_script: Synchronously run a script and return the output.";
+const char kExecScript_Help[] =
+ R"(exec_script: Synchronously run a script and return the output.
+
+ exec_script(filename,
+ arguments = [],
+ input_conversion = "",
+ file_dependencies = [])
+
+ Runs the given script, returning the stdout of the script. The build
+ generation will fail if the script does not exist or returns a nonzero exit
+ code.
+
+ The current directory when executing the script will be the root build
+ directory. If you are passing file names, you will want to use the
+ rebase_path() function to make file names relative to this path (see "gn help
+ rebase_path").
+
+ The default script interpreter is Python ("python" on POSIX, "python.exe" or
+ "python.bat" on Windows). This can be configured by the script_executable
+ variable, see "gn help dotfile".
+
+Arguments:
+
+ filename:
+ File name of script to execute. Non-absolute names will be treated as
+ relative to the current build file.
+
+ arguments:
+ A list of strings to be passed to the script as arguments. May be
+ unspecified or the empty list which means no arguments.
+
+ input_conversion:
+ Controls how the file is read and parsed. See "gn help io_conversion".
+
+ If unspecified, defaults to the empty string which causes the script
+ result to be discarded. exec script will return None.
+
+ dependencies:
+ (Optional) A list of files that this script reads or otherwise depends
+ on. These dependencies will be added to the build result such that if any
+ of them change, the build will be regenerated and the script will be
+ re-run.
+
+ The script itself will be an implicit dependency so you do not need to
+ list it.
+
+Example
+
+ all_lines = exec_script(
+ "myscript.py", [some_input], "list lines",
+ [ rebase_path("data_file.txt", root_build_dir) ])
+
+ # This example just calls the script with no arguments and discards the
+ # result.
+ exec_script("//foo/bar/myscript.py")
+)";
+
+Value RunExecScript(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() < 1 || args.size() > 4) {
+ *err = Err(function->function(), "Wrong number of arguments to exec_script",
+ "I expected between one and four arguments.");
+ return Value();
+ }
+
+ const Settings* settings = scope->settings();
+ const BuildSettings* build_settings = settings->build_settings();
+ const SourceDir& cur_dir = scope->GetSourceDir();
+
+ if (!CheckExecScriptPermissions(build_settings, function, err))
+ return Value();
+
+ // Find the script to run.
+ std::string script_source_path = cur_dir.ResolveRelativeAs(
+ true, args[0], err,
+ scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error())
+ return Value();
+ base::FilePath script_path =
+ build_settings->GetFullPath(script_source_path, true);
+ if (!build_settings->secondary_source_path().empty() &&
+ !base::PathExists(script_path)) {
+ // Fall back to secondary source root when the file doesn't exist.
+ script_path =
+ build_settings->GetFullPathSecondary(script_source_path, true);
+ }
+
+ ScopedTrace trace(TraceItem::TRACE_SCRIPT_EXECUTE, script_source_path);
+ trace.SetToolchain(settings->toolchain_label());
+
+ // Add all dependencies of this script, including the script itself, to the
+ // build deps.
+ g_scheduler->AddGenDependency(script_path);
+ if (args.size() == 4) {
+ const Value& deps_value = args[3];
+ if (!deps_value.VerifyTypeIs(Value::LIST, err))
+ return Value();
+
+ for (const auto& dep : deps_value.list_value()) {
+ if (!dep.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ g_scheduler->AddGenDependency(build_settings->GetFullPath(
+ cur_dir.ResolveRelativeAs(
+ true, dep, err,
+ scope->settings()->build_settings()->root_path_utf8()),
+ true));
+ if (err->has_error())
+ return Value();
+ }
+ }
+
+ // Make the command line.
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ // CommandLine tries to interpret arguments by default. Disable that so
+ // that the arguments will be passed through exactly as specified.
+ cmdline.SetParseSwitches(false);
+
+ // If an interpreter path is set, initialize it as the first entry and
+ // pass script_path as the first argument. Otherwise, set the
+ // program to script_path directly.
+ const base::FilePath& interpreter_path = build_settings->python_path();
+ if (!interpreter_path.empty()) {
+ cmdline.SetProgram(interpreter_path);
+ cmdline.AppendArgPath(script_path);
+ } else {
+ cmdline.SetProgram(script_path);
+ }
+
+ if (args.size() >= 2) {
+ // Optional command-line arguments to the script.
+ const Value& script_args = args[1];
+ if (!script_args.VerifyTypeIs(Value::LIST, err))
+ return Value();
+ for (const auto& arg : script_args.list_value()) {
+ if (!arg.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ cmdline.AppendArg(arg.string_value());
+ }
+ }
+
+ // Log command line for debugging help.
+ trace.SetCommandLine(cmdline);
+ Ticks begin_exec = 0;
+ if (g_scheduler->verbose_logging()) {
+#if defined(OS_WIN)
+ g_scheduler->Log("Executing",
+ base::UTF16ToUTF8(cmdline.GetCommandLineString()));
+#else
+ g_scheduler->Log("Executing", cmdline.GetCommandLineString());
+#endif
+ begin_exec = TicksNow();
+ }
+
+ base::FilePath startup_dir =
+ build_settings->GetFullPath(build_settings->build_dir());
+ // The first time a build is run, no targets will have been written so the
+ // build output directory won't exist. We need to make sure it does before
+ // running any scripts with this as its startup directory, although it will
+ // be relatively rare that the directory won't exist by the time we get here.
+ //
+ // If this shows up on benchmarks, we can cache whether we've done this
+ // or not and skip creating the directory.
+ base::CreateDirectory(startup_dir);
+
+ // Execute the process.
+ // TODO(brettw) set the environment block.
+ std::string output;
+ std::string stderr_output;
+ int exit_code = 0;
+ {
+ if (!internal::ExecProcess(cmdline, startup_dir, &output, &stderr_output,
+ &exit_code)) {
+ *err = Err(
+ function->function(), "Could not execute interpreter.",
+ "I was trying to execute \"" + FilePathToUTF8(interpreter_path) +
+ "\".");
+ return Value();
+ }
+ }
+ if (g_scheduler->verbose_logging()) {
+ g_scheduler->Log(
+ "Executing",
+ script_source_path + " took " +
+ base::Int64ToString(
+ TicksDelta(TicksNow(), begin_exec).InMilliseconds()) +
+ "ms");
+ }
+
+ if (exit_code != 0) {
+ std::string msg =
+ "Current dir: " + FilePathToUTF8(startup_dir) +
+ "\nCommand: " + FilePathToUTF8(cmdline.GetCommandLineString()) +
+ "\nReturned " + base::IntToString(exit_code);
+ if (!output.empty())
+ msg += " and printed out:\n\n" + output;
+ else
+ msg += ".";
+ if (!stderr_output.empty())
+ msg += "\nstderr:\n\n" + stderr_output;
+
+ *err =
+ Err(function->function(), "Script returned non-zero exit code.", msg);
+ return Value();
+ }
+
+ // Default to None value for the input conversion if unspecified.
+ return ConvertInputToValue(scope->settings(), output, function,
+ args.size() >= 3 ? args[2] : Value(), err);
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_filter.cc b/gn/src/gn/function_filter.cc
new file mode 100644
index 00000000000..49eacfa1f05
--- /dev/null
+++ b/gn/src/gn/function_filter.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 <stddef.h>
+
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+
+namespace functions {
+
+const char kFilterExclude[] = "filter_exclude";
+const char kFilterExclude_HelpShort[] =
+ "filter_exclude: Remove values that match a set of patterns.";
+const char kFilterExclude_Help[] =
+ R"(filter_exclude: Remove values that match a set of patterns.
+
+ filter_exclude(values, exclude_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument exclude_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Any elements in values matching at least one
+ of those patterns will be excluded.
+
+Examples
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_exclude(values, [ "*.proto" ])
+ # result will be [ "foo.cc", "foo.h" ]
+)";
+
+const char kFilterInclude[] = "filter_include";
+const char kFilterInclude_HelpShort[] =
+ "filter_include: Remove values that do not match a set of patterns.";
+const char kFilterInclude_Help[] =
+ R"(filter_include: Remove values that do not match a set of patterns.
+
+ filter_include(values, include_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument include_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Only elements from values matching at least
+ one of the pattern will be included.
+
+Examples
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_include(values, [ "*.proto" ])
+ # result will be [ "foo.proto" ]
+)";
+
+namespace {
+
+enum FilterSelection {
+ kExcludeFilter,
+ kIncludeFilter,
+};
+
+Value RunFilter(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ FilterSelection selection,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function, "Expecting exactly two arguments.");
+ return Value();
+ }
+
+ // Extract "values".
+ if (args[0].type() != Value::LIST) {
+ *err = Err(args[0], "First argument must be a list of strings.");
+ return Value();
+ }
+
+ // Extract "patterns".
+ PatternList patterns;
+ patterns.SetFromValue(args[1], err);
+ if (err->has_error())
+ return Value();
+
+ Value result(function, Value::LIST);
+ for (const auto& value : args[0].list_value()) {
+ if (value.type() != Value::STRING) {
+ *err = Err(args[0], "First argument must be a list of strings.");
+ return Value();
+ }
+
+ const bool matches_pattern = patterns.MatchesValue(value);
+ switch (selection) {
+ case kIncludeFilter:
+ if (matches_pattern)
+ result.list_value().push_back(value);
+ break;
+
+ case kExcludeFilter:
+ if (!matches_pattern)
+ result.list_value().push_back(value);
+ break;
+ }
+ }
+ return result;
+}
+
+} // anonymous namespace
+
+Value RunFilterExclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ return RunFilter(scope, function, args, kExcludeFilter, err);
+}
+
+Value RunFilterInclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ return RunFilter(scope, function, args, kIncludeFilter, err);
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_filter_unittest.cc b/gn/src/gn/function_filter_unittest.cc
new file mode 100644
index 00000000000..4269f18e3b4
--- /dev/null
+++ b/gn/src/gn/function_filter_unittest.cc
@@ -0,0 +1,244 @@
+// 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 "gn/functions.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(FilterExcludeTest, Filter) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(result.type(), Value::LIST);
+ ASSERT_EQ(result.list_value().size(), 2);
+ EXPECT_EQ(result.list_value()[0].type(), Value::STRING);
+ EXPECT_EQ(result.list_value()[0].string_value(), "foo.cc");
+ EXPECT_EQ(result.list_value()[1].type(), Value::STRING);
+ EXPECT_EQ(result.list_value()[1].string_value(), "foo.h");
+}
+
+TEST(FilterExcludeTest, NotEnoughArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, TooManyArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ Value extra_argument(nullptr, Value::LIST);
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns, extra_argument};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, IncorrectValuesType) {
+ TestWithScope setup;
+
+ Value values(nullptr, "foo.cc");
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, IncorrectValuesElementType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, Value::LIST));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, IncorrectPatternsType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, "foo.cc");
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, Filter) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(result.type(), Value::LIST);
+ ASSERT_EQ(result.list_value().size(), 1);
+ EXPECT_EQ(result.list_value()[0].type(), Value::STRING);
+ EXPECT_EQ(result.list_value()[0].string_value(), "foo.proto");
+}
+
+TEST(FilterIncludeTest, NotEnoughArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, TooManyArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ Value extra_argument(nullptr, Value::LIST);
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns, extra_argument};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, IncorrectValuesType) {
+ TestWithScope setup;
+
+ Value values(nullptr, "foo.cc");
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, IncorrectValuesElementType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, Value::LIST));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, IncorrectPatternsType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, "foo.cc");
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
diff --git a/gn/src/gn/function_foreach.cc b/gn/src/gn/function_foreach.cc
new file mode 100644
index 00000000000..9c16e342c2a
--- /dev/null
+++ b/gn/src/gn/function_foreach.cc
@@ -0,0 +1,112 @@
+// 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 "gn/err.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+
+namespace functions {
+
+const char kForEach[] = "foreach";
+const char kForEach_HelpShort[] = "foreach: Iterate over a list.";
+const char kForEach_Help[] =
+ R"(foreach: Iterate over a list.
+
+ foreach(<loop_var>, <list>) {
+ <loop contents>
+ }
+
+ Executes the loop contents block over each item in the list, assigning the
+ loop_var to each item in sequence. The <loop_var> will be a copy so assigning
+ to it will not mutate the list. The loop will iterate over a copy of <list>
+ so mutating it inside the loop will not affect iteration.
+
+ The block does not introduce a new scope, so that variable assignments inside
+ the loop will be visible once the loop terminates.
+
+ The loop variable will temporarily shadow any existing variables with the
+ same name for the duration of the loop. After the loop terminates the loop
+ variable will no longer be in scope, and the previous value (if any) will be
+ restored.
+
+Example
+
+ mylist = [ "a", "b", "c" ]
+ foreach(i, mylist) {
+ print(i)
+ }
+
+ Prints:
+ a
+ b
+ c
+)";
+
+Value RunForEach(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() != 2) {
+ *err = Err(function, "Wrong number of arguments to foreach().",
+ "Expecting exactly two.");
+ return Value();
+ }
+
+ // Extract the loop variable.
+ const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
+ if (!identifier) {
+ *err =
+ Err(args_vector[0].get(), "Expected an identifier for the loop var.");
+ return Value();
+ }
+ std::string_view loop_var(identifier->value().value());
+
+ // Extract the list to iterate over. Always copy in case the code changes
+ // the list variable inside the loop.
+ Value list_value = args_vector[1]->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ list_value.VerifyTypeIs(Value::Type::LIST, err);
+ if (err->has_error())
+ return Value();
+ const std::vector<Value>& list = list_value.list_value();
+
+ // Block to execute.
+ const BlockNode* block = function->block();
+ if (!block) {
+ *err = Err(function, "Expected { after foreach.");
+ return Value();
+ }
+
+ // If the loop variable was previously defined in this scope, save it so we
+ // can put it back after the loop is done.
+ const Value* old_loop_value_ptr = scope->GetValue(loop_var);
+ Value old_loop_value;
+ if (old_loop_value_ptr)
+ old_loop_value = *old_loop_value_ptr;
+
+ for (const auto& cur : list) {
+ scope->SetValue(loop_var, cur, function);
+ block->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ }
+
+ // Put back loop var.
+ if (old_loop_value_ptr) {
+ // Put back old value. Use the copy we made, rather than use the pointer,
+ // which will probably point to the new value now in the scope.
+ scope->SetValue(loop_var, std::move(old_loop_value),
+ old_loop_value.origin());
+ } else {
+ // Loop variable was undefined before loop, delete it.
+ scope->RemoveIdentifier(loop_var);
+ }
+
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_foreach_unittest.cc b/gn/src/gn/function_foreach_unittest.cc
new file mode 100644
index 00000000000..2093d4582b1
--- /dev/null
+++ b/gn/src/gn/function_foreach_unittest.cc
@@ -0,0 +1,100 @@
+// 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 "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(FunctionForeach, CollisionOnLoopVar) {
+ TestWithScope setup;
+ TestParseInput input(
+ "a = 5\n"
+ "i = 6\n"
+ "foreach(i, [1, 2, 3]) {\n" // Use same loop var name previously defined.
+ " print(\"$a $i\")\n"
+ " a = a + 1\n" // Test for side effects inside loop.
+ "}\n"
+ "print(\"$a $i\")"); // Make sure that i goes back to original value.
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("5 1\n6 2\n7 3\n8 6\n", setup.print_output());
+}
+
+TEST(FunctionForeach, UniqueLoopVar) {
+ TestWithScope setup;
+ TestParseInput input_good(
+ "foreach(i, [1, 2, 3]) {\n"
+ " print(i)\n"
+ "}\n");
+ ASSERT_FALSE(input_good.has_error());
+
+ Err err;
+ input_good.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("1\n2\n3\n", setup.print_output());
+ setup.print_output().clear();
+
+ // Same thing but try to use the loop var after loop is done. It should be
+ // undefined and throw an error.
+ TestParseInput input_bad(
+ "foreach(i, [1, 2, 3]) {\n"
+ " print(i)\n"
+ "}\n"
+ "print(i)");
+ ASSERT_FALSE(input_bad.has_error()); // Should parse OK.
+
+ input_bad.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()); // Shouldn't actually run.
+}
+
+// Checks that the identifier used as the list is marked as "used".
+TEST(FunctionForeach, MarksIdentAsUsed) {
+ TestWithScope setup;
+ TestParseInput input_good(
+ "a = [1, 2]\n"
+ "foreach(i, a) {\n"
+ " print(i)\n"
+ "}\n");
+ ASSERT_FALSE(input_good.has_error());
+
+ Err err;
+ input_good.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("1\n2\n", setup.print_output());
+ setup.print_output().clear();
+
+ // Check for unused vars.
+ EXPECT_TRUE(setup.scope()->CheckForUnusedVars(&err));
+ EXPECT_FALSE(err.has_error());
+}
+
+// Checks that the list can be modified during iteration without crashing.
+TEST(FunctionForeach, ListModification) {
+ TestWithScope setup;
+ TestParseInput input_grow(
+ "a = [1, 2]\n"
+ "foreach(i, a) {\n"
+ " print(i)\n"
+ " if (i <= 8) {\n"
+ " a += [ i + 2 ]\n"
+ " }\n"
+ "}\n"
+ "print(a)");
+ ASSERT_FALSE(input_grow.has_error());
+
+ Err err;
+ input_grow.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // The result of the loop should have been unaffected by the mutations of
+ // the list variable inside the loop, but the modifications made to it
+ // should have been persisted.
+ EXPECT_EQ("1\n2\n[1, 2, 3, 4]\n", setup.print_output());
+ setup.print_output().clear();
+}
diff --git a/gn/src/gn/function_forward_variables_from.cc b/gn/src/gn/function_forward_variables_from.cc
new file mode 100644
index 00000000000..ba392e1c753
--- /dev/null
+++ b/gn/src/gn/function_forward_variables_from.cc
@@ -0,0 +1,246 @@
+// 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 "gn/err.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+
+namespace functions {
+
+namespace {
+
+void ForwardAllValues(const FunctionCallNode* function,
+ Scope* source,
+ Scope* dest,
+ const std::set<std::string>& exclusion_set,
+ Err* err) {
+ Scope::MergeOptions options;
+ // This function needs to clobber existing for it to be useful. It will be
+ // called in a template to forward all values, but there will be some
+ // default stuff like configs set up in both scopes, so it would always
+ // fail if it didn't clobber.
+ options.clobber_existing = true;
+ options.skip_private_vars = true;
+ options.mark_dest_used = false;
+ options.excluded_values = exclusion_set;
+ source->NonRecursiveMergeTo(dest, options, function, "source scope", err);
+ source->MarkAllUsed();
+}
+
+void ForwardValuesFromList(Scope* source,
+ Scope* dest,
+ const std::vector<Value>& list,
+ const std::set<std::string>& exclusion_set,
+ Err* err) {
+ for (const Value& cur : list) {
+ if (!cur.VerifyTypeIs(Value::STRING, err))
+ return;
+ if (exclusion_set.find(cur.string_value()) != exclusion_set.end())
+ continue;
+ const Value* value = source->GetValue(cur.string_value(), true);
+ if (value) {
+ // Use the storage key for the original value rather than the string in
+ // "cur" because "cur" is a temporary that will be deleted, and Scopes
+ // expect a persistent std::string_view (it won't copy). Not doing this
+ // will lead the scope's key to point to invalid memory after this
+ // returns.
+ std::string_view storage_key = source->GetStorageKey(cur.string_value());
+ if (storage_key.empty()) {
+ // Programmatic value, don't allow copying.
+ *err =
+ Err(cur, "This value can't be forwarded.",
+ "The variable \"" + cur.string_value() + "\" is a built-in.");
+ return;
+ }
+
+ // Don't allow clobbering existing values.
+ const Value* existing_value = dest->GetValue(storage_key);
+ if (existing_value) {
+ *err = Err(
+ cur, "Clobbering existing value.",
+ "The current scope already defines a value \"" +
+ cur.string_value() +
+ "\".\nforward_variables_from() won't clobber "
+ "existing values. If you want to\nmerge lists, you'll need to "
+ "do this explicitly.");
+ err->AppendSubErr(Err(*existing_value, "value being clobbered."));
+ return;
+ }
+
+ // Keep the origin information from the original value. The normal
+ // usage is for this to be used in a template, and if there's an error,
+ // the user expects to see the line where they set the variable
+ // blamed, rather than a template call to forward_variables_from().
+ dest->SetValue(storage_key, *value, value->origin());
+ }
+ }
+}
+
+} // namespace
+
+const char kForwardVariablesFrom[] = "forward_variables_from";
+const char kForwardVariablesFrom_HelpShort[] =
+ "forward_variables_from: Copies variables from a different scope.";
+const char kForwardVariablesFrom_Help[] =
+ R"(forward_variables_from: Copies variables from a different scope.
+
+ forward_variables_from(from_scope, variable_list_or_star,
+ variable_to_not_forward_list = [])
+
+ Copies the given variables from the given scope to the local scope if they
+ exist. This is normally used in the context of templates to use the values of
+ variables defined in the template invocation to a template-defined target.
+
+ The variables in the given variable_list will be copied if they exist in the
+ given scope or any enclosing scope. If they do not exist, nothing will happen
+ and they be left undefined in the current scope.
+
+ As a special case, if the variable_list is a string with the value of "*",
+ all variables from the given scope will be copied. "*" only copies variables
+ set directly on the from_scope, not enclosing ones. Otherwise it would
+ duplicate all global variables.
+
+ When an explicit list of variables is supplied, if the variable exists in the
+ current (destination) scope already, an error will be thrown. If "*" is
+ specified, variables in the current scope will be clobbered (the latter is
+ important because most targets have an implicit configs list, which means it
+ wouldn't work at all if it didn't clobber).
+
+ If variables_to_not_forward_list is non-empty, then it must contains a list
+ of variable names that will not be forwarded. This is mostly useful when
+ variable_list_or_star has a value of "*".
+
+Examples
+
+ # forward_variables_from(invoker, ["foo"])
+ # is equivalent to:
+ assert(!defined(foo))
+ if (defined(invoker.foo)) {
+ foo = invoker.foo
+ }
+
+ # This is a common action template. It would invoke a script with some given
+ # parameters, and wants to use the various types of deps and the visibility
+ # from the invoker if it's defined. It also injects an additional dependency
+ # to all targets.
+ template("my_test") {
+ action(target_name) {
+ forward_variables_from(invoker, [ "data_deps", "deps",
+ "public_deps", "visibility"])
+ # Add our test code to the dependencies.
+ # "deps" may or may not be defined at this point.
+ if (defined(deps)) {
+ deps += [ "//tools/doom_melon" ]
+ } else {
+ deps = [ "//tools/doom_melon" ]
+ }
+ }
+ }
+
+ # This is a template around a target whose type depends on a global variable.
+ # It forwards all values from the invoker.
+ template("my_wrapper") {
+ target(my_wrapper_target_type, target_name) {
+ forward_variables_from(invoker, "*")
+ }
+ }
+
+ # A template that wraps another. It adds behavior based on one
+ # variable, and forwards all others to the nested target.
+ template("my_ios_test_app") {
+ ios_test_app(target_name) {
+ forward_variables_from(invoker, "*", ["test_bundle_name"])
+ if (!defined(extra_substitutions)) {
+ extra_substitutions = []
+ }
+ extra_substitutions += [ "BUNDLE_ID_TEST_NAME=$test_bundle_name" ]
+ }
+ }
+)";
+
+// This function takes a ListNode rather than a resolved vector of values
+// both avoid copying the potentially-large source scope, and so the variables
+// in the source scope can be marked as used.
+Value RunForwardVariablesFrom(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() != 2 && args_vector.size() != 3) {
+ *err = Err(function, "Wrong number of arguments.",
+ "Expecting two or three arguments.");
+ return Value();
+ }
+
+ Value* value = nullptr; // Value to use, may point to result_value.
+ Value result_value; // Storage for the "evaluate" case.
+ const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
+ if (identifier) {
+ // Optimize the common case where the input scope is an identifier. This
+ // prevents a copy of a potentially large Scope object.
+ value = scope->GetMutableValue(identifier->value().value(),
+ Scope::SEARCH_NESTED, true);
+ if (!value) {
+ *err = Err(identifier, "Undefined identifier.");
+ return Value();
+ }
+ } else {
+ // Non-optimized case, just evaluate the argument.
+ result_value = args_vector[0]->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
+ }
+
+ // Extract the source scope.
+ if (!value->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+ Scope* source = value->scope_value();
+
+ // Extract the exclusion list if defined.
+ std::set<std::string> exclusion_set;
+ if (args_vector.size() == 3) {
+ Value exclusion_value = args_vector[2]->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+
+ if (exclusion_value.type() != Value::LIST) {
+ *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
+ "Expecting a list of strings.");
+ return Value();
+ }
+
+ for (const Value& cur : exclusion_value.list_value()) {
+ if (!cur.VerifyTypeIs(Value::STRING, err))
+ return Value();
+
+ exclusion_set.insert(cur.string_value());
+ }
+ }
+
+ // Extract the list. If all_values is not set, the what_value will be a list.
+ Value what_value = args_vector[1]->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (what_value.type() == Value::STRING) {
+ if (what_value.string_value() == "*") {
+ ForwardAllValues(function, source, scope, exclusion_set, err);
+ return Value();
+ }
+ } else {
+ if (what_value.type() == Value::LIST) {
+ ForwardValuesFromList(source, scope, what_value.list_value(),
+ exclusion_set, err);
+ return Value();
+ }
+ }
+
+ // Not the right type of argument.
+ *err = Err(what_value, "Not a valid list of variables to copy.",
+ "Expecting either the string \"*\" or a list of strings.");
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_forward_variables_from_unittest.cc b/gn/src/gn/function_forward_variables_from_unittest.cc
new file mode 100644
index 00000000000..2f60f82da1c
--- /dev/null
+++ b/gn/src/gn/function_forward_variables_from_unittest.cc
@@ -0,0 +1,244 @@
+// 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 "gn/scheduler.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using FunctionForwardVariablesFromTest = TestWithScheduler;
+
+TEST_F(FunctionForwardVariablesFromTest, List) {
+ Err err;
+ std::string program =
+ "template(\"a\") {\n"
+ " forward_variables_from(invoker, [\"x\", \"y\", \"z\"])\n"
+ " assert(!defined(z))\n" // "z" should still be undefined.
+ " print(\"$target_name, $x, $y\")\n"
+ "}\n"
+ "a(\"target\") {\n"
+ " x = 1\n"
+ " y = 2\n"
+ "}\n";
+
+ {
+ TestWithScope setup;
+
+ // Defines a template and copy the two x and y, and z values out.
+ TestParseInput input(program);
+ ASSERT_FALSE(input.has_error());
+
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("target, 1, 2\n", setup.print_output());
+ setup.print_output().clear();
+ }
+
+ {
+ TestWithScope setup;
+
+ // Test that the same input but forwarding a variable with the name of
+ // something in the given scope throws an error rather than clobbering it.
+ // This uses the same known-good program as before, but adds another
+ // variable in the scope before it.
+ TestParseInput clobber("x = 1\n" + program);
+ ASSERT_FALSE(clobber.has_error());
+
+ clobber.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()); // Should thow a clobber error.
+ EXPECT_EQ("Clobbering existing value.", err.message());
+ }
+}
+
+TEST_F(FunctionForwardVariablesFromTest, LiteralList) {
+ TestWithScope setup;
+
+ // Forwards all variables from a literal scope into another scope definition.
+ TestParseInput input(
+ "a = {\n"
+ " forward_variables_from({x = 1 y = 2}, \"*\")\n"
+ " z = 3\n"
+ "}\n"
+ "print(\"${a.x} ${a.y} ${a.z}\")\n");
+
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("1 2 3\n", setup.print_output());
+ setup.print_output().clear();
+}
+
+TEST_F(FunctionForwardVariablesFromTest, ListWithExclusion) {
+ TestWithScope setup;
+
+ // Defines a template and copy the two x and y, and z values out.
+ TestParseInput input(
+ "template(\"a\") {\n"
+ " forward_variables_from(invoker, [\"x\", \"y\", \"z\"], [\"z\"])\n"
+ " assert(!defined(z))\n" // "z" should still be undefined.
+ " print(\"$target_name, $x, $y\")\n"
+ "}\n"
+ "a(\"target\") {\n"
+ " x = 1\n"
+ " y = 2\n"
+ " z = 3\n"
+ " print(\"$z\")\n"
+ "}\n");
+
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("3\ntarget, 1, 2\n", setup.print_output());
+ setup.print_output().clear();
+}
+
+TEST_F(FunctionForwardVariablesFromTest, ErrorCases) {
+ TestWithScope setup;
+
+ // Type check the source scope.
+ TestParseInput invalid_source(
+ "template(\"a\") {\n"
+ " forward_variables_from(42, [\"x\"])\n"
+ " print(\"$target_name\")\n" // Prevent unused var error.
+ "}\n"
+ "a(\"target\") {\n"
+ "}\n");
+ ASSERT_FALSE(invalid_source.has_error());
+ Err err;
+ invalid_source.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("This is not a scope.", err.message());
+
+ // Type check the list. We need to use a new template name each time since
+ // all of these invocations are executing in sequence in the same scope.
+ TestParseInput invalid_list(
+ "template(\"b\") {\n"
+ " forward_variables_from(invoker, 42)\n"
+ " print(\"$target_name\")\n"
+ "}\n"
+ "b(\"target\") {\n"
+ "}\n");
+ ASSERT_FALSE(invalid_list.has_error());
+ err = Err();
+ invalid_list.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Not a valid list of variables to copy.", err.message());
+
+ // Type check the exclusion list.
+ TestParseInput invalid_exclusion_list(
+ "template(\"c\") {\n"
+ " forward_variables_from(invoker, \"*\", 42)\n"
+ " print(\"$target_name\")\n"
+ "}\n"
+ "c(\"target\") {\n"
+ "}\n");
+ ASSERT_FALSE(invalid_exclusion_list.has_error());
+ err = Err();
+ invalid_exclusion_list.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Not a valid list of variables to exclude.", err.message());
+
+ // Programmatic values should error.
+ TestParseInput prog(
+ "template(\"d\") {\n"
+ " forward_variables_from(invoker, [\"root_out_dir\"])\n"
+ " print(\"$target_name\")\n"
+ "}\n"
+ "d(\"target\") {\n"
+ "}\n");
+ ASSERT_FALSE(prog.has_error());
+ err = Err();
+ prog.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("This value can't be forwarded.", err.message());
+
+ // Not enough arguments.
+ TestParseInput not_enough_arguments(
+ "template(\"e\") {\n"
+ " forward_variables_from(invoker)\n"
+ " print(\"$target_name\")\n"
+ "}\n"
+ "e(\"target\") {\n"
+ "}\n");
+ ASSERT_FALSE(not_enough_arguments.has_error());
+ err = Err();
+ not_enough_arguments.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ // Too many arguments.
+ TestParseInput too_many_arguments(
+ "template(\"f\") {\n"
+ " forward_variables_from(invoker, \"*\", [], [])\n"
+ " print(\"$target_name\")\n"
+ "}\n"
+ "f(\"target\") {\n"
+ "}\n");
+ ASSERT_FALSE(too_many_arguments.has_error());
+ err = Err();
+ too_many_arguments.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+}
+
+TEST_F(FunctionForwardVariablesFromTest, Star) {
+ TestWithScope setup;
+
+ // Defines a template and copy the two x and y values out. The "*" behavior
+ // should clobber existing variables with the same name.
+ TestParseInput input(
+ "template(\"a\") {\n"
+ " x = 1000000\n" // Should be clobbered.
+ " forward_variables_from(invoker, \"*\")\n"
+ " print(\"$target_name, $x, $y\")\n"
+ "}\n"
+ "a(\"target\") {\n"
+ " x = 1\n"
+ " y = 2\n"
+ "}\n");
+
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("target, 1, 2\n", setup.print_output());
+ setup.print_output().clear();
+}
+
+TEST_F(FunctionForwardVariablesFromTest, StarWithExclusion) {
+ TestWithScope setup;
+
+ // Defines a template and copy all values except z value. The "*" behavior
+ // should clobber existing variables with the same name.
+ TestParseInput input(
+ "template(\"a\") {\n"
+ " x = 1000000\n" // Should be clobbered.
+ " forward_variables_from(invoker, \"*\", [\"z\"])\n"
+ " print(\"$target_name, $x, $y\")\n"
+ "}\n"
+ "a(\"target\") {\n"
+ " x = 1\n"
+ " y = 2\n"
+ " z = 3\n"
+ " print(\"$z\")\n"
+ "}\n");
+
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("3\ntarget, 1, 2\n", setup.print_output());
+ setup.print_output().clear();
+}
diff --git a/gn/src/gn/function_get_label_info.cc b/gn/src/gn/function_get_label_info.cc
new file mode 100644
index 00000000000..04fa9a7dd4a
--- /dev/null
+++ b/gn/src/gn/function_get_label_info.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 "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/label.h"
+#include "gn/parse_tree.h"
+#include "gn/value.h"
+
+namespace functions {
+
+const char kGetLabelInfo[] = "get_label_info";
+const char kGetLabelInfo_HelpShort[] =
+ "get_label_info: Get an attribute from a target's label.";
+const char kGetLabelInfo_Help[] =
+ R"*(get_label_info: Get an attribute from a target's label.
+
+ get_label_info(target_label, what)
+
+ Given the label of a target, returns some attribute of that target. The
+ target need not have been previously defined in the same file, since none of
+ the attributes depend on the actual target definition, only the label itself.
+
+ See also "gn help get_target_outputs".
+
+Possible values for the "what" parameter
+
+ "name"
+ The short name of the target. This will match the value of the
+ "target_name" variable inside that target's declaration. For the label
+ "//foo/bar:baz" this will return "baz".
+
+ "dir"
+ The directory containing the target's definition, with no slash at the
+ end. For the label "//foo/bar:baz" this will return "//foo/bar".
+
+ "target_gen_dir"
+ The generated file directory for the target. This will match the value of
+ the "target_gen_dir" variable when inside that target's declaration.
+
+ "root_gen_dir"
+ The root of the generated file tree for the target. This will match the
+ value of the "root_gen_dir" variable when inside that target's
+ declaration.
+
+ "target_out_dir
+ The output directory for the target. This will match the value of the
+ "target_out_dir" variable when inside that target's declaration.
+
+ "root_out_dir"
+ The root of the output file tree for the target. This will match the
+ value of the "root_out_dir" variable when inside that target's
+ declaration.
+
+ "label_no_toolchain"
+ The fully qualified version of this label, not including the toolchain.
+ For the input ":bar" it might return "//foo:bar".
+
+ "label_with_toolchain"
+ The fully qualified version of this label, including the toolchain. For
+ the input ":bar" it might return "//foo:bar(//toolchain:x64)".
+
+ "toolchain"
+ The label of the toolchain. This will match the value of the
+ "current_toolchain" variable when inside that target's declaration.
+
+Examples
+
+ get_label_info(":foo", "name")
+ # Returns string "foo".
+
+ get_label_info("//foo/bar:baz", "target_gen_dir")
+ # Returns string "//out/Debug/gen/foo/bar".
+)*";
+
+Value RunGetLabelInfo(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function, "Expected two arguments.");
+ return Value();
+ }
+
+ // Resolve the requested label.
+ Label label =
+ Label::Resolve(scope->GetSourceDir(),
+ scope->settings()->build_settings()->root_path_utf8(),
+ ToolchainLabelForScope(scope), args[0], err);
+ if (label.is_null())
+ return Value();
+
+ // Extract the "what" parameter.
+ if (!args[1].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string& what = args[1].string_value();
+
+ Value result(function, Value::STRING);
+ if (what == "name") {
+ result.string_value() = label.name();
+
+ } else if (what == "dir") {
+ result.string_value() = DirectoryWithNoLastSlash(label.dir());
+
+ } else if (what == "target_gen_dir") {
+ result.string_value() = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(scope, label.GetToolchainLabel()), label.dir(),
+ BuildDirType::GEN));
+
+ } else if (what == "root_gen_dir") {
+ result.string_value() = DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
+ BuildDirContext(scope, label.GetToolchainLabel()), BuildDirType::GEN));
+
+ } else if (what == "target_out_dir") {
+ result.string_value() = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(scope, label.GetToolchainLabel()), label.dir(),
+ BuildDirType::OBJ));
+
+ } else if (what == "root_out_dir") {
+ result.string_value() = DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
+ BuildDirContext(scope, label.GetToolchainLabel()),
+ BuildDirType::TOOLCHAIN_ROOT));
+
+ } else if (what == "toolchain") {
+ result.string_value() = label.GetToolchainLabel().GetUserVisibleName(false);
+
+ } else if (what == "label_no_toolchain") {
+ result.string_value() =
+ label.GetWithNoToolchain().GetUserVisibleName(false);
+
+ } else if (what == "label_with_toolchain") {
+ result.string_value() = label.GetUserVisibleName(true);
+
+ } else {
+ *err = Err(args[1], "Unknown value for \"what\" parameter.");
+ return Value();
+ }
+
+ return result;
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_get_label_info_unittest.cc b/gn/src/gn/function_get_label_info_unittest.cc
new file mode 100644
index 00000000000..3e934f1e063
--- /dev/null
+++ b/gn/src/gn/function_get_label_info_unittest.cc
@@ -0,0 +1,107 @@
+// 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 "gn/functions.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+class GetLabelInfoTest : public testing::Test {
+ public:
+ GetLabelInfoTest() : testing::Test() {
+ setup_.scope()->set_source_dir(SourceDir("//src/foo/"));
+ }
+
+ // Convenience wrapper to call GetLabelInfo.
+ std::string Call(const std::string& label, const std::string& what) {
+ FunctionCallNode function;
+
+ std::vector<Value> args;
+ args.push_back(Value(nullptr, label));
+ args.push_back(Value(nullptr, what));
+
+ Err err;
+ Value result =
+ functions::RunGetLabelInfo(setup_.scope(), &function, args, &err);
+ if (err.has_error()) {
+ EXPECT_TRUE(result.type() == Value::NONE);
+ return std::string();
+ }
+ return result.string_value();
+ }
+
+ protected:
+ // Note: TestWithScope's default toolchain is "//toolchain:default" and
+ // output dir is "//out/Debug".
+ TestWithScope setup_;
+};
+
+} // namespace
+
+TEST_F(GetLabelInfoTest, BadInput) {
+ EXPECT_EQ("", Call(":name", "incorrect_value"));
+ EXPECT_EQ("", Call("", "name"));
+}
+
+TEST_F(GetLabelInfoTest, Name) {
+ EXPECT_EQ("name", Call(":name", "name"));
+ EXPECT_EQ("name", Call("//foo/bar:name", "name"));
+ EXPECT_EQ("name", Call("//foo/bar:name(//other:tc)", "name"));
+}
+
+TEST_F(GetLabelInfoTest, Dir) {
+ EXPECT_EQ("//src/foo", Call(":name", "dir"));
+ EXPECT_EQ("//foo/bar", Call("//foo/bar:baz", "dir"));
+ EXPECT_EQ("//foo/bar", Call("//foo/bar:baz(//other:tc)", "dir"));
+}
+
+TEST_F(GetLabelInfoTest, RootOutDir) {
+ EXPECT_EQ("//out/Debug", Call(":name", "root_out_dir"));
+ EXPECT_EQ("//out/Debug/random",
+ Call(":name(//toolchain:random)", "root_out_dir"));
+}
+
+TEST_F(GetLabelInfoTest, RootGenDir) {
+ EXPECT_EQ("//out/Debug/gen", Call(":name", "root_gen_dir"));
+ EXPECT_EQ("//out/Debug/gen",
+ Call(":name(//toolchain:default)", "root_gen_dir"));
+ EXPECT_EQ("//out/Debug/random/gen",
+ Call(":name(//toolchain:random)", "root_gen_dir"));
+}
+
+TEST_F(GetLabelInfoTest, TargetOutDir) {
+ EXPECT_EQ("//out/Debug/obj/src/foo", Call(":name", "target_out_dir"));
+ EXPECT_EQ("//out/Debug/obj/foo",
+ Call("//foo:name(//toolchain:default)", "target_out_dir"));
+ EXPECT_EQ("//out/Debug/random/obj/foo",
+ Call("//foo:name(//toolchain:random)", "target_out_dir"));
+}
+
+TEST_F(GetLabelInfoTest, TargetGenDir) {
+ EXPECT_EQ("//out/Debug/gen/src/foo", Call(":name", "target_gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/foo",
+ Call("//foo:name(//toolchain:default)", "target_gen_dir"));
+ EXPECT_EQ("//out/Debug/random/gen/foo",
+ Call("//foo:name(//toolchain:random)", "target_gen_dir"));
+}
+
+TEST_F(GetLabelInfoTest, LabelNoToolchain) {
+ EXPECT_EQ("//src/foo:name", Call(":name", "label_no_toolchain"));
+ EXPECT_EQ("//src/foo:name",
+ Call("//src/foo:name(//toolchain:random)", "label_no_toolchain"));
+}
+
+TEST_F(GetLabelInfoTest, LabelWithToolchain) {
+ EXPECT_EQ("//src/foo:name(//toolchain:default)",
+ Call(":name", "label_with_toolchain"));
+ EXPECT_EQ("//src/foo:name(//toolchain:random)",
+ Call(":name(//toolchain:random)", "label_with_toolchain"));
+}
+
+TEST_F(GetLabelInfoTest, Toolchain) {
+ EXPECT_EQ("//toolchain:default", Call(":name", "toolchain"));
+ EXPECT_EQ("//toolchain:random",
+ Call(":name(//toolchain:random)", "toolchain"));
+}
diff --git a/gn/src/gn/function_get_path_info.cc b/gn/src/gn/function_get_path_info.cc
new file mode 100644
index 00000000000..00ed043cc1b
--- /dev/null
+++ b/gn/src/gn/function_get_path_info.cc
@@ -0,0 +1,251 @@
+// 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 <stddef.h>
+
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+
+namespace functions {
+
+namespace {
+
+// Corresponds to the various values of "what" in the function call.
+enum What {
+ WHAT_FILE,
+ WHAT_NAME,
+ WHAT_EXTENSION,
+ WHAT_DIR,
+ WHAT_ABSPATH,
+ WHAT_GEN_DIR,
+ WHAT_OUT_DIR,
+};
+
+// Returns the directory containing the input (resolving it against the
+// |current_dir|), regardless of whether the input is a directory or a file.
+SourceDir DirForInput(const Settings* settings,
+ const SourceDir& current_dir,
+ const Value& input,
+ Err* err) {
+ // Input should already have been validated as a string.
+ const std::string& input_string = input.string_value();
+
+ if (!input_string.empty() && input_string[input_string.size() - 1] == '/') {
+ // Input is a directory.
+ return current_dir.ResolveRelativeDir(
+ input, err, settings->build_settings()->root_path_utf8());
+ }
+
+ // Input is a file.
+ return current_dir
+ .ResolveRelativeFile(input, err,
+ settings->build_settings()->root_path_utf8())
+ .GetDir();
+}
+
+std::string GetOnePathInfo(const Settings* settings,
+ const SourceDir& current_dir,
+ What what,
+ const Value& input,
+ Err* err) {
+ if (!input.VerifyTypeIs(Value::STRING, err))
+ return std::string();
+ const std::string& input_string = input.string_value();
+ if (input_string.empty()) {
+ *err = Err(input, "Calling get_path_info on an empty string.");
+ return std::string();
+ }
+
+ switch (what) {
+ case WHAT_FILE: {
+ return std::string(FindFilename(&input_string));
+ }
+ case WHAT_NAME: {
+ std::string file(FindFilename(&input_string));
+ size_t extension_offset = FindExtensionOffset(file);
+ if (extension_offset == std::string::npos)
+ return file;
+ // Trim extension and dot.
+ return file.substr(0, extension_offset - 1);
+ }
+ case WHAT_EXTENSION: {
+ return std::string(FindExtension(&input_string));
+ }
+ case WHAT_DIR: {
+ std::string_view dir_incl_slash = FindDir(&input_string);
+ if (dir_incl_slash.empty())
+ return std::string(".");
+ // Trim slash since this function doesn't return trailing slashes. The
+ // times we don't do this are if the result is "/" and "//" since those
+ // slashes can't be trimmed.
+ if (dir_incl_slash == "/")
+ return std::string("/.");
+ if (dir_incl_slash == "//")
+ return std::string("//.");
+ return std::string(dir_incl_slash.substr(0, dir_incl_slash.size() - 1));
+ }
+ case WHAT_GEN_DIR: {
+ return DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings),
+ DirForInput(settings, current_dir, input, err), BuildDirType::GEN));
+ }
+ case WHAT_OUT_DIR: {
+ return DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings),
+ DirForInput(settings, current_dir, input, err), BuildDirType::OBJ));
+ }
+ case WHAT_ABSPATH: {
+ bool as_dir =
+ !input_string.empty() && input_string[input_string.size() - 1] == '/';
+
+ return current_dir.ResolveRelativeAs(
+ !as_dir, input, err, settings->build_settings()->root_path_utf8(),
+ &input_string);
+ }
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+}
+
+} // namespace
+
+const char kGetPathInfo[] = "get_path_info";
+const char kGetPathInfo_HelpShort[] =
+ "get_path_info: Extract parts of a file or directory name.";
+const char kGetPathInfo_Help[] =
+ R"(get_path_info: Extract parts of a file or directory name.
+
+ get_path_info(input, what)
+
+ The first argument is either a string representing a file or directory name,
+ or a list of such strings. If the input is a list the return value will be a
+ list containing the result of applying the rule to each item in the input.
+
+Possible values for the "what" parameter
+
+ "file"
+ The substring after the last slash in the path, including the name and
+ extension. If the input ends in a slash, the empty string will be
+ returned.
+ "foo/bar.txt" => "bar.txt"
+ "bar.txt" => "bar.txt"
+ "foo/" => ""
+ "" => ""
+
+ "name"
+ The substring of the file name not including the extension.
+ "foo/bar.txt" => "bar"
+ "foo/bar" => "bar"
+ "foo/" => ""
+
+ "extension"
+ The substring following the last period following the last slash, or the
+ empty string if not found. The period is not included.
+ "foo/bar.txt" => "txt"
+ "foo/bar" => ""
+
+ "dir"
+ The directory portion of the name, not including the slash.
+ "foo/bar.txt" => "foo"
+ "//foo/bar" => "//foo"
+ "foo" => "."
+
+ The result will never end in a slash, so if the resulting is empty, the
+ system ("/") or source ("//") roots, a "." will be appended such that it
+ is always legal to append a slash and a filename and get a valid path.
+
+ "out_dir"
+ The output file directory corresponding to the path of the given file,
+ not including a trailing slash.
+ "//foo/bar/baz.txt" => "//out/Default/obj/foo/bar"
+
+ "gen_dir"
+ The generated file directory corresponding to the path of the given file,
+ not including a trailing slash.
+ "//foo/bar/baz.txt" => "//out/Default/gen/foo/bar"
+
+ "abspath"
+ The full absolute path name to the file or directory. It will be resolved
+ relative to the current directory, and then the source- absolute version
+ will be returned. If the input is system- absolute, the same input will
+ be returned.
+ "foo/bar.txt" => "//mydir/foo/bar.txt"
+ "foo/" => "//mydir/foo/"
+ "//foo/bar" => "//foo/bar" (already absolute)
+ "/usr/include" => "/usr/include" (already absolute)
+
+ If you want to make the path relative to another directory, or to be
+ system-absolute, see rebase_path().
+
+Examples
+ sources = [ "foo.cc", "foo.h" ]
+ result = get_path_info(source, "abspath")
+ # result will be [ "//mydir/foo.cc", "//mydir/foo.h" ]
+
+ result = get_path_info("//foo/bar/baz.cc", "dir")
+ # result will be "//foo/bar"
+
+ # Extract the source-absolute directory name,
+ result = get_path_info(get_path_info(path, "dir"), "abspath")
+)";
+
+Value RunGetPathInfo(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function, "Expecting two arguments to get_path_info.");
+ return Value();
+ }
+
+ // Extract the "what".
+ if (!args[1].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ What what;
+ if (args[1].string_value() == "file") {
+ what = WHAT_FILE;
+ } else if (args[1].string_value() == "name") {
+ what = WHAT_NAME;
+ } else if (args[1].string_value() == "extension") {
+ what = WHAT_EXTENSION;
+ } else if (args[1].string_value() == "dir") {
+ what = WHAT_DIR;
+ } else if (args[1].string_value() == "out_dir") {
+ what = WHAT_OUT_DIR;
+ } else if (args[1].string_value() == "gen_dir") {
+ what = WHAT_GEN_DIR;
+ } else if (args[1].string_value() == "abspath") {
+ what = WHAT_ABSPATH;
+ } else {
+ *err = Err(args[1], "Unknown value for 'what'.");
+ return Value();
+ }
+
+ const SourceDir& current_dir = scope->GetSourceDir();
+ if (args[0].type() == Value::STRING) {
+ return Value(function, GetOnePathInfo(scope->settings(), current_dir, what,
+ args[0], err));
+ } else if (args[0].type() == Value::LIST) {
+ const std::vector<Value>& input_list = args[0].list_value();
+ Value result(function, Value::LIST);
+ for (const auto& cur : input_list) {
+ result.list_value().push_back(Value(
+ function,
+ GetOnePathInfo(scope->settings(), current_dir, what, cur, err)));
+ if (err->has_error())
+ return Value();
+ }
+ return result;
+ }
+
+ *err = Err(args[0], "Path must be a string or a list of strings.");
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_get_path_info_unittest.cc b/gn/src/gn/function_get_path_info_unittest.cc
new file mode 100644
index 00000000000..4afad0c9a8c
--- /dev/null
+++ b/gn/src/gn/function_get_path_info_unittest.cc
@@ -0,0 +1,120 @@
+// 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 "gn/functions.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+namespace {
+
+class GetPathInfoTest : public testing::Test {
+ public:
+ GetPathInfoTest() : testing::Test() {
+ setup_.scope()->set_source_dir(SourceDir("//src/foo/"));
+ }
+
+ // Convenience wrapper to call GetLabelInfo.
+ std::string Call(const std::string& input, const std::string& what) {
+ FunctionCallNode function;
+
+ std::vector<Value> args;
+ args.push_back(Value(nullptr, input));
+ args.push_back(Value(nullptr, what));
+
+ Err err;
+ Value result =
+ functions::RunGetPathInfo(setup_.scope(), &function, args, &err);
+ if (err.has_error()) {
+ EXPECT_TRUE(result.type() == Value::NONE);
+ return std::string();
+ }
+ return result.string_value();
+ }
+
+ protected:
+ TestWithScope setup_;
+};
+
+} // namespace
+
+TEST_F(GetPathInfoTest, File) {
+ EXPECT_EQ("bar.txt", Call("foo/bar.txt", "file"));
+ EXPECT_EQ("bar.txt", Call("bar.txt", "file"));
+ EXPECT_EQ("bar.txt", Call("/bar.txt", "file"));
+ EXPECT_EQ("", Call("foo/", "file"));
+ EXPECT_EQ("", Call("//", "file"));
+ EXPECT_EQ("", Call("/", "file"));
+}
+
+TEST_F(GetPathInfoTest, Name) {
+ EXPECT_EQ("bar", Call("foo/bar.txt", "name"));
+ EXPECT_EQ("bar", Call("bar.", "name"));
+ EXPECT_EQ("", Call("/.txt", "name"));
+ EXPECT_EQ("", Call("foo/", "name"));
+ EXPECT_EQ("", Call("//", "name"));
+ EXPECT_EQ("", Call("/", "name"));
+}
+
+TEST_F(GetPathInfoTest, Extension) {
+ EXPECT_EQ("txt", Call("foo/bar.txt", "extension"));
+ EXPECT_EQ("", Call("bar.", "extension"));
+ EXPECT_EQ("txt", Call("/.txt", "extension"));
+ EXPECT_EQ("", Call("f.oo/", "extension"));
+ EXPECT_EQ("", Call("//", "extension"));
+ EXPECT_EQ("", Call("/", "extension"));
+}
+
+TEST_F(GetPathInfoTest, Dir) {
+ EXPECT_EQ("foo", Call("foo/bar.txt", "dir"));
+ EXPECT_EQ(".", Call("bar.txt", "dir"));
+ EXPECT_EQ("foo/bar", Call("foo/bar/baz", "dir"));
+ EXPECT_EQ("//foo", Call("//foo/", "dir"));
+ EXPECT_EQ("//.", Call("//", "dir"));
+ EXPECT_EQ("/foo", Call("/foo/", "dir"));
+ EXPECT_EQ("/.", Call("/", "dir"));
+}
+
+// Note "current dir" is "//src/foo"
+TEST_F(GetPathInfoTest, AbsPath) {
+ EXPECT_EQ("//src/foo/foo/bar.txt", Call("foo/bar.txt", "abspath"));
+ EXPECT_EQ("//src/foo/bar.txt", Call("bar.txt", "abspath"));
+ EXPECT_EQ("//src/foo/bar/", Call("bar/", "abspath"));
+ EXPECT_EQ("//foo", Call("//foo", "abspath"));
+ EXPECT_EQ("//foo/", Call("//foo/", "abspath"));
+ EXPECT_EQ("//", Call("//", "abspath"));
+ EXPECT_EQ("/foo", Call("/foo", "abspath"));
+ EXPECT_EQ("/foo/", Call("/foo/", "abspath"));
+ EXPECT_EQ("/", Call("/", "abspath"));
+}
+
+// Note build dir is "//out/Debug/".
+TEST_F(GetPathInfoTest, OutDir) {
+ EXPECT_EQ("//out/Debug/obj/src/foo/foo", Call("foo/bar.txt", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/src/foo/bar", Call("bar/", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/src/foo", Call(".", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/src/foo", Call("bar", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/foo", Call("//foo/bar.txt", "out_dir"));
+ // System paths go into the ABS_PATH obj directory.
+ EXPECT_EQ("//out/Debug/obj/ABS_PATH/foo", Call("/foo/bar.txt", "out_dir"));
+#if defined(OS_WIN)
+ EXPECT_EQ("//out/Debug/obj/ABS_PATH/C/foo",
+ Call("/C:/foo/bar.txt", "out_dir"));
+#endif
+}
+
+// Note build dir is "//out/Debug/".
+TEST_F(GetPathInfoTest, GenDir) {
+ EXPECT_EQ("//out/Debug/gen/src/foo/foo", Call("foo/bar.txt", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/src/foo/bar", Call("bar/", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/src/foo", Call(".", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/src/foo", Call("bar", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/foo", Call("//foo/bar.txt", "gen_dir"));
+ // System paths go into the ABS_PATH gen directory
+ EXPECT_EQ("//out/Debug/gen/ABS_PATH/foo", Call("/foo/bar.txt", "gen_dir"));
+#if defined(OS_WIN)
+ EXPECT_EQ("//out/Debug/gen/ABS_PATH/C/foo",
+ Call("/C:/foo/bar.txt", "gen_dir"));
+#endif
+}
diff --git a/gn/src/gn/function_get_target_outputs.cc b/gn/src/gn/function_get_target_outputs.cc
new file mode 100644
index 00000000000..fa851f3c47c
--- /dev/null
+++ b/gn/src/gn/function_get_target_outputs.cc
@@ -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.
+
+#include "gn/build_settings.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/settings.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/value.h"
+
+namespace functions {
+
+const char kGetTargetOutputs[] = "get_target_outputs";
+const char kGetTargetOutputs_HelpShort[] =
+ "get_target_outputs: [file list] Get the list of outputs from a target.";
+const char kGetTargetOutputs_Help[] =
+ R"(get_target_outputs: [file list] Get the list of outputs from a target.
+
+ get_target_outputs(target_label)
+
+ Returns a list of output files for the named target. The named target must
+ have been previously defined in the current file before this function is
+ called (it can't reference targets in other files because there isn't a
+ defined execution order, and it obviously can't reference targets that are
+ defined after the function call).
+
+ Only copy, generated_file, and action targets are supported. The outputs from
+ binary targets will depend on the toolchain definition which won't
+ necessarily have been loaded by the time a given line of code has run, and
+ source sets and groups have no useful output file.
+
+Return value
+
+ The names in the resulting list will be absolute file paths (normally like
+ "//out/Debug/bar.exe", depending on the build directory).
+
+ action, copy, and generated_file targets: this will just return the files
+ specified in the "outputs" variable of the target.
+
+ action_foreach targets: this will return the result of applying the output
+ template to the sources (see "gn help source_expansion"). This will be the
+ same result (though with guaranteed absolute file paths), as
+ process_file_template will return for those inputs (see "gn help
+ process_file_template").
+
+ source sets and groups: this will return a list containing the path of the
+ "stamp" file that Ninja will produce once all outputs are generated. This
+ probably isn't very useful.
+
+Example
+
+ # Say this action generates a bunch of C source files.
+ action_foreach("my_action") {
+ sources = [ ... ]
+ outputs = [ ... ]
+ }
+
+ # Compile the resulting source files into a source set.
+ source_set("my_lib") {
+ sources = get_target_outputs(":my_action")
+ }
+)";
+
+Value RunGetTargetOutputs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function, "Expected one argument.");
+ return Value();
+ }
+
+ // Resolve the requested label.
+ Label label =
+ Label::Resolve(scope->GetSourceDir(),
+ scope->settings()->build_settings()->root_path_utf8(),
+ ToolchainLabelForScope(scope), args[0], err);
+ if (label.is_null())
+ return Value();
+
+ // Find the referenced target. The targets previously encountered in this
+ // scope will have been stashed in the item collector (they'll be dispatched
+ // when this file is done running) so we can look through them.
+ const Target* target = nullptr;
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function, "No targets defined in this context.");
+ return Value();
+ }
+ for (const auto& item : *collector) {
+ if (item->label() != label)
+ continue;
+
+ const Target* as_target = item->AsTarget();
+ if (!as_target) {
+ *err = Err(function, "Label does not refer to a target.",
+ label.GetUserVisibleName(false) + "\nrefers to a " +
+ item->GetItemTypeName());
+ return Value();
+ }
+ target = as_target;
+ break;
+ }
+
+ if (!target) {
+ *err = Err(function, "Target not found in this context.",
+ label.GetUserVisibleName(false) +
+ "\nwas not found. get_target_outputs() can only be used for "
+ "targets\n"
+ "previously defined in the current file.");
+ return Value();
+ }
+
+ // Range for GetOutputsAsSourceFiles to blame for errors.
+ LocationRange arg_range;
+ if (args[0].origin())
+ arg_range = args[0].origin()->GetRange();
+
+ std::vector<SourceFile> files;
+
+ // The build is currently running so only non-binary targets (they don't
+ // depend on the toolchain definition which may not have been loaded yet) can
+ // be queried. Pass false for build_complete so it will flag such queries as
+ // an error.
+ if (!target->GetOutputsAsSourceFiles(arg_range, false, &files, err))
+ return Value();
+
+ // Convert to Values.
+ Value ret(function, Value::LIST);
+ ret.list_value().reserve(files.size());
+ for (const auto& file : files)
+ ret.list_value().push_back(Value(function, file.value()));
+
+ return ret;
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_get_target_outputs_unittest.cc b/gn/src/gn/function_get_target_outputs_unittest.cc
new file mode 100644
index 00000000000..4cbd8f9272c
--- /dev/null
+++ b/gn/src/gn/function_get_target_outputs_unittest.cc
@@ -0,0 +1,107 @@
+// 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 <memory>
+#include <utility>
+
+#include "gn/functions.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+class GetTargetOutputsTest : public testing::Test {
+ public:
+ GetTargetOutputsTest() { setup_.scope()->set_item_collector(&items_); }
+
+ Value GetTargetOutputs(const std::string& name, Err* err) {
+ FunctionCallNode function;
+ std::vector<Value> args;
+ args.push_back(Value(nullptr, name));
+ return functions::RunGetTargetOutputs(setup_.scope(), &function, args, err);
+ }
+
+ // Shortcut to get a label with the current toolchain.
+ Label GetLabel(const std::string& dir, const std::string& name) {
+ return Label(SourceDir(dir), name, setup_.toolchain()->label().dir(),
+ setup_.toolchain()->label().name());
+ }
+
+ // Asserts that the given list contains a single string with the given value.
+ void AssertSingleStringEquals(const Value& list,
+ const std::string& expected) {
+ ASSERT_TRUE(list.type() == Value::LIST);
+ ASSERT_EQ(1u, list.list_value().size());
+ ASSERT_TRUE(list.list_value()[0].type() == Value::STRING);
+ ASSERT_EQ(expected, list.list_value()[0].string_value());
+ }
+
+ void AssertTwoStringsEqual(const Value& list,
+ const std::string& expected1,
+ const std::string& expected2) {
+ ASSERT_TRUE(list.type() == Value::LIST);
+ ASSERT_EQ(2u, list.list_value().size());
+ ASSERT_TRUE(list.list_value()[0].type() == Value::STRING);
+ ASSERT_EQ(expected1, list.list_value()[0].string_value());
+ ASSERT_TRUE(list.list_value()[1].type() == Value::STRING);
+ ASSERT_EQ(expected2, list.list_value()[1].string_value());
+ }
+
+ protected:
+ TestWithScope setup_;
+
+ Scope::ItemVector items_;
+};
+
+} // namespace
+
+TEST_F(GetTargetOutputsTest, Copy) {
+ auto action =
+ std::make_unique<Target>(setup_.settings(), GetLabel("//foo/", "bar"));
+ action->set_output_type(Target::COPY_FILES);
+ action->sources().push_back(SourceFile("//file.txt"));
+ action->action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one");
+
+ items_.push_back(std::move(action));
+
+ Err err;
+ Value result = GetTargetOutputs("//foo:bar", &err);
+ ASSERT_FALSE(err.has_error());
+ AssertSingleStringEquals(result, "//out/Debug/file.txt.one");
+}
+
+TEST_F(GetTargetOutputsTest, Action) {
+ auto action =
+ std::make_unique<Target>(setup_.settings(), GetLabel("//foo/", "bar"));
+ action->set_output_type(Target::ACTION);
+ action->action_values().outputs() =
+ SubstitutionList::MakeForTest("//output1.txt", "//output2.txt");
+
+ items_.push_back(std::move(action));
+
+ Err err;
+ Value result = GetTargetOutputs("//foo:bar", &err);
+ ASSERT_FALSE(err.has_error());
+ AssertTwoStringsEqual(result, "//output1.txt", "//output2.txt");
+}
+
+TEST_F(GetTargetOutputsTest, ActionForeach) {
+ auto action =
+ std::make_unique<Target>(setup_.settings(), GetLabel("//foo/", "bar"));
+ action->set_output_type(Target::ACTION_FOREACH);
+ action->sources().push_back(SourceFile("//file.txt"));
+ action->action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one",
+ "//out/Debug/{{source_file_part}}.two");
+
+ items_.push_back(std::move(action));
+
+ Err err;
+ Value result = GetTargetOutputs("//foo:bar", &err);
+ ASSERT_FALSE(err.has_error());
+ AssertTwoStringsEqual(result, "//out/Debug/file.txt.one",
+ "//out/Debug/file.txt.two");
+}
diff --git a/gn/src/gn/function_process_file_template.cc b/gn/src/gn/function_process_file_template.cc
new file mode 100644
index 00000000000..d4e1e56b6f5
--- /dev/null
+++ b/gn/src/gn/function_process_file_template.cc
@@ -0,0 +1,115 @@
+// 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 "base/stl_util.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/value_extractors.h"
+
+namespace functions {
+
+const char kProcessFileTemplate[] = "process_file_template";
+const char kProcessFileTemplate_HelpShort[] =
+ "process_file_template: Do template expansion over a list of files.";
+const char kProcessFileTemplate_Help[] =
+ R"(process_file_template: Do template expansion over a list of files.
+
+ process_file_template(source_list, template)
+
+ process_file_template applies a template list to a source file list,
+ returning the result of applying each template to each source. This is
+ typically used for computing output file names from input files.
+
+ In most cases, get_target_outputs() will give the same result with shorter,
+ more maintainable code. This function should only be used when that function
+ can't be used (like there's no target or the target is defined in another
+ build file).
+
+Arguments
+
+ The source_list is a list of file names.
+
+ The template can be a string or a list. If it is a list, multiple output
+ strings are generated for each input.
+
+ The template should contain source expansions to which each name in the
+ source list is applied. See "gn help source_expansion".
+
+Example
+
+ sources = [
+ "foo.idl",
+ "bar.idl",
+ ]
+ myoutputs = process_file_template(
+ sources,
+ [ "$target_gen_dir/{{source_name_part}}.cc",
+ "$target_gen_dir/{{source_name_part}}.h" ])
+
+ The result in this case will be:
+ [ "//out/Debug/foo.cc"
+ "//out/Debug/foo.h"
+ "//out/Debug/bar.cc"
+ "//out/Debug/bar.h" ]
+)";
+
+Value RunProcessFileTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function->function(), "Expected two arguments");
+ return Value();
+ }
+
+ // Source list.
+ Target::FileList input_files;
+ if (!ExtractListOfRelativeFiles(scope->settings()->build_settings(), args[0],
+ scope->GetSourceDir(), &input_files, err))
+ return Value();
+
+ std::vector<std::string> result_files;
+ SubstitutionList subst;
+
+ // Template.
+ const Value& template_arg = args[1];
+ if (template_arg.type() == Value::STRING) {
+ // Convert the string to a SubstitutionList with one pattern in it to
+ // simplify the code below.
+ std::vector<std::string> list;
+ list.push_back(template_arg.string_value());
+ if (!subst.Parse(list, template_arg.origin(), err))
+ return Value();
+ } else if (template_arg.type() == Value::LIST) {
+ if (!subst.Parse(template_arg, err))
+ return Value();
+ } else {
+ *err = Err(template_arg, "Not a string or a list.");
+ return Value();
+ }
+
+ auto& types = subst.required_types();
+ if (base::ContainsValue(types, &SubstitutionSourceTargetRelative)) {
+ *err = Err(template_arg, "Not a valid substitution type for the function.");
+ return Value();
+ }
+
+ SubstitutionWriter::ApplyListToSourcesAsString(
+ nullptr, scope->settings(), subst, input_files, &result_files);
+
+ // Convert the list of strings to the return Value.
+ Value ret(function, Value::LIST);
+ ret.list_value().reserve(result_files.size());
+ for (const auto& file : result_files)
+ ret.list_value().push_back(Value(function, file));
+
+ return ret;
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_process_file_template_unittest.cc b/gn/src/gn/function_process_file_template_unittest.cc
new file mode 100644
index 00000000000..d9c99192ebf
--- /dev/null
+++ b/gn/src/gn/function_process_file_template_unittest.cc
@@ -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.
+
+#include "gn/functions.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(FunctionProcessFileTemplates, SingleString) {
+ TestWithScope setup;
+
+ std::vector<Value> args;
+
+ Value sources(nullptr, Value::LIST);
+ sources.list_value().push_back(Value(nullptr, "//src/foo.txt"));
+ args.push_back(sources);
+
+ Value expansion(nullptr, "1234{{source_name_part}}5678");
+ args.push_back(expansion);
+
+ Err err;
+ Value result =
+ functions::RunProcessFileTemplate(setup.scope(), nullptr, args, &err);
+ EXPECT_FALSE(err.has_error());
+
+ ASSERT_TRUE(result.type() == Value::LIST);
+ ASSERT_EQ(1u, result.list_value().size());
+ ASSERT_TRUE(result.list_value()[0].type() == Value::STRING);
+ ASSERT_EQ("1234foo5678", result.list_value()[0].string_value());
+}
+
+TEST(FunctionProcessFileTemplates, MultipleStrings) {
+ TestWithScope setup;
+
+ std::vector<Value> args;
+
+ Value sources(nullptr, Value::LIST);
+ sources.list_value().push_back(Value(nullptr, "//src/one.txt"));
+ sources.list_value().push_back(Value(nullptr, "//src/two.txt"));
+ args.push_back(sources);
+
+ Value expansions(nullptr, Value::LIST);
+ expansions.list_value().push_back(
+ Value(nullptr, "1234{{source_name_part}}5678"));
+ expansions.list_value().push_back(
+ Value(nullptr, "ABCD{{source_file_part}}EFGH"));
+ args.push_back(expansions);
+
+ Err err;
+ Value result =
+ functions::RunProcessFileTemplate(setup.scope(), nullptr, args, &err);
+ EXPECT_FALSE(err.has_error());
+
+ ASSERT_TRUE(result.type() == Value::LIST);
+ ASSERT_EQ(4u, result.list_value().size());
+ ASSERT_TRUE(result.list_value()[0].type() == Value::STRING);
+ ASSERT_TRUE(result.list_value()[1].type() == Value::STRING);
+ ASSERT_TRUE(result.list_value()[2].type() == Value::STRING);
+ ASSERT_TRUE(result.list_value()[3].type() == Value::STRING);
+ ASSERT_EQ("1234one5678", result.list_value()[0].string_value());
+ ASSERT_EQ("ABCDone.txtEFGH", result.list_value()[1].string_value());
+ ASSERT_EQ("1234two5678", result.list_value()[2].string_value());
+ ASSERT_EQ("ABCDtwo.txtEFGH", result.list_value()[3].string_value());
+}
diff --git a/gn/src/gn/function_read_file.cc b/gn/src/gn/function_read_file.cc
new file mode 100644
index 00000000000..7aedf8e838d
--- /dev/null
+++ b/gn/src/gn/function_read_file.cc
@@ -0,0 +1,78 @@
+// 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 "base/files/file_util.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/input_conversion.h"
+#include "gn/input_file.h"
+#include "gn/scheduler.h"
+
+// TODO(brettw) consider removing this. I originally wrote it for making the
+// WebKit bindings but misundersood what was required, and didn't need to
+// use this. This seems to have a high potential for misuse.
+
+namespace functions {
+
+const char kReadFile[] = "read_file";
+const char kReadFile_HelpShort[] = "read_file: Read a file into a variable.";
+const char kReadFile_Help[] =
+ R"(read_file: Read a file into a variable.
+
+ read_file(filename, input_conversion)
+
+ Whitespace will be trimmed from the end of the file. Throws an error if the
+ file can not be opened.
+
+Arguments
+
+ filename
+ Filename to read, relative to the build file.
+
+ input_conversion
+ Controls how the file is read and parsed. See "gn help io_conversion".
+
+Example
+
+ lines = read_file("foo.txt", "list lines")
+)";
+
+Value RunReadFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function->function(), "Wrong number of arguments to read_file",
+ "I expected two arguments.");
+ return Value();
+ }
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+
+ // Compute the file name.
+ const SourceDir& cur_dir = scope->GetSourceDir();
+ SourceFile source_file = cur_dir.ResolveRelativeFile(
+ args[0], err, scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error())
+ return Value();
+ base::FilePath file_path =
+ scope->settings()->build_settings()->GetFullPath(source_file);
+
+ // Ensure that everything is recomputed if the read file changes.
+ g_scheduler->AddGenDependency(file_path);
+
+ // Read contents.
+ std::string file_contents;
+ if (!base::ReadFileToString(file_path, &file_contents)) {
+ *err = Err(args[0], "Could not read file.",
+ "I resolved this to \"" + FilePathToUTF8(file_path) + "\".");
+ return Value();
+ }
+
+ return ConvertInputToValue(scope->settings(), file_contents, function,
+ args[1], err);
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_rebase_path.cc b/gn/src/gn/function_rebase_path.cc
new file mode 100644
index 00000000000..7e912aa1a5e
--- /dev/null
+++ b/gn/src/gn/function_rebase_path.cc
@@ -0,0 +1,288 @@
+// 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 <stddef.h>
+
+#include "gn/build_settings.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/value.h"
+
+namespace functions {
+
+namespace {
+
+// We want the output to match the input in terms of ending in a slash or not.
+// Through all the transformations, these can get added or removed in various
+// cases.
+void MakeSlashEndingMatchInput(const std::string& input, std::string* output) {
+ if (EndsWithSlash(input)) {
+ if (!EndsWithSlash(*output)) // Preserve same slash type as input.
+ output->push_back(input[input.size() - 1]);
+ } else {
+ if (EndsWithSlash(*output))
+ output->resize(output->size() - 1);
+ }
+}
+
+// Returns true if the given value looks like a directory, otherwise we'll
+// assume it's a file.
+bool ValueLooksLikeDir(const std::string& value) {
+ if (value.empty())
+ return true;
+ size_t value_size = value.size();
+
+ // Count the number of dots at the end of the string.
+ size_t num_dots = 0;
+ while (num_dots < value_size && value[value_size - num_dots - 1] == '.')
+ num_dots++;
+
+ if (num_dots == value.size())
+ return true; // String is all dots.
+
+ if (IsSlash(value[value_size - num_dots - 1]))
+ return true; // String is a [back]slash followed by 0 or more dots.
+
+ // Anything else.
+ return false;
+}
+
+Value ConvertOnePath(const Scope* scope,
+ const FunctionCallNode* function,
+ const Value& value,
+ const SourceDir& from_dir,
+ const SourceDir& to_dir,
+ bool convert_to_system_absolute,
+ Err* err) {
+ Value result; // Ensure return value optimization.
+
+ if (!value.VerifyTypeIs(Value::STRING, err))
+ return result;
+ const std::string& string_value = value.string_value();
+
+ bool looks_like_dir = ValueLooksLikeDir(string_value);
+
+ // System-absolute output special case.
+ if (convert_to_system_absolute) {
+ base::FilePath system_path;
+ if (looks_like_dir) {
+ system_path = scope->settings()->build_settings()->GetFullPath(
+ from_dir.ResolveRelativeDir(
+ value, err,
+ scope->settings()->build_settings()->root_path_utf8()));
+ } else {
+ system_path = scope->settings()->build_settings()->GetFullPath(
+ from_dir.ResolveRelativeFile(
+ value, err,
+ scope->settings()->build_settings()->root_path_utf8()));
+ }
+ if (err->has_error())
+ return Value();
+
+ result = Value(function, FilePathToUTF8(system_path));
+ if (looks_like_dir)
+ MakeSlashEndingMatchInput(string_value, &result.string_value());
+ return result;
+ }
+
+ result = Value(function, Value::STRING);
+ if (looks_like_dir) {
+ result.string_value() = RebasePath(
+ from_dir
+ .ResolveRelativeDir(
+ value, err,
+ scope->settings()->build_settings()->root_path_utf8())
+ .value(),
+ to_dir, scope->settings()->build_settings()->root_path_utf8());
+ MakeSlashEndingMatchInput(string_value, &result.string_value());
+ } else {
+ SourceFile resolved_file = from_dir.ResolveRelativeFile(
+ value, err, scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error())
+ return Value();
+ result.string_value() =
+ RebasePath(resolved_file.value(), to_dir,
+ scope->settings()->build_settings()->root_path_utf8());
+ }
+
+ return result;
+}
+
+} // namespace
+
+const char kRebasePath[] = "rebase_path";
+const char kRebasePath_HelpShort[] =
+ "rebase_path: Rebase a file or directory to another location.";
+const char kRebasePath_Help[] =
+ R"(rebase_path: Rebase a file or directory to another location.
+
+ converted = rebase_path(input,
+ new_base = "",
+ current_base = ".")
+
+ Takes a string argument representing a file name, or a list of such strings
+ and converts it/them to be relative to a different base directory.
+
+ When invoking the compiler or scripts, GN will automatically convert sources
+ and include directories to be relative to the build directory. However, if
+ you're passing files directly in the "args" array or doing other manual
+ manipulations where GN doesn't know something is a file name, you will need
+ to convert paths to be relative to what your tool is expecting.
+
+ The common case is to use this to convert paths relative to the current
+ directory to be relative to the build directory (which will be the current
+ directory when executing scripts).
+
+ If you want to convert a file path to be source-absolute (that is, beginning
+ with a double slash like "//foo/bar"), you should use the get_path_info()
+ function. This function won't work because it will always make relative
+ paths, and it needs to support making paths relative to the source root, so
+ it can't also generate source-absolute paths without more special-cases.
+
+Arguments
+
+ input
+ A string or list of strings representing file or directory names. These
+ can be relative paths ("foo/bar.txt"), system absolute paths
+ ("/foo/bar.txt"), or source absolute paths ("//foo/bar.txt").
+
+ new_base
+ The directory to convert the paths to be relative to. This can be an
+ absolute path or a relative path (which will be treated as being relative
+ to the current BUILD-file's directory).
+
+ As a special case, if new_base is the empty string (the default), all
+ paths will be converted to system-absolute native style paths with system
+ path separators. This is useful for invoking external programs.
+
+ current_base
+ Directory representing the base for relative paths in the input. If this
+ is not an absolute path, it will be treated as being relative to the
+ current build file. Use "." (the default) to convert paths from the
+ current BUILD-file's directory.
+
+Return value
+
+ The return value will be the same type as the input value (either a string or
+ a list of strings). All relative and source-absolute file names will be
+ converted to be relative to the requested output System-absolute paths will
+ be unchanged.
+
+ Whether an output path will end in a slash will match whether the
+ corresponding input path ends in a slash. It will return "." or "./"
+ (depending on whether the input ends in a slash) to avoid returning empty
+ strings. This means if you want a root path ("//" or "/") not ending in a
+ slash, you can add a dot ("//.").
+
+Example
+
+ # Convert a file in the current directory to be relative to the build
+ # directory (the current dir when executing compilers and scripts).
+ foo = rebase_path("myfile.txt", root_build_dir)
+ # might produce "../../project/myfile.txt".
+
+ # Convert a file to be system absolute:
+ foo = rebase_path("myfile.txt")
+ # Might produce "D:\\source\\project\\myfile.txt" on Windows or
+ # "/home/you/source/project/myfile.txt" on Linux.
+
+ # Typical usage for converting to the build directory for a script.
+ action("myscript") {
+ # Don't convert sources, GN will automatically convert these to be relative
+ # to the build directory when it constructs the command line for your
+ # script.
+ sources = [ "foo.txt", "bar.txt" ]
+
+ # Extra file args passed manually need to be explicitly converted
+ # to be relative to the build directory:
+ args = [
+ "--data",
+ rebase_path("//mything/data/input.dat", root_build_dir),
+ "--rel",
+ rebase_path("relative_path.txt", root_build_dir)
+ ] + rebase_path(sources, root_build_dir)
+ }
+)";
+
+Value RunRebasePath(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ Value result;
+
+ // Argument indices.
+ static const size_t kArgIndexInputs = 0;
+ static const size_t kArgIndexDest = 1;
+ static const size_t kArgIndexFrom = 2;
+
+ // Inputs.
+ if (args.size() < 1 || args.size() > 3) {
+ *err = Err(function->function(), "Wrong # of arguments for rebase_path.");
+ return result;
+ }
+ const Value& inputs = args[kArgIndexInputs];
+
+ // To path.
+ bool convert_to_system_absolute = true;
+ SourceDir to_dir;
+ const SourceDir& current_dir = scope->GetSourceDir();
+ if (args.size() > kArgIndexDest) {
+ if (!args[kArgIndexDest].VerifyTypeIs(Value::STRING, err))
+ return result;
+ if (!args[kArgIndexDest].string_value().empty()) {
+ to_dir = current_dir.ResolveRelativeDir(
+ args[kArgIndexDest], err,
+ scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error())
+ return Value();
+ convert_to_system_absolute = false;
+ }
+ }
+
+ // From path.
+ SourceDir from_dir;
+ if (args.size() > kArgIndexFrom) {
+ if (!args[kArgIndexFrom].VerifyTypeIs(Value::STRING, err))
+ return result;
+ from_dir = current_dir.ResolveRelativeDir(
+ args[kArgIndexFrom], err,
+ scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error())
+ return Value();
+ } else {
+ // Default to current directory if unspecified.
+ from_dir = current_dir;
+ }
+
+ // Path conversion.
+ if (inputs.type() == Value::STRING) {
+ return ConvertOnePath(scope, function, inputs, from_dir, to_dir,
+ convert_to_system_absolute, err);
+
+ } else if (inputs.type() == Value::LIST) {
+ result = Value(function, Value::LIST);
+ result.list_value().reserve(inputs.list_value().size());
+
+ for (const auto& input : inputs.list_value()) {
+ result.list_value().push_back(
+ ConvertOnePath(scope, function, input, from_dir, to_dir,
+ convert_to_system_absolute, err));
+ if (err->has_error()) {
+ result = Value();
+ return result;
+ }
+ }
+ return result;
+ }
+
+ *err = Err(function->function(), "rebase_path requires a list or a string.");
+ return result;
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_rebase_path_unittest.cc b/gn/src/gn/function_rebase_path_unittest.cc
new file mode 100644
index 00000000000..a4c403f1069
--- /dev/null
+++ b/gn/src/gn/function_rebase_path_unittest.cc
@@ -0,0 +1,183 @@
+// 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 "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+namespace {
+
+std::string RebaseOne(Scope* scope,
+ const char* input,
+ const char* to_dir,
+ const char* from_dir) {
+ std::vector<Value> args;
+ args.push_back(Value(nullptr, input));
+ args.push_back(Value(nullptr, to_dir));
+ args.push_back(Value(nullptr, from_dir));
+
+ Err err;
+ FunctionCallNode function;
+ Value result = functions::RunRebasePath(scope, &function, args, &err);
+ bool is_string = result.type() == Value::STRING;
+ EXPECT_TRUE(is_string);
+
+ return result.string_value();
+}
+
+} // namespace
+
+TEST(RebasePath, Strings) {
+ TestWithScope setup;
+ Scope* scope = setup.scope();
+ scope->set_source_dir(SourceDir("//tools/gn/"));
+
+ // Build-file relative paths.
+ EXPECT_EQ("../../tools/gn", RebaseOne(scope, ".", "//out/Debug", "."));
+ EXPECT_EQ("../../tools/gn/", RebaseOne(scope, "./", "//out/Debug", "."));
+ EXPECT_EQ("../../tools/gn/foo", RebaseOne(scope, "foo", "//out/Debug", "."));
+ EXPECT_EQ("../..", RebaseOne(scope, "../..", "//out/Debug", "."));
+ EXPECT_EQ("../../", RebaseOne(scope, "../../", "//out/Debug", "."));
+
+ // Without a source root defined, we cannot move out of the source tree.
+ EXPECT_EQ("../..", RebaseOne(scope, "../../..", "//out/Debug", "."));
+
+ // Source-absolute input paths.
+ EXPECT_EQ("./", RebaseOne(scope, "//", "//", "//"));
+ EXPECT_EQ("foo", RebaseOne(scope, "//foo", "//", "//"));
+ EXPECT_EQ("foo/", RebaseOne(scope, "//foo/", "//", "//"));
+ EXPECT_EQ("../../foo/bar", RebaseOne(scope, "//foo/bar", "//out/Debug", "."));
+ EXPECT_EQ("./", RebaseOne(scope, "//foo/", "//foo/", "//"));
+ EXPECT_EQ(".", RebaseOne(scope, "//foo", "//foo", "//"));
+
+ // Test slash conversion.
+ EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", "."));
+ EXPECT_EQ("foo/bar", RebaseOne(scope, "foo\\bar", ".", "."));
+
+// Test system path output.
+#if defined(OS_WIN)
+ setup.build_settings()->SetRootPath(base::FilePath(u"C:/path/to/src"));
+ EXPECT_EQ("C:/path/to/src", RebaseOne(scope, ".", "", "//"));
+ EXPECT_EQ("C:/path/to/src/", RebaseOne(scope, "//", "", "//"));
+ EXPECT_EQ("C:/path/to/src/foo", RebaseOne(scope, "foo", "", "//"));
+ EXPECT_EQ("C:/path/to/src/foo/", RebaseOne(scope, "foo/", "", "//"));
+ EXPECT_EQ("C:/path/to/src/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
+ EXPECT_EQ("C:/path/to/other/tools",
+ RebaseOne(scope, "//../other/tools", "", "//"));
+ EXPECT_EQ("C:/path/to/src/foo/bar",
+ RebaseOne(scope, "//../src/foo/bar", "", "//"));
+ EXPECT_EQ("C:/path/to", RebaseOne(scope, "//..", "", "//"));
+ EXPECT_EQ("C:/path", RebaseOne(scope, "../../../..", "", "."));
+ EXPECT_EQ("C:/path/to/external/dir/",
+ RebaseOne(scope, "//../external/dir/", "", "//"));
+
+#else
+ setup.build_settings()->SetRootPath(base::FilePath("/path/to/src"));
+ EXPECT_EQ("/path/to/src", RebaseOne(scope, ".", "", "//"));
+ EXPECT_EQ("/path/to/src/", RebaseOne(scope, "//", "", "//"));
+ EXPECT_EQ("/path/to/src/foo", RebaseOne(scope, "foo", "", "//"));
+ EXPECT_EQ("/path/to/src/foo/", RebaseOne(scope, "foo/", "", "//"));
+ EXPECT_EQ("/path/to/src/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
+ EXPECT_EQ("/path/to/other/tools",
+ RebaseOne(scope, "//../other/tools", "", "//"));
+ EXPECT_EQ("/path/to/src/foo/bar",
+ RebaseOne(scope, "//../src/foo/bar", "", "//"));
+ EXPECT_EQ("/path/to", RebaseOne(scope, "//..", "", "//"));
+ EXPECT_EQ("/path", RebaseOne(scope, "../../../..", "", "."));
+ EXPECT_EQ("/path/to/external/dir/",
+ RebaseOne(scope, "//../external/dir/", "", "//"));
+#endif
+}
+
+TEST(RebasePath, StringsSystemPaths) {
+ TestWithScope setup;
+ Scope* scope = setup.scope();
+
+#if defined(OS_WIN)
+ setup.build_settings()->SetBuildDir(SourceDir("C:/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath(u"C:/hdd/src"));
+
+ // Test system absolute to-dir.
+ EXPECT_EQ("../../ssd/out/Debug",
+ RebaseOne(scope, ".", "//", "C:/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/",
+ RebaseOne(scope, "./", "//", "C:/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo",
+ RebaseOne(scope, "foo", "//", "C:/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo/",
+ RebaseOne(scope, "foo/", "//", "C:/ssd/out/Debug"));
+
+ // Test system absolute from-dir.
+ EXPECT_EQ("../../../hdd/src",
+ RebaseOne(scope, ".", "C:/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/",
+ RebaseOne(scope, "./", "C:/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo",
+ RebaseOne(scope, "foo", "C:/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo/",
+ RebaseOne(scope, "foo/", "C:/ssd/out/Debug", "//"));
+#else
+ setup.build_settings()->SetBuildDir(SourceDir("/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath("/ssd/out/Debug"));
+ EXPECT_EQ("../Debug-suffix/a", RebaseOne(scope, "/ssd/out/Debug-suffix/a",
+ "/ssd/out/Debug", "/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath("/hdd/src"));
+
+ // Test system absolute to-dir.
+ EXPECT_EQ("../../ssd/out/Debug",
+ RebaseOne(scope, ".", "//", "/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/",
+ RebaseOne(scope, "./", "//", "/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo",
+ RebaseOne(scope, "foo", "//", "/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo/",
+ RebaseOne(scope, "foo/", "//", "/ssd/out/Debug"));
+
+ // Test system absolute from-dir.
+ EXPECT_EQ("../../../hdd/src", RebaseOne(scope, ".", "/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/",
+ RebaseOne(scope, "./", "/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo",
+ RebaseOne(scope, "foo", "/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo/",
+ RebaseOne(scope, "foo/", "/ssd/out/Debug", "//"));
+#endif
+}
+
+// Test list input.
+TEST(RebasePath, List) {
+ TestWithScope setup;
+ setup.scope()->set_source_dir(SourceDir("//gn/"));
+
+ std::vector<Value> args;
+ args.push_back(Value(nullptr, Value::LIST));
+ args[0].list_value().push_back(Value(nullptr, "foo.txt"));
+ args[0].list_value().push_back(Value(nullptr, "bar.txt"));
+ args.push_back(Value(nullptr, "//out/Debug/"));
+ args.push_back(Value(nullptr, "."));
+
+ Err err;
+ FunctionCallNode function;
+ Value ret = functions::RunRebasePath(setup.scope(), &function, args, &err);
+ EXPECT_FALSE(err.has_error());
+
+ ASSERT_EQ(Value::LIST, ret.type());
+ ASSERT_EQ(2u, ret.list_value().size());
+
+ EXPECT_EQ("../../gn/foo.txt", ret.list_value()[0].string_value());
+ EXPECT_EQ("../../gn/bar.txt", ret.list_value()[1].string_value());
+}
+
+TEST(RebasePath, Errors) {
+ TestWithScope setup;
+
+ // No arg input should issue an error.
+ Err err;
+ std::vector<Value> args;
+ FunctionCallNode function;
+ Value ret = functions::RunRebasePath(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
diff --git a/gn/src/gn/function_set_default_toolchain.cc b/gn/src/gn/function_set_default_toolchain.cc
new file mode 100644
index 00000000000..13693773e01
--- /dev/null
+++ b/gn/src/gn/function_set_default_toolchain.cc
@@ -0,0 +1,88 @@
+// 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 "gn/build_settings.h"
+#include "gn/functions.h"
+#include "gn/loader.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+
+namespace functions {
+
+const char kSetDefaultToolchain[] = "set_default_toolchain";
+const char kSetDefaultToolchain_HelpShort[] =
+ "set_default_toolchain: Sets the default toolchain name.";
+const char kSetDefaultToolchain_Help[] =
+ R"(set_default_toolchain: Sets the default toolchain name.
+
+ set_default_toolchain(toolchain_label)
+
+ The given label should identify a toolchain definition (see "gn help
+ toolchain"). This toolchain will be used for all targets unless otherwise
+ specified.
+
+ This function is only valid to call during the processing of the build
+ configuration file. Since the build configuration file is processed
+ separately for each toolchain, this function will be a no-op when called
+ under any non-default toolchains.
+
+ For example, the default toolchain should be appropriate for the current
+ environment. If the current environment is 32-bit and somebody references a
+ target with a 64-bit toolchain, we wouldn't want processing of the build
+ config file for the 64-bit toolchain to reset the default toolchain to
+ 64-bit, we want to keep it 32-bits.
+
+Argument
+
+ toolchain_label
+ Toolchain name.
+
+Example
+
+ # Set default toolchain only has an effect when run in the context of the
+ # default toolchain. Pick the right one according to the current CPU
+ # architecture.
+ if (target_cpu == "x64") {
+ set_default_toolchain("//toolchains:64")
+ } else if (target_cpu == "x86") {
+ set_default_toolchain("//toolchains:32")
+ }
+)";
+
+Value RunSetDefaultToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!scope->IsProcessingBuildConfig()) {
+ *err = Err(
+ function->function(), "Must be called from build config.",
+ "set_default_toolchain can only be called from the build configuration "
+ "file.");
+ return Value();
+ }
+
+ // When the loader is expecting the default toolchain to be set, it will set
+ // this key on the scope to point to the destination.
+ Label* default_toolchain_dest = static_cast<Label*>(
+ scope->GetProperty(Loader::kDefaultToolchainKey, nullptr));
+ if (!default_toolchain_dest)
+ return Value();
+
+ const SourceDir& current_dir = scope->GetSourceDir();
+ const Label& default_toolchain = ToolchainLabelForScope(scope);
+
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ Label toolchain_label(Label::Resolve(
+ current_dir, scope->settings()->build_settings()->root_path_utf8(),
+ default_toolchain, args[0], err));
+ if (toolchain_label.is_null())
+ return Value();
+
+ *default_toolchain_dest = toolchain_label;
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_set_defaults.cc b/gn/src/gn/function_set_defaults.cc
new file mode 100644
index 00000000000..031edc32178
--- /dev/null
+++ b/gn/src/gn/function_set_defaults.cc
@@ -0,0 +1,76 @@
+// 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 "gn/err.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+
+namespace functions {
+
+const char kSetDefaults[] = "set_defaults";
+const char kSetDefaults_HelpShort[] =
+ "set_defaults: Set default values for a target type.";
+const char kSetDefaults_Help[] =
+ R"(set_defaults: Set default values for a target type.
+
+ set_defaults(<target_type_name>) { <values...> }
+
+ Sets the default values for a given target type. Whenever target_type_name is
+ seen in the future, the values specified in set_default's block will be
+ copied into the current scope.
+
+ When the target type is used, the variable copying is very strict. If a
+ variable with that name is already in scope, the build will fail with an
+ error.
+
+ set_defaults can be used for built-in target types ("executable",
+ "shared_library", etc.) and custom ones defined via the "template" command.
+ It can be called more than once and the most recent call in any scope will
+ apply, but there is no way to refer to the previous defaults and modify them
+ (each call to set_defaults must supply a complete list of all defaults it
+ wants). If you want to share defaults, store them in a separate variable.
+
+Example
+
+ set_defaults("static_library") {
+ configs = [ "//tools/mything:settings" ]
+ }
+
+ static_library("mylib") {
+ # The configs will be auto-populated as above. You can remove it if
+ # you don't want the default for a particular default:
+ configs -= [ "//tools/mything:settings" ]
+ }
+)";
+
+Value RunSetDefaults(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ const std::string& target_type(args[0].string_value());
+
+ if (!block) {
+ FillNeedsBlockError(function, err);
+ return Value();
+ }
+
+ // Run the block for the rule invocation.
+ Scope block_scope(scope);
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Now copy the values set on the scope we made into the free-floating one
+ // (with no containing scope) used to hold the target defaults.
+ Scope* dest = scope->MakeTargetDefaults(target_type);
+ block_scope.NonRecursiveMergeTo(dest, Scope::MergeOptions(), function,
+ "<SHOULD NOT FAIL>", err);
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_template.cc b/gn/src/gn/function_template.cc
new file mode 100644
index 00000000000..92d541977ea
--- /dev/null
+++ b/gn/src/gn/function_template.cc
@@ -0,0 +1,226 @@
+// 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 "gn/functions.h"
+
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/template.h"
+#include "gn/value.h"
+
+namespace functions {
+
+const char kTemplate[] = "template";
+const char kTemplate_HelpShort[] = "template: Define a template rule.";
+const char kTemplate_Help[] =
+ R"(template: Define a template rule.
+
+ A template defines a custom name that acts like a function. It provides a way
+ to add to the built-in target types.
+
+ The template() function is used to declare a template. To invoke the
+ template, just use the name of the template like any other target type.
+
+ Often you will want to declare your template in a special file that other
+ files will import (see "gn help import") so your template rule can be shared
+ across build files.
+
+Variables and templates:
+
+ When you call template() it creates a closure around all variables currently
+ in scope with the code in the template block. When the template is invoked,
+ the closure will be executed.
+
+ When the template is invoked, the code in the caller is executed and passed
+ to the template code as an implicit "invoker" variable. The template uses
+ this to read state out of the invoking code.
+
+ One thing explicitly excluded from the closure is the "current directory"
+ against which relative file names are resolved. The current directory will be
+ that of the invoking code, since typically that code specifies the file
+ names. This means all files internal to the template should use absolute
+ names.
+
+ A template will typically forward some or all variables from the invoking
+ scope to a target that it defines. Often, such variables might be optional.
+ Use the pattern:
+
+ if (defined(invoker.deps)) {
+ deps = invoker.deps
+ }
+
+ The function forward_variables_from() provides a shortcut to forward one or
+ more or possibly all variables in this manner:
+
+ forward_variables_from(invoker, ["deps", "public_deps"])
+
+Target naming
+
+ Your template should almost always define a built-in target with the name the
+ template invoker specified. For example, if you have an IDL template and
+ somebody does:
+ idl("foo") {...
+ you will normally want this to expand to something defining a source_set or
+ static_library named "foo" (among other things you may need). This way, when
+ another target specifies a dependency on "foo", the static_library or
+ source_set will be linked.
+
+ It is also important that any other targets your template expands to have
+ unique names, or you will get collisions.
+
+ Access the invoking name in your template via the implicit "target_name"
+ variable. This should also be the basis for how other targets that a template
+ expands to ensure uniqueness.
+
+ A typical example would be a template that defines an action to generate some
+ source files, and a source_set to compile that source. Your template would
+ name the source_set "target_name" because that's what you want external
+ targets to depend on to link your code. And you would name the action
+ something like "${target_name}_action" to make it unique. The source set
+ would have a dependency on the action to make it run.
+
+Overriding builtin targets
+
+ You can use template to redefine a built-in target in which case your template
+ takes a precedence over the built-in one. All uses of the target from within
+ the template definition will refer to the built-in target which makes it
+ possible to extend the behavior of the built-in target:
+
+ template("shared_library") {
+ shared_library(shlib) {
+ forward_variables_from(invoker, "*")
+ ...
+ }
+ }
+
+Example of defining a template
+
+ template("my_idl") {
+ # Be nice and help callers debug problems by checking that the variables
+ # the template requires are defined. This gives a nice message rather than
+ # giving the user an error about an undefined variable in the file defining
+ # the template
+ #
+ # You can also use defined() to give default values to variables
+ # unspecified by the invoker.
+ assert(defined(invoker.sources),
+ "Need sources in $target_name listing the idl files.")
+
+ # Name of the intermediate target that does the code gen. This must
+ # incorporate the target name so it's unique across template
+ # instantiations.
+ code_gen_target_name = target_name + "_code_gen"
+
+ # Intermediate target to convert IDL to C source. Note that the name is
+ # based on the name the invoker of the template specified. This way, each
+ # time the template is invoked we get a unique intermediate action name
+ # (since all target names are in the global scope).
+ action_foreach(code_gen_target_name) {
+ # Access the scope defined by the invoker via the implicit "invoker"
+ # variable.
+ sources = invoker.sources
+
+ # Note that we need an absolute path for our script file name. The
+ # current directory when executing this code will be that of the invoker
+ # (this is why we can use the "sources" directly above without having to
+ # rebase all of the paths). But if we need to reference a script relative
+ # to the template file, we'll need to use an absolute path instead.
+ script = "//tools/idl/idl_code_generator.py"
+
+ # Tell GN how to expand output names given the sources.
+ # See "gn help source_expansion" for more.
+ outputs = [ "$target_gen_dir/{{source_name_part}}.cc",
+ "$target_gen_dir/{{source_name_part}}.h" ]
+ }
+
+ # Name the source set the same as the template invocation so instancing
+ # this template produces something that other targets can link to in their
+ # deps.
+ source_set(target_name) {
+ # Generates the list of sources, we get these from the action_foreach
+ # above.
+ sources = get_target_outputs(":$code_gen_target_name")
+
+ # This target depends on the files produced by the above code gen target.
+ deps = [ ":$code_gen_target_name" ]
+ }
+ }
+
+Example of invoking the resulting template
+
+ # This calls the template code above, defining target_name to be
+ # "foo_idl_files" and "invoker" to be the set of stuff defined in the curly
+ # brackets.
+ my_idl("foo_idl_files") {
+ # Goes into the template as "invoker.sources".
+ sources = [ "foo.idl", "bar.idl" ]
+ }
+
+ # Here is a target that depends on our template.
+ executable("my_exe") {
+ # Depend on the name we gave the template call above. Internally, this will
+ # produce a dependency from executable to the source_set inside the
+ # template (since it has this name), which will in turn depend on the code
+ # gen action.
+ deps = [ ":foo_idl_files" ]
+ }
+)";
+
+Value RunTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ // Of course you can have configs and targets in a template. But here, we're
+ // not actually executing the block, only declaring it. Marking the template
+ // declaration as non-nestable means that you can't put it inside a target,
+ // for example.
+ NonNestableBlock non_nestable(scope, function, "template");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ // TODO(brettw) determine if the function is built-in and throw an error if
+ // it is.
+ if (args.size() != 1) {
+ *err =
+ Err(function->function(), "Need exactly one string arg to template.");
+ return Value();
+ }
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ std::string template_name = args[0].string_value();
+
+ const Template* existing_template = scope->GetTemplate(template_name);
+ if (existing_template) {
+ *err = Err(function, "Duplicate template definition.",
+ "A template with this name was already defined.");
+ err->AppendSubErr(
+ Err(existing_template->GetDefinitionRange(), "Previous definition."));
+ return Value();
+ }
+
+ scope->AddTemplate(template_name, new Template(scope, function));
+
+ // The template object above created a closure around the variables in the
+ // current scope. The template code will execute in that context when it's
+ // invoked. But this means that any variables defined above that are used
+ // by the template won't get marked used just by defining the template. The
+ // result can be spurious unused variable errors.
+ //
+ // The "right" thing to do would be to walk the syntax tree inside the
+ // template, find all identifier references, and mark those variables used.
+ // This is annoying and error-prone to implement and takes extra time to run
+ // for this narrow use case.
+ //
+ // Templates are most often defined in .gni files which don't get
+ // used-variable checking anyway, and this case is annoying enough that the
+ // incremental value of unused variable checking isn't worth the
+ // alternatives. So all values in scope before this template definition are
+ // exempted from unused variable checking.
+ scope->MarkAllUsed();
+
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_template_unittest.cc b/gn/src/gn/function_template_unittest.cc
new file mode 100644
index 00000000000..2ea9e8b484a
--- /dev/null
+++ b/gn/src/gn/function_template_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium 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 "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+// Checks that variables used inside template definitions aren't reported
+// unused if they were declared above the template.
+TEST(FunctionTemplate, MarkUsed) {
+ TestWithScope setup;
+ TestParseInput input(
+ "a = 1\n" // Unused outside of template.
+ "template(\"templ\") {\n"
+ " print(a)\n"
+ "}\n");
+ ASSERT_FALSE(input.has_error()) << input.parse_err().message();
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // Normally the loader calls CheckForUnusedVars() when it loads a file
+ // since normal blocks don't do this check. To avoid having to make this
+ // test much more complicated, just explicitly do the check to make sure
+ // things are marked properly.
+ setup.scope()->CheckForUnusedVars(&err);
+ EXPECT_FALSE(err.has_error());
+}
diff --git a/gn/src/gn/function_toolchain.cc b/gn/src/gn/function_toolchain.cc
new file mode 100644
index 00000000000..a6deec68039
--- /dev/null
+++ b/gn/src/gn/function_toolchain.cc
@@ -0,0 +1,928 @@
+// 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 <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "gn/c_tool.h"
+#include "gn/err.h"
+#include "gn/functions.h"
+#include "gn/general_tool.h"
+#include "gn/label.h"
+#include "gn/label_ptr.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/tool.h"
+#include "gn/toolchain.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+namespace functions {
+
+namespace {
+
+// This is just a unique value to take the address of to use as the key for
+// the toolchain property on a scope.
+const int kToolchainPropertyKey = 0;
+
+} // namespace
+
+// toolchain -------------------------------------------------------------------
+
+const char kToolchain[] = "toolchain";
+const char kToolchain_HelpShort[] = "toolchain: Defines a toolchain.";
+const char kToolchain_Help[] =
+ R"*(toolchain: Defines a toolchain.
+
+ A toolchain is a set of commands and build flags used to compile the source
+ code. The toolchain() function defines these commands.
+
+Toolchain overview
+
+ You can have more than one toolchain in use at once in a build and a target
+ can exist simultaneously in multiple toolchains. A build file is executed
+ once for each toolchain it is referenced in so the GN code can vary all
+ parameters of each target (or which targets exist) on a per-toolchain basis.
+
+ When you have a simple build with only one toolchain, the build config file
+ is loaded only once at the beginning of the build. It must call
+ set_default_toolchain() (see "gn help set_default_toolchain") to tell GN the
+ label of the toolchain definition to use. The "toolchain_args" section of the
+ toolchain definition is ignored.
+
+ When a target has a dependency on a target using different toolchain (see "gn
+ help labels" for how to specify this), GN will start a build using that
+ secondary toolchain to resolve the target. GN will load the build config file
+ with the build arguments overridden as specified in the toolchain_args.
+ Because the default toolchain is already known, calls to
+ set_default_toolchain() are ignored.
+
+ To load a file in an alternate toolchain, GN does the following:
+
+ 1. Loads the file with the toolchain definition in it (as determined by the
+ toolchain label).
+ 2. Re-runs the master build configuration file, applying the arguments
+ specified by the toolchain_args section of the toolchain definition.
+ 3. Loads the destination build file in the context of the configuration file
+ in the previous step.
+
+ The toolchain configuration is two-way. In the default toolchain (i.e. the
+ main build target) the configuration flows from the build config file to the
+ toolchain. The build config file looks at the state of the build (OS type,
+ CPU architecture, etc.) and decides which toolchain to use (via
+ set_default_toolchain()). In secondary toolchains, the configuration flows
+ from the toolchain to the build config file: the "toolchain_args" in the
+ toolchain definition specifies the arguments to re-invoke the build.
+
+Functions and variables
+
+ tool()
+ The tool() function call specifies the commands to run for a given step. See
+ "gn help tool".
+
+ toolchain_args [scope]
+ Overrides for build arguments to pass to the toolchain when invoking it.
+ This is a variable of type "scope" where the variable names correspond to
+ variables in declare_args() blocks.
+
+ When you specify a target using an alternate toolchain, the master build
+ configuration file is re-interpreted in the context of that toolchain.
+ toolchain_args allows you to control the arguments passed into this
+ alternate invocation of the build.
+
+ Any default system arguments or arguments passed in via "gn args" will also
+ be passed to the alternate invocation unless explicitly overridden by
+ toolchain_args.
+
+ The toolchain_args will be ignored when the toolchain being defined is the
+ default. In this case, it's expected you want the default argument values.
+
+ See also "gn help buildargs" for an overview of these arguments.
+
+ propagates_configs [boolean, default=false]
+ Determines whether public_configs and all_dependent_configs in this
+ toolchain propagate to targets in other toolchains.
+
+ When false (the default), this toolchain will not propagate any configs to
+ targets in other toolchains that depend on it targets inside this
+ toolchain. This matches the most common usage of toolchains where they
+ represent different architectures or compilers and the settings that apply
+ to one won't necessarily apply to others.
+
+ When true, configs (public and all-dependent) will cross the boundary out
+ of this toolchain as if the toolchain boundary wasn't there. This only
+ affects one direction of dependencies: a toolchain can't control whether
+ it accepts such configs, only whether it pushes them. The build is
+ responsible for ensuring that any external targets depending on targets in
+ this toolchain are compatible with the compiler flags, etc. that may be
+ propagated.
+
+ deps [string list]
+ Dependencies of this toolchain. These dependencies will be resolved before
+ any target in the toolchain is compiled. To avoid circular dependencies
+ these must be targets defined in another toolchain.
+
+ This is expressed as a list of targets, and generally these targets will
+ always specify a toolchain:
+ deps = [ "//foo/bar:baz(//build/toolchain:bootstrap)" ]
+
+ This concept is somewhat inefficient to express in Ninja (it requires a lot
+ of duplicate of rules) so should only be used when absolutely necessary.
+
+Example of defining a toolchain
+
+ toolchain("32") {
+ tool("cc") {
+ command = "gcc {{source}}"
+ ...
+ }
+
+ toolchain_args = {
+ use_doom_melon = true # Doom melon always required for 32-bit builds.
+ current_cpu = "x86"
+ }
+ }
+
+ toolchain("64") {
+ tool("cc") {
+ command = "gcc {{source}}"
+ ...
+ }
+
+ toolchain_args = {
+ # use_doom_melon is not overridden here, it will take the default.
+ current_cpu = "x64"
+ }
+ }
+
+Example of cross-toolchain dependencies
+
+ If a 64-bit target wants to depend on a 32-bit binary, it would specify a
+ dependency using data_deps (data deps are like deps that are only needed at
+ runtime and aren't linked, since you can't link a 32-bit and a 64-bit
+ library).
+
+ executable("my_program") {
+ ...
+ if (target_cpu == "x64") {
+ # The 64-bit build needs this 32-bit helper.
+ data_deps = [ ":helper(//toolchains:32)" ]
+ }
+ }
+
+ if (target_cpu == "x86") {
+ # Our helper library is only compiled in 32-bits.
+ shared_library("helper") {
+ ...
+ }
+ }
+)*";
+
+Value RunToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "toolchain");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ if (!EnsureNotProcessingImport(function, scope, err) ||
+ !EnsureNotProcessingBuildConfig(function, scope, err))
+ return Value();
+
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+
+ // Note that we don't want to use MakeLabelForScope since that will include
+ // the toolchain name in the label, and toolchain labels don't themselves
+ // have toolchain names.
+ const SourceDir& input_dir = scope->GetSourceDir();
+ Label label(input_dir, args[0].string_value());
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Defining toolchain", label.GetUserVisibleName(false));
+
+ // This object will actually be copied into the one owned by the toolchain
+ // manager, but that has to be done in the lock.
+ std::unique_ptr<Toolchain> toolchain = std::make_unique<Toolchain>(
+ scope->settings(), label, scope->build_dependency_files());
+ toolchain->set_defined_from(function);
+ toolchain->visibility().SetPublic();
+
+ Scope block_scope(scope);
+ block_scope.SetProperty(&kToolchainPropertyKey, toolchain.get());
+ block->Execute(&block_scope, err);
+ block_scope.SetProperty(&kToolchainPropertyKey, nullptr);
+ if (err->has_error())
+ return Value();
+
+ // Read deps (if any).
+ const Value* deps_value = block_scope.GetValue(variables::kDeps, true);
+ if (deps_value) {
+ ExtractListOfLabels(scope->settings()->build_settings(), *deps_value,
+ block_scope.GetSourceDir(),
+ ToolchainLabelForScope(&block_scope),
+ &toolchain->deps(), err);
+ if (err->has_error())
+ return Value();
+ }
+
+ // Read toolchain args (if any).
+ const Value* toolchain_args = block_scope.GetValue("toolchain_args", true);
+ if (toolchain_args) {
+ if (!toolchain_args->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+
+ Scope::KeyValueMap values;
+ toolchain_args->scope_value()->GetCurrentScopeValues(&values);
+ toolchain->args() = values;
+ }
+
+ // Read propagates_configs (if present).
+ const Value* propagates_configs =
+ block_scope.GetValue("propagates_configs", true);
+ if (propagates_configs) {
+ if (!propagates_configs->VerifyTypeIs(Value::BOOLEAN, err))
+ return Value();
+ toolchain->set_propagates_configs(propagates_configs->boolean_value());
+ }
+
+ if (!block_scope.CheckForUnusedVars(err))
+ return Value();
+
+ // Save this toolchain.
+ toolchain->ToolchainSetupComplete();
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function, "Can't define a toolchain in this context.");
+ return Value();
+ }
+ collector->push_back(std::move(toolchain));
+ return Value();
+}
+
+// tool ------------------------------------------------------------------------
+
+const char kTool[] = "tool";
+const char kTool_HelpShort[] = "tool: Specify arguments to a toolchain tool.";
+const char kTool_Help[] =
+ R"(tool: Specify arguments to a toolchain tool.
+
+Usage
+
+ tool(<tool type>) {
+ <tool variables...>
+ }
+
+Tool types
+
+ Compiler tools:
+ "cc": C compiler
+ "cxx": C++ compiler
+ "cxx_module": C++ compiler used for Clang .modulemap files
+ "objc": Objective C compiler
+ "objcxx": Objective C++ compiler
+ "rc": Resource compiler (Windows .rc files)
+ "asm": Assembler
+ "swift": Swift compiler driver
+
+ Linker tools:
+ "alink": Linker for static libraries (archives)
+ "solink": Linker for shared libraries
+ "link": Linker for executables
+
+ Other tools:
+ "stamp": Tool for creating stamp files
+ "copy": Tool to copy files.
+ "action": Defaults for actions
+
+ Platform specific tools:
+ "copy_bundle_data": [iOS, macOS] Tool to copy files in a bundle.
+ "compile_xcassets": [iOS, macOS] Tool to compile asset catalogs.
+
+ Rust tools:
+ "rust_bin": Tool for compiling Rust binaries
+ "rust_cdylib": Tool for compiling C-compatible dynamic libraries.
+ "rust_dylib": Tool for compiling Rust dynamic libraries.
+ "rust_macro": Tool for compiling Rust procedural macros.
+ "rust_rlib": Tool for compiling Rust libraries.
+ "rust_staticlib": Tool for compiling Rust static libraries.
+
+Tool variables
+
+ command [string with substitutions]
+ Valid for: all tools except "action" (required)
+
+ The command to run.
+
+ command_launcher [string]
+ Valid for: all tools except "action" (optional)
+
+ The prefix with which to launch the command (e.g. the path to a Goma or
+ CCache compiler launcher).
+
+ Note that this prefix will not be included in the compilation database or
+ IDE files generated from the build.
+
+ default_output_dir [string with substitutions]
+ Valid for: linker tools
+
+ Default directory name for the output file relative to the
+ root_build_dir. It can contain other substitution patterns. This will
+ be the default value for the {{output_dir}} expansion (discussed below)
+ but will be overridden by the "output_dir" variable in a target, if one
+ is specified.
+
+ GN doesn't do anything with this string other than pass it along,
+ potentially with target-specific overrides. It is the tool's job to use
+ the expansion so that the files will be in the right place.
+
+ default_output_extension [string]
+ Valid for: linker tools
+
+ Extension for the main output of a linkable tool. It includes the
+ leading dot. This will be the default value for the
+ {{output_extension}} expansion (discussed below) but will be overridden
+ by by the "output extension" variable in a target, if one is specified.
+ Empty string means no extension.
+
+ GN doesn't actually do anything with this extension other than pass it
+ along, potentially with target-specific overrides. One would typically
+ use the {{output_extension}} value in the "outputs" to read this value.
+
+ Example: default_output_extension = ".exe"
+
+ depfile [string with substitutions]
+ Valid for: compiler tools (optional)
+
+ If the tool can write ".d" files, this specifies the name of the
+ resulting file. These files are used to list header file dependencies
+ (or other implicit input dependencies) that are discovered at build
+ time. See also "depsformat".
+
+ Example: depfile = "{{output}}.d"
+
+ depsformat [string]
+ Valid for: compiler tools (when depfile is specified)
+
+ Format for the deps outputs. This is either "gcc" or "msvc". See the
+ ninja documentation for "deps" for more information.
+
+ Example: depsformat = "gcc"
+
+ description [string with substitutions, optional]
+ Valid for: all tools
+
+ What to print when the command is run.
+
+ Example: description = "Compiling {{source}}"
+
+ exe_output_extension [string, optional, rust tools only]
+ rlib_output_extension [string, optional, rust tools only]
+ dylib_output_extension [string, optional, rust tools only]
+ cdylib_output_extension [string, optional, rust tools only]
+ rust_proc_macro_output_extension [string, optional, rust tools only]
+ Valid for: Rust tools
+
+ These specify the default tool output for each of the crate types.
+ The default is empty for executables, shared, and static libraries and
+ ".rlib" for rlibs. Note that the Rust compiler complains with an error
+ if external crates do not take the form `lib<name>.rlib` or
+ `lib<name>.<shared_extension>`, where `<shared_extension>` is `.so`,
+ `.dylib`, or `.dll` as appropriate for the platform.
+
+ lib_switch [string, optional, link tools only]
+ lib_dir_switch [string, optional, link tools only]
+ Valid for: Linker tools except "alink"
+
+ These strings will be prepended to the libraries and library search
+ directories, respectively, because linkers differ on how to specify
+ them.
+
+ If you specified:
+ lib_switch = "-l"
+ lib_dir_switch = "-L"
+ then the "{{libs}}" expansion for
+ [ "freetype", "expat" ]
+ would be
+ "-lfreetype -lexpat".
+
+ framework_switch [string, optional, link tools only]
+ weak_framework_switch [string, optional, link tools only]
+ framework_dir_switch [string, optional, link tools only]
+ Valid for: Linker tools
+
+ These strings will be prepended to the frameworks and framework search
+ path directories, respectively, because linkers differ on how to specify
+ them.
+
+ If you specified:
+ framework_switch = "-framework "
+ weak_framework_switch = "-weak_framework "
+ framework_dir_switch = "-F"
+ and:
+ framework_dirs = [ "$root_out_dir" ]
+ frameworks = [ "UIKit.framework", "Foo.framework" ]
+ weak_frameworks = [ "MediaPlayer.framework" ]
+ would be:
+ "-F. -framework UIKit -framework Foo -weak_framework MediaPlayer"
+
+ swiftmodule_switch [string, optional, link tools only]
+ Valid for: Linker tools except "alink"
+
+ The string will be prependend to the path to the .swiftmodule files
+ that are embedded in the linker output.
+
+ If you specified:
+ swiftmodule_swift = "-Wl,-add_ast_path,"
+ then the "{{swiftmodules}}" expansion for
+ [ "obj/foo/Foo.swiftmodule" ]
+ would be
+ "-Wl,-add_ast_path,obj/foo/Foo.swiftmodule"
+
+ outputs [list of strings with substitutions]
+ Valid for: Linker and compiler tools (required)
+
+ An array of names for the output files the tool produces. These are
+ relative to the build output directory. There must always be at least
+ one output file. There can be more than one output (a linker might
+ produce a library and an import library, for example).
+
+ This array just declares to GN what files the tool will produce. It is
+ your responsibility to specify the tool command that actually produces
+ these files.
+
+ If you specify more than one output for shared library links, you
+ should consider setting link_output, depend_output, and
+ runtime_outputs.
+
+ Example for a compiler tool that produces .obj files:
+ outputs = [
+ "{{source_out_dir}}/{{source_name_part}}.obj"
+ ]
+
+ Example for a linker tool that produces a .dll and a .lib. The use of
+ {{target_output_name}}, {{output_extension}} and {{output_dir}} allows
+ the target to override these values.
+ outputs = [
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}",
+ "{{output_dir}}/{{target_output_name}}.lib",
+ ]
+
+ partial_outputs [list of strings with substitutions]
+ Valid for: "swift" only
+
+ An array of names for the partial outputs the tool produces. These
+ are relative to the build output directory. The expansion will be
+ evaluated for each file listed in the "sources" of the target.
+
+ This is used to deal with whole module optimization, allowing to
+ list one object file per source file when whole module optimization
+ is disabled.
+
+ pool [label, optional]
+ Valid for: all tools (optional)
+
+ Label of the pool to use for the tool. Pools are used to limit the
+ number of tasks that can execute concurrently during the build.
+
+ See also "gn help pool".
+
+ link_output [string with substitutions]
+ depend_output [string with substitutions]
+ Valid for: "solink" only (optional)
+
+ These two files specify which of the outputs from the solink tool
+ should be used for linking and dependency tracking. These should match
+ entries in the "outputs". If unspecified, the first item in the
+ "outputs" array will be used for all. See "Separate linking and
+ dependencies for shared libraries" below for more.
+
+ On Windows, where the tools produce a .dll shared library and a .lib
+ import library, you will want the first two to be the import library
+ and the third one to be the .dll file. On Linux, if you're not doing
+ the separate linking/dependency optimization, all of these should be
+ the .so output.
+
+ output_prefix [string]
+ Valid for: Linker tools (optional)
+
+ Prefix to use for the output name. Defaults to empty. This prefix will
+ be prepended to the name of the target (or the output_name if one is
+ manually specified for it) if the prefix is not already there. The
+ result will show up in the {{output_name}} substitution pattern.
+
+ Individual targets can opt-out of the output prefix by setting:
+ output_prefix_override = true
+ (see "gn help output_prefix_override").
+
+ This is typically used to prepend "lib" to libraries on
+ Posix systems:
+ output_prefix = "lib"
+
+ precompiled_header_type [string]
+ Valid for: "cc", "cxx", "objc", "objcxx"
+
+ Type of precompiled headers. If undefined or the empty string,
+ precompiled headers will not be used for this tool. Otherwise use "gcc"
+ or "msvc".
+
+ For precompiled headers to be used for a given target, the target (or a
+ config applied to it) must also specify a "precompiled_header" and, for
+ "msvc"-style headers, a "precompiled_source" value. If the type is
+ "gcc", then both "precompiled_header" and "precompiled_source" must
+ resolve to the same file, despite the different formats required for
+ each."
+
+ See "gn help precompiled_header" for more.
+
+ restat [boolean]
+ Valid for: all tools (optional, defaults to false)
+
+ Requests that Ninja check the file timestamp after this tool has run to
+ determine if anything changed. Set this if your tool has the ability to
+ skip writing output if the output file has not changed.
+
+ Normally, Ninja will assume that when a tool runs the output be new and
+ downstream dependents must be rebuild. When this is set to trye, Ninja
+ can skip rebuilding downstream dependents for input changes that don't
+ actually affect the output.
+
+ Example:
+ restat = true
+
+ rspfile [string with substitutions]
+ Valid for: all tools except "action" (optional)
+
+ Name of the response file. If empty, no response file will be
+ used. See "rspfile_content".
+
+ rspfile_content [string with substitutions]
+ Valid for: all tools except "action" (required when "rspfile" is used)
+
+ The contents to be written to the response file. This may include all
+ or part of the command to send to the tool which allows you to get
+ around OS command-line length limits.
+
+ This example adds the inputs and libraries to a response file, but
+ passes the linker flags directly on the command line:
+ tool("link") {
+ command = "link -o {{output}} {{ldflags}} @{{output}}.rsp"
+ rspfile = "{{output}}.rsp"
+ rspfile_content = "{{inputs}} {{solibs}} {{libs}} {{rlibs}}"
+ }
+
+ runtime_outputs [string list with substitutions]
+ Valid for: linker tools
+
+ If specified, this list is the subset of the outputs that should be
+ added to runtime deps (see "gn help runtime_deps"). By default (if
+ runtime_outputs is empty or unspecified), it will be the link_output.
+
+)" // String break to prevent overflowing the 16K max VC string length.
+ R"(Expansions for tool variables
+
+ All paths are relative to the root build directory, which is the current
+ directory for running all tools. These expansions are available to all tools:
+
+ {{label}}
+ The label of the current target. This is typically used in the
+ "description" field for link tools. The toolchain will be omitted from
+ the label for targets in the default toolchain, and will be included
+ for targets in other toolchains.
+
+ {{label_name}}
+ The short name of the label of the target. This is the part after the
+ colon. For "//foo/bar:baz" this will be "baz". Unlike
+ {{target_output_name}}, this is not affected by the "output_prefix" in
+ the tool or the "output_name" set on the target.
+
+ {{label_no_toolchain}}
+ The label of the current target, never including the toolchain
+ (otherwise, this is identical to {{label}}). This is used as the module
+ name when using .modulemap files.
+
+ {{output}}
+ The relative path and name of the output(s) of the current build step.
+ If there is more than one output, this will expand to a list of all of
+ them. Example: "out/base/my_file.o"
+
+ {{target_gen_dir}}
+ {{target_out_dir}}
+ The directory of the generated file and output directories,
+ respectively, for the current target. There is no trailing slash. See
+ also {{output_dir}} for linker tools. Example: "out/base/test"
+
+ {{target_output_name}}
+ The short name of the current target with no path information, or the
+ value of the "output_name" variable if one is specified in the target.
+ This will include the "output_prefix" if any. See also {{label_name}}.
+
+ Example: "libfoo" for the target named "foo" and an output prefix for
+ the linker tool of "lib".
+
+ Compiler tools have the notion of a single input and a single output, along
+ with a set of compiler-specific flags. The following expansions are
+ available:
+
+ {{asmflags}}
+ {{cflags}}
+ {{cflags_c}}
+ {{cflags_cc}}
+ {{cflags_objc}}
+ {{cflags_objcc}}
+ {{defines}}
+ {{include_dirs}}
+ Strings correspond that to the processed flags/defines/include
+ directories specified for the target.
+ Example: "--enable-foo --enable-bar"
+
+ Defines will be prefixed by "-D" and include directories will be
+ prefixed by "-I" (these work with Posix tools as well as Microsoft
+ ones).
+
+ {{module_deps}}
+ {{module_deps_no_self}}
+ Strings that correspond to the flags necessary to depend upon the Clang
+ modules referenced by the current target. The "_no_self" version doesn't
+ include the module for the current target, and can be used to compile
+ the pcm itself.
+
+ {{source}}
+ The relative path and name of the current input file.
+ Example: "../../base/my_file.cc"
+
+ {{source_file_part}}
+ The file part of the source including the extension (with no directory
+ information).
+ Example: "foo.cc"
+
+ {{source_name_part}}
+ The filename part of the source file with no directory or extension.
+ Example: "foo"
+
+ {{source_gen_dir}}
+ {{source_out_dir}}
+ The directory in the generated file and output directories,
+ respectively, for the current input file. If the source file is in the
+ same directory as the target is declared in, they will will be the same
+ as the "target" versions above. Example: "gen/base/test"
+
+ Linker tools have multiple inputs and (potentially) multiple outputs. The
+ static library tool ("alink") is not considered a linker tool. The following
+ expansions are available:
+
+ {{inputs}}
+ {{inputs_newline}}
+ Expands to the inputs to the link step. This will be a list of object
+ files and static libraries.
+ Example: "obj/foo.o obj/bar.o obj/somelibrary.a"
+
+ The "_newline" version will separate the input files with newlines
+ instead of spaces. This is useful in response files: some linkers can
+ take a "-filelist" flag which expects newline separated files, and some
+ Microsoft tools have a fixed-sized buffer for parsing each line of a
+ response file.
+
+ {{ldflags}}
+ Expands to the processed set of ldflags and library search paths
+ specified for the target.
+ Example: "-m64 -fPIC -pthread -L/usr/local/mylib"
+
+ {{libs}}
+ Expands to the list of system libraries to link to. Each will be
+ prefixed by the "lib_switch".
+
+ Example: "-lfoo -lbar"
+
+ {{output_dir}}
+ The value of the "output_dir" variable in the target, or the the value
+ of the "default_output_dir" value in the tool if the target does not
+ override the output directory. This will be relative to the
+ root_build_dir and will not end in a slash. Will be "." for output to
+ the root_build_dir.
+
+ This is subtly different than {{target_out_dir}} which is defined by GN
+ based on the target's path and not overridable. {{output_dir}} is for
+ the final output, {{target_out_dir}} is generally for object files and
+ other outputs.
+
+ Usually {{output_dir}} would be defined in terms of either
+ {{target_out_dir}} or {{root_out_dir}}
+
+ {{output_extension}}
+ The value of the "output_extension" variable in the target, or the
+ value of the "default_output_extension" value in the tool if the target
+ does not specify an output extension.
+ Example: ".so"
+
+ {{solibs}}
+ Extra libraries from shared library dependencies not specified in the
+ {{inputs}}. This is the list of link_output files from shared libraries
+ (if the solink tool specifies a "link_output" variable separate from
+ the "depend_output").
+
+ These should generally be treated the same as libs by your tool.
+
+ Example: "libfoo.so libbar.so"
+
+ {{rlibs}}
+ Any Rust .rlibs which need to be linked into a final C++ target.
+ These should be treated as {{inputs}} except that sometimes
+ they might have different linker directives applied.
+
+ Example: "obj/foo/libfoo.rlib"
+
+ {{frameworks}}
+ Shared libraries packaged as framework bundle. This is principally
+ used on Apple's platforms (macOS and iOS). All name must be ending
+ with ".framework" suffix; the suffix will be stripped when expanding
+ {{frameworks}} and each item will be preceded by "-framework" or
+ "-weak_framework".
+
+ {{swiftmodules}}
+ Swift .swiftmodule files that needs to be embedded into the binary.
+ This is necessary to correctly link with object generated by the
+ Swift compiler (the .swiftmodule file cannot be embedded in object
+ files directly). Those will be prefixed with "swiftmodule_switch"
+ value.
+
+)" // String break to prevent overflowing the 16K max VC string length.
+ R"( The static library ("alink") tool allows {{arflags}} plus the common tool
+ substitutions.
+
+ The copy tool allows the common compiler/linker substitutions, plus
+ {{source}} which is the source of the copy. The stamp tool allows only the
+ common tool substitutions.
+
+ The copy_bundle_data and compile_xcassets tools only allows the common tool
+ substitutions. Both tools are required to create iOS/macOS bundles and need
+ only be defined on those platforms.
+
+ The copy_bundle_data tool will be called with one source and needs to copy
+ (optionally optimizing the data representation) to its output. It may be
+ called with a directory as input and it needs to be recursively copied.
+
+ The compile_xcassets tool will be called with one or more source (each an
+ asset catalog) that needs to be compiled to a single output. The following
+ substitutions are available:
+
+ {{inputs}}
+ Expands to the list of .xcassets to use as input to compile the asset
+ catalog.
+
+ {{bundle_product_type}}
+ Expands to the product_type of the bundle that will contain the
+ compiled asset catalog. Usually corresponds to the product_type
+ property of the corresponding create_bundle target.
+
+ {{bundle_partial_info_plist}}
+ Expands to the path to the partial Info.plist generated by the
+ assets catalog compiler. Usually based on the target_name of
+ the create_bundle target.
+
+ {{xcasset_compiler_flags}}
+ Expands to the list of flags specified in corresponding
+ create_bundle target.
+
+ The Swift tool has multiple input and outputs. It must have exactly one
+ output of .swiftmodule type, but can have one or more object file outputs,
+ in addition to other type of outputs. The following expansions are available:
+
+ {{module_name}}
+ Expands to the string representing the module name of target under
+ compilation (see "module_name" variable).
+
+ {{module_dirs}}
+ Expands to the list of -I<path> for the target Swift module search
+ path computed from target dependencies.
+
+ {{swiftflags}}
+ Expands to the list of strings representing Swift compiler flags.
+
+ Rust tools have the notion of a single input and a single output, along
+ with a set of compiler-specific flags. The following expansions are
+ available:
+
+ {{crate_name}}
+ Expands to the string representing the crate name of target under
+ compilation.
+
+ {{crate_type}}
+ Expands to the string representing the type of crate for the target
+ under compilation.
+
+ {{externs}}
+ Expands to the list of --extern flags needed to include addition Rust
+ libraries in this target. Includes any specified renamed dependencies.
+
+ {{rustdeps}}
+ Expands to the list of -Ldependency=<path> strings needed to compile
+ this target.
+
+ {{rustenv}}
+ Expands to the list of environment variables.
+
+ {{rustflags}}
+ Expands to the list of strings representing Rust compiler flags.
+
+Separate linking and dependencies for shared libraries
+
+ Shared libraries are special in that not all changes to them require that
+ dependent targets be re-linked. If the shared library is changed but no
+ imports or exports are different, dependent code needn't be relinked, which
+ can speed up the build.
+
+ If your link step can output a list of exports from a shared library and
+ writes the file only if the new one is different, the timestamp of this file
+ can be used for triggering re-links, while the actual shared library would be
+ used for linking.
+
+ You will need to specify
+ restat = true
+ in the linker tool to make this work, so Ninja will detect if the timestamp
+ of the dependency file has changed after linking (otherwise it will always
+ assume that running a command updates the output):
+
+ tool("solink") {
+ command = "..."
+ outputs = [
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}",
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}.TOC",
+ ]
+ link_output =
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+ depend_output =
+ "{{output_dir}}/{{target_output_name}}{{output_extension}}.TOC"
+ restat = true
+ }
+
+Example
+
+ toolchain("my_toolchain") {
+ # Put these at the top to apply to all tools below.
+ lib_switch = "-l"
+ lib_dir_switch = "-L"
+
+ tool("cc") {
+ command = "gcc {{source}} -o {{output}}"
+ outputs = [ "{{source_out_dir}}/{{source_name_part}}.o" ]
+ description = "GCC {{source}}"
+ }
+ tool("cxx") {
+ command = "g++ {{source}} -o {{output}}"
+ outputs = [ "{{source_out_dir}}/{{source_name_part}}.o" ]
+ description = "G++ {{source}}"
+ }
+ };
+)";
+
+Value RunTool(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ // Find the toolchain definition we're executing inside of. The toolchain
+ // function will set a property pointing to it that we'll pick up.
+ Toolchain* toolchain = reinterpret_cast<Toolchain*>(
+ scope->GetProperty(&kToolchainPropertyKey, nullptr));
+ if (!toolchain) {
+ *err = Err(function->function(), "tool() called outside of toolchain().",
+ "The tool() function can only be used inside a toolchain() "
+ "definition.");
+ return Value();
+ }
+
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ const std::string& tool_name = args[0].string_value();
+
+ // Run the tool block.
+ Scope block_scope(scope);
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ std::unique_ptr<Tool> tool =
+ Tool::CreateTool(function, tool_name, &block_scope, toolchain, err);
+
+ if (!tool) {
+ return Value();
+ }
+
+ tool->set_defined_from(function);
+ toolchain->SetTool(std::move(tool));
+
+ // Make sure there weren't any vars set in this tool that were unused.
+ if (!block_scope.CheckForUnusedVars(err))
+ return Value();
+
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_toolchain_unittest.cc b/gn/src/gn/function_toolchain_unittest.cc
new file mode 100644
index 00000000000..74cbef9f42a
--- /dev/null
+++ b/gn/src/gn/function_toolchain_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright 2016 The Chromium 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 "gn/functions.h"
+#include "gn/rust_tool.h"
+#include "gn/scheduler.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using FunctionToolchain = TestWithScheduler;
+
+TEST_F(FunctionToolchain, NoArguments) {
+ TestWithScope setup;
+
+ // Check that creating a toolchain with no name reports an error.
+ {
+ TestParseInput input(R"(toolchain() {})");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+ }
+
+ // Check that creating a toolchain with too many arguments is an error.
+ {
+ TestParseInput input(R"(toolchain("too", "many", "arguments") {})");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+ }
+}
+
+TEST_F(FunctionToolchain, RuntimeOutputs) {
+ TestWithScope setup;
+
+ // These runtime outputs are a subset of the outputs so are OK.
+ {
+ TestParseInput input(
+ R"(toolchain("good") {
+ tool("link") {
+ command = "link"
+ outputs = [ "foo" ]
+ runtime_outputs = [ "foo" ]
+ }
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // It should have generated a toolchain.
+ ASSERT_EQ(1u, setup.items().size());
+ const Toolchain* toolchain = setup.items()[0]->AsToolchain();
+ ASSERT_TRUE(toolchain);
+
+ // The toolchain should have a link tool with the two outputs.
+ const Tool* link = toolchain->GetTool(CTool::kCToolLink);
+ ASSERT_TRUE(link);
+ ASSERT_EQ(1u, link->outputs().list().size());
+ EXPECT_EQ("foo", link->outputs().list()[0].AsString());
+ ASSERT_EQ(1u, link->runtime_outputs().list().size());
+ EXPECT_EQ("foo", link->runtime_outputs().list()[0].AsString());
+ }
+
+ // This one is not a subset so should throw an error.
+ {
+ TestParseInput input(
+ R"(toolchain("bad") {
+ tool("link") {
+ outputs = [ "foo" ]
+ runtime_outputs = [ "bar" ]
+ }
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+ }
+}
+
+TEST_F(FunctionToolchain, Rust) {
+ TestWithScope setup;
+
+ // These runtime outputs are a subset of the outputs so are OK.
+ {
+ TestParseInput input(
+ R"(toolchain("rust") {
+ tool("rust_bin") {
+ command = "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin {{rustflags}} -o {{output}} {{externs}} {{source}}"
+ description = "RUST {{output}}"
+ }
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // It should have generated a toolchain.
+ ASSERT_EQ(1u, setup.items().size());
+ const Toolchain* toolchain = setup.items()[0]->AsToolchain();
+ ASSERT_TRUE(toolchain);
+
+ const Tool* rust = toolchain->GetTool(RustTool::kRsToolBin);
+ ASSERT_TRUE(rust);
+ ASSERT_EQ(rust->command().AsString(),
+ "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin "
+ "{{rustflags}} -o {{output}} {{externs}} {{source}}");
+ ASSERT_EQ(rust->description().AsString(), "RUST {{output}}");
+ }
+}
+
+TEST_F(FunctionToolchain, Command) {
+ TestWithScope setup;
+
+ TestParseInput input(
+ R"(toolchain("missing_command") {
+ tool("cxx") {}
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+}
+
+TEST_F(FunctionToolchain, CommandLauncher) {
+ TestWithScope setup;
+
+ TestParseInput input(
+ R"(toolchain("good") {
+ tool("cxx") {
+ command = "cxx"
+ command_launcher = "/usr/goma/gomacc"
+ }
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // It should have generated a toolchain.
+ ASSERT_EQ(1u, setup.items().size());
+ const Toolchain* toolchain = setup.items()[0]->AsToolchain();
+ ASSERT_TRUE(toolchain);
+
+ // The toolchain should have a link tool with the two outputs.
+ const Tool* link = toolchain->GetTool(CTool::kCToolCxx);
+ ASSERT_TRUE(link);
+ EXPECT_EQ("/usr/goma/gomacc", link->command_launcher());
+}
diff --git a/gn/src/gn/function_write_file.cc b/gn/src/gn/function_write_file.cc
new file mode 100644
index 00000000000..2c568c0ff73
--- /dev/null
+++ b/gn/src/gn/function_write_file.cc
@@ -0,0 +1,106 @@
+// 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 <sstream>
+
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/input_file.h"
+#include "gn/output_conversion.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "util/build_config.h"
+
+namespace functions {
+
+const char kWriteFile[] = "write_file";
+const char kWriteFile_HelpShort[] = "write_file: Write a file to disk.";
+const char kWriteFile_Help[] =
+ R"(write_file: Write a file to disk.
+
+ write_file(filename, data, output_conversion = "")
+
+ If data is a list, the list will be written one-item-per-line with no quoting
+ or brackets.
+
+ If the file exists and the contents are identical to that being written, the
+ file will not be updated. This will prevent unnecessary rebuilds of targets
+ that depend on this file.
+
+ One use for write_file is to write a list of inputs to an script that might
+ be too long for the command line. However, it is preferable to use response
+ files for this purpose. See "gn help response_file_contents".
+
+Arguments
+
+ filename
+ Filename to write. This must be within the output directory.
+
+ data
+ The list or string to write.
+
+ output_conversion
+ Controls how the output is written. See "gn help io_conversion".
+)";
+
+Value RunWriteFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 3 && args.size() != 2) {
+ *err = Err(function->function(), "Wrong number of arguments to write_file",
+ "I expected two or three arguments.");
+ return Value();
+ }
+
+ // Compute the file name and make sure it's in the output dir.
+ const SourceDir& cur_dir = scope->GetSourceDir();
+ SourceFile source_file = cur_dir.ResolveRelativeFile(
+ args[0], err, scope->settings()->build_settings()->root_path_utf8());
+ if (err->has_error())
+ return Value();
+ if (!EnsureStringIsInOutputDir(
+ scope->settings()->build_settings()->build_dir(), source_file.value(),
+ args[0].origin(), err))
+ return Value();
+ g_scheduler->AddWrittenFile(source_file); // Track that we wrote this file.
+
+ // Track how to recreate this file, since we write it a gen time.
+ // Note this is a hack since the correct output is not a dependency proper,
+ // but an addition of this file to the output of the gn rule that writes it.
+ // This dependency will, however, cause the gen step to be re-run and the
+ // build restarted if the file is missing.
+ g_scheduler->AddGenDependency(
+ scope->settings()->build_settings()->GetFullPath(source_file));
+
+ // Extract conversion value.
+ Value output_conversion;
+ if (args.size() != 3)
+ output_conversion = Value();
+ else
+ output_conversion = args[2];
+
+ // Compute output.
+ std::ostringstream contents;
+ ConvertValueToOutput(scope->settings(), args[1], output_conversion, contents,
+ err);
+ if (err->has_error())
+ return Value();
+
+ base::FilePath file_path =
+ scope->settings()->build_settings()->GetFullPath(source_file);
+
+ // Make sure we're not replacing the same contents.
+ if (!WriteFileIfChanged(file_path, contents.str(), err))
+ *err = Err(function->function(), err->message(), err->help_text());
+
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/src/gn/function_write_file_unittest.cc b/gn/src/gn/function_write_file_unittest.cc
new file mode 100644
index 00000000000..5ee61239aa4
--- /dev/null
+++ b/gn/src/gn/function_write_file_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 <stdint.h>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "gn/functions.h"
+#include "gn/scheduler.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_HAIKU) || defined(OS_MSYS)
+#include <sys/time.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+// Returns true on success, false if write_file signaled an error.
+bool CallWriteFile(Scope* scope,
+ const std::string& filename,
+ const Value& data) {
+ Err err;
+
+ std::vector<Value> args;
+ args.push_back(Value(nullptr, filename));
+ args.push_back(data);
+
+ FunctionCallNode function_call;
+ Value result = functions::RunWriteFile(scope, &function_call, args, &err);
+ EXPECT_EQ(Value::NONE, result.type()); // Should always return none.
+
+ return !err.has_error();
+}
+
+} // namespace
+
+using WriteFileTest = TestWithScheduler;
+
+TEST_F(WriteFileTest, WithData) {
+ TestWithScope setup;
+
+ // Make a real directory for writing the files.
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ setup.build_settings()->SetRootPath(temp_dir.GetPath());
+ setup.build_settings()->SetBuildDir(SourceDir("//out/"));
+
+ Value some_string(nullptr, "some string contents");
+
+ // Should refuse to write files outside of the output dir.
+ EXPECT_FALSE(CallWriteFile(setup.scope(), "//in_root.txt", some_string));
+ EXPECT_FALSE(
+ CallWriteFile(setup.scope(), "//other_dir/foo.txt", some_string));
+
+ // Should be able to write to a new dir inside the out dir.
+ EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_string));
+ base::FilePath foo_name = temp_dir.GetPath()
+ .Append(FILE_PATH_LITERAL("out"))
+ .Append(FILE_PATH_LITERAL("foo.txt"));
+ std::string result_contents;
+ EXPECT_TRUE(base::ReadFileToString(foo_name, &result_contents));
+ EXPECT_EQ(some_string.string_value(), result_contents);
+
+ // Update the contents with a list of a string and a number.
+ Value some_list(nullptr, Value::LIST);
+ some_list.list_value().push_back(Value(nullptr, "line 1"));
+ some_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(2)));
+ EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_list));
+ EXPECT_TRUE(base::ReadFileToString(foo_name, &result_contents));
+ EXPECT_EQ("line 1\n2\n", result_contents);
+
+ // Test that the file is not rewritten if the contents are not changed.
+ base::File foo_file(foo_name, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
+ ASSERT_TRUE(foo_file.IsValid());
+
+ // Start by setting the modified time to something old to avoid clock
+ // resolution issues.
+#if defined(OS_WIN)
+ FILETIME last_access_filetime = {};
+ FILETIME last_modified_filetime = {};
+ ASSERT_TRUE(::SetFileTime(foo_file.GetPlatformFile(), nullptr,
+ &last_access_filetime, &last_modified_filetime));
+#elif defined(OS_AIX) || defined(OS_HAIKU) || defined(OS_SOLARIS)
+ struct timeval times[2] = {};
+ ASSERT_EQ(utimes(foo_name.value().c_str(), times), 0);
+#else
+ struct timeval times[2] = {};
+ ASSERT_EQ(futimes(foo_file.GetPlatformFile(), times), 0);
+#endif
+
+ // Read the current time to avoid timer resolution issues when comparing
+ // below.
+ base::File::Info original_info;
+ foo_file.GetInfo(&original_info);
+
+ EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_list));
+
+ // Verify that the last modified time is the same as before.
+ base::File::Info new_info;
+ foo_file.GetInfo(&new_info);
+ EXPECT_EQ(original_info.last_modified, new_info.last_modified);
+}
diff --git a/gn/src/gn/functions.cc b/gn/src/gn/functions.cc
new file mode 100644
index 00000000000..6acf41ae43c
--- /dev/null
+++ b/gn/src/gn/functions.cc
@@ -0,0 +1,1498 @@
+// 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 "gn/functions.h"
+
+#include <stddef.h>
+#include <cctype>
+#include <memory>
+#include <utility>
+
+#include "base/environment.h"
+#include "base/strings/string_util.h"
+#include "gn/build_settings.h"
+#include "gn/config.h"
+#include "gn/config_values_generator.h"
+#include "gn/err.h"
+#include "gn/input_file.h"
+#include "gn/parse_node_value_adapter.h"
+#include "gn/parse_tree.h"
+#include "gn/pool.h"
+#include "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/template.h"
+#include "gn/token.h"
+#include "gn/value.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+namespace {
+
+// Some functions take a {} following them, and some don't. For the ones that
+// don't, this is used to verify that the given block node is null and will
+// set the error accordingly if it's not. Returns true if the block is null.
+bool VerifyNoBlockForFunctionCall(const FunctionCallNode* function,
+ const BlockNode* block,
+ Err* err) {
+ if (!block)
+ return true;
+
+ *err =
+ Err(block, "Unexpected '{'.",
+ "This function call doesn't take a {} block following it, and you\n"
+ "can't have a {} block that's not connected to something like an if\n"
+ "statement or a target declaration.");
+ err->AppendRange(function->function().range());
+ return false;
+}
+
+// This key is set as a scope property on the scope of a declare_args() block,
+// in order to prevent reading a variable defined earlier in the same call
+// (see `gn help declare_args` for more).
+const void* kInDeclareArgsKey = nullptr;
+
+} // namespace
+
+bool EnsureNotReadingFromSameDeclareArgs(const ParseNode* node,
+ const Scope* cur_scope,
+ const Scope* val_scope,
+ Err* err) {
+ // If the value didn't come from a scope at all, we're safe.
+ if (!val_scope)
+ return true;
+
+ const Scope* val_args_scope = nullptr;
+ val_scope->GetProperty(&kInDeclareArgsKey, &val_args_scope);
+
+ const Scope* cur_args_scope = nullptr;
+ cur_scope->GetProperty(&kInDeclareArgsKey, &cur_args_scope);
+ if (!val_args_scope || !cur_args_scope || (val_args_scope != cur_args_scope))
+ return true;
+
+ *err =
+ Err(node,
+ "Reading a variable defined in the same declare_args() call.\n"
+ "\n"
+ "If you need to set the value of one arg based on another, put\n"
+ "them in two separate declare_args() calls, one after the other.\n");
+ return false;
+}
+
+bool EnsureNotProcessingImport(const ParseNode* node,
+ const Scope* scope,
+ Err* err) {
+ if (scope->IsProcessingImport()) {
+ *err =
+ Err(node, "Not valid from an import.",
+ "Imports are for defining defaults, variables, and rules. The\n"
+ "appropriate place for this kind of thing is really in a normal\n"
+ "BUILD file.");
+ return false;
+ }
+ return true;
+}
+
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+ const Scope* scope,
+ Err* err) {
+ if (scope->IsProcessingBuildConfig()) {
+ *err = Err(node, "Not valid from the build config.",
+ "You can't do this kind of thing from the build config script, "
+ "silly!\nPut it in a regular BUILD file.");
+ return false;
+ }
+ return true;
+}
+
+bool FillTargetBlockScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& target_type,
+ const BlockNode* block,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err) {
+ if (!block) {
+ FillNeedsBlockError(function, err);
+ return false;
+ }
+
+ // Copy the target defaults, if any, into the scope we're going to execute
+ // the block in.
+ const Scope* default_scope = scope->GetTargetDefaults(target_type);
+ if (default_scope) {
+ Scope::MergeOptions merge_options;
+ merge_options.skip_private_vars = true;
+ if (!default_scope->NonRecursiveMergeTo(block_scope, merge_options,
+ function, "target defaults", err))
+ return false;
+ }
+
+ // The name is the single argument to the target function.
+ if (!EnsureSingleStringArg(function, args, err))
+ return false;
+
+ // Set the target name variable to the current target, and mark it used
+ // because we don't want to issue an error if the script ignores it.
+ const std::string_view target_name(variables::kTargetName);
+ block_scope->SetValue(target_name, Value(function, args[0].string_value()),
+ function);
+ block_scope->MarkUsed(target_name);
+ return true;
+}
+
+void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
+ *err = Err(function->function(), "This function call requires a block.",
+ "The block's \"{\" must be on the same line as the function "
+ "call's \")\".");
+}
+
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function->function(), "Incorrect arguments.",
+ "This function requires a single string argument.");
+ return false;
+ }
+ return args[0].VerifyTypeIs(Value::STRING, err);
+}
+
+const Label& ToolchainLabelForScope(const Scope* scope) {
+ return scope->settings()->toolchain_label();
+}
+
+Label MakeLabelForScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& name) {
+ const Label& toolchain_label = ToolchainLabelForScope(scope);
+ return Label(scope->GetSourceDir(), name, toolchain_label.dir(),
+ toolchain_label.name());
+}
+
+// static
+const int NonNestableBlock::kKey = 0;
+
+NonNestableBlock::NonNestableBlock(Scope* scope,
+ const FunctionCallNode* function,
+ const char* type_description)
+ : scope_(scope),
+ function_(function),
+ type_description_(type_description),
+ key_added_(false) {}
+
+NonNestableBlock::~NonNestableBlock() {
+ if (key_added_)
+ scope_->SetProperty(&kKey, nullptr);
+}
+
+bool NonNestableBlock::Enter(Err* err) {
+ void* scope_value = scope_->GetProperty(&kKey, nullptr);
+ if (scope_value) {
+ // Existing block.
+ const NonNestableBlock* existing =
+ reinterpret_cast<const NonNestableBlock*>(scope_value);
+ *err = Err(function_, "Can't nest these things.",
+ std::string("You are trying to nest a ") + type_description_ +
+ " inside a " + existing->type_description_ + ".");
+ err->AppendSubErr(Err(existing->function_, "The enclosing block."));
+ return false;
+ }
+
+ scope_->SetProperty(&kKey, this);
+ key_added_ = true;
+ return true;
+}
+
+namespace functions {
+
+// assert ----------------------------------------------------------------------
+
+const char kAssert[] = "assert";
+const char kAssert_HelpShort[] =
+ "assert: Assert an expression is true at generation time.";
+const char kAssert_Help[] =
+ R"(assert: Assert an expression is true at generation time.
+
+ assert(<condition> [, <error string>])
+
+ If the condition is false, the build will fail with an error. If the
+ optional second argument is provided, that string will be printed
+ with the error message.
+
+Examples
+
+ assert(is_win)
+ assert(defined(sources), "Sources must be defined");
+)";
+
+Value RunAssert(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ // Check usage: Assert takes 1 or 2 arguments.
+ if (args.size() != 1 && args.size() != 2) {
+ *err = Err(function->function(), "Wrong number of arguments.",
+ "assert() takes one or two arguments, "
+ "were you expecting something else?");
+ return Value();
+ }
+
+ // Check usage: The first argument must be a boolean.
+ if (args[0].type() != Value::BOOLEAN) {
+ *err = Err(function->function(), "Assertion value not a bool.");
+ return Value();
+ }
+ bool assertion_passed = args[0].boolean_value();
+
+ // Check usage: The second argument, if present, must be a string.
+ if (args.size() == 2 && args[1].type() != Value::STRING) {
+ *err = Err(function->function(), "Assertion message is not a string.");
+ return Value();
+ }
+
+ // Assertion passed: there is nothing to do, so return an empty value.
+ if (assertion_passed) {
+ return Value();
+ }
+
+ // Assertion failed; try to make a useful message and report it.
+ if (args.size() == 2) {
+ *err =
+ Err(function->function(), "Assertion failed.", args[1].string_value());
+ } else {
+ *err = Err(function->function(), "Assertion failed.");
+ }
+ if (args[0].origin()) {
+ // If you do "assert(foo)" we'd ideally like to show you where foo was
+ // set, and in this case the origin of the args will tell us that.
+ // However, if you do "assert(foo && bar)" the source of the value will
+ // be the assert like, which isn't so helpful.
+ //
+ // So we try to see if the args are from the same line or not. This will
+ // break if you do "assert(\nfoo && bar)" and we may show the second line
+ // as the source, oh well. The way around this is to check to see if the
+ // origin node is inside our function call block.
+ Location origin_location = args[0].origin()->GetRange().begin();
+ if (origin_location.file() != function->function().location().file() ||
+ origin_location.line_number() !=
+ function->function().location().line_number()) {
+ err->AppendSubErr(
+ Err(args[0].origin()->GetRange(), "", "This is where it was set."));
+ }
+ }
+ return Value();
+}
+
+// config ----------------------------------------------------------------------
+
+const char kConfig[] = "config";
+const char kConfig_HelpShort[] = "config: Defines a configuration object.";
+const char kConfig_Help[] =
+ R"(config: Defines a configuration object.
+
+ Configuration objects can be applied to targets and specify sets of compiler
+ flags, includes, defines, etc. They provide a way to conveniently group sets
+ of this configuration information.
+
+ A config is referenced by its label just like a target.
+
+ The values in a config are additive only. If you want to remove a flag you
+ need to remove the corresponding config that sets it. The final set of flags,
+ defines, etc. for a target is generated in this order:
+
+ 1. The values specified directly on the target (rather than using a config.
+ 2. The configs specified in the target's "configs" list, in order.
+ 3. Public_configs from a breadth-first traversal of the dependency tree in
+ the order that the targets appear in "deps".
+ 4. All dependent configs from a breadth-first traversal of the dependency
+ tree in the order that the targets appear in "deps".
+
+More background
+
+ Configs solve a problem where the build system needs to have a higher-level
+ understanding of various compiler settings. For example, some compiler flags
+ have to appear in a certain order relative to each other, some settings like
+ defines and flags logically go together, and the build system needs to
+ de-duplicate flags even though raw command-line parameters can't always be
+ operated on in that way.
+
+ The config gives a name to a group of settings that can then be reasoned
+ about by GN. GN can know that configs with the same label are the same thing
+ so can be de-duplicated. It allows related settings to be grouped so they
+ are added or removed as a unit. And it allows targets to refer to settings
+ with conceptual names ("no_rtti", "enable_exceptions", etc.) rather than
+ having to hard-coding every compiler's flags each time they are referred to.
+
+Variables valid in a config definition
+
+)"
+
+ CONFIG_VALUES_VARS_HELP
+
+ R"( Nested configs: configs
+
+Variables on a target used to apply configs
+
+ all_dependent_configs, configs, public_configs
+
+Example
+
+ config("myconfig") {
+ include_dirs = [ "include/common" ]
+ defines = [ "ENABLE_DOOM_MELON" ]
+ }
+
+ executable("mything") {
+ configs = [ ":myconfig" ]
+ }
+)";
+
+Value RunConfig(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* scope,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "config");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
+
+ // Create the new config.
+ std::unique_ptr<Config> config = std::make_unique<Config>(
+ scope->settings(), label, scope->build_dependency_files());
+ config->set_defined_from(function);
+ if (!Visibility::FillItemVisibility(config.get(), scope, err))
+ return Value();
+
+ // Fill the flags and such.
+ const SourceDir& input_dir = scope->GetSourceDir();
+ ConfigValuesGenerator gen(&config->own_values(), scope, input_dir, err);
+ gen.Run();
+ if (err->has_error())
+ return Value();
+
+ // Read sub-configs.
+ const Value* configs_value = scope->GetValue(variables::kConfigs, true);
+ if (configs_value) {
+ ExtractListOfUniqueLabels(scope->settings()->build_settings(),
+ *configs_value, scope->GetSourceDir(),
+ ToolchainLabelForScope(scope), &config->configs(),
+ err);
+ }
+ if (err->has_error())
+ return Value();
+
+ // Save the generated item.
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function, "Can't define a config in this context.");
+ return Value();
+ }
+ collector->push_back(std::move(config));
+
+ return Value();
+}
+
+// declare_args ----------------------------------------------------------------
+
+const char kDeclareArgs[] = "declare_args";
+const char kDeclareArgs_HelpShort[] = "declare_args: Declare build arguments.";
+const char kDeclareArgs_Help[] =
+ R"(declare_args: Declare build arguments.
+
+ Introduces the given arguments into the current scope. If they are not
+ specified on the command line or in a toolchain's arguments, the default
+ values given in the declare_args block will be used. However, these defaults
+ will not override command-line values.
+
+ See also "gn help buildargs" for an overview.
+
+ The precise behavior of declare args is:
+
+ 1. The declare_args() block executes. Any variable defined in the enclosing
+ scope is available for reading, but any variable defined earlier in
+ the current scope is not (since the overrides haven't been applied yet).
+
+ 2. At the end of executing the block, any variables set within that scope
+ are saved, with the values specified in the block used as the "default value"
+ for that argument. Once saved, these variables are available for override
+ via args.gn.
+
+ 3. User-defined overrides are applied. Anything set in "gn args" now
+ overrides any default values. The resulting set of variables is promoted
+ to be readable from the following code in the file.
+
+ This has some ramifications that may not be obvious:
+
+ - You should not perform difficult work inside a declare_args block since
+ this only sets a default value that may be discarded. In particular,
+ don't use the result of exec_script() to set the default value. If you
+ want to have a script-defined default, set some default "undefined" value
+ like [], "", or -1, and after the declare_args block, call exec_script if
+ the value is unset by the user.
+
+ - Because you cannot read the value of a variable defined in the same
+ block, if you need to make the default value of one arg depend
+ on the possibly-overridden value of another, write two separate
+ declare_args() blocks:
+
+ declare_args() {
+ enable_foo = true
+ }
+ declare_args() {
+ # Bar defaults to same user-overridden state as foo.
+ enable_bar = enable_foo
+ }
+
+Example
+
+ declare_args() {
+ enable_teleporter = true
+ enable_doom_melon = false
+ }
+
+ If you want to override the (default disabled) Doom Melon:
+ gn --args="enable_doom_melon=true enable_teleporter=true"
+ This also sets the teleporter, but it's already defaulted to on so it will
+ have no effect.
+)";
+
+Value RunDeclareArgs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "declare_args");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ Scope block_scope(scope);
+ block_scope.SetProperty(&kInDeclareArgsKey, &block_scope);
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Pass the values from our scope into the Args object for adding to the
+ // scope with the proper values (taking into account the defaults given in
+ // the block_scope, and arguments passed into the build).
+ Scope::KeyValueMap values;
+ block_scope.GetCurrentScopeValues(&values);
+ scope->settings()->build_settings()->build_args().DeclareArgs(values, scope,
+ err);
+ return Value();
+}
+
+// defined ---------------------------------------------------------------------
+
+const char kDefined[] = "defined";
+const char kDefined_HelpShort[] =
+ "defined: Returns whether an identifier is defined.";
+const char kDefined_Help[] =
+ R"(defined: Returns whether an identifier is defined.
+
+ Returns true if the given argument is defined. This is most useful in
+ templates to assert that the caller set things up properly.
+
+ You can pass an identifier:
+ defined(foo)
+ which will return true or false depending on whether foo is defined in the
+ current scope.
+
+ You can also check a named scope:
+ defined(foo.bar)
+ which will return true or false depending on whether bar is defined in the
+ named scope foo. It will throw an error if foo is not defined or is not a
+ scope.
+
+Example
+
+ template("mytemplate") {
+ # To help users call this template properly...
+ assert(defined(invoker.sources), "Sources must be defined")
+
+ # If we want to accept an optional "values" argument, we don't
+ # want to dereference something that may not be defined.
+ if (defined(invoker.values)) {
+ values = invoker.values
+ } else {
+ values = "some default value"
+ }
+ }
+)";
+
+Value RunDefined(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() != 1) {
+ *err = Err(function, "Wrong number of arguments to defined().",
+ "Expecting exactly one.");
+ return Value();
+ }
+
+ const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
+ if (identifier) {
+ // Passed an identifier "defined(foo)".
+ if (scope->GetValue(identifier->value().value()))
+ return Value(function, true);
+ return Value(function, false);
+ }
+
+ const AccessorNode* accessor = args_vector[0]->AsAccessor();
+ if (accessor) {
+ // Passed an accessor "defined(foo.bar)".
+ if (accessor->member()) {
+ // The base of the accessor must be a scope if it's defined.
+ const Value* base = scope->GetValue(accessor->base().value());
+ if (!base) {
+ *err = Err(accessor, "Undefined identifier");
+ return Value();
+ }
+ if (!base->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+
+ // Check the member inside the scope to see if its defined.
+ if (base->scope_value()->GetValue(accessor->member()->value().value()))
+ return Value(function, true);
+ return Value(function, false);
+ }
+ }
+
+ // Argument is invalid.
+ *err = Err(function, "Bad thing passed to defined().",
+ "It should be of the form defined(foo) or defined(foo.bar).");
+ return Value();
+}
+
+// getenv ----------------------------------------------------------------------
+
+const char kGetEnv[] = "getenv";
+const char kGetEnv_HelpShort[] = "getenv: Get an environment variable.";
+const char kGetEnv_Help[] =
+ R"(getenv: Get an environment variable.
+
+ value = getenv(env_var_name)
+
+ Returns the value of the given environment variable. If the value is not
+ found, it will try to look up the variable with the "opposite" case (based on
+ the case of the first letter of the variable), but is otherwise
+ case-sensitive.
+
+ If the environment variable is not found, the empty string will be returned.
+ Note: it might be nice to extend this if we had the concept of "none" in the
+ language to indicate lookup failure.
+
+Example
+
+ home_dir = getenv("HOME")
+)";
+
+Value RunGetEnv(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+
+ std::string result;
+ if (!env->GetVar(args[0].string_value().c_str(), &result))
+ return Value(function, ""); // Not found, return empty string.
+ return Value(function, result);
+}
+
+// import ----------------------------------------------------------------------
+
+const char kImport[] = "import";
+const char kImport_HelpShort[] =
+ "import: Import a file into the current scope.";
+const char kImport_Help[] =
+ R"(import: Import a file into the current scope.
+
+ The import command loads the rules and variables resulting from executing the
+ given file into the current scope.
+
+ By convention, imported files are named with a .gni extension.
+
+ An import is different than a C++ "include". The imported file is executed in
+ a standalone environment from the caller of the import command. The results
+ of this execution are cached for other files that import the same .gni file.
+
+ Note that you can not import a BUILD.gn file that's otherwise used in the
+ build. Files must either be imported or implicitly loaded as a result of deps
+ rules, but not both.
+
+ The imported file's scope will be merged with the scope at the point import
+ was called. If there is a conflict (both the current scope and the imported
+ file define some variable or rule with the same name but different value), a
+ runtime error will be thrown. Therefore, it's good practice to minimize the
+ stuff that an imported file defines.
+
+ Variables and templates beginning with an underscore '_' are considered
+ private and will not be imported. Imported files can use such variables for
+ internal computation without affecting other files.
+
+Examples
+
+ import("//build/rules/idl_compilation_rule.gni")
+
+ # Looks in the current directory.
+ import("my_vars.gni")
+)";
+
+Value RunImport(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+
+ const SourceDir& input_dir = scope->GetSourceDir();
+ SourceFile import_file = input_dir.ResolveRelativeFile(
+ args[0], err, scope->settings()->build_settings()->root_path_utf8());
+ scope->AddBuildDependencyFile(import_file);
+ if (!err->has_error()) {
+ scope->settings()->import_manager().DoImport(import_file, function, scope,
+ err);
+ }
+ return Value();
+}
+
+// not_needed -----------------------------------------------------------------
+
+const char kNotNeeded[] = "not_needed";
+const char kNotNeeded_HelpShort[] =
+ "not_needed: Mark variables from scope as not needed.";
+const char kNotNeeded_Help[] =
+ R"(not_needed: Mark variables from scope as not needed.
+
+ not_needed(variable_list_or_star, variable_to_ignore_list = [])
+ not_needed(from_scope, variable_list_or_star,
+ variable_to_ignore_list = [])
+
+ Mark the variables in the current or given scope as not needed, which means
+ you will not get an error about unused variables for these. The
+ variable_to_ignore_list allows excluding variables from "all matches" if
+ variable_list_or_star is "*".
+
+Example
+
+ not_needed("*", [ "config" ])
+ not_needed([ "data_deps", "deps" ])
+ not_needed(invoker, "*", [ "config" ])
+ not_needed(invoker, [ "data_deps", "deps" ])
+)";
+
+Value RunNotNeeded(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() < 1 || args_vector.size() > 3) {
+ *err = Err(function, "Wrong number of arguments.",
+ "Expecting one, two or three arguments.");
+ return Value();
+ }
+ auto args_cur = args_vector.begin();
+
+ Value* value = nullptr; // Value to use, may point to result_value.
+ Value result_value; // Storage for the "evaluate" case.
+ Value scope_value; // Storage for an evaluated scope.
+ const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
+ if (identifier) {
+ // Optimize the common case where the input scope is an identifier. This
+ // prevents a copy of a potentially large Scope object.
+ value = scope->GetMutableValue(identifier->value().value(),
+ Scope::SEARCH_NESTED, true);
+ if (!value) {
+ *err = Err(identifier, "Undefined identifier.");
+ return Value();
+ }
+ } else {
+ // Non-optimized case, just evaluate the argument.
+ result_value = (*args_cur)->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
+ }
+ args_cur++;
+
+ // Extract the source scope if different from current one.
+ Scope* source = scope;
+ if (value->type() == Value::SCOPE) {
+ if (args_cur == args_vector.end()) {
+ *err = Err(
+ function, "Wrong number of arguments.",
+ "The first argument is a scope, expecting two or three arguments.");
+ return Value();
+ }
+ // Copy the scope value if it will be overridden.
+ if (value == &result_value) {
+ scope_value = Value(nullptr, value->scope_value()->MakeClosure());
+ source = scope_value.scope_value();
+ } else {
+ source = value->scope_value();
+ }
+ result_value = (*args_cur)->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
+ args_cur++;
+ } else if (args_vector.size() > 2) {
+ *err = Err(
+ function, "Wrong number of arguments.",
+ "The first argument is not a scope, expecting one or two arguments.");
+ return Value();
+ }
+
+ // Extract the exclusion list if defined.
+ Value exclusion_value;
+ std::set<std::string> exclusion_set;
+ if (args_cur != args_vector.end()) {
+ exclusion_value = (*args_cur)->Execute(source, err);
+ if (err->has_error())
+ return Value();
+
+ if (exclusion_value.type() != Value::LIST) {
+ *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
+ "Expecting a list of strings.");
+ return Value();
+ }
+
+ for (const Value& cur : exclusion_value.list_value()) {
+ if (!cur.VerifyTypeIs(Value::STRING, err))
+ return Value();
+
+ exclusion_set.insert(cur.string_value());
+ }
+ }
+
+ if (value->type() == Value::STRING) {
+ if (value->string_value() == "*") {
+ source->MarkAllUsed(exclusion_set);
+ return Value();
+ }
+ } else if (value->type() == Value::LIST) {
+ if (exclusion_value.type() != Value::NONE) {
+ *err = Err(exclusion_value, "Not supported with a variable list.",
+ "Exclusion list can only be used with the string \"*\".");
+ return Value();
+ }
+ for (const Value& cur : value->list_value()) {
+ if (!cur.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ // We don't need the return value, we invoke scope::GetValue only to mark
+ // the value as used. Note that we cannot use Scope::MarkUsed because we
+ // want to also search in the parent scope.
+ (void)source->GetValue(cur.string_value(), true);
+ }
+ return Value();
+ }
+
+ // Not the right type of argument.
+ *err = Err(*value, "Not a valid list of variables.",
+ "Expecting either the string \"*\" or a list of strings.");
+ return Value();
+}
+
+// pool ------------------------------------------------------------------------
+
+const char kPool[] = "pool";
+const char kPool_HelpShort[] = "pool: Defines a pool object.";
+const char kPool_Help[] =
+ R"*(pool: Defines a pool object.
+
+ Pool objects can be applied to a tool to limit the parallelism of the
+ build. This object has a single property "depth" corresponding to
+ the number of tasks that may run simultaneously.
+
+ As the file containing the pool definition may be executed in the
+ context of more than one toolchain it is recommended to specify an
+ explicit toolchain when defining and referencing a pool.
+
+ A pool named "console" defined in the root build file represents Ninja's
+ console pool. Targets using this pool will have access to the console's
+ stdin and stdout, and output will not be buffered. This special pool must
+ have a depth of 1. Pools not defined in the root must not be named "console".
+ The console pool can only be defined for the default toolchain.
+ Refer to the Ninja documentation on the console pool for more info.
+
+ A pool is referenced by its label just like a target.
+
+Variables
+
+ depth*
+ * = required
+
+Example
+
+ if (current_toolchain == default_toolchain) {
+ pool("link_pool") {
+ depth = 1
+ }
+ }
+
+ toolchain("toolchain") {
+ tool("link") {
+ command = "..."
+ pool = ":link_pool($default_toolchain)"
+ }
+ }
+)*";
+
+const char kDepth[] = "depth";
+
+Value RunPool(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* scope,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "pool");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Defining pool", label.GetUserVisibleName(true));
+
+ // Get the pool depth. It is an error to define a pool without a depth,
+ // so check first for the presence of the value.
+ const Value* depth = scope->GetValue(kDepth, true);
+ if (!depth) {
+ *err = Err(function, "Can't define a pool without depth.");
+ return Value();
+ }
+
+ if (!depth->VerifyTypeIs(Value::INTEGER, err))
+ return Value();
+
+ if (depth->int_value() < 0) {
+ *err = Err(*depth, "depth must be positive or 0.");
+ return Value();
+ }
+
+ // Create the new pool.
+ std::unique_ptr<Pool> pool = std::make_unique<Pool>(
+ scope->settings(), label, scope->build_dependency_files());
+
+ if (label.name() == "console") {
+ const Settings* settings = scope->settings();
+ if (!settings->is_default()) {
+ *err = Err(
+ function,
+ "\"console\" pool must be defined only in the default toolchain.");
+ return Value();
+ }
+ if (label.dir() != settings->build_settings()->root_target_label().dir()) {
+ *err = Err(function, "\"console\" pool must be defined in the root //.");
+ return Value();
+ }
+ if (depth->int_value() != 1) {
+ *err = Err(*depth, "\"console\" pool must have depth 1.");
+ return Value();
+ }
+ }
+ pool->set_depth(depth->int_value());
+
+ // Save the generated item.
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function, "Can't define a pool in this context.");
+ return Value();
+ }
+ collector->push_back(std::move(pool));
+
+ return Value();
+}
+
+// print -----------------------------------------------------------------------
+
+const char kPrint[] = "print";
+const char kPrint_HelpShort[] = "print: Prints to the console.";
+const char kPrint_Help[] =
+ R"(print: Prints to the console.
+
+ Prints all arguments to the console separated by spaces. A newline is
+ automatically appended to the end.
+
+ This function is intended for debugging. Note that build files are run in
+ parallel so you may get interleaved prints. A buildfile may also be executed
+ more than once in parallel in the context of different toolchains so the
+ prints from one file may be duplicated or
+ interleaved with itself.
+
+Examples
+
+ print("Hello world")
+
+ print(sources, deps)
+)";
+
+Value RunPrint(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ std::string output;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (i != 0)
+ output.push_back(' ');
+ output.append(args[i].ToString(false));
+ }
+ output.push_back('\n');
+
+ const BuildSettings::PrintCallback& cb =
+ scope->settings()->build_settings()->print_callback();
+ if (cb) {
+ cb(output);
+ } else {
+ printf("%s", output.c_str());
+ fflush(stdout);
+ }
+
+ return Value();
+}
+
+// split_list ------------------------------------------------------------------
+
+const char kSplitList[] = "split_list";
+const char kSplitList_HelpShort[] =
+ "split_list: Splits a list into N different sub-lists.";
+const char kSplitList_Help[] =
+ R"(split_list: Splits a list into N different sub-lists.
+
+ result = split_list(input, n)
+
+ Given a list and a number N, splits the list into N sub-lists of
+ approximately equal size. The return value is a list of the sub-lists. The
+ result will always be a list of size N. If N is greater than the number of
+ elements in the input, it will be padded with empty lists.
+
+ The expected use is to divide source files into smaller uniform chunks.
+
+Example
+
+ The code:
+ mylist = [1, 2, 3, 4, 5, 6]
+ print(split_list(mylist, 3))
+
+ Will print:
+ [[1, 2], [3, 4], [5, 6]
+)";
+Value RunSplitList(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() != 2) {
+ *err = Err(function, "Wrong number of arguments to split_list().",
+ "Expecting exactly two.");
+ return Value();
+ }
+
+ ParseNodeValueAdapter list_adapter;
+ if (!list_adapter.InitForType(scope, args_vector[0].get(), Value::LIST, err))
+ return Value();
+ const std::vector<Value>& input = list_adapter.get().list_value();
+
+ ParseNodeValueAdapter count_adapter;
+ if (!count_adapter.InitForType(scope, args_vector[1].get(), Value::INTEGER,
+ err))
+ return Value();
+ int64_t count = count_adapter.get().int_value();
+ if (count <= 0) {
+ *err = Err(function, "Requested result size is not positive.");
+ return Value();
+ }
+
+ Value result(function, Value::LIST);
+ result.list_value().resize(count);
+
+ // Every result list gets at least this many items in it.
+ int64_t min_items_per_list = static_cast<int64_t>(input.size()) / count;
+
+ // This many result lists get an extra item which is the remainder from above.
+ int64_t extra_items = static_cast<int64_t>(input.size()) % count;
+
+ // Allocate all lists that have a remainder assigned to them (max items).
+ int64_t max_items_per_list = min_items_per_list + 1;
+ auto last_item_end = input.begin();
+ for (int64_t i = 0; i < extra_items; i++) {
+ result.list_value()[i] = Value(function, Value::LIST);
+
+ auto begin_add = last_item_end;
+ last_item_end += max_items_per_list;
+ result.list_value()[i].list_value().assign(begin_add, last_item_end);
+ }
+
+ // Allocate all smaller items that don't have a remainder.
+ for (int64_t i = extra_items; i < count; i++) {
+ result.list_value()[i] = Value(function, Value::LIST);
+
+ auto begin_add = last_item_end;
+ last_item_end += min_items_per_list;
+ result.list_value()[i].list_value().assign(begin_add, last_item_end);
+ }
+
+ return result;
+}
+
+// string_join -----------------------------------------------------------------
+
+const char kStringJoin[] = "string_join";
+const char kStringJoin_HelpShort[] =
+ "string_join: Concatenates a list of strings with a separator.";
+const char kStringJoin_Help[] =
+ R"(string_join: Concatenates a list of strings with a separator.
+
+ result = string_join(separator, strings)
+
+ Concatenate a list of strings with intervening occurrences of separator.
+
+Examples
+
+ string_join("", ["a", "b", "c"]) --> "abc"
+ string_join("|", ["a", "b", "c"]) --> "a|b|c"
+ string_join(", ", ["a", "b", "c"]) --> "a, b, c"
+ string_join("s", ["", ""]) --> "s"
+)";
+
+Value RunStringJoin(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ // Check usage: Number of arguments.
+ if (args.size() != 2) {
+ *err = Err(function, "Wrong number of arguments to string_join().",
+ "Expecting exactly two. usage: string_join(separator, strings)");
+ return Value();
+ }
+
+ // Check usage: separator is a string.
+ if (!args[0].VerifyTypeIs(Value::STRING, err)) {
+ *err = Err(function,
+ "separator in string_join(separator, strings) is not "
+ "a string",
+ "Expecting separator argument to be a string.");
+ return Value();
+ }
+ const std::string separator = args[0].string_value();
+
+ // Check usage: strings is a list.
+ if (!args[1].VerifyTypeIs(Value::LIST, err)) {
+ *err = Err(function,
+ "strings in string_join(separator, strings) "
+ "is not a list",
+ "Expecting strings argument to be a list.");
+ return Value();
+ }
+ const std::vector<Value> strings = args[1].list_value();
+
+ // Arguments looks good; do the join.
+ std::stringstream stream;
+ for (size_t i = 0; i < strings.size(); ++i) {
+ if (!strings[i].VerifyTypeIs(Value::STRING, err)) {
+ return Value();
+ }
+ if (i != 0) {
+ stream << separator;
+ }
+ stream << strings[i].string_value();
+ }
+ return Value(function, stream.str());
+}
+
+// string_replace --------------------------------------------------------------
+
+const char kStringReplace[] = "string_replace";
+const char kStringReplace_HelpShort[] =
+ "string_replace: Replaces substring in the given string.";
+const char kStringReplace_Help[] =
+ R"(string_replace: Replaces substring in the given string.
+
+ result = string_replace(str, old, new[, max])
+
+ Returns a copy of the string str in which the occurrences of old have been
+ replaced with new, optionally restricting the number of replacements. The
+ replacement is performed sequentially, so if new contains old, it won't be
+ replaced.
+
+Example
+
+ The code:
+ mystr = "Hello, world!"
+ print(string_replace(mystr, "world", "GN"))
+
+ Will print:
+ Hello, GN!
+)";
+
+Value RunStringReplace(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() < 3 || args.size() > 4) {
+ *err = Err(function, "Wrong number of arguments to string_replace().");
+ return Value();
+ }
+
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string str = args[0].string_value();
+
+ if (!args[1].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string& old = args[1].string_value();
+
+ if (!args[2].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string& new_ = args[2].string_value();
+
+ int64_t max = INT64_MAX;
+ if (args.size() > 3) {
+ if (!args[3].VerifyTypeIs(Value::INTEGER, err))
+ return Value();
+ max = args[3].int_value();
+ if (max <= 0) {
+ *err = Err(function, "Requested number of replacements is not positive.");
+ return Value();
+ }
+ }
+
+ int64_t n = 0;
+ std::string val(str);
+ size_t start_pos = 0;
+ while ((start_pos = val.find(old, start_pos)) != std::string::npos) {
+ val.replace(start_pos, old.length(), new_);
+ start_pos += new_.length();
+ if (++n >= max)
+ break;
+ }
+ return Value(function, std::move(val));
+}
+
+// string_split ----------------------------------------------------------------
+
+const char kStringSplit[] = "string_split";
+const char kStringSplit_HelpShort[] =
+ "string_split: Split string into a list of strings.";
+const char kStringSplit_Help[] =
+ R"(string_split: Split string into a list of strings.
+
+ result = string_split(str[, sep])
+
+ Split string into all substrings separated by separator and returns a list
+ of the substrings between those separators.
+
+ If the separator argument is omitted, the split is by any whitespace, and
+ any leading/trailing whitespace is ignored; similar to Python's str.split().
+
+Examples without a separator (split on whitespace):
+
+ string_split("") --> []
+ string_split("a") --> ["a"]
+ string_split(" aa bb") --> ["aa", "bb"]
+
+Examples with a separator (split on separators):
+
+ string_split("", "|") --> [""]
+ string_split(" a b ", " ") --> ["", "", "a", "b", "", ""]
+ string_split("aa+-bb+-c", "+-") --> ["aa", "bb", "c"]
+)";
+
+Value RunStringSplit(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ // Check usage: argument count.
+ if (args.size() != 1 && args.size() != 2) {
+ *err = Err(function, "Wrong number of arguments to string_split().",
+ "Usage: string_split(str[, sep])");
+ return Value();
+ }
+
+ // Check usage: str is a string.
+ if (!args[0].VerifyTypeIs(Value::STRING, err)) {
+ return Value();
+ }
+ const std::string str = args[0].string_value();
+
+ // Check usage: separator is a non-empty string.
+ std::string separator;
+ if (args.size() == 2) {
+ if (!args[1].VerifyTypeIs(Value::STRING, err)) {
+ return Value();
+ }
+ separator = args[1].string_value();
+ if (separator.empty()) {
+ *err = Err(function,
+ "Separator argument to string_split() "
+ "cannot be empty string",
+ "Usage: string_split(str[, sep])");
+ return Value();
+ }
+ }
+
+ // Split the string into a std::vector.
+ std::vector<std::string> strings;
+ if (!separator.empty()) {
+ // Case: Explicit separator argument.
+ // Note: split_string("", "x") --> [""] like Python.
+ size_t pos = 0;
+ size_t next_pos = 0;
+ while ((next_pos = str.find(separator, pos)) != std::string::npos) {
+ strings.push_back(str.substr(pos, next_pos - pos));
+ pos = next_pos + separator.length();
+ }
+ strings.push_back(str.substr(pos, std::string::npos));
+ } else {
+ // Case: Split on any whitespace and strip ends.
+ // Note: split_string("") --> [] like Python.
+ std::string::const_iterator pos = str.cbegin();
+ while (pos != str.end()) {
+ // Advance past spaces. After this, pos is pointing to non-whitespace.
+ pos = find_if(pos, str.end(), [](char x) { return !std::isspace(x); });
+ if (pos == str.end()) {
+ // Tail is all whitespace, so we're done.
+ break;
+ }
+ // Advance past non-whitespace to get next chunk.
+ std::string::const_iterator next_whitespace_position =
+ find_if(pos, str.end(), [](char x) { return std::isspace(x); });
+ strings.push_back(std::string(pos, next_whitespace_position));
+ pos = next_whitespace_position;
+ }
+ }
+
+ // Convert vector of std::strings to list of GN strings.
+ Value result(function, Value::LIST);
+ result.list_value().resize(strings.size());
+ for (size_t i = 0; i < strings.size(); ++i) {
+ result.list_value()[i] = Value(function, strings[i]);
+ }
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+FunctionInfo::FunctionInfo()
+ : self_evaluating_args_runner(nullptr),
+ generic_block_runner(nullptr),
+ executed_block_runner(nullptr),
+ no_block_runner(nullptr),
+ help_short(nullptr),
+ help(nullptr),
+ is_target(false) {}
+
+FunctionInfo::FunctionInfo(SelfEvaluatingArgsFunction seaf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target)
+ : self_evaluating_args_runner(seaf),
+ generic_block_runner(nullptr),
+ executed_block_runner(nullptr),
+ no_block_runner(nullptr),
+ help_short(in_help_short),
+ help(in_help),
+ is_target(in_is_target) {}
+
+FunctionInfo::FunctionInfo(GenericBlockFunction gbf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target)
+ : self_evaluating_args_runner(nullptr),
+ generic_block_runner(gbf),
+ executed_block_runner(nullptr),
+ no_block_runner(nullptr),
+ help_short(in_help_short),
+ help(in_help),
+ is_target(in_is_target) {}
+
+FunctionInfo::FunctionInfo(ExecutedBlockFunction ebf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target)
+ : self_evaluating_args_runner(nullptr),
+ generic_block_runner(nullptr),
+ executed_block_runner(ebf),
+ no_block_runner(nullptr),
+ help_short(in_help_short),
+ help(in_help),
+ is_target(in_is_target) {}
+
+FunctionInfo::FunctionInfo(NoBlockFunction nbf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target)
+ : self_evaluating_args_runner(nullptr),
+ generic_block_runner(nullptr),
+ executed_block_runner(nullptr),
+ no_block_runner(nbf),
+ help_short(in_help_short),
+ help(in_help),
+ is_target(in_is_target) {}
+
+// Setup the function map via a static initializer. We use this because it
+// avoids race conditions without having to do some global setup function or
+// locking-heavy singleton checks at runtime. In practice, we always need this
+// before we can do anything interesting, so it's OK to wait for the
+// initializer.
+struct FunctionInfoInitializer {
+ FunctionInfoMap map;
+
+ FunctionInfoInitializer() {
+#define INSERT_FUNCTION(command, is_target) \
+ map[k##command] = FunctionInfo(&Run##command, k##command##_HelpShort, \
+ k##command##_Help, is_target);
+
+ INSERT_FUNCTION(Action, true)
+ INSERT_FUNCTION(ActionForEach, true)
+ INSERT_FUNCTION(BundleData, true)
+ INSERT_FUNCTION(CreateBundle, true)
+ INSERT_FUNCTION(Copy, true)
+ INSERT_FUNCTION(Executable, true)
+ INSERT_FUNCTION(Group, true)
+ INSERT_FUNCTION(LoadableModule, true)
+ INSERT_FUNCTION(SharedLibrary, true)
+ INSERT_FUNCTION(SourceSet, true)
+ INSERT_FUNCTION(StaticLibrary, true)
+ INSERT_FUNCTION(Target, true)
+ INSERT_FUNCTION(GeneratedFile, true)
+ INSERT_FUNCTION(RustLibrary, true)
+ INSERT_FUNCTION(RustProcMacro, true)
+
+ INSERT_FUNCTION(Assert, false)
+ INSERT_FUNCTION(Config, false)
+ INSERT_FUNCTION(DeclareArgs, false)
+ INSERT_FUNCTION(Defined, false)
+ INSERT_FUNCTION(ExecScript, false)
+ INSERT_FUNCTION(FilterExclude, false)
+ INSERT_FUNCTION(FilterInclude, false)
+ INSERT_FUNCTION(ForEach, false)
+ INSERT_FUNCTION(ForwardVariablesFrom, false)
+ INSERT_FUNCTION(GetEnv, false)
+ INSERT_FUNCTION(GetLabelInfo, false)
+ INSERT_FUNCTION(GetPathInfo, false)
+ INSERT_FUNCTION(GetTargetOutputs, false)
+ INSERT_FUNCTION(Import, false)
+ INSERT_FUNCTION(NotNeeded, false)
+ INSERT_FUNCTION(Pool, false)
+ INSERT_FUNCTION(Print, false)
+ INSERT_FUNCTION(ProcessFileTemplate, false)
+ INSERT_FUNCTION(ReadFile, false)
+ INSERT_FUNCTION(RebasePath, false)
+ INSERT_FUNCTION(SetDefaults, false)
+ INSERT_FUNCTION(SetDefaultToolchain, false)
+ INSERT_FUNCTION(SplitList, false)
+ INSERT_FUNCTION(StringJoin, false)
+ INSERT_FUNCTION(StringReplace, false)
+ INSERT_FUNCTION(StringSplit, false)
+ INSERT_FUNCTION(Template, false)
+ INSERT_FUNCTION(Tool, false)
+ INSERT_FUNCTION(Toolchain, false)
+ INSERT_FUNCTION(WriteFile, false)
+
+#undef INSERT_FUNCTION
+ }
+};
+const FunctionInfoInitializer function_info;
+
+const FunctionInfoMap& GetFunctions() {
+ return function_info.map;
+}
+
+Value RunFunction(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ BlockNode* block,
+ Err* err) {
+ const Token& name = function->function();
+
+ std::string template_name(function->function().value());
+ const Template* templ = scope->GetTemplate(template_name);
+ if (templ) {
+ Value args = args_list->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ return templ->Invoke(scope, function, template_name, args.list_value(),
+ block, err);
+ }
+
+ // No template matching this, check for a built-in function.
+ const FunctionInfoMap& function_map = GetFunctions();
+ FunctionInfoMap::const_iterator found_function =
+ function_map.find(name.value());
+ if (found_function == function_map.end()) {
+ *err = Err(name, "Unknown function.");
+ return Value();
+ }
+
+ if (found_function->second.self_evaluating_args_runner) {
+ // Self evaluating args functions are special weird built-ins like foreach.
+ // Rather than force them all to check that they have a block or no block
+ // and risk bugs for new additions, check a whitelist here.
+ if (found_function->second.self_evaluating_args_runner != &RunForEach) {
+ if (!VerifyNoBlockForFunctionCall(function, block, err))
+ return Value();
+ }
+ return found_function->second.self_evaluating_args_runner(scope, function,
+ args_list, err);
+ }
+
+ // All other function types take a pre-executed set of args.
+ Value args = args_list->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+
+ if (found_function->second.generic_block_runner) {
+ if (!block) {
+ FillNeedsBlockError(function, err);
+ return Value();
+ }
+ return found_function->second.generic_block_runner(
+ scope, function, args.list_value(), block, err);
+ }
+
+ if (found_function->second.executed_block_runner) {
+ if (!block) {
+ FillNeedsBlockError(function, err);
+ return Value();
+ }
+
+ Scope block_scope(scope);
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ Value result = found_function->second.executed_block_runner(
+ function, args.list_value(), &block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ if (!block_scope.CheckForUnusedVars(err))
+ return Value();
+ return result;
+ }
+
+ // Otherwise it's a no-block function.
+ if (!VerifyNoBlockForFunctionCall(function, block, err))
+ return Value();
+ return found_function->second.no_block_runner(scope, function,
+ args.list_value(), err);
+}
+
+} // namespace functions
diff --git a/gn/src/gn/functions.h b/gn/src/gn/functions.h
new file mode 100644
index 00000000000..e0af41313a3
--- /dev/null
+++ b/gn/src/gn/functions.h
@@ -0,0 +1,554 @@
+// 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 TOOLS_GN_FUNCTIONS_H_
+#define TOOLS_GN_FUNCTIONS_H_
+
+#include <map>
+#include <string>
+#include <string_view>
+#include <vector>
+
+class Err;
+class BlockNode;
+class FunctionCallNode;
+class Label;
+class ListNode;
+class ParseNode;
+class Scope;
+class Value;
+
+// -----------------------------------------------------------------------------
+
+namespace functions {
+
+// This type of function invocation has no block and evaluates its arguments
+// itself rather than taking a pre-executed list. This allows us to implement
+// certain built-in functions.
+using SelfEvaluatingArgsFunction = Value (*)(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err);
+
+// This type of function invocation takes a block node that it will execute.
+using GenericBlockFunction = Value (*)(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+// This type of function takes a block, but does not need to control execution
+// of it. The dispatch function will pre-execute the block and pass the
+// resulting block_scope to the function.
+using ExecutedBlockFunction = Value (*)(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err);
+
+// This type of function does not take a block. It just has arguments.
+using NoBlockFunction = Value (*)(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kAction[];
+extern const char kAction_HelpShort[];
+extern const char kAction_Help[];
+Value RunAction(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kActionForEach[];
+extern const char kActionForEach_HelpShort[];
+extern const char kActionForEach_Help[];
+Value RunActionForEach(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kAssert[];
+extern const char kAssert_HelpShort[];
+extern const char kAssert_Help[];
+Value RunAssert(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kBundleData[];
+extern const char kBundleData_HelpShort[];
+extern const char kBundleData_Help[];
+Value RunBundleData(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kCreateBundle[];
+extern const char kCreateBundle_HelpShort[];
+extern const char kCreateBundle_Help[];
+Value RunCreateBundle(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kConfig[];
+extern const char kConfig_HelpShort[];
+extern const char kConfig_Help[];
+Value RunConfig(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err);
+
+extern const char kCopy[];
+extern const char kCopy_HelpShort[];
+extern const char kCopy_Help[];
+Value RunCopy(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err);
+
+extern const char kDeclareArgs[];
+extern const char kDeclareArgs_HelpShort[];
+extern const char kDeclareArgs_Help[];
+Value RunDeclareArgs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kDefined[];
+extern const char kDefined_HelpShort[];
+extern const char kDefined_Help[];
+Value RunDefined(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err);
+
+extern const char kExecScript[];
+extern const char kExecScript_HelpShort[];
+extern const char kExecScript_Help[];
+Value RunExecScript(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kExecutable[];
+extern const char kExecutable_HelpShort[];
+extern const char kExecutable_Help[];
+Value RunExecutable(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kFilterExclude[];
+extern const char kFilterExclude_HelpShort[];
+extern const char kFilterExclude_Help[];
+Value RunFilterExclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kFilterInclude[];
+extern const char kFilterInclude_HelpShort[];
+extern const char kFilterInclude_Help[];
+Value RunFilterInclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kForEach[];
+extern const char kForEach_HelpShort[];
+extern const char kForEach_Help[];
+Value RunForEach(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err);
+
+extern const char kForwardVariablesFrom[];
+extern const char kForwardVariablesFrom_HelpShort[];
+extern const char kForwardVariablesFrom_Help[];
+Value RunForwardVariablesFrom(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err);
+
+extern const char kGetEnv[];
+extern const char kGetEnv_HelpShort[];
+extern const char kGetEnv_Help[];
+Value RunGetEnv(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kGetLabelInfo[];
+extern const char kGetLabelInfo_HelpShort[];
+extern const char kGetLabelInfo_Help[];
+Value RunGetLabelInfo(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kGetPathInfo[];
+extern const char kGetPathInfo_HelpShort[];
+extern const char kGetPathInfo_Help[];
+Value RunGetPathInfo(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kGetTargetOutputs[];
+extern const char kGetTargetOutputs_HelpShort[];
+extern const char kGetTargetOutputs_Help[];
+Value RunGetTargetOutputs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kGeneratedFile[];
+extern const char kGeneratedFile_HelpShort[];
+extern const char kGeneratedFile_Help[];
+Value RunGeneratedFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kGroup[];
+extern const char kGroup_HelpShort[];
+extern const char kGroup_Help[];
+Value RunGroup(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kImport[];
+extern const char kImport_HelpShort[];
+extern const char kImport_Help[];
+Value RunImport(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kLoadableModule[];
+extern const char kLoadableModule_HelpShort[];
+extern const char kLoadableModule_Help[];
+Value RunLoadableModule(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kNotNeeded[];
+extern const char kNotNeeded_HelpShort[];
+extern const char kNotNeeded_Help[];
+Value RunNotNeeded(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err);
+
+extern const char kPool[];
+extern const char kPool_HelpShort[];
+extern const char kPool_Help[];
+Value RunPool(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err);
+
+extern const char kPrint[];
+extern const char kPrint_HelpShort[];
+extern const char kPrint_Help[];
+Value RunPrint(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kProcessFileTemplate[];
+extern const char kProcessFileTemplate_HelpShort[];
+extern const char kProcessFileTemplate_Help[];
+Value RunProcessFileTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kReadFile[];
+extern const char kReadFile_HelpShort[];
+extern const char kReadFile_Help[];
+Value RunReadFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kRebasePath[];
+extern const char kRebasePath_HelpShort[];
+extern const char kRebasePath_Help[];
+Value RunRebasePath(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kRustLibrary[];
+extern const char kRustLibrary_HelpShort[];
+extern const char kRustLibrary_Help[];
+Value RunRustLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kRustProcMacro[];
+extern const char kRustProcMacro_HelpShort[];
+extern const char kRustProcMacro_Help[];
+Value RunRustProcMacro(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kSetDefaults[];
+extern const char kSetDefaults_HelpShort[];
+extern const char kSetDefaults_Help[];
+Value RunSetDefaults(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kSetDefaultToolchain[];
+extern const char kSetDefaultToolchain_HelpShort[];
+extern const char kSetDefaultToolchain_Help[];
+Value RunSetDefaultToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kSharedLibrary[];
+extern const char kSharedLibrary_HelpShort[];
+extern const char kSharedLibrary_Help[];
+Value RunSharedLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kSourceSet[];
+extern const char kSourceSet_HelpShort[];
+extern const char kSourceSet_Help[];
+Value RunSourceSet(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kSplitList[];
+extern const char kSplitList_HelpShort[];
+extern const char kSplitList_Help[];
+Value RunSplitList(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err);
+
+extern const char kStaticLibrary[];
+extern const char kStaticLibrary_HelpShort[];
+extern const char kStaticLibrary_Help[];
+Value RunStaticLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kReplaceSubstr[];
+extern const char kReplaceSubstr_HelpShort[];
+extern const char kReplaceSubstr_Help[];
+Value RunReplaceSubstr(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args_list,
+ Err* err);
+
+extern const char kTarget[];
+extern const char kTarget_HelpShort[];
+extern const char kTarget_Help[];
+Value RunTarget(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kTemplate[];
+extern const char kTemplate_HelpShort[];
+extern const char kTemplate_Help[];
+Value RunTemplate(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kTool[];
+extern const char kTool_HelpShort[];
+extern const char kTool_Help[];
+Value RunTool(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kToolchain[];
+extern const char kToolchain_HelpShort[];
+extern const char kToolchain_Help[];
+Value RunToolchain(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
+extern const char kWriteFile[];
+extern const char kWriteFile_HelpShort[];
+extern const char kWriteFile_Help[];
+Value RunWriteFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+// -----------------------------------------------------------------------------
+
+// One function record. Only one of the given runner types will be non-null
+// which indicates the type of function it is.
+struct FunctionInfo {
+ FunctionInfo();
+ FunctionInfo(SelfEvaluatingArgsFunction seaf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target);
+ FunctionInfo(GenericBlockFunction gbf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target);
+ FunctionInfo(ExecutedBlockFunction ebf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target);
+ FunctionInfo(NoBlockFunction nbf,
+ const char* in_help_short,
+ const char* in_help,
+ bool in_is_target);
+
+ SelfEvaluatingArgsFunction self_evaluating_args_runner;
+ GenericBlockFunction generic_block_runner;
+ ExecutedBlockFunction executed_block_runner;
+ NoBlockFunction no_block_runner;
+
+ const char* help_short;
+ const char* help;
+
+ bool is_target;
+};
+
+using FunctionInfoMap = std::map<std::string_view, FunctionInfo>;
+
+// Returns the mapping of all built-in functions.
+const FunctionInfoMap& GetFunctions();
+
+// Runs the given function.
+Value RunFunction(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ BlockNode* block, // Optional.
+ Err* err);
+
+} // namespace functions
+
+// Helper functions -----------------------------------------------------------
+
+// Validates that the scope that a value is defined in is not the scope
+// of the current declare_args() call, if that's what we're in. It is
+// illegal to read a value from inside the same declare_args() call, since
+// the overrides will not have been applied yet (see `gn help declare_args`
+// for more).
+bool EnsureNotReadingFromSameDeclareArgs(const ParseNode* node,
+ const Scope* cur_scope,
+ const Scope* val_scope,
+ Err* err);
+
+// Verifies that the current scope is not processing an import. If it is, it
+// will set the error, blame the given parse node for it, and return false.
+bool EnsureNotProcessingImport(const ParseNode* node,
+ const Scope* scope,
+ Err* err);
+
+// Like EnsureNotProcessingImport but checks for running the build config.
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+ const Scope* scope,
+ Err* err);
+
+// Sets up the |block_scope| for executing a target (or something like it).
+// The |scope| is the containing scope. It should have been already set as the
+// parent for the |block_scope| when the |block_scope| was created.
+//
+// This will set up the target defaults and set the |target_name| variable in
+// the block scope to the current target name, which is assumed to be the first
+// argument to the function.
+//
+// On success, returns true. On failure, sets the error and returns false.
+bool FillTargetBlockScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& target_type,
+ const BlockNode* block,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err);
+
+// Sets the given error to a message explaining that the function call requires
+// a block.
+void FillNeedsBlockError(const FunctionCallNode* function, Err* err);
+
+// Validates that the given function call has one string argument. This is
+// the most common function signature, so it saves space to have this helper.
+// Returns false and sets the error on failure.
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+// Returns the name of the toolchain for the given scope.
+const Label& ToolchainLabelForScope(const Scope* scope);
+
+// Generates a label for the given scope, using the current directory and
+// toolchain, and the given name.
+Label MakeLabelForScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& name);
+
+// Some types of blocks can't be nested inside other ones. For such cases,
+// instantiate this object upon entering the block and Enter() will fail if
+// there is already another non-nestable block on the stack.
+class NonNestableBlock {
+ public:
+ // type_description is a string that will be used in error messages
+ // describing the type of the block, for example, "template" or "config".
+ NonNestableBlock(Scope* scope,
+ const FunctionCallNode* function,
+ const char* type_description);
+ ~NonNestableBlock();
+
+ bool Enter(Err* err);
+
+ private:
+ // Used as a void* key for the Scope to track our property. The actual value
+ // is never used.
+ static const int kKey;
+
+ Scope* scope_;
+ const FunctionCallNode* function_;
+ const char* type_description_;
+
+ // Set to true when the key is added to the scope so we don't try to
+ // delete nonexistant keys which will cause assertions.
+ bool key_added_;
+};
+
+#endif // TOOLS_GN_FUNCTIONS_H_
diff --git a/gn/src/gn/functions_target.cc b/gn/src/gn/functions_target.cc
new file mode 100644
index 00000000000..d781fd539ea
--- /dev/null
+++ b/gn/src/gn/functions_target.cc
@@ -0,0 +1,1015 @@
+// 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 "gn/functions.h"
+
+#include "gn/config_values_generator.h"
+#include "gn/err.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/target_generator.h"
+#include "gn/template.h"
+#include "gn/value.h"
+#include "gn/variables.h"
+
+#define DEPENDENT_CONFIG_VARS \
+ " Dependent configs: all_dependent_configs, public_configs\n"
+#define DEPS_VARS " Deps: data_deps, deps, public_deps\n"
+#define GENERAL_TARGET_VARS \
+ " General: check_includes, configs, data, friend, inputs, metadata,\n" \
+ " output_name, output_extension, public, sources, testonly,\n" \
+ " visibility\n"
+#define RUST_VARS \
+ " Rust variables: aliased_deps, crate_root, crate_name\n"
+#define RUST_SHARED_VARS \
+ " Rust variables: aliased_deps, crate_root, crate_name, crate_type\n"
+
+namespace functions {
+
+namespace {
+
+Value ExecuteGenericTarget(const char* target_type,
+ Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "target");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ if (!EnsureNotProcessingImport(function, scope, err) ||
+ !EnsureNotProcessingBuildConfig(function, scope, err))
+ return Value();
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, function, target_type, block, args,
+ &block_scope, err))
+ return Value();
+
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ TargetGenerator::GenerateTarget(&block_scope, function, args, target_type,
+ err);
+ if (err->has_error())
+ return Value();
+
+ block_scope.CheckForUnusedVars(err);
+ return Value();
+}
+
+} // namespace
+
+// action ----------------------------------------------------------------------
+
+// Common help paragraph on script runtime execution directories.
+#define SCRIPT_EXECUTION_CONTEXT \
+ "\n" \
+ " The script will be executed with the given arguments with the current\n" \
+ " directory being that of the root build directory. If you pass files\n" \
+ " to your script, see \"gn help rebase_path\" for how to convert\n" \
+ " file names to be relative to the build directory (file names in the\n" \
+ " sources, outputs, and inputs will be all treated as relative to the\n" \
+ " current build file and converted as needed automatically).\n" \
+ "\n" \
+ " GN sets Ninja's flag 'restat = 1` for all action commands. This means\n" \
+ " that Ninja will check the timestamp of the output after the action\n" \
+ " completes. If output timestamp is unchanged, the step will be treated\n" \
+ " as if it never needed to be rebuilt, potentially eliminating some\n" \
+ " downstream steps for incremental builds. Scripts can improve build\n" \
+ " performance by taking care not to change the timstamp of the output\n" \
+ " file(s) if the contents have not changed.\n"
+
+// Common help paragraph on script output directories.
+#define SCRIPT_EXECUTION_OUTPUTS \
+ "\n" \
+ " All output files must be inside the output directory of the build.\n" \
+ " You would generally use |$target_out_dir| or |$target_gen_dir| to\n" \
+ " reference the output or generated intermediate file directories,\n" \
+ " respectively.\n"
+
+#define ACTION_DEPS \
+ "\n" \
+ " The \"deps\" and \"public_deps\" for an action will always be\n" \
+ " completed before any part of the action is run so it can depend on\n" \
+ " the output of previous steps. The \"data_deps\" will be built if the\n" \
+ " action is built, but may not have completed before all steps of the\n" \
+ " action are started. This can give additional parallelism in the build\n" \
+ " for runtime-only dependencies.\n"
+
+// Common help paragraph on targets that can use different languages.
+#define LANGUAGE_HELP \
+ "\n" \
+ " The tools and commands used to create this target type will be\n" \
+ " determined by the source files in its sources. Targets containing\n" \
+ " multiple compiler-incompatible languages are not allowed (e.g. a\n" \
+ " target containing both C and C++ sources is acceptable, but a\n" \
+ " target containing C and Rust sources is not).\n"
+
+const char kAction[] = "action";
+const char kAction_HelpShort[] =
+ "action: Declare a target that runs a script a single time.";
+const char kAction_Help[] =
+ R"(action: Declare a target that runs a script a single time.
+
+ This target type allows you to run a script a single time to produce one or
+ more output files. If you want to run a script once for each of a set of
+ input files, see "gn help action_foreach".
+
+Inputs
+
+ In an action the "sources" and "inputs" are treated the same: they're both
+ input dependencies on script execution with no special handling. If you want
+ to pass the sources to your script, you must do so explicitly by including
+ them in the "args". Note also that this means there is no special handling of
+ paths since GN doesn't know which of the args are paths and not. You will
+ want to use rebase_path() to convert paths to be relative to the
+ root_build_dir.
+
+ You can dynamically write input dependencies (for incremental rebuilds if an
+ input file changes) by writing a depfile when the script is run (see "gn help
+ depfile"). This is more flexible than "inputs".
+
+ If the command line length is very long, you can use response files to pass
+ args to your script. See "gn help response_file_contents".
+
+ It is recommended you put inputs to your script in the "sources" variable,
+ and stuff like other Python files required to run your script in the "inputs"
+ variable.
+)"
+
+ ACTION_DEPS
+
+ R"(
+Outputs
+
+ You should specify files created by your script by specifying them in the
+ "outputs".
+)"
+
+ SCRIPT_EXECUTION_CONTEXT
+
+ R"(
+File name handling
+)"
+
+ SCRIPT_EXECUTION_OUTPUTS
+
+ R"(
+Variables
+
+ args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
+ response_file_contents, script*, sources
+ * = required
+
+Example
+
+ action("run_this_guy_once") {
+ script = "doprocessing.py"
+ sources = [ "my_configuration.txt" ]
+ outputs = [ "$target_gen_dir/insightful_output.txt" ]
+
+ # Our script imports this Python file so we want to rebuild if it changes.
+ inputs = [ "helper_library.py" ]
+
+ # Note that we have to manually pass the sources to our script if the
+ # script needs them as inputs.
+ args = [ "--out", rebase_path(target_gen_dir, root_build_dir) ] +
+ rebase_path(sources, root_build_dir)
+ }
+)";
+
+Value RunAction(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kAction, scope, function, args, block,
+ err);
+}
+
+// action_foreach --------------------------------------------------------------
+
+const char kActionForEach[] = "action_foreach";
+const char kActionForEach_HelpShort[] =
+ "action_foreach: Declare a target that runs a script over a set of files.";
+const char kActionForEach_Help[] =
+ R"(action_foreach: Declare a target that runs a script over a set of files.
+
+ This target type allows you to run a script once-per-file over a set of
+ sources. If you want to run a script once that takes many files as input, see
+ "gn help action".
+
+Inputs
+
+ The script will be run once per file in the "sources" variable. The "outputs"
+ variable should specify one or more files with a source expansion pattern in
+ it (see "gn help source_expansion"). The output file(s) for each script
+ invocation should be unique. Normally you use "{{source_name_part}}" in each
+ output file.
+
+ If your script takes additional data as input, such as a shared configuration
+ file or a Python module it uses, those files should be listed in the "inputs"
+ variable. These files are treated as dependencies of each script invocation.
+
+ If the command line length is very long, you can use response files to pass
+ args to your script. See "gn help response_file_contents".
+
+ You can dynamically write input dependencies (for incremental rebuilds if an
+ input file changes) by writing a depfile when the script is run (see "gn help
+ depfile"). This is more flexible than "inputs".
+)" ACTION_DEPS
+ R"(
+Outputs
+)" SCRIPT_EXECUTION_CONTEXT
+ R"(
+File name handling
+)" SCRIPT_EXECUTION_OUTPUTS
+ R"(
+Variables
+
+ args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
+ response_file_contents, script*, sources*
+ * = required
+
+Example
+
+ # Runs the script over each IDL file. The IDL script will generate both a .cc
+ # and a .h file for each input.
+ action_foreach("my_idl") {
+ script = "idl_processor.py"
+ sources = [ "foo.idl", "bar.idl" ]
+
+ # Our script reads this file each time, so we need to list it as a
+ # dependency so we can rebuild if it changes.
+ inputs = [ "my_configuration.txt" ]
+
+ # Transformation from source file name to output file names.
+ outputs = [ "$target_gen_dir/{{source_name_part}}.h",
+ "$target_gen_dir/{{source_name_part}}.cc" ]
+
+ # Note that since "args" is opaque to GN, if you specify paths here, you
+ # will need to convert it to be relative to the build directory using
+ # rebase_path().
+ args = [
+ "{{source}}",
+ "-o",
+ rebase_path(target_gen_dir, root_build_dir) +
+ "/{{source_name_part}}.h" ]
+ }
+)";
+
+Value RunActionForEach(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kActionForEach, scope, function, args,
+ block, err);
+}
+
+// bundle_data -----------------------------------------------------------------
+
+const char kBundleData[] = "bundle_data";
+const char kBundleData_HelpShort[] =
+ "bundle_data: [iOS/macOS] Declare a target without output.";
+const char kBundleData_Help[] =
+ R"(bundle_data: [iOS/macOS] Declare a target without output.
+
+ This target type allows one to declare data that is required at runtime. It is
+ used to inform "create_bundle" targets of the files to copy into generated
+ bundle, see "gn help create_bundle" for help.
+
+ The target must define a list of files as "sources" and a single "outputs".
+ If there are multiple files, source expansions must be used to express the
+ output. The output must reference a file inside of {{bundle_root_dir}}.
+
+ This target can be used on all platforms though it is designed only to
+ generate iOS/macOS bundle. In cross-platform projects, it is advised to put it
+ behind iOS/macOS conditionals.
+
+ See "gn help create_bundle" for more information.
+
+Variables
+
+ sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
+ * = required
+
+Examples
+
+ bundle_data("icudata") {
+ sources = [ "sources/data/in/icudtl.dat" ]
+ outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
+ }
+
+ bundle_data("base_unittests_bundle_data]") {
+ sources = [ "test/data" ]
+ outputs = [
+ "{{bundle_resources_dir}}/{{source_root_relative_dir}}/" +
+ "{{source_file_part}}"
+ ]
+ }
+
+ bundle_data("material_typography_bundle_data") {
+ sources = [
+ "src/MaterialTypography.bundle/Roboto-Bold.ttf",
+ "src/MaterialTypography.bundle/Roboto-Italic.ttf",
+ "src/MaterialTypography.bundle/Roboto-Regular.ttf",
+ "src/MaterialTypography.bundle/Roboto-Thin.ttf",
+ ]
+ outputs = [
+ "{{bundle_resources_dir}}/MaterialTypography.bundle/"
+ "{{source_file_part}}"
+ ]
+ }
+)";
+
+Value RunBundleData(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kBundleData, scope, function, args,
+ block, err);
+}
+
+// create_bundle ---------------------------------------------------------------
+
+const char kCreateBundle[] = "create_bundle";
+const char kCreateBundle_HelpShort[] =
+ "create_bundle: [iOS/macOS] Build an iOS or macOS bundle.";
+const char kCreateBundle_Help[] =
+ R"(create_bundle: [ios/macOS] Build an iOS or macOS bundle.
+
+ This target generates an iOS or macOS bundle (which is a directory with a
+ well-know structure). This target does not define any sources, instead they
+ are computed from all "bundle_data" target this one depends on transitively
+ (the recursion stops at "create_bundle" targets).
+
+ The "bundle_*_dir" are be used for the expansion of {{bundle_*_dir}} rules in
+ "bundle_data" outputs. The properties are optional but must be defined if any
+ of the "bundle_data" target use them.
+
+ This target can be used on all platforms though it is designed only to
+ generate iOS or macOS bundle. In cross-platform projects, it is advised to put
+ it behind iOS/macOS conditionals.
+
+ If a create_bundle is specified as a data_deps for another target, the bundle
+ is considered a leaf, and its public and private dependencies will not
+ contribute to any data or data_deps. Required runtime dependencies should be
+ placed in the bundle. A create_bundle can declare its own explicit data and
+ data_deps, however.
+
+Code signing
+
+ Some bundle needs to be code signed as part of the build (on iOS all
+ application needs to be code signed to run on a device). The code signature
+ can be configured via the code_signing_script variable.
+
+ If set, code_signing_script is the path of a script that invoked after all
+ files have been moved into the bundle. The script must not change any file in
+ the bundle, but may add new files.
+
+ If code_signing_script is defined, then code_signing_outputs must also be
+ defined and non-empty to inform when the script needs to be re-run. The
+ code_signing_args will be passed as is to the script (so path have to be
+ rebased) and additional inputs may be listed with the variable
+ code_signing_sources.
+
+Variables
+
+ bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
+ bundle_executable_dir, bundle_deps_filter, deps, data_deps, public_deps,
+ visibility, product_type, code_signing_args, code_signing_script,
+ code_signing_sources, code_signing_outputs, xcode_extra_attributes,
+ xcode_test_application_name, partial_info_plist, metadata
+
+Example
+
+ # Defines a template to create an application. On most platform, this is just
+ # an alias for an "executable" target, but on iOS/macOS, it builds an
+ # application bundle.
+ template("app") {
+ if (!is_ios && !is_mac) {
+ executable(target_name) {
+ forward_variables_from(invoker, "*")
+ }
+ } else {
+ app_name = target_name
+ gen_path = target_gen_dir
+
+ action("${app_name}_generate_info_plist") {
+ script = [ "//build/ios/ios_gen_plist.py" ]
+ sources = [ "templates/Info.plist" ]
+ outputs = [ "$gen_path/Info.plist" ]
+ args = rebase_path(sources, root_build_dir) +
+ rebase_path(outputs, root_build_dir)
+ }
+
+ bundle_data("${app_name}_bundle_info_plist") {
+ public_deps = [ ":${app_name}_generate_info_plist" ]
+ sources = [ "$gen_path/Info.plist" ]
+ outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
+ }
+
+ executable("${app_name}_generate_executable") {
+ forward_variables_from(invoker, "*", [
+ "output_name",
+ "visibility",
+ ])
+ output_name =
+ rebase_path("$gen_path/$app_name", root_build_dir)
+ }
+
+ code_signing =
+ defined(invoker.code_signing) && invoker.code_signing
+
+ if (!is_ios || !code_signing) {
+ bundle_data("${app_name}_bundle_executable") {
+ public_deps = [ ":${app_name}_generate_executable" ]
+ sources = [ "$gen_path/$app_name" ]
+ outputs = [ "{{bundle_executable_dir}}/$app_name" ]
+ }
+ }
+
+ create_bundle("$app_name.app") {
+ product_type = "com.apple.product-type.application"
+
+ if (is_ios) {
+ bundle_root_dir = "$root_build_dir/$target_name"
+ bundle_contents_dir = bundle_root_dir
+ bundle_resources_dir = bundle_contents_dir
+ bundle_executable_dir = bundle_contents_dir
+
+ xcode_extra_attributes = {
+ ONLY_ACTIVE_ARCH = "YES"
+ DEBUG_INFORMATION_FORMAT = "dwarf"
+ }
+ } else {
+ bundle_root_dir = "$root_build_dir/$target_name"
+ bundle_contents_dir = "$bundle_root_dir/Contents"
+ bundle_resources_dir = "$bundle_contents_dir/Resources"
+ bundle_executable_dir = "$bundle_contents_dir/MacOS"
+ }
+ deps = [ ":${app_name}_bundle_info_plist" ]
+ if (is_ios && code_signing) {
+ deps += [ ":${app_name}_generate_executable" ]
+ code_signing_script = "//build/config/ios/codesign.py"
+ code_signing_sources = [
+ invoker.entitlements_path,
+ "$target_gen_dir/$app_name",
+ ]
+ code_signing_outputs = [
+ "$bundle_root_dir/$app_name",
+ "$bundle_root_dir/_CodeSignature/CodeResources",
+ "$bundle_root_dir/embedded.mobileprovision",
+ "$target_gen_dir/$app_name.xcent",
+ ]
+ code_signing_args = [
+ "-i=" + ios_code_signing_identity,
+ "-b=" + rebase_path(
+ "$target_gen_dir/$app_name", root_build_dir),
+ "-e=" + rebase_path(
+ invoker.entitlements_path, root_build_dir),
+ "-e=" + rebase_path(
+ "$target_gen_dir/$app_name.xcent", root_build_dir),
+ rebase_path(bundle_root_dir, root_build_dir),
+ ]
+ } else {
+ deps += [ ":${app_name}_bundle_executable" ]
+ }
+ }
+ }
+ }
+)";
+
+Value RunCreateBundle(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kCreateBundle, scope, function, args,
+ block, err);
+}
+
+// copy ------------------------------------------------------------------------
+
+const char kCopy[] = "copy";
+const char kCopy_HelpShort[] = "copy: Declare a target that copies files.";
+const char kCopy_Help[] =
+ R"(copy: Declare a target that copies files.
+
+File name handling
+
+ All output files must be inside the output directory of the build. You would
+ generally use |$target_out_dir| or |$target_gen_dir| to reference the output
+ or generated intermediate file directories, respectively.
+
+ Both "sources" and "outputs" must be specified. Sources can include as many
+ files as you want, but there can only be one item in the outputs list (plural
+ is used for the name for consistency with other target types).
+
+ If there is more than one source file, your output name should specify a
+ mapping from each source file to an output file name using source expansion
+ (see "gn help source_expansion"). The placeholders will look like
+ "{{source_name_part}}", for example.
+
+Examples
+
+ # Write a rule that copies a checked-in DLL to the output directory.
+ copy("mydll") {
+ sources = [ "mydll.dll" ]
+ outputs = [ "$target_out_dir/mydll.dll" ]
+ }
+
+ # Write a rule to copy several files to the target generated files directory.
+ copy("myfiles") {
+ sources = [ "data1.dat", "data2.dat", "data3.dat" ]
+
+ # Use source expansion to generate output files with the corresponding file
+ # names in the gen dir. This will just copy each file.
+ outputs = [ "$target_gen_dir/{{source_file_part}}" ]
+ }
+)";
+
+Value RunCopy(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* scope,
+ Err* err) {
+ if (!EnsureNotProcessingImport(function, scope, err) ||
+ !EnsureNotProcessingBuildConfig(function, scope, err))
+ return Value();
+ TargetGenerator::GenerateTarget(scope, function, args, functions::kCopy, err);
+ return Value();
+}
+
+// executable ------------------------------------------------------------------
+
+const char kExecutable[] = "executable";
+const char kExecutable_HelpShort[] =
+ "executable: Declare an executable target.";
+const char kExecutable_Help[] =
+ R"(executable: Declare an executable target.
+
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS;
+
+Value RunExecutable(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kExecutable, scope, function, args,
+ block, err);
+}
+
+// group -----------------------------------------------------------------------
+
+const char kGroup[] = "group";
+const char kGroup_HelpShort[] = "group: Declare a named group of targets.";
+const char kGroup_Help[] =
+ R"(group: Declare a named group of targets.
+
+ This target type allows you to create meta-targets that just collect a set of
+ dependencies into one named target. Groups can additionally specify configs
+ that apply to their dependents.
+
+Variables
+
+)" DEPS_VARS DEPENDENT_CONFIG_VARS
+
+ R"(
+Example
+
+ group("all") {
+ deps = [
+ "//project:runner",
+ "//project:unit_tests",
+ ]
+ }
+)";
+
+Value RunGroup(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kGroup, scope, function, args, block,
+ err);
+}
+
+// loadable_module -------------------------------------------------------------
+
+const char kLoadableModule[] = "loadable_module";
+const char kLoadableModule_HelpShort[] =
+ "loadable_module: Declare a loadable module target.";
+const char kLoadableModule_Help[] =
+ R"(loadable_module: Declare a loadable module target.
+
+ This target type allows you to create an object file that is (and can only
+ be) loaded and unloaded at runtime.
+
+ A loadable module will be specified on the linker line for targets listing
+ the loadable module in its "deps". If you don't want this (if you don't need
+ to dynamically load the library at runtime), then you should use a
+ "shared_library" target type instead.
+
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_SHARED_VARS;
+
+Value RunLoadableModule(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kLoadableModule, scope, function, args,
+ block, err);
+}
+
+// rust_library ----------------------------------------------------------------
+
+const char kRustLibrary[] = "rust_library";
+const char kRustLibrary_HelpShort[] =
+ "rust_library: Declare a Rust library target.";
+const char kRustLibrary_Help[] =
+ R"(rust_library: Declare a Rust library target.
+
+ A Rust library is an archive containing additional rust-c provided metadata.
+ These are the files produced by the rustc compiler with the `.rlib`
+ extension, and are the intermediate step for most Rust-based binaries.
+
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS;
+Value RunRustLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kRustLibrary, scope, function, args,
+ block, err);
+}
+
+// rust_proc_macro ----------------------------------------------------------------
+
+const char kRustProcMacro[] = "rust_proc_macro";
+const char kRustProcMacro_HelpShort[] =
+ "rust_proc_macro: Declare a Rust procedural macro target.";
+const char kRustProcMacro_Help[] =
+ R"(rust_proc_macro: Declare a Rust procedural macro target.
+
+ A Rust procedural macro allows creating syntax extensions as execution of a
+ function. They are compiled as dynamic libraries and used by the compiler at
+ runtime.
+
+ Their use is the same as of other Rust libraries, but their build has some
+ additional restrictions in terms of supported flags.
+
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS;
+Value RunRustProcMacro(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kRustProcMacro, scope, function, args,
+ block, err);
+}
+
+// shared_library --------------------------------------------------------------
+
+const char kSharedLibrary[] = "shared_library";
+const char kSharedLibrary_HelpShort[] =
+ "shared_library: Declare a shared library target.";
+const char kSharedLibrary_Help[] =
+ R"(shared_library: Declare a shared library target.
+
+ A shared library will be specified on the linker line for targets listing the
+ shared library in its "deps". If you don't want this (say you dynamically
+ load the library at runtime), then you should depend on the shared library
+ via "data_deps" or, on Darwin platforms, use a "loadable_module" target type
+ instead.
+
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_SHARED_VARS;
+
+Value RunSharedLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kSharedLibrary, scope, function, args,
+ block, err);
+}
+
+// source_set ------------------------------------------------------------------
+
+const char kSourceSet[] = "source_set";
+const char kSourceSet_HelpShort[] = "source_set: Declare a source set target.";
+const char kSourceSet_Help[] =
+ R"(source_set: Declare a source set target.
+
+ Only C-language source sets are supported at the moment.
+
+C-language source_sets
+
+ A source set is a collection of sources that get compiled, but are not linked
+ to produce any kind of library. Instead, the resulting object files are
+ implicitly added to the linker line of all targets that depend on the source
+ set.
+
+ In most cases, a source set will behave like a static library, except no
+ actual library file will be produced. This will make the build go a little
+ faster by skipping creation of a large static library, while maintaining the
+ organizational benefits of focused build targets.
+
+ The main difference between a source set and a static library is around
+ handling of exported symbols. Most linkers assume declaring a function
+ exported means exported from the static library. The linker can then do dead
+ code elimination to delete code not reachable from exported functions.
+
+ A source set will not do this code elimination since there is no link step.
+ This allows you to link many source sets into a shared library and have the
+ "exported symbol" notation indicate "export from the final shared library and
+ not from the intermediate targets." There is no way to express this concept
+ when linking multiple static libraries into a shared library.
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+
+Value RunSourceSet(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kSourceSet, scope, function, args,
+ block, err);
+}
+
+// static_library --------------------------------------------------------------
+
+const char kStaticLibrary[] = "static_library";
+const char kStaticLibrary_HelpShort[] =
+ "static_library: Declare a static library target.";
+const char kStaticLibrary_Help[] =
+ R"(static_library: Declare a static library target.
+
+ Make a ".a" / ".lib" file.
+
+ If you only need the static library for intermediate results in the build,
+ you should consider a source_set instead since it will skip the (potentially
+ slow) step of creating the intermediate library file.
+
+Variables
+
+ complete_static_lib
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS LANGUAGE_HELP;
+
+Value RunStaticLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kStaticLibrary, scope, function, args,
+ block, err);
+}
+
+// target ---------------------------------------------------------------------
+
+const char kTarget[] = "target";
+const char kTarget_HelpShort[] =
+ "target: Declare an target with the given programmatic type.";
+const char kTarget_Help[] =
+ R"(target: Declare an target with the given programmatic type.
+
+ target(target_type_string, target_name_string) { ... }
+
+ The target() function is a way to invoke a built-in target or template with a
+ type determined at runtime. This is useful for cases where the type of a
+ target might not be known statically.
+
+ Only templates and built-in target functions are supported for the
+ target_type_string parameter. Arbitrary functions, configs, and toolchains
+ are not supported.
+
+ The call:
+ target("source_set", "doom_melon") {
+ Is equivalent to:
+ source_set("doom_melon") {
+
+Example
+
+ if (foo_build_as_shared) {
+ my_type = "shared_library"
+ } else {
+ my_type = "source_set"
+ }
+
+ target(my_type, "foo") {
+ ...
+ }
+)";
+Value RunTarget(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ if (args.size() != 2) {
+ *err = Err(function, "Expected two arguments.", "Try \"gn help target\".");
+ return Value();
+ }
+
+ // The first argument must be a string (the target type). Don't type-check
+ // the second argument since the target-specific function will do that.
+ if (!args[0].VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string& target_type = args[0].string_value();
+
+ // The rest of the args are passed to the function.
+ std::vector<Value> sub_args(args.begin() + 1, args.end());
+
+ // Run a template if it is one.
+ const Template* templ = scope->GetTemplate(target_type);
+ if (templ)
+ return templ->Invoke(scope, function, target_type, sub_args, block, err);
+
+ // Otherwise, assume the target is a built-in target type.
+ return ExecuteGenericTarget(target_type.c_str(), scope, function, sub_args,
+ block, err);
+}
+
+const char kGeneratedFile[] = "generated_file";
+const char kGeneratedFile_HelpShort[] =
+ "generated_file: Declare a generated_file target.";
+const char kGeneratedFile_Help[] =
+ R"(generated_file: Declare a generated_file target.
+
+ Writes data value(s) to disk on resolution. This target type mirrors some
+ functionality of the write_file() function, but also provides the ability to
+ collect metadata from its dependencies on resolution rather than writing out
+ at parse time.
+
+ The `outputs` variable is required to be a list with a single element,
+ specifying the intended location of the output file.
+
+ The `output_conversion` variable specified the format to write the
+ value. See `gn help output_conversion`.
+
+ One of `contents` or `data_keys` must be specified; use of `data` will write
+ the contents of that value to file, while use of `data_keys` will trigger a
+ metadata collection walk based on the dependencies of the target and the
+ optional values of the `rebase` and `walk_keys` variables. See
+ `gn help metadata`.
+
+ Collected metadata, if specified, will be returned in postorder of
+ dependencies. See the example for details.
+
+Example (metadata collection)
+
+ Given the following targets defined in //base/BUILD.gn, where A depends on B
+ and B depends on C and D:
+
+ group("a") {
+ metadata = {
+ doom_melon = [ "enable" ]
+ my_files = [ "foo.cpp" ]
+
+ # Note: this is functionally equivalent to not defining `my_barrier`
+ # at all in this target's metadata.
+ my_barrier = [ "" ]
+ }
+
+ deps = [ ":b" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "bar.cpp" ]
+ my_barrier = [ ":c" ]
+ }
+
+ deps = [ ":c", ":d" ]
+ }
+
+ group("c") {
+ metadata = {
+ doom_melon = [ "disable" ]
+ my_files = [ "baz.cpp" ]
+ }
+ }
+
+ group("d") {
+ metadata = {
+ my_files = [ "missing.cpp" ]
+ }
+ }
+
+ If the following generated_file target is defined:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (less the
+ comments):
+ [
+ "baz.cpp", // from //base:c via //base:b
+ "missing.cpp" // from //base:d via //base:b
+ "bar.cpp", // from //base:b via //base:a
+ "foo.cpp", // from //base:a
+ ]
+
+ Alternatively, as an example of using walk_keys, if the following
+ generated_file target is defined:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_barrier" ]
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (again less
+ the comments):
+ [
+ "baz.cpp", // from //base:c via //base:b
+ "bar.cpp", // from //base:b via //base:a
+ "foo.cpp", // from //base:a
+ ]
+
+ If `rebase` is used in the following generated_file target:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_barrier" ]
+ rebase = root_build_dir
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (again less
+ the comments) (assuming root_build_dir = "//out"):
+ [
+ "../base/baz.cpp", // from //base:c via //base:b
+ "../base/bar.cpp", // from //base:b via //base:a
+ "../base/foo.cpp", // from //base:a
+ ]
+
+
+Variables
+
+ contents
+ data_keys
+ rebase
+ walk_keys
+ output_conversion
+)" DEPS_VARS DEPENDENT_CONFIG_VARS;
+
+Value RunGeneratedFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kGeneratedFile, scope, function, args,
+ block, err);
+}
+
+} // namespace functions
diff --git a/gn/src/gn/functions_target_rust_unittest.cc b/gn/src/gn/functions_target_rust_unittest.cc
new file mode 100644
index 00000000000..a8239aa6bac
--- /dev/null
+++ b/gn/src/gn/functions_target_rust_unittest.cc
@@ -0,0 +1,324 @@
+// Copyright 2019 The Chromium 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 "gn/config.h"
+#include "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using RustFunctionsTarget = TestWithScheduler;
+
+// Checks that the appropriate crate type is used.
+TEST_F(RustFunctionsTarget, CrateName) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "executable(\"foo\") {\n"
+ " crate_name = \"foo_crate\"\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
+ "foo_crate");
+
+ TestParseInput lib_input(
+ "executable(\"foo\") {\n"
+ " sources = [ \"lib.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ err = Err();
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
+ "foo")
+ << item_collector.back()->AsTarget()->rust_values().crate_name();
+}
+
+// Checks that the appropriate crate root is found.
+TEST_F(RustFunctionsTarget, CrateRootFind) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput normal_input(
+ "executable(\"foo\") {\n"
+ " crate_root = \"foo.rs\""
+ " sources = [ \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(normal_input.has_error());
+ Err err;
+ normal_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/foo.rs");
+
+ TestParseInput normal_shlib_input(
+ "shared_library(\"foo\") {\n"
+ " crate_root = \"foo.rs\""
+ " crate_type = \"dylib\"\n"
+ " sources = [ \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(normal_shlib_input.has_error());
+ err = Err();
+ normal_shlib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/foo.rs");
+
+ TestParseInput exe_input(
+ "executable(\"foo\") {\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ err = Err();
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/main.rs");
+
+ TestParseInput lib_input(
+ "rust_library(\"libfoo\") {\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ err = Err();
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/lib.rs");
+
+ TestParseInput singlesource_input(
+ "executable(\"bar\") {\n"
+ " sources = [ \"bar.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(singlesource_input.has_error());
+ err = Err();
+ singlesource_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/bar.rs");
+
+ TestParseInput error_input(
+ "rust_library(\"foo\") {\n"
+ " sources = [ \"foo.rs\", \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(error_input.has_error());
+ err = Err();
+ error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Missing \"crate_root\" and missing \"lib.rs\" in sources.",
+ err.message());
+
+ TestParseInput nosources_input(
+ "executable(\"bar\") {\n"
+ " crate_root = \"bar.rs\"\n"
+ "}\n");
+ ASSERT_FALSE(nosources_input.has_error());
+ err = Err();
+ nosources_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/bar.rs");
+}
+
+// Checks that the appropriate crate type is used.
+TEST_F(RustFunctionsTarget, CrateTypeSelection) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput lib_input(
+ "shared_library(\"libfoo\") {\n"
+ " crate_type = \"dylib\"\n"
+ " sources = [ \"lib.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ Err err;
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
+ RustValues::CRATE_DYLIB);
+
+ TestParseInput exe_non_default_input(
+ "executable(\"foo\") {\n"
+ " crate_type = \"rlib\"\n"
+ " sources = [ \"main.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(exe_non_default_input.has_error());
+ err = Err();
+ exe_non_default_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
+ RustValues::CRATE_RLIB);
+
+ TestParseInput lib_error_input(
+ "shared_library(\"foo\") {\n"
+ " crate_type = \"bad\"\n"
+ " sources = [ \"lib.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(lib_error_input.has_error());
+ err = Err();
+ lib_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Inadmissible crate type \"bad\".", err.message()) << err.message();
+
+ TestParseInput lib_missing_error_input(
+ "shared_library(\"foo\") {\n"
+ " sources = [ \"lib.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(lib_missing_error_input.has_error());
+ err = Err();
+ lib_missing_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Must set \"crate_type\" on a Rust \"shared_library\".",
+ err.message());
+}
+
+// Checks that the appropriate config values are propagated.
+TEST_F(RustFunctionsTarget, ConfigValues) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "config(\"foo\") {\n"
+ " rustflags = [ \"-Cdebuginfo=2\" ]\n"
+ " rustenv = [ \"RUST_BACKTRACE=1\" ]"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags().size(),
+ 1U);
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags()[0],
+ "-Cdebuginfo=2");
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv().size(),
+ 1U);
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv()[0],
+ "RUST_BACKTRACE=1");
+}
+
+// Checks that set_defaults works properly.
+TEST_F(RustFunctionsTarget, SetDefaults) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "config(\"foo\") {\n"
+ " rustflags = [ \"-Cdebuginfo=2\" ]\n"
+ " rustenv = [ \"RUST_BACKTRACE=1\" ]"
+ "}\n"
+ "set_defaults(\"rust_library\") {\n"
+ " configs = [ \":foo\" ]\n"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << err.message();
+
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->type(),
+ Value::LIST);
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->list_value()
+ .size(),
+ 1U);
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->list_value()[0]
+ .type(),
+ Value::STRING);
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->list_value()[0]
+ .string_value(),
+ ":foo");
+}
+
+// Checks aliased_deps parsing.
+TEST_F(RustFunctionsTarget, AliasedDeps) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "executable(\"foo\") {\n"
+ " sources = [ \"main.rs\" ]\n"
+ " deps = [ \"//bar\", \"//baz\" ]\n"
+ " aliased_deps = {\n"
+ " bar_renamed = \"//bar\"\n"
+ " baz_renamed = \"//baz:baz\"\n"
+ " }\n"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ item_collector.back()->AsTarget()->rust_values().aliased_deps().size(),
+ 2U);
+}
+
+TEST_F(RustFunctionsTarget, PublicConfigs) {
+ TestWithScope setup;
+
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "config(\"bar\") {\n"
+ " defines = [ \"DOOM_MELON\" ]"
+ "}\n"
+ "executable(\"foo\") {\n"
+ " crate_name = \"foo_crate\"\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ " public_configs = [ \":bar\" ]"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+}
diff --git a/gn/src/gn/functions_target_unittest.cc b/gn/src/gn/functions_target_unittest.cc
new file mode 100644
index 00000000000..04b2a4bc6d6
--- /dev/null
+++ b/gn/src/gn/functions_target_unittest.cc
@@ -0,0 +1,206 @@
+// 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 "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using FunctionsTarget = TestWithScheduler;
+
+// Checks that we find unused identifiers in targets.
+TEST_F(FunctionsTarget, CheckUnused) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+
+ // Test a good one first.
+ TestParseInput good_input(
+ "source_set(\"foo\") {\n"
+ "}\n");
+ ASSERT_FALSE(good_input.has_error());
+ Err err;
+ good_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // Test a source set with an unused variable.
+ TestParseInput source_set_input(
+ "source_set(\"foo\") {\n"
+ " unused = 5\n"
+ "}\n");
+ ASSERT_FALSE(source_set_input.has_error());
+ err = Err();
+ source_set_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+}
+
+// Checks that we find uses of identifiers marked as not needed.
+TEST_F(FunctionsTarget, CheckNotNeeded) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+
+ TestParseInput nonscoped_input(
+ "source_set(\"foo\") {\n"
+ " a = 1\n"
+ " not_needed([ \"a\" ])\n"
+ "}\n");
+ ASSERT_FALSE(nonscoped_input.has_error());
+ Err err;
+ nonscoped_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ TestParseInput scoped_input(
+ "source_set(\"foo\") {\n"
+ " a = {x = 1 y = 2}\n"
+ " not_needed(a, \"*\")\n"
+ "}\n");
+ ASSERT_FALSE(scoped_input.has_error());
+ err = Err();
+ scoped_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ TestParseInput nonexistent_arg_input(
+ "source_set(\"foo\") {\n"
+ " a = {x = 1}\n"
+ " not_needed(a, [ \"x\", \"y\" ])\n"
+ "}\n");
+ ASSERT_FALSE(nonexistent_arg_input.has_error());
+ err = Err();
+ nonexistent_arg_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ TestParseInput exclusion_input(
+ "source_set(\"foo\") {\n"
+ " x = 1\n"
+ " y = 2\n"
+ " not_needed(\"*\", [ \"y\" ])\n"
+ "}\n");
+ ASSERT_FALSE(exclusion_input.has_error());
+ err = Err();
+ exclusion_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << err.message();
+ EXPECT_EQ("Assignment had no effect.", err.message());
+
+ TestParseInput error_input(
+ "source_set(\"foo\") {\n"
+ " a = {x = 1 y = 2}\n"
+ " not_needed(a, [ \"x \"], [ \"y\" ])\n"
+ "}\n");
+ ASSERT_FALSE(error_input.has_error());
+ err = Err();
+ error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Not supported with a variable list.", err.message());
+
+ TestParseInput argcount_error_input(
+ "source_set(\"foo\") {\n"
+ " not_needed()\n"
+ "}\n");
+ ASSERT_FALSE(argcount_error_input.has_error());
+ err = Err();
+ argcount_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ TestParseInput scope_error_input(
+ "source_set(\"foo\") {\n"
+ " a = {x = 1 y = 2}\n"
+ " not_needed(a)\n"
+ "}\n");
+ ASSERT_FALSE(scope_error_input.has_error());
+ err = Err();
+ scope_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ TestParseInput string_error_input(
+ "source_set(\"foo\") {\n"
+ " not_needed(\"*\", {}, \"*\")\n"
+ "}\n");
+ ASSERT_FALSE(string_error_input.has_error());
+ err = Err();
+ string_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ TestParseInput template_input(
+ R"(# Test that not_needed() propagates through templates correctly;
+ # no error should arise from not using "a".
+ template("inner_templ") {
+ source_set(target_name) {
+ not_needed(invoker, [ "a" ])
+ }
+ }
+ template("outer_templ") {
+ inner_templ(target_name) {
+ forward_variables_from(invoker, "*")
+ }
+ }
+ outer_templ("foo") {
+ a = 1
+ })");
+ ASSERT_FALSE(template_input.has_error());
+ err = Err();
+ template_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+}
+
+// Checks that the defaults applied to a template invoked by target() use
+// the name of the template, rather than the string "target" (which is the
+// name of the actual function being called).
+TEST_F(FunctionsTarget, TemplateDefaults) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+
+ // Test a good one first.
+ TestParseInput good_input(
+ R"(# Make a template with defaults set.
+ template("my_templ") {
+ source_set(target_name) {
+ forward_variables_from(invoker, "*")
+ }
+ }
+ set_defaults("my_templ") {
+ default_value = 1
+ }
+
+ # Invoke the template with target(). This will fail to execute if the
+ # defaults were not set properly, because "default_value" won't exist.
+ target("my_templ", "foo") {
+ print(default_value)
+ })");
+ ASSERT_FALSE(good_input.has_error());
+ Err err;
+ good_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+}
+
+// Checks that we find unused identifiers in targets.
+TEST_F(FunctionsTarget, MixedSourceError) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+
+ // Test a good one first.
+ TestParseInput good_input(
+ "source_set(\"foo\") {\n"
+ " sources = [ \"cpp.cc\", \"rust.rs\" ]"
+ "}\n");
+ ASSERT_FALSE(good_input.has_error());
+ Err err;
+ good_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ ASSERT_EQ(err.message(), "More than one language used in target sources.");
+}
diff --git a/gn/src/gn/functions_unittest.cc b/gn/src/gn/functions_unittest.cc
new file mode 100644
index 00000000000..40ac5d9ced8
--- /dev/null
+++ b/gn/src/gn/functions_unittest.cc
@@ -0,0 +1,459 @@
+// 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 "gn/functions.h"
+
+#include <memory>
+#include <utility>
+
+#include "gn/parse_tree.h"
+#include "gn/test_with_scope.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+TEST(Functions, Assert) {
+ TestWithScope setup;
+
+ // Verify cases where the assertion passes.
+ std::vector<std::string> assert_pass_examples = {
+ R"gn(assert(true))gn",
+ R"gn(assert(true, "This message is ignored for passed assertions."))gn",
+ };
+ for (const auto& assert_pass_example : assert_pass_examples) {
+ TestParseInput input(assert_pass_example);
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << assert_pass_example;
+ }
+
+ // Verify case where the assertion fails, with no message.
+ {
+ TestParseInput input("assert(false)");
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ ASSERT_EQ(err.message(), "Assertion failed.");
+ }
+
+ // Verify case where the assertion fails, with a message.
+ {
+ TestParseInput input("assert(false, \"What failed\")");
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ ASSERT_EQ(err.message(), "Assertion failed.");
+ ASSERT_EQ(err.help_text(), "What failed");
+ }
+
+ // Verify usage errors are detected.
+ std::vector<std::string> bad_usage_examples = {
+ // Number of arguments.
+ R"gn(assert())gn",
+ R"gn(assert(1, 2, 3))gn",
+
+ // Argument types.
+ R"gn(assert(1))gn",
+ R"gn(assert("oops"))gn",
+ R"gn(assert(true, 1))gn",
+ R"gn(assert(true, []))gn",
+ };
+ for (const auto& bad_usage_example : bad_usage_examples) {
+ TestParseInput input(bad_usage_example);
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << bad_usage_example;
+ // We are checking for usage errors, not assertion failures.
+ ASSERT_NE(err.message(), "Assertion failed.") << bad_usage_example;
+ }
+}
+
+TEST(Functions, Defined) {
+ TestWithScope setup;
+
+ FunctionCallNode function_call;
+ Err err;
+
+ // Test an undefined identifier.
+ Token undefined_token(Location(), Token::IDENTIFIER, "undef");
+ ListNode args_list_identifier_undefined;
+ args_list_identifier_undefined.append_item(
+ std::make_unique<IdentifierNode>(undefined_token));
+ Value result = functions::RunDefined(setup.scope(), &function_call,
+ &args_list_identifier_undefined, &err);
+ ASSERT_EQ(Value::BOOLEAN, result.type());
+ EXPECT_FALSE(result.boolean_value());
+
+ // Define a value that's itself a scope value.
+ const char kDef[] = "def"; // Defined variable name.
+ setup.scope()->SetValue(
+ kDef, Value(nullptr, std::make_unique<Scope>(setup.scope())), nullptr);
+
+ // Test the defined identifier.
+ Token defined_token(Location(), Token::IDENTIFIER, kDef);
+ ListNode args_list_identifier_defined;
+ args_list_identifier_defined.append_item(
+ std::make_unique<IdentifierNode>(defined_token));
+ result = functions::RunDefined(setup.scope(), &function_call,
+ &args_list_identifier_defined, &err);
+ ASSERT_EQ(Value::BOOLEAN, result.type());
+ EXPECT_TRUE(result.boolean_value());
+
+ // Should also work by passing an accessor node so you can do
+ // "defined(def.foo)" to see if foo is defined on the def scope.
+ std::unique_ptr<AccessorNode> undef_accessor =
+ std::make_unique<AccessorNode>();
+ undef_accessor->set_base(defined_token);
+ undef_accessor->set_member(std::make_unique<IdentifierNode>(undefined_token));
+ ListNode args_list_accessor_defined;
+ args_list_accessor_defined.append_item(std::move(undef_accessor));
+ result = functions::RunDefined(setup.scope(), &function_call,
+ &args_list_accessor_defined, &err);
+ ASSERT_EQ(Value::BOOLEAN, result.type());
+ EXPECT_FALSE(result.boolean_value());
+}
+
+// Tests that an error is thrown when a {} is supplied to a function that
+// doesn't take one.
+TEST(Functions, FunctionsWithBlock) {
+ TestWithScope setup;
+ Err err;
+
+ // No scope to print() is OK.
+ TestParseInput print_no_scope("print(6)");
+ EXPECT_FALSE(print_no_scope.has_error());
+ Value result = print_no_scope.parsed()->Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Passing a scope should pass parsing (it doesn't know about what kind of
+ // function it is) and then throw an error during execution.
+ TestParseInput print_with_scope("print(foo) {}");
+ EXPECT_FALSE(print_with_scope.has_error());
+ result = print_with_scope.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+
+ // defined() is a special function so test it separately.
+ TestParseInput defined_no_scope("defined(foo)");
+ EXPECT_FALSE(defined_no_scope.has_error());
+ result = defined_no_scope.parsed()->Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // A block to defined should fail.
+ TestParseInput defined_with_scope("defined(foo) {}");
+ EXPECT_FALSE(defined_with_scope.has_error());
+ result = defined_with_scope.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(Functions, SplitList) {
+ TestWithScope setup;
+
+ TestParseInput input(
+ // Empty input with varying result items.
+ "out1 = split_list([], 1)\n"
+ "out2 = split_list([], 3)\n"
+ "print(\"empty = $out1 $out2\")\n"
+
+ // One item input.
+ "out3 = split_list([1], 1)\n"
+ "out4 = split_list([1], 2)\n"
+ "print(\"one = $out3 $out4\")\n"
+
+ // Multiple items.
+ "out5 = split_list([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)\n"
+ "print(\"many = $out5\")\n"
+
+ // Rounding.
+ "out6 = split_list([1, 2, 3, 4, 5, 6], 4)\n"
+ "print(\"rounding = $out6\")\n");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ "empty = [[]] [[], [], []]\n"
+ "one = [[1]] [[1], []]\n"
+ "many = [[1, 2, 3, 4, 5], [6, 7, 8, 9]]\n"
+ "rounding = [[1, 2], [3, 4], [5], [6]]\n",
+ setup.print_output());
+}
+
+TEST(Functions, StringJoin) {
+ TestWithScope setup;
+
+ // Verify outputs when string_join() is called correctly.
+ {
+ TestParseInput input(R"gn(
+ # No elements in the list and empty separator.
+ print("<" + string_join("", []) + ">")
+
+ # No elements in the list.
+ print("<" + string_join(" ", []) + ">")
+
+ # One element in the list.
+ print(string_join("|", ["a"]))
+
+ # Multiple elements in the list.
+ print(string_join(" ", ["a", "b", "c"]))
+
+ # Multi-character separator.
+ print(string_join("-.", ["a", "b", "c"]))
+
+ # Empty separator.
+ print(string_join("", ["x", "y", "z"]))
+
+ # Empty string list elements.
+ print(string_join("x", ["", "", ""]))
+
+ # Empty string list elements and separator
+ print(string_join("", ["", "", ""]))
+ )gn");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ "<>\n"
+ "<>\n"
+ "a\n"
+ "a b c\n"
+ "a-.b-.c\n"
+ "xyz\n"
+ "xx\n"
+ "\n",
+ setup.print_output()) << setup.print_output();
+ }
+
+ // Verify usage errors are detected.
+ std::vector<std::string> bad_usage_examples = {
+ // Number of arguments.
+ R"gn(string_join())gn",
+ R"gn(string_join(["oops"]))gn",
+ R"gn(string_join("kk", [], "oops"))gn",
+
+ // Argument types.
+ R"gn(string_join(1, []))gn",
+ R"gn(string_join("kk", "oops"))gn",
+ R"gn(string_join(["oops"], []))gn",
+
+ // Non-string elements in list of strings.
+ R"gn(string_join("kk", [1]))gn",
+ R"gn(string_join("kk", ["hello", 1]))gn",
+ R"gn(string_join("kk", ["hello", []]))gn",
+ };
+ for (const auto& bad_usage_example : bad_usage_examples) {
+ TestParseInput input(bad_usage_example);
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << bad_usage_example;
+ }
+}
+
+TEST(Functions, StringReplace) {
+ TestWithScope setup;
+
+ TestParseInput input(
+ // Replace all occurrences of string.
+ "out1 = string_replace(\"abbcc\", \"b\", \"d\")\n"
+ "print(out1)\n"
+
+ // Replace only the first occurrence.
+ "out2 = string_replace(\"abbcc\", \"b\", \"d\", 1)\n"
+ "print(out2)\n"
+
+ // Duplicate string to be replaced.
+ "out3 = string_replace(\"abbcc\", \"b\", \"bb\")\n"
+ "print(out3)\n"
+
+ // Handle overlapping occurrences.
+ "out4 = string_replace(\"aaa\", \"aa\", \"b\")\n"
+ "print(out4)\n");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ "addcc\n"
+ "adbcc\n"
+ "abbbbcc\n"
+ "ba\n",
+ setup.print_output());
+}
+
+TEST(Functions, StringSplit) {
+ TestWithScope setup;
+
+ // Verify outputs when string_join() is called correctly.
+ {
+ TestParseInput input(R"gn(
+ # Split on all whitespace: empty string.
+ print(string_split(""))
+
+ # Split on all whitespace: string is only whitespace
+ print(string_split(" "))
+
+ # Split on all whitespace: leading, trailing, runs; one element.
+ print(string_split("hello"))
+ print(string_split(" hello"))
+ print(string_split(" hello "))
+ print(string_split("hello "))
+
+ # Split on all whitespace: leading, trailing, runs; multiple elements.
+ print(string_split("a b")) # Pre-stripped
+ print(string_split(" a b")) # Leading whitespace
+ print(string_split(" a b ")) # Leading & trailing whitespace
+ print(string_split("a b ")) # Trailing whitespace
+ print(string_split("a b ")) # Whitespace run between words
+ print(string_split(" a b cc ddd")) # More & multi-character elements
+
+ # Split on string.
+ print(string_split("", "|")) # Empty string
+ print(string_split("|", "|")) # Only a separator
+ print(string_split("||", "|")) # Only separators
+ print(string_split("ab", "|")) # String is missing separator
+ print(string_split("a|b", "|")) # Two elements
+ print(string_split("|a|b", "|")) # Leading separator
+ print(string_split("a|b|", "|")) # Trailing separator
+ print(string_split("||x", "|")) # Leading consecutive separators
+ print(string_split("x||", "|")) # Trailing consecutive separators
+ print(string_split("a|bb|ccc", "|")) # Multiple elements
+ print(string_split(".x.x.x.", ".x.")) # Self-overlapping separators 1
+ print(string_split("x.x.x.", ".x.")) # Self-overlapping separators 2
+ )gn");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ // Split on all whitespace: empty string.
+ "[]\n"
+
+ // Split on all whitespace: string is only whitespace.
+ "[]\n"
+
+ // Split on all whitespace: leading, trailing, runs; one element.
+ "[\"hello\"]\n"
+ "[\"hello\"]\n"
+ "[\"hello\"]\n"
+ "[\"hello\"]\n"
+
+ // Split on all whitespace: leading, trailing, runs; multiple elements.
+ "[\"a\", \"b\"]\n"
+ "[\"a\", \"b\"]\n"
+ "[\"a\", \"b\"]\n"
+ "[\"a\", \"b\"]\n"
+ "[\"a\", \"b\"]\n"
+ "[\"a\", \"b\", \"cc\", \"ddd\"]\n"
+
+ // Split on string.
+ "[\"\"]\n" // Empty string (like Python)
+ "[\"\", \"\"]\n" // Only a separator
+ "[\"\", \"\", \"\"]\n" // Only separators
+ "[\"ab\"]\n" // String is missing separator
+ "[\"a\", \"b\"]\n" // Two elements
+ "[\"\", \"a\", \"b\"]\n" // Leading
+ "[\"a\", \"b\", \"\"]\n" // Trailing
+ "[\"\", \"\", \"x\"]\n" // Leading consecutive separators
+ "[\"x\", \"\", \"\"]\n" // Trailing consecutive separators
+ "[\"a\", \"bb\", \"ccc\"]\n" // Multiple elements
+ "[\"\", \"x\", \"\"]\n" // Self-overlapping separators 1
+ "[\"x\", \"x.\"]\n" // Self-overlapping separators 2
+ ,
+ setup.print_output()) << setup.print_output();
+ }
+
+ // Verify usage errors are detected.
+ std::vector<std::string> bad_usage_examples = {
+ // Number of arguments.
+ R"gn(string_split())gn",
+ R"gn(string_split("a", "b", "c"))gn",
+
+ // Argument types.
+ R"gn(string_split(1))gn",
+ R"gn(string_split(["oops"]))gn",
+ R"gn(string_split("kk", 1))gn",
+ R"gn(string_split("kk", ["oops"]))gn",
+
+ // Empty separator argument.
+ R"gn(string_split("kk", ""))gn",
+ };
+ for (const auto& bad_usage_example : bad_usage_examples) {
+ TestParseInput input(bad_usage_example);
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error()) << bad_usage_example;
+ }
+}
+
+TEST(Functions, DeclareArgs) {
+ TestWithScope setup;
+ Err err;
+
+ // It is not legal to read the value of an argument declared in a
+ // declare_args() from inside the call, but outside the call and in
+ // a separate call should work.
+
+ TestParseInput reading_from_same_call(R"(
+ declare_args() {
+ foo = true
+ bar = foo
+ })");
+ reading_from_same_call.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+
+ TestParseInput reading_from_outside_call(R"(
+ declare_args() {
+ foo = true
+ }
+
+ bar = foo
+ assert(bar)
+ )");
+ err = Err();
+ reading_from_outside_call.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error());
+
+ TestParseInput reading_from_different_call(R"(
+ declare_args() {
+ foo = true
+ }
+
+ declare_args() {
+ bar = foo
+ }
+
+ assert(bar)
+ )");
+ err = Err();
+ TestWithScope setup2;
+ reading_from_different_call.parsed()->Execute(setup2.scope(), &err);
+ ASSERT_FALSE(err.has_error());
+}
+
+TEST(Functions, NotNeeded) {
+ TestWithScope setup;
+
+ TestParseInput input("not_needed({ a = 1 }, \"*\")");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error())
+ << err.message() << err.location().Describe(true);
+}
diff --git a/gn/src/gn/general_tool.cc b/gn/src/gn/general_tool.cc
new file mode 100644
index 00000000000..fc8878db527
--- /dev/null
+++ b/gn/src/gn/general_tool.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium 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 "gn/general_tool.h"
+#include "gn/target.h"
+
+const char* GeneralTool::kGeneralToolStamp = "stamp";
+const char* GeneralTool::kGeneralToolCopy = "copy";
+const char* GeneralTool::kGeneralToolCopyBundleData = "copy_bundle_data";
+const char* GeneralTool::kGeneralToolCompileXCAssets = "compile_xcassets";
+const char* GeneralTool::kGeneralToolAction = "action";
+
+GeneralTool::GeneralTool(const char* n) : Tool(n) {
+ CHECK(ValidateName(n));
+}
+
+GeneralTool::~GeneralTool() = default;
+
+GeneralTool* GeneralTool::AsGeneral() {
+ return this;
+}
+const GeneralTool* GeneralTool::AsGeneral() const {
+ return this;
+}
+
+bool GeneralTool::ValidateName(const char* name) const {
+ return name == kGeneralToolStamp || name == kGeneralToolCopy ||
+ name == kGeneralToolCopyBundleData ||
+ name == kGeneralToolCompileXCAssets || name == kGeneralToolAction;
+}
+
+void GeneralTool::SetComplete() {
+ SetToolComplete();
+}
+
+bool GeneralTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ return Tool::InitTool(scope, toolchain, err);
+}
+
+bool GeneralTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kGeneralToolStamp || name_ == kGeneralToolAction)
+ return IsValidToolSubstitution(sub_type);
+ else if (name_ == kGeneralToolCopy || name_ == kGeneralToolCopyBundleData)
+ return IsValidCopySubstitution(sub_type);
+ else if (name_ == kGeneralToolCompileXCAssets)
+ return IsValidCompileXCassetsSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
diff --git a/gn/src/gn/general_tool.h b/gn/src/gn/general_tool.h
new file mode 100644
index 00000000000..eb16b2c241b
--- /dev/null
+++ b/gn/src/gn/general_tool.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_GENERAL_TOOL_H_
+#define TOOLS_GN_GENERAL_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/label.h"
+#include "gn/label_ptr.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+#include "gn/tool.h"
+
+class GeneralTool : public Tool {
+ public:
+ // General tools
+ static const char* kGeneralToolStamp;
+ static const char* kGeneralToolCopy;
+ static const char* kGeneralToolAction;
+
+ // Platform-specific tools
+ static const char* kGeneralToolCopyBundleData;
+ static const char* kGeneralToolCompileXCAssets;
+
+ GeneralTool(const char* n);
+ ~GeneralTool();
+
+ // Manual RTTI and required functions ---------------------------------------
+
+ bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+ bool ValidateName(const char* name) const override;
+ void SetComplete() override;
+ bool ValidateSubstitution(const Substitution* sub_type) const override;
+
+ GeneralTool* AsGeneral() override;
+ const GeneralTool* AsGeneral() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GeneralTool);
+};
+
+#endif // TOOLS_GN_GENERAL_TOOL_H_
diff --git a/gn/src/gn/generated_file_target_generator.cc b/gn/src/gn/generated_file_target_generator.cc
new file mode 100644
index 00000000000..16ac63991a2
--- /dev/null
+++ b/gn/src/gn/generated_file_target_generator.cc
@@ -0,0 +1,165 @@
+// Copyright 2018 The Chromium 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 "gn/generated_file_target_generator.h"
+
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/target.h"
+#include "gn/variables.h"
+
+GeneratedFileTargetGenerator::GeneratedFileTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
+
+GeneratedFileTargetGenerator::~GeneratedFileTargetGenerator() = default;
+
+void GeneratedFileTargetGenerator::DoRun() {
+ target_->set_output_type(output_type_);
+
+ if (!FillOutputs(false))
+ return;
+ if (target_->action_values().outputs().list().size() != 1) {
+ *err_ = Err(
+ function_call_, "generated_file target must have exactly one output.",
+ "You must specify exactly one value in the \"outputs\" array for the "
+ "destination of the write\n(see \"gn help generated_file\").");
+ return;
+ }
+
+ if (!FillContents())
+ return;
+ if (!FillDataKeys())
+ return;
+
+ // One of data and data_keys should be defined.
+ if (!contents_defined_ && !data_keys_defined_) {
+ *err_ = Err(
+ function_call_, "Either contents or data_keys should be set.",
+ "The generated_file target requires either the \"contents\" variable "
+ "or the \"data_keys\" variable be set. See \"gn help "
+ "generated_file\".");
+ return;
+ }
+
+ if (!FillRebase())
+ return;
+ if (!FillWalkKeys())
+ return;
+
+ if (!FillOutputConversion())
+ return;
+}
+
+bool GeneratedFileTargetGenerator::FillContents() {
+ const Value* value = scope_->GetValue(variables::kWriteValueContents, true);
+ if (!value)
+ return true;
+ target_->set_contents(*value);
+ contents_defined_ = true;
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::IsMetadataCollectionTarget(
+ const std::string_view& variable,
+ const ParseNode* origin) {
+ if (contents_defined_) {
+ *err_ =
+ Err(origin, std::string(variable) + " won't be used.",
+ "\"contents\" is defined on this target, and so setting " +
+ std::string(variable) +
+ " will have no effect as no metdata collection will occur.");
+ return false;
+ }
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillOutputConversion() {
+ const Value* value =
+ scope_->GetValue(variables::kWriteOutputConversion, true);
+ if (!value) {
+ target_->set_output_conversion(Value(function_call_, ""));
+ return true;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ // Otherwise, the value itself will be checked when the conversion is done.
+ target_->set_output_conversion(*value);
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillRebase() {
+ const Value* value = scope_->GetValue(variables::kRebase, true);
+ if (!value)
+ return true;
+ if (!IsMetadataCollectionTarget(variables::kRebase, value->origin()))
+ return false;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Treat empty string as the default and do nothing.
+
+ const BuildSettings* build_settings = scope_->settings()->build_settings();
+ SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
+ *value, err_, build_settings->root_path_utf8());
+ if (err_->has_error())
+ return false;
+
+ target_->set_rebase(dir);
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillDataKeys() {
+ const Value* value = scope_->GetValue(variables::kDataKeys, true);
+ if (!value)
+ return true;
+ if (!IsMetadataCollectionTarget(variables::kDataKeys, value->origin()))
+ return false;
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ for (const Value& v : value->list_value()) {
+ // Keys must be strings.
+ if (!v.VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->data_keys().push_back(v.string_value());
+ }
+
+ data_keys_defined_ = true;
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillWalkKeys() {
+ const Value* value = scope_->GetValue(variables::kWalkKeys, true);
+ // If we define this and contents, that's an error.
+ if (value &&
+ !IsMetadataCollectionTarget(variables::kWalkKeys, value->origin()))
+ return false;
+
+ // If we don't define it, we want the default value which is a list
+ // containing the empty string.
+ if (!value) {
+ target_->walk_keys().push_back("");
+ return true;
+ }
+
+ // Otherwise, pull and validate the specified value.
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+ for (const Value& v : value->list_value()) {
+ // Keys must be strings.
+ if (!v.VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->walk_keys().push_back(v.string_value());
+ }
+ return true;
+}
diff --git a/gn/src/gn/generated_file_target_generator.h b/gn/src/gn/generated_file_target_generator.h
new file mode 100644
index 00000000000..dcc481408b3
--- /dev/null
+++ b/gn/src/gn/generated_file_target_generator.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
+#define TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target.h"
+#include "gn/target_generator.h"
+
+// Collects and writes specified data.
+class GeneratedFileTargetGenerator : public TargetGenerator {
+ public:
+ GeneratedFileTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err);
+ ~GeneratedFileTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ bool FillGeneratedFileOutput();
+ bool FillOutputConversion();
+ bool FillContents();
+ bool FillDataKeys();
+ bool FillWalkKeys();
+ bool FillRebase();
+
+ // Returns false if `contents` is defined (i.e. if this target was provided
+ // with explicit contents to write). Returns false otherwise, indicating that
+ // it is okay to set metadata collection variables on this target.
+ //
+ // Should be called before FillContents().
+ bool IsMetadataCollectionTarget(const std::string_view& variable,
+ const ParseNode* origin);
+
+ bool contents_defined_ = false;
+ bool data_keys_defined_ = false;
+
+ Target::OutputType output_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(GeneratedFileTargetGenerator);
+};
+
+#endif // TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/gn_main.cc b/gn/src/gn/gn_main.cc
new file mode 100644
index 00000000000..e839b322d38
--- /dev/null
+++ b/gn/src/gn/gn_main.cc
@@ -0,0 +1,85 @@
+// 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 <algorithm>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/commands.h"
+#include "gn/err.h"
+#include "gn/location.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "util/build_config.h"
+#include "util/msg_loop.h"
+#include "util/sys_info.h"
+
+#include "last_commit_position.h"
+
+namespace {
+
+std::vector<std::string> GetArgs(const base::CommandLine& cmdline) {
+ base::CommandLine::StringVector in_args = cmdline.GetArgs();
+#if defined(OS_WIN)
+ std::vector<std::string> out_args;
+ for (const auto& arg : in_args)
+ out_args.push_back(base::UTF16ToUTF8(arg));
+ return out_args;
+#else
+ return in_args;
+#endif
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+#if defined(OS_WIN)
+ base::CommandLine::set_slash_is_not_a_switch();
+#endif
+ base::CommandLine::Init(argc, argv);
+
+ const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
+ std::vector<std::string> args = GetArgs(cmdline);
+
+ std::string command;
+ if (cmdline.HasSwitch("help") || cmdline.HasSwitch("h")) {
+ // Make "-h" and "--help" default to help command.
+ command = commands::kHelp;
+ } else if (cmdline.HasSwitch(switches::kVersion)) {
+ // Make "--version" print the version and exit.
+ OutputString(std::string(LAST_COMMIT_POSITION) + "\n");
+ exit(0);
+ } else if (args.empty()) {
+ // No command, print error and exit.
+ Err(Location(), "No command specified.",
+ "Most commonly you want \"gn gen <out_dir>\" to make a build dir.\n"
+ "Or try \"gn help\" for more commands.")
+ .PrintToStdout();
+ return 1;
+ } else {
+ command = args[0];
+ args.erase(args.begin());
+ }
+
+ const commands::CommandInfoMap& command_map = commands::GetCommands();
+ commands::CommandInfoMap::const_iterator found_command =
+ command_map.find(command);
+
+ int retval;
+ if (found_command != command_map.end()) {
+ MsgLoop msg_loop;
+ retval = found_command->second.runner(args);
+ } else {
+ Err(Location(), "Command \"" + command + "\" unknown.").PrintToStdout();
+ OutputString(
+ "Available commands (type \"gn help <command>\" for more details):\n");
+ for (const auto& cmd : commands::GetCommands())
+ PrintShortHelp(cmd.second.help_short);
+
+ retval = 1;
+ }
+
+ exit(retval); // Don't free memory, it can be really slow!
+}
diff --git a/gn/src/gn/group_target_generator.cc b/gn/src/gn/group_target_generator.cc
new file mode 100644
index 00000000000..2b9106669ca
--- /dev/null
+++ b/gn/src/gn/group_target_generator.cc
@@ -0,0 +1,23 @@
+// 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 "gn/group_target_generator.h"
+
+#include "gn/target.h"
+#include "gn/variables.h"
+
+GroupTargetGenerator::GroupTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err) {}
+
+GroupTargetGenerator::~GroupTargetGenerator() = default;
+
+void GroupTargetGenerator::DoRun() {
+ target_->set_output_type(Target::GROUP);
+ // Groups only have the default types filled in by the target generator
+ // base class.
+}
diff --git a/gn/src/gn/group_target_generator.h b/gn/src/gn/group_target_generator.h
new file mode 100644
index 00000000000..98459290391
--- /dev/null
+++ b/gn/src/gn/group_target_generator.h
@@ -0,0 +1,27 @@
+// 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 TOOLS_GN_GROUP_TARGET_GENERATOR_H_
+#define TOOLS_GN_GROUP_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target_generator.h"
+
+// Populates a Target with the values for a group rule.
+class GroupTargetGenerator : public TargetGenerator {
+ public:
+ GroupTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ ~GroupTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GroupTargetGenerator);
+};
+
+#endif // TOOLS_GN_GROUP_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/hash_table_base.h b/gn/src/gn/hash_table_base.h
new file mode 100644
index 00000000000..22fcaea7663
--- /dev/null
+++ b/gn/src/gn/hash_table_base.h
@@ -0,0 +1,503 @@
+// Copyright (c) 2020 The Chromium 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 TOOLS_GN_HASH_TABLE_BASE_H_
+#define TOOLS_GN_HASH_TABLE_BASE_H_
+
+#include "base/compiler_specific.h"
+
+#include <stdlib.h>
+#include <type_traits>
+#include <utility>
+
+// IMPORTANT DISCLAIMER:
+//
+// THIS IS *NOT* A GENERAL PURPOSE HASH TABLE TEMPLATE. INSTEAD, IT CAN
+// CAN BE USED AS THE BASIS FOR VERY HIGH SPEED AND COMPACT HASH TABLES
+// THAT OBEY VERY STRICT CONDITIONS DESCRIBED BELOW.
+//
+// DO NOT USE THIS UNLESS YOU HAVE A GOOD REASON, I.E. THAT PROFILING
+// SHOWS THERE *IS* AN OVERALL BENEFIT TO DO SO. FOR MOST CASES,
+// std::set<>, std::unordered_set<> and base::flat_set<> ARE PERFECTLY
+// FINE AND SHOULD BE PREFERRED.
+//
+//
+// That being said, this implementation uses a completely typical
+// open-addressing scheme with a buckets array size which is always
+// a power of 2, instead of a prime number. Experience shows this is
+// not detrimental to performance as long as you have a sufficiently
+// good hash function (which is the case of all C++ standard libraries
+// these days for strings and pointers).
+//
+// The reason it is so fast is due to its compactness and the very
+// simple but tight code for a typical lookup / insert / deletion
+// operation.
+//
+// The |buckets_| field holds a pointer to an array of NODE_TYPE
+// instances, called nodes. Each node represents one of the following:
+// a free entry in the table, an inserted item, or a tombstone marking
+// the location of a previously deleted item. Tombstones are only
+// used if the hash table instantiation requires deletion support
+// (see the is_tombstone() description below).
+//
+// The template requires that Node be a type with the following traits:
+//
+// - It *must* be a trivial type, that is zero-initialized.
+//
+// - It provides an is_null() const method, which should return true
+// iff the corresponding node matches a 'free' entry in the hash
+// table, i.e. one that has not been assigned to an item, or
+// to a deletion tombstone (see below).
+//
+// Of course, a default (zeroed) value, should always return true.
+//
+// - It provides an is_tombstone() const method, which should return
+// return true iff the corresponding node indicates a previously
+// deleted item.
+//
+// Note that if your hash table does not need deletion support,
+// it is highly recommended to make this a static constexpr method
+// that always return false. Doing so will optimize the lookup loop
+// automatically!
+//
+// - It must provide an is_valid() method, that simply returns
+// (!is_null() && !is_tombstone()). This is a convenience for this
+// template, but also for the derived class that will extend it
+// (more on this below, in the usage description).
+//
+// Note that because Node instances are trivial, std::unique_ptr<>
+// cannot be used in them. Item lifecycle must this be managed
+// explicitly by a derived class of the template instantiation
+// instead.
+//
+// Lookup, insertion and deletion are performed in ways that
+// are *very* different from standard containers, and the reason
+// is, unsuprisingly, performance.
+//
+// Use NodeLookup() to look for an existing item in the hash table.
+// This takes the item's hash value, and a templated callable to
+// compare a node against the search key. This scheme allows
+// heterogeneous lookups, and keeps the node type details
+// out of this header. Any recent C++ optimizer will generate
+// very tight machine code for this call.
+//
+// The NodeLookup() function always returns a non-null and
+// mutable |node| pointer. If |node->is_valid()| is true,
+// then the item was found, and |node| points to it.
+//
+// Otherwise, |node| corresponds to a location that may be
+// used for insertion. To do so, the caller should update the
+// content of |node| appropriately (e.g. writing a pointer and/or
+// hash value to it), then call UpdateAfterInsertion(), which
+// may eventually grow the table and rehash nodes in it.
+//
+// To delete an item, call NodeLookup() first to verify that
+// the item is present, then write a tombstone value to |node|,
+// then call UpdateAfterDeletion().
+//
+// Note that what the tombstone value is doesn't matter to this
+// header, as long as |node->is_tombstone()| returns true for
+// it.
+//
+// Here's an example of a concrete implementation that stores
+// a hash value and an owning pointer in each node:
+//
+// struct MyFooNode {
+// size_t hash;
+// Foo* foo;
+//
+// bool is_null() const { return !foo; }
+// bool is_tombstone() const { return foo == &kTombstone; }
+// bool is_valid() const { return !is_null() && !is_tombstone(); }
+//
+// static const Foo kTombstone;
+// };
+//
+// class MyFooSet : public HashTableBase<MyFoodNode> {
+// public:
+// using BaseType = HashTableBase<MyFooNode>;
+// using Node = BaseType::Node;
+//
+// ~MyFooSet() {
+// // Destroy all items in the set.
+// // Note that the iterator only parses over valid items.
+// for (Node* node : *this) {
+// delete node->foo;
+// }
+// }
+//
+// // Returns true iff this set contains |key|.
+// bool contains(const Foo& key) const {
+// Node* node = BaseType::NodeLookup(
+// MakeHash(key),
+// [&](const Node* node) { return node->foo == key; });
+//
+// return node->is_valid();
+// }
+//
+// // Try to add |key| to the set. Return true if the insertion
+// // was succesful, or false if the item was already in the set.
+// bool add(const Foo& key) {
+// size_t hash = MakeHash(key);
+// Node* node = NodeLookup(
+// hash,
+// [&](const Node* node) { return node->foo == key; });
+//
+// if (node->is_valid()) {
+// // Already in the set.
+// return false;
+// }
+//
+// // Add it.
+// node->hash = hash;
+// node->foo = new Foo(key);
+// UpdateAfterInsert();
+// return true;
+// }
+//
+// // Try to remove |key| from the set. Return true if the
+// // item was already in it, false otherwise.
+// bool erase(const Foo& key) {
+// Node* node = BaseType::NodeLookup(
+// MakeHash(key),
+// [&](const Node* node) { return node->foo == key; });
+//
+// if (!node->is_valid()) {
+// // Not in the set.
+// return false;
+// }
+//
+// delete node->foo;
+// node->foo = Node::kTombstone;
+// UpdateAfterDeletion().
+// }
+//
+// static size_t MakeHash(const Foo& foo) {
+// return std::hash<Foo>()();
+// }
+// };
+//
+// For more concrete examples, see the implementation of
+// StringAtom or UniqueVector<>
+//
+template <typename NODE_TYPE>
+class HashTableBase {
+ public:
+ using Node = NODE_TYPE;
+
+ static_assert(std::is_trivial<NODE_TYPE>::value,
+ "KEY_TYPE should be a trivial type!");
+
+ static_assert(std::is_standard_layout<NODE_TYPE>::value,
+ "KEY_TYPE should be a standard layout type!");
+
+ // Default constructor.
+ HashTableBase() = default;
+
+ // Destructor. This only deals with the |buckets_| array itself.
+ ~HashTableBase() {
+ if (buckets_ != buckets0_)
+ free(buckets_);
+ }
+
+ // Copy and move operations. These only operate on the |buckets_| array
+ // so any owned pointer inside nodes should be handled by custom
+ // constructors and operators in the derived class, if needed.
+ HashTableBase(const HashTableBase& other)
+ : count_(other.count_), size_(other.size_) {
+ if (other.buckets_ != other.buckets0_) {
+ // NOTE: using malloc() here to clarify that no object construction
+ // should occur here.
+ buckets_ = reinterpret_cast<Node*>(malloc(other.size_ * sizeof(Node)));
+ }
+ memcpy(buckets_, other.buckets_, other.size_ * sizeof(Node));
+ }
+
+ HashTableBase& operator=(const HashTableBase& other) {
+ if (this != &other) {
+ this->~HashTableBase();
+ new (this) HashTableBase(other);
+ }
+ return *this;
+ }
+
+ HashTableBase(HashTableBase&& other) noexcept
+ : count_(other.count_), size_(other.size_), buckets_(other.buckets_) {
+ if (buckets_ == other.buckets0_) {
+ buckets0_[0] = other.buckets0_[0];
+ buckets_ = buckets0_;
+ } else {
+ other.buckets_ = other.buckets0_;
+ }
+ other.NodeClear();
+ }
+
+ HashTableBase& operator=(HashTableBase&& other) noexcept {
+ if (this != &other) {
+ this->~HashTableBase();
+ new (this) HashTableBase(std::move(other));
+ }
+ return *this;
+ }
+
+ // Return true iff the table is empty.
+ bool empty() const { return count_ == 0; }
+
+ // Return the number of keys in the set.
+ size_t size() const { return count_; }
+
+ protected:
+ // The following should only be called by derived classes that
+ // extend this template class, and are not available to their
+ // clients. This forces the derived class to implement lookup
+ // insertion and deletion with sane APIs instead.
+
+ // Iteration support note:
+ //
+ // Derived classes, if they wish to support iteration, should provide their
+ // own iterator/const_iterator/begin()/end() types and methods, possibly as
+ // simple wrappers around the NodeIterator/NodeBegin/NodeEnd below.
+ //
+ // The ValidNodesRange() method also returns a object that has begin() and
+ // end() methods to be used in for-range loops as in:
+ //
+ // for (Node& node : my_table.ValidNodesRange()) { ... }
+ //
+ struct NodeIterator {
+ Node& operator*() { return *node_; }
+ const Node& operator*() const { return *node_; }
+
+ Node* operator->() { return node_; }
+ const Node* operator->() const { return node_; }
+
+ bool operator==(const NodeIterator& other) const {
+ return node_ == other.node_;
+ }
+
+ bool operator!=(const NodeIterator& other) const {
+ return node_ != other.node_;
+ }
+
+ // pre-increment
+ NodeIterator& operator++() {
+ node_++;
+ while (node_ < node_limit_ && !node_->is_valid())
+ node_++;
+
+ return *this;
+ }
+
+ // post-increment
+ NodeIterator operator++(int) {
+ NodeIterator result = *this;
+ ++(*this);
+ return result;
+ }
+
+ Node* node_ = nullptr;
+ Node* node_limit_ = nullptr;
+ };
+
+ // NOTE: This is intentionally not public to avoid exposing
+ // them in derived classes by mistake. If a derived class
+ // wants to support iteration, it provide its own begin() and end()
+ // methods, possibly using NodeBegin() and NodeEnd() below to
+ // implement them.
+ NodeIterator begin() { return NodeBegin(); }
+ NodeIterator end() { return NodeEnd(); }
+
+ // Providing methods named NodeBegin() and NodeEnd(), instead of
+ // just using begin() and end() is a convenience to derived classes
+ // that need to provide their own begin() and end() method, e.g.
+ // consider this class:
+ //
+ // struct FooNode {
+ // size_t hash;
+ // Foo* foo_ptr;
+ // ...
+ // };
+ //
+ // class FooTable : public HashTableBase<FooNode> {
+ // public:
+ // ...
+ //
+ // // Iterators point to Foo instances, not table nodes.
+ // struct ConstIterator {
+ // const Foo* operator->() { return iter_->foo_ptr; }
+ // const Foo& operator->() { return *(iter_->foo_ptr); }
+ // ...
+ // NodeIterator iter_;
+ // };
+ //
+ // and compare two ways to implement its begin() method:
+ //
+ // Foo::ConstIterator Foo::begin() const {
+ // return {
+ // reinterpret_cast<const HashTableBase<FooNode> *>(this)->begin()
+ // };
+ // };
+ //
+ // with:
+ //
+ // Foo::ConstIterator Foo::begin() const {
+ // return { NodeBegin(); }
+ // }
+ //
+ NodeIterator NodeBegin() const {
+ Node* node = buckets_;
+ Node* limit = node + size_;
+ while (node < limit && !node->is_valid())
+ node++;
+
+ return {node, limit};
+ }
+
+ NodeIterator NodeEnd() const {
+ Node* limit = buckets_ + size_;
+ return {limit, limit};
+ }
+
+ // ValidNodeRange() allows a derived-class to use range-based loops
+ // over valid nodes, even if they have defined their own begin() and
+ // end() methods, e.g. following the same class design as in the
+ // above comment:
+ //
+ // FooTable::~FooTable() {
+ // for (const FooNode& node : ValidNodesRange()) {
+ // delete node->foo_ptr;
+ // }
+ // }
+ //
+ // which is a little bit clearer than the (valid) alternative:
+ //
+ // FooTable::~FooTable() {
+ // for (Foo& foo : *this) {
+ // delete &foo; // WHAT!?
+ // }
+ // }
+ //
+ struct NodeIteratorPair {
+ NodeIterator begin() { return begin_; }
+ NodeIterator end() { return end_; }
+
+ NodeIterator begin_;
+ NodeIterator end_;
+ };
+
+ NodeIteratorPair ValidNodesRange() const { return {NodeBegin(), NodeEnd()}; }
+
+ // Clear the nodes table completely.
+ void NodeClear() {
+ if (buckets_ != buckets0_)
+ free(buckets_);
+
+ count_ = 0;
+ size_ = 1;
+ buckets_ = buckets0_;
+ buckets0_[0] = Node{};
+ }
+
+ // Lookup for a node in the hash table that matches the |node_equal|
+ // predicate, which takes a const Node* pointer argument, and returns
+ // true iff this corresponds to a lookup match.
+ //
+ // IMPORTANT: |node_equal| may or may not check the node hash value,
+ // the choice is left to the implementation.
+ //
+ // Returns a non-null *mutable* node pointer. |node->is_valid()| will
+ // be true for matches, and false for misses.
+ //
+ // NOTE: In case of a miss, this will return the location of the first
+ // tombstone encountered during probing, if any, or the first free entry
+ // otherwise. This keeps the table consistent in case the node is overwritten
+ // by the caller in a following insert operation.
+ template <typename NODE_EQUAL>
+ Node* NodeLookup(size_t hash, NODE_EQUAL node_equal) const {
+ size_t mask = size_ - 1;
+ size_t index = hash & mask;
+ Node* tombstone = nullptr; // First tombstone node found, if any.
+ for (;;) {
+ Node* node = buckets_ + index;
+ if (node->is_null()) {
+ return tombstone ? tombstone : node;
+ }
+ if (node->is_tombstone()) {
+ if (!tombstone)
+ tombstone = node;
+ } else if (node_equal(node)) {
+ return node;
+ }
+ index = (index + 1) & mask;
+ }
+ }
+
+ // Call this method after updating the content of the |node| pointer
+ // returned by an unsucessful NodeLookup(). Return true to indicate that
+ // the table size changed, and that existing iterators were invalidated.
+ bool UpdateAfterInsert() {
+ count_ += 1;
+ if (UNLIKELY(count_ * 4 >= size_ * 3)) {
+ GrowBuckets();
+ return true;
+ }
+ return false;
+ }
+
+ // Call this method after updating the content of the |node| value
+ // returned a by succesful NodeLookup, to the tombstone value, if any.
+ // Return true to indicate a table size change, ie. that existing
+ // iterators where invalidated.
+ bool UpdateAfterRemoval() {
+ count_ -= 1;
+ // For now don't support shrinking since this is not useful for GN.
+ return false;
+ }
+
+ private:
+#if defined(__GNUC__) || defined(__clang__)
+ [[gnu::noinline]]
+#endif
+ void
+ GrowBuckets() {
+ size_t size = size_;
+ size_t new_size = (size == 1) ? 8 : size * 2;
+ size_t new_mask = new_size - 1;
+
+ // NOTE: Using calloc() since no object constructiopn can or should take
+ // place here.
+ Node* new_buckets = reinterpret_cast<Node*>(calloc(new_size, sizeof(Node)));
+
+ for (size_t src_index = 0; src_index < size; ++src_index) {
+ const Node* node = &buckets_[src_index];
+ if (!node->is_valid())
+ continue;
+ size_t dst_index = node->hash_value() & new_mask;
+ for (;;) {
+ Node* node2 = new_buckets + dst_index;
+ if (node2->is_null()) {
+ *node2 = *node;
+ break;
+ }
+ dst_index = (dst_index + 1) & new_mask;
+ }
+ }
+
+ if (buckets_ != buckets0_)
+ free(buckets_);
+
+ buckets_ = new_buckets;
+ buckets0_[0] = Node{};
+ size_ = new_size;
+ }
+
+ // NOTE: The reason for default-initializing |buckets_| to a storage slot
+ // inside the object is to ensure the value is never null. This removes one
+ // nullptr check from each NodeLookup() instantiation.
+ size_t count_ = 0;
+ size_t size_ = 1;
+ Node* buckets_ = buckets0_;
+ Node buckets0_[1] = {{}};
+};
+
+#endif // TOOLS_GN_HASH_TABLE_BASE_H_
diff --git a/gn/src/gn/hash_table_base_unittest.cc b/gn/src/gn/hash_table_base_unittest.cc
new file mode 100644
index 00000000000..a4c3244af5c
--- /dev/null
+++ b/gn/src/gn/hash_table_base_unittest.cc
@@ -0,0 +1,356 @@
+// Copyright (c) 2020 The Chromium 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 "hash_table_base.h"
+
+#include "util/test/test.h"
+
+#include <algorithm>
+#include <vector>
+
+// This unit-test is also used to illustrate how to use HashTableBase<>
+// in a concrete way. Here, each node is a simple pointer to a Int class
+// that wraps a simple integer, but also keeps tracks of
+// construction/destruction steps in global counters. This is used by the
+// test to verify that operations like copies or moves do not miss or
+// create allocations/deallocations.
+//
+// Because the derived table HashTableTest owns all pointer objects, it
+// needs to manually create/deallocate them in its destructor, copy/move
+// constructors and operators, as well as insert()/erase()/clear() methods.
+//
+// Finally, iteration support is provided through const_iterator and
+// begin(), end() methods, which are enough for range-based for loops.
+//
+
+// A simple int wrapper class that can also count creation/destruction.
+class Int {
+ public:
+ explicit Int(int x) : x_(x) { creation_counter++; }
+
+ Int(const Int& other) : x_(other.x_) { creation_counter++; }
+
+ ~Int() { destruction_counter++; }
+
+ int& x() { return x_; }
+ const int& x() const { return x_; }
+
+ size_t hash() const { return static_cast<size_t>(x_); }
+
+ static void ResetCounters() {
+ creation_counter = 0;
+ destruction_counter = 0;
+ }
+
+ int x_;
+
+ static size_t creation_counter;
+ static size_t destruction_counter;
+};
+
+size_t Int::creation_counter;
+size_t Int::destruction_counter;
+
+// A HashTableBase<> node type that contains a simple pointer to a Int value.
+struct TestHashNode {
+ Int* int_ptr;
+
+ bool is_null() const { return !int_ptr; }
+ bool is_tombstone() const { return int_ptr == &kTombstone; }
+ bool is_valid() const { return !is_null() && !is_tombstone(); }
+ size_t hash_value() const { return int_ptr->hash(); }
+
+ static Int kTombstone;
+};
+
+// Value is irrelevant since only its address is used.
+Int TestHashNode::kTombstone(-1);
+
+// TestHashTable derives a HashTableBase<> instantiation to demonstrate
+// full uses of the template. This includes:
+//
+// - Storing a pointer in each node, and managing ownership of pointed
+// objects explicitly in the destructor, copy/move constructor and
+// operator, as well as insert() and erase() methods.
+//
+// - The internal pointed objects are Int instance, but the TestHashTable
+// API masks that entirely, instead implementing a simple set of integers,
+// including iteration support.
+//
+// Note that placing the integers directly in the nodes would be much easier,
+// but would not allow demonstrating how to manage ownership in thedestructor
+class TestHashTable : public HashTableBase<TestHashNode> {
+ public:
+ using BaseType = HashTableBase<TestHashNode>;
+ using Node = BaseType::Node;
+
+ static_assert(std::is_same<Node, TestHashNode>::value,
+ "HashTableBase<>::Node is not defined properly!");
+
+ BaseType& asBaseType() { return *this; }
+ const BaseType& asBaseType() const { return *this; }
+
+ TestHashTable() = default;
+
+ // IMPORTANT NOTE: Because the table contains bare owning pointers, we
+ // have to use explicit copy and move constructor/operators for things
+ // to work as expected. This is yet another reason why HashTableBase<>
+ // should only be used with care (preferrably with non-owning pointers).
+ //
+ TestHashTable(const TestHashTable& other) : BaseType(other) {
+ // Only node (i.e. pointers) are copied by the base type.
+ for (Node& node : ValidNodesRange()) {
+ node.int_ptr = new Int(*node.int_ptr);
+ }
+ }
+
+ TestHashTable& operator=(const TestHashTable& other) {
+ if (this != &other) {
+ this->~TestHashTable();
+ new (this) TestHashTable(other);
+ }
+ return *this;
+ }
+
+ TestHashTable(TestHashTable&& other) noexcept : BaseType(std::move(other)) {}
+ TestHashTable& operator=(TestHashTable&& other) noexcept {
+ if (this != &other) {
+ this->~TestHashTable();
+ new (this) TestHashTable(std::move(other));
+ }
+ return *this;
+ }
+
+ ~TestHashTable() {
+ // Discard all valid Int pointers in the hash table.
+ for (Node& node : ValidNodesRange())
+ delete node.int_ptr;
+ }
+
+ // Return true iff the table contains |x|.
+ bool contains(int x) const {
+ size_t hash = static_cast<size_t>(x);
+ Node* node = NodeLookup(
+ hash, [&](const Node* node) { return node->int_ptr->x() == x; });
+ return node->is_valid();
+ }
+
+ // Try to insert |x| in the table. Returns true on success, or false if
+ // the value was already in it.
+ bool insert(int x) {
+ size_t hash = static_cast<size_t>(x);
+ Node* node = NodeLookup(
+ hash, [&](const Node* node) { return node->int_ptr->x() == x; });
+ if (node->is_valid())
+ return false;
+
+ node->int_ptr = new Int(x);
+ UpdateAfterInsert();
+ return true;
+ }
+
+ // Try to remove |x| from the table. Return true on success, or false
+ // if the value was not in it.
+ bool erase(int x) {
+ size_t hash = static_cast<size_t>(x);
+ Node* node = NodeLookup(
+ hash, [&](const Node* node) { return node->int_ptr->x() == x; });
+ if (!node->is_valid())
+ return false;
+
+ delete node->int_ptr;
+ node->int_ptr = &TestHashNode::kTombstone;
+ UpdateAfterRemoval();
+ return true;
+ }
+
+ // Remove all items
+ void clear() {
+ // Remove all pointed objects, since NodeClear() will not do it.
+ for (Node& node : ValidNodesRange())
+ delete node.int_ptr;
+
+ NodeClear();
+ }
+
+ // Define const_iterator that point to the integer value instead of the node
+ // of the Int instance to completely hide these from this class' API.
+ struct const_iterator : public BaseType::NodeIterator {
+ const int& operator*() const {
+ return (this->BaseType::NodeIterator::operator*()).int_ptr->x();
+ }
+ const int* operator->() const { return &(this->operator*()); }
+ };
+
+ const_iterator begin() const { return {BaseType::NodeBegin()}; }
+
+ const_iterator end() const { return {BaseType::NodeEnd()}; }
+};
+
+TEST(HashTableBaseTest, Construction) {
+ Int::ResetCounters();
+ {
+ TestHashTable table;
+ EXPECT_TRUE(table.empty());
+ EXPECT_EQ(table.size(), 0u);
+ EXPECT_EQ(table.begin(), table.end());
+ }
+
+ // No item was created or destroyed.
+ EXPECT_EQ(Int::creation_counter, 0u);
+ EXPECT_EQ(Int::destruction_counter, 0u);
+}
+
+TEST(HashTableBaseTest, InsertionsAndLookups) {
+ Int::ResetCounters();
+ {
+ TestHashTable table;
+ table.insert(1);
+ table.insert(5);
+ table.insert(7);
+
+ EXPECT_FALSE(table.empty());
+ EXPECT_EQ(table.size(), 3u);
+ EXPECT_NE(table.begin(), table.end());
+
+ EXPECT_EQ(Int::creation_counter, 3u);
+ EXPECT_EQ(Int::destruction_counter, 0u);
+
+ EXPECT_FALSE(table.contains(0));
+ EXPECT_TRUE(table.contains(1));
+ EXPECT_FALSE(table.contains(2));
+ EXPECT_FALSE(table.contains(3));
+ EXPECT_TRUE(table.contains(5));
+ EXPECT_FALSE(table.contains(6));
+ EXPECT_TRUE(table.contains(7));
+ EXPECT_FALSE(table.contains(8));
+ }
+
+ EXPECT_EQ(Int::creation_counter, 3u);
+ EXPECT_EQ(Int::destruction_counter, 3u);
+}
+
+TEST(HashTableBaseTest, CopyAssignment) {
+ Int::ResetCounters();
+ {
+ TestHashTable table;
+ table.insert(1);
+ table.insert(5);
+ table.insert(7);
+
+ EXPECT_FALSE(table.empty());
+ EXPECT_EQ(table.size(), 3u);
+
+ TestHashTable table2;
+ EXPECT_TRUE(table2.empty());
+ table2 = table;
+ EXPECT_FALSE(table2.empty());
+ EXPECT_EQ(table2.size(), 3u);
+ EXPECT_FALSE(table.empty());
+ EXPECT_EQ(table.size(), 3u);
+
+ EXPECT_EQ(Int::creation_counter, 6u);
+ EXPECT_EQ(Int::destruction_counter, 0u);
+
+ EXPECT_FALSE(table.contains(0));
+ EXPECT_TRUE(table.contains(1));
+ EXPECT_FALSE(table.contains(2));
+ EXPECT_FALSE(table.contains(3));
+ EXPECT_TRUE(table.contains(5));
+ EXPECT_FALSE(table.contains(6));
+ EXPECT_TRUE(table.contains(7));
+ EXPECT_FALSE(table.contains(8));
+
+ EXPECT_FALSE(table2.contains(0));
+ EXPECT_TRUE(table2.contains(1));
+ EXPECT_FALSE(table2.contains(2));
+ EXPECT_FALSE(table2.contains(3));
+ EXPECT_TRUE(table2.contains(5));
+ EXPECT_FALSE(table2.contains(6));
+ EXPECT_TRUE(table2.contains(7));
+ EXPECT_FALSE(table2.contains(8));
+ }
+
+ EXPECT_EQ(Int::creation_counter, 6u);
+ EXPECT_EQ(Int::destruction_counter, 6u);
+}
+
+TEST(HashTableBaseTest, MoveAssignment) {
+ Int::ResetCounters();
+ {
+ TestHashTable table;
+ table.insert(1);
+ table.insert(5);
+ table.insert(7);
+
+ EXPECT_FALSE(table.empty());
+ EXPECT_EQ(table.size(), 3u);
+
+ TestHashTable table2;
+ EXPECT_TRUE(table2.empty());
+ table2 = std::move(table);
+ EXPECT_FALSE(table2.empty());
+ EXPECT_EQ(table2.size(), 3u);
+ EXPECT_TRUE(table.empty());
+ EXPECT_EQ(table.size(), 0u);
+
+ EXPECT_EQ(Int::creation_counter, 3u);
+ EXPECT_EQ(Int::destruction_counter, 0u);
+
+ EXPECT_FALSE(table2.contains(0));
+ EXPECT_TRUE(table2.contains(1));
+ EXPECT_FALSE(table2.contains(2));
+ EXPECT_FALSE(table2.contains(3));
+ EXPECT_TRUE(table2.contains(5));
+ EXPECT_FALSE(table2.contains(6));
+ EXPECT_TRUE(table2.contains(7));
+ EXPECT_FALSE(table2.contains(8));
+ }
+
+ EXPECT_EQ(Int::creation_counter, 3u);
+ EXPECT_EQ(Int::destruction_counter, 3u);
+}
+
+TEST(HashTableBaseTest, Clear) {
+ Int::ResetCounters();
+ {
+ TestHashTable table;
+ table.insert(1);
+ table.insert(5);
+ table.insert(7);
+
+ EXPECT_FALSE(table.empty());
+ EXPECT_EQ(table.size(), 3u);
+
+ table.clear();
+ EXPECT_TRUE(table.empty());
+
+ EXPECT_EQ(Int::creation_counter, 3u);
+ EXPECT_EQ(Int::destruction_counter, 3u);
+ }
+
+ EXPECT_EQ(Int::creation_counter, 3u);
+ EXPECT_EQ(Int::destruction_counter, 3u);
+}
+
+TEST(HashTableBaseTest, Iteration) {
+ TestHashTable table;
+ table.insert(1);
+ table.insert(5);
+ table.insert(7);
+
+ EXPECT_FALSE(table.empty());
+ EXPECT_EQ(table.size(), 3u);
+
+ std::vector<int> values;
+
+ for (const int& x : table)
+ values.push_back(x);
+
+ std::sort(values.begin(), values.end());
+ EXPECT_EQ(values.size(), 3u);
+ EXPECT_EQ(values[0], 1);
+ EXPECT_EQ(values[1], 5);
+ EXPECT_EQ(values[2], 7);
+}
diff --git a/gn/src/gn/header_checker.cc b/gn/src/gn/header_checker.cc
new file mode 100644
index 00000000000..212f5816dc8
--- /dev/null
+++ b/gn/src/gn/header_checker.cc
@@ -0,0 +1,642 @@
+// 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 "gn/header_checker.h"
+
+#include <algorithm>
+
+#include "base/containers/queue.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "gn/build_settings.h"
+#include "gn/builder.h"
+#include "gn/c_include_iterator.h"
+#include "gn/config.h"
+#include "gn/config_values_extractors.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/scheduler.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+#include "util/worker_pool.h"
+
+namespace {
+
+struct PublicGeneratedPair {
+ PublicGeneratedPair() : is_public(false), is_generated(false) {}
+ bool is_public;
+ bool is_generated;
+};
+
+// This class makes InputFiles on the stack as it reads files to check. When
+// we throw an error, the Err indicates a locatin which has a pointer to
+// an InputFile that must persist as long as the Err does.
+//
+// To make this work, this function creates a clone of the InputFile managed
+// by the InputFileManager so the error can refer to something that
+// persists. This means that the current file contents will live as long as
+// the program, but this is OK since we're erroring out anyway.
+LocationRange CreatePersistentRange(const InputFile& input_file,
+ const LocationRange& range) {
+ InputFile* clone_input_file;
+ std::vector<Token>* tokens; // Don't care about this.
+ std::unique_ptr<ParseNode>* parse_root; // Don't care about this.
+
+ g_scheduler->input_file_manager()->AddDynamicInput(
+ input_file.name(), &clone_input_file, &tokens, &parse_root);
+ clone_input_file->SetContents(input_file.contents());
+
+ return LocationRange(
+ Location(clone_input_file, range.begin().line_number(),
+ range.begin().column_number(), -1 /* TODO(scottmg) */),
+ Location(clone_input_file, range.end().line_number(),
+ range.end().column_number(), -1 /* TODO(scottmg) */));
+}
+
+// Given a reverse dependency chain where the target chain[0]'s includes are
+// being used by chain[end] and not all deps are public, returns the string
+// describing the error.
+std::string GetDependencyChainPublicError(const HeaderChecker::Chain& chain) {
+ std::string ret =
+ "The target:\n " +
+ chain[chain.size() - 1].target->label().GetUserVisibleName(false) +
+ "\nis including a file from the target:\n " +
+ chain[0].target->label().GetUserVisibleName(false) + "\n";
+
+ // Invalid chains should always be 0 (no chain) or more than two
+ // (intermediate private dependencies). 1 and 2 are impossible because a
+ // target can always include headers from itself and its direct dependents.
+ DCHECK(chain.size() != 1 && chain.size() != 2);
+ if (chain.empty()) {
+ ret += "There is no dependency chain between these targets.";
+ } else {
+ // Indirect dependency chain, print the chain.
+ ret +=
+ "\nIt's usually best to depend directly on the destination target.\n"
+ "In some cases, the destination target is considered a subcomponent\n"
+ "of an intermediate target. In this case, the intermediate target\n"
+ "should depend publicly on the destination to forward the ability\n"
+ "to include headers.\n"
+ "\n"
+ "Dependency chain (there may also be others):\n";
+
+ for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
+ ret.append(" " + chain[i].target->label().GetUserVisibleName(false));
+ if (i != 0) {
+ // Identify private dependencies so the user can see where in the
+ // dependency chain things went bad. Don't list this for the first link
+ // in the chain since direct dependencies are OK, and listing that as
+ // "private" may make people feel like they need to fix it.
+ if (i == static_cast<int>(chain.size()) - 1 || chain[i - 1].is_public)
+ ret.append(" -->");
+ else
+ ret.append(" --[private]-->");
+ }
+ ret.append("\n");
+ }
+ }
+ return ret;
+}
+
+// Returns true if the two targets have the same label not counting the
+// toolchain.
+bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) {
+ return a->label().dir() == b->label().dir() &&
+ a->label().name() == b->label().name();
+}
+
+// Returns true if the target |annotation_on| includes a friend annotation
+// that allows |is_marked_friend| as a friend.
+bool FriendMatches(const Target* annotation_on,
+ const Target* is_marked_friend) {
+ return LabelPattern::VectorMatches(annotation_on->friends(),
+ is_marked_friend->label());
+}
+
+} // namespace
+
+HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
+ const std::vector<const Target*>& targets,
+ bool check_generated,
+ bool check_system)
+ : build_settings_(build_settings),
+ check_generated_(check_generated),
+ check_system_(check_system),
+ lock_(),
+ task_count_cv_() {
+ for (auto* target : targets)
+ AddTargetToFileMap(target, &file_map_);
+}
+
+HeaderChecker::~HeaderChecker() = default;
+
+bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
+ bool force_check,
+ std::vector<Err>* errors) {
+ FileMap files_to_check;
+ for (auto* check : to_check) {
+ // This function will get called with all target types, but check only
+ // applies to binary targets.
+ if (check->IsBinary())
+ AddTargetToFileMap(check, &files_to_check);
+ }
+ RunCheckOverFiles(files_to_check, force_check);
+
+ if (errors_.empty())
+ return true;
+ *errors = errors_;
+ return false;
+}
+
+void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
+ WorkerPool pool;
+
+ for (const auto& file : files) {
+ // Only check C-like source files (RC files also have includes).
+ SourceFile::Type type = file.first.type();
+ if (type != SourceFile::SOURCE_CPP && type != SourceFile::SOURCE_H &&
+ type != SourceFile::SOURCE_C && type != SourceFile::SOURCE_M &&
+ type != SourceFile::SOURCE_MM && type != SourceFile::SOURCE_RC)
+ continue;
+
+ if (!check_generated_) {
+ // If any target marks it as generated, don't check it. We have to check
+ // file_map_, which includes all known files; files only includes those
+ // being checked.
+ bool is_generated = false;
+ for (const auto& vect_i : file_map_[file.first])
+ is_generated |= vect_i.is_generated;
+ if (is_generated)
+ continue;
+ }
+
+ for (const auto& vect_i : file.second) {
+ if (vect_i.target->check_includes()) {
+ task_count_.Increment();
+ pool.PostTask([this, target = vect_i.target, file = file.first]() {
+ DoWork(target, file);
+ });
+ }
+ }
+ }
+
+ // Wait for all tasks posted by this method to complete.
+ std::unique_lock<std::mutex> auto_lock(lock_);
+ while (!task_count_.IsZero())
+ task_count_cv_.wait(auto_lock);
+}
+
+void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
+ std::vector<Err> errors;
+ if (!CheckFile(target, file, &errors)) {
+ std::lock_guard<std::mutex> lock(lock_);
+ errors_.insert(errors_.end(), errors.begin(), errors.end());
+ }
+
+ if (!task_count_.Decrement()) {
+ // Signal |task_count_cv_| when |task_count_| becomes zero.
+ std::unique_lock<std::mutex> auto_lock(lock_);
+ task_count_cv_.notify_one();
+ }
+}
+
+// static
+void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
+ // Files in the sources have this public bit by default.
+ bool default_public = target->all_headers_public();
+
+ std::map<SourceFile, PublicGeneratedPair> files_to_public;
+
+ // First collect the normal files, they get the default visibility. If you
+ // depend on the compiled target, it should be enough to be able to include
+ // the header.
+ for (const auto& source : target->sources()) {
+ files_to_public[source].is_public = default_public;
+ }
+
+ // Add in the public files, forcing them to public. This may overwrite some
+ // entries, and it may add new ones.
+ if (default_public) // List only used when default is not public.
+ DCHECK(target->public_headers().empty());
+ for (const auto& source : target->public_headers()) {
+ files_to_public[source].is_public = true;
+ }
+
+ // Add in outputs from actions. These are treated as public (since if other
+ // targets can't use them, then there wouldn't be any point in outputting).
+ std::vector<SourceFile> outputs;
+ target->action_values().GetOutputsAsSourceFiles(target, &outputs);
+ for (const auto& output : outputs) {
+ PublicGeneratedPair* pair = &files_to_public[output];
+ pair->is_public = true;
+ pair->is_generated = true;
+ }
+
+ // Add the merged list to the master list of all files.
+ for (const auto& cur : files_to_public) {
+ (*dest)[cur.first].push_back(
+ TargetInfo(target, cur.second.is_public, cur.second.is_generated));
+ }
+}
+
+bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
+ const std::string& build_dir = build_settings_->build_dir().value();
+ return file.value().compare(0, build_dir.size(), build_dir) == 0;
+}
+
+SourceFile HeaderChecker::SourceFileForInclude(
+ const IncludeStringWithLocation& include,
+ const std::vector<SourceDir>& include_dirs,
+ const InputFile& source_file,
+ Err* err) const {
+ using base::FilePath;
+
+ Value relative_file_value(nullptr, std::string(include.contents));
+
+ auto find_predicate = [relative_file_value, err, this](const SourceDir& dir) -> bool {
+ SourceFile include_file =
+ dir.ResolveRelativeFile(relative_file_value, err);
+ return file_map_.find(include_file) != file_map_.end();
+ };
+ if (!include.system_style_include) {
+ const SourceDir& file_dir = source_file.dir();
+ if (find_predicate(file_dir)) {
+ return file_dir.ResolveRelativeFile(relative_file_value, err);
+ }
+ }
+
+ auto it = std::find_if(
+ include_dirs.begin(), include_dirs.end(), find_predicate);
+
+ if (it != include_dirs.end())
+ return it->ResolveRelativeFile(relative_file_value, err);
+
+ return SourceFile();
+}
+
+bool HeaderChecker::CheckFile(const Target* from_target,
+ const SourceFile& file,
+ std::vector<Err>* errors) const {
+ ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
+
+ // Sometimes you have generated source files included as sources in another
+ // target. These won't exist at checking time. Since we require all generated
+ // files to be somewhere in the output tree, we can just check the name to
+ // see if they should be skipped.
+ if (!check_generated_ && IsFileInOuputDir(file))
+ return true;
+
+ base::FilePath path = build_settings_->GetFullPath(file);
+ std::string contents;
+ if (!base::ReadFileToString(path, &contents)) {
+ // A missing (not yet) generated file is an acceptable problem
+ // considering this code does not understand conditional includes.
+ if (IsFileInOuputDir(file))
+ return true;
+
+ errors->emplace_back(from_target->defined_from(), "Source file not found.",
+ "The target:\n " +
+ from_target->label().GetUserVisibleName(false) +
+ "\nhas a source file:\n " + file.value() +
+ "\nwhich was not found.");
+ return false;
+ }
+
+ InputFile input_file(file);
+ input_file.SetContents(contents);
+
+ std::vector<SourceDir> include_dirs;
+ for (ConfigValuesIterator iter(from_target); !iter.done(); iter.Next()) {
+ const std::vector<SourceDir>& target_include_dirs =
+ iter.cur().include_dirs();
+ include_dirs.insert(include_dirs.end(), target_include_dirs.begin(),
+ target_include_dirs.end());
+ }
+
+ size_t error_count_before = errors->size();
+ CIncludeIterator iter(&input_file);
+
+ IncludeStringWithLocation include;
+
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+
+ while (iter.GetNextIncludeString(&include)) {
+ if (include.system_style_include && !check_system_)
+ continue;
+
+ Err err;
+ SourceFile included_file = SourceFileForInclude(include,
+ include_dirs,
+ input_file,
+ &err);
+ if (!included_file.is_null()) {
+ CheckInclude(from_target, input_file, included_file, include.location,
+ &no_dependency_cache, errors);
+ }
+ }
+
+ return errors->size() == error_count_before;
+}
+
+// If the file exists:
+// - The header must be in the public section of a target, or it must
+// be in the sources with no public list (everything is implicitly public).
+// - The dependency path to the included target must follow only public_deps.
+// - If there are multiple targets with the header in it, only one need be
+// valid for the check to pass.
+void HeaderChecker::CheckInclude(
+ const Target* from_target,
+ const InputFile& source_file,
+ const SourceFile& include_file,
+ const LocationRange& range,
+ std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
+ std::vector<Err>* errors) const {
+ // Assume if the file isn't declared in our sources that we don't need to
+ // check it. It would be nice if we could give an error if this happens, but
+ // our include finder is too primitive and returns all includes, even if
+ // they're in a #if not executed in the current build. In that case, it's
+ // not unusual for the buildfiles to not specify that header at all.
+ FileMap::const_iterator found = file_map_.find(include_file);
+ if (found == file_map_.end())
+ return;
+
+ const TargetVector& targets = found->second;
+ Chain chain; // Prevent reallocating in the loop.
+
+ // If the file is unknown in the current toolchain (rather than being private
+ // or in a target not visible to the current target), ignore it. This is a
+ // bit of a hack to account for the fact that the include finder doesn't
+ // understand the preprocessor.
+ //
+ // When not cross-compiling, if a platform specific header is conditionally
+ // included in the build, and preprocessor conditions around #includes of
+ // that match the build conditions, everything will be OK because the file
+ // won't be known to GN even though the #include finder identified the file.
+ //
+ // Cross-compiling breaks this. When compiling Android on Linux, for example,
+ // we might see both Linux and Android definitions of a target and know
+ // about the union of all headers in the build. Since the #include finder
+ // ignores preprocessor, we will find the Linux headers in the Android
+ // build and note that a dependency from the Android target to the Linux
+ // one is missing (these might even be the same target in different
+ // toolchains!).
+ bool present_in_current_toolchain = false;
+ for (const auto& target : targets) {
+ if (from_target->label().ToolchainsEqual(target.target->label())) {
+ present_in_current_toolchain = true;
+ break;
+ }
+ }
+ if (!present_in_current_toolchain)
+ return;
+
+ // For all targets containing this file, we require that at least one be
+ // a direct or public dependency of the current target, and either (1) the
+ // header is public within the target, or (2) there is a friend definition
+ // whitelisting the includor.
+ //
+ // If there is more than one target containing this header, we may encounter
+ // some error cases before finding a good one. This error stores the previous
+ // one encountered, which we may or may not throw away.
+ Err last_error;
+
+ bool found_dependency = false;
+ for (const auto& target : targets) {
+ // We always allow source files in a target to include headers also in that
+ // target.
+ const Target* to_target = target.target;
+ if (to_target == from_target)
+ return;
+
+ bool is_permitted_chain = false;
+
+ bool cached_no_dependency =
+ no_dependency_cache->find(std::make_pair(to_target, from_target)) !=
+ no_dependency_cache->end();
+
+ bool add_to_cache = !cached_no_dependency;
+
+ if (!cached_no_dependency &&
+ IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
+ add_to_cache = false;
+
+ DCHECK(chain.size() >= 2);
+ DCHECK(chain[0].target == to_target);
+ DCHECK(chain[chain.size() - 1].target == from_target);
+
+ found_dependency = true;
+
+ bool effectively_public =
+ target.is_public || FriendMatches(to_target, from_target);
+
+ if (effectively_public && is_permitted_chain) {
+ // This one is OK, we're done.
+ last_error = Err();
+ break;
+ }
+
+ // Diagnose the error.
+ if (!effectively_public) {
+ // Danger: must call CreatePersistentRange to put in Err.
+ last_error = Err(CreatePersistentRange(source_file, range),
+ "Including a private header.",
+ "This file is private to the target " +
+ target.target->label().GetUserVisibleName(false));
+ } else if (!is_permitted_chain) {
+ last_error = Err(CreatePersistentRange(source_file, range),
+ "Can't include this header from here.",
+ GetDependencyChainPublicError(chain));
+ } else {
+ NOTREACHED();
+ }
+ } else if (to_target->allow_circular_includes_from().find(
+ from_target->label()) !=
+ to_target->allow_circular_includes_from().end()) {
+ // Not a dependency, but this include is whitelisted from the destination.
+ found_dependency = true;
+ last_error = Err();
+ break;
+ }
+
+ if (add_to_cache) {
+ no_dependency_cache->emplace(to_target, from_target);
+ }
+ }
+
+ if (!found_dependency || last_error.has_error()) {
+ if (!found_dependency) {
+ DCHECK(!last_error.has_error());
+ Err err = MakeUnreachableError(source_file, range, from_target, targets);
+ errors->push_back(std::move(err));
+ } else {
+ // Found at least one dependency chain above, but it had an error.
+ errors->push_back(std::move(last_error));
+ }
+ return;
+ }
+
+ // One thing we didn't check for is targets that expose their dependents
+ // headers in their own public headers.
+ //
+ // Say we have A -> B -> C. If C has public_configs, everybody getting headers
+ // from C should get the configs also or things could be out-of-sync. Above,
+ // we check for A including C's headers directly, but A could also include a
+ // header from B that in turn includes a header from C.
+ //
+ // There are two ways to solve this:
+ // - If a public header in B includes C, force B to publicly depend on C.
+ // This is possible to check, but might be super annoying because most
+ // targets (especially large leaf-node targets) don't declare
+ // public/private headers and you'll get lots of false positives.
+ //
+ // - Save the includes found in each file and actually compute the graph of
+ // includes to detect when A implicitly includes C's header. This will not
+ // have the annoying false positive problem, but is complex to write.
+}
+
+bool HeaderChecker::IsDependencyOf(const Target* search_for,
+ const Target* search_from,
+ Chain* chain,
+ bool* is_permitted) const {
+ if (search_for == search_from) {
+ // A target is always visible from itself.
+ *is_permitted = true;
+ return false;
+ }
+
+ // Find the shortest public dependency chain.
+ if (IsDependencyOf(search_for, search_from, true, chain)) {
+ *is_permitted = true;
+ return true;
+ }
+
+ // If not, try to find any dependency chain at all.
+ if (IsDependencyOf(search_for, search_from, false, chain)) {
+ *is_permitted = false;
+ return true;
+ }
+
+ *is_permitted = false;
+ return false;
+}
+
+bool HeaderChecker::IsDependencyOf(const Target* search_for,
+ const Target* search_from,
+ bool require_permitted,
+ Chain* chain) const {
+ // This method conducts a breadth-first search through the dependency graph
+ // to find a shortest chain from search_from to search_for.
+ //
+ // work_queue maintains a queue of targets which need to be considered as
+ // part of this chain, in the order they were first traversed.
+ //
+ // Each time a new transitive dependency of search_from is discovered for
+ // the first time, it is added to work_queue and a "breadcrumb" is added,
+ // indicating which target it was reached from when first discovered.
+ //
+ // Once this search finds search_for, the breadcrumbs are used to reconstruct
+ // a shortest dependency chain (in reverse order) from search_from to
+ // search_for.
+
+ std::map<const Target*, ChainLink> breadcrumbs;
+ base::queue<ChainLink> work_queue;
+ work_queue.push(ChainLink(search_from, true));
+
+ bool first_time = true;
+ while (!work_queue.empty()) {
+ ChainLink cur_link = work_queue.front();
+ const Target* target = cur_link.target;
+ work_queue.pop();
+
+ if (target == search_for) {
+ // Found it! Reconstruct the chain.
+ chain->clear();
+ while (target != search_from) {
+ chain->push_back(cur_link);
+ cur_link = breadcrumbs[target];
+ target = cur_link.target;
+ }
+ chain->push_back(ChainLink(search_from, true));
+ return true;
+ }
+
+ // Always consider public dependencies as possibilities.
+ for (const auto& dep : target->public_deps()) {
+ if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
+ work_queue.push(ChainLink(dep.ptr, true));
+ }
+
+ if (first_time || !require_permitted) {
+ // Consider all dependencies since all target paths are allowed, so add
+ // in private ones. Also do this the first time through the loop, since
+ // a target can include headers from its direct deps regardless of
+ // public/private-ness.
+ first_time = false;
+ for (const auto& dep : target->private_deps()) {
+ if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
+ work_queue.push(ChainLink(dep.ptr, false));
+ }
+ }
+ }
+
+ return false;
+}
+
+Err HeaderChecker::MakeUnreachableError(const InputFile& source_file,
+ const LocationRange& range,
+ const Target* from_target,
+ const TargetVector& targets) {
+ // Normally the toolchains will all match, but when cross-compiling, we can
+ // get targets with more than one toolchain in the list of possibilities.
+ std::vector<const Target*> targets_with_matching_toolchains;
+ std::vector<const Target*> targets_with_other_toolchains;
+ for (const TargetInfo& candidate : targets) {
+ if (candidate.target->toolchain() == from_target->toolchain())
+ targets_with_matching_toolchains.push_back(candidate.target);
+ else
+ targets_with_other_toolchains.push_back(candidate.target);
+ }
+
+ // It's common when cross-compiling to have a target with the same file in
+ // more than one toolchain. We could output all of them, but this is
+ // generally confusing to people (most end-users won't understand toolchains
+ // well).
+ //
+ // So delete any candidates in other toolchains that also appear in the same
+ // toolchain as the from_target.
+ for (int other_index = 0;
+ other_index < static_cast<int>(targets_with_other_toolchains.size());
+ other_index++) {
+ for (const Target* cur_matching : targets_with_matching_toolchains) {
+ if (TargetLabelsMatchExceptToolchain(
+ cur_matching, targets_with_other_toolchains[other_index])) {
+ // Found a duplicate, erase it.
+ targets_with_other_toolchains.erase(
+ targets_with_other_toolchains.begin() + other_index);
+ other_index--;
+ break;
+ }
+ }
+ }
+
+ // Only display toolchains on labels if they don't all match.
+ bool include_toolchain = !targets_with_other_toolchains.empty();
+
+ std::string msg = "It is not in any dependency of\n " +
+ from_target->label().GetUserVisibleName(include_toolchain);
+ msg += "\nThe include file is in the target(s):\n";
+ for (auto* target : targets_with_matching_toolchains)
+ msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
+ for (auto* target : targets_with_other_toolchains)
+ msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
+ if (targets_with_other_toolchains.size() +
+ targets_with_matching_toolchains.size() >
+ 1)
+ msg += "at least one of ";
+ msg += "which should somehow be reachable.";
+
+ // Danger: must call CreatePersistentRange to put in Err.
+ return Err(CreatePersistentRange(source_file, range), "Include not allowed.",
+ msg);
+}
diff --git a/gn/src/gn/header_checker.h b/gn/src/gn/header_checker.h
new file mode 100644
index 00000000000..9e1065209b0
--- /dev/null
+++ b/gn/src/gn/header_checker.h
@@ -0,0 +1,208 @@
+// 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 TOOLS_GN_HEADER_CHECKER_H_
+#define TOOLS_GN_HEADER_CHECKER_H_
+
+#include <condition_variable>
+#include <functional>
+#include <map>
+#include <mutex>
+#include <set>
+#include <string_view>
+#include <vector>
+
+#include "base/atomic_ref_count.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "gn/c_include_iterator.h"
+#include "gn/err.h"
+#include "gn/source_dir.h"
+
+class BuildSettings;
+class InputFile;
+class SourceFile;
+class Target;
+
+namespace base {
+class FilePath;
+}
+
+class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> {
+ public:
+ // Represents a dependency chain.
+ struct ChainLink {
+ ChainLink() : target(nullptr), is_public(false) {}
+ ChainLink(const Target* t, bool p) : target(t), is_public(p) {}
+
+ const Target* target;
+
+ // True when the dependency on this target is public.
+ bool is_public;
+
+ // Used for testing.
+ bool operator==(const ChainLink& other) const {
+ return target == other.target && is_public == other.is_public;
+ }
+ };
+ using Chain = std::vector<ChainLink>;
+
+ // check_generated, if true, will also check generated
+ // files. Something that can only be done after running a build that
+ // has generated them.
+ HeaderChecker(const BuildSettings* build_settings,
+ const std::vector<const Target*>& targets,
+ bool check_generated,
+ bool check_system);
+
+ // Runs the check. The targets in to_check will be checked.
+ //
+ // This assumes that the current thread already has a message loop. On
+ // error, fills the given vector with the errors and returns false. Returns
+ // true on success.
+ //
+ // force_check, if true, will override targets opting out of header checking
+ // with "check_includes = false" and will check them anyway.
+ bool Run(const std::vector<const Target*>& to_check,
+ bool force_check,
+ std::vector<Err>* errors);
+
+ private:
+ friend class base::RefCountedThreadSafe<HeaderChecker>;
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, IsDependencyOf);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckInclude);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, PublicFirst);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckIncludeAllowCircular);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, SourceFileForInclude);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest,
+ SourceFileForInclude_FileNotFound);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, Friend);
+
+ ~HeaderChecker();
+
+ struct TargetInfo {
+ TargetInfo() : target(nullptr), is_public(false), is_generated(false) {}
+ TargetInfo(const Target* t, bool is_pub, bool is_gen)
+ : target(t), is_public(is_pub), is_generated(is_gen) {}
+
+ const Target* target;
+
+ // True if the file is public in the given target.
+ bool is_public;
+
+ // True if this file is generated and won't actually exist on disk.
+ bool is_generated;
+ };
+
+ using TargetVector = std::vector<TargetInfo>;
+ using FileMap = std::map<SourceFile, TargetVector>;
+ using PathExistsCallback = std::function<bool(const base::FilePath& path)>;
+
+ // Backend for Run() that takes the list of files to check. The errors_ list
+ // will be populate on failure.
+ void RunCheckOverFiles(const FileMap& flies, bool force_check);
+
+ void DoWork(const Target* target, const SourceFile& file);
+
+ // Adds the sources and public files from the given target to the given map.
+ static void AddTargetToFileMap(const Target* target, FileMap* dest);
+
+ // Returns true if the given file is in the output directory.
+ bool IsFileInOuputDir(const SourceFile& file) const;
+
+ // Resolves the contents of an include to a SourceFile.
+ SourceFile SourceFileForInclude(const IncludeStringWithLocation& include,
+ const std::vector<SourceDir>& include_dirs,
+ const InputFile& source_file,
+ Err* err) const;
+
+ // from_target is the target the file was defined from. It will be used in
+ // error messages.
+ bool CheckFile(const Target* from_target,
+ const SourceFile& file,
+ std::vector<Err>* err) const;
+
+ // Checks that the given file in the given target can include the
+ // given include file. If disallowed, adds the error or errors to
+ // the errors array. The range indicates the location of the
+ // include in the file for error reporting.
+ // |no_depeency_cache| is used to cache or check whether there is no
+ // dependency from |from_target| to target having |include_file|.
+ void CheckInclude(
+ const Target* from_target,
+ const InputFile& source_file,
+ const SourceFile& include_file,
+ const LocationRange& range,
+ std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
+ std::vector<Err>* errors) const;
+
+ // Returns true if the given search_for target is a dependency of
+ // search_from.
+ //
+ // If found, the vector given in "chain" will be filled with the reverse
+ // dependency chain from the dest target (chain[0] = search_for) to the src
+ // target (chain[chain.size() - 1] = search_from).
+ //
+ // Chains with permitted dependencies will be considered first. If a
+ // permitted match is found, *is_permitted will be set to true. A chain with
+ // indirect, non-public dependencies will only be considered if there are no
+ // public or direct chains. In this case, *is_permitted will be false.
+ //
+ // A permitted dependency is a sequence of public dependencies. The first
+ // one may be private, since a direct dependency always allows headers to be
+ // included.
+ bool IsDependencyOf(const Target* search_for,
+ const Target* search_from,
+ Chain* chain,
+ bool* is_permitted) const;
+
+ // For internal use by the previous override of IsDependencyOf. If
+ // require_public is true, only public dependency chains are searched.
+ bool IsDependencyOf(const Target* search_for,
+ const Target* search_from,
+ bool require_permitted,
+ Chain* chain) const;
+
+ // Makes a very descriptive error message for when an include is disallowed
+ // from a given from_target, with a missing dependency to one of the given
+ // targets.
+ static Err MakeUnreachableError(const InputFile& source_file,
+ const LocationRange& range,
+ const Target* from_target,
+ const TargetVector& targets);
+
+ // Non-locked variables ------------------------------------------------------
+ //
+ // These are initialized during construction (which happens on one thread)
+ // and are not modified after, so any thread can read these without locking.
+
+ const BuildSettings* build_settings_;
+
+ bool check_generated_;
+
+ bool check_system_;
+
+ // Maps source files to targets it appears in (usually just one target).
+ FileMap file_map_;
+
+ // Number of tasks posted by RunCheckOverFiles() that haven't completed their
+ // execution.
+ base::AtomicRefCount task_count_;
+
+ // Locked variables ----------------------------------------------------------
+ //
+ // These are mutable during runtime and require locking.
+
+ std::mutex lock_;
+
+ std::vector<Err> errors_;
+
+ // Signaled when |task_count_| becomes zero.
+ std::condition_variable task_count_cv_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeaderChecker);
+};
+
+#endif // TOOLS_GN_HEADER_CHECKER_H_
diff --git a/gn/src/gn/header_checker_unittest.cc b/gn/src/gn/header_checker_unittest.cc
new file mode 100644
index 00000000000..a290d453183
--- /dev/null
+++ b/gn/src/gn/header_checker_unittest.cc
@@ -0,0 +1,427 @@
+// 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 <ostream>
+#include <vector>
+
+#include "gn/config.h"
+#include "gn/header_checker.h"
+#include "gn/scheduler.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+class HeaderCheckerTest : public TestWithScheduler {
+ public:
+ HeaderCheckerTest()
+ : a_(setup_.settings(), Label(SourceDir("//a/"), "a")),
+ b_(setup_.settings(), Label(SourceDir("//b/"), "b")),
+ c_(setup_.settings(), Label(SourceDir("//c/"), "c")),
+ d_(setup_.settings(), Label(SourceDir("//d/"), "d")) {
+ a_.set_output_type(Target::SOURCE_SET);
+ b_.set_output_type(Target::SOURCE_SET);
+ c_.set_output_type(Target::SOURCE_SET);
+ d_.set_output_type(Target::SOURCE_SET);
+
+ Err err;
+ a_.SetToolchain(setup_.toolchain(), &err);
+ b_.SetToolchain(setup_.toolchain(), &err);
+ c_.SetToolchain(setup_.toolchain(), &err);
+ d_.SetToolchain(setup_.toolchain(), &err);
+
+ a_.public_deps().push_back(LabelTargetPair(&b_));
+ b_.public_deps().push_back(LabelTargetPair(&c_));
+
+ // Start with all public visibility.
+ a_.visibility().SetPublic();
+ b_.visibility().SetPublic();
+ c_.visibility().SetPublic();
+ d_.visibility().SetPublic();
+
+ d_.OnResolved(&err);
+ c_.OnResolved(&err);
+ b_.OnResolved(&err);
+ a_.OnResolved(&err);
+
+ targets_.push_back(&a_);
+ targets_.push_back(&b_);
+ targets_.push_back(&c_);
+ targets_.push_back(&d_);
+ }
+
+ protected:
+ scoped_refptr<HeaderChecker> CreateChecker() {
+ bool check_generated = false;
+ bool check_system = true;
+ return base::MakeRefCounted<HeaderChecker>(
+ setup_.build_settings(), targets_, check_generated, check_system);
+ }
+
+ TestWithScope setup_;
+
+ // Some headers that are automatically set up with a public dependency chain.
+ // a -> b -> c. D is unconnected.
+ Target a_;
+ Target b_;
+ Target c_;
+ Target d_;
+
+ std::vector<const Target*> targets_;
+};
+
+} // namespace
+
+void PrintTo(const SourceFile& source_file, ::std::ostream* os) {
+ *os << source_file.value();
+}
+
+TEST_F(HeaderCheckerTest, IsDependencyOf) {
+ auto checker = CreateChecker();
+
+ // Add a target P ("private") that privately depends on C, and hook up the
+ // chain so that A -> P -> C. A will depend on C via two different paths.
+ Err err;
+ Target p(setup_.settings(), Label(SourceDir("//p/"), "p"));
+ p.set_output_type(Target::SOURCE_SET);
+ p.SetToolchain(setup_.toolchain(), &err);
+ EXPECT_FALSE(err.has_error());
+ p.private_deps().push_back(LabelTargetPair(&c_));
+ p.visibility().SetPublic();
+ p.OnResolved(&err);
+
+ a_.public_deps().push_back(LabelTargetPair(&p));
+
+ // A does not depend on itself.
+ bool is_permitted = false;
+ HeaderChecker::Chain chain;
+ EXPECT_FALSE(checker->IsDependencyOf(&a_, &a_, &chain, &is_permitted));
+
+ // A depends publicly on B.
+ chain.clear();
+ is_permitted = false;
+ EXPECT_TRUE(checker->IsDependencyOf(&b_, &a_, &chain, &is_permitted));
+ ASSERT_EQ(2u, chain.size());
+ EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[0]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[1]);
+ EXPECT_TRUE(is_permitted);
+
+ // A indirectly depends on C. The "public" dependency path through B should
+ // be identified.
+ chain.clear();
+ is_permitted = false;
+ EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
+ ASSERT_EQ(3u, chain.size());
+ EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[0]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[1]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
+ EXPECT_TRUE(is_permitted);
+
+ // C does not depend on A.
+ chain.clear();
+ is_permitted = false;
+ EXPECT_FALSE(checker->IsDependencyOf(&a_, &c_, &chain, &is_permitted));
+ EXPECT_TRUE(chain.empty());
+ EXPECT_FALSE(is_permitted);
+
+ // Remove the B -> C public dependency, leaving P's private dep on C the only
+ // path from A to C. This should now be found.
+ chain.clear();
+ EXPECT_EQ(&c_, b_.public_deps()[0].ptr); // Validate it's the right one.
+ b_.public_deps().erase(b_.public_deps().begin());
+ EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
+ EXPECT_EQ(3u, chain.size());
+ EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
+ EXPECT_FALSE(is_permitted);
+
+ // P privately depends on C. That dependency should be OK since it's only
+ // one hop.
+ chain.clear();
+ is_permitted = false;
+ EXPECT_TRUE(checker->IsDependencyOf(&c_, &p, &chain, &is_permitted));
+ ASSERT_EQ(2u, chain.size());
+ EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
+ EXPECT_TRUE(is_permitted);
+}
+
+TEST_F(HeaderCheckerTest, CheckInclude) {
+ InputFile input_file(SourceFile("//some_file.cc"));
+ input_file.SetContents(std::string());
+ LocationRange range; // Dummy value.
+
+ // Add a disconnected target d with a header to check that you have to have
+ // to depend on a target listing a header.
+ SourceFile d_header("//d_header.h");
+ d_.sources().push_back(SourceFile(d_header));
+
+ // Add a header on B and say everything in B is public.
+ SourceFile b_public("//b_public.h");
+ b_.sources().push_back(b_public);
+ c_.set_all_headers_public(true);
+
+ // Add a public and private header on C.
+ SourceFile c_public("//c_public.h");
+ SourceFile c_private("//c_private.h");
+ c_.sources().push_back(c_private);
+ c_.public_headers().push_back(c_public);
+ c_.set_all_headers_public(false);
+
+ // Create another toolchain.
+ Settings other_settings(setup_.build_settings(), "other/");
+ Toolchain other_toolchain(&other_settings,
+ Label(SourceDir("//toolchain/"), "other"));
+ TestWithScope::SetupToolchain(&other_toolchain);
+ other_settings.set_toolchain_label(other_toolchain.label());
+ other_settings.set_default_toolchain_label(setup_.toolchain()->label());
+
+ // Add a target in the other toolchain with a header in it that is not
+ // connected to any targets in the main toolchain.
+ Target otc(&other_settings,
+ Label(SourceDir("//p/"), "otc", other_toolchain.label().dir(),
+ other_toolchain.label().name()));
+ otc.set_output_type(Target::SOURCE_SET);
+ Err err;
+ EXPECT_TRUE(otc.SetToolchain(&other_toolchain, &err));
+ otc.visibility().SetPublic();
+ targets_.push_back(&otc);
+
+ SourceFile otc_header("//otc_header.h");
+ otc.sources().push_back(otc_header);
+ EXPECT_TRUE(otc.OnResolved(&err));
+
+ auto checker = CreateChecker();
+
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+ // A file in target A can't include a header from D because A has no
+ // dependency on D.
+ std::vector<Err> errors;
+ checker->CheckInclude(&a_, input_file, d_header, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
+
+ // A can include the public header in B.
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, b_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
+
+ // Check A depending on the public and private headers in C.
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, c_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
+
+ // A can depend on a random file unknown to the build.
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, SourceFile("//random.h"), range,
+ &no_dependency_cache, &errors);
+ EXPECT_EQ(errors.size(), 0);
+
+ // A can depend on a file present only in another toolchain even with no
+ // dependency path.
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, otc_header, range,
+ &no_dependency_cache, &errors);
+ EXPECT_EQ(errors.size(), 0);
+}
+
+// A public chain of dependencies should always be identified first, even if
+// it is longer than a private one.
+TEST_F(HeaderCheckerTest, PublicFirst) {
+ // Now make a A -> Z -> D private dependency chain (one shorter than the
+ // public one to get to D).
+ Target z(setup_.settings(), Label(SourceDir("//a/"), "a"));
+ z.set_output_type(Target::SOURCE_SET);
+ Err err;
+ EXPECT_TRUE(z.SetToolchain(setup_.toolchain(), &err));
+ z.private_deps().push_back(LabelTargetPair(&d_));
+ EXPECT_TRUE(z.OnResolved(&err));
+ targets_.push_back(&z);
+
+ a_.private_deps().push_back(LabelTargetPair(&z));
+
+ // Check that D can be found from A, but since it's private, it will be
+ // marked as not permitted.
+ bool is_permitted = false;
+ HeaderChecker::Chain chain;
+ auto checker = CreateChecker();
+ EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
+
+ EXPECT_FALSE(is_permitted);
+ ASSERT_EQ(3u, chain.size());
+ EXPECT_EQ(HeaderChecker::ChainLink(&d_, false), chain[0]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&z, false), chain[1]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
+
+ // Hook up D to the existing public A -> B -> C chain to make a long one, and
+ // search for D again.
+ c_.public_deps().push_back(LabelTargetPair(&d_));
+ checker = CreateChecker();
+ chain.clear();
+ EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
+
+ // This should have found the long public one.
+ EXPECT_TRUE(is_permitted);
+ ASSERT_EQ(4u, chain.size());
+ EXPECT_EQ(HeaderChecker::ChainLink(&d_, true), chain[0]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[1]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[2]);
+ EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[3]);
+}
+
+// Checks that the allow_circular_includes_from list works.
+TEST_F(HeaderCheckerTest, CheckIncludeAllowCircular) {
+ InputFile input_file(SourceFile("//some_file.cc"));
+ input_file.SetContents(std::string());
+ LocationRange range; // Dummy value.
+
+ // Add an include file to A.
+ SourceFile a_public("//a_public.h");
+ a_.sources().push_back(a_public);
+
+ auto checker = CreateChecker();
+
+ // A depends on B. So B normally can't include headers from A.
+ std::vector<Err> errors;
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+ checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
+
+ // Add an allow_circular_includes_from on A that lists B.
+ a_.allow_circular_includes_from().insert(b_.label());
+
+ // Now the include from B to A should be allowed.
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
+}
+
+TEST_F(HeaderCheckerTest, SourceFileForInclude) {
+ using base::FilePath;
+ const std::vector<SourceDir> kIncludeDirs = {
+ SourceDir("/c/custom_include/"), SourceDir("//"), SourceDir("//subdir")};
+ a_.sources().push_back(SourceFile("//lib/header1.h"));
+ b_.sources().push_back(SourceFile("/c/custom_include/header2.h"));
+ d_.sources().push_back(SourceFile("/d/subdir/header3.h"));
+
+ InputFile dummy_input_file(SourceFile("/d/subdir/some_file.cc"));
+ dummy_input_file.SetContents(std::string());
+
+ auto checker = CreateChecker();
+ {
+ Err err;
+ IncludeStringWithLocation include;
+ include.contents = "lib/header1.h";
+ SourceFile source_file = checker->SourceFileForInclude(
+ include, kIncludeDirs, dummy_input_file, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(SourceFile("//lib/header1.h"), source_file);
+ }
+
+ {
+ Err err;
+ IncludeStringWithLocation include;
+ include.contents = "header2.h";
+ SourceFile source_file = checker->SourceFileForInclude(
+ include, kIncludeDirs, dummy_input_file, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(SourceFile("/c/custom_include/header2.h"), source_file);
+ }
+
+ // A non system style include should find a header file in the same directory
+ // as the source file, regardless of include dirs.
+ {
+ Err err;
+ IncludeStringWithLocation include;
+ include.contents = "header3.h";
+ include.system_style_include = false;
+ SourceFile source_file = checker->SourceFileForInclude(
+ include, kIncludeDirs, dummy_input_file, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(SourceFile("/d/subdir/header3.h"), source_file);
+ }
+
+ // A system style include should *not* find a header file in the same
+ // directory as the source file if that directory is not in the include dirs.
+ {
+ Err err;
+ IncludeStringWithLocation include;
+ include.contents = "header3.h";
+ include.system_style_include = true;
+ SourceFile source_file = checker->SourceFileForInclude(
+ include, kIncludeDirs, dummy_input_file, &err);
+ EXPECT_TRUE(source_file.is_null());
+ EXPECT_FALSE(err.has_error());
+ }
+}
+
+TEST_F(HeaderCheckerTest, SourceFileForInclude_FileNotFound) {
+ using base::FilePath;
+ const char kFileContents[] = "Some dummy contents";
+ const std::vector<SourceDir> kIncludeDirs = {SourceDir("//")};
+ auto checker = CreateChecker();
+
+ Err err;
+ InputFile input_file(SourceFile("//input.cc"));
+ input_file.SetContents(std::string(kFileContents));
+
+ IncludeStringWithLocation include;
+ include.contents = "header.h";
+ SourceFile source_file =
+ checker->SourceFileForInclude(include, kIncludeDirs, input_file, &err);
+ EXPECT_TRUE(source_file.is_null());
+ EXPECT_FALSE(err.has_error());
+}
+
+TEST_F(HeaderCheckerTest, Friend) {
+ // Note: we have a public dependency chain A -> B -> C set up already.
+ InputFile input_file(SourceFile("//some_file.cc"));
+ input_file.SetContents(std::string());
+ LocationRange range; // Dummy value.
+
+ // Add a private header on C.
+ SourceFile c_private("//c_private.h");
+ c_.sources().push_back(c_private);
+ c_.set_all_headers_public(false);
+
+ // List A as a friend of C.
+ Err err;
+ c_.friends().push_back(LabelPattern::GetPattern(
+ SourceDir("//"), std::string_view(), Value(nullptr, "//a:*"), &err));
+ ASSERT_FALSE(err.has_error());
+
+ // Must be after setting everything up for it to find the files.
+ auto checker = CreateChecker();
+
+ // B should not be allowed to include C's private header.
+ std::vector<Err> errors;
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+ checker->CheckInclude(&b_, input_file, c_private, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
+
+ // A should be able to because of the friend declaration.
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
+}
diff --git a/gn/src/gn/import_manager.cc b/gn/src/gn/import_manager.cc
new file mode 100644
index 00000000000..a4928bf10c2
--- /dev/null
+++ b/gn/src/gn/import_manager.cc
@@ -0,0 +1,162 @@
+// 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 "gn/import_manager.h"
+
+#include <memory>
+
+#include "gn/err.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "gn/scope_per_file_provider.h"
+#include "gn/trace.h"
+#include "util/ticks.h"
+
+namespace {
+
+// Returns a newly-allocated scope on success, null on failure.
+std::unique_ptr<Scope> UncachedImport(const Settings* settings,
+ const SourceFile& file,
+ const ParseNode* node_for_err,
+ Err* err) {
+ ScopedTrace load_trace(TraceItem::TRACE_IMPORT_LOAD, file.value());
+ load_trace.SetToolchain(settings->toolchain_label());
+
+ const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
+ node_for_err->GetRange(), settings->build_settings(), file, err);
+ if (!node)
+ return nullptr;
+
+ std::unique_ptr<Scope> scope =
+ std::make_unique<Scope>(settings->base_config());
+ scope->set_source_dir(file.GetDir());
+
+ // Don't allow ScopePerFileProvider to provide target-related variables.
+ // These will be relative to the imported file, which is probably not what
+ // people mean when they use these.
+ ScopePerFileProvider per_file_provider(scope.get(), false);
+
+ scope->SetProcessingImport();
+ node->Execute(scope.get(), err);
+ if (err->has_error()) {
+ // If there was an error, append the caller location so the error message
+ // displays a why the file was imported (esp. useful for failed asserts).
+ err->AppendSubErr(Err(node_for_err, "whence it was imported."));
+ return nullptr;
+ }
+ scope->ClearProcessingImport();
+
+ return scope;
+}
+
+} // namespace
+
+struct ImportManager::ImportInfo {
+ ImportInfo() = default;
+ ~ImportInfo() = default;
+
+ // This lock protects the unique_ptr. Once the scope is computed,
+ // it is const and can be accessed read-only outside of the lock.
+ std::mutex load_lock;
+
+ std::unique_ptr<const Scope> scope;
+
+ // The result of loading the import. If the load failed, the scope will be
+ // null but this will be set to error. In this case the thread should not
+ // attempt to load the file, even if the scope is null.
+ Err load_result;
+};
+
+ImportManager::ImportManager() = default;
+
+ImportManager::~ImportManager() = default;
+
+bool ImportManager::DoImport(const SourceFile& file,
+ const ParseNode* node_for_err,
+ Scope* scope,
+ Err* err) {
+ // Key for the current import on the current thread in imports_in_progress_.
+ std::stringstream ss;
+ ss << std::this_thread::get_id() << file.value();
+ std::string key = ss.str();
+
+ // See if we have a cached import, but be careful to actually do the scope
+ // copying outside of the lock.
+ ImportInfo* import_info = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(imports_lock_);
+ std::unique_ptr<ImportInfo>& info_ptr = imports_[file];
+ if (!info_ptr)
+ info_ptr = std::make_unique<ImportInfo>();
+
+ // Promote the ImportInfo to outside of the imports lock.
+ import_info = info_ptr.get();
+
+ if (imports_in_progress_.find(key) != imports_in_progress_.end()) {
+ *err = Err(Location(), file.value() + " is part of an import loop.");
+ return false;
+ }
+ imports_in_progress_.insert(key);
+ }
+
+ // Now use the per-import-file lock to block this thread if another thread
+ // is already processing the import.
+ const Scope* import_scope = nullptr;
+ {
+ Ticks import_block_begin = TicksNow();
+ std::lock_guard<std::mutex> lock(import_info->load_lock);
+
+ if (!import_info->scope) {
+ // Only load if the import hasn't already failed.
+ if (!import_info->load_result.has_error()) {
+ import_info->scope = UncachedImport(
+ scope->settings(), file, node_for_err, &import_info->load_result);
+ }
+ if (import_info->load_result.has_error()) {
+ *err = import_info->load_result;
+ return false;
+ }
+ } else {
+ // Add trace if this thread was blocked for a long period of time and did
+ // not load the import itself.
+ Ticks import_block_end = TicksNow();
+ constexpr auto kImportBlockTraceThresholdMS = 20;
+ if (TracingEnabled() &&
+ TicksDelta(import_block_end, import_block_begin).InMilliseconds() >
+ kImportBlockTraceThresholdMS) {
+ auto* import_block_trace =
+ new TraceItem(TraceItem::TRACE_IMPORT_BLOCK, file.value(),
+ std::this_thread::get_id());
+ import_block_trace->set_begin(import_block_begin);
+ import_block_trace->set_end(import_block_end);
+ import_block_trace->set_toolchain(
+ scope->settings()->toolchain_label().GetUserVisibleName(false));
+ AddTrace(import_block_trace);
+ }
+ }
+
+ // Promote the now-read-only scope to outside the load lock.
+ import_scope = import_info->scope.get();
+ }
+
+ Scope::MergeOptions options;
+ options.skip_private_vars = true;
+ options.mark_dest_used = true; // Don't require all imported values be used.
+
+ {
+ std::lock_guard<std::mutex> lock(imports_lock_);
+ imports_in_progress_.erase(key);
+ }
+
+ return import_scope->NonRecursiveMergeTo(scope, options, node_for_err,
+ "import", err);
+}
+
+std::vector<SourceFile> ImportManager::GetImportedFiles() const {
+ std::vector<SourceFile> imported_files;
+ imported_files.resize(imports_.size());
+ std::transform(imports_.begin(), imports_.end(), imported_files.begin(),
+ [](const ImportMap::value_type& val) { return val.first; });
+ return imported_files;
+}
diff --git a/gn/src/gn/import_manager.h b/gn/src/gn/import_manager.h
new file mode 100644
index 00000000000..e03318332c4
--- /dev/null
+++ b/gn/src/gn/import_manager.h
@@ -0,0 +1,54 @@
+// 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 TOOLS_GN_IMPORT_MANAGER_H_
+#define TOOLS_GN_IMPORT_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "base/macros.h"
+
+class Err;
+class ParseNode;
+class Scope;
+class SourceFile;
+
+// Provides a cache of the results of importing scopes so the results can
+// be re-used rather than running the imported files multiple times.
+class ImportManager {
+ public:
+ ImportManager();
+ ~ImportManager();
+
+ // Does an import of the given file into the given scope. On error, sets the
+ // error and returns false.
+ bool DoImport(const SourceFile& file,
+ const ParseNode* node_for_err,
+ Scope* scope,
+ Err* err);
+
+ std::vector<SourceFile> GetImportedFiles() const;
+
+ private:
+ struct ImportInfo;
+
+ // Protects access to imports_ and imports_in_progress_. Do not hold when
+ // actually executing imports.
+ std::mutex imports_lock_;
+
+ // Owning pointers to the scopes.
+ using ImportMap = std::map<SourceFile, std::unique_ptr<ImportInfo>>;
+ ImportMap imports_;
+
+ std::unordered_set<std::string> imports_in_progress_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportManager);
+};
+
+#endif // TOOLS_GN_IMPORT_MANAGER_H_
diff --git a/gn/src/gn/inherited_libraries.cc b/gn/src/gn/inherited_libraries.cc
new file mode 100644
index 00000000000..87974ae985e
--- /dev/null
+++ b/gn/src/gn/inherited_libraries.cc
@@ -0,0 +1,74 @@
+// 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 "gn/inherited_libraries.h"
+
+#include "gn/target.h"
+
+InheritedLibraries::InheritedLibraries() = default;
+
+InheritedLibraries::~InheritedLibraries() = default;
+
+std::vector<const Target*> InheritedLibraries::GetOrdered() const {
+ std::vector<const Target*> result;
+ result.resize(map_.size());
+
+ // The indices in the map should be from 0 to the number of items in the
+ // map, so insert directly into the result (with some sanity checks).
+ for (const auto& pair : map_) {
+ size_t index = pair.second.index;
+ DCHECK(index < result.size());
+ DCHECK(!result[index]);
+ result[index] = pair.first;
+ }
+
+ return result;
+}
+
+std::vector<std::pair<const Target*, bool>>
+InheritedLibraries::GetOrderedAndPublicFlag() const {
+ std::vector<std::pair<const Target*, bool>> result;
+ result.resize(map_.size());
+
+ for (const auto& pair : map_) {
+ size_t index = pair.second.index;
+ DCHECK(index < result.size());
+ DCHECK(!result[index].first);
+ result[index] = std::make_pair(pair.first, pair.second.is_public);
+ }
+
+ return result;
+}
+
+void InheritedLibraries::Append(const Target* target, bool is_public) {
+ // Try to insert a new node.
+ auto insert_result =
+ map_.insert(std::make_pair(target, Node(map_.size(), is_public)));
+
+ if (!insert_result.second) {
+ // Element already present, insert failed and insert_result indicates the
+ // old one. The old one may need to have its public flag updated.
+ if (is_public) {
+ Node& existing_node = insert_result.first->second;
+ existing_node.is_public = true;
+ }
+ }
+}
+
+void InheritedLibraries::AppendInherited(const InheritedLibraries& other,
+ bool is_public) {
+ // Append all items in order, mark them public only if the're already public
+ // and we're adding them publically.
+ for (const auto& cur : other.GetOrderedAndPublicFlag())
+ Append(cur.first, is_public && cur.second);
+}
+
+void InheritedLibraries::AppendPublicSharedLibraries(
+ const InheritedLibraries& other,
+ bool is_public) {
+ for (const auto& cur : other.GetOrderedAndPublicFlag()) {
+ if (cur.first->output_type() == Target::SHARED_LIBRARY && cur.second)
+ Append(cur.first, is_public);
+ }
+}
diff --git a/gn/src/gn/inherited_libraries.h b/gn/src/gn/inherited_libraries.h
new file mode 100644
index 00000000000..71d02f46946
--- /dev/null
+++ b/gn/src/gn/inherited_libraries.h
@@ -0,0 +1,71 @@
+// 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 TOOLS_GN_INHERITED_LIBRARIES_H_
+#define TOOLS_GN_INHERITED_LIBRARIES_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+
+class Target;
+
+// Represents an ordered uniquified set of all shared/static libraries for
+// a given target. These are pushed up the dependency tree.
+//
+// Maintaining the order is important so GN links all libraries in the same
+// order specified in the build files.
+//
+// Since this list is uniquified, appending to the list will not actually
+// append a new item if the target already exists. However, the existing one
+// may have its is_public flag updated. "Public" always wins, so is_public will
+// be true if any dependency with that name has been set to public.
+class InheritedLibraries {
+ public:
+ InheritedLibraries();
+ ~InheritedLibraries();
+
+ // Returns the list of dependencies in order, optionally with the flag
+ // indicating whether the dependency is public.
+ std::vector<const Target*> GetOrdered() const;
+ std::vector<std::pair<const Target*, bool>> GetOrderedAndPublicFlag() const;
+
+ // Adds a single dependency to the end of the list. See note on adding above.
+ void Append(const Target* target, bool is_public);
+
+ // Appends all items from the "other" list to the current one. The is_public
+ // parameter indicates how the current target depends on the items in
+ // "other". If is_public is true, the existing public flags of the appended
+ // items will be preserved (propogating the public-ness up the dependency
+ // chain). If is_public is false, all deps will be added as private since
+ // the current target isn't forwarding them.
+ void AppendInherited(const InheritedLibraries& other, bool is_public);
+
+ // Like AppendInherited but only appends the items in "other" that are of
+ // type SHARED_LIBRARY and only when they're marked public. This is used
+ // to push shared libraries up the dependency chain, following only public
+ // deps, to dependent targets that need to use them.
+ void AppendPublicSharedLibraries(const InheritedLibraries& other,
+ bool is_public);
+
+ private:
+ struct Node {
+ Node() : index(static_cast<size_t>(-1)), is_public(false) {}
+ Node(size_t i, bool p) : index(i), is_public(p) {}
+
+ size_t index;
+ bool is_public;
+ };
+
+ using LibraryMap = std::map<const Target*, Node>;
+ LibraryMap map_;
+
+ DISALLOW_COPY_AND_ASSIGN(InheritedLibraries);
+};
+
+#endif // TOOLS_GN_INHERITED_LIBRARIES_H_
diff --git a/gn/src/gn/inherited_libraries_unittest.cc b/gn/src/gn/inherited_libraries_unittest.cc
new file mode 100644
index 00000000000..bd7742613bd
--- /dev/null
+++ b/gn/src/gn/inherited_libraries_unittest.cc
@@ -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.
+
+#include "gn/inherited_libraries.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+// In these tests, Pair can't be used conveniently because the
+// "const" won't be inferred and the types won't match. This helper makes the
+// right type of pair with the const Target.
+std::pair<const Target*, bool> Pair(const Target* t, bool b) {
+ return std::pair<const Target*, bool>(t, b);
+}
+
+} // namespace
+
+TEST(InheritedLibraries, Unique) {
+ TestWithScope setup;
+
+ Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
+ Target b(setup.settings(), Label(SourceDir("//foo/"), "b"));
+
+ // Setup, add the two targets as private.
+ InheritedLibraries libs;
+ libs.Append(&a, false);
+ libs.Append(&b, false);
+ auto result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(2u, result.size());
+ EXPECT_EQ(Pair(&a, false), result[0]);
+ EXPECT_EQ(Pair(&b, false), result[1]);
+
+ // Add again as private, this should be a NOP.
+ libs.Append(&a, false);
+ libs.Append(&b, false);
+ result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(2u, result.size());
+ EXPECT_EQ(Pair(&a, false), result[0]);
+ EXPECT_EQ(Pair(&b, false), result[1]);
+
+ // Add as public, this should make both public.
+ libs.Append(&a, true);
+ libs.Append(&b, true);
+ result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(2u, result.size());
+ EXPECT_EQ(Pair(&a, true), result[0]);
+ EXPECT_EQ(Pair(&b, true), result[1]);
+
+ // Add again private, they should stay public.
+ libs.Append(&a, false);
+ libs.Append(&b, false);
+ result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(2u, result.size());
+ EXPECT_EQ(Pair(&a, true), result[0]);
+ EXPECT_EQ(Pair(&b, true), result[1]);
+}
+
+TEST(InheritedLibraries, AppendInherited) {
+ TestWithScope setup;
+
+ Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
+ Target b(setup.settings(), Label(SourceDir("//foo/"), "b"));
+ Target w(setup.settings(), Label(SourceDir("//foo/"), "w"));
+ Target x(setup.settings(), Label(SourceDir("//foo/"), "x"));
+ Target y(setup.settings(), Label(SourceDir("//foo/"), "y"));
+ Target z(setup.settings(), Label(SourceDir("//foo/"), "z"));
+
+ InheritedLibraries libs;
+ libs.Append(&a, false);
+ libs.Append(&b, false);
+
+ // Appending these things with private inheritance should make them private,
+ // no matter how they're listed in the appended class.
+ InheritedLibraries append_private;
+ append_private.Append(&a, true);
+ append_private.Append(&b, false);
+ append_private.Append(&w, true);
+ append_private.Append(&x, false);
+ libs.AppendInherited(append_private, false);
+
+ auto result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(4u, result.size());
+ EXPECT_EQ(Pair(&a, false), result[0]);
+ EXPECT_EQ(Pair(&b, false), result[1]);
+ EXPECT_EQ(Pair(&w, false), result[2]);
+ EXPECT_EQ(Pair(&x, false), result[3]);
+
+ // Appending these things with public inheritance should convert them.
+ InheritedLibraries append_public;
+ append_public.Append(&a, true);
+ append_public.Append(&b, false);
+ append_public.Append(&y, true);
+ append_public.Append(&z, false);
+ libs.AppendInherited(append_public, true);
+
+ result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(6u, result.size());
+ EXPECT_EQ(Pair(&a, true), result[0]); // Converted to public.
+ EXPECT_EQ(Pair(&b, false), result[1]);
+ EXPECT_EQ(Pair(&w, false), result[2]);
+ EXPECT_EQ(Pair(&x, false), result[3]);
+ EXPECT_EQ(Pair(&y, true), result[4]); // Appended as public.
+ EXPECT_EQ(Pair(&z, false), result[5]);
+}
+
+TEST(InheritedLibraries, AppendPublicSharedLibraries) {
+ TestWithScope setup;
+ InheritedLibraries append;
+
+ // Two source sets.
+ Target set_pub(setup.settings(), Label(SourceDir("//foo/"), "set_pub"));
+ set_pub.set_output_type(Target::SOURCE_SET);
+ append.Append(&set_pub, true);
+ Target set_priv(setup.settings(), Label(SourceDir("//foo/"), "set_priv"));
+ set_priv.set_output_type(Target::SOURCE_SET);
+ append.Append(&set_priv, false);
+
+ // Two shared libraries.
+ Target sh_pub(setup.settings(), Label(SourceDir("//foo/"), "sh_pub"));
+ sh_pub.set_output_type(Target::SHARED_LIBRARY);
+ append.Append(&sh_pub, true);
+ Target sh_priv(setup.settings(), Label(SourceDir("//foo/"), "sh_priv"));
+ sh_priv.set_output_type(Target::SHARED_LIBRARY);
+ append.Append(&sh_priv, false);
+
+ InheritedLibraries libs;
+ libs.AppendPublicSharedLibraries(append, true);
+
+ auto result = libs.GetOrderedAndPublicFlag();
+ ASSERT_EQ(1u, result.size());
+ EXPECT_EQ(Pair(&sh_pub, true), result[0]);
+}
diff --git a/gn/src/gn/input_conversion.cc b/gn/src/gn/input_conversion.cc
new file mode 100644
index 00000000000..e82a939b5e6
--- /dev/null
+++ b/gn/src/gn/input_conversion.cc
@@ -0,0 +1,359 @@
+// 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 "gn/input_conversion.h"
+
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "gn/build_settings.h"
+#include "gn/err.h"
+#include "gn/input_file.h"
+#include "gn/label.h"
+#include "gn/parse_tree.h"
+#include "gn/parser.h"
+#include "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/tokenizer.h"
+#include "gn/value.h"
+
+namespace {
+
+enum ValueOrScope {
+ PARSE_VALUE, // Treat the input as an expression.
+ PARSE_SCOPE, // Treat the input as code and return the resulting scope.
+};
+
+// Sets the origin of the value and any nested values with the given node.
+Value ParseValueOrScope(const Settings* settings,
+ const std::string& input,
+ ValueOrScope what,
+ const ParseNode* origin,
+ Err* err) {
+ // The memory for these will be kept around by the input file manager
+ // so the origin parse nodes for the values will be preserved.
+ InputFile* input_file;
+ std::vector<Token>* tokens;
+ std::unique_ptr<ParseNode>* parse_root_ptr;
+ g_scheduler->input_file_manager()->AddDynamicInput(SourceFile(), &input_file,
+ &tokens, &parse_root_ptr);
+
+ input_file->SetContents(input);
+ if (origin) {
+ // This description will be the blame for any error messages caused by
+ // script parsing or if a value is blamed. It will say
+ // "Error at <...>:line:char" so here we try to make a string for <...>
+ // that reads well in this context.
+ input_file->set_friendly_name("dynamically parsed input that " +
+ origin->GetRange().begin().Describe(true) +
+ " loaded ");
+ } else {
+ input_file->set_friendly_name("dynamic input");
+ }
+
+ *tokens = Tokenizer::Tokenize(input_file, err);
+ if (err->has_error())
+ return Value();
+
+ // Parse the file according to what we're looking for.
+ if (what == PARSE_VALUE)
+ *parse_root_ptr = Parser::ParseValue(*tokens, err);
+ else
+ *parse_root_ptr = Parser::Parse(*tokens, err); // Will return a Block.
+ if (err->has_error())
+ return Value();
+ ParseNode* parse_root = parse_root_ptr->get(); // For nicer syntax below.
+
+ // It's valid for the result to be a null pointer, this just means that the
+ // script returned nothing.
+ if (!parse_root)
+ return Value();
+
+ std::unique_ptr<Scope> scope = std::make_unique<Scope>(settings);
+ Value result = parse_root->Execute(scope.get(), err);
+ if (err->has_error())
+ return Value();
+
+ // When we want the result as a scope, the result is actually the scope
+ // we made, rather than the result of running the block (which will be empty).
+ if (what == PARSE_SCOPE) {
+ DCHECK(result.type() == Value::NONE);
+ result = Value(origin, std::move(scope));
+ }
+ return result;
+}
+
+Value ParseList(const std::string& input, const ParseNode* origin, Err* err) {
+ Value ret(origin, Value::LIST);
+ std::vector<std::string> as_lines = base::SplitString(
+ input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ // Trim one empty line from the end since the last line might end in a
+ // newline. If the user wants more trimming, they'll specify "trim" in the
+ // input conversion options.
+ if (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
+ as_lines.resize(as_lines.size() - 1);
+
+ ret.list_value().reserve(as_lines.size());
+ for (const auto& line : as_lines)
+ ret.list_value().push_back(Value(origin, line));
+ return ret;
+}
+
+bool IsIdentifier(const std::string_view& buffer) {
+ DCHECK(buffer.size() > 0);
+ if (!Tokenizer::IsIdentifierFirstChar(buffer[0]))
+ return false;
+ for (size_t i = 1; i < buffer.size(); i++)
+ if (!Tokenizer::IsIdentifierContinuingChar(buffer[i]))
+ return false;
+ return true;
+}
+
+Value ParseJSONValue(const Settings* settings,
+ const base::Value& value,
+ const ParseNode* origin,
+ InputFile* input_file,
+ Err* err) {
+ switch (value.type()) {
+ case base::Value::Type::NONE:
+ *err = Err(origin, "Null values are not supported.");
+ return Value();
+ case base::Value::Type::BOOLEAN:
+ return Value(origin, value.GetBool());
+ case base::Value::Type::INTEGER:
+ return Value(origin, static_cast<int64_t>(value.GetInt()));
+ case base::Value::Type::STRING:
+ return Value(origin, value.GetString());
+ case base::Value::Type::BINARY:
+ *err = Err(origin, "Binary values are not supported.");
+ return Value();
+ case base::Value::Type::DICTIONARY: {
+ std::unique_ptr<Scope> scope = std::make_unique<Scope>(settings);
+ for (auto it : value.DictItems()) {
+ Value parsed_value =
+ ParseJSONValue(settings, it.second, origin, input_file, err);
+ if (!IsIdentifier(it.first)) {
+ *err = Err(origin, "Invalid identifier \"" + it.first + "\".");
+ return Value();
+ }
+ // Search for the key in the input file. We know it's present because
+ // it was parsed by the JSON reader, but we need its location to
+ // construct a std::string_view that can be used as key in the Scope.
+ size_t off = input_file->contents().find("\"" + it.first + "\"");
+ if (off == std::string::npos) {
+ *err = Err(origin, "Invalid encoding \"" + it.first + "\".");
+ return Value();
+ }
+ std::string_view key(&input_file->contents()[off + 1], it.first.size());
+ scope->SetValue(key, std::move(parsed_value), origin);
+ }
+ return Value(origin, std::move(scope));
+ }
+ case base::Value::Type::LIST: {
+ Value result(origin, Value::LIST);
+ result.list_value().reserve(value.GetList().size());
+ for (const auto& val : value.GetList()) {
+ Value parsed_value =
+ ParseJSONValue(settings, val, origin, input_file, err);
+ result.list_value().push_back(parsed_value);
+ }
+ return result;
+ }
+ }
+ return Value();
+}
+
+// Parses the JSON string and converts it to GN value.
+Value ParseJSON(const Settings* settings,
+ const std::string& input,
+ const ParseNode* origin,
+ Err* err) {
+ InputFile* input_file;
+ std::vector<Token>* tokens;
+ std::unique_ptr<ParseNode>* parse_root_ptr;
+ g_scheduler->input_file_manager()->AddDynamicInput(SourceFile(), &input_file,
+ &tokens, &parse_root_ptr);
+ input_file->SetContents(input);
+
+ int error_code_out;
+ std::string error_msg_out;
+ std::unique_ptr<base::Value> value = base::JSONReader::ReadAndReturnError(
+ input, base::JSONParserOptions::JSON_PARSE_RFC, &error_code_out,
+ &error_msg_out);
+ if (!value) {
+ *err = Err(origin, "Input is not a valid JSON: " + error_msg_out);
+ return Value();
+ }
+
+ return ParseJSONValue(settings, *value, origin, input_file, err);
+}
+
+// Backend for ConvertInputToValue, this takes the extracted string for the
+// input conversion so we can recursively call ourselves to handle the optional
+// "trim" prefix. This original value is also kept for the purposes of throwing
+// errors.
+Value DoConvertInputToValue(const Settings* settings,
+ const std::string& input,
+ const ParseNode* origin,
+ const Value& original_input_conversion,
+ const std::string& input_conversion,
+ Err* err) {
+ if (input_conversion.empty())
+ return Value(); // Empty string means discard the result.
+
+ const char kTrimPrefix[] = "trim ";
+ if (base::StartsWith(input_conversion, kTrimPrefix,
+ base::CompareCase::SENSITIVE)) {
+ std::string trimmed;
+ base::TrimWhitespaceASCII(input, base::TRIM_ALL, &trimmed);
+
+ // Remove "trim" prefix from the input conversion and re-run.
+ return DoConvertInputToValue(
+ settings, trimmed, origin, original_input_conversion,
+ input_conversion.substr(std::size(kTrimPrefix) - 1), err);
+ }
+
+ if (input_conversion == "value")
+ return ParseValueOrScope(settings, input, PARSE_VALUE, origin, err);
+ if (input_conversion == "string")
+ return Value(origin, input);
+ if (input_conversion == "list lines")
+ return ParseList(input, origin, err);
+ if (input_conversion == "scope")
+ return ParseValueOrScope(settings, input, PARSE_SCOPE, origin, err);
+ if (input_conversion == "json")
+ return ParseJSON(settings, input, origin, err);
+
+ *err = Err(original_input_conversion, "Not a valid input_conversion.",
+ "Run gn help input_conversion to see your options.");
+ return Value();
+}
+
+} // namespace
+
+const char kInputOutputConversion_Help[] =
+ R"(Input and output conversion
+
+ Input and output conversions are arguments to file and process functions
+ that specify how to convert data to or from external formats. The possible
+ values for parameters specifying conversions are:
+
+ "" (the default)
+ input: Discard the result and return None.
+
+ output: If value is a list, then "list lines"; otherwise "value".
+
+ "list lines"
+ input:
+ Return the file contents as a list, with a string for each line. The
+ newlines will not be present in the result. The last line may or may
+ not end in a newline.
+
+ After splitting, each individual line will be trimmed of whitespace on
+ both ends.
+
+ output:
+ Renders the value contents as a list, with a string for each line. The
+ newlines will not be present in the result. The last line will end in
+ with a newline.
+
+ "scope"
+ input:
+ Execute the block as GN code and return a scope with the resulting
+ values in it. If the input was:
+ a = [ "hello.cc", "world.cc" ]
+ b = 26
+ and you read the result into a variable named "val", then you could
+ access contents the "." operator on "val":
+ sources = val.a
+ some_count = val.b
+
+ output:
+ Renders the value contents as a GN code block, reversing the input
+ result above.
+
+ "string"
+ input: Return the file contents into a single string.
+
+ output:
+ Render the value contents into a single string. The output is:
+ a string renders with quotes, e.g. "str"
+ an integer renders as a stringified integer, e.g. "6"
+ a boolean renders as the associated string, e.g. "true"
+ a list renders as a representation of its contents, e.g. "[\"str\", 6]"
+ a scope renders as a GN code block of its values. If the Value was:
+ Value val;
+ val.a = [ "hello.cc", "world.cc" ];
+ val.b = 26
+ the resulting output would be:
+ "{
+ a = [ \"hello.cc\", \"world.cc\" ]
+ b = 26
+ }"
+
+ "value"
+ input:
+ Parse the input as if it was a literal rvalue in a buildfile. Examples of
+ typical program output using this mode:
+ [ "foo", "bar" ] (result will be a list)
+ or
+ "foo bar" (result will be a string)
+ or
+ 5 (result will be an integer)
+
+ Note that if the input is empty, the result will be a null value which
+ will produce an error if assigned to a variable.
+
+ output:
+ Render the value contents as a literal rvalue. Strings render with
+ escaped quotes.
+
+ "json"
+ input: Parse the input as a JSON and convert it to equivalent GN rvalue.
+
+ output: Convert the Value to equivalent JSON value.
+
+ The data type mapping is:
+ a string in JSON maps to string in GN
+ an integer in JSON maps to integer in GN
+ a float in JSON is unsupported and will result in an error
+ an object in JSON maps to scope in GN
+ an array in JSON maps to list in GN
+ a boolean in JSON maps to boolean in GN
+ a null in JSON is unsupported and will result in an error
+
+ Nota that the input dictionary keys have to be valid GN identifiers
+ otherwise they will produce an error.
+
+ "trim ..." (input only)
+ Prefixing any of the other transformations with the word "trim" will
+ result in whitespace being trimmed from the beginning and end of the
+ result before processing.
+
+ Examples: "trim string" or "trim list lines"
+
+ Note that "trim value" is useless because the value parser skips
+ whitespace anyway.
+)";
+
+Value ConvertInputToValue(const Settings* settings,
+ const std::string& input,
+ const ParseNode* origin,
+ const Value& input_conversion_value,
+ Err* err) {
+ if (input_conversion_value.type() == Value::NONE)
+ return Value(); // Allow null inputs to mean discard the result.
+ if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ return DoConvertInputToValue(settings, input, origin, input_conversion_value,
+ input_conversion_value.string_value(), err);
+}
diff --git a/gn/tools/gn/input_conversion.h b/gn/src/gn/input_conversion.h
index 6e09eb4b0e9..6e09eb4b0e9 100644
--- a/gn/tools/gn/input_conversion.h
+++ b/gn/src/gn/input_conversion.h
diff --git a/gn/src/gn/input_conversion_unittest.cc b/gn/src/gn/input_conversion_unittest.cc
new file mode 100644
index 00000000000..054b3377609
--- /dev/null
+++ b/gn/src/gn/input_conversion_unittest.cc
@@ -0,0 +1,275 @@
+// 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 "gn/input_conversion.h"
+
+#include "gn/err.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+namespace {
+
+// InputConversion needs a global scheduler object.
+class InputConversionTest : public TestWithScheduler {
+ public:
+ InputConversionTest() = default;
+
+ const Settings* settings() { return setup_.settings(); }
+
+ private:
+ TestWithScope setup_;
+};
+
+} // namespace
+
+TEST_F(InputConversionTest, String) {
+ Err err;
+ std::string input("\nfoo bar \n");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "string"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::STRING, result.type());
+ EXPECT_EQ(input, result.string_value());
+
+ // Test with trimming.
+ result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "trim string"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::STRING, result.type());
+ EXPECT_EQ("foo bar", result.string_value());
+}
+
+TEST_F(InputConversionTest, ListLines) {
+ Err err;
+ std::string input("\nfoo\nbar \n\n");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "list lines"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::LIST, result.type());
+ ASSERT_EQ(4u, result.list_value().size());
+ EXPECT_EQ("", result.list_value()[0].string_value());
+ EXPECT_EQ("foo", result.list_value()[1].string_value());
+ EXPECT_EQ("bar", result.list_value()[2].string_value());
+ EXPECT_EQ("", result.list_value()[3].string_value());
+
+ // Test with trimming.
+ result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "trim list lines"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::LIST, result.type());
+ ASSERT_EQ(2u, result.list_value().size());
+ EXPECT_EQ("foo", result.list_value()[0].string_value());
+ EXPECT_EQ("bar", result.list_value()[1].string_value());
+}
+
+TEST_F(InputConversionTest, ValueString) {
+ Err err;
+ std::string input("\"str\"");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::STRING, result.type());
+ EXPECT_EQ("str", result.string_value());
+}
+
+TEST_F(InputConversionTest, ValueInt) {
+ Err err;
+ std::string input("\n\n 6 \n ");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::INTEGER, result.type());
+ EXPECT_EQ(6, result.int_value());
+}
+
+TEST_F(InputConversionTest, ValueList) {
+ Err err;
+ std::string input("\n [ \"a\", 5]");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(Value::LIST, result.type());
+ ASSERT_EQ(2u, result.list_value().size());
+ EXPECT_EQ("a", result.list_value()[0].string_value());
+ EXPECT_EQ(5, result.list_value()[1].int_value());
+}
+
+TEST_F(InputConversionTest, ValueDict) {
+ Err err;
+ std::string input("\n a = 5 b = \"foo\" c = a + 2");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "scope"), &err);
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(Value::SCOPE, result.type());
+
+ const Value* a_value = result.scope_value()->GetValue("a");
+ ASSERT_TRUE(a_value);
+ EXPECT_EQ(5, a_value->int_value());
+
+ const Value* b_value = result.scope_value()->GetValue("b");
+ ASSERT_TRUE(b_value);
+ EXPECT_EQ("foo", b_value->string_value());
+
+ const Value* c_value = result.scope_value()->GetValue("c");
+ ASSERT_TRUE(c_value);
+ EXPECT_EQ(7, c_value->int_value());
+
+ // Tests that when we get Values out of the input conversion, the resulting
+ // values have an origin set to something corresponding to the input.
+ const ParseNode* a_origin = a_value->origin();
+ ASSERT_TRUE(a_origin);
+ LocationRange a_range = a_origin->GetRange();
+ EXPECT_EQ(2, a_range.begin().line_number());
+ EXPECT_EQ(6, a_range.begin().column_number());
+
+ const InputFile* a_file = a_range.begin().file();
+ EXPECT_EQ(input, a_file->contents());
+}
+
+TEST_F(InputConversionTest, ValueJSON) {
+ Err err;
+ std::string input(R"*({
+ "a": 5,
+ "b": "foo",
+ "c": {
+ "d": true,
+ "e": [
+ {
+ "f": "bar"
+ }
+ ]
+ }
+})*");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "json"), &err);
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(Value::SCOPE, result.type());
+
+ const Value* a_value = result.scope_value()->GetValue("a");
+ ASSERT_TRUE(a_value);
+ EXPECT_EQ(5, a_value->int_value());
+
+ const Value* b_value = result.scope_value()->GetValue("b");
+ ASSERT_TRUE(b_value);
+ EXPECT_EQ("foo", b_value->string_value());
+
+ const Value* c_value = result.scope_value()->GetValue("c");
+ ASSERT_TRUE(c_value);
+ ASSERT_EQ(Value::SCOPE, c_value->type());
+
+ const Value* d_value = c_value->scope_value()->GetValue("d");
+ ASSERT_TRUE(d_value);
+ EXPECT_EQ(true, d_value->boolean_value());
+
+ const Value* e_value = c_value->scope_value()->GetValue("e");
+ ASSERT_TRUE(e_value);
+ ASSERT_EQ(Value::LIST, e_value->type());
+
+ EXPECT_EQ(1u, e_value->list_value().size());
+ ASSERT_EQ(Value::SCOPE, e_value->list_value()[0].type());
+ const Value* f_value = e_value->list_value()[0].scope_value()->GetValue("f");
+ ASSERT_TRUE(f_value);
+ EXPECT_EQ("bar", f_value->string_value());
+}
+
+TEST_F(InputConversionTest, ValueJSONInvalidInput) {
+ Err err;
+ std::string input(R"*({
+ "a": 5,
+ "b":
+})*");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "json"), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Input is not a valid JSON: Line: 4, column: 2, Unexpected token.",
+ err.message());
+}
+
+TEST_F(InputConversionTest, ValueJSONUnsupportedValue) {
+ Err err;
+ std::string input(R"*({
+ "a": null
+})*");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "json"), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Null values are not supported.", err.message());
+}
+
+TEST_F(InputConversionTest, ValueJSONInvalidVariable) {
+ Err err;
+ std::string input(R"*({
+ "a\\x0001b": 5
+})*");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "json"), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ("Invalid identifier \"a\\x0001b\".", err.message());
+}
+
+TEST_F(InputConversionTest, ValueJSONUnsupported) {
+ Err err;
+ std::string input(R"*({
+ "d": 0.0
+})*");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "json"), &err);
+ EXPECT_TRUE(err.has_error());
+ // Doubles aren't supported.
+ EXPECT_EQ("Input is not a valid JSON: ", err.message());
+}
+
+TEST_F(InputConversionTest, ValueEmpty) {
+ Err err;
+ Value result = ConvertInputToValue(settings(), "", nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::NONE, result.type());
+}
+
+TEST_F(InputConversionTest, ValueError) {
+ static const char* const kTests[] = {
+ "\n [ \"a\", 5\nfoo bar",
+
+ // Blocks not allowed.
+ "{ foo = 5 }",
+
+ // Function calls not allowed.
+ "print(5)",
+
+ // Trailing junk not allowed.
+ "233105-1",
+
+ // Non-literals hidden in arrays are not allowed.
+ "[233105 - 1]",
+ "[rebase_path(\"//\")]",
+ };
+
+ for (auto* test : kTests) {
+ Err err;
+ std::string input(test);
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_TRUE(err.has_error()) << test;
+ }
+}
+
+// Passing none or the empty string for input conversion should ignore the
+// result.
+TEST_F(InputConversionTest, Ignore) {
+ Err err;
+ Value result = ConvertInputToValue(settings(), "foo", nullptr, Value(), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::NONE, result.type());
+
+ result =
+ ConvertInputToValue(settings(), "foo", nullptr, Value(nullptr, ""), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(Value::NONE, result.type());
+}
diff --git a/gn/src/gn/input_file.cc b/gn/src/gn/input_file.cc
new file mode 100644
index 00000000000..8c92c480f17
--- /dev/null
+++ b/gn/src/gn/input_file.cc
@@ -0,0 +1,26 @@
+// 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 "gn/input_file.h"
+
+#include "base/files/file_util.h"
+
+InputFile::InputFile(const SourceFile& name)
+ : name_(name), dir_(name_.GetDir()) {}
+
+InputFile::~InputFile() = default;
+
+void InputFile::SetContents(const std::string& c) {
+ contents_loaded_ = true;
+ contents_ = c;
+}
+
+bool InputFile::Load(const base::FilePath& system_path) {
+ if (base::ReadFileToString(system_path, &contents_)) {
+ contents_loaded_ = true;
+ physical_name_ = system_path;
+ return true;
+ }
+ return false;
+}
diff --git a/gn/src/gn/input_file.h b/gn/src/gn/input_file.h
new file mode 100644
index 00000000000..efb15bd9d4e
--- /dev/null
+++ b/gn/src/gn/input_file.h
@@ -0,0 +1,65 @@
+// 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 TOOLS_GN_INPUT_FILE_H_
+#define TOOLS_GN_INPUT_FILE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+
+class InputFile {
+ public:
+ explicit InputFile(const SourceFile& name);
+
+ ~InputFile();
+
+ // The virtual name passed into the constructor. This does not take into
+ // account whether the file was loaded from the secondary source tree (see
+ // BuildSettings secondary_source_path).
+ const SourceFile& name() const { return name_; }
+
+ // The directory is just a cached version of name()->GetDir() but we get this
+ // a lot so computing it once up front saves a bunch of work.
+ const SourceDir& dir() const { return dir_; }
+
+ // The physical name tells the actual name on disk, if there is one.
+ const base::FilePath& physical_name() const { return physical_name_; }
+
+ // The friendly name can be set to override the name() in cases where there
+ // is no name (like SetContents is used instead) or if the name doesn't
+ // make sense. This will be displayed in error messages.
+ const std::string& friendly_name() const { return friendly_name_; }
+ void set_friendly_name(const std::string& f) { friendly_name_ = f; }
+
+ const std::string& contents() const {
+ DCHECK(contents_loaded_);
+ return contents_;
+ }
+
+ // For testing and in cases where this input doesn't actually refer to
+ // "a file".
+ void SetContents(const std::string& c);
+
+ // Loads the given file synchronously, returning true on success. This
+ bool Load(const base::FilePath& system_path);
+
+ private:
+ SourceFile name_;
+ SourceDir dir_;
+
+ base::FilePath physical_name_;
+ std::string friendly_name_;
+
+ bool contents_loaded_ = false;
+ std::string contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputFile);
+};
+
+#endif // TOOLS_GN_INPUT_FILE_H_
diff --git a/gn/src/gn/input_file_manager.cc b/gn/src/gn/input_file_manager.cc
new file mode 100644
index 00000000000..7e0d7ee7993
--- /dev/null
+++ b/gn/src/gn/input_file_manager.cc
@@ -0,0 +1,341 @@
+// 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 "gn/input_file_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/stl_util.h"
+#include "gn/filesystem_utils.h"
+#include "gn/parser.h"
+#include "gn/scheduler.h"
+#include "gn/scope_per_file_provider.h"
+#include "gn/tokenizer.h"
+#include "gn/trace.h"
+#include "gn/vector_utils.h"
+
+namespace {
+
+// The opposite of std::lock_guard.
+struct ScopedUnlock {
+ ScopedUnlock(std::unique_lock<std::mutex>& lock) : lock_(lock) {
+ lock_.unlock();
+ }
+ ~ScopedUnlock() { lock_.lock(); }
+
+ private:
+ std::unique_lock<std::mutex>& lock_;
+};
+
+void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb,
+ const ParseNode* node) {
+ cb(node);
+}
+
+bool DoLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFileManager::SyncLoadFileCallback load_file_callback,
+ InputFile* file,
+ std::vector<Token>* tokens,
+ std::unique_ptr<ParseNode>* root,
+ Err* err) {
+ // Do all of this stuff outside the lock. We should not give out file
+ // pointers until the read is complete.
+ if (g_scheduler->verbose_logging()) {
+ std::string logmsg = name.value();
+ if (origin.begin().file())
+ logmsg += " (referenced from " + origin.begin().Describe(false) + ")";
+ g_scheduler->Log("Loading", logmsg);
+ }
+
+ // Read.
+ base::FilePath primary_path = build_settings->GetFullPath(name);
+ ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, name.value());
+ if (load_file_callback) {
+ if (!load_file_callback(name, file)) {
+ *err = Err(origin, "Can't load input file.",
+ "File not mocked by load_file_callback:\n " + name.value());
+ return false;
+ }
+ } else if (!file->Load(primary_path)) {
+ if (!build_settings->secondary_source_path().empty()) {
+ // Fall back to secondary source tree.
+ base::FilePath secondary_path =
+ build_settings->GetFullPathSecondary(name);
+ if (!file->Load(secondary_path)) {
+ *err = Err(origin, "Can't load input file.",
+ "Unable to load:\n " + FilePathToUTF8(primary_path) +
+ "\n"
+ "I also checked in the secondary tree for:\n " +
+ FilePathToUTF8(secondary_path));
+ return false;
+ }
+ } else {
+ *err = Err(origin,
+ "Unable to load \"" + FilePathToUTF8(primary_path) + "\".");
+ return false;
+ }
+ }
+ load_trace.Done();
+
+ ScopedTrace exec_trace(TraceItem::TRACE_FILE_PARSE, name.value());
+
+ // Tokenize.
+ *tokens = Tokenizer::Tokenize(file, err);
+ if (err->has_error())
+ return false;
+
+ // Parse.
+ *root = Parser::Parse(*tokens, err);
+ if (err->has_error())
+ return false;
+
+ exec_trace.Done();
+ return true;
+}
+
+} // namespace
+
+InputFileManager::InputFileData::InputFileData(const SourceFile& file_name)
+ : file(file_name), loaded(false), sync_invocation(false) {}
+
+InputFileManager::InputFileData::~InputFileData() = default;
+
+InputFileManager::InputFileManager() = default;
+
+InputFileManager::~InputFileManager() {
+ // Should be single-threaded by now.
+}
+
+bool InputFileManager::AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const FileLoadCallback& callback,
+ Err* err) {
+ // Try not to schedule callbacks while holding the lock. All cases that don't
+ // want to schedule should return early. Otherwise, this will be scheduled
+ // after we leave the lock.
+ std::function<void()> schedule_this;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ InputFileMap::const_iterator found = input_files_.find(file_name);
+ if (found == input_files_.end()) {
+ // New file, schedule load.
+ std::unique_ptr<InputFileData> data =
+ std::make_unique<InputFileData>(file_name);
+ data->scheduled_callbacks.push_back(callback);
+ schedule_this = [this, origin, build_settings, file_name,
+ file = &data->file]() {
+ BackgroundLoadFile(origin, build_settings, file_name, file);
+ };
+ input_files_[file_name] = std::move(data);
+
+ } else {
+ InputFileData* data = found->second.get();
+
+ // Prevent mixing async and sync loads. See SyncLoadFile for discussion.
+ if (data->sync_invocation) {
+ g_scheduler->FailWithError(Err(
+ origin, "Load type mismatch.",
+ "The file \"" + file_name.value() +
+ "\" was previously loaded\n"
+ "synchronously (via an import) and now you're trying to load "
+ "it "
+ "asynchronously\n(via a deps rule). This is a class 2 "
+ "misdemeanor: "
+ "a single input file must\nbe loaded the same way each time to "
+ "avoid blowing my tiny, tiny mind."));
+ return false;
+ }
+
+ if (data->loaded) {
+ // Can just directly issue the callback on the background thread.
+ schedule_this = [callback, root = data->parsed_root.get()]() {
+ InvokeFileLoadCallback(callback, root);
+ };
+ } else {
+ // Load is pending on this file, schedule the invoke.
+ data->scheduled_callbacks.push_back(callback);
+ return true;
+ }
+ }
+ }
+ g_scheduler->ScheduleWork(std::move(schedule_this));
+ return true;
+}
+
+const ParseNode* InputFileManager::SyncLoadFile(
+ const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ Err* err) {
+ std::unique_lock<std::mutex> lock(lock_);
+
+ InputFileData* data = nullptr;
+ InputFileMap::iterator found = input_files_.find(file_name);
+ if (found == input_files_.end()) {
+ // Haven't seen this file yet, start loading right now.
+ std::unique_ptr<InputFileData> new_data =
+ std::make_unique<InputFileData>(file_name);
+ data = new_data.get();
+ data->sync_invocation = true;
+ input_files_[file_name] = std::move(new_data);
+
+ ScopedUnlock unlock(lock);
+ if (!LoadFile(origin, build_settings, file_name, &data->file, err))
+ return nullptr;
+ } else {
+ // This file has either been loaded or is pending loading.
+ data = found->second.get();
+
+ if (!data->sync_invocation) {
+ // Don't allow mixing of sync and async loads. If an async load is
+ // scheduled and then a bunch of threads need to load it synchronously
+ // and block on it loading, it could deadlock or at least cause a lot
+ // of wasted CPU while those threads wait for the load to complete (which
+ // may be far back in the input queue).
+ //
+ // We could work around this by promoting the load to a sync load. This
+ // requires a bunch of extra code to either check flags and likely do
+ // extra locking (bad) or to just do both types of load on the file and
+ // deal with the race condition.
+ //
+ // I have no practical way to test this, and generally we should have
+ // all include files processed synchronously and all build files
+ // processed asynchronously, so it doesn't happen in practice.
+ *err = Err(origin, "Load type mismatch.",
+ "The file \"" + file_name.value() +
+ "\" was previously loaded\n"
+ "asynchronously (via a deps rule) and now you're trying "
+ "to load it "
+ "synchronously.\nThis is a class 2 misdemeanor: a single "
+ "input file "
+ "must be loaded the same way\neach time to avoid blowing "
+ "my tiny, "
+ "tiny mind.");
+ return nullptr;
+ }
+
+ if (!data->loaded) {
+ // Wait for the already-pending sync load to complete.
+ if (!data->completion_event) {
+ data->completion_event = std::make_unique<AutoResetEvent>();
+ }
+ {
+ ScopedUnlock unlock(lock);
+ data->completion_event->Wait();
+ }
+ // If there were multiple waiters on the same event, we now need to wake
+ // up the next one.
+ data->completion_event->Signal();
+ }
+ }
+
+ // The other load could have failed. It is possible that this thread's error
+ // will be reported to the scheduler before the other thread's (and the first
+ // error reported "wins"). Forward the parse error from the other load for
+ // this thread so that the error message is useful.
+ if (!data->parsed_root)
+ *err = data->parse_error;
+ return data->parsed_root.get();
+}
+
+void InputFileManager::AddDynamicInput(
+ const SourceFile& name,
+ InputFile** file,
+ std::vector<Token>** tokens,
+ std::unique_ptr<ParseNode>** parse_root) {
+ std::unique_ptr<InputFileData> data = std::make_unique<InputFileData>(name);
+ *file = &data->file;
+ *tokens = &data->tokens;
+ *parse_root = &data->parsed_root;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ dynamic_inputs_.push_back(std::move(data));
+ }
+}
+
+int InputFileManager::GetInputFileCount() const {
+ std::lock_guard<std::mutex> lock(lock_);
+ return static_cast<int>(input_files_.size());
+}
+
+void InputFileManager::AddAllPhysicalInputFileNamesToVectorSetSorter(
+ VectorSetSorter<base::FilePath>* sorter) const {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ for (const auto& file : input_files_) {
+ if (!file.second->file.physical_name().empty())
+ sorter->Add(file.second->file.physical_name());
+ }
+}
+
+void InputFileManager::BackgroundLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file) {
+ Err err;
+ if (!LoadFile(origin, build_settings, name, file, &err))
+ g_scheduler->FailWithError(err);
+}
+
+bool InputFileManager::LoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file,
+ Err* err) {
+ std::vector<Token> tokens;
+ std::unique_ptr<ParseNode> root;
+ bool success =
+ DoLoadFile(origin, build_settings, name, load_file_callback_, file, &tokens, &root, err);
+ // Can't return early. We have to ensure that the completion event is
+ // signaled in all cases bacause another thread could be blocked on this one.
+
+ // Save this pointer for running the callbacks below, which happens after the
+ // scoped ptr ownership is taken away inside the lock.
+ ParseNode* unowned_root = root.get();
+
+ std::vector<FileLoadCallback> callbacks;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ DCHECK(input_files_.find(name) != input_files_.end());
+
+ InputFileData* data = input_files_[name].get();
+ data->loaded = true;
+ if (success) {
+ data->tokens = std::move(tokens);
+ data->parsed_root = std::move(root);
+ } else {
+ data->parse_error = *err;
+ }
+
+ // Unblock waiters on this event.
+ //
+ // It's somewhat bad to signal this inside the lock. When it's used, it's
+ // lazily created inside the lock. So we need to do the check and signal
+ // inside the lock to avoid race conditions on the lazy creation of the
+ // lock.
+ //
+ // We could avoid this by creating the lock every time, but the lock is
+ // very seldom used and will generally be NULL, so my current theory is that
+ // several signals of a completion event inside a lock is better than
+ // creating about 1000 extra locks (one for each file).
+ if (data->completion_event)
+ data->completion_event->Signal();
+
+ callbacks = std::move(data->scheduled_callbacks);
+ }
+
+ // Run pending invocations. Theoretically we could schedule each of these
+ // separately to get some parallelism. But normally there will only be one
+ // item in the list, so that's extra overhead and complexity for no gain.
+ if (success) {
+ for (const auto& cb : callbacks)
+ cb(unowned_root);
+ }
+ return success;
+}
diff --git a/gn/src/gn/input_file_manager.h b/gn/src/gn/input_file_manager.h
new file mode 100644
index 00000000000..d5e55533074
--- /dev/null
+++ b/gn/src/gn/input_file_manager.h
@@ -0,0 +1,175 @@
+// 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 TOOLS_GN_INPUT_FILE_MANAGER_H_
+#define TOOLS_GN_INPUT_FILE_MANAGER_H_
+
+#include <functional>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/settings.h"
+#include "gn/vector_utils.h"
+#include "util/auto_reset_event.h"
+
+class BuildSettings;
+class Err;
+class LocationRange;
+class ParseNode;
+class Token;
+
+// Manages loading and parsing files from disk. This doesn't actually have
+// any context for executing the results, so potentially multiple configs
+// could use the same input file (saving parsing).
+//
+// This class is threadsafe.
+//
+// InputFile objects must never be deleted while the program is running since
+// various state points into them.
+class InputFileManager : public base::RefCountedThreadSafe<InputFileManager> {
+ public:
+ // Callback issued when a file is laoded. On auccess, the parse node will
+ // refer to the root block of the file. On failure, this will be NULL.
+ using FileLoadCallback = std::function<void(const ParseNode*)>;
+
+ // Callback to emulate SyncLoadFile in tests.
+ using SyncLoadFileCallback =
+ std::function<bool(const SourceFile& file_name, InputFile* file)>;
+
+ InputFileManager();
+
+ // Loads the given file and executes the callback on the worker pool.
+ //
+ // There are two types of errors. For errors known synchronously, the error
+ // will be set, it will return false, and no work will be scheduled.
+ //
+ // For parse errors and such that happen in the future, the error will be
+ // logged to the scheduler and the callback will be invoked with a null
+ // ParseNode pointer. The given |origin| will be blamed for the invocation.
+ bool AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const FileLoadCallback& callback,
+ Err* err);
+
+ // Loads and parses the given file synchronously, returning the root block
+ // corresponding to the parsed result. On error, return NULL and the given
+ // Err is set.
+ const ParseNode* SyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ Err* err);
+
+ // Creates an entry to manage the memory associated with keeping a parsed
+ // set of code in memory.
+ //
+ // The values pointed to by the parameters will be filled with pointers to
+ // the file, tokens, and parse node that this class created. The calling
+ // code is responsible for populating these values and maintaining
+ // threadsafety. This class' only job is to hold onto the memory and delete
+ // it when the program exits.
+ //
+ // This solves the problem that sometimes we need to execute something
+ // dynamic and save the result, but the values all have references to the
+ // nodes and file that created it. Either we need to reset the origin of
+ // the values and lose context for error reporting, or somehow keep the
+ // associated parse nodes, tokens, and file data in memory. This function
+ // allows the latter.
+ void AddDynamicInput(const SourceFile& name,
+ InputFile** file,
+ std::vector<Token>** tokens,
+ std::unique_ptr<ParseNode>** parse_root);
+
+ // Does not count dynamic input.
+ int GetInputFileCount() const;
+
+ // Add all physical input files to a VectorSetSorter instance.
+ // This allows fast merging and sorting with other file paths sets.
+ //
+ // This is more memory efficient than returning a vector of base::FilePath
+ // instance, especially with projects with a very large number of input files,
+ // but note that the VectorSetSorter only holds pointers to the
+ // items recorded in this InputFileManager instance, and it is up to the
+ // caller to ensure these will not change until the sorter is destroyed.
+ void AddAllPhysicalInputFileNamesToVectorSetSorter(
+ VectorSetSorter<base::FilePath>* sorter) const;
+
+ void set_load_file_callback(SyncLoadFileCallback load_file_callback) {
+ load_file_callback_ = load_file_callback;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<InputFileManager>;
+
+ struct InputFileData {
+ explicit InputFileData(const SourceFile& file_name);
+ ~InputFileData();
+
+ // Don't touch this outside the lock until it's marked loaded.
+ InputFile file;
+
+ bool loaded;
+
+ bool sync_invocation;
+
+ // Lists all invocations that need to be executed when the file completes
+ // loading.
+ std::vector<FileLoadCallback> scheduled_callbacks;
+
+ // Event to signal when the load is complete (or fails). This is lazily
+ // created only when a thread is synchronously waiting for this load (which
+ // only happens for imports).
+ std::unique_ptr<AutoResetEvent> completion_event;
+
+ std::vector<Token> tokens;
+
+ // Null before the file is loaded or if loading failed.
+ std::unique_ptr<ParseNode> parsed_root;
+ Err parse_error;
+ };
+
+ virtual ~InputFileManager();
+
+ void BackgroundLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file);
+
+ // Loads the given file. On error, sets the Err and return false.
+ bool LoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& name,
+ InputFile* file,
+ Err* err);
+
+ mutable std::mutex lock_;
+
+ // Maps repo-relative filenames to the corresponding owned pointer.
+ using InputFileMap =
+ std::unordered_map<SourceFile, std::unique_ptr<InputFileData>>;
+ InputFileMap input_files_;
+
+ // Tracks all dynamic inputs. The data are holders for memory management
+ // purposes and should not be read or modified by this class. The values
+ // will be vended out to the code creating the dynamic input, who is in
+ // charge of the threadsafety requirements.
+ //
+ // See AddDynamicInput().
+ std::vector<std::unique_ptr<InputFileData>> dynamic_inputs_;
+
+ // Used by unit tests to mock out SyncLoadFile().
+ SyncLoadFileCallback load_file_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputFileManager);
+};
+
+#endif // TOOLS_GN_INPUT_FILE_MANAGER_H_
diff --git a/gn/src/gn/item.cc b/gn/src/gn/item.cc
new file mode 100644
index 00000000000..3a3a166b36c
--- /dev/null
+++ b/gn/src/gn/item.cc
@@ -0,0 +1,60 @@
+// 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 "gn/item.h"
+
+#include "base/logging.h"
+#include "gn/settings.h"
+
+Item::Item(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files)
+ : settings_(settings),
+ label_(label),
+ build_dependency_files_(build_dependency_files),
+ defined_from_(nullptr) {}
+
+Item::~Item() = default;
+
+Config* Item::AsConfig() {
+ return nullptr;
+}
+const Config* Item::AsConfig() const {
+ return nullptr;
+}
+Pool* Item::AsPool() {
+ return nullptr;
+}
+const Pool* Item::AsPool() const {
+ return nullptr;
+}
+Target* Item::AsTarget() {
+ return nullptr;
+}
+const Target* Item::AsTarget() const {
+ return nullptr;
+}
+Toolchain* Item::AsToolchain() {
+ return nullptr;
+}
+const Toolchain* Item::AsToolchain() const {
+ return nullptr;
+}
+
+std::string Item::GetItemTypeName() const {
+ if (AsConfig())
+ return "config";
+ if (AsTarget())
+ return "target";
+ if (AsToolchain())
+ return "toolchain";
+ if (AsPool())
+ return "pool";
+ NOTREACHED();
+ return "this thing that I have no idea what it is";
+}
+
+bool Item::OnResolved(Err* err) {
+ return true;
+}
diff --git a/gn/src/gn/item.h b/gn/src/gn/item.h
new file mode 100644
index 00000000000..2942a665fd2
--- /dev/null
+++ b/gn/src/gn/item.h
@@ -0,0 +1,80 @@
+// 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 TOOLS_GN_ITEM_H_
+#define TOOLS_GN_ITEM_H_
+
+#include <set>
+#include <string>
+
+#include "gn/label.h"
+#include "gn/source_file.h"
+#include "gn/visibility.h"
+
+class Config;
+class ParseNode;
+class Pool;
+class Settings;
+class SourceFile;
+class Target;
+class Toolchain;
+
+// A named item (target, config, etc.) that participates in the dependency
+// graph.
+class Item {
+ public:
+ Item(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files = {});
+ virtual ~Item();
+
+ const Settings* settings() const { return settings_; }
+
+ // This is guaranteed to never change after construction so this can be
+ // accessed from any thread with no locking once the item is constructed.
+ const Label& label() const { return label_; }
+
+ const ParseNode* defined_from() const { return defined_from_; }
+ void set_defined_from(const ParseNode* df) { defined_from_ = df; }
+
+ Visibility& visibility() { return visibility_; }
+ const Visibility& visibility() const { return visibility_; }
+
+ // Manual RTTI.
+ virtual Config* AsConfig();
+ virtual const Config* AsConfig() const;
+ virtual Pool* AsPool();
+ virtual const Pool* AsPool() const;
+ virtual Target* AsTarget();
+ virtual const Target* AsTarget() const;
+ virtual Toolchain* AsToolchain();
+ virtual const Toolchain* AsToolchain() const;
+
+ // Returns a name like "target" or "config" for the type of item this is, to
+ // be used in logging and error messages.
+ std::string GetItemTypeName() const;
+
+ // Returns the set of build files that may affect this item, please refer to
+ // Scope for how this is determined.
+ const SourceFileSet& build_dependency_files() const {
+ return build_dependency_files_;
+ }
+
+ SourceFileSet& build_dependency_files() { return build_dependency_files_; }
+
+ // Called when this item is resolved, meaning it and all of its dependents
+ // have no unresolved deps. Returns true on success. Sets the error and
+ // returns false on failure.
+ virtual bool OnResolved(Err* err);
+
+ private:
+ const Settings* settings_;
+ Label label_;
+ SourceFileSet build_dependency_files_;
+ const ParseNode* defined_from_;
+
+ Visibility visibility_;
+};
+
+#endif // TOOLS_GN_ITEM_H_
diff --git a/gn/src/gn/json_project_writer.cc b/gn/src/gn/json_project_writer.cc
new file mode 100644
index 00000000000..a8aad90dd77
--- /dev/null
+++ b/gn/src/gn/json_project_writer.cc
@@ -0,0 +1,514 @@
+// Copyright (c) 2016 The Chromium 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 "gn/json_project_writer.h"
+
+#include <algorithm>
+#include <fstream>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/strings/string_number_conversions.h"
+#include "gn/builder.h"
+#include "gn/commands.h"
+#include "gn/deps_iterator.h"
+#include "gn/desc_builder.h"
+#include "gn/exec_process.h"
+#include "gn/filesystem_utils.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/string_output_buffer.h"
+
+// Structure of JSON output file
+// {
+// "build_settings" : {
+// "root_path" : "absolute path of project root",
+// "build_dir" : "build directory (project relative)",
+// "default_toolchain" : "name of default toolchain"
+// }
+// "targets" : {
+// "target x full label" : { target x properties },
+// "target y full label" : { target y properties },
+// ...
+// }
+// }
+// See desc_builder.cc for overview of target properties
+
+namespace {
+
+void AddTargetDependencies(const Target* target,
+ std::set<const Target*>* deps) {
+ for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
+ if (deps->find(pair.ptr) == deps->end()) {
+ deps->insert(pair.ptr);
+ AddTargetDependencies(pair.ptr, deps);
+ }
+ }
+}
+
+// Filters targets according to filter string; Will also recursively
+// add dependent targets.
+bool FilterTargets(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets,
+ std::vector<const Target*>* targets,
+ const std::string& dir_filter_string,
+ Err* err) {
+ if (dir_filter_string.empty()) {
+ *targets = all_targets;
+ } else {
+ targets->reserve(all_targets.size());
+ std::vector<LabelPattern> filters;
+ if (!commands::FilterPatternsFromString(build_settings, dir_filter_string,
+ &filters, err)) {
+ return false;
+ }
+ commands::FilterTargetsByPatterns(all_targets, filters, targets);
+
+ std::set<const Target*> target_set(targets->begin(), targets->end());
+ for (const auto* target : *targets)
+ AddTargetDependencies(target, &target_set);
+
+ targets->clear();
+ targets->insert(targets->end(), target_set.begin(), target_set.end());
+ }
+
+ // Sort the list of targets per-label to get a consistent ordering of them
+ // in the generated project (and thus stability of the file generated).
+ std::sort(targets->begin(), targets->end(),
+ [](const Target* a, const Target* b) {
+ return a->label().name() < b->label().name();
+ });
+
+ return true;
+}
+
+bool InvokePython(const BuildSettings* build_settings,
+ const base::FilePath& python_script_path,
+ const std::string& python_script_extra_args,
+ const base::FilePath& output_path,
+ bool quiet,
+ Err* err) {
+ const base::FilePath& python_path = build_settings->python_path();
+ base::CommandLine cmdline(python_path);
+ cmdline.AppendArg("--");
+ cmdline.AppendArgPath(python_script_path);
+ cmdline.AppendArgPath(output_path);
+ if (!python_script_extra_args.empty()) {
+ cmdline.AppendArg(python_script_extra_args);
+ }
+ base::FilePath startup_dir =
+ build_settings->GetFullPath(build_settings->build_dir());
+
+ std::string output;
+ std::string stderr_output;
+
+ int exit_code = 0;
+ if (!internal::ExecProcess(cmdline, startup_dir, &output, &stderr_output,
+ &exit_code)) {
+ *err =
+ Err(Location(), "Could not execute python.",
+ "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
+ return false;
+ }
+
+ if (!quiet) {
+ printf("%s", output.c_str());
+ fprintf(stderr, "%s", stderr_output.c_str());
+ }
+
+ if (exit_code != 0) {
+ *err = Err(Location(), "Python has quit with exit code " +
+ base::IntToString(exit_code) + ".");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool JSONProjectWriter::RunAndWriteFiles(
+ const BuildSettings* build_settings,
+ const Builder& builder,
+ const std::string& file_name,
+ const std::string& exec_script,
+ const std::string& exec_script_extra_args,
+ const std::string& dir_filter_string,
+ bool quiet,
+ Err* err) {
+ SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
+ Value(nullptr, file_name), err);
+ if (output_file.is_null()) {
+ return false;
+ }
+
+ base::FilePath output_path = build_settings->GetFullPath(output_file);
+
+ std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
+ std::vector<const Target*> targets;
+ if (!FilterTargets(build_settings, all_targets, &targets, dir_filter_string,
+ err)) {
+ return false;
+ }
+
+ StringOutputBuffer json = GenerateJSON(build_settings, targets);
+ if (!json.ContentsEqual(output_path)) {
+ if (!json.WriteToFile(output_path, err)) {
+ return false;
+ }
+
+ if (!exec_script.empty()) {
+ SourceFile script_file;
+ if (exec_script[0] != '/') {
+ // Relative path, assume the base is in build_dir.
+ script_file = build_settings->build_dir().ResolveRelativeFile(
+ Value(nullptr, exec_script), err);
+ if (script_file.is_null()) {
+ return false;
+ }
+ } else {
+ script_file = SourceFile(exec_script);
+ }
+ base::FilePath script_path = build_settings->GetFullPath(script_file);
+ return InvokePython(build_settings, script_path, exec_script_extra_args,
+ output_path, quiet, err);
+ }
+ }
+
+ return true;
+}
+
+namespace {
+
+// NOTE: Intentional macro definition allows compile-time string concatenation.
+// (see usage below).
+#if defined(OS_WINDOWS)
+#define LINE_ENDING "\r\n"
+#else
+#define LINE_ENDING "\n"
+#endif
+
+// Helper class to output a, potentially very large, JSON file to a
+// StringOutputBuffer. Note that sorting the keys, if desired, is left to
+// the user (unlike base::JSONWriter). This allows rendering to be performed
+// in series of incremental steps. Usage is:
+//
+// 1) Create instance, passing a StringOutputBuffer reference as the
+// destination.
+//
+// 2) Add keys and values using one of the following:
+//
+// a) AddString(key, string_value) to add one string value.
+//
+// b) BeginList(key), AddListItem(), EndList() to add a string list.
+// NOTE: Only lists of strings are supported here.
+//
+// c) BeginDict(key), ... add other keys, followed by EndDict() to add
+// a dictionary key.
+//
+// 3) Call Close() or destroy the instance to finalize the output.
+//
+class SimpleJSONWriter {
+ public:
+ // Constructor.
+ SimpleJSONWriter(StringOutputBuffer& out) : out_(out) {
+ out_ << "{" LINE_ENDING;
+ SetIndentation(1u);
+ }
+
+ // Destructor.
+ ~SimpleJSONWriter() { Close(); }
+
+ // Closing finalizes the output.
+ void Close() {
+ if (indentation_ > 0) {
+ DCHECK(indentation_ == 1u);
+ if (comma_.size())
+ out_ << LINE_ENDING;
+
+ out_ << "}" LINE_ENDING;
+ SetIndentation(0);
+ }
+ }
+
+ // Add new string-valued key.
+ void AddString(std::string_view key, std::string_view value) {
+ if (comma_.size()) {
+ out_ << comma_;
+ }
+ AddMargin() << Escape(key) << ": " << Escape(value);
+ comma_ = "," LINE_ENDING;
+ }
+
+ // Begin a new list. Must be followed by zero or more AddListItem() calls,
+ // then by EndList().
+ void BeginList(std::string_view key) {
+ if (comma_.size())
+ out_ << comma_;
+ AddMargin() << Escape(key) << ": [ ";
+ comma_ = {};
+ }
+
+ // Add a new list item. For now only string values are supported.
+ void AddListItem(std::string_view item) {
+ if (comma_.size())
+ out_ << comma_;
+ out_ << Escape(item);
+ comma_ = ", ";
+ }
+
+ // End current list.
+ void EndList() {
+ out_ << " ]";
+ comma_ = "," LINE_ENDING;
+ }
+
+ // Begin new dictionaary. Must be followed by zero or more other key
+ // additions, then a call to EndDict().
+ void BeginDict(std::string_view key) {
+ if (comma_.size())
+ out_ << comma_;
+
+ AddMargin() << Escape(key) << ": {";
+ SetIndentation(indentation_ + 1);
+ comma_ = LINE_ENDING;
+ }
+
+ // End current dictionary.
+ void EndDict() {
+ if (comma_.size())
+ out_ << LINE_ENDING;
+
+ SetIndentation(indentation_ - 1);
+ AddMargin() << "}";
+ comma_ = "," LINE_ENDING;
+ }
+
+ // Add a dictionary-valued key, whose value is already formatted as a valid
+ // JSON string. Useful to insert the output of base::JSONWriter::Write()
+ // into the target buffer.
+ void AddJSONDict(std::string_view key, std::string_view json) {
+ if (comma_.size())
+ out_ << comma_;
+ AddMargin() << Escape(key) << ": ";
+ if (json.empty()) {
+ out_ << "{ }";
+ } else {
+ DCHECK(json[0] == '{');
+ bool first_line = true;
+ do {
+ size_t line_end = json.find('\n');
+
+ // NOTE: Do not add margin if original input line is empty.
+ // This needs to deal with CR/LF which are part of |json| on Windows
+ // only, due to the way base::JSONWriter::Write() is implemented.
+ bool line_empty = (line_end == 0 || (line_end == 1 && json[0] == '\r'));
+ if (!first_line && !line_empty)
+ AddMargin();
+
+ if (line_end == std::string_view::npos) {
+ out_ << json;
+ ;
+ comma_ = {};
+ return;
+ }
+ // Important: do not add the final newline.
+ out_ << json.substr(
+ 0, (line_end == json.size() - 1) ? line_end : line_end + 1);
+ json.remove_prefix(line_end + 1);
+ first_line = false;
+ } while (!json.empty());
+ }
+ comma_ = "," LINE_ENDING;
+ }
+
+ private:
+ // Return the JSON-escape version of |str|.
+ static std::string Escape(std::string_view str) {
+ std::string result;
+ base::EscapeJSONString(str, true, &result);
+ return result;
+ }
+
+ // Adjust indentation level.
+ void SetIndentation(size_t indentation) { indentation_ = indentation; }
+
+ // Append margin, and return reference to output buffer.
+ StringOutputBuffer& AddMargin() const {
+ static const char kMargin[17] = " ";
+ size_t margin_len = indentation_ * 3;
+ while (margin_len > 0) {
+ size_t span = (margin_len > 16u) ? 16u : margin_len;
+ out_.Append(kMargin, span);
+ margin_len -= span;
+ }
+ return out_;
+ }
+
+ size_t indentation_ = 0;
+ std::string_view comma_;
+ StringOutputBuffer& out_;
+};
+
+} // namespace
+
+StringOutputBuffer JSONProjectWriter::GenerateJSON(
+ const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets) {
+ Label default_toolchain_label;
+ if (!all_targets.empty())
+ default_toolchain_label =
+ all_targets[0]->settings()->default_toolchain_label();
+
+ StringOutputBuffer out;
+
+ // Sort the targets according to their human visible labels first.
+ std::unordered_map<const Target*, std::string> target_labels;
+ for (const Target* target : all_targets) {
+ target_labels[target] =
+ target->label().GetUserVisibleName(default_toolchain_label);
+ }
+
+ std::vector<const Target*> sorted_targets(all_targets.begin(),
+ all_targets.end());
+ std::sort(sorted_targets.begin(), sorted_targets.end(),
+ [&target_labels](const Target* a, const Target* b) {
+ return target_labels[a] < target_labels[b];
+ });
+
+ SimpleJSONWriter json_writer(out);
+
+ // IMPORTANT: Keep the keys sorted when adding them to |json_writer|.
+
+ json_writer.BeginDict("build_settings");
+ {
+ json_writer.AddString("build_dir", build_settings->build_dir().value());
+
+ json_writer.AddString("default_toolchain",
+ default_toolchain_label.GetUserVisibleName(false));
+
+ json_writer.BeginList("gen_input_files");
+
+ // Other files read by the build.
+ std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
+
+ const InputFileManager* input_file_manager =
+ g_scheduler->input_file_manager();
+
+ VectorSetSorter<base::FilePath> sorter(
+ input_file_manager->GetInputFileCount() + other_files.size());
+
+ input_file_manager->AddAllPhysicalInputFileNamesToVectorSetSorter(&sorter);
+
+ sorter.Add(other_files.begin(), other_files.end());
+
+ std::string build_path = FilePathToUTF8(build_settings->root_path());
+ auto item_callback = [&json_writer,
+ &build_path](const base::FilePath& input_file) {
+ std::string file;
+ if (MakeAbsolutePathRelativeIfPossible(
+ build_path, FilePathToUTF8(input_file), &file)) {
+ json_writer.AddListItem(file);
+ }
+ };
+ sorter.IterateOver(item_callback);
+
+ json_writer.EndList(); // gen_input_files
+
+ json_writer.AddString("root_path", build_settings->root_path_utf8());
+ }
+ json_writer.EndDict(); // build_settings
+
+ std::map<Label, const Toolchain*> toolchains;
+ json_writer.BeginDict("targets");
+ {
+ for (const auto* target : sorted_targets) {
+ auto description =
+ DescBuilder::DescriptionForTarget(target, "", false, false, false);
+ // Outputs need to be asked for separately.
+ auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs",
+ false, false, false);
+ base::DictionaryValue* outputs_value = nullptr;
+ if (outputs->GetDictionary("source_outputs", &outputs_value) &&
+ !outputs_value->empty()) {
+ description->MergeDictionary(outputs.get());
+ }
+
+ std::string json_dict;
+ base::JSONWriter::WriteWithOptions(*description.get(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_dict);
+ json_writer.AddJSONDict(target_labels[target], json_dict);
+ toolchains[target->toolchain()->label()] = target->toolchain();
+ }
+ }
+ json_writer.EndDict(); // targets
+
+ json_writer.BeginDict("toolchains");
+ {
+ for (const auto& tool_chain_kv : toolchains) {
+ base::Value toolchain{base::Value::Type::DICTIONARY};
+ const auto& tools = tool_chain_kv.second->tools();
+ for (const auto& tool_kv : tools) {
+ base::Value tool_info{base::Value::Type::DICTIONARY};
+ auto setIfNotEmptry = [&](const auto& key, const auto& value) {
+ if (value.size())
+ tool_info.SetKey(key, base::Value{value});
+ };
+ auto setSubstitutionList = [&](const auto& key,
+ const SubstitutionList& list) {
+ if (list.list().empty())
+ return;
+ base::Value values{base::Value::Type::LIST};
+ for (const auto& v : list.list())
+ values.GetList().emplace_back(base::Value{v.AsString()});
+ tool_info.SetKey(key, std::move(values));
+ };
+ const auto& tool = tool_kv.second;
+ setIfNotEmptry("command", tool->command().AsString());
+ setIfNotEmptry("command_launcher", tool->command_launcher());
+ setIfNotEmptry("default_output_extension",
+ tool->default_output_extension());
+ setIfNotEmptry("default_output_dir",
+ tool->default_output_dir().AsString());
+ setIfNotEmptry("depfile", tool->depfile().AsString());
+ setIfNotEmptry("description", tool->description().AsString());
+ setIfNotEmptry("framework_switch", tool->framework_switch());
+ setIfNotEmptry("weak_framework_switch", tool->weak_framework_switch());
+ setIfNotEmptry("framework_dir_switch", tool->framework_dir_switch());
+ setIfNotEmptry("lib_switch", tool->lib_switch());
+ setIfNotEmptry("lib_dir_switch", tool->lib_dir_switch());
+ setIfNotEmptry("linker_arg", tool->linker_arg());
+ setSubstitutionList("outputs", tool->outputs());
+ setSubstitutionList("partial_outputs", tool->partial_outputs());
+ setSubstitutionList("runtime_outputs", tool->runtime_outputs());
+ setIfNotEmptry("output_prefix", tool->output_prefix());
+
+ toolchain.SetKey(tool_kv.first, std::move(tool_info));
+ }
+ std::string json_dict;
+ base::JSONWriter::WriteWithOptions(
+ toolchain, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_dict);
+ json_writer.AddJSONDict(tool_chain_kv.first.GetUserVisibleName(false), json_dict);
+ }
+ }
+ json_writer.EndDict(); // toolchains
+
+ json_writer.Close();
+
+ return out;
+}
+
+std::string JSONProjectWriter::RenderJSON(
+ const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets) {
+ StringOutputBuffer storage = GenerateJSON(build_settings, all_targets);
+ return storage.str();
+}
diff --git a/gn/src/gn/json_project_writer.h b/gn/src/gn/json_project_writer.h
new file mode 100644
index 00000000000..74293a494a5
--- /dev/null
+++ b/gn/src/gn/json_project_writer.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 The Chromium 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 TOOLS_GN_JSON_WRITER_H_
+#define TOOLS_GN_JSON_WRITER_H_
+
+#include "gn/err.h"
+#include "gn/target.h"
+
+class Builder;
+class BuildSettings;
+class StringOutputBuffer;
+
+class JSONProjectWriter {
+ public:
+ static bool RunAndWriteFiles(const BuildSettings* build_setting,
+ const Builder& builder,
+ const std::string& file_name,
+ const std::string& exec_script,
+ const std::string& exec_script_extra_args,
+ const std::string& dir_filter_string,
+ bool quiet,
+ Err* err);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(JSONWriter, ActionWithResponseFile);
+ FRIEND_TEST_ALL_PREFIXES(JSONWriter, ForEachWithResponseFile);
+ FRIEND_TEST_ALL_PREFIXES(JSONWriter, RustTarget);
+
+ static StringOutputBuffer GenerateJSON(
+ const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets);
+
+ static std::string RenderJSON(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets);
+};
+
+#endif
diff --git a/gn/src/gn/json_project_writer_unittest.cc b/gn/src/gn/json_project_writer_unittest.cc
new file mode 100644
index 00000000000..a22511b7e87
--- /dev/null
+++ b/gn/src/gn/json_project_writer_unittest.cc
@@ -0,0 +1,719 @@
+// Copyright (c) 2019 The Chromium 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 "gn/json_project_writer.h"
+#include "base/strings/string_util.h"
+#include "gn/substitution_list.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+using JSONWriter = TestWithScheduler;
+
+TEST_F(JSONWriter, ActionWithResponseFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION);
+
+ target.sources().push_back(SourceFile("//foo/source1.txt"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Make sure we get interesting substitutions for both the args and the
+ // response file contents.
+ target.action_values().args() =
+ SubstitutionList::MakeForTest("{{response_file_name}}");
+ target.action_values().rsp_file_contents() =
+ SubstitutionList::MakeForTest("-j", "3");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/output1.out");
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+#if defined(OS_WIN)
+ base::FilePath root_path =
+ base::FilePath(FILE_PATH_LITERAL("c:/path/to/src"));
+#else
+ base::FilePath root_path = base::FilePath(FILE_PATH_LITERAL("/path/to/src"));
+#endif
+ setup.build_settings()->SetRootPath(root_path);
+ g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL(".gn")));
+ g_scheduler->AddGenDependency(
+ root_path.Append(FILE_PATH_LITERAL("BUILD.gn")));
+ g_scheduler->AddGenDependency(
+ root_path.Append(FILE_PATH_LITERAL("build/BUILD.gn")));
+ std::string out =
+ JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ R"_({
+ "build_settings": {
+ "build_dir": "//out/Debug/",
+ "default_toolchain": "//toolchain:default",
+ "gen_input_files": [ "//.gn", "//BUILD.gn", "//build/BUILD.gn" ],
+)_"
+#if defined(OS_WIN)
+ " \"root_path\": \"c:/path/to/src\"\n"
+#else
+ " \"root_path\": \"/path/to/src\"\n"
+#endif
+ R"_( },
+ "targets": {
+ "//foo:bar()": {
+ "args": [ "{{response_file_name}}" ],
+ "deps": [ ],
+ "inputs": [ "//foo/input1.txt" ],
+ "metadata": {
+
+ },
+ "outputs": [ "//out/Debug/output1.out" ],
+ "public": "*",
+ "response_file_contents": [ "-j", "3" ],
+ "script": "//foo/script.py",
+ "sources": [ "//foo/source1.txt" ],
+ "testonly": false,
+ "toolchain": "",
+ "type": "action",
+ "visibility": [ ]
+ }
+ },
+ "toolchains": {
+ "//toolchain:default": {
+ "alink": {
+ "command": "ar {{output}} {{source}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}.a" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "cc": {
+ "command": "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "compile_xcassets": {
+ "command": "touch {{output}}"
+ },
+ "copy": {
+ "command": "cp {{source}} {{output}}"
+ },
+ "copy_bundle_data": {
+ "command": "cp {{source}} {{output}}"
+ },
+ "cxx": {
+ "command": "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "command_launcher": "launcher",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "link": {
+ "command": "ld -o {{target_output_name}} {{source}} {{ldflags}} {{libs}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "objc": {
+ "command": "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "objcxx": {
+ "command": "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "rust_bin": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "outputs": [ "{{root_out_dir}}/{{crate_name}}{{output_extension}}" ]
+ },
+ "rust_cdylib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "" ]
+ },
+ "rust_dylib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "" ]
+ },
+ "rust_macro": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "rust_rlib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".rlib",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "rust_staticlib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".a",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "solink": {
+ "command": "ld -shared -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+ "default_output_extension": ".so",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "solink_module": {
+ "command": "ld -bundle -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+ "default_output_extension": ".so",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "stamp": {
+ "command": "touch {{output}}"
+ },
+ "swift": {
+ "command": "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+ "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ }
+ }
+ }
+}
+)_";
+ EXPECT_EQ(expected_json, out) << out;
+}
+
+TEST_F(JSONWriter, RustTarget) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ std::string out =
+ JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ R"_({
+ "build_settings": {
+ "build_dir": "//out/Debug/",
+ "default_toolchain": "//toolchain:default",
+ "gen_input_files": [ ],
+ "root_path": ""
+ },
+ "targets": {
+ "//foo:bar()": {
+ "allow_circular_includes_from": [ ],
+ "check_includes": true,
+ "crate_name": "foo",
+ "crate_root": "//foo/lib.rs",
+ "deps": [ ],
+ "externs": {
+
+ },
+ "metadata": {
+
+ },
+ "outputs": [ "//out/Debug/obj/foo/libbar.rlib" ],
+ "public": "*",
+ "sources": [ "//foo/lib.rs" ],
+ "testonly": false,
+ "toolchain": "",
+ "type": "rust_library",
+ "visibility": [ "*" ]
+ }
+ },
+ "toolchains": {
+ "//toolchain:default": {
+ "alink": {
+ "command": "ar {{output}} {{source}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}.a" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "cc": {
+ "command": "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "compile_xcassets": {
+ "command": "touch {{output}}"
+ },
+ "copy": {
+ "command": "cp {{source}} {{output}}"
+ },
+ "copy_bundle_data": {
+ "command": "cp {{source}} {{output}}"
+ },
+ "cxx": {
+ "command": "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "command_launcher": "launcher",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "link": {
+ "command": "ld -o {{target_output_name}} {{source}} {{ldflags}} {{libs}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "objc": {
+ "command": "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "objcxx": {
+ "command": "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "rust_bin": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "outputs": [ "{{root_out_dir}}/{{crate_name}}{{output_extension}}" ]
+ },
+ "rust_cdylib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "" ]
+ },
+ "rust_dylib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "" ]
+ },
+ "rust_macro": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "rust_rlib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".rlib",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "rust_staticlib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".a",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "solink": {
+ "command": "ld -shared -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+ "default_output_extension": ".so",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "solink_module": {
+ "command": "ld -bundle -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+ "default_output_extension": ".so",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "stamp": {
+ "command": "touch {{output}}"
+ },
+ "swift": {
+ "command": "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+ "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ }
+ }
+ }
+}
+)_";
+ EXPECT_EQ(expected_json, out) << out;
+}
+
+TEST_F(JSONWriter, ForEachWithResponseFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Make sure we get interesting substitutions for both the args and the
+ // response file contents.
+ target.action_values().args() = SubstitutionList::MakeForTest(
+ "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
+ target.action_values().rsp_file_contents() =
+ SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+#if defined(OS_WIN)
+ base::FilePath root_path =
+ base::FilePath(FILE_PATH_LITERAL("c:/path/to/src"));
+#else
+ base::FilePath root_path = base::FilePath(FILE_PATH_LITERAL("/path/to/src"));
+#endif
+ setup.build_settings()->SetRootPath(root_path);
+ g_scheduler->AddGenDependency(root_path.Append(FILE_PATH_LITERAL(".gn")));
+ g_scheduler->AddGenDependency(
+ root_path.Append(FILE_PATH_LITERAL("BUILD.gn")));
+ std::string out =
+ JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ R"_({
+ "build_settings": {
+ "build_dir": "//out/Debug/",
+ "default_toolchain": "//toolchain:default",
+ "gen_input_files": [ "//.gn", "//BUILD.gn" ],
+)_"
+#if defined(OS_WIN)
+ " \"root_path\": \"c:/path/to/src\"\n"
+#else
+ " \"root_path\": \"/path/to/src\"\n"
+#endif
+ R"_( },
+ "targets": {
+ "//foo:bar()": {
+ "args": [ "{{source}}", "{{source_file_part}}", "{{response_file_name}}" ],
+ "deps": [ ],
+ "metadata": {
+
+ },
+ "output_patterns": [ "//out/Debug/{{source_name_part}}.out" ],
+ "outputs": [ "//out/Debug/input1.out" ],
+ "public": "*",
+ "response_file_contents": [ "-j", "{{source_name_part}}" ],
+ "script": "//foo/script.py",
+ "source_outputs": {
+ "//foo/input1.txt": [ "input1.out" ]
+ },
+ "sources": [ "//foo/input1.txt" ],
+ "testonly": false,
+ "toolchain": "",
+ "type": "action_foreach",
+ "visibility": [ ]
+ }
+ },
+ "toolchains": {
+ "//toolchain:default": {
+ "alink": {
+ "command": "ar {{output}} {{source}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}.a" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "cc": {
+ "command": "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "compile_xcassets": {
+ "command": "touch {{output}}"
+ },
+ "copy": {
+ "command": "cp {{source}} {{output}}"
+ },
+ "copy_bundle_data": {
+ "command": "cp {{source}} {{output}}"
+ },
+ "cxx": {
+ "command": "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "command_launcher": "launcher",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "link": {
+ "command": "ld -o {{target_output_name}} {{source}} {{ldflags}} {{libs}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "objc": {
+ "command": "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "objcxx": {
+ "command": "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} {{include_dirs}} -o {{output}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "rust_bin": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "outputs": [ "{{root_out_dir}}/{{crate_name}}{{output_extension}}" ]
+ },
+ "rust_cdylib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "" ]
+ },
+ "rust_dylib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "" ]
+ },
+ "rust_macro": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".so",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "rust_rlib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".rlib",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "rust_staticlib": {
+ "command": "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} {{rustflags}} -o {{output}} {{rustdeps}} {{externs}}",
+ "default_output_extension": ".a",
+ "framework_switch": "-lframework=",
+ "lib_dir_switch": "-Lnative=",
+ "lib_switch": "-l",
+ "linker_arg": "-Clink-arg=",
+ "output_prefix": "lib",
+ "outputs": [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
+ },
+ "solink": {
+ "command": "ld -shared -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+ "default_output_extension": ".so",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "solink_module": {
+ "command": "ld -bundle -o {{target_output_name}}.so {{inputs}} {{ldflags}} {{libs}}",
+ "default_output_extension": ".so",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "output_prefix": "lib",
+ "outputs": [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ],
+ "weak_framework_switch": "-weak_framework "
+ },
+ "stamp": {
+ "command": "touch {{output}}"
+ },
+ "swift": {
+ "command": "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+ "framework_dir_switch": "-F",
+ "framework_switch": "-framework ",
+ "lib_dir_switch": "-L",
+ "lib_switch": "-l",
+ "outputs": [ "{{target_out_dir}}/{{module_name}}.swiftmodule" ],
+ "partial_outputs": [ "{{target_out_dir}}/{{source_name_part}}.o" ],
+ "weak_framework_switch": "-weak_framework "
+ }
+ }
+ }
+}
+)_";
+ EXPECT_EQ(expected_json, out) << out;
+}
diff --git a/gn/src/gn/label.cc b/gn/src/gn/label.cc
new file mode 100644
index 00000000000..173c3d5bf1b
--- /dev/null
+++ b/gn/src/gn/label.cc
@@ -0,0 +1,331 @@
+// 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 "gn/label.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/parse_tree.h"
+#include "gn/value.h"
+#include "util/build_config.h"
+
+namespace {
+
+// We print user visible label names with no trailing slash after the
+// directory name.
+std::string DirWithNoTrailingSlash(const SourceDir& dir) {
+ // Be careful not to trim if the input is just "/" or "//".
+ if (dir.value().size() > 2)
+ return dir.value().substr(0, dir.value().size() - 1);
+ return dir.value();
+}
+
+// Given the separate-out input (everything before the colon) in the dep rule,
+// computes the final build rule. Sets err on failure. On success,
+// |*used_implicit| will be set to whether the implicit current directory was
+// used. The value is used only for generating error messages.
+bool ComputeBuildLocationFromDep(const Value& input_value,
+ const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const std::string_view& input,
+ SourceDir* result,
+ Err* err) {
+ // No rule, use the current location.
+ if (input.empty()) {
+ *result = current_dir;
+ return true;
+ }
+
+ *result =
+ current_dir.ResolveRelativeDir(input_value, input, err, source_root);
+ return true;
+}
+
+// Given the separated-out target name (after the colon) computes the final
+// name, using the implicit name from the previously-generated
+// computed_location if necessary. The input_value is used only for generating
+// error messages.
+bool ComputeTargetNameFromDep(const Value& input_value,
+ const SourceDir& computed_location,
+ const std::string_view& input,
+ StringAtom* result,
+ Err* err) {
+ if (!input.empty()) {
+ // Easy case: input is specified, just use it.
+ *result = StringAtom(input);
+ return true;
+ }
+
+ const std::string& loc = computed_location.value();
+
+ // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc.
+ if (loc.size() <= 2) {
+ *err = Err(input_value, "This dependency name is empty");
+ return false;
+ }
+
+ size_t next_to_last_slash = loc.rfind('/', loc.size() - 2);
+ DCHECK(next_to_last_slash != std::string::npos);
+ *result = StringAtom(std::string_view{&loc[next_to_last_slash + 1],
+ loc.size() - next_to_last_slash - 2});
+ return true;
+}
+
+// The original value is used only for error reporting, use the |input| as the
+// input to this function (which may be a substring of the original value when
+// we're parsing toolchains.
+//
+// If the output toolchain vars are NULL, then we'll report an error if we
+// find a toolchain specified (this is used when recursively parsing toolchain
+// labels which themselves can't have toolchain specs).
+//
+// We assume that the output variables are initialized to empty so we don't
+// write them unless we need them to contain something.
+//
+// Returns true on success. On failure, the out* variables might be written to
+// but shouldn't be used.
+bool Resolve(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Label& current_toolchain,
+ const Value& original_value,
+ const std::string_view& input,
+ SourceDir* out_dir,
+ StringAtom* out_name,
+ SourceDir* out_toolchain_dir,
+ StringAtom* out_toolchain_name,
+ Err* err) {
+ // To workaround the problem that std::string_view operator[] doesn't return a
+ // ref.
+ const char* input_str = input.data();
+ size_t offset = 0;
+#if defined(OS_WIN)
+ if (IsPathAbsolute(input)) {
+ size_t drive_letter_pos = input[0] == '/' ? 1 : 0;
+ if (input.size() > drive_letter_pos + 2 &&
+ input[drive_letter_pos + 1] == ':' &&
+ IsSlash(input[drive_letter_pos + 2]) &&
+ base::IsAsciiAlpha(input[drive_letter_pos])) {
+ // Skip over the drive letter colon.
+ offset = drive_letter_pos + 2;
+ }
+ }
+#endif
+ size_t path_separator = input.find_first_of(":(", offset);
+ std::string_view location_piece;
+ std::string_view name_piece;
+ std::string_view toolchain_piece;
+ if (path_separator == std::string::npos) {
+ location_piece = input;
+ // Leave name & toolchain piece null.
+ } else {
+ location_piece = std::string_view(&input_str[0], path_separator);
+
+ size_t toolchain_separator = input.find('(', path_separator);
+ if (toolchain_separator == std::string::npos) {
+ name_piece = std::string_view(&input_str[path_separator + 1],
+ input.size() - path_separator - 1);
+ // Leave location piece null.
+ } else if (!out_toolchain_dir) {
+ // Toolchain specified but not allows in this context.
+ *err =
+ Err(original_value, "Toolchain has a toolchain.",
+ "Your toolchain definition (inside the parens) seems to itself "
+ "have a\ntoolchain. Don't do this.");
+ return false;
+ } else {
+ // Name piece is everything between the two separators. Note that the
+ // separators may be the same (e.g. "//foo(bar)" which means empty name.
+ if (toolchain_separator > path_separator) {
+ name_piece = std::string_view(&input_str[path_separator + 1],
+ toolchain_separator - path_separator - 1);
+ }
+
+ // Toolchain name should end in a ) and this should be the end of the
+ // string.
+ if (input[input.size() - 1] != ')') {
+ *err =
+ Err(original_value, "Bad toolchain name.",
+ "Toolchain name must end in a \")\" at the end of the label.");
+ return false;
+ }
+
+ // Subtract off the two parens to just get the toolchain name.
+ toolchain_piece =
+ std::string_view(&input_str[toolchain_separator + 1],
+ input.size() - toolchain_separator - 2);
+ }
+ }
+
+ // Everything before the separator is the filename.
+ // We allow three cases:
+ // Absolute: "//foo:bar" -> /foo:bar
+ // Target in current file: ":foo" -> <currentdir>:foo
+ // Path with implicit name: "/foo" -> /foo:foo
+ if (location_piece.empty() && name_piece.empty()) {
+ // Can't use both implicit filename and name (":").
+ *err = Err(original_value, "This doesn't specify a dependency.");
+ return false;
+ }
+
+ if (!ComputeBuildLocationFromDep(original_value, current_dir, source_root,
+ location_piece, out_dir, err))
+ return false;
+
+ if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece, out_name,
+ err))
+ return false;
+
+ // Last, do the toolchains.
+ if (out_toolchain_dir) {
+ // Handle empty toolchain strings. We don't allow normal labels to be
+ // empty so we can't allow the recursive call of this function to do this
+ // check.
+ if (toolchain_piece.empty()) {
+ *out_toolchain_dir = current_toolchain.dir();
+ *out_toolchain_name = current_toolchain.name_atom();
+ return true;
+ } else {
+ return Resolve(current_dir, source_root, current_toolchain,
+ original_value, toolchain_piece, out_toolchain_dir,
+ out_toolchain_name, nullptr, nullptr, err);
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+const char kLabels_Help[] =
+ R"*(About labels
+
+ Everything that can participate in the dependency graph (targets, configs,
+ and toolchains) are identified by labels. A common label looks like:
+
+ //base/test:test_support
+
+ This consists of a source-root-absolute path, a colon, and a name. This means
+ to look for the thing named "test_support" in "base/test/BUILD.gn".
+
+ You can also specify system absolute paths if necessary. Typically such
+ paths would be specified via a build arg so the developer can specify where
+ the component is on their system.
+
+ /usr/local/foo:bar (Posix)
+ /C:/Program Files/MyLibs:bar (Windows)
+
+Toolchains
+
+ A canonical label includes the label of the toolchain being used. Normally,
+ the toolchain label is implicitly inherited from the current execution
+ context, but you can override this to specify cross-toolchain dependencies:
+
+ //base/test:test_support(//build/toolchain/win:msvc)
+
+ Here GN will look for the toolchain definition called "msvc" in the file
+ "//build/toolchain/win" to know how to compile this target.
+
+Relative labels
+
+ If you want to refer to something in the same buildfile, you can omit
+ the path name and just start with a colon. This format is recommended for
+ all same-file references.
+
+ :base
+
+ Labels can be specified as being relative to the current directory.
+ Stylistically, we prefer to use absolute paths for all non-file-local
+ references unless a build file needs to be run in different contexts (like a
+ project needs to be both standalone and pulled into other projects in
+ difference places in the directory hierarchy).
+
+ source/plugin:myplugin
+ ../net:url_request
+
+Implicit names
+
+ If a name is unspecified, it will inherit the directory name. Stylistically,
+ we prefer to omit the colon and name when possible:
+
+ //net -> //net:net
+ //tools/gn -> //tools/gn:gn
+)*";
+
+Label::Label() : hash_(ComputeHash()) {}
+
+Label::Label(const SourceDir& dir,
+ const std::string_view& name,
+ const SourceDir& toolchain_dir,
+ const std::string_view& toolchain_name)
+ : dir_(dir),
+ name_(StringAtom(name)),
+ toolchain_dir_(toolchain_dir),
+ toolchain_name_(StringAtom(toolchain_name)),
+ hash_(ComputeHash()) {}
+
+Label::Label(const SourceDir& dir, const std::string_view& name)
+ : dir_(dir), name_(StringAtom(name)),
+ hash_(ComputeHash()) {}
+
+// static
+Label Label::Resolve(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Label& current_toolchain,
+ const Value& input,
+ Err* err) {
+ Label ret;
+ if (input.type() != Value::STRING) {
+ *err = Err(input, "Dependency is not a string.");
+ return ret;
+ }
+ const std::string& input_string = input.string_value();
+ if (input_string.empty()) {
+ *err = Err(input, "Dependency string is empty.");
+ return ret;
+ }
+
+ if (!::Resolve(current_dir, source_root, current_toolchain, input,
+ input_string, &ret.dir_, &ret.name_, &ret.toolchain_dir_,
+ &ret.toolchain_name_, err))
+ return Label();
+ return ret;
+}
+
+Label Label::GetToolchainLabel() const {
+ return Label(toolchain_dir_, toolchain_name_);
+}
+
+Label Label::GetWithNoToolchain() const {
+ return Label(dir_, name_);
+}
+
+std::string Label::GetUserVisibleName(bool include_toolchain) const {
+ std::string ret;
+ ret.reserve(dir_.value().size() + name_.str().size() + 1);
+
+ if (dir_.is_null())
+ return ret;
+
+ ret = DirWithNoTrailingSlash(dir_);
+ ret.push_back(':');
+ ret.append(name_.str());
+
+ if (include_toolchain) {
+ ret.push_back('(');
+ if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) {
+ ret.append(DirWithNoTrailingSlash(toolchain_dir_));
+ ret.push_back(':');
+ ret.append(toolchain_name_.str());
+ }
+ ret.push_back(')');
+ }
+ return ret;
+}
+
+std::string Label::GetUserVisibleName(const Label& default_toolchain) const {
+ bool include_toolchain = default_toolchain.dir() != toolchain_dir_ ||
+ default_toolchain.name_atom() != toolchain_name_;
+ return GetUserVisibleName(include_toolchain);
+}
diff --git a/gn/src/gn/label.h b/gn/src/gn/label.h
new file mode 100644
index 00000000000..dc9813b439f
--- /dev/null
+++ b/gn/src/gn/label.h
@@ -0,0 +1,143 @@
+// 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 TOOLS_GN_LABEL_H_
+#define TOOLS_GN_LABEL_H_
+
+#include <tuple>
+
+#include <stddef.h>
+
+#include "gn/source_dir.h"
+#include "gn/string_atom.h"
+
+class Err;
+class Value;
+
+// A label represents the name of a target or some other named thing in
+// the source path. The label is always absolute and always includes a name
+// part, so it starts with a slash, and has one colon.
+class Label {
+ public:
+ Label();
+
+ // Makes a label given an already-separated out path and name.
+ // See also Resolve().
+ Label(const SourceDir& dir,
+ const std::string_view& name,
+ const SourceDir& toolchain_dir,
+ const std::string_view& toolchain_name);
+
+ // Makes a label with an empty toolchain.
+ Label(const SourceDir& dir, const std::string_view& name);
+
+ // Resolves a string from a build file that may be relative to the
+ // current directory into a fully qualified label. On failure returns an
+ // is_null() label and sets the error.
+ static Label Resolve(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Label& current_toolchain,
+ const Value& input,
+ Err* err);
+
+ bool is_null() const { return dir_.is_null(); }
+
+ const SourceDir& dir() const { return dir_; }
+ const std::string& name() const { return name_.str(); }
+ StringAtom name_atom() const { return name_; }
+
+ const SourceDir& toolchain_dir() const { return toolchain_dir_; }
+ const std::string& toolchain_name() const { return toolchain_name_.str(); }
+ StringAtom toolchain_name_atom() const { return toolchain_name_; }
+
+ // Returns the current label's toolchain as its own Label.
+ Label GetToolchainLabel() const;
+
+ // Returns a copy of this label but with an empty toolchain.
+ Label GetWithNoToolchain() const;
+
+ // Formats this label in a way that we can present to the user or expose to
+ // other parts of the system. SourceDirs end in slashes, but the user
+ // expects names like "//chrome/renderer:renderer_config" when printed. The
+ // toolchain is optionally included.
+ std::string GetUserVisibleName(bool include_toolchain) const;
+
+ // Like the above version, but automatically includes the toolchain if it's
+ // not the default one. Normally the user only cares about the toolchain for
+ // non-default ones, so this can make certain output more clear.
+ std::string GetUserVisibleName(const Label& default_toolchain) const;
+
+ bool operator==(const Label& other) const {
+ return name_.SameAs(other.name_) && dir_ == other.dir_ &&
+ toolchain_dir_ == other.toolchain_dir_ &&
+ toolchain_name_.SameAs(other.toolchain_name_);
+ }
+ bool operator!=(const Label& other) const { return !operator==(other); }
+ bool operator<(const Label& other) const {
+ // This custom comparison function uses the fact that SourceDir and
+ // StringAtom values have very fast equality comparison to avoid
+ // un-necessary string comparisons when components are equal.
+ if (dir_ != other.dir_)
+ return dir_ < other.dir_;
+
+ if (!name_.SameAs(other.name_))
+ return name_ < other.name_;
+
+ if (toolchain_dir_ != other.toolchain_dir_)
+ return toolchain_dir_ < other.toolchain_dir_;
+
+ return toolchain_name_ < other.toolchain_name_;
+ }
+
+ // Returns true if the toolchain dir/name of this object matches some
+ // other object.
+ bool ToolchainsEqual(const Label& other) const {
+ return toolchain_dir_ == other.toolchain_dir_ &&
+ toolchain_name_.SameAs(other.toolchain_name_);
+ }
+
+ size_t hash() const { return hash_; }
+
+ private:
+ Label(SourceDir dir, StringAtom name) : dir_(dir), name_(name) {}
+
+ Label(SourceDir dir,
+ StringAtom name,
+ SourceDir toolchain_dir,
+ StringAtom toolchain_name)
+ : dir_(dir),
+ name_(name),
+ toolchain_dir_(toolchain_dir),
+ toolchain_name_(toolchain_name) {}
+
+ size_t ComputeHash() const {
+ size_t h0 = dir_.hash();
+ size_t h1 = name_.hash();
+ size_t h2 = toolchain_dir_.hash();
+ size_t h3 = toolchain_name_.hash();
+ return ((h3 * 131 + h2) * 131 + h1) * 131 + h0;
+ }
+
+ SourceDir dir_;
+ StringAtom name_;
+
+ SourceDir toolchain_dir_;
+ StringAtom toolchain_name_;
+
+ size_t hash_;
+ // NOTE: Must be initialized by constructors with ComputeHash() value.
+};
+
+namespace std {
+
+template <>
+struct hash<Label> {
+ std::size_t operator()(const Label& v) const { return v.hash(); }
+};
+
+} // namespace std
+
+extern const char kLabels_Help[];
+
+#endif // TOOLS_GN_LABEL_H_
diff --git a/gn/src/gn/label_pattern.cc b/gn/src/gn/label_pattern.cc
new file mode 100644
index 00000000000..35235e5c26e
--- /dev/null
+++ b/gn/src/gn/label_pattern.cc
@@ -0,0 +1,275 @@
+// 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 "gn/label_pattern.h"
+
+#include <stddef.h>
+
+#include "base/strings/string_util.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/value.h"
+#include "util/build_config.h"
+
+const char kLabelPattern_Help[] =
+ R"*(Label patterns
+
+ A label pattern is a way of expressing one or more labels in a portion of the
+ source tree. They are not general regular expressions.
+
+ They can take the following forms only:
+
+ - Explicit (no wildcard):
+ "//foo/bar:baz"
+ ":baz"
+
+ - Wildcard target names:
+ "//foo/bar:*" (all targets in the //foo/bar/BUILD.gn file)
+ ":*" (all targets in the current build file)
+
+ - Wildcard directory names ("*" is only supported at the end)
+ "*" (all targets)
+ "//foo/bar/*" (all targets in any subdir of //foo/bar)
+ "./*" (all targets in the current build file or sub dirs)
+
+ Any of the above forms can additionally take an explicit toolchain
+ in parenthesis at the end of the label pattern. In this case, the
+ toolchain must be fully qualified (no wildcards are supported in the
+ toolchain name).
+
+ "//foo:bar(//build/toolchain:mac)"
+ An explicit target in an explicit toolchain.
+
+ ":*(//build/toolchain/linux:32bit)"
+ All targets in the current build file using the 32-bit Linux toolchain.
+
+ "//foo/*(//build/toolchain:win)"
+ All targets in //foo and any subdirectory using the Windows
+ toolchain.
+)*";
+
+LabelPattern::LabelPattern() : type_(MATCH) {}
+
+LabelPattern::LabelPattern(Type type,
+ const SourceDir& dir,
+ const std::string_view& name,
+ const Label& toolchain_label)
+ : toolchain_(toolchain_label), type_(type), dir_(dir), name_(name) {}
+
+LabelPattern::LabelPattern(const LabelPattern& other) = default;
+
+LabelPattern::~LabelPattern() = default;
+
+// static
+LabelPattern LabelPattern::GetPattern(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Value& value,
+ Err* err) {
+ if (!value.VerifyTypeIs(Value::STRING, err))
+ return LabelPattern();
+
+ std::string_view str(value.string_value());
+ if (str.empty()) {
+ *err = Err(value, "Label pattern must not be empty.");
+ return LabelPattern();
+ }
+
+ // If there's no wildcard, this is specifying an exact label, use the
+ // label resolution code to get all the implicit name stuff.
+ size_t star = str.find('*');
+ if (star == std::string::npos) {
+ Label label = Label::Resolve(current_dir, source_root, Label(), value, err);
+ if (err->has_error())
+ return LabelPattern();
+
+ // Toolchain.
+ Label toolchain_label;
+ if (!label.toolchain_dir().is_null() || !label.toolchain_name().empty())
+ toolchain_label = label.GetToolchainLabel();
+
+ return LabelPattern(MATCH, label.dir(), label.name(), toolchain_label);
+ }
+
+ // Wildcard case, need to split apart the label to see what it specifies.
+ Label toolchain_label;
+ size_t open_paren = str.find('(');
+ if (open_paren != std::string::npos) {
+ // Has a toolchain definition, extract inside the parens.
+ size_t close_paren = str.find(')', open_paren);
+ if (close_paren == std::string::npos) {
+ *err = Err(value, "No close paren when looking for toolchain name.");
+ return LabelPattern();
+ }
+
+ std::string toolchain_string(
+ str.substr(open_paren + 1, close_paren - open_paren - 1));
+ if (toolchain_string.find('*') != std::string::npos) {
+ *err = Err(value, "Can't have a wildcard in the toolchain.");
+ return LabelPattern();
+ }
+
+ // Parse the inside of the parens as a label for a toolchain.
+ Value value_for_toolchain(value.origin(), toolchain_string);
+ toolchain_label = Label::Resolve(current_dir, source_root, Label(),
+ value_for_toolchain, err);
+ if (err->has_error())
+ return LabelPattern();
+
+ // Trim off the toolchain for the processing below.
+ str = str.substr(0, open_paren);
+ }
+
+ // Extract path and name.
+ std::string_view path;
+ std::string_view name;
+ size_t offset = 0;
+#if defined(OS_WIN)
+ if (IsPathAbsolute(str)) {
+ size_t drive_letter_pos = str[0] == '/' ? 1 : 0;
+ if (str.size() > drive_letter_pos + 2 && str[drive_letter_pos + 1] == ':' &&
+ IsSlash(str[drive_letter_pos + 2]) &&
+ base::IsAsciiAlpha(str[drive_letter_pos])) {
+ // Skip over the drive letter colon.
+ offset = drive_letter_pos + 2;
+ }
+ }
+#endif
+ size_t colon = str.find(':', offset);
+ if (colon == std::string::npos) {
+ path = std::string_view(str);
+ } else {
+ path = str.substr(0, colon);
+ name = str.substr(colon + 1);
+ }
+
+ // The path can have these forms:
+ // 1. <empty> (use current dir)
+ // 2. <non wildcard stuff> (send through directory resolution)
+ // 3. <non wildcard stuff>* (send stuff through dir resolution, note star)
+ // 4. * (matches anything)
+ SourceDir dir;
+ bool has_path_star = false;
+ if (path.empty()) {
+ // Looks like ":foo".
+ dir = current_dir;
+ } else if (path[path.size() - 1] == '*') {
+ // Case 3 or 4 above.
+ has_path_star = true;
+
+ // Adjust path to contain everything but the star.
+ path = path.substr(0, path.size() - 1);
+
+ if (!path.empty() && path[path.size() - 1] != '/') {
+ // The input was "foo*" which is invalid.
+ *err =
+ Err(value, "'*' must match full directories in a label pattern.",
+ "You did \"foo*\" but this thing doesn't do general pattern\n"
+ "matching. Instead, you have to add a slash: \"foo/*\" to match\n"
+ "all targets in a directory hierarchy.");
+ return LabelPattern();
+ }
+ }
+
+ // Resolve the part of the path that's not the wildcard.
+ if (!path.empty()) {
+ // The non-wildcard stuff better not have a wildcard.
+ if (path.find('*') != std::string_view::npos) {
+ *err = Err(value, "Label patterns only support wildcard suffixes.",
+ "The pattern contained a '*' that wasn't at the end.");
+ return LabelPattern();
+ }
+
+ // Resolve the non-wildcard stuff.
+ dir = current_dir.ResolveRelativeDir(value, path, err, source_root);
+ if (err->has_error())
+ return LabelPattern();
+ }
+
+ // Resolve the name. At this point, we're doing wildcard matches so the
+ // name should either be empty ("foo/*") or a wildcard ("foo:*");
+ if (colon != std::string::npos && name != "*") {
+ *err = Err(
+ value, "Invalid label pattern.",
+ "You seem to be using the wildcard more generally that is supported.\n"
+ "Did you mean \"foo:*\" to match everything in the file, or\n"
+ "\"./*\" to recursively match everything in the current subtree.");
+ return LabelPattern();
+ }
+
+ Type type;
+ if (has_path_star) {
+ // We know there's a wildcard, so if the name is empty it looks like
+ // "foo/*".
+ type = RECURSIVE_DIRECTORY;
+ } else {
+ // Everything else should be of the form "foo:*".
+ type = DIRECTORY;
+ }
+
+ // When we're doing wildcard matching, the name is always empty.
+ return LabelPattern(type, dir, std::string_view(), toolchain_label);
+}
+
+bool LabelPattern::HasWildcard(const std::string& str) {
+ // Just look for a star. In the future, we may want to handle escaping or
+ // other types of patterns.
+ return str.find('*') != std::string::npos;
+}
+
+bool LabelPattern::Matches(const Label& label) const {
+ if (!toolchain_.is_null()) {
+ // Toolchain must match exactly.
+ if (toolchain_.dir() != label.toolchain_dir() ||
+ toolchain_.name() != label.toolchain_name())
+ return false;
+ }
+
+ switch (type_) {
+ case MATCH:
+ return label.name() == name_ && label.dir() == dir_;
+ case DIRECTORY:
+ // The directories must match exactly.
+ return label.dir() == dir_;
+ case RECURSIVE_DIRECTORY:
+ // Our directory must be a prefix of the input label for recursive.
+ return label.dir().value().compare(0, dir_.value().size(),
+ dir_.value()) == 0;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+// static
+bool LabelPattern::VectorMatches(const std::vector<LabelPattern>& patterns,
+ const Label& label) {
+ for (const auto& pattern : patterns) {
+ if (pattern.Matches(label))
+ return true;
+ }
+ return false;
+}
+
+std::string LabelPattern::Describe() const {
+ std::string result;
+
+ switch (type()) {
+ case MATCH:
+ result = DirectoryWithNoLastSlash(dir()) + ":" + name();
+ break;
+ case DIRECTORY:
+ result = DirectoryWithNoLastSlash(dir()) + ":*";
+ break;
+ case RECURSIVE_DIRECTORY:
+ result = dir().value() + "*";
+ break;
+ }
+
+ if (!toolchain_.is_null()) {
+ result.push_back('(');
+ result.append(toolchain_.GetUserVisibleName(false));
+ result.push_back(')');
+ }
+ return result;
+}
diff --git a/gn/src/gn/label_pattern.h b/gn/src/gn/label_pattern.h
new file mode 100644
index 00000000000..f40750eea0c
--- /dev/null
+++ b/gn/src/gn/label_pattern.h
@@ -0,0 +1,82 @@
+// 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 TOOLS_GN_LABEL_PATTERN_H_
+#define TOOLS_GN_LABEL_PATTERN_H_
+
+#include <string_view>
+
+#include "gn/label.h"
+#include "gn/source_dir.h"
+
+class Err;
+class Value;
+
+extern const char kLabelPattern_Help[];
+
+// A label pattern is a simple pattern that matches labels. It is used for
+// specifying visibility and other times when multiple targets need to be
+// referenced.
+class LabelPattern {
+ public:
+ enum Type {
+ MATCH = 1, // Exact match for a given target.
+ DIRECTORY, // Only targets in the file in the given directory.
+ RECURSIVE_DIRECTORY // The given directory and any subdir.
+ // (also indicates "public" when dir is empty).
+ };
+
+ LabelPattern();
+ LabelPattern(Type type,
+ const SourceDir& dir,
+ const std::string_view& name,
+ const Label& toolchain_label);
+ LabelPattern(const LabelPattern& other);
+ ~LabelPattern();
+
+ // Converts the given input string to a pattern. This does special stuff
+ // to treat the pattern as a label. Sets the error on failure.
+ static LabelPattern GetPattern(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Value& value,
+ Err* err);
+
+ // Returns true if the given input string might match more than one thing.
+ static bool HasWildcard(const std::string& str);
+
+ // Returns true if this pattern matches the given label.
+ bool Matches(const Label& label) const;
+
+ // Returns true if any of the patterns in the vector match the label.
+ static bool VectorMatches(const std::vector<LabelPattern>& patterns,
+ const Label& label);
+
+ // Returns a string representation of this pattern.
+ std::string Describe() const;
+
+ Type type() const { return type_; }
+
+ const SourceDir& dir() const { return dir_; }
+ const std::string& name() const { return name_; }
+
+ const Label& toolchain() const { return toolchain_; }
+ void set_toolchain(const Label& tc) { toolchain_ = tc; }
+
+ private:
+ // If nonempty, specifies the toolchain to use. If empty, this will match
+ // all toolchains. This is independent of the match type.
+ Label toolchain_;
+
+ Type type_;
+
+ // Used when type_ == PRIVATE and PRIVATE_RECURSIVE. This specifies the
+ // directory that to which the pattern is private to.
+ SourceDir dir_;
+
+ // Empty name means match everything. Otherwise the name must match
+ // exactly.
+ std::string name_;
+};
+
+#endif // TOOLS_GN_LABEL_PATTERN_H_
diff --git a/gn/src/gn/label_pattern_unittest.cc b/gn/src/gn/label_pattern_unittest.cc
new file mode 100644
index 00000000000..ad984016f0f
--- /dev/null
+++ b/gn/src/gn/label_pattern_unittest.cc
@@ -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.
+
+#include <stddef.h>
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "gn/err.h"
+#include "gn/label_pattern.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+namespace {
+
+struct PatternCase {
+ const char* input;
+ bool success;
+
+ LabelPattern::Type type;
+ const char* dir;
+ const char* name;
+ const char* toolchain;
+};
+
+} // namespace
+
+TEST(LabelPattern, PatternParse) {
+ SourceDir current_dir("//foo/");
+ PatternCase cases[] = {
+ // Missing stuff.
+ {"", false, LabelPattern::MATCH, "", "", ""},
+ {":", false, LabelPattern::MATCH, "", "", ""},
+ // Normal things.
+ {":bar", true, LabelPattern::MATCH, "//foo/", "bar", ""},
+ {"//la:bar", true, LabelPattern::MATCH, "//la/", "bar", ""},
+ {"*", true, LabelPattern::RECURSIVE_DIRECTORY, "", "", ""},
+ {":*", true, LabelPattern::DIRECTORY, "//foo/", "", ""},
+ {"la:*", true, LabelPattern::DIRECTORY, "//foo/la/", "", ""},
+ {"la/*:*", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/la/", "", ""},
+ {"//la:*", true, LabelPattern::DIRECTORY, "//la/", "", ""},
+ {"./*", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/", "", ""},
+ {"foo/*", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/foo/", "", ""},
+ {"//l/*", true, LabelPattern::RECURSIVE_DIRECTORY, "//l/", "", ""},
+ // Toolchains.
+ {"//foo()", true, LabelPattern::MATCH, "//foo/", "foo", ""},
+ {"//foo(//bar)", true, LabelPattern::MATCH, "//foo/", "foo", "//bar:bar"},
+ {"//foo:*(//bar)", true, LabelPattern::DIRECTORY, "//foo/", "",
+ "//bar:bar"},
+ {"//foo/*(//bar)", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/", "",
+ "//bar:bar"},
+ // Wildcards in invalid places.
+ {"*foo*:bar", false, LabelPattern::MATCH, "", "", ""},
+ {"foo*:*bar", false, LabelPattern::MATCH, "", "", ""},
+ {"*foo:bar", false, LabelPattern::MATCH, "", "", ""},
+ {"foo:bar*", false, LabelPattern::MATCH, "", "", ""},
+ {"*:*", true, LabelPattern::RECURSIVE_DIRECTORY, "", "", ""},
+ // Invalid toolchain stuff.
+ {"//foo(//foo/bar:*)", false, LabelPattern::MATCH, "", "", ""},
+ {"//foo/*(*)", false, LabelPattern::MATCH, "", "", ""},
+ {"//foo(//bar", false, LabelPattern::MATCH, "", "", ""},
+ // Absolute paths.
+ {"/la/*", true, LabelPattern::RECURSIVE_DIRECTORY, "/la/", "", ""},
+ {"/la:bar", true, LabelPattern::MATCH, "/la/", "bar", ""},
+#if defined(OS_WIN)
+ {"/C:/la/*", true, LabelPattern::RECURSIVE_DIRECTORY, "/C:/la/", "", ""},
+ {"C:/la/*", true, LabelPattern::RECURSIVE_DIRECTORY, "/C:/la/", "", ""},
+ {"/C:/la:bar", true, LabelPattern::MATCH, "/C:/la/", "bar", ""},
+ {"C:/la:bar", true, LabelPattern::MATCH, "/C:/la/", "bar", ""},
+ {"C:foo", true, LabelPattern::MATCH, "//foo/C/", "foo", ""},
+#endif
+ };
+
+ for (size_t i = 0; i < std::size(cases); i++) {
+ const PatternCase& cur = cases[i];
+ Err err;
+ LabelPattern result = LabelPattern::GetPattern(
+ current_dir, std::string_view(), Value(nullptr, cur.input), &err);
+
+ EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.input;
+ EXPECT_EQ(cur.type, result.type()) << i << " " << cur.input;
+ EXPECT_EQ(cur.dir, result.dir().value()) << i << " " << cur.input;
+ EXPECT_EQ(cur.name, result.name()) << i << " " << cur.input;
+ EXPECT_EQ(cur.toolchain, result.toolchain().GetUserVisibleName(false))
+ << i << " " << cur.input;
+ }
+}
+
+// Tests a non-empty source root which allows patterns to reference above the
+// source root.
+TEST(LabelPattern, PatternParseAboveSourceRoot) {
+ SourceDir current_dir("//foo/");
+ std::string source_root = "/foo/bar/baz/";
+
+ Err err;
+ LabelPattern result = LabelPattern::GetPattern(
+ current_dir, source_root, Value(nullptr, "../../../*"), &err);
+ ASSERT_FALSE(err.has_error());
+
+ EXPECT_EQ(LabelPattern::RECURSIVE_DIRECTORY, result.type());
+ EXPECT_EQ("/foo/", result.dir().value()) << result.dir().value();
+}
diff --git a/gn/src/gn/label_ptr.h b/gn/src/gn/label_ptr.h
new file mode 100644
index 00000000000..fd4d5c0bc1b
--- /dev/null
+++ b/gn/src/gn/label_ptr.h
@@ -0,0 +1,79 @@
+// 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 TOOLS_GN_LABEL_PTR_H_
+#define TOOLS_GN_LABEL_PTR_H_
+
+#include <stddef.h>
+
+#include <functional>
+
+#include "gn/label.h"
+
+class Config;
+class ParseNode;
+class Target;
+
+// Structure that holds a labeled "thing". This is used for various places
+// where we need to store lists of targets or configs. We sometimes populate
+// the pointers on another thread from where we compute the labels, so this
+// structure lets us save them separately. This also allows us to store the
+// location of the thing that added this dependency.
+template <typename T>
+struct LabelPtrPair {
+ using DestType = T;
+
+ LabelPtrPair() = default;
+
+ explicit LabelPtrPair(const Label& l) : label(l) {}
+
+ // This contructor is typically used in unit tests, it extracts the label
+ // automatically from a given pointer.
+ explicit LabelPtrPair(const T* p) : label(p->label()), ptr(p) {}
+
+ ~LabelPtrPair() = default;
+
+ Label label;
+ const T* ptr = nullptr;
+
+ // The origin of this dependency. This will be null for internally generated
+ // dependencies. This happens when a group is automatically expanded and that
+ // group's members are added to the target that depends on that group.
+ const ParseNode* origin = nullptr;
+};
+
+using LabelConfigPair = LabelPtrPair<Config>;
+using LabelTargetPair = LabelPtrPair<Target>;
+
+using LabelConfigVector = std::vector<LabelConfigPair>;
+using LabelTargetVector = std::vector<LabelTargetPair>;
+
+// Default comparison operators -----------------------------------------------
+//
+// The default hash and comparison operators operate on the label, which should
+// always be valid, whereas the pointer is sometimes null.
+
+template <typename T>
+inline bool operator==(const LabelPtrPair<T>& a, const LabelPtrPair<T>& b) {
+ return a.label == b.label;
+}
+
+template <typename T>
+inline bool operator<(const LabelPtrPair<T>& a, const LabelPtrPair<T>& b) {
+ return a.label < b.label;
+}
+
+namespace std {
+
+template <typename T>
+struct hash<LabelPtrPair<T>> {
+ std::size_t operator()(const LabelPtrPair<T>& v) const {
+ hash<Label> h;
+ return h(v.label);
+ }
+};
+
+} // namespace std
+
+#endif // TOOLS_GN_LABEL_PTR_H_
diff --git a/gn/src/gn/label_unittest.cc b/gn/src/gn/label_unittest.cc
new file mode 100644
index 00000000000..88820190502
--- /dev/null
+++ b/gn/src/gn/label_unittest.cc
@@ -0,0 +1,140 @@
+// 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 <stddef.h>
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "gn/err.h"
+#include "gn/label.h"
+#include "gn/value.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+namespace {
+
+struct ParseDepStringCase {
+ const char* cur_dir;
+ const char* str;
+ bool success;
+ const char* expected_dir;
+ const char* expected_name;
+ const char* expected_toolchain_dir;
+ const char* expected_toolchain_name;
+};
+
+} // namespace
+
+TEST(Label, Resolve) {
+ ParseDepStringCase cases[] = {
+ {"//chrome/", "", false, "", "", "", ""},
+ {"//chrome/", "/", false, "", "", "", ""},
+ {"//chrome/", ":", false, "", "", "", ""},
+ {"//chrome/", "/:", false, "", "", "", ""},
+ {"//chrome/", "blah", true, "//chrome/blah/", "blah", "//t/", "d"},
+ {"//chrome/", "blah:bar", true, "//chrome/blah/", "bar", "//t/", "d"},
+ // Absolute paths.
+ {"//chrome/", "/chrome:bar", true, "/chrome/", "bar", "//t/", "d"},
+ {"//chrome/", "/chrome/:bar", true, "/chrome/", "bar", "//t/", "d"},
+#if defined(OS_WIN)
+ {"//chrome/", "/C:/chrome:bar", true, "/C:/chrome/", "bar", "//t/", "d"},
+ {"//chrome/", "/C:/chrome/:bar", true, "/C:/chrome/", "bar", "//t/", "d"},
+ {"//chrome/", "C:/chrome:bar", true, "/C:/chrome/", "bar", "//t/", "d"},
+#endif
+ // Refers to root dir.
+ {"//chrome/", "//:bar", true, "//", "bar", "//t/", "d"},
+ // Implicit directory
+ {"//chrome/", ":bar", true, "//chrome/", "bar", "//t/", "d"},
+ {"//chrome/renderer/", ":bar", true, "//chrome/renderer/", "bar", "//t/",
+ "d"},
+ // Implicit names.
+ {"//chrome/", "//base", true, "//base/", "base", "//t/", "d"},
+ {"//chrome/", "//base/i18n", true, "//base/i18n/", "i18n", "//t/", "d"},
+ {"//chrome/", "//base/i18n:foo", true, "//base/i18n/", "foo", "//t/", "d"},
+ {"//chrome/", "//", false, "", "", "", ""},
+ // Toolchain parsing.
+ {"//chrome/", "//chrome:bar(//t:n)", true, "//chrome/", "bar", "//t/", "n"},
+ {"//chrome/", "//chrome:bar(//t)", true, "//chrome/", "bar", "//t/", "t"},
+ {"//chrome/", "//chrome:bar(//t:)", true, "//chrome/", "bar", "//t/", "t"},
+ {"//chrome/", "//chrome:bar()", true, "//chrome/", "bar", "//t/", "d"},
+ {"//chrome/", "//chrome:bar(foo)", true, "//chrome/", "bar",
+ "//chrome/foo/", "foo"},
+ {"//chrome/", "//chrome:bar(:foo)", true, "//chrome/", "bar", "//chrome/",
+ "foo"},
+ // TODO(brettw) it might be nice to make this an error:
+ //{"//chrome/", "//chrome:bar())", false, "", "", "", "" },
+ {"//chrome/", "//chrome:bar(//t:bar(tc))", false, "", "", "", ""},
+ {"//chrome/", "//chrome:bar(()", false, "", "", "", ""},
+ {"//chrome/", "(t:b)", false, "", "", "", ""},
+ {"//chrome/", ":bar(//t/b)", true, "//chrome/", "bar", "//t/b/", "b"},
+ {"//chrome/", ":bar(/t/b)", true, "//chrome/", "bar", "/t/b/", "b"},
+ {"//chrome/", ":bar(t/b)", true, "//chrome/", "bar", "//chrome/t/b/", "b"},
+ };
+
+ Label default_toolchain(SourceDir("//t/"), "d");
+
+ for (size_t i = 0; i < std::size(cases); i++) {
+ const ParseDepStringCase& cur = cases[i];
+
+ std::string location, name;
+ Err err;
+ Value v(nullptr, Value::STRING);
+ v.string_value() = cur.str;
+ Label result = Label::Resolve(SourceDir(cur.cur_dir), std::string_view(),
+ default_toolchain, v, &err);
+ EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.str;
+ if (!err.has_error() && cur.success) {
+ EXPECT_EQ(cur.expected_dir, result.dir().value()) << i << " " << cur.str;
+ EXPECT_EQ(cur.expected_name, result.name()) << i << " " << cur.str;
+ EXPECT_EQ(cur.expected_toolchain_dir, result.toolchain_dir().value())
+ << i << " " << cur.str;
+ EXPECT_EQ(cur.expected_toolchain_name, result.toolchain_name())
+ << i << " " << cur.str;
+ }
+ }
+}
+
+// Tests the case where the path resolves to something above "//". It should get
+// converted to an absolute path "/foo/bar".
+TEST(Label, ResolveAboveRootBuildDir) {
+ Label default_toolchain(SourceDir("//t/"), "d");
+
+ std::string location, name;
+ Err err;
+
+ SourceDir cur_dir("//cur/");
+ std::string source_root("/foo/bar/baz");
+
+ // No source root given, should not go above the root build dir.
+ Label result = Label::Resolve(cur_dir, std::string_view(), default_toolchain,
+ Value(nullptr, "../../..:target"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ("//", result.dir().value()) << result.dir().value();
+ EXPECT_EQ("target", result.name());
+
+ // Source root provided, it should go into that.
+ result = Label::Resolve(cur_dir, source_root, default_toolchain,
+ Value(nullptr, "../../..:target"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ("/foo/", result.dir().value()) << result.dir().value();
+ EXPECT_EQ("target", result.name());
+
+ // It should't go up higher than the system root.
+ result = Label::Resolve(cur_dir, source_root, default_toolchain,
+ Value(nullptr, "../../../../..:target"), &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ("/", result.dir().value()) << result.dir().value();
+ EXPECT_EQ("target", result.name());
+
+ // Test an absolute label that goes above the source root. This currently
+ // stops at the source root. It should arguably keep going and produce "/foo/"
+ // but this test just makes sure the current behavior isn't regressed by
+ // accident.
+ result = Label::Resolve(cur_dir, source_root, default_toolchain,
+ Value(nullptr, "//../.."), &err);
+ EXPECT_FALSE(err.has_error()) << err.message();
+ EXPECT_EQ("/foo/", result.dir().value()) << result.dir().value();
+ EXPECT_EQ("foo", result.name());
+}
diff --git a/gn/src/gn/lib_file.cc b/gn/src/gn/lib_file.cc
new file mode 100644
index 00000000000..cbb67f14e76
--- /dev/null
+++ b/gn/src/gn/lib_file.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.
+
+#include "gn/lib_file.h"
+
+#include "base/logging.h"
+
+LibFile::LibFile(const SourceFile& source_file) : source_file_(source_file) {}
+
+LibFile::LibFile(const std::string_view& lib_name)
+ : name_(lib_name.data(), lib_name.size()) {
+ DCHECK(!lib_name.empty());
+}
+
+const std::string& LibFile::value() const {
+ return is_source_file() ? source_file_.value() : name_;
+}
+
+const SourceFile& LibFile::source_file() const {
+ DCHECK(is_source_file());
+ return source_file_;
+}
diff --git a/gn/src/gn/lib_file.h b/gn/src/gn/lib_file.h
new file mode 100644
index 00000000000..47952f8e14d
--- /dev/null
+++ b/gn/src/gn/lib_file.h
@@ -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.
+
+#ifndef TOOLS_GN_LIB_FILE_H_
+#define TOOLS_GN_LIB_FILE_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <string>
+#include <string_view>
+
+#include "gn/source_file.h"
+
+// Represents an entry in "libs" list. Can be either a path (a SourceFile) or
+// a library name (a string).
+class LibFile {
+ public:
+ LibFile() = default;
+
+ explicit LibFile(const std::string_view& lib_name);
+ explicit LibFile(const SourceFile& source_file);
+
+ bool is_source_file() const { return name_.empty(); }
+
+ // Returns name, or source_file().value() (whichever is set).
+ const std::string& value() const;
+ const SourceFile& source_file() const;
+
+ bool operator==(const LibFile& other) const {
+ return value() == other.value();
+ }
+ bool operator!=(const LibFile& other) const { return !operator==(other); }
+ bool operator<(const LibFile& other) const { return value() < other.value(); }
+
+ private:
+ std::string name_;
+ SourceFile source_file_;
+};
+
+namespace std {
+
+template <>
+struct hash<LibFile> {
+ std::size_t operator()(const LibFile& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+
+} // namespace std
+
+#endif // TOOLS_GN_LIB_FILE_H_
diff --git a/gn/src/gn/loader.cc b/gn/src/gn/loader.cc
new file mode 100644
index 00000000000..c6151705fa2
--- /dev/null
+++ b/gn/src/gn/loader.cc
@@ -0,0 +1,448 @@
+// 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 "gn/loader.h"
+
+#include <memory>
+
+#include "gn/build_settings.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file_manager.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "gn/scope_per_file_provider.h"
+#include "gn/settings.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/trace.h"
+
+namespace {
+
+struct SourceFileAndOrigin {
+ SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
+ : file(f), origin(o) {}
+
+ SourceFile file;
+ LocationRange origin;
+};
+
+} // namespace
+
+// Identifies one time a file is loaded in a given toolchain so we don't load
+// it more than once.
+struct LoaderImpl::LoadID {
+ LoadID() = default;
+ LoadID(const SourceFile& f, const Label& tc_name)
+ : file(f), toolchain_name(tc_name) {}
+
+ bool operator<(const LoadID& other) const {
+ if (file.value() == other.file.value())
+ return toolchain_name < other.toolchain_name;
+ return file < other.file;
+ }
+
+ SourceFile file;
+ Label toolchain_name;
+};
+
+// Our tracking information for a toolchain.
+struct LoaderImpl::ToolchainRecord {
+ // The default toolchain label can be empty for the first time the default
+ // toolchain is loaded, since we don't know it yet. This will be fixed up
+ // later. It should be valid in all other cases.
+ ToolchainRecord(const BuildSettings* build_settings,
+ const Label& toolchain_label,
+ const Label& default_toolchain_label)
+ : settings(
+ build_settings,
+ GetOutputSubdirName(toolchain_label,
+ toolchain_label == default_toolchain_label)),
+ is_toolchain_loaded(false),
+ is_config_loaded(false) {
+ settings.set_default_toolchain_label(default_toolchain_label);
+ settings.set_toolchain_label(toolchain_label);
+ }
+
+ Settings settings;
+
+ bool is_toolchain_loaded;
+ bool is_config_loaded;
+
+ std::vector<SourceFileAndOrigin> waiting_on_me;
+};
+
+// -----------------------------------------------------------------------------
+
+const void* const Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
+
+Loader::Loader() = default;
+
+Loader::~Loader() = default;
+
+void Loader::Load(const Label& label, const LocationRange& origin) {
+ Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
+}
+
+// -----------------------------------------------------------------------------
+
+LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
+ : pending_loads_(0), build_settings_(build_settings) {
+ // There may not be an active TaskRunner at this point. When that's the case,
+ // the calling code is expected to call set_task_runner().
+ task_runner_ = MsgLoop::Current();
+}
+
+LoaderImpl::~LoaderImpl() = default;
+
+void LoaderImpl::Load(const SourceFile& file,
+ const LocationRange& origin,
+ const Label& in_toolchain_name) {
+ const Label& toolchain_name = in_toolchain_name.is_null()
+ ? default_toolchain_label_
+ : in_toolchain_name;
+ LoadID load_id(file, toolchain_name);
+ if (!invocations_.insert(load_id).second)
+ return; // Already in set, so this file was already loaded or schedulerd.
+
+ if (toolchain_records_.empty()) {
+ // Nothing loaded, need to load the default build config. The initial load
+ // should not specify a toolchain.
+ DCHECK(toolchain_name.is_null());
+
+ std::unique_ptr<ToolchainRecord> new_record =
+ std::make_unique<ToolchainRecord>(build_settings_, Label(), Label());
+ ToolchainRecord* record = new_record.get();
+ Label empty_label; // VS issues spurious warning using ...[Label()].
+ toolchain_records_[empty_label] = std::move(new_record);
+
+ // The default build config is no dependent on the toolchain definition,
+ // since we need to load the build config before we know what the default
+ // toolchain name is.
+ record->is_toolchain_loaded = true;
+
+ record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
+ ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
+
+ return;
+ }
+
+ ToolchainRecord* record;
+ if (toolchain_name.is_null())
+ record = toolchain_records_[default_toolchain_label_].get();
+ else
+ record = toolchain_records_[toolchain_name].get();
+
+ if (!record) {
+ DCHECK(!default_toolchain_label_.is_null());
+
+ // No reference to this toolchain found yet, make one.
+ std::unique_ptr<ToolchainRecord> new_record =
+ std::make_unique<ToolchainRecord>(build_settings_, toolchain_name,
+ default_toolchain_label_);
+ record = new_record.get();
+ toolchain_records_[toolchain_name] = std::move(new_record);
+
+ // Schedule a load of the toolchain using the default one.
+ Load(BuildFileForLabel(toolchain_name), origin, default_toolchain_label_);
+ }
+
+ if (record->is_config_loaded)
+ ScheduleLoadFile(&record->settings, origin, file);
+ else
+ record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
+}
+
+void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
+ ToolchainRecord* record = toolchain_records_[toolchain->label()].get();
+ if (!record) {
+ DCHECK(!default_toolchain_label_.is_null());
+ std::unique_ptr<ToolchainRecord> new_record =
+ std::make_unique<ToolchainRecord>(build_settings_, toolchain->label(),
+ default_toolchain_label_);
+ record = new_record.get();
+ toolchain_records_[toolchain->label()] = std::move(new_record);
+ }
+ record->is_toolchain_loaded = true;
+
+ // The default build config is loaded first, then its toolchain. Secondary
+ // ones are loaded in the opposite order so we can pass toolchain parameters
+ // to the build config. So we may or may not have a config at this point.
+ if (!record->is_config_loaded) {
+ ScheduleLoadBuildConfig(&record->settings, toolchain->args());
+ } else {
+ // There should be nobody waiting on this if the build config is already
+ // loaded.
+ DCHECK(record->waiting_on_me.empty());
+ }
+}
+
+Label LoaderImpl::GetDefaultToolchain() const {
+ return default_toolchain_label_;
+}
+
+const Settings* LoaderImpl::GetToolchainSettings(const Label& label) const {
+ ToolchainRecordMap::const_iterator found_toolchain;
+ if (label.is_null()) {
+ if (default_toolchain_label_.is_null())
+ return nullptr;
+ found_toolchain = toolchain_records_.find(default_toolchain_label_);
+ } else {
+ found_toolchain = toolchain_records_.find(label);
+ }
+
+ if (found_toolchain == toolchain_records_.end())
+ return nullptr;
+ return &found_toolchain->second->settings;
+}
+
+SourceFile LoaderImpl::BuildFileForLabel(const Label& label) const {
+ return SourceFile(
+ label.dir().value() + "BUILD" + build_file_extension_ + ".gn");
+}
+
+void LoaderImpl::ScheduleLoadFile(const Settings* settings,
+ const LocationRange& origin,
+ const SourceFile& file) {
+ Err err;
+ pending_loads_++;
+ if (!AsyncLoadFile(
+ origin, settings->build_settings(), file,
+ [this, settings, file, origin](const ParseNode* parse_node) {
+ BackgroundLoadFile(settings, file, origin, parse_node);
+ },
+ &err)) {
+ g_scheduler->FailWithError(err);
+ DecrementPendingLoads();
+ }
+}
+
+void LoaderImpl::ScheduleLoadBuildConfig(
+ Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides) {
+ Err err;
+ pending_loads_++;
+ if (!AsyncLoadFile(
+ LocationRange(), settings->build_settings(),
+ settings->build_settings()->build_config_file(),
+ [this, settings, toolchain_overrides](const ParseNode* root) {
+ BackgroundLoadBuildConfig(settings, toolchain_overrides, root);
+ },
+ &err)) {
+ g_scheduler->FailWithError(err);
+ DecrementPendingLoads();
+ }
+}
+
+void LoaderImpl::BackgroundLoadFile(const Settings* settings,
+ const SourceFile& file_name,
+ const LocationRange& origin,
+ const ParseNode* root) {
+ if (!root) {
+ task_runner_->PostTask([this]() { DecrementPendingLoads(); });
+ return;
+ }
+
+ if (g_scheduler->verbose_logging()) {
+ g_scheduler->Log("Running",
+ file_name.value() + " with toolchain " +
+ settings->toolchain_label().GetUserVisibleName(false));
+ }
+
+ Scope our_scope(settings->base_config());
+ ScopePerFileProvider per_file_provider(&our_scope, true);
+ our_scope.set_source_dir(file_name.GetDir());
+ our_scope.AddBuildDependencyFile(file_name);
+
+ // Targets, etc. generated as part of running this file will end up here.
+ Scope::ItemVector collected_items;
+ our_scope.set_item_collector(&collected_items);
+
+ ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
+ trace.SetToolchain(settings->toolchain_label());
+
+ Err err;
+ root->Execute(&our_scope, &err);
+ if (!err.has_error())
+ our_scope.CheckForUnusedVars(&err);
+
+ if (err.has_error()) {
+ if (!origin.is_null())
+ err.AppendSubErr(Err(origin, "which caused the file to be included."));
+
+ if (!settings->is_default())
+ err.set_toolchain_label(settings->toolchain_label());
+
+ g_scheduler->FailWithError(err);
+ }
+
+ // Pass all of the items that were defined off to the builder.
+ for (auto& item : collected_items)
+ settings->build_settings()->ItemDefined(std::move(item));
+
+ trace.Done();
+
+ task_runner_->PostTask([this]() { DidLoadFile(); });
+}
+
+void LoaderImpl::BackgroundLoadBuildConfig(
+ Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides,
+ const ParseNode* root) {
+ if (!root) {
+ task_runner_->PostTask([this]() { DecrementPendingLoads(); });
+ return;
+ }
+
+ Scope* base_config = settings->base_config();
+ base_config->set_source_dir(SourceDir("//"));
+ base_config->AddBuildDependencyFile(
+ settings->build_settings()->build_config_file());
+
+ settings->build_settings()->build_args().SetupRootScope(base_config,
+ toolchain_overrides);
+
+ base_config->SetProcessingBuildConfig();
+
+ // See kDefaultToolchainKey in the header.
+ Label default_toolchain_label;
+ if (settings->is_default())
+ base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
+
+ ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
+ settings->build_settings()->build_config_file().value());
+ trace.SetToolchain(settings->toolchain_label());
+
+ // Run the BUILDCONFIG with its directory as the current one. We want
+ // BUILDCONFIG to modify the base_config so can't make a copy or a nested one.
+ base_config->set_source_dir(
+ settings->build_settings()->build_config_file().GetDir());
+
+ Err err;
+ root->Execute(base_config, &err);
+
+ // Put back the root as the default source dir. This probably isn't necessary
+ // as other scopes will set their directories to their own path, but it's a
+ // better default than the build config's directory.
+ base_config->set_source_dir(SourceDir("//"));
+
+ // Clear all private variables left in the scope. We want the root build
+ // config to be like a .gni file in that variables beginning with an
+ // underscore aren't exported.
+ base_config->RemovePrivateIdentifiers();
+
+ trace.Done();
+
+ if (err.has_error()) {
+ if (!settings->is_default())
+ err.set_toolchain_label(settings->toolchain_label());
+
+ g_scheduler->FailWithError(err);
+ }
+
+ base_config->ClearProcessingBuildConfig();
+ if (settings->is_default()) {
+ // The default toolchain must have been set in the default build config
+ // file.
+ if (default_toolchain_label.is_null()) {
+ g_scheduler->FailWithError(Err(
+ Location(),
+ "The default build config file did not call set_default_toolchain()",
+ "If you don't call this, I can't figure out what toolchain to use\n"
+ "for all of this code."));
+ } else {
+ DCHECK(settings->toolchain_label().is_null());
+ settings->set_toolchain_label(default_toolchain_label);
+ }
+ }
+
+ task_runner_->PostTask(
+ [this, toolchain_label = settings->toolchain_label()]() {
+ DidLoadBuildConfig(toolchain_label);
+ });
+}
+
+void LoaderImpl::DidLoadFile() {
+ DecrementPendingLoads();
+}
+
+void LoaderImpl::DidLoadBuildConfig(const Label& label) {
+ // Do not return early, we must call DecrementPendingLoads() at the bottom.
+
+ ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
+ ToolchainRecord* record = nullptr;
+ if (found_toolchain == toolchain_records_.end()) {
+ // When loading the default build config, we'll insert it into the record
+ // map with an empty label since we don't yet know what to call it.
+ //
+ // In this case, we should have exactly one entry in the map with an empty
+ // label. We now need to fix up the naming so it refers to the "real" one.
+ CHECK_EQ(1U, toolchain_records_.size());
+ ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
+ CHECK(empty_label != toolchain_records_.end());
+
+ // Fix up the toolchain record.
+ std::unique_ptr<ToolchainRecord> moved_record =
+ std::move(empty_label->second);
+ record = moved_record.get();
+ toolchain_records_[label] = std::move(moved_record);
+ toolchain_records_.erase(empty_label);
+
+ // Save the default toolchain label.
+ default_toolchain_label_ = label;
+ DCHECK(record->settings.default_toolchain_label().is_null());
+ record->settings.set_default_toolchain_label(label);
+
+ // The settings object should have the toolchain label already set.
+ DCHECK(!record->settings.toolchain_label().is_null());
+
+ // Update any stored invocations that refer to the empty toolchain label.
+ // This will normally only be one, for the root build file, so brute-force
+ // is OK.
+ LoadIDSet old_loads;
+ invocations_.swap(old_loads);
+ for (const auto& load : old_loads) {
+ if (load.toolchain_name.is_null()) {
+ // Fix up toolchain label
+ invocations_.emplace(load.file, label);
+ } else {
+ // Can keep the old one.
+ invocations_.insert(load);
+ }
+ }
+ } else {
+ record = found_toolchain->second.get();
+ }
+
+ DCHECK(!record->is_config_loaded);
+ DCHECK(record->is_toolchain_loaded);
+ record->is_config_loaded = true;
+
+ // Schedule all waiting file loads.
+ for (const auto& waiting : record->waiting_on_me)
+ ScheduleLoadFile(&record->settings, waiting.origin, waiting.file);
+ record->waiting_on_me.clear();
+
+ DecrementPendingLoads();
+}
+
+void LoaderImpl::DecrementPendingLoads() {
+ DCHECK_GT(pending_loads_, 0);
+ pending_loads_--;
+ if (pending_loads_ == 0 && complete_callback_)
+ complete_callback_();
+}
+
+bool LoaderImpl::AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ std::function<void(const ParseNode*)> callback,
+ Err* err) {
+ if (async_load_file_) {
+ return async_load_file_(origin, build_settings, file_name,
+ std::move(callback), err);
+ }
+ return g_scheduler->input_file_manager()->AsyncLoadFile(
+ origin, build_settings, file_name, std::move(callback), err);
+}
diff --git a/gn/src/gn/loader.h b/gn/src/gn/loader.h
new file mode 100644
index 00000000000..8f95bb8817f
--- /dev/null
+++ b/gn/src/gn/loader.h
@@ -0,0 +1,187 @@
+// 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 TOOLS_GN_LOADER_H_
+#define TOOLS_GN_LOADER_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/memory/ref_counted.h"
+#include "gn/label.h"
+#include "gn/scope.h"
+#include "util/msg_loop.h"
+
+class BuildSettings;
+class LocationRange;
+class Settings;
+class SourceFile;
+class Toolchain;
+
+// The loader manages execution of the different build files. It receives
+// requests (normally from the Builder) when new references are found, and also
+// manages loading the build config files.
+//
+// This loader class is abstract so it can be mocked out for testing the
+// Builder.
+class Loader : public base::RefCountedThreadSafe<Loader> {
+ public:
+ Loader();
+
+ // Loads the given file in the conext of the given toolchain. The initial
+ // call to this (the one that actually starts the generation) should have an
+ // empty toolchain name, which will trigger the load of the default build
+ // config.
+ virtual void Load(const SourceFile& file,
+ const LocationRange& origin,
+ const Label& toolchain_name) = 0;
+
+ // Notification that the given toolchain has loaded. This will unblock files
+ // waiting on this definition.
+ virtual void ToolchainLoaded(const Toolchain* toolchain) = 0;
+
+ // Returns the label of the default toolchain.
+ virtual Label GetDefaultToolchain() const = 0;
+
+ // Returns information about the toolchain with the given label. Will return
+ // false if we haven't processed this toolchain yet.
+ virtual const Settings* GetToolchainSettings(const Label& label) const = 0;
+
+ // Returns the build file that the given label references.
+ virtual SourceFile BuildFileForLabel(const Label& label) const = 0;
+
+ // Helper function that extracts the file and toolchain name from the given
+ // label, and calls Load().
+ void Load(const Label& label, const LocationRange& origin);
+
+ // When processing the default build config, we want to capture the argument
+ // of set_default_build_config. The implementation of that function uses this
+ // constant as a property key to get the Label* out of the scope where the
+ // label should be stored.
+ static const void* const kDefaultToolchainKey;
+
+ protected:
+ friend class base::RefCountedThreadSafe<Loader>;
+ virtual ~Loader();
+};
+
+class LoaderImpl : public Loader {
+ public:
+ // Callback to emulate InputFileManager::AsyncLoadFile.
+ using AsyncLoadFileCallback =
+ std::function<bool(const LocationRange&,
+ const BuildSettings*,
+ const SourceFile&,
+ std::function<void(const ParseNode*)>,
+ Err*)>;
+
+ explicit LoaderImpl(const BuildSettings* build_settings);
+
+ // Loader implementation.
+ void Load(const SourceFile& file,
+ const LocationRange& origin,
+ const Label& toolchain_name) override;
+ void ToolchainLoaded(const Toolchain* toolchain) override;
+ Label GetDefaultToolchain() const override;
+ const Settings* GetToolchainSettings(const Label& label) const override;
+ SourceFile BuildFileForLabel(const Label& label) const override;
+
+ // Sets the task runner corresponding to the main thread. By default this
+ // class will use the thread active during construction, but there is not
+ // a task runner active during construction all the time.
+ void set_task_runner(MsgLoop* task_runner) { task_runner_ = task_runner; }
+
+ // The complete callback is called whenever there are no more pending loads.
+ // Called on the main thread only. This may be called more than once if the
+ // queue is drained, but then more stuff gets added.
+ void set_complete_callback(std::function<void()> cb) {
+ complete_callback_ = std::move(cb);
+ }
+
+ // This callback is used when the loader finds it wants to load a file.
+ void set_async_load_file(AsyncLoadFileCallback cb) {
+ async_load_file_ = std::move(cb);
+ }
+
+ // Sets the additional extension for build files in this build.
+ // The resulting file name will be "BUILD.<extension>.gn".
+ void set_build_file_extension(std::string extension) {
+ build_file_extension_ = "." + extension;
+ }
+
+ const Label& default_toolchain_label() const {
+ return default_toolchain_label_;
+ }
+
+ private:
+ struct LoadID;
+ struct ToolchainRecord;
+
+ ~LoaderImpl() override;
+
+ // Schedules the input file manager to load the given file.
+ void ScheduleLoadFile(const Settings* settings,
+ const LocationRange& origin,
+ const SourceFile& file);
+ void ScheduleLoadBuildConfig(Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides);
+
+ // Runs the given file on the background thread. These are called by the
+ // input file manager.
+ void BackgroundLoadFile(const Settings* settings,
+ const SourceFile& file_name,
+ const LocationRange& origin,
+ const ParseNode* root);
+ void BackgroundLoadBuildConfig(Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides,
+ const ParseNode* root);
+
+ // Posted to the main thread when any file other than a build config file
+ // file has completed running.
+ void DidLoadFile();
+
+ // Posted to the main thread when any build config file has completed
+ // running. The label should be the name of the toolchain.
+ //
+ // If there is no defauled toolchain loaded yet, we'll assume that the first
+ // call to this indicates to the default toolchain, and this function will
+ // set the default toolchain name to the given label.
+ void DidLoadBuildConfig(const Label& label);
+
+ // Decrements the pending_loads_ variable and issues the complete callback if
+ // necessary.
+ void DecrementPendingLoads();
+
+ // Forwards to the appropriate location to load the file.
+ bool AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ std::function<void(const ParseNode*)> callback,
+ Err* err);
+
+ MsgLoop* task_runner_;
+
+ int pending_loads_;
+ std::function<void()> complete_callback_;
+
+ // When non-null, use this callback instead of the InputFileManager for
+ // mocking purposes.
+ AsyncLoadFileCallback async_load_file_;
+
+ using LoadIDSet = std::set<LoadID>;
+ LoadIDSet invocations_;
+
+ const BuildSettings* build_settings_;
+ Label default_toolchain_label_;
+
+ // Records for the build config file loads.
+ using ToolchainRecordMap = std::map<Label, std::unique_ptr<ToolchainRecord>>;
+ ToolchainRecordMap toolchain_records_;
+
+ std::string build_file_extension_;
+};
+
+#endif // TOOLS_GN_LOADER_H_
diff --git a/gn/src/gn/loader_unittest.cc b/gn/src/gn/loader_unittest.cc
new file mode 100644
index 00000000000..ce14894a63b
--- /dev/null
+++ b/gn/src/gn/loader_unittest.cc
@@ -0,0 +1,393 @@
+// 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 <functional>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "gn/build_settings.h"
+#include "gn/err.h"
+#include "gn/loader.h"
+#include "gn/parse_tree.h"
+#include "gn/parser.h"
+#include "gn/scheduler.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/tokenizer.h"
+#include "util/msg_loop.h"
+#include "util/test/test.h"
+
+namespace {
+
+bool ItemContainsBuildDependencyFile(const Item* item,
+ const SourceFile& source_file) {
+ const auto& build_dependency_files = item->build_dependency_files();
+ return build_dependency_files.end() !=
+ build_dependency_files.find(source_file);
+}
+
+class MockBuilder {
+ public:
+ void OnItemDefined(std::unique_ptr<Item> item);
+ std::vector<const Item*> GetAllItems() const;
+
+ private:
+ std::vector<std::unique_ptr<Item>> items_;
+};
+
+void MockBuilder::OnItemDefined(std::unique_ptr<Item> item) {
+ items_.push_back(std::move(item));
+}
+
+std::vector<const Item*> MockBuilder::GetAllItems() const {
+ std::vector<const Item*> result;
+ for (const auto& item : items_) {
+ result.push_back(item.get());
+ }
+
+ return result;
+}
+
+class MockInputFileManager {
+ public:
+ using Callback = std::function<void(const ParseNode*)>;
+
+ MockInputFileManager();
+ ~MockInputFileManager();
+
+ LoaderImpl::AsyncLoadFileCallback GetAsyncCallback();
+
+ // Sets a given response for a given source file.
+ void AddCannedResponse(const SourceFile& source_file,
+ const std::string& source);
+
+ // Returns true if there is/are pending load(s) matching the given file(s).
+ bool HasOnePending(const SourceFile& f) const;
+ bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const;
+
+ void IssueAllPending();
+
+ private:
+ struct CannedResult {
+ std::unique_ptr<InputFile> input_file;
+ std::vector<Token> tokens;
+ std::unique_ptr<ParseNode> root;
+ };
+
+ InputFileManager::SyncLoadFileCallback GetSyncCallback();
+
+ bool AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const Callback& callback,
+ Err* err) {
+ pending_.push_back(std::make_pair(file_name, callback));
+ return true;
+ }
+
+ using CannedResponseMap = std::map<SourceFile, std::unique_ptr<CannedResult>>;
+ CannedResponseMap canned_responses_;
+
+ std::vector<std::pair<SourceFile, Callback>> pending_;
+};
+
+MockInputFileManager::MockInputFileManager() {
+ g_scheduler->input_file_manager()->set_load_file_callback(GetSyncCallback());
+}
+
+MockInputFileManager::~MockInputFileManager() {
+ g_scheduler->input_file_manager()->set_load_file_callback(nullptr);
+}
+
+LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetAsyncCallback() {
+ return
+ [this](const LocationRange& origin, const BuildSettings* build_settings,
+ const SourceFile& file_name, const Callback& callback, Err* err) {
+ return AsyncLoadFile(origin, build_settings, file_name, callback, err);
+ };
+}
+
+InputFileManager::SyncLoadFileCallback MockInputFileManager::GetSyncCallback() {
+ return
+ [this](const SourceFile& file_name, InputFile* file) {
+ CannedResponseMap::const_iterator found = canned_responses_.find(file_name);
+ if (found == canned_responses_.end())
+ return false;
+ file->SetContents(found->second->input_file->contents());
+ return true;
+ };
+}
+
+// Sets a given response for a given source file.
+void MockInputFileManager::AddCannedResponse(const SourceFile& source_file,
+ const std::string& source) {
+ std::unique_ptr<CannedResult> canned = std::make_unique<CannedResult>();
+ canned->input_file = std::make_unique<InputFile>(source_file);
+ canned->input_file->SetContents(source);
+
+ // Tokenize.
+ Err err;
+ canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Parse.
+ canned->root = Parser::Parse(canned->tokens, &err);
+ if (err.has_error())
+ err.PrintToStdout();
+ EXPECT_FALSE(err.has_error());
+
+ canned_responses_[source_file] = std::move(canned);
+}
+
+bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
+ return pending_.size() == 1u && pending_[0].first == f;
+}
+
+bool MockInputFileManager::HasTwoPending(const SourceFile& f1,
+ const SourceFile& f2) const {
+ if (pending_.size() != 2u)
+ return false;
+ return pending_[0].first == f1 && pending_[1].first == f2;
+}
+
+void MockInputFileManager::IssueAllPending() {
+ BlockNode block(BlockNode::DISCARDS_RESULT); // Default response.
+
+ for (const auto& cur : pending_) {
+ CannedResponseMap::const_iterator found = canned_responses_.find(cur.first);
+ if (found == canned_responses_.end())
+ cur.second(&block);
+ else
+ cur.second(found->second->root.get());
+ }
+ pending_.clear();
+}
+
+// LoaderTest ------------------------------------------------------------------
+
+class LoaderTest : public TestWithScheduler {
+ public:
+ LoaderTest() { build_settings_.SetBuildDir(SourceDir("//out/Debug/")); }
+
+ protected:
+ BuildSettings build_settings_;
+ MockBuilder mock_builder_;
+ MockInputFileManager mock_ifm_;
+};
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+TEST_F(LoaderTest, Foo) {
+ SourceFile build_config("//build/config/BUILDCONFIG.gn");
+ build_settings_.set_build_config_file(build_config);
+
+ scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
+
+ // The default toolchain needs to be set by the build config file.
+ mock_ifm_.AddCannedResponse(build_config,
+ "set_default_toolchain(\"//tc:tc\")");
+
+ loader->set_async_load_file(mock_ifm_.GetAsyncCallback());
+
+ // Request the root build file be loaded. This should kick off the default
+ // build config loading.
+ SourceFile root_build("//BUILD.gn");
+ loader->Load(root_build, LocationRange(), Label());
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Completing the build config load should kick off the root build file load.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+ EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
+
+ // Load the root build file.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+
+ // Schedule some other file to load in another toolchain.
+ Label second_tc(SourceDir("//tc2/"), "tc2");
+ SourceFile second_file("//foo/BUILD.gn");
+ loader->Load(second_file, LocationRange(), second_tc);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn")));
+
+ // Running the toolchain file should schedule the build config file to load
+ // for that toolchain.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+
+ // We have to tell it we have a toolchain definition now (normally the
+ // builder would do this).
+ const Settings* default_settings = loader->GetToolchainSettings(Label());
+ Toolchain second_tc_object(default_settings, second_tc);
+ loader->ToolchainLoaded(&second_tc_object);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Scheduling a second file to load in that toolchain should not make it
+ // pending yet (it's waiting for the build config).
+ SourceFile third_file("//bar/BUILD.gn");
+ loader->Load(third_file, LocationRange(), second_tc);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Running the build config file should make our third file pending.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+ EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file));
+
+ EXPECT_FALSE(scheduler().is_failed());
+}
+
+TEST_F(LoaderTest, BuildDependencyFilesAreCollected) {
+ SourceFile build_config("//build/config/BUILDCONFIG.gn");
+ SourceFile root_build("//BUILD.gn");
+ build_settings_.set_build_config_file(build_config);
+ build_settings_.set_item_defined_callback(
+ [builder = &mock_builder_](std::unique_ptr<Item> item) {
+ builder->OnItemDefined(std::move(item));
+ });
+
+ scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
+ mock_ifm_.AddCannedResponse(build_config,
+ "set_default_toolchain(\"//tc:tc\")");
+ std::string root_build_content =
+ "executable(\"a\") { sources = [ \"a.cc\" ] }\n"
+ "config(\"b\") { configs = [\"//t:t\"] }\n"
+ "toolchain(\"c\") {}\n"
+ "pool(\"d\") { depth = 1 }";
+ mock_ifm_.AddCannedResponse(root_build, root_build_content);
+
+ loader->set_async_load_file(mock_ifm_.GetAsyncCallback());
+
+ // Request the root build file be loaded. This should kick off the default
+ // build config loading.
+ loader->Load(root_build, LocationRange(), Label());
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Completing the build config load should kick off the root build file load.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+ EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
+
+ // Completing the root build file should define a target which must have
+ // set of source files hashes.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+
+ std::vector<const Item*> items = mock_builder_.GetAllItems();
+ EXPECT_TRUE(items[0]->AsTarget());
+ EXPECT_TRUE(ItemContainsBuildDependencyFile(items[0], root_build));
+ EXPECT_TRUE(items[1]->AsConfig());
+ EXPECT_TRUE(ItemContainsBuildDependencyFile(items[1], root_build));
+ EXPECT_TRUE(items[2]->AsToolchain());
+ EXPECT_TRUE(ItemContainsBuildDependencyFile(items[2], root_build));
+ EXPECT_TRUE(items[3]->AsPool());
+ EXPECT_TRUE(ItemContainsBuildDependencyFile(items[3], root_build));
+
+ EXPECT_FALSE(scheduler().is_failed());
+}
+
+TEST_F(LoaderTest, TemplateBuildDependencyFilesAreCollected) {
+ SourceFile build_config("//build/config/BUILDCONFIG.gn");
+ SourceFile root_build("//BUILD.gn");
+ build_settings_.set_build_config_file(build_config);
+ build_settings_.set_item_defined_callback(
+ [builder = &mock_builder_](std::unique_ptr<Item> item) {
+ builder->OnItemDefined(std::move(item));
+ });
+
+ scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
+ mock_ifm_.AddCannedResponse(build_config,
+ "set_default_toolchain(\"//tc:tc\")");
+ mock_ifm_.AddCannedResponse(
+ SourceFile("//test.gni"),
+ "template(\"tmpl\") {\n"
+ " executable(target_name) { sources = invoker.sources }\n"
+ "}\n");
+ mock_ifm_.AddCannedResponse(root_build,
+ "import(\"//test.gni\")\n"
+ "tmpl(\"a\") {sources = [ \"a.cc\" ]}\n");
+
+ loader->set_async_load_file(mock_ifm_.GetAsyncCallback());
+
+ // Request the root build file be loaded. This should kick off the default
+ // build config loading.
+ loader->Load(root_build, LocationRange(), Label());
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Completing the build config load should kick off the root build file load.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+ EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
+
+ // Completing the root build file should define a target which must have
+ // set of source files hashes.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+
+ std::vector<const Item*> items = mock_builder_.GetAllItems();
+ EXPECT_TRUE(items[0]->AsTarget());
+ // Ensure the target as a dep on BUILD.gn even though it was defined via
+ // a template.
+ EXPECT_TRUE(ItemContainsBuildDependencyFile(items[0], root_build));
+
+ EXPECT_FALSE(scheduler().is_failed());
+}
+
+TEST_F(LoaderTest, NonDefaultBuildFileName) {
+ std::string new_name = "BUILD.more.gn";
+
+ SourceFile build_config("//build/config/BUILDCONFIG.gn");
+ build_settings_.set_build_config_file(build_config);
+
+ scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
+ loader->set_build_file_extension("more");
+
+ // The default toolchain needs to be set by the build config file.
+ mock_ifm_.AddCannedResponse(build_config,
+ "set_default_toolchain(\"//tc:tc\")");
+
+ loader->set_async_load_file(mock_ifm_.GetAsyncCallback());
+
+ // Request the root build file be loaded. This should kick off the default
+ // build config loading.
+ SourceFile root_build("//" + new_name);
+ loader->Load(root_build, LocationRange(), Label());
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Completing the build config load should kick off the root build file load.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+ EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
+
+ // Load the root build file.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+
+ // Schedule some other file to load in another toolchain.
+ Label second_tc(SourceDir("//tc2/"), "tc2");
+ SourceFile second_file("//foo/" + new_name);
+ loader->Load(second_file, LocationRange(), second_tc);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/" + new_name)));
+
+ // Running the toolchain file should schedule the build config file to load
+ // for that toolchain.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+
+ // We have to tell it we have a toolchain definition now (normally the
+ // builder would do this).
+ const Settings* default_settings = loader->GetToolchainSettings(Label());
+ Toolchain second_tc_object(default_settings, second_tc);
+ loader->ToolchainLoaded(&second_tc_object);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Running the build config file should make our second file pending.
+ mock_ifm_.IssueAllPending();
+ MsgLoop::Current()->RunUntilIdleForTesting();
+ EXPECT_TRUE(mock_ifm_.HasOnePending(second_file));
+
+ EXPECT_FALSE(scheduler().is_failed());
+}
diff --git a/gn/src/gn/location.cc b/gn/src/gn/location.cc
new file mode 100644
index 00000000000..95bdd133089
--- /dev/null
+++ b/gn/src/gn/location.cc
@@ -0,0 +1,69 @@
+// 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 "gn/location.h"
+
+#include <tuple>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "gn/input_file.h"
+
+Location::Location() = default;
+
+Location::Location(const InputFile* file,
+ int line_number,
+ int column_number,
+ int byte)
+ : file_(file),
+ line_number_(line_number),
+ column_number_(column_number),
+ byte_(byte) {}
+
+bool Location::operator==(const Location& other) const {
+ return other.file_ == file_ && other.line_number_ == line_number_ &&
+ other.column_number_ == column_number_;
+}
+
+bool Location::operator!=(const Location& other) const {
+ return !operator==(other);
+}
+
+bool Location::operator<(const Location& other) const {
+ DCHECK(file_ == other.file_);
+ return std::tie(line_number_, column_number_) <
+ std::tie(other.line_number_, other.column_number_);
+}
+
+std::string Location::Describe(bool include_column_number) const {
+ if (!file_)
+ return std::string();
+
+ std::string ret;
+ if (file_->friendly_name().empty())
+ ret = file_->name().value();
+ else
+ ret = file_->friendly_name();
+
+ ret += ":";
+ ret += base::IntToString(line_number_);
+ if (include_column_number) {
+ ret += ":";
+ ret += base::IntToString(column_number_);
+ }
+ return ret;
+}
+
+LocationRange::LocationRange() = default;
+
+LocationRange::LocationRange(const Location& begin, const Location& end)
+ : begin_(begin), end_(end) {
+ DCHECK(begin_.file() == end_.file());
+}
+
+LocationRange LocationRange::Union(const LocationRange& other) const {
+ DCHECK(begin_.file() == other.begin_.file());
+ return LocationRange(begin_ < other.begin_ ? begin_ : other.begin_,
+ end_ < other.end_ ? other.end_ : end_);
+}
diff --git a/gn/tools/gn/location.h b/gn/src/gn/location.h
index 471391b374a..471391b374a 100644
--- a/gn/tools/gn/location.h
+++ b/gn/src/gn/location.h
diff --git a/gn/src/gn/metadata.cc b/gn/src/gn/metadata.cc
new file mode 100644
index 00000000000..fa52081a03e
--- /dev/null
+++ b/gn/src/gn/metadata.cc
@@ -0,0 +1,273 @@
+// Copyright 2018 The Chromium 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 "gn/metadata.h"
+
+#include "gn/filesystem_utils.h"
+
+const char kMetadata_Help[] =
+ R"(Metadata Collection
+
+ Metadata is information attached to targets throughout the dependency tree. GN
+ allows for the collection of this data into files written during the generation
+ step, enabling users to expose and aggregate this data based on the dependency
+ tree.
+
+generated_file targets
+
+ Similar to the write_file() function, the generated_file target type
+ creates a file in the specified location with the specified content. The
+ primary difference between write_file() and this target type is that the
+ write_file function does the file write at parse time, while the
+ generated_file target type writes at target resolution time. See
+ "gn help generated_file" for more detail.
+
+ When written at target resolution time, generated_file enables GN to
+ collect and write aggregated metadata from dependents.
+
+ A generated_file target can declare either 'contents' to write statically
+ known contents to a file or 'data_keys' to aggregate metadata and write the
+ result to a file. It can also specify 'walk_keys' (to restrict the metadata
+ collection), 'output_conversion', and 'rebase'.
+
+
+Collection and Aggregation
+
+ Targets can declare a 'metadata' variable containing a scope, and this
+ metadata may be collected and written out to a file specified by
+ generated_file aggregation targets. The 'metadata' scope must contain
+ only list values since the aggregation step collects a list of these values.
+
+ During the target resolution, generated_file targets will walk their
+ dependencies recursively, collecting metadata based on the specified
+ 'data_keys'. 'data_keys' is specified as a list of strings, used by the walk
+ to identify which variables in dependencies' 'metadata' scopes to collect.
+
+ The walk begins with the listed dependencies of the 'generated_file' target.
+ The 'metadata' scope for each dependency is inspected for matching elements
+ of the 'generated_file' target's 'data_keys' list. If a match is found, the
+ data from the dependent's matching key list is appended to the aggregate walk
+ list. Note that this means that if more than one walk key is specified, the
+ data in all of them will be aggregated into one list. From there, the walk
+ will then recurse into the dependencies of each target it encounters,
+ collecting the specified metadata for each.
+
+ For example:
+
+ group("a") {
+ metadata = {
+ doom_melon = [ "enable" ]
+ my_files = [ "foo.cpp" ]
+ my_extra_files = [ "bar.cpp" ]
+ }
+
+ deps = [ ":b" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "baz.cpp" ]
+ }
+ }
+
+ generated_file("metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files", "my_extra_files" ]
+
+ deps = [ ":a" ]
+ }
+
+ The above will produce the following file data:
+
+ foo.cpp
+ bar.cpp
+ baz.cpp
+
+ The dependency walk can be limited by using the 'walk_keys'. This is a list of
+ labels that should be included in the walk. All labels specified here should
+ also be in one of the deps lists. These keys act as barriers, where the walk
+ will only recurse into the targets listed. An empty list in all specified
+ barriers will end that portion of the walk.
+
+ group("a") {
+ metadata = {
+ my_files = [ "foo.cpp" ]
+ my_files_barrier = [ ":b" ]
+ }
+
+ deps = [ ":b", ":c" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "bar.cpp" ]
+ }
+ }
+
+ group("c") {
+ metadata = {
+ my_files = [ "doom_melon.cpp" ]
+ }
+ }
+
+ generated_file("metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_files_barrier" ]
+
+ deps = [ ":a" ]
+ }
+
+ The above will produce the following file data (note that `doom_melon.cpp` is
+ not included):
+
+ foo.cpp
+ bar.cpp
+
+ A common example of this sort of barrier is in builds that have host tools
+ built as part of the tree, but do not want the metadata from those host tools
+ to be collected with the target-side code.
+
+Common Uses
+
+ Metadata can be used to collect information about the different targets in the
+ build, and so a common use is to provide post-build tooling with a set of data
+ necessary to do aggregation tasks. For example, if each test target specifies
+ the output location of its binary to run in a metadata field, that can be
+ collected into a single file listing the locations of all tests in the
+ dependency tree. A local build tool (or continuous integration infrastructure)
+ can then use that file to know which tests exist, and where, and run them
+ accordingly.
+
+ Another use is in image creation, where a post-build image tool needs to know
+ various pieces of information about the components it should include in order
+ to put together the correct image.
+)";
+
+bool Metadata::WalkStep(const BuildSettings* settings,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::vector<Value>* next_walk_keys,
+ std::vector<Value>* result,
+ Err* err) const {
+ // If there's no metadata, there's nothing to find, so quick exit.
+ if (contents_.empty()) {
+ next_walk_keys->emplace_back(nullptr, "");
+ return true;
+ }
+
+ // Pull the data from each specified key.
+ for (const auto& key : keys_to_extract) {
+ auto iter = contents_.find(key);
+ if (iter == contents_.end())
+ continue;
+ assert(iter->second.type() == Value::LIST);
+
+ if (!rebase_dir.is_null()) {
+ for (const auto& val : iter->second.list_value()) {
+ std::pair<Value, bool> pair =
+ this->RebaseValue(settings, rebase_dir, val, err);
+ if (!pair.second)
+ return false;
+ result->push_back(pair.first);
+ }
+ } else {
+ result->insert(result->end(), iter->second.list_value().begin(),
+ iter->second.list_value().end());
+ }
+ }
+
+ // Get the targets to look at next. If no keys_to_walk are present, we push
+ // the empty string to the list so that the target knows to include its deps
+ // and data_deps. The values used here must be lists of strings.
+ bool found_walk_key = false;
+ for (const auto& key : keys_to_walk) {
+ auto iter = contents_.find(key);
+ if (iter != contents_.end()) {
+ found_walk_key = true;
+ assert(iter->second.type() == Value::LIST);
+ for (const auto& val : iter->second.list_value()) {
+ if (!val.VerifyTypeIs(Value::STRING, err))
+ return false;
+ next_walk_keys->emplace_back(val);
+ }
+ }
+ }
+
+ if (!found_walk_key)
+ next_walk_keys->emplace_back(nullptr, "");
+
+ return true;
+}
+
+std::pair<Value, bool> Metadata::RebaseValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ switch (value.type()) {
+ case Value::STRING:
+ return this->RebaseStringValue(settings, rebase_dir, value, err);
+ case Value::LIST:
+ return this->RebaseListValue(settings, rebase_dir, value, err);
+ case Value::SCOPE:
+ return this->RebaseScopeValue(settings, rebase_dir, value, err);
+ default:
+ return std::make_pair(value, true);
+ }
+}
+
+std::pair<Value, bool> Metadata::RebaseStringValue(
+ const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ if (!value.VerifyTypeIs(Value::STRING, err))
+ return std::make_pair(value, false);
+ std::string filename = source_dir_.ResolveRelativeAs(
+ /*as_file = */ true, value, err, settings->root_path_utf8());
+ if (err->has_error())
+ return std::make_pair(value, false);
+ Value rebased_value(value.origin(), RebasePath(filename, rebase_dir,
+ settings->root_path_utf8()));
+ return std::make_pair(rebased_value, true);
+}
+
+std::pair<Value, bool> Metadata::RebaseListValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return std::make_pair(value, false);
+
+ Value rebased_list_value(value.origin(), Value::LIST);
+ for (auto& val : value.list_value()) {
+ std::pair<Value, bool> pair = RebaseValue(settings, rebase_dir, val, err);
+ if (!pair.second)
+ return std::make_pair(value, false);
+ rebased_list_value.list_value().push_back(pair.first);
+ }
+ return std::make_pair(rebased_list_value, true);
+}
+
+std::pair<Value, bool> Metadata::RebaseScopeValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ if (!value.VerifyTypeIs(Value::SCOPE, err))
+ return std::make_pair(value, false);
+ Value rebased_scope_value(value);
+ Scope::KeyValueMap scope_values;
+ value.scope_value()->GetCurrentScopeValues(&scope_values);
+ for (auto& value_pair : scope_values) {
+ std::pair<Value, bool> pair =
+ RebaseValue(settings, rebase_dir, value_pair.second, err);
+ if (!pair.second)
+ return std::make_pair(value, false);
+
+ rebased_scope_value.scope_value()->SetValue(value_pair.first, pair.first,
+ value.origin());
+ }
+ return std::make_pair(rebased_scope_value, true);
+}
diff --git a/gn/src/gn/metadata.h b/gn/src/gn/metadata.h
new file mode 100644
index 00000000000..d8298e8b933
--- /dev/null
+++ b/gn/src/gn/metadata.h
@@ -0,0 +1,90 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_METADATA_H_
+#define TOOLS_GN_METADATA_H_
+
+#include <memory>
+
+#include "gn/build_settings.h"
+#include "gn/scope.h"
+#include "gn/source_dir.h"
+
+extern const char kMetadata_Help[];
+
+// Metadata about a particular target.
+//
+// Metadata is a collection of keys and values relating to a particular target.
+// Generally, these keys will include three categories of strings: ordinary
+// strings, filenames intended to be rebased according to their particular
+// source directory, and target labels intended to be used as barriers to the
+// walk. Verification of these categories occurs at walk time, not creation
+// time (since it is not clear until the walk which values are intended for
+// which purpose).
+//
+// Represented as a scope in the expression language, here it is reduced to just
+// the KeyValueMap (since it doesn't need the logical overhead of a full scope).
+// Values must be lists of strings, as the walking collection logic contatenates
+// their values across targets.
+class Metadata {
+ public:
+ using Contents = Scope::KeyValueMap;
+
+ Metadata() = default;
+
+ const ParseNode* origin() const { return origin_; }
+ void set_origin(const ParseNode* origin) { origin_ = origin; }
+
+ // The contents of this metadata varaiable.
+ const Contents& contents() const { return contents_; }
+ Contents& contents() { return contents_; }
+ void set_contents(Contents&& contents) { contents_ = std::move(contents); }
+
+ // The relative source directory to use when rebasing.
+ const SourceDir& source_dir() const { return source_dir_; }
+ SourceDir& source_dir() { return source_dir_; }
+ void set_source_dir(const SourceDir& d) { source_dir_ = d; }
+
+ // Collect the specified metadata from this instance.
+ //
+ // Calling this will populate `next_walk_keys` with the values of targets to
+ // be walked next (with the empty string "" indicating that the target should
+ // walk all of its deps and data_deps).
+ bool WalkStep(const BuildSettings* settings,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::vector<Value>* next_walk_keys,
+ std::vector<Value>* result,
+ Err* err) const;
+
+ private:
+ const ParseNode* origin_ = nullptr;
+ Contents contents_;
+ SourceDir source_dir_;
+
+ std::pair<Value, bool> RebaseValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ std::pair<Value, bool> RebaseStringValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ std::pair<Value, bool> RebaseListValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ std::pair<Value, bool> RebaseScopeValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ DISALLOW_COPY_AND_ASSIGN(Metadata);
+};
+
+#endif // TOOLS_GN_METADATA_H_
diff --git a/gn/src/gn/metadata_unittest.cc b/gn/src/gn/metadata_unittest.cc
new file mode 100644
index 00000000000..df57cd511cc
--- /dev/null
+++ b/gn/src/gn/metadata_unittest.cc
@@ -0,0 +1,233 @@
+// Copyright 2018 The Chromium 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 "gn/metadata.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(MetadataTest, SetContents) {
+ Metadata metadata;
+
+ ASSERT_TRUE(metadata.contents().empty());
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+
+ Metadata::Contents contents;
+ contents.insert(std::pair<std::string_view, Value>("a", a_expected));
+ contents.insert(std::pair<std::string_view, Value>("b", b_expected));
+
+ metadata.set_contents(std::move(contents));
+
+ ASSERT_EQ(metadata.contents().size(), 2);
+ auto a_actual = metadata.contents().find("a");
+ auto b_actual = metadata.contents().find("b");
+ ASSERT_FALSE(a_actual == metadata.contents().end());
+ ASSERT_FALSE(b_actual == metadata.contents().end());
+ ASSERT_EQ(a_actual->second, a_expected);
+ ASSERT_EQ(b_actual->second, b_expected);
+}
+
+TEST(MetadataTest, Walk) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
+ a_expected.list_value().push_back(Value(nullptr, "bar.h"));
+
+ metadata.contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.emplace_back("a");
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected;
+ expected.emplace_back(Value(nullptr, "foo.cpp"));
+ expected.emplace_back(Value(nullptr, "bar.h"));
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_EQ(results, expected);
+}
+
+TEST(MetadataTest, WalkWithRebase) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
+ a_expected.list_value().push_back(Value(nullptr, "foo/bar.h"));
+
+ metadata.contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.emplace_back("a");
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected;
+ expected.emplace_back(Value(nullptr, "../home/files/foo.cpp"));
+ expected.emplace_back(Value(nullptr, "../home/files/foo/bar.h"));
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir("/usr/foo_dir/"),
+ &next_walk_keys, &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_EQ(results, expected);
+}
+
+TEST(MetadataTest, WalkWithRebaseNonString) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a(nullptr, Value::LIST);
+ Value inner_list(nullptr, Value::LIST);
+ Value inner_scope(nullptr, Value::SCOPE);
+ inner_list.list_value().push_back(Value(nullptr, "foo.cpp"));
+ inner_list.list_value().push_back(Value(nullptr, "foo/bar.h"));
+ a.list_value().push_back(inner_list);
+
+ std::unique_ptr<Scope> scope(new Scope(setup.settings()));
+ scope->SetValue("a1", Value(nullptr, "foo2.cpp"), nullptr);
+ scope->SetValue("a2", Value(nullptr, "foo/bar2.h"), nullptr);
+ inner_scope.SetScopeValue(std::move(scope));
+ a.list_value().push_back(inner_scope);
+
+ metadata.contents().insert(std::pair<std::string_view, Value>("a", a));
+ std::vector<std::string> data_keys;
+ data_keys.emplace_back("a");
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected;
+ Value inner_list_expected(nullptr, Value::LIST);
+ Value inner_scope_expected(nullptr, Value::SCOPE);
+ inner_list_expected.list_value().push_back(
+ Value(nullptr, "../home/files/foo.cpp"));
+ inner_list_expected.list_value().push_back(
+ Value(nullptr, "../home/files/foo/bar.h"));
+ expected.push_back(inner_list_expected);
+
+ std::unique_ptr<Scope> scope_expected(new Scope(setup.settings()));
+ scope_expected->SetValue("a1", Value(nullptr, "../home/files/foo2.cpp"),
+ nullptr);
+ scope_expected->SetValue("a2", Value(nullptr, "../home/files/foo/bar2.h"),
+ nullptr);
+ inner_scope_expected.SetScopeValue(std::move(scope_expected));
+ expected.push_back(inner_scope_expected);
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir("/usr/foo_dir/"),
+ &next_walk_keys, &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_EQ(results, expected);
+}
+
+TEST(MetadataTest, WalkKeysToWalk) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "//target"));
+
+ metadata.contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ std::vector<std::string> walk_keys;
+ walk_keys.emplace_back("a");
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "//target");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_TRUE(results.empty());
+}
+
+TEST(MetadataTest, WalkNoContents) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ std::vector<std::string> data_keys;
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_TRUE(results.empty());
+}
+
+TEST(MetadataTest, WalkNoKeysWithContents) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "//target"));
+
+ metadata.contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_TRUE(results.empty());
+}
diff --git a/gn/src/gn/metadata_walk.cc b/gn/src/gn/metadata_walk.cc
new file mode 100644
index 00000000000..19011cfeb4a
--- /dev/null
+++ b/gn/src/gn/metadata_walk.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium 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 "gn/metadata_walk.h"
+
+std::vector<Value> WalkMetadata(
+ const UniqueVector<const Target*>& targets_to_walk,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::set<const Target*>* targets_walked,
+ Err* err) {
+ std::vector<Value> result;
+ for (const auto* target : targets_to_walk) {
+ auto pair = targets_walked->insert(target);
+ if (pair.second) {
+ if (!target->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir, false,
+ &result, targets_walked, err))
+ return std::vector<Value>();
+ }
+ }
+ return result;
+} \ No newline at end of file
diff --git a/gn/src/gn/metadata_walk.h b/gn/src/gn/metadata_walk.h
new file mode 100644
index 00000000000..011ed0747b1
--- /dev/null
+++ b/gn/src/gn/metadata_walk.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_METADATAWALK_H_
+#define TOOLS_GN_METADATAWALK_H_
+
+#include "gn/build_settings.h"
+#include "gn/target.h"
+#include "gn/unique_vector.h"
+#include "gn/value.h"
+
+// Function to collect metadata from resolved targets listed in targets_walked.
+// Intended to be called after all targets are resolved.
+//
+// This populates targets_walked with all targets touched by this walk, and
+// returns the list of metadata values.
+std::vector<Value> WalkMetadata(
+ const UniqueVector<const Target*>& targets_to_walk,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::set<const Target*>* targets_walked,
+ Err* err);
+
+#endif // TOOLS_GN_METADATAWALK_H_
diff --git a/gn/src/gn/metadata_walk_unittest.cc b/gn/src/gn/metadata_walk_unittest.cc
new file mode 100644
index 00000000000..4fcb31d7373
--- /dev/null
+++ b/gn/src/gn/metadata_walk_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2018 The Chromium 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 "gn/metadata_walk.h"
+
+#include "gn/metadata.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "gn/unique_vector.h"
+#include "util/test/test.h"
+
+TEST(MetadataWalkTest, CollectNoRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("b", b_expected));
+
+ one.metadata().set_source_dir(SourceDir("/usr/home/files/"));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_2_expected));
+
+ Value b_2_expected(nullptr, Value::LIST);
+ b_2_expected.list_value().push_back(Value(nullptr, false));
+ two.metadata().contents().insert(
+ std::pair<std::string_view, Value>("b", b_2_expected));
+
+ two.metadata().set_source_dir(SourceDir("/usr/home/files/inner"));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+ targets.push_back(&two);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, false));
+ EXPECT_EQ(result, expected);
+
+ std::set<const Target*> expected_walked_targets;
+ expected_walked_targets.insert(&one);
+ expected_walked_targets.insert(&two);
+ EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("b", b_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_2_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ EXPECT_EQ(result, expected);
+
+ std::set<const Target*> expected_walked_targets;
+ expected_walked_targets.insert(&one);
+ expected_walked_targets.insert(&two);
+ EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithBarrier) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(
+ Value(nullptr, "//foo:two(//toolchain:default)"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("walk", walk_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_2_expected));
+
+ TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
+ Value a_3_expected(nullptr, Value::LIST);
+ a_3_expected.list_value().push_back(Value(nullptr, "baz"));
+ three.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_3_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+ one.public_deps().push_back(LabelTargetPair(&three));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_FALSE(err.has_error()) << err.message();
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ EXPECT_EQ(result, expected) << result.size();
+
+ std::set<const Target*> expected_walked_targets;
+ expected_walked_targets.insert(&one);
+ expected_walked_targets.insert(&two);
+ EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithError) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("walk", walk_expected));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_TRUE(result.empty());
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.message(),
+ "I was expecting //foo:missing(//toolchain:default) to be a "
+ "dependency of //foo:one(//toolchain:default). "
+ "Make sure it's included in the deps or data_deps, and that you've "
+ "specified the appropriate toolchain.")
+ << err.message();
+}
diff --git a/gn/src/gn/ninja_action_target_writer.cc b/gn/src/gn/ninja_action_target_writer.cc
new file mode 100644
index 00000000000..c48da06cf6e
--- /dev/null
+++ b/gn/src/gn/ninja_action_target_writer.cc
@@ -0,0 +1,250 @@
+// 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 "gn/ninja_action_target_writer.h"
+
+#include <stddef.h>
+
+#include "base/strings/string_util.h"
+#include "gn/deps_iterator.h"
+#include "gn/err.h"
+#include "gn/general_tool.h"
+#include "gn/pool.h"
+#include "gn/settings.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+
+NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out),
+ path_output_no_escaping_(
+ target->settings()->build_settings()->build_dir(),
+ target->settings()->build_settings()->root_path_utf8(),
+ ESCAPE_NONE) {}
+
+NinjaActionTargetWriter::~NinjaActionTargetWriter() = default;
+
+void NinjaActionTargetWriter::Run() {
+ std::string custom_rule_name = WriteRuleDefinition();
+
+ // Collect our deps to pass as "extra hard dependencies" for input deps. This
+ // will force all of the action's dependencies to be completed before the
+ // action is run. Usually, if an action has a dependency, it will be
+ // operating on the result of that previous step, so we need to be sure to
+ // serialize these.
+ std::vector<const Target*> extra_hard_deps;
+ std::vector<OutputFile> data_outs;
+ for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
+ if (pair.ptr->IsDataOnly()) {
+ data_outs.push_back(pair.ptr->dependency_output_file());
+ } else {
+ extra_hard_deps.push_back(pair.ptr);
+ }
+ }
+
+ // For ACTIONs, the input deps appear only once in the generated ninja
+ // file, so WriteInputDepsStampAndGetDep() won't create a stamp file
+ // and the action will just depend on all the input deps directly.
+ size_t num_stamp_uses =
+ target_->output_type() == Target::ACTION ? 1u : target_->sources().size();
+ std::vector<OutputFile> input_deps =
+ WriteInputDepsStampAndGetDep(extra_hard_deps, num_stamp_uses);
+ out_ << std::endl;
+
+ // Collects all output files for writing below.
+ std::vector<OutputFile> output_files;
+
+ if (target_->output_type() == Target::ACTION_FOREACH) {
+ // Write separate build lines for each input source file.
+ WriteSourceRules(custom_rule_name, input_deps, &output_files);
+ } else {
+ DCHECK(target_->output_type() == Target::ACTION);
+
+ // Write a rule that invokes the script once with the outputs as outputs,
+ // and the data as inputs. It does not depend on the sources.
+ out_ << "build";
+ SubstitutionWriter::GetListAsOutputFiles(
+ settings_, target_->action_values().outputs(), &output_files);
+ path_output_.WriteFiles(out_, output_files);
+
+ out_ << ": " << custom_rule_name;
+ if (!input_deps.empty()) {
+ // As in WriteSourceRules, we want to force this target to rebuild any
+ // time any of its dependencies change.
+ out_ << " |";
+ path_output_.WriteFiles(out_, input_deps);
+ }
+ out_ << std::endl;
+ if (target_->action_values().has_depfile()) {
+ WriteDepfile(SourceFile());
+ }
+ if (target_->action_values().pool().ptr) {
+ out_ << " pool = ";
+ out_ << target_->action_values().pool().ptr->GetNinjaName(
+ settings_->default_toolchain_label());
+ out_ << std::endl;
+ }
+ }
+ out_ << std::endl;
+
+ // Write the stamp, which also depends on all data deps. These are needed at
+ // runtime and should be compiled when the action is, but don't need to be
+ // done before we run the action.
+ // TODO(thakis): If the action has just a single output, make things depend
+ // on that output directly without writing a stamp file.
+ for (const auto& dep : target_->data_deps())
+ data_outs.push_back(dep.ptr->dependency_output_file());
+ WriteStampForTarget(output_files, data_outs);
+}
+
+std::string NinjaActionTargetWriter::WriteRuleDefinition() {
+ // Make a unique name for this rule.
+ //
+ // Use a unique name for the response file when there are multiple build
+ // steps so that they don't stomp on each other. When there are no sources,
+ // there will be only one invocation so we can use a simple name.
+ std::string target_label = target_->label().GetUserVisibleName(true);
+ std::string custom_rule_name(target_label);
+ base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
+ custom_rule_name.append("_rule");
+
+ const SubstitutionList& args = target_->action_values().args();
+ EscapeOptions args_escape_options;
+ args_escape_options.mode = ESCAPE_NINJA_COMMAND;
+
+ out_ << "rule " << custom_rule_name << std::endl;
+
+ if (target_->action_values().uses_rsp_file()) {
+ // Needs a response file. The unique_name part is for action_foreach so
+ // each invocation of the rule gets a different response file. This isn't
+ // strictly necessary for regular one-shot actions, but it's easier to
+ // just always define unique_name.
+ std::string rspfile = custom_rule_name;
+ if (!target_->sources().empty())
+ rspfile += ".$unique_name";
+ rspfile += ".rsp";
+ out_ << " rspfile = " << rspfile << std::endl;
+
+ // Response file contents.
+ out_ << " rspfile_content =";
+ for (const auto& arg :
+ target_->action_values().rsp_file_contents().list()) {
+ out_ << " ";
+ SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options,
+ out_);
+ }
+ out_ << std::endl;
+ }
+
+ out_ << " command = ";
+ path_output_.WriteFile(out_, settings_->build_settings()->python_path());
+ out_ << " ";
+ path_output_.WriteFile(out_, target_->action_values().script());
+ for (const auto& arg : args.list()) {
+ out_ << " ";
+ SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
+ }
+ out_ << std::endl;
+ out_ << " description = ACTION " << target_label << std::endl;
+ out_ << " restat = 1" << std::endl;
+ const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
+ if (tool && tool->pool().ptr) {
+ out_ << " pool = ";
+ out_ << tool->pool().ptr->GetNinjaName(
+ settings_->default_toolchain_label());
+ out_ << std::endl;
+ }
+
+ return custom_rule_name;
+}
+
+void NinjaActionTargetWriter::WriteSourceRules(
+ const std::string& custom_rule_name,
+ const std::vector<OutputFile>& input_deps,
+ std::vector<OutputFile>* output_files) {
+ EscapeOptions args_escape_options;
+ args_escape_options.mode = ESCAPE_NINJA_COMMAND;
+ // We're writing the substitution values, these should not be quoted since
+ // they will get pasted into the real command line.
+ args_escape_options.inhibit_quoting = true;
+
+ const Target::FileList& sources = target_->sources();
+ for (size_t i = 0; i < sources.size(); i++) {
+ out_ << "build";
+ WriteOutputFilesForBuildLine(sources[i], output_files);
+
+ out_ << ": " << custom_rule_name << " ";
+ path_output_.WriteFile(out_, sources[i]);
+ if (!input_deps.empty()) {
+ // Using "|" for the dependencies forces all implicit dependencies to be
+ // fully up to date before running the action, and will re-run this
+ // action if any input dependencies change. This is important because
+ // this action may consume the outputs of previous steps.
+ out_ << " |";
+ path_output_.WriteFiles(out_, input_deps);
+ }
+ out_ << std::endl;
+
+ // Response files require a unique name be defined.
+ if (target_->action_values().uses_rsp_file())
+ out_ << " unique_name = " << i << std::endl;
+
+ // The required types is the union of the args and response file. This
+ // might theoretically duplicate a definition if the same substitution is
+ // used in both the args and the response file. However, this should be
+ // very unusual (normally the substitutions will go in one place or the
+ // other) and the redundant assignment won't bother Ninja.
+ SubstitutionWriter::WriteNinjaVariablesForSource(
+ target_, settings_, sources[i],
+ target_->action_values().args().required_types(), args_escape_options,
+ out_);
+ SubstitutionWriter::WriteNinjaVariablesForSource(
+ target_, settings_, sources[i],
+ target_->action_values().rsp_file_contents().required_types(),
+ args_escape_options, out_);
+
+ if (target_->action_values().has_depfile()) {
+ WriteDepfile(sources[i]);
+ }
+ if (target_->action_values().pool().ptr) {
+ out_ << " pool = ";
+ out_ << target_->action_values().pool().ptr->GetNinjaName(
+ settings_->default_toolchain_label());
+ out_ << std::endl;
+ }
+ }
+}
+
+void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
+ const SourceFile& source,
+ std::vector<OutputFile>* output_files) {
+ size_t first_output_index = output_files->size();
+
+ SubstitutionWriter::ApplyListToSourceAsOutputFile(
+ target_, settings_, target_->action_values().outputs(), source,
+ output_files);
+
+ for (size_t i = first_output_index; i < output_files->size(); i++) {
+ out_ << " ";
+ path_output_.WriteFile(out_, (*output_files)[i]);
+ }
+}
+
+void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
+ out_ << " depfile = ";
+ path_output_.WriteFile(
+ out_,
+ SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
+ target_, settings_, target_->action_values().depfile(), source));
+ out_ << std::endl;
+ // Using "deps = gcc" allows Ninja to read and store the depfile content in
+ // its internal database which improves performance, especially for large
+ // depfiles. The use of this feature with depfiles that contain multiple
+ // outputs require Ninja version 1.9.0 or newer.
+ if (settings_->build_settings()->ninja_required_version() >=
+ Version{1, 9, 0}) {
+ out_ << " deps = gcc" << std::endl;
+ }
+}
diff --git a/gn/src/gn/ninja_action_target_writer.h b/gn/src/gn/ninja_action_target_writer.h
new file mode 100644
index 00000000000..eb94284dde1
--- /dev/null
+++ b/gn/src/gn/ninja_action_target_writer.h
@@ -0,0 +1,62 @@
+// 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 TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "gn/ninja_target_writer.h"
+
+class OutputFile;
+
+// Writes a .ninja file for a action target type.
+class NinjaActionTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaActionTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaActionTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter,
+ WriteOutputFilesForBuildLine);
+ FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter,
+ WriteOutputFilesForBuildLineWithDepfile);
+ FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter, WriteArgsSubstitutions);
+
+ // Writes the Ninja rule for invoking the script.
+ //
+ // Returns the name of the custom rule generated. This will be based on the
+ // target name, and will include the string "$unique_name" if there are
+ // multiple inputs.
+ std::string WriteRuleDefinition();
+
+ // Writes the rules for compiling each source, writing all output files
+ // to the given vector.
+ //
+ // input_deps are the dependencies common to all build steps.
+ void WriteSourceRules(const std::string& custom_rule_name,
+ const std::vector<OutputFile>& input_deps,
+ std::vector<OutputFile>* output_files);
+
+ // Writes the output files generated by the output template for the given
+ // source file. This will start with a space and will not include a newline.
+ // Appends the output files to the given vector.
+ void WriteOutputFilesForBuildLine(const SourceFile& source,
+ std::vector<OutputFile>* output_files);
+
+ void WriteDepfile(const SourceFile& source);
+
+ // Path output writer that doesn't do any escaping or quoting. It does,
+ // however, convert slashes. Used for
+ // computing intermediate strings.
+ PathOutput path_output_no_escaping_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaActionTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_action_target_writer_unittest.cc b/gn/src/gn/ninja_action_target_writer_unittest.cc
new file mode 100644
index 00000000000..80b125b8f30
--- /dev/null
+++ b/gn/src/gn/ninja_action_target_writer_unittest.cc
@@ -0,0 +1,488 @@
+// 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 <algorithm>
+#include <sstream>
+
+#include "gn/ninja_action_target_writer.h"
+#include "gn/pool.h"
+#include "gn/substitution_list.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/gen/a b{{source_name_part}}.h",
+ "//out/Debug/gen/{{source_name_part}}.cc");
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+
+ SourceFile source("//foo/bar.in");
+ std::vector<OutputFile> output_files;
+ writer.WriteOutputFilesForBuildLine(source, &output_files);
+
+ EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out.str());
+}
+
+// Tests an action with no sources.
+TEST(NinjaActionTargetWriter, ActionNoSources) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION);
+
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+ target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
+
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char* expected = R"(rule __foo_bar___rule
+ command = /usr/bin/python ../../foo/script.py
+ description = ACTION //foo:bar()
+ restat = 1
+
+build foo.out: __foo_bar___rule | ../../foo/script.py ../../foo/included.txt
+
+build obj/foo/bar.stamp: stamp foo.out
+)";
+ EXPECT_EQ(expected, out.str()) << expected << "--" << out.str();
+}
+
+// Tests an action with no sources and pool
+TEST(NinjaActionTargetWriter, ActionNoSourcesConsole) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION);
+
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+ target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
+
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+
+ Pool pool(setup.settings(),
+ Label(SourceDir("//"), "console", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ pool.set_depth(1);
+ target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ // The console pool's name must be mapped exactly to the string "console"
+ // which is a special pre-defined pool name in ninja.
+ const char* expected = R"(rule __foo_bar___rule
+ command = /usr/bin/python ../../foo/script.py
+ description = ACTION //foo:bar()
+ restat = 1
+
+build foo.out: __foo_bar___rule | ../../foo/script.py ../../foo/included.txt
+ pool = console
+
+build obj/foo/bar.stamp: stamp foo.out
+)";
+ EXPECT_EQ(expected, out.str());
+}
+
+// Makes sure that we write sources as input dependencies for actions with
+// both sources and inputs (ACTION_FOREACH treats the sources differently).
+TEST(NinjaActionTargetWriter, ActionWithSources) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION);
+
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.sources().push_back(SourceFile("//foo/source.txt"));
+ target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
+
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __foo_bar___rule\n"
+ " command = /usr/bin/python ../../foo/script.py\n"
+ " description = ACTION //foo:bar()\n"
+ " restat = 1\n"
+ "\n"
+ "build foo.out: __foo_bar___rule | ../../foo/script.py "
+ "../../foo/included.txt ../../foo/source.txt\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp foo.out\n";
+ EXPECT_EQ(expected_linux, out.str());
+}
+
+TEST(NinjaActionTargetWriter, ForEach) {
+ Err err;
+ TestWithScope setup;
+
+ // Some dependencies that the action can depend on. Use actions for these
+ // so they have a nice platform-independent stamp file that can appear in the
+ // output (rather than having to worry about how the current platform names
+ // binaries).
+ Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
+ dep.set_output_type(Target::ACTION);
+ dep.visibility().SetPublic();
+ dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target bundle_data_dep(setup.settings(),
+ Label(SourceDir("//foo/"), "bundle_data_dep"));
+ bundle_data_dep.sources().push_back(SourceFile("//foo/some_data.txt"));
+ bundle_data_dep.set_output_type(Target::BUNDLE_DATA);
+ bundle_data_dep.visibility().SetPublic();
+ bundle_data_dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(bundle_data_dep.OnResolved(&err));
+
+ Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
+ datadep.set_output_type(Target::ACTION);
+ datadep.visibility().SetPublic();
+ datadep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+ target.private_deps().push_back(LabelTargetPair(&dep));
+ target.private_deps().push_back(LabelTargetPair(&bundle_data_dep));
+ target.data_deps().push_back(LabelTargetPair(&datadep));
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.sources().push_back(SourceFile("//foo/input2.txt"));
+
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.action_values().args() = SubstitutionList::MakeForTest(
+ "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __foo_bar___rule\n"
+ " command = /usr/bin/python ../../foo/script.py -i ${in} "
+// Escaping is different between Windows and Posix.
+#if defined(OS_WIN)
+ "\"--out=foo$ bar${source_name_part}.o\"\n"
+#else
+ "--out=foo\\$ bar${source_name_part}.o\n"
+#endif
+ " description = ACTION //foo:bar()\n"
+ " restat = 1\n"
+ "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
+ "../../foo/included.txt obj/foo/dep.stamp\n"
+ "\n"
+ "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
+ "obj/foo/bar.inputdeps.stamp\n"
+ " source_name_part = input1\n"
+ "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
+ "obj/foo/bar.inputdeps.stamp\n"
+ " source_name_part = input2\n"
+ "\n"
+ "build obj/foo/bar.stamp: "
+ "stamp input1.out input2.out || obj/foo/bundle_data_dep.stamp "
+ "obj/foo/datadep.stamp\n";
+
+ std::string out_str = out.str();
+#if defined(OS_WIN)
+ std::replace(out_str.begin(), out_str.end(), '\\', '/');
+#endif
+ EXPECT_EQ(expected_linux, out_str);
+}
+
+TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.sources().push_back(SourceFile("//foo/input2.txt"));
+
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ SubstitutionPattern depfile;
+ ASSERT_TRUE(
+ depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", nullptr, &err));
+ target.action_values().set_depfile(depfile);
+
+ target.action_values().args() = SubstitutionList::MakeForTest(
+ "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+ setup.build_settings()->set_ninja_required_version(Version{1, 9, 0});
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __foo_bar___rule\n"
+ " command = /usr/bin/python ../../foo/script.py -i ${in} "
+#if defined(OS_WIN)
+ "\"--out=foo$ bar${source_name_part}.o\"\n"
+#else
+ "--out=foo\\$ bar${source_name_part}.o\n"
+#endif
+ " description = ACTION //foo:bar()\n"
+ " restat = 1\n"
+ "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
+ "../../foo/included.txt\n"
+ "\n"
+ "build input1.out: __foo_bar___rule ../../foo/input1.txt"
+ " | obj/foo/bar.inputdeps.stamp\n"
+ " source_name_part = input1\n"
+ " depfile = gen/input1.d\n"
+ " deps = gcc\n"
+ "build input2.out: __foo_bar___rule ../../foo/input2.txt"
+ " | obj/foo/bar.inputdeps.stamp\n"
+ " source_name_part = input2\n"
+ " depfile = gen/input2.d\n"
+ " deps = gcc\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
+ EXPECT_EQ(expected_linux, out.str());
+}
+
+TEST(NinjaActionTargetWriter, ForEachWithResponseFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Make sure we get interesting substitutions for both the args and the
+ // response file contents.
+ target.action_values().args() = SubstitutionList::MakeForTest(
+ "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
+ target.action_values().rsp_file_contents() =
+ SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __foo_bar___rule\n"
+ // This name is autogenerated from the target rule name.
+ " rspfile = __foo_bar___rule.$unique_name.rsp\n"
+ // These come from rsp_file_contents above.
+ " rspfile_content = -j ${source_name_part}\n"
+ // These come from the args.
+ " command = /usr/bin/python ../../foo/script.py ${in} "
+ "${source_file_part} ${rspfile}\n"
+ " description = ACTION //foo:bar()\n"
+ " restat = 1\n"
+ "\n"
+ "build input1.out: __foo_bar___rule ../../foo/input1.txt"
+ " | ../../foo/script.py\n"
+ // Necessary for the rspfile defined in the rule.
+ " unique_name = 0\n"
+ // Substitution for the args.
+ " source_file_part = input1.txt\n"
+ // Substitution for the rspfile contents.
+ " source_name_part = input1\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp input1.out\n";
+ EXPECT_EQ(expected_linux, out.str());
+}
+
+TEST(NinjaActionTargetWriter, ForEachWithPool) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ Pool pool(setup.settings(),
+ Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ pool.set_depth(5);
+ target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Make sure we get interesting substitutions for both the args and the
+ // response file contents.
+ target.action_values().args() =
+ SubstitutionList::MakeForTest("{{source}}", "{{source_file_part}}");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __foo_bar___rule\n"
+ // These come from the args.
+ " command = /usr/bin/python ../../foo/script.py ${in} "
+ "${source_file_part}\n"
+ " description = ACTION //foo:bar()\n"
+ " restat = 1\n"
+ "\n"
+ "build input1.out: __foo_bar___rule ../../foo/input1.txt"
+ " | ../../foo/script.py\n"
+ // Substitution for the args.
+ " source_file_part = input1.txt\n"
+ " pool = foo_pool\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp input1.out\n";
+ EXPECT_EQ(expected_linux, out.str());
+}
+
+TEST(NinjaActionTargetWriter, NoTransitiveHardDeps) {
+ Err err;
+ TestWithScope setup;
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
+ dep.set_output_type(Target::ACTION);
+ dep.visibility().SetPublic();
+ dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ foo.set_output_type(Target::ACTION);
+ foo.visibility().SetPublic();
+ foo.sources().push_back(SourceFile("//foo/input1.txt"));
+ foo.action_values().set_script(SourceFile("//foo/script.py"));
+ foo.private_deps().push_back(LabelTargetPair(&dep));
+ foo.SetToolchain(setup.toolchain());
+ foo.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+ ASSERT_TRUE(foo.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&foo, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __foo_foo___rule\n"
+ // These come from the args.
+ " command = /usr/bin/python ../../foo/script.py\n"
+ " description = ACTION //foo:foo()\n"
+ " restat = 1\n"
+ "\n"
+ "build foo.out: __foo_foo___rule | ../../foo/script.py"
+ " ../../foo/input1.txt obj/foo/dep.stamp\n"
+ "\n"
+ "build obj/foo/foo.stamp: stamp foo.out\n";
+ EXPECT_EQ(expected_linux, out.str());
+ }
+
+ Target bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ bar.set_output_type(Target::ACTION);
+ bar.sources().push_back(SourceFile("//bar/input1.txt"));
+ bar.action_values().set_script(SourceFile("//bar/script.py"));
+ bar.private_deps().push_back(LabelTargetPair(&foo));
+ bar.SetToolchain(setup.toolchain());
+ bar.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/bar.out");
+ ASSERT_TRUE(bar.OnResolved(&err)) << err.message();
+
+ {
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&bar, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "rule __bar_bar___rule\n"
+ // These come from the args.
+ " command = /usr/bin/python ../../bar/script.py\n"
+ " description = ACTION //bar:bar()\n"
+ " restat = 1\n"
+ "\n"
+ // Do not have obj/foo/dep.stamp as dependency.
+ "build bar.out: __bar_bar___rule | ../../bar/script.py"
+ " ../../bar/input1.txt obj/foo/foo.stamp\n"
+ "\n"
+ "build obj/bar/bar.stamp: stamp bar.out\n";
+ EXPECT_EQ(expected_linux, out.str());
+ }
+}
diff --git a/gn/src/gn/ninja_binary_target_writer.cc b/gn/src/gn/ninja_binary_target_writer.cc
new file mode 100644
index 00000000000..e4b06520759
--- /dev/null
+++ b/gn/src/gn/ninja_binary_target_writer.cc
@@ -0,0 +1,391 @@
+// 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 "gn/ninja_binary_target_writer.h"
+
+#include <sstream>
+
+#include "base/strings/string_util.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_c_binary_target_writer.h"
+#include "gn/ninja_rust_binary_target_writer.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/ninja_utils.h"
+#include "gn/settings.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/variables.h"
+
+namespace {
+
+// Returns the proper escape options for writing compiler and linker flags.
+EscapeOptions GetFlagOptions() {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ return opts;
+}
+
+} // namespace
+
+NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out),
+ rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
+
+NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default;
+
+void NinjaBinaryTargetWriter::Run() {
+ if (target_->source_types_used().RustSourceUsed()) {
+ NinjaRustBinaryTargetWriter writer(target_, out_);
+ writer.Run();
+ return;
+ }
+
+ NinjaCBinaryTargetWriter writer(target_, out_);
+ writer.Run();
+}
+
+std::vector<OutputFile> NinjaBinaryTargetWriter::WriteInputsStampAndGetDep(
+ size_t num_stamp_uses) const {
+ CHECK(target_->toolchain()) << "Toolchain not set on target "
+ << target_->label().GetUserVisibleName(true);
+
+ UniqueVector<const SourceFile*> inputs;
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ for (const auto& input : iter.cur().inputs()) {
+ inputs.push_back(&input);
+ }
+ }
+
+ if (inputs.size() == 0)
+ return std::vector<OutputFile>(); // No inputs
+
+ // If we only have one input, return it directly instead of writing a stamp
+ // file for it.
+ if (inputs.size() == 1) {
+ return std::vector<OutputFile>{
+ OutputFile(settings_->build_settings(), *inputs[0])};
+ }
+
+ std::vector<OutputFile> outs;
+ for (const SourceFile* source : inputs)
+ outs.push_back(OutputFile(settings_->build_settings(), *source));
+
+ // If there are multiple inputs, but the stamp file would be referenced only
+ // once, don't write it but depend on the inputs directly.
+ if (num_stamp_uses == 1u)
+ return outs;
+
+ // Make a stamp file.
+ OutputFile stamp_file =
+ GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
+ stamp_file.value().append(target_->label().name());
+ stamp_file.value().append(".inputs.stamp");
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, stamp_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolStamp;
+
+ // File inputs.
+ for (const auto* input : inputs) {
+ out_ << " ";
+ path_output_.WriteFile(out_, *input);
+ }
+
+ out_ << std::endl;
+ return {stamp_file};
+}
+
+void NinjaBinaryTargetWriter::WriteSourceSetStamp(
+ const std::vector<OutputFile>& object_files) {
+ // The stamp rule for source sets is generally not used, since targets that
+ // depend on this will reference the object files directly. However, writing
+ // this rule allows the user to type the name of the target and get a build
+ // which can be convenient for development.
+ ClassifiedDeps classified_deps = GetClassifiedDeps();
+
+ // The classifier should never put extra object files in a source sets: any
+ // source sets that we depend on should appear in our non-linkable deps
+ // instead.
+ DCHECK(classified_deps.extra_object_files.empty());
+
+ std::vector<OutputFile> order_only_deps;
+ for (auto* dep : classified_deps.non_linkable_deps)
+ order_only_deps.push_back(dep->dependency_output_file());
+
+ WriteStampForTarget(object_files, order_only_deps);
+}
+
+NinjaBinaryTargetWriter::ClassifiedDeps
+NinjaBinaryTargetWriter::GetClassifiedDeps() const {
+ ClassifiedDeps classified_deps;
+
+ // Normal public/private deps.
+ for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
+ ClassifyDependency(pair.ptr, &classified_deps);
+ }
+
+ // Inherited libraries.
+ for (auto* inherited_target : target_->inherited_libraries().GetOrdered()) {
+ ClassifyDependency(inherited_target, &classified_deps);
+ }
+
+ // Data deps.
+ for (const auto& data_dep_pair : target_->data_deps())
+ classified_deps.non_linkable_deps.push_back(data_dep_pair.ptr);
+
+ return classified_deps;
+}
+
+void NinjaBinaryTargetWriter::ClassifyDependency(
+ const Target* dep,
+ ClassifiedDeps* classified_deps) const {
+ // Only the following types of outputs have libraries linked into them:
+ // EXECUTABLE
+ // SHARED_LIBRARY
+ // _complete_ STATIC_LIBRARY
+ //
+ // Child deps of intermediate static libraries get pushed up the
+ // dependency tree until one of these is reached, and source sets
+ // don't link at all.
+ bool can_link_libs = target_->IsFinal();
+
+ if (can_link_libs && dep->swift_values().builds_module())
+ classified_deps->swiftmodule_deps.push_back(dep);
+
+ if (target_->source_types_used().RustSourceUsed() &&
+ (target_->output_type() == Target::RUST_LIBRARY ||
+ target_->output_type() == Target::STATIC_LIBRARY) &&
+ dep->IsLinkable()) {
+ // Rust libraries and static libraries aren't final, but need to have the
+ // link lines of all transitive deps specified.
+ classified_deps->linkable_deps.push_back(dep);
+ } else if (dep->output_type() == Target::SOURCE_SET ||
+ // If a complete static library depends on an incomplete static
+ // library, manually link in the object files of the dependent
+ // library as if it were a source set. This avoids problems with
+ // braindead tools such as ar which don't properly link dependent
+ // static libraries.
+ (target_->complete_static_lib() &&
+ (dep->output_type() == Target::STATIC_LIBRARY &&
+ !dep->complete_static_lib()))) {
+ // Source sets have their object files linked into final targets
+ // (shared libraries, executables, loadable modules, and complete static
+ // libraries). Intermediate static libraries and other source sets
+ // just forward the dependency, otherwise the files in the source
+ // set can easily get linked more than once which will cause
+ // multiple definition errors.
+ if (can_link_libs)
+ AddSourceSetFiles(dep, &classified_deps->extra_object_files);
+
+ // Add the source set itself as a non-linkable dependency on the current
+ // target. This will make sure that anything the source set's stamp file
+ // depends on (like data deps) are also built before the current target
+ // can be complete. Otherwise, these will be skipped since this target
+ // will depend only on the source set's object files.
+ classified_deps->non_linkable_deps.push_back(dep);
+ } else if (target_->complete_static_lib() && dep->IsFinal()) {
+ classified_deps->non_linkable_deps.push_back(dep);
+ } else if (can_link_libs && dep->IsLinkable()) {
+ classified_deps->linkable_deps.push_back(dep);
+ } else if (dep->output_type() == Target::CREATE_BUNDLE &&
+ dep->bundle_data().is_framework()) {
+ classified_deps->framework_deps.push_back(dep);
+ } else {
+ classified_deps->non_linkable_deps.push_back(dep);
+ }
+}
+
+void NinjaBinaryTargetWriter::AddSourceSetFiles(
+ const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) const {
+ std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
+
+ // Compute object files for all sources. Only link the first output from
+ // the tool if there are more than one.
+ for (const auto& source : source_set->sources()) {
+ const char* tool_name = Tool::kToolNone;
+ if (source_set->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
+ obj_files->push_back(tool_outputs[0]);
+ }
+
+ // Swift files may generate one object file per module or one per source file
+ // depending on how the compiler is invoked (whole module optimization).
+ if (source_set->source_types_used().SwiftSourceUsed()) {
+ const Tool* tool = source_set->toolchain()->GetToolForSourceTypeAsC(
+ SourceFile::SOURCE_SWIFT);
+
+ std::vector<OutputFile> outputs;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ source_set, tool, tool->outputs(), &outputs);
+
+ for (const OutputFile& output : outputs) {
+ SourceFile output_as_source =
+ output.AsSourceFile(source_set->settings()->build_settings());
+ if (output_as_source.type() == SourceFile::SOURCE_O) {
+ obj_files->push_back(output);
+ }
+ }
+ }
+
+ // Add MSVC precompiled header object files. GCC .gch files are not object
+ // files so they are omitted.
+ if (source_set->config_values().has_precompiled_headers()) {
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_C)) {
+ const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCc);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolCc, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_CPP)) {
+ const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCxx);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolCxx, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_M)) {
+ const CTool* tool =
+ source_set->toolchain()->GetToolAsC(CTool::kCToolObjC);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolObjC, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_MM)) {
+ const CTool* tool =
+ source_set->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolObjCxx, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ }
+}
+
+void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
+ const std::vector<SourceFile>& sources,
+ const std::vector<OutputFile>& extra_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ const char* tool_name,
+ const std::vector<OutputFile>& outputs) {
+ out_ << "build";
+ path_output_.WriteFiles(out_, outputs);
+
+ out_ << ": " << rule_prefix_ << tool_name;
+ path_output_.WriteFiles(out_, sources);
+
+ if (!extra_deps.empty()) {
+ out_ << " |";
+ path_output_.WriteFiles(out_, extra_deps);
+ }
+
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+ out_ << std::endl;
+}
+
+void NinjaBinaryTargetWriter::WriteLinkerFlags(
+ std::ostream& out,
+ const Tool* tool,
+ const SourceFile* optional_def_file) {
+ if (tool->AsC()) {
+ // First the ldflags from the target and its config.
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
+ GetFlagOptions(), out);
+ }
+
+ // Followed by library search paths that have been recursively pushed
+ // through the dependency tree.
+ const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
+ if (!all_lib_dirs.empty()) {
+ // Since we're passing these on the command line to the linker and not
+ // to Ninja, we need to do shell escaping.
+ PathOutput lib_path_output(path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA_COMMAND);
+ for (size_t i = 0; i < all_lib_dirs.size(); i++) {
+ out << " " << tool->lib_dir_switch();
+ lib_path_output.WriteDir(out, all_lib_dirs[i],
+ PathOutput::DIR_NO_LAST_SLASH);
+ }
+ }
+
+ const auto& all_framework_dirs = target_->all_framework_dirs();
+ if (!all_framework_dirs.empty()) {
+ // Since we're passing these on the command line to the linker and not
+ // to Ninja, we need to do shell escaping.
+ PathOutput framework_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ for (size_t i = 0; i < all_framework_dirs.size(); i++) {
+ out << " " << tool->framework_dir_switch();
+ framework_path_output.WriteDir(out, all_framework_dirs[i],
+ PathOutput::DIR_NO_LAST_SLASH);
+ }
+ }
+
+ if (optional_def_file) {
+ out_ << " /DEF:";
+ path_output_.WriteFile(out, *optional_def_file);
+ }
+}
+
+void NinjaBinaryTargetWriter::WriteLibs(std::ostream& out, const Tool* tool) {
+ // Libraries that have been recursively pushed through the dependency tree.
+ EscapeOptions lib_escape_opts;
+ lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
+ const OrderedSet<LibFile> all_libs = target_->all_libs();
+ for (size_t i = 0; i < all_libs.size(); i++) {
+ const LibFile& lib_file = all_libs[i];
+ const std::string& lib_value = lib_file.value();
+ if (lib_file.is_source_file()) {
+ out << " " << tool->linker_arg();
+ path_output_.WriteFile(out, lib_file.source_file());
+ } else {
+ out << " " << tool->lib_switch();
+ EscapeStringToStream(out, lib_value, lib_escape_opts);
+ }
+ }
+}
+
+void NinjaBinaryTargetWriter::WriteFrameworks(std::ostream& out,
+ const Tool* tool) {
+ // Frameworks that have been recursively pushed through the dependency tree.
+ FrameworksWriter writer(tool->framework_switch());
+ const auto& all_frameworks = target_->all_frameworks();
+ for (size_t i = 0; i < all_frameworks.size(); i++) {
+ writer(all_frameworks[i], out);
+ }
+
+ FrameworksWriter weak_writer(tool->weak_framework_switch());
+ const auto& all_weak_frameworks = target_->all_weak_frameworks();
+ for (size_t i = 0; i < all_weak_frameworks.size(); i++) {
+ weak_writer(all_weak_frameworks[i], out);
+ }
+}
+
+void NinjaBinaryTargetWriter::WriteSwiftModules(
+ std::ostream& out,
+ const Tool* tool,
+ const std::vector<OutputFile>& swiftmodules) {
+ // Since we're passing these on the command line to the linker and not
+ // to Ninja, we need to do shell escaping.
+ PathOutput swiftmodule_path_output(
+ path_output_.current_dir(), settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA_COMMAND);
+
+ for (const OutputFile& swiftmodule : swiftmodules) {
+ out << " " << tool->swiftmodule_switch();
+ swiftmodule_path_output.WriteFile(out, swiftmodule);
+ }
+}
diff --git a/gn/src/gn/ninja_binary_target_writer.h b/gn/src/gn/ninja_binary_target_writer.h
new file mode 100644
index 00000000000..76a8a4eb798
--- /dev/null
+++ b/gn/src/gn/ninja_binary_target_writer.h
@@ -0,0 +1,88 @@
+// 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 TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/c_tool.h"
+#include "gn/config_values.h"
+#include "gn/ninja_target_writer.h"
+#include "gn/toolchain.h"
+#include "gn/unique_vector.h"
+
+struct EscapeOptions;
+
+// Writes a .ninja file for a binary target type (an executable, a shared
+// library, or a static library).
+class NinjaBinaryTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaBinaryTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaBinaryTargetWriter() override;
+
+ void Run() override;
+
+ protected:
+ // Structure used to return the classified deps from |GetDeps| method.
+ struct ClassifiedDeps {
+ UniqueVector<OutputFile> extra_object_files;
+ UniqueVector<const Target*> linkable_deps;
+ UniqueVector<const Target*> non_linkable_deps;
+ UniqueVector<const Target*> framework_deps;
+ UniqueVector<const Target*> swiftmodule_deps;
+ };
+
+ // Writes to the output stream a stamp rule for inputs, and
+ // returns the file to be appended to source rules that encodes the
+ // implicit dependencies for the current target.
+ // If num_stamp_uses is small, this might return all input dependencies
+ // directly, without writing a stamp file.
+ // If there are no implicit dependencies and no extra target dependencies
+ // are passed in, this returns an empty vector.
+ std::vector<OutputFile> WriteInputsStampAndGetDep(
+ size_t num_stamp_uses) const;
+
+ // Writes the stamp line for a source set. These are not linked.
+ void WriteSourceSetStamp(const std::vector<OutputFile>& object_files);
+
+ // Gets all target dependencies and classifies them, as well as accumulates
+ // object files from source sets we need to link.
+ ClassifiedDeps GetClassifiedDeps() const;
+
+ // Classifies the dependency as linkable or nonlinkable with the current
+ // target, adding it to the appropriate vector of |classified_deps|. If the
+ // dependency is a source set we should link in, the source set's object
+ // files will be appended to |classified_deps.extra_object_files|.
+ void ClassifyDependency(const Target* dep,
+ ClassifiedDeps* classified_deps) const;
+
+ OutputFile WriteStampAndGetDep(const UniqueVector<const SourceFile*>& files,
+ const std::string& stamp_ext) const;
+
+ void WriteCompilerBuildLine(const std::vector<SourceFile>& sources,
+ const std::vector<OutputFile>& extra_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ const char* tool_name,
+ const std::vector<OutputFile>& outputs);
+
+ void WriteLinkerFlags(std::ostream& out,
+ const Tool* tool,
+ const SourceFile* optional_def_file);
+ void WriteLibs(std::ostream& out, const Tool* tool);
+ void WriteFrameworks(std::ostream& out, const Tool* tool);
+ void WriteSwiftModules(std::ostream& out,
+ const Tool* tool,
+ const std::vector<OutputFile>& swiftmodules);
+
+ void AddSourceSetFiles(const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) const;
+
+ // Cached version of the prefix used for rule types for this toolchain.
+ std::string rule_prefix_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NinjaBinaryTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_binary_target_writer_unittest.cc b/gn/src/gn/ninja_binary_target_writer_unittest.cc
new file mode 100644
index 00000000000..970aa82f7d1
--- /dev/null
+++ b/gn/src/gn/ninja_binary_target_writer_unittest.cc
@@ -0,0 +1,183 @@
+// 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 "gn/ninja_binary_target_writer.h"
+
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using NinjaBinaryTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaBinaryTargetWriterTest, CSources) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ // Also test object files, which should be just passed through to the
+ // dependents to link.
+ target.sources().push_back(SourceFile("//foo/input3.o"));
+ target.sources().push_back(SourceFile("//foo/input4.obj"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.source_types_used().Set(SourceFile::SOURCE_O);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaBinaryTargetWriterTest, NoSourcesSourceSet) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaBinaryTargetWriterTest, NoSourcesStaticLib) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.visibility().SetPublic();
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "\n"
+ "build obj/foo/libbar.a: alink\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaBinaryTargetWriterTest, Inputs) {
+ Err err;
+ TestWithScope setup;
+
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/source1.cc"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input1"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input2"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.source1.o: cxx ../../foo/source1.cc | "
+ "../../foo/input1 ../../foo/input2\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.source1.o\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/source1.cc"));
+ target.sources().push_back(SourceFile("//foo/source2.cc"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input1"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input2"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.inputs.stamp: stamp "
+ "../../foo/input1 ../../foo/input2\n"
+ "build obj/foo/bar.source1.o: cxx ../../foo/source1.cc | "
+ "obj/foo/bar.inputs.stamp\n"
+ "build obj/foo/bar.source2.o: cxx ../../foo/source2.cc | "
+ "obj/foo/bar.inputs.stamp\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.source1.o "
+ "obj/foo/bar.source2.o\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
diff --git a/gn/src/gn/ninja_build_writer.cc b/gn/src/gn/ninja_build_writer.cc
new file mode 100644
index 00000000000..04e0754f973
--- /dev/null
+++ b/gn/src/gn/ninja_build_writer.cc
@@ -0,0 +1,642 @@
+// 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 "gn/ninja_build_writer.h"
+
+#include <stddef.h>
+
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <unordered_set>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/build_settings.h"
+#include "gn/builder.h"
+#include "gn/err.h"
+#include "gn/escape.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file_manager.h"
+#include "gn/loader.h"
+#include "gn/ninja_utils.h"
+#include "gn/pool.h"
+#include "gn/scheduler.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+#include "util/build_config.h"
+#include "util/exe_path.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+struct Counts {
+ Counts() : count(0), last_seen(nullptr) {}
+
+ // Number of targets of this type.
+ int count;
+
+ // The last one we encountered.
+ const Target* last_seen;
+};
+
+} // namespace
+
+base::CommandLine GetSelfInvocationCommandLine(
+ const BuildSettings* build_settings) {
+ const base::FilePath build_path =
+ build_settings->build_dir().Resolve(build_settings->root_path());
+
+ base::FilePath exe_path = GetExePath();
+ if (build_path.IsAbsolute())
+ exe_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, exe_path);
+
+ base::CommandLine cmdline(exe_path.NormalizePathSeparatorsTo('/'));
+
+ // Use "." for the directory to generate. When Ninja runs the command it
+ // will have the build directory as the current one. Coding it explicitly
+ // will cause everything to get confused if the user renames the directory.
+ cmdline.AppendArg("gen");
+ cmdline.AppendArg(".");
+
+ base::FilePath root_path = build_settings->root_path();
+ if (build_path.IsAbsolute())
+ root_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, root_path);
+
+ cmdline.AppendSwitchPath(std::string("--") + switches::kRoot,
+ root_path.NormalizePathSeparatorsTo('/'));
+ // Successful automatic invocations shouldn't print output.
+ cmdline.AppendSwitch(std::string("-") + switches::kQuiet);
+
+ EscapeOptions escape_shell;
+ escape_shell.mode = ESCAPE_NINJA_COMMAND;
+#if defined(OS_WIN)
+ // The command line code quoting varies by platform. We have one string,
+ // possibly with spaces, that we want to quote. The Windows command line
+ // quotes again, so we don't want quoting. The Posix one doesn't.
+ escape_shell.inhibit_quoting = true;
+#endif
+
+ // If both --root and --dotfile are passed, make sure the --dotfile is
+ // made relative to the build dir here.
+ base::FilePath dotfile_path = build_settings->dotfile_name();
+ if (!dotfile_path.empty()) {
+ if (build_path.IsAbsolute()) {
+ dotfile_path =
+ MakeAbsoluteFilePathRelativeIfPossible(build_path, dotfile_path);
+ }
+ cmdline.AppendSwitchPath(std::string("--") + switches::kDotfile,
+ dotfile_path.NormalizePathSeparatorsTo('/'));
+ }
+
+ const base::CommandLine& our_cmdline =
+ *base::CommandLine::ForCurrentProcess();
+ const base::CommandLine::SwitchMap& switches = our_cmdline.GetSwitches();
+ for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
+ i != switches.end(); ++i) {
+ // Only write arguments we haven't already written. Always skip "args"
+ // since those will have been written to the file and will be used
+ // implicitly in the future. Keeping --args would mean changes to the file
+ // would be ignored.
+ if (i->first != switches::kQuiet && i->first != switches::kRoot &&
+ i->first != switches::kDotfile && i->first != switches::kArgs) {
+ std::string escaped_value =
+ EscapeString(FilePathToUTF8(i->second), escape_shell, nullptr);
+ cmdline.AppendSwitchASCII(i->first, escaped_value);
+ }
+ }
+
+ // Add the regeneration switch if not already present. This is so that when
+ // the regeneration is invoked by ninja, the gen command is aware that it is a
+ // regeneration invocation and not an user invocation. This allows the gen
+ // command to elide ninja post processing steps that ninja will perform
+ // itself.
+ if (!cmdline.HasSwitch(switches::kRegeneration)) {
+ cmdline.AppendSwitch(switches::kRegeneration);
+ }
+
+ return cmdline;
+}
+
+namespace {
+
+std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
+ base::CommandLine cmdline = GetSelfInvocationCommandLine(build_settings);
+#if defined(OS_WIN)
+ return base::UTF16ToUTF8(cmdline.GetCommandLineString());
+#else
+ return cmdline.GetCommandLineString();
+#endif
+}
+
+// Given an output that appears more than once, generates an error message
+// that describes the problem and which targets generate it.
+Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets,
+ const OutputFile& bad_output) {
+ std::vector<const Target*> matches;
+ for (const Target* target : all_targets) {
+ for (const auto& output : target->computed_outputs()) {
+ if (output == bad_output) {
+ matches.push_back(target);
+ break;
+ }
+ }
+ }
+
+ // There should always be at least two targets generating this file for this
+ // function to be called in the first place.
+ DCHECK(matches.size() >= 2);
+ std::string matches_string;
+ for (const Target* target : matches)
+ matches_string += " " + target->label().GetUserVisibleName(false) + "\n";
+
+ Err result(matches[0]->defined_from(), "Duplicate output file.",
+ "Two or more targets generate the same output:\n " +
+ bad_output.value() +
+ "\n\n"
+ "This is can often be fixed by changing one of the target "
+ "names, or by \n"
+ "setting an output_name on one of them.\n"
+ "\nCollisions:\n" +
+ matches_string);
+ for (size_t i = 1; i < matches.size(); i++)
+ result.AppendSubErr(Err(matches[i]->defined_from(), "Collision."));
+ return result;
+}
+
+// Given two toolchains with the same name, generates an error message
+// that describes the problem.
+Err GetDuplicateToolchainError(const SourceFile& source_file,
+ const Toolchain* previous_toolchain,
+ const Toolchain* toolchain) {
+ Err result(
+ toolchain->defined_from(), "Duplicate toolchain.",
+ "Two or more toolchains write to the same directory:\n " +
+ source_file.GetDir().value() +
+ "\n\n"
+ "This can be fixed by making sure that distinct toolchains have\n"
+ "distinct names.\n");
+ result.AppendSubErr(
+ Err(previous_toolchain->defined_from(), "Previous toolchain."));
+ return result;
+}
+
+} // namespace
+
+NinjaBuildWriter::NinjaBuildWriter(
+ const BuildSettings* build_settings,
+ const std::unordered_map<const Settings*, const Toolchain*>&
+ used_toolchains,
+ const std::vector<const Target*>& all_targets,
+ const Toolchain* default_toolchain,
+ const std::vector<const Target*>& default_toolchain_targets,
+ std::ostream& out,
+ std::ostream& dep_out)
+ : build_settings_(build_settings),
+ used_toolchains_(used_toolchains),
+ all_targets_(all_targets),
+ default_toolchain_(default_toolchain),
+ default_toolchain_targets_(default_toolchain_targets),
+ out_(out),
+ dep_out_(dep_out),
+ path_output_(build_settings->build_dir(),
+ build_settings->root_path_utf8(),
+ ESCAPE_NINJA) {}
+
+NinjaBuildWriter::~NinjaBuildWriter() = default;
+
+bool NinjaBuildWriter::Run(Err* err) {
+ WriteNinjaRules();
+ WriteAllPools();
+ return WriteSubninjas(err) && WritePhonyAndAllRules(err);
+}
+
+// static
+bool NinjaBuildWriter::RunAndWriteFile(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err) {
+ ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja");
+
+ std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
+ std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
+
+ // Find the default toolchain info.
+ Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
+ const Settings* default_toolchain_settings =
+ builder.loader()->GetToolchainSettings(default_toolchain_label);
+ const Toolchain* default_toolchain =
+ builder.GetToolchain(default_toolchain_label);
+
+ // Most targets will be in the default toolchain. Add it at the beginning and
+ // skip adding it to the list every time in the loop.
+ used_toolchains[default_toolchain_settings] = default_toolchain;
+
+ std::vector<const Target*> default_toolchain_targets;
+ default_toolchain_targets.reserve(all_targets.size());
+ for (const Target* target : all_targets) {
+ if (target->settings() == default_toolchain_settings) {
+ default_toolchain_targets.push_back(target);
+ // The default toolchain will already have been added to the used
+ // settings array.
+ } else if (used_toolchains.find(target->settings()) ==
+ used_toolchains.end()) {
+ used_toolchains[target->settings()] =
+ builder.GetToolchain(target->settings()->toolchain_label());
+ }
+ }
+
+ std::stringstream file;
+ std::stringstream depfile;
+ NinjaBuildWriter gen(build_settings, used_toolchains, all_targets,
+ default_toolchain, default_toolchain_targets, file,
+ depfile);
+ if (!gen.Run(err))
+ return false;
+
+ // Unconditionally write the build.ninja. Ninja's build-out-of-date checking
+ // will re-run GN when any build input is newer than build.ninja, so any time
+ // the build is updated, build.ninja's timestamp needs to updated also, even
+ // if the contents haven't been changed.
+ base::FilePath ninja_file_name(build_settings->GetFullPath(
+ SourceFile(build_settings->build_dir().value() + "build.ninja")));
+ base::CreateDirectory(ninja_file_name.DirName());
+ std::string ninja_contents = file.str();
+ if (base::WriteFile(ninja_file_name, ninja_contents.data(),
+ static_cast<int>(ninja_contents.size())) !=
+ static_cast<int>(ninja_contents.size()))
+ return false;
+
+ // Dep file listing build dependencies.
+ base::FilePath dep_file_name(build_settings->GetFullPath(
+ SourceFile(build_settings->build_dir().value() + "build.ninja.d")));
+ std::string dep_contents = depfile.str();
+ if (base::WriteFile(dep_file_name, dep_contents.data(),
+ static_cast<int>(dep_contents.size())) !=
+ static_cast<int>(dep_contents.size()))
+ return false;
+
+ return true;
+}
+
+void NinjaBuildWriter::WriteNinjaRules() {
+ out_ << "ninja_required_version = "
+ << build_settings_->ninja_required_version().Describe() << "\n\n";
+ out_ << "rule gn\n";
+ out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n";
+ // Putting gn rule to console pool for colorful output on regeneration
+ out_ << " pool = console\n";
+ out_ << " description = Regenerating ninja files\n\n";
+
+ // This rule will regenerate the ninja files when any input file has changed.
+ out_ << "build build.ninja: gn\n"
+ << " generator = 1\n"
+ << " depfile = build.ninja.d\n";
+
+ // Input build files. These go in the ".d" file. If we write them as
+ // dependencies in the .ninja file itself, ninja will expect the files to
+ // exist and will error if they don't. When files are listed in a depfile,
+ // missing files are ignored.
+ dep_out_ << "build.ninja:";
+
+ // Other files read by the build.
+ std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
+
+ const InputFileManager* input_file_manager =
+ g_scheduler->input_file_manager();
+
+ VectorSetSorter<base::FilePath> sorter(
+ input_file_manager->GetInputFileCount() + other_files.size());
+
+ input_file_manager->AddAllPhysicalInputFileNamesToVectorSetSorter(&sorter);
+ sorter.Add(other_files.begin(), other_files.end());
+
+ const base::FilePath build_path =
+ build_settings_->build_dir().Resolve(build_settings_->root_path());
+
+ EscapeOptions depfile_escape;
+ depfile_escape.mode = ESCAPE_DEPFILE;
+ auto item_callback = [this, &depfile_escape,
+ &build_path](const base::FilePath& input_file) {
+ const base::FilePath file =
+ MakeAbsoluteFilePathRelativeIfPossible(build_path, input_file);
+ dep_out_ << " ";
+ EscapeStringToStream(dep_out_,
+ FilePathToUTF8(file.NormalizePathSeparatorsTo('/')),
+ depfile_escape);
+ };
+
+ sorter.IterateOver(item_callback);
+
+ out_ << std::endl;
+}
+
+void NinjaBuildWriter::WriteAllPools() {
+ // Compute the pools referenced by all tools of all used toolchains.
+ std::unordered_set<const Pool*> used_pools;
+ for (const auto& pair : used_toolchains_) {
+ for (const auto& tool : pair.second->tools()) {
+ if (tool.second->pool().ptr)
+ used_pools.insert(tool.second->pool().ptr);
+ }
+ }
+
+ for (const Target* target : all_targets_) {
+ if (target->output_type() == Target::ACTION) {
+ const LabelPtrPair<Pool>& pool = target->action_values().pool();
+ if (pool.ptr)
+ used_pools.insert(pool.ptr);
+ }
+ }
+
+ // Write pools sorted by their name, to make output deterministic.
+ std::vector<const Pool*> sorted_pools(used_pools.begin(), used_pools.end());
+ auto pool_name = [this](const Pool* pool) {
+ return pool->GetNinjaName(default_toolchain_->label());
+ };
+ std::sort(sorted_pools.begin(), sorted_pools.end(),
+ [&pool_name](const Pool* a, const Pool* b) {
+ return pool_name(a) < pool_name(b);
+ });
+ for (const Pool* pool : sorted_pools) {
+ std::string name = pool_name(pool);
+ if (name == "console")
+ continue;
+ out_ << "pool " << name << std::endl
+ << " depth = " << pool->depth() << std::endl
+ << std::endl;
+ }
+}
+
+bool NinjaBuildWriter::WriteSubninjas(Err* err) {
+ // Write toolchains sorted by their name, to make output deterministic.
+ std::vector<std::pair<const Settings*, const Toolchain*>> sorted_settings(
+ used_toolchains_.begin(), used_toolchains_.end());
+ std::sort(sorted_settings.begin(), sorted_settings.end(),
+ [this](const std::pair<const Settings*, const Toolchain*>& a,
+ const std::pair<const Settings*, const Toolchain*>& b) {
+ // Always put the default toolchain first.
+ if (b.second == default_toolchain_)
+ return false;
+ if (a.second == default_toolchain_)
+ return true;
+ return GetNinjaFileForToolchain(a.first) <
+ GetNinjaFileForToolchain(b.first);
+ });
+
+ SourceFile previous_subninja;
+ const Toolchain* previous_toolchain = nullptr;
+
+ for (const auto& pair : sorted_settings) {
+ SourceFile subninja = GetNinjaFileForToolchain(pair.first);
+
+ // Since the toolchains are sorted, comparing to the previous subninja is
+ // enough to find duplicates.
+ if (subninja == previous_subninja) {
+ *err =
+ GetDuplicateToolchainError(subninja, previous_toolchain, pair.second);
+ return false;
+ }
+
+ out_ << "subninja ";
+ path_output_.WriteFile(out_, subninja);
+ out_ << std::endl;
+ previous_subninja = subninja;
+ previous_toolchain = pair.second;
+ }
+ out_ << std::endl;
+ return true;
+}
+
+const char kNinjaRules_Help[] =
+ R"(Ninja build rules
+
+The "all" and "default" rules
+
+ All generated targets (see "gn help execution") will be added to an implicit
+ build rule called "all" so "ninja all" will always compile everything. The
+ default rule will be used by Ninja if no specific target is specified (just
+ typing "ninja"). If there is a target named "default" in the root build file,
+ it will be the default build rule, otherwise the implicit "all" rule will be
+ used.
+
+Phony rules
+
+ GN generates Ninja "phony" rules for targets in the default toolchain. The
+ phony rules can collide with each other and with the names of generated files
+ so are generated with the following priority:
+
+ 1. Actual files generated by the build always take precedence.
+
+ 2. Targets in the toplevel //BUILD.gn file.
+
+ 3. Targets in toplevel directories matching the names of the directories.
+ So "ninja foo" can be used to compile "//foo:foo". This only applies to
+ the first level of directories since usually these are the most
+ important (so this won't apply to "//foo/bar:bar").
+
+ 4. The short names of executables if there is only one executable with that
+ short name. Use "ninja doom_melon" to compile the
+ "//tools/fruit:doom_melon" executable.
+
+ 5. The short names of all targets if there is only one target with that
+ short name.
+
+ 6. Full label name with no leading slashes. So you can use
+ "ninja tools/fruit:doom_melon" to build "//tools/fruit:doom_melon".
+
+ 7. Labels with an implicit name part (when the short names match the
+ directory). So you can use "ninja foo/bar" to compile "//foo/bar:bar".
+
+ These "phony" rules are provided only for running Ninja since this matches
+ people's historical expectations for building. For consistency with the rest
+ of the program, GN introspection commands accept explicit labels.
+
+ To explicitly compile a target in a non-default toolchain, you must give
+ Ninja the exact name of the output file relative to the build directory.
+)";
+
+bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) {
+ // Track rules as we generate them so we don't accidentally write a phony
+ // rule that collides with something else.
+ // GN internally generates an "all" target, so don't duplicate it.
+ std::unordered_set<std::string> written_rules;
+ written_rules.insert("all");
+
+ // Set if we encounter a target named "//:default".
+ const Target* default_target = nullptr;
+
+ // Targets in the root build file.
+ std::vector<const Target*> toplevel_targets;
+
+ // Targets with names matching their toplevel directories. For example
+ // "//foo:foo". Expect this is the naming scheme for "big components."
+ std::vector<const Target*> toplevel_dir_targets;
+
+ // Tracks the number of each target with the given short name, as well
+ // as the short names of executables (which will be a subset of short_names).
+ std::map<std::string, Counts> short_names;
+ std::map<std::string, Counts> exes;
+
+ // ----------------------------------------------------
+ // If you change this algorithm, update the help above!
+ // ----------------------------------------------------
+
+ for (const Target* target : default_toolchain_targets_) {
+ const Label& label = target->label();
+ const std::string& short_name = label.name();
+
+ if (label.dir() == build_settings_->root_target_label().dir() &&
+ short_name == "default")
+ default_target = target;
+
+ // Count the number of targets with the given short name.
+ Counts& short_names_counts = short_names[short_name];
+ short_names_counts.count++;
+ short_names_counts.last_seen = target;
+
+ // Count executables with the given short name.
+ if (target->output_type() == Target::EXECUTABLE) {
+ Counts& exes_counts = exes[short_name];
+ exes_counts.count++;
+ exes_counts.last_seen = target;
+ }
+
+ // Find targets in "important" directories.
+ const std::string& dir_string = label.dir().value();
+ if (dir_string.size() == 2 && dir_string[0] == '/' &&
+ dir_string[1] == '/') {
+ toplevel_targets.push_back(target);
+ } else if (dir_string.size() == label.name().size() + 3 && // Size matches.
+ dir_string[0] == '/' &&
+ dir_string[1] == '/' && // "//" at beginning.
+ dir_string[dir_string.size() - 1] == '/' && // "/" at end.
+ dir_string.compare(2, label.name().size(), label.name()) == 0) {
+ toplevel_dir_targets.push_back(target);
+ }
+
+ // Add the output files from each target to the written rules so that
+ // we don't write phony rules that collide with anything generated by the
+ // build.
+ //
+ // If at this point there is a collision (no phony rules have been
+ // generated yet), two targets make the same output so throw an error.
+ for (const auto& output : target->computed_outputs()) {
+ // Need to normalize because many toolchain outputs will be preceeded
+ // with "./".
+ std::string output_string(output.value());
+ NormalizePath(&output_string);
+ if (!written_rules.insert(output_string).second) {
+ *err = GetDuplicateOutputError(default_toolchain_targets_, output);
+ return false;
+ }
+ }
+ }
+
+ // First prefer the short names of toplevel targets.
+ for (const Target* target : toplevel_targets) {
+ if (written_rules.insert(target->label().name()).second)
+ WritePhonyRule(target, target->label().name());
+ }
+
+ // Next prefer short names of toplevel dir targets.
+ for (const Target* target : toplevel_dir_targets) {
+ if (written_rules.insert(target->label().name()).second)
+ WritePhonyRule(target, target->label().name());
+ }
+
+ // Write out the names labels of executables. Many toolchains will produce
+ // executables in the root build directory with no extensions, so the names
+ // will already exist and this will be a no-op. But on Windows such programs
+ // will have extensions, and executables may override the output directory to
+ // go into some other place.
+ //
+ // Putting this after the "toplevel" rules above also means that you can
+ // steal the short name from an executable by outputting the executable to
+ // a different directory or using a different output name, and writing a
+ // toplevel build rule.
+ for (const auto& pair : exes) {
+ const Counts& counts = pair.second;
+ const std::string& short_name = counts.last_seen->label().name();
+ if (counts.count == 1 && written_rules.insert(short_name).second)
+ WritePhonyRule(counts.last_seen, short_name);
+ }
+
+ // Write short names when those names are unique and not already taken.
+ for (const auto& pair : short_names) {
+ const Counts& counts = pair.second;
+ const std::string& short_name = counts.last_seen->label().name();
+ if (counts.count == 1 && written_rules.insert(short_name).second)
+ WritePhonyRule(counts.last_seen, short_name);
+ }
+
+ // Write the label variants of the target name.
+ for (const Target* target : default_toolchain_targets_) {
+ const Label& label = target->label();
+
+ // Write the long name "foo/bar:baz" for the target "//foo/bar:baz".
+ std::string long_name = label.GetUserVisibleName(false);
+ base::TrimString(long_name, "/", &long_name);
+ if (written_rules.insert(long_name).second)
+ WritePhonyRule(target, long_name);
+
+ // Write the directory name with no target name if they match
+ // (e.g. "//foo/bar:bar" -> "foo/bar").
+ if (FindLastDirComponent(label.dir()) == label.name()) {
+ std::string medium_name = DirectoryWithNoLastSlash(label.dir());
+ base::TrimString(medium_name, "/", &medium_name);
+ // That may have generated a name the same as the short name of the
+ // target which we already wrote.
+ if (medium_name != label.name() &&
+ written_rules.insert(medium_name).second)
+ WritePhonyRule(target, medium_name);
+ }
+ }
+
+ // Write the autogenerated "all" rule.
+ if (!default_toolchain_targets_.empty()) {
+ out_ << "\nbuild all: phony";
+
+ EscapeOptions ninja_escape;
+ ninja_escape.mode = ESCAPE_NINJA;
+ for (const Target* target : default_toolchain_targets_) {
+ out_ << " $\n ";
+ path_output_.WriteFile(out_, target->dependency_output_file());
+ }
+ }
+ out_ << std::endl;
+
+ if (default_target) {
+ // Use the short name when available
+ if (written_rules.find("default") != written_rules.end()) {
+ out_ << "\ndefault default" << std::endl;
+ } else {
+ out_ << "\ndefault ";
+ path_output_.WriteFile(out_, default_target->dependency_output_file());
+ out_ << std::endl;
+ }
+ } else if (!default_toolchain_targets_.empty()) {
+ out_ << "\ndefault all" << std::endl;
+ }
+
+ return true;
+}
+
+void NinjaBuildWriter::WritePhonyRule(const Target* target,
+ const std::string& phony_name) {
+ EscapeOptions ninja_escape;
+ ninja_escape.mode = ESCAPE_NINJA;
+
+ // Escape for special chars Ninja will handle.
+ std::string escaped = EscapeString(phony_name, ninja_escape, nullptr);
+
+ out_ << "build " << escaped << ": phony ";
+ path_output_.WriteFile(out_, target->dependency_output_file());
+ out_ << std::endl;
+}
diff --git a/gn/src/gn/ninja_build_writer.h b/gn/src/gn/ninja_build_writer.h
new file mode 100644
index 00000000000..9f41a38d33c
--- /dev/null
+++ b/gn/src/gn/ninja_build_writer.h
@@ -0,0 +1,81 @@
+// 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 TOOLS_GN_NINJA_BUILD_WRITER_H_
+#define TOOLS_GN_NINJA_BUILD_WRITER_H_
+
+#include <iosfwd>
+#include <map>
+#include <unordered_map>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/path_output.h"
+
+class Builder;
+class BuildSettings;
+class Err;
+class Settings;
+class Target;
+class Toolchain;
+
+namespace base {
+class CommandLine;
+} // base
+
+// Generates the toplevel "build.ninja" file. This references the individual
+// toolchain files and lists all input .gn files as dependencies of the
+// build itself.
+class NinjaBuildWriter {
+ public:
+ NinjaBuildWriter(const BuildSettings* settings,
+ const std::unordered_map<const Settings*, const Toolchain*>&
+ used_toolchains,
+ const std::vector<const Target*>& all_targets,
+ const Toolchain* default_toolchain,
+ const std::vector<const Target*>& default_toolchain_targets,
+ std::ostream& out,
+ std::ostream& dep_out);
+ ~NinjaBuildWriter();
+
+ // The design of this class is that this static factory function takes the
+ // Builder, extracts the relevant information, and passes it to the class
+ // constructor. The class itself doesn't depend on the Builder at all which
+ // makes testing much easier (tests integrating various functions along with
+ // the Builder get very complicated).
+ static bool RunAndWriteFile(const BuildSettings* settings,
+ const Builder& builder,
+ Err* err);
+
+ bool Run(Err* err);
+
+ private:
+ void WriteNinjaRules();
+ void WriteAllPools();
+ bool WriteSubninjas(Err* err);
+ bool WritePhonyAndAllRules(Err* err);
+
+ void WritePhonyRule(const Target* target, const std::string& phony_name);
+
+ const BuildSettings* build_settings_;
+
+ const std::unordered_map<const Settings*, const Toolchain*>& used_toolchains_;
+ const std::vector<const Target*>& all_targets_;
+ const Toolchain* default_toolchain_;
+ const std::vector<const Target*>& default_toolchain_targets_;
+
+ std::ostream& out_;
+ std::ostream& dep_out_;
+ PathOutput path_output_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaBuildWriter);
+};
+
+extern const char kNinjaRules_Help[];
+
+// Exposed for testing.
+base::CommandLine GetSelfInvocationCommandLine(
+ const BuildSettings* build_settings);
+
+#endif // TOOLS_GN_NINJA_BUILD_WRITER_H_
diff --git a/gn/src/gn/ninja_build_writer_unittest.cc b/gn/src/gn/ninja_build_writer_unittest.cc
new file mode 100644
index 00000000000..8bc683910d7
--- /dev/null
+++ b/gn/src/gn/ninja_build_writer_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2016 The Chromium 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 <sstream>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "gn/ninja_build_writer.h"
+#include "gn/pool.h"
+#include "gn/scheduler.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using NinjaBuildWriterTest = TestWithScheduler;
+
+class ScopedDotGNFile {
+ public:
+ ScopedDotGNFile(const base::FilePath& path)
+ : path_(path),
+ file_(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE) {
+ EXPECT_TRUE(file_.IsValid());
+ }
+ ~ScopedDotGNFile() {
+ file_.Close();
+ base::DeleteFile(path_, false);
+ }
+
+ private:
+ base::FilePath path_;
+ base::File file_;
+};
+
+TEST_F(NinjaBuildWriterTest, GetSelfInvocationCommandLine) {
+ // TestWithScope sets up a config with a build dir of //out/Debug.
+ TestWithScope setup;
+ base::CommandLine cmd_out(base::CommandLine::NO_PROGRAM);
+
+ // Setup sets the default root dir to ".".
+ base::FilePath root(FILE_PATH_LITERAL("."));
+ base::FilePath root_realpath = base::MakeAbsoluteFilePath(root);
+
+ base::FilePath gn(FILE_PATH_LITERAL("testdot.gn"));
+
+ // The file must exist on disk for MakeAbsoluteFilePath() to work.
+ ScopedDotGNFile dot_gn(gn);
+ base::FilePath gn_realpath = base::MakeAbsoluteFilePath(gn);
+
+ // Without any parameters the self invocation should pass --root=../..
+ // (from //out/Debug to //).
+ setup.build_settings()->SetRootPath(root_realpath);
+ cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
+ EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
+ EXPECT_FALSE(cmd_out.HasSwitch(switches::kDotfile));
+
+ // If --root is . and --dotfile is foo/.gn, then --dotfile also needs
+ // to to become ../../foo/.gn.
+ setup.build_settings()->SetRootPath(root_realpath);
+ setup.build_settings()->set_dotfile_name(gn_realpath);
+ cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
+ EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
+ EXPECT_EQ("../../testdot.gn",
+ cmd_out.GetSwitchValueASCII(switches::kDotfile));
+}
+
+TEST_F(NinjaBuildWriterTest, TwoTargets) {
+ TestWithScope setup;
+ Err err;
+
+ Target target_foo(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target_foo.set_output_type(Target::ACTION);
+ target_foo.action_values().set_script(SourceFile("//foo/script.py"));
+ target_foo.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out1.out", "//out/Debug/out2.out");
+ target_foo.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target_foo.OnResolved(&err));
+
+ Target target_bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target_bar.set_output_type(Target::ACTION);
+ target_bar.action_values().set_script(SourceFile("//bar/script.py"));
+ target_bar.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out3.out", "//out/Debug/out4.out");
+ target_bar.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target_bar.OnResolved(&err));
+
+ // Make a secondary toolchain that references two pools.
+ Label other_toolchain_label(SourceDir("//other/"), "toolchain");
+ Toolchain other_toolchain(setup.settings(), other_toolchain_label);
+ TestWithScope::SetupToolchain(&other_toolchain);
+
+ Pool other_regular_pool(
+ setup.settings(),
+ Label(SourceDir("//other/"), "depth_pool", other_toolchain_label.dir(),
+ other_toolchain_label.name()));
+ other_regular_pool.set_depth(42);
+ other_toolchain.GetTool(CTool::kCToolLink)
+ ->set_pool(LabelPtrPair<Pool>(&other_regular_pool));
+
+ // Make another target that uses its own pool
+
+ Pool another_regular_pool(
+ setup.settings(),
+ Label(SourceDir("//another/"), "depth_pool", other_toolchain_label.dir(),
+ other_toolchain_label.name()));
+ another_regular_pool.set_depth(7);
+
+ Target target_baz(setup.settings(), Label(SourceDir("//baz/"), "baz"));
+ target_baz.set_output_type(Target::ACTION);
+ target_baz.action_values().set_script(SourceFile("//baz/script.py"));
+ target_baz.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out5.out", "//out/Debug/out6.out");
+ target_baz.SetToolchain(&other_toolchain);
+ target_baz.action_values().set_pool(
+ LabelPtrPair<Pool>(&another_regular_pool));
+ ASSERT_TRUE(target_baz.OnResolved(&err));
+
+ // The console pool must be in the default toolchain.
+ Pool console_pool(setup.settings(), Label(SourceDir("//"), "console",
+ setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ console_pool.set_depth(1);
+ other_toolchain.GetTool(GeneralTool::kGeneralToolStamp)
+ ->set_pool(LabelPtrPair<Pool>(&console_pool));
+
+ // Settings to go with the other toolchain.
+ Settings other_settings(setup.build_settings(), "toolchain/");
+ other_settings.set_toolchain_label(other_toolchain_label);
+
+ std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
+ used_toolchains[setup.settings()] = setup.toolchain();
+ used_toolchains[&other_settings] = &other_toolchain;
+
+ std::vector<const Target*> targets = {&target_foo, &target_bar, &target_baz};
+
+ std::ostringstream ninja_out;
+ std::ostringstream depfile_out;
+
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
+ setup.toolchain(), targets, ninja_out, depfile_out);
+ ASSERT_TRUE(writer.Run(&err));
+
+ const char expected_rule_gn[] = "rule gn\n";
+ const char expected_build_ninja[] =
+ "build build.ninja: gn\n"
+ " generator = 1\n"
+ " depfile = build.ninja.d\n";
+ const char expected_other_pool[] =
+ "pool other_toolchain_another_depth_pool\n"
+ " depth = 7\n"
+ "\n"
+ "pool other_toolchain_other_depth_pool\n"
+ " depth = 42\n";
+ const char expected_toolchain[] = "subninja toolchain.ninja\n";
+ const char expected_targets[] =
+ "build bar: phony obj/bar/bar.stamp\n"
+ "build baz: phony obj/baz/baz.stamp\n"
+ "build foo$:bar: phony obj/foo/bar.stamp\n"
+ "build bar$:bar: phony obj/bar/bar.stamp\n"
+ "build baz$:baz: phony obj/baz/baz.stamp\n";
+ const char expected_root_target[] =
+ "build all: phony $\n"
+ " obj/foo/bar.stamp $\n"
+ " obj/bar/bar.stamp $\n"
+ " obj/baz/baz.stamp\n";
+ const char expected_default[] = "default all\n";
+ std::string out_str = ninja_out.str();
+#define EXPECT_SNIPPET(expected) \
+ EXPECT_NE(std::string::npos, out_str.find(expected)) \
+ << "Expected to find: " << expected << "\n" \
+ << "Within: " << out_str
+ EXPECT_SNIPPET(expected_rule_gn);
+ EXPECT_SNIPPET(expected_build_ninja);
+ EXPECT_SNIPPET(expected_other_pool);
+ EXPECT_SNIPPET(expected_toolchain);
+ EXPECT_SNIPPET(expected_targets);
+ EXPECT_SNIPPET(expected_root_target);
+ EXPECT_SNIPPET(expected_default);
+#undef EXPECT_SNIPPET
+
+ // A pool definition for ninja's built-in console pool must not be written.
+ EXPECT_EQ(std::string::npos, out_str.find("pool console"));
+}
+
+TEST_F(NinjaBuildWriterTest, SpaceInDepfile) {
+ TestWithScope setup;
+ Err err;
+
+ // Setup sets the default root dir to ".".
+ base::FilePath root(FILE_PATH_LITERAL("."));
+ base::FilePath root_realpath = base::MakeAbsoluteFilePath(root);
+ setup.build_settings()->SetRootPath(root_realpath);
+
+ // Cannot use MakeAbsoluteFilePath for non-existed paths
+ base::FilePath dependency =
+ root_realpath.Append(FILE_PATH_LITERAL("path with space/BUILD.gn"));
+ g_scheduler->AddGenDependency(dependency);
+
+ std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
+ used_toolchains[setup.settings()] = setup.toolchain();
+ std::vector<const Target*> targets;
+ std::ostringstream ninja_out;
+ std::ostringstream depfile_out;
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
+ setup.toolchain(), targets, ninja_out, depfile_out);
+ ASSERT_TRUE(writer.Run(&err));
+
+ EXPECT_EQ(depfile_out.str(),
+ "build.ninja: ../../path\\ with\\ space/BUILD.gn");
+}
+
+TEST_F(NinjaBuildWriterTest, DuplicateOutputs) {
+ TestWithScope setup;
+ Err err;
+
+ Target target_foo(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target_foo.set_output_type(Target::ACTION);
+ target_foo.action_values().set_script(SourceFile("//foo/script.py"));
+ target_foo.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out1.out", "//out/Debug/out2.out");
+ target_foo.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target_foo.OnResolved(&err));
+
+ Target target_bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target_bar.set_output_type(Target::ACTION);
+ target_bar.action_values().set_script(SourceFile("//bar/script.py"));
+ target_bar.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out3.out", "//out/Debug/out2.out");
+ target_bar.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target_bar.OnResolved(&err));
+
+ std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
+ used_toolchains[setup.settings()] = setup.toolchain();
+ std::vector<const Target*> targets = {&target_foo, &target_bar};
+ std::ostringstream ninja_out;
+ std::ostringstream depfile_out;
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
+ setup.toolchain(), targets, ninja_out, depfile_out);
+ ASSERT_FALSE(writer.Run(&err));
+
+ const char expected_help_test[] =
+ "Two or more targets generate the same output:\n"
+ " out2.out\n"
+ "\n"
+ "This is can often be fixed by changing one of the target names, or by \n"
+ "setting an output_name on one of them.\n"
+ "\n"
+ "Collisions:\n"
+ " //foo:bar\n"
+ " //bar:bar\n";
+
+ EXPECT_EQ(expected_help_test, err.help_text());
+}
diff --git a/gn/src/gn/ninja_bundle_data_target_writer.cc b/gn/src/gn/ninja_bundle_data_target_writer.cc
new file mode 100644
index 00000000000..0e3bcb0acb4
--- /dev/null
+++ b/gn/src/gn/ninja_bundle_data_target_writer.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium 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 "gn/ninja_bundle_data_target_writer.h"
+
+#include "gn/output_file.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+
+NinjaBundleDataTargetWriter::NinjaBundleDataTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+NinjaBundleDataTargetWriter::~NinjaBundleDataTargetWriter() = default;
+
+void NinjaBundleDataTargetWriter::Run() {
+ std::vector<OutputFile> output_files;
+ for (const SourceFile& source_file : target_->sources()) {
+ output_files.push_back(
+ OutputFile(settings_->build_settings(), source_file));
+ }
+
+ std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), /*num_stamp_uses=*/1);
+ output_files.insert(output_files.end(), input_deps.begin(), input_deps.end());
+
+ std::vector<OutputFile> order_only_deps;
+ for (const auto& pair : target_->data_deps())
+ order_only_deps.push_back(pair.ptr->dependency_output_file());
+
+ WriteStampForTarget(output_files, order_only_deps);
+}
diff --git a/gn/src/gn/ninja_bundle_data_target_writer.h b/gn/src/gn/ninja_bundle_data_target_writer.h
new file mode 100644
index 00000000000..9b8986d65eb
--- /dev/null
+++ b/gn/src/gn/ninja_bundle_data_target_writer.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_NINJA_BUNDLE_DATA_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_BUNDLE_DATA_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/ninja_target_writer.h"
+
+// Writes a .ninja file for a bundle_data target type.
+class NinjaBundleDataTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaBundleDataTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaBundleDataTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NinjaBundleDataTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_BUNDLE_DATA_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_bundle_data_target_writer_unittest.cc b/gn/src/gn/ninja_bundle_data_target_writer_unittest.cc
new file mode 100644
index 00000000000..06b5eb221c4
--- /dev/null
+++ b/gn/src/gn/ninja_bundle_data_target_writer_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium 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 "gn/ninja_bundle_data_target_writer.h"
+
+#include <algorithm>
+#include <sstream>
+
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(NinjaBundleDataTargetWriter, Run) {
+ Err err;
+ TestWithScope setup;
+
+ Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
+ bundle_data.set_output_type(Target::BUNDLE_DATA);
+ bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
+ bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
+ bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data.SetToolchain(setup.toolchain());
+ bundle_data.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBundleDataTargetWriter writer(&bundle_data, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/foo/data.stamp: stamp "
+ "../../foo/input1.txt "
+ "../../foo/input2.txt "
+ "../../foo/Foo.xcassets/Contents.json "
+ "../../foo/Foo.xcassets/foo.colorset/Contents.json "
+ "../../foo/Foo.xcassets/foo.imageset/Contents.json "
+ "../../foo/Foo.xcassets/foo.imageset/FooIcon-29.png "
+ "../../foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png "
+ "../../foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
diff --git a/gn/src/gn/ninja_c_binary_target_writer.cc b/gn/src/gn/ninja_c_binary_target_writer.cc
new file mode 100644
index 00000000000..17f9c0855da
--- /dev/null
+++ b/gn/src/gn/ninja_c_binary_target_writer.cc
@@ -0,0 +1,909 @@
+// Copyright 2019 The Chromium 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 "gn/ninja_c_binary_target_writer.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include <cstring>
+#include <set>
+#include <sstream>
+#include <unordered_set>
+
+#include "base/strings/string_util.h"
+#include "gn/c_substitution_type.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/err.h"
+#include "gn/escape.h"
+#include "gn/filesystem_utils.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/ninja_utils.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+
+struct ModuleDep {
+ ModuleDep(const SourceFile* modulemap,
+ const std::string& module_name,
+ const OutputFile& pcm,
+ bool is_self)
+ : modulemap(modulemap),
+ module_name(module_name),
+ pcm(pcm),
+ is_self(is_self) {}
+
+ // The input module.modulemap source file.
+ const SourceFile* modulemap;
+
+ // The internal module name, in GN this is the target's label.
+ std::string module_name;
+
+ // The compiled version of the module.
+ OutputFile pcm;
+
+ // Is this the module for the current target.
+ bool is_self;
+};
+
+namespace {
+
+// Returns the proper escape options for writing compiler and linker flags.
+EscapeOptions GetFlagOptions() {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ return opts;
+}
+
+// Returns the language-specific lang recognized by gcc’s -x flag for
+// precompiled header files.
+const char* GetPCHLangForToolType(const char* name) {
+ if (name == CTool::kCToolCc)
+ return "c-header";
+ if (name == CTool::kCToolCxx)
+ return "c++-header";
+ if (name == CTool::kCToolObjC)
+ return "objective-c-header";
+ if (name == CTool::kCToolObjCxx)
+ return "objective-c++-header";
+ NOTREACHED() << "Not a valid PCH tool type: " << name;
+ return "";
+}
+
+const SourceFile* GetModuleMapFromTargetSources(const Target* target) {
+ for (const SourceFile& sf : target->sources()) {
+ if (sf.type() == SourceFile::SOURCE_MODULEMAP) {
+ return &sf;
+ }
+ }
+ return nullptr;
+}
+
+std::vector<ModuleDep> GetModuleDepsInformation(const Target* target) {
+ std::vector<ModuleDep> ret;
+
+ auto add = [&ret](const Target* t, bool is_self) {
+ const SourceFile* modulemap = GetModuleMapFromTargetSources(t);
+ CHECK(modulemap);
+
+ std::string label;
+ CHECK(SubstitutionWriter::GetTargetSubstitution(
+ t, &SubstitutionLabelNoToolchain, &label));
+
+ const char* tool_type;
+ std::vector<OutputFile> modulemap_outputs;
+ CHECK(
+ t->GetOutputFilesForSource(*modulemap, &tool_type, &modulemap_outputs));
+ // Must be only one .pcm from .modulemap.
+ CHECK(modulemap_outputs.size() == 1u);
+ ret.emplace_back(modulemap, label, modulemap_outputs[0], is_self);
+ };
+
+ if (target->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
+ add(target, true);
+ }
+
+ for (const auto& pair: target->GetDeps(Target::DEPS_LINKED)) {
+ // Having a .modulemap source means that the dependency is modularized.
+ if (pair.ptr->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
+ add(pair.ptr, false);
+ }
+ }
+
+ return ret;
+}
+
+} // namespace
+
+NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaBinaryTargetWriter(target, out),
+ tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {}
+
+NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default;
+
+void NinjaCBinaryTargetWriter::Run() {
+ std::vector<ModuleDep> module_dep_info = GetModuleDepsInformation(target_);
+
+ WriteCompilerVars(module_dep_info);
+
+ size_t num_stamp_uses = target_->sources().size();
+
+ std::vector<OutputFile> input_deps = WriteInputsStampAndGetDep(
+ num_stamp_uses);
+
+ // The input dependencies will be an order-only dependency. This will cause
+ // Ninja to make sure the inputs are up to date before compiling this source,
+ // but changes in the inputs deps won't cause the file to be recompiled.
+ //
+ // This is important to prevent changes in unrelated actions that are
+ // upstream of this target from causing everything to be recompiled.
+ //
+ // Why can we get away with this rather than using implicit deps ("|", which
+ // will force rebuilds when the inputs change)? For source code, the
+ // computed dependencies of all headers will be computed by the compiler,
+ // which will cause source rebuilds if any "real" upstream dependencies
+ // change.
+ //
+ // If a .cc file is generated by an input dependency, Ninja will see the
+ // input to the build rule doesn't exist, and that it is an output from a
+ // previous step, and build the previous step first. This is a "real"
+ // dependency and doesn't need | or || to express.
+ //
+ // The only case where this rule matters is for the first build where no .d
+ // files exist, and Ninja doesn't know what that source file depends on. In
+ // this case it's sufficient to ensure that the upstream dependencies are
+ // built first. This is exactly what Ninja's order-only dependencies
+ // expresses.
+ //
+ // The order only deps are referenced by each source file compile,
+ // but also by PCH compiles. The latter are annoying to count, so omit
+ // them here. This means that binary targets with a single source file
+ // that also use PCH files won't have a stamp file even though having
+ // one would make output ninja file size a bit lower. That's ok, binary
+ // targets with a single source are rare.
+ std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), num_stamp_uses);
+
+ // For GCC builds, the .gch files are not object files, but still need to be
+ // added as explicit dependencies below. The .gch output files are placed in
+ // |pch_other_files|. This is to prevent linking against them.
+ std::vector<OutputFile> pch_obj_files;
+ std::vector<OutputFile> pch_other_files;
+ WritePCHCommands(input_deps, order_only_deps, &pch_obj_files,
+ &pch_other_files);
+ std::vector<OutputFile>* pch_files =
+ !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files;
+
+ // Treat all pch output files as explicit dependencies of all
+ // compiles that support them. Some notes:
+ //
+ // - On Windows, the .pch file is the input to the compile, not the
+ // precompiled header's corresponding object file that we're using here.
+ // But Ninja's depslog doesn't support multiple outputs from the
+ // precompiled header compile step (it outputs both the .pch file and a
+ // corresponding .obj file). So we consistently list the .obj file and the
+ // .pch file we really need comes along with it.
+ //
+ // - GCC .gch files are not object files, therefore they are not added to the
+ // object file list.
+ std::vector<OutputFile> obj_files;
+ std::vector<SourceFile> other_files;
+ if (!target_->source_types_used().SwiftSourceUsed()) {
+ WriteSources(*pch_files, input_deps, order_only_deps, module_dep_info,
+ &obj_files, &other_files);
+ } else {
+ WriteSwiftSources(input_deps, order_only_deps, &obj_files);
+ }
+
+ // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
+ obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
+ if (!CheckForDuplicateObjectFiles(obj_files))
+ return;
+
+ if (target_->output_type() == Target::SOURCE_SET) {
+ WriteSourceSetStamp(obj_files);
+#ifndef NDEBUG
+ // Verify that the function that separately computes a source set's object
+ // files match the object files just computed.
+ UniqueVector<OutputFile> computed_obj;
+ AddSourceSetFiles(target_, &computed_obj);
+ DCHECK_EQ(obj_files.size(), computed_obj.size());
+ for (const auto& obj : obj_files)
+ DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj));
+#endif
+ } else {
+ WriteLinkerStuff(obj_files, other_files, input_deps);
+ }
+}
+
+void NinjaCBinaryTargetWriter::WriteCompilerVars(
+ const std::vector<ModuleDep>& module_dep_info) {
+ const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
+ // Defines.
+ if (subst.used.count(&CSubstitutionDefines)) {
+ out_ << CSubstitutionDefines.ninja_name << " =";
+ RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
+ DefineWriter(), out_);
+ out_ << std::endl;
+ }
+
+ // Framework search path.
+ if (subst.used.count(&CSubstitutionFrameworkDirs)) {
+ const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
+
+ out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
+ PathOutput framework_dirs_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ target_, &ConfigValues::framework_dirs,
+ FrameworkDirsWriter(framework_dirs_output,
+ tool->framework_dir_switch()),
+ out_);
+ out_ << std::endl;
+ }
+
+ // Include directories.
+ if (subst.used.count(&CSubstitutionIncludeDirs)) {
+ out_ << CSubstitutionIncludeDirs.ninja_name << " =";
+ PathOutput include_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ target_, &ConfigValues::include_dirs,
+ IncludeWriter(include_path_output), out_);
+ out_ << std::endl;
+ }
+
+ if (!module_dep_info.empty()) {
+ // TODO(scottmg): Currently clang modules only working for C++.
+ if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
+ WriteModuleDepsSubstitution(&CSubstitutionModuleDeps, module_dep_info,
+ true);
+ WriteModuleDepsSubstitution(&CSubstitutionModuleDepsNoSelf,
+ module_dep_info, false);
+ }
+ }
+
+ bool has_precompiled_headers =
+ target_->config_values().has_precompiled_headers();
+
+ EscapeOptions opts = GetFlagOptions();
+ if (target_->source_types_used().Get(SourceFile::SOURCE_S) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_ASM)) {
+ WriteOneFlag(target_, &CSubstitutionAsmFlags, false, Tool::kToolNone,
+ &ConfigValues::asmflags, opts, path_output_, out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_M) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
+ WriteOneFlag(target_, &CSubstitutionCFlags, false, Tool::kToolNone,
+ &ConfigValues::cflags, opts, path_output_, out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_C)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsC, has_precompiled_headers,
+ CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output_,
+ out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsCc, has_precompiled_headers,
+ CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
+ out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_M)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsObjC, has_precompiled_headers,
+ CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
+ path_output_, out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsObjCc, has_precompiled_headers,
+ CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
+ path_output_, out_);
+ }
+ if (target_->source_types_used().SwiftSourceUsed()) {
+ if (subst.used.count(&CSubstitutionSwiftModuleName)) {
+ out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
+ EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
+ out_ << std::endl;
+ }
+
+ if (subst.used.count(&CSubstitutionSwiftBridgeHeader)) {
+ out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
+ if (!target_->swift_values().bridge_header().is_null()) {
+ path_output_.WriteFile(out_, target_->swift_values().bridge_header());
+ } else {
+ out_ << R"("")";
+ }
+ out_ << std::endl;
+ }
+
+ if (subst.used.count(&CSubstitutionSwiftModuleDirs)) {
+ // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
+ // are generated in the same directory).
+ UniqueVector<SourceDir> swiftmodule_dirs;
+ for (const Target* dep : target_->swift_values().modules())
+ swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
+
+ out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
+ PathOutput swiftmodule_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
+ for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
+ swiftmodule_path_writer(swiftmodule_dir, out_);
+ }
+ out_ << std::endl;
+ }
+
+ WriteOneFlag(target_, &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
+ &ConfigValues::swiftflags, opts, path_output_, out_);
+ }
+
+ WriteSharedVars(subst);
+}
+
+void NinjaCBinaryTargetWriter::WriteModuleDepsSubstitution(
+ const Substitution* substitution,
+ const std::vector<ModuleDep>& module_dep_info,
+ bool include_self) {
+ if (target_->toolchain()->substitution_bits().used.count(
+ substitution)) {
+ EscapeOptions options;
+ options.mode = ESCAPE_NINJA_COMMAND;
+
+ out_ << substitution->ninja_name << " = -Xclang ";
+ EscapeStringToStream(out_, "-fmodules-embed-all-files", options);
+
+ for (const auto& module_dep : module_dep_info) {
+ if (!module_dep.is_self || include_self) {
+ out_ << " ";
+ EscapeStringToStream(out_, "-fmodule-file=", options);
+ path_output_.WriteFile(out_, module_dep.pcm);
+ }
+ }
+
+ out_ << std::endl;
+ }
+}
+
+void NinjaCBinaryTargetWriter::WritePCHCommands(
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files) {
+ if (!target_->config_values().has_precompiled_headers())
+ return;
+
+ const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc);
+ if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE &&
+ target_->source_types_used().Get(SourceFile::SOURCE_C)) {
+ WritePCHCommand(&CSubstitutionCFlagsC, CTool::kCToolCc,
+ tool_c->precompiled_header_type(), input_deps,
+ order_only_deps, object_files, other_files);
+ }
+ const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx);
+ if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE &&
+ target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
+ WritePCHCommand(&CSubstitutionCFlagsCc, CTool::kCToolCxx,
+ tool_cxx->precompiled_header_type(), input_deps,
+ order_only_deps, object_files, other_files);
+ }
+
+ const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC);
+ if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC &&
+ target_->source_types_used().Get(SourceFile::SOURCE_M)) {
+ WritePCHCommand(&CSubstitutionCFlagsObjC, CTool::kCToolObjC,
+ tool_objc->precompiled_header_type(), input_deps,
+ order_only_deps, object_files, other_files);
+ }
+
+ const CTool* tool_objcxx =
+ target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
+ if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC &&
+ target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
+ WritePCHCommand(&CSubstitutionCFlagsObjCc, CTool::kCToolObjCxx,
+ tool_objcxx->precompiled_header_type(), input_deps,
+ order_only_deps, object_files, other_files);
+ }
+}
+
+void NinjaCBinaryTargetWriter::WritePCHCommand(
+ const Substitution* flag_type,
+ const char* tool_name,
+ CTool::PrecompiledHeaderType header_type,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files) {
+ switch (header_type) {
+ case CTool::PCH_MSVC:
+ WriteWindowsPCHCommand(flag_type, tool_name, input_deps, order_only_deps,
+ object_files);
+ break;
+ case CTool::PCH_GCC:
+ WriteGCCPCHCommand(flag_type, tool_name, input_deps, order_only_deps,
+ other_files);
+ break;
+ case CTool::PCH_NONE:
+ NOTREACHED() << "Cannot write a PCH command with no PCH header type";
+ break;
+ }
+}
+
+void NinjaCBinaryTargetWriter::WriteGCCPCHCommand(
+ const Substitution* flag_type,
+ const char* tool_name,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* gch_files) {
+ // Compute the pch output file (it will be language-specific).
+ std::vector<OutputFile> outputs;
+ GetPCHOutputFiles(target_, tool_name, &outputs);
+ if (outputs.empty())
+ return;
+
+ gch_files->insert(gch_files->end(), outputs.begin(), outputs.end());
+
+ std::vector<OutputFile> extra_deps;
+ std::copy(input_deps.begin(), input_deps.end(),
+ std::back_inserter(extra_deps));
+
+ // Build line to compile the file.
+ WriteCompilerBuildLine({target_->config_values().precompiled_source()},
+ extra_deps, order_only_deps, tool_name, outputs);
+
+ // This build line needs a custom language-specific flags value. Rule-specific
+ // variables are just indented underneath the rule line.
+ out_ << " " << flag_type->ninja_name << " =";
+
+ // Each substitution flag is overwritten in the target rule to replace the
+ // implicitly generated -include flag with the -x <header lang> flag required
+ // for .gch targets.
+ EscapeOptions opts = GetFlagOptions();
+ if (tool_name == CTool::kCToolCc) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts,
+ out_);
+ } else if (tool_name == CTool::kCToolCxx) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc,
+ opts, out_);
+ } else if (tool_name == CTool::kCToolObjC) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc,
+ opts, out_);
+ } else if (tool_name == CTool::kCToolObjCxx) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc,
+ opts, out_);
+ }
+
+ // Append the command to specify the language of the .gch file.
+ out_ << " -x " << GetPCHLangForToolType(tool_name);
+
+ // Write two blank lines to help separate the PCH build lines from the
+ // regular source build lines.
+ out_ << std::endl << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand(
+ const Substitution* flag_type,
+ const char* tool_name,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files) {
+ // Compute the pch output file (it will be language-specific).
+ std::vector<OutputFile> outputs;
+ GetPCHOutputFiles(target_, tool_name, &outputs);
+ if (outputs.empty())
+ return;
+
+ object_files->insert(object_files->end(), outputs.begin(), outputs.end());
+
+ std::vector<OutputFile> extra_deps;
+ std::copy(input_deps.begin(), input_deps.end(),
+ std::back_inserter(extra_deps));
+
+ // Build line to compile the file.
+ WriteCompilerBuildLine({target_->config_values().precompiled_source()},
+ extra_deps, order_only_deps, tool_name, outputs);
+
+ // This build line needs a custom language-specific flags value. Rule-specific
+ // variables are just indented underneath the rule line.
+ out_ << " " << flag_type->ninja_name << " =";
+
+ // Append the command to generate the .pch file.
+ // This adds the value to the existing flag instead of overwriting it.
+ out_ << " ${" << flag_type->ninja_name << "}";
+ out_ << " /Yc" << target_->config_values().precompiled_header();
+
+ // Write two blank lines to help separate the PCH build lines from the
+ // regular source build lines.
+ out_ << std::endl << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteSources(
+ const std::vector<OutputFile>& pch_deps,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ const std::vector<ModuleDep>& module_dep_info,
+ std::vector<OutputFile>* object_files,
+ std::vector<SourceFile>* other_files) {
+ DCHECK(!target_->source_types_used().SwiftSourceUsed());
+ object_files->reserve(object_files->size() + target_->sources().size());
+
+ std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
+ std::vector<OutputFile> deps;
+ for (const auto& source : target_->sources()) {
+ DCHECK_NE(source.type(), SourceFile::SOURCE_SWIFT);
+
+ // Clear the vector but maintain the max capacity to prevent reallocations.
+ deps.resize(0);
+ const char* tool_name = Tool::kToolNone;
+ if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) {
+ if (source.type() == SourceFile::SOURCE_DEF)
+ other_files->push_back(source);
+ continue; // No output for this source.
+ }
+
+ std::copy(input_deps.begin(), input_deps.end(), std::back_inserter(deps));
+
+ if (tool_name != Tool::kToolNone) {
+ // Only include PCH deps that correspond to the tool type, for instance,
+ // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
+ // for the output of a C tool type.
+ //
+ // This makes the assumption that pch_deps only contains pch output files
+ // with the naming scheme specified in GetWindowsPCHObjectExtension or
+ // GetGCCPCHOutputExtension.
+ const CTool* tool = target_->toolchain()->GetToolAsC(tool_name);
+ if (tool->precompiled_header_type() != CTool::PCH_NONE) {
+ for (const auto& dep : pch_deps) {
+ const std::string& output_value = dep.value();
+ size_t extension_offset = FindExtensionOffset(output_value);
+ if (extension_offset == std::string::npos)
+ continue;
+ std::string output_extension;
+ if (tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ output_extension = GetWindowsPCHObjectExtension(
+ tool_name, output_value.substr(extension_offset - 1));
+ } else if (tool->precompiled_header_type() == CTool::PCH_GCC) {
+ output_extension = GetGCCPCHOutputExtension(tool_name);
+ }
+ if (output_value.compare(
+ output_value.size() - output_extension.size(),
+ output_extension.size(), output_extension) == 0) {
+ deps.push_back(dep);
+ }
+ }
+ }
+
+ for (const auto& module_dep : module_dep_info) {
+ if (tool_outputs[0] != module_dep.pcm)
+ deps.push_back(module_dep.pcm);
+ }
+
+ WriteCompilerBuildLine({source}, deps, order_only_deps, tool_name,
+ tool_outputs);
+ }
+
+ // It's theoretically possible for a compiler to produce more than one
+ // output, but we'll only link to the first output.
+ if (source.type() != SourceFile::SOURCE_MODULEMAP) {
+ object_files->push_back(tool_outputs[0]);
+ }
+ }
+
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteSwiftSources(
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files) {
+ DCHECK(target_->source_types_used().SwiftSourceUsed());
+ object_files->reserve(object_files->size() + target_->sources().size());
+
+ // If the target contains .swift source files, they needs to be compiled as
+ // a single unit but still can produce more than one object file (if the
+ // whole module optimization is disabled).
+ if (target_->source_types_used().SwiftSourceUsed()) {
+ const Tool* tool =
+ target_->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
+
+ const OutputFile swiftmodule_output_file =
+ target_->swift_values().module_output_file();
+
+ std::vector<OutputFile> additional_outputs;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ target_, tool, tool->outputs(), &additional_outputs);
+
+ additional_outputs.erase(
+ std::remove(additional_outputs.begin(), additional_outputs.end(),
+ swiftmodule_output_file),
+ additional_outputs.end());
+
+ for (const OutputFile& output : additional_outputs) {
+ const SourceFile output_as_source =
+ output.AsSourceFile(target_->settings()->build_settings());
+
+ if (output_as_source.type() == SourceFile::SOURCE_O) {
+ object_files->push_back(output);
+ }
+ }
+
+ const SubstitutionList& partial_outputs_subst = tool->partial_outputs();
+ if (!partial_outputs_subst.list().empty()) {
+ // Avoid re-allocation during loop.
+ std::vector<OutputFile> partial_outputs;
+ for (const auto& source : target_->sources()) {
+ if (source.type() != SourceFile::SOURCE_SWIFT)
+ continue;
+
+ partial_outputs.resize(0);
+ SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+ target_, source, partial_outputs_subst, &partial_outputs);
+
+ for (const OutputFile& output : partial_outputs) {
+ additional_outputs.push_back(output);
+ SourceFile output_as_source =
+ output.AsSourceFile(target_->settings()->build_settings());
+ if (output_as_source.type() == SourceFile::SOURCE_O) {
+ object_files->push_back(output);
+ }
+ }
+ }
+ }
+
+ UniqueVector<OutputFile> swift_order_only_deps;
+ swift_order_only_deps.reserve(order_only_deps.size());
+ swift_order_only_deps.Append(order_only_deps.begin(),
+ order_only_deps.end());
+
+ for (const Target* swiftmodule : target_->swift_values().modules())
+ swift_order_only_deps.push_back(swiftmodule->dependency_output_file());
+
+ WriteCompilerBuildLine(target_->sources(), input_deps,
+ swift_order_only_deps.vector(), tool->name(),
+ {swiftmodule_output_file});
+
+ if (!additional_outputs.empty()) {
+ out_ << std::endl;
+ WriteCompilerBuildLine(
+ {swiftmodule_output_file.AsSourceFile(settings_->build_settings())},
+ input_deps, swift_order_only_deps.vector(),
+ GeneralTool::kGeneralToolStamp, additional_outputs);
+ }
+ }
+
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteLinkerStuff(
+ const std::vector<OutputFile>& object_files,
+ const std::vector<SourceFile>& other_files,
+ const std::vector<OutputFile>& input_deps) {
+ std::vector<OutputFile> output_files;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ target_, tool_, tool_->outputs(), &output_files);
+
+ out_ << "build";
+ path_output_.WriteFiles(out_, output_files);
+
+ out_ << ": " << rule_prefix_
+ << Tool::GetToolTypeForTargetFinalOutput(target_);
+
+ ClassifiedDeps classified_deps = GetClassifiedDeps();
+
+ // Object files.
+ path_output_.WriteFiles(out_, object_files);
+ path_output_.WriteFiles(out_, classified_deps.extra_object_files);
+
+ // Dependencies.
+ std::vector<OutputFile> implicit_deps;
+ std::vector<OutputFile> solibs;
+ for (const Target* cur : classified_deps.linkable_deps) {
+ // All linkable deps should have a link output file.
+ DCHECK(!cur->link_output_file().value().empty())
+ << "No link output file for "
+ << target_->label().GetUserVisibleName(false);
+
+ if (cur->output_type() == Target::RUST_LIBRARY ||
+ cur->output_type() == Target::RUST_PROC_MACRO)
+ continue;
+
+ if (cur->dependency_output_file().value() !=
+ cur->link_output_file().value()) {
+ // This is a shared library with separate link and deps files. Save for
+ // later.
+ implicit_deps.push_back(cur->dependency_output_file());
+ solibs.push_back(cur->link_output_file());
+ } else {
+ // Normal case, just link to this target.
+ out_ << " ";
+ path_output_.WriteFile(out_, cur->link_output_file());
+ }
+ }
+
+ const SourceFile* optional_def_file = nullptr;
+ if (!other_files.empty()) {
+ for (const SourceFile& src_file : other_files) {
+ if (src_file.type() == SourceFile::SOURCE_DEF) {
+ optional_def_file = &src_file;
+ implicit_deps.push_back(
+ OutputFile(settings_->build_settings(), src_file));
+ break; // Only one def file is allowed.
+ }
+ }
+ }
+
+ // Libraries specified by paths.
+ const OrderedSet<LibFile>& libs = target_->all_libs();
+ for (size_t i = 0; i < libs.size(); i++) {
+ if (libs[i].is_source_file()) {
+ implicit_deps.push_back(
+ OutputFile(settings_->build_settings(), libs[i].source_file()));
+ }
+ }
+
+ // If any target creates a framework bundle, then treat it as an implicit
+ // dependency via the .stamp file. This is a pessimisation as it is not
+ // always necessary to relink the current target if one of the framework
+ // is regenerated, but it ensure that if one of the framework API changes,
+ // any dependent target will relink it (see crbug.com/1037607).
+ for (const Target* dep : classified_deps.framework_deps) {
+ implicit_deps.push_back(dep->dependency_output_file());
+ }
+
+ // The input dependency is only needed if there are no object files, as the
+ // dependency is normally provided transitively by the source files.
+ std::copy(input_deps.begin(), input_deps.end(),
+ std::back_inserter(implicit_deps));
+
+ // Any C++ target which depends on a Rust .rlib has to depend on its
+ // entire tree of transitive rlibs.
+ std::vector<OutputFile> transitive_rustlibs;
+ if (target_->IsFinal()) {
+ for (const auto* dep :
+ target_->rust_values().transitive_libs().GetOrdered()) {
+ if (dep->output_type() == Target::RUST_LIBRARY) {
+ transitive_rustlibs.push_back(dep->dependency_output_file());
+ implicit_deps.push_back(dep->dependency_output_file());
+ }
+ }
+ }
+
+ // Swift modules from dependencies (and possibly self).
+ std::vector<OutputFile> swiftmodules;
+ if (target_->IsFinal()) {
+ for (const Target* dep : classified_deps.swiftmodule_deps) {
+ swiftmodules.push_back(dep->swift_values().module_output_file());
+ implicit_deps.push_back(dep->swift_values().module_output_file());
+ }
+ if (target_->swift_values().builds_module()) {
+ swiftmodules.push_back(target_->swift_values().module_output_file());
+ implicit_deps.push_back(target_->swift_values().module_output_file());
+ }
+ }
+
+ // Append implicit dependencies collected above.
+ if (!implicit_deps.empty()) {
+ out_ << " |";
+ path_output_.WriteFiles(out_, implicit_deps);
+ }
+
+ // Append data dependencies as order-only dependencies.
+ //
+ // This will include data dependencies and input dependencies (like when
+ // this target depends on an action). Having the data dependencies in this
+ // list ensures that the data is available at runtime when the user builds
+ // this target.
+ //
+ // The action dependencies are not strictly necessary in this case. They
+ // should also have been collected via the input deps stamp that each source
+ // file has for an order-only dependency, and since this target depends on
+ // the sources, there is already an implicit order-only dependency. However,
+ // it's extra work to separate these out and there's no disadvantage to
+ // listing them again.
+ WriteOrderOnlyDependencies(classified_deps.non_linkable_deps);
+
+ // End of the link "build" line.
+ out_ << std::endl;
+
+ // The remaining things go in the inner scope of the link line.
+ if (target_->output_type() == Target::EXECUTABLE ||
+ target_->output_type() == Target::SHARED_LIBRARY ||
+ target_->output_type() == Target::LOADABLE_MODULE) {
+ out_ << " ldflags =";
+ WriteLinkerFlags(out_, tool_, optional_def_file);
+ out_ << std::endl;
+ out_ << " libs =";
+ WriteLibs(out_, tool_);
+ out_ << std::endl;
+ out_ << " frameworks =";
+ WriteFrameworks(out_, tool_);
+ out_ << std::endl;
+ out_ << " swiftmodules =";
+ WriteSwiftModules(out_, tool_, swiftmodules);
+ out_ << std::endl;
+ } else if (target_->output_type() == Target::STATIC_LIBRARY) {
+ out_ << " arflags =";
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags,
+ GetFlagOptions(), out_);
+ out_ << std::endl;
+ }
+ WriteOutputSubstitutions();
+ WriteLibsList("solibs", solibs);
+ WriteLibsList("rlibs", transitive_rustlibs);
+}
+
+void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
+ out_ << " output_extension = "
+ << SubstitutionWriter::GetLinkerSubstitution(
+ target_, tool_, &SubstitutionOutputExtension);
+ out_ << std::endl;
+ out_ << " output_dir = "
+ << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
+ &SubstitutionOutputDir);
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteLibsList(
+ const std::string& label,
+ const std::vector<OutputFile>& libs) {
+ if (libs.empty())
+ return;
+
+ out_ << " " << label << " =";
+ path_output_.WriteFiles(out_, libs);
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies(
+ const UniqueVector<const Target*>& non_linkable_deps) {
+ if (!non_linkable_deps.empty()) {
+ out_ << " ||";
+
+ // Non-linkable targets.
+ for (auto* non_linkable_dep : non_linkable_deps) {
+ out_ << " ";
+ path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
+ }
+ }
+}
+
+bool NinjaCBinaryTargetWriter::CheckForDuplicateObjectFiles(
+ const std::vector<OutputFile>& files) const {
+ std::unordered_set<std::string> set;
+ for (const auto& file : files) {
+ if (!set.insert(file.value()).second) {
+ Err err(
+ target_->defined_from(), "Duplicate object file",
+ "The target " + target_->label().GetUserVisibleName(false) +
+ "\ngenerates two object files with the same name:\n " +
+ file.value() +
+ "\n"
+ "\n"
+ "It could be you accidentally have a file listed twice in the\n"
+ "sources. Or, depending on how your toolchain maps sources to\n"
+ "object files, two source files with the same name in different\n"
+ "directories could map to the same object file.\n"
+ "\n"
+ "In the latter case, either rename one of the files or move one "
+ "of\n"
+ "the sources to a separate source_set to avoid them both being "
+ "in\n"
+ "the same target.");
+ g_scheduler->FailWithError(err);
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/gn/src/gn/ninja_c_binary_target_writer.h b/gn/src/gn/ninja_c_binary_target_writer.h
new file mode 100644
index 00000000000..bbc71c534ef
--- /dev/null
+++ b/gn/src/gn/ninja_c_binary_target_writer.h
@@ -0,0 +1,112 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/config_values.h"
+#include "gn/ninja_binary_target_writer.h"
+#include "gn/toolchain.h"
+#include "gn/unique_vector.h"
+
+struct EscapeOptions;
+struct ModuleDep;
+
+// Writes a .ninja file for a binary target type (an executable, a shared
+// library, or a static library).
+class NinjaCBinaryTargetWriter : public NinjaBinaryTargetWriter {
+ public:
+ NinjaCBinaryTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaCBinaryTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ using OutputFileSet = std::set<OutputFile>;
+
+ // Writes all flags for the compiler: includes, defines, cflags, etc.
+ void WriteCompilerVars(const std::vector<ModuleDep>& module_dep_info);
+
+ // Write module_deps or module_deps_no_self flags for clang modulemaps.
+ void WriteModuleDepsSubstitution(
+ const Substitution* substitution,
+ const std::vector<ModuleDep>& module_dep_info,
+ bool include_self);
+
+ // Writes build lines required for precompiled headers. Any generated
+ // object files will be appended to the |object_files|. Any generated
+ // non-object files (for instance, .gch files from a GCC toolchain, are
+ // appended to |other_files|).
+ //
+ // input_deps is the stamp file collecting the dependencies required before
+ // compiling this target. It will be empty if there are no input deps.
+ void WritePCHCommands(const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files);
+
+ // Writes a .pch compile build line for a language type.
+ void WritePCHCommand(const Substitution* flag_type,
+ const char* tool_name,
+ CTool::PrecompiledHeaderType header_type,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files);
+
+ void WriteGCCPCHCommand(const Substitution* flag_type,
+ const char* tool_name,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* gch_files);
+
+ void WriteWindowsPCHCommand(const Substitution* flag_type,
+ const char* tool_name,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files);
+
+ // pch_deps are additional dependencies to run before the rule. They are
+ // expected to abide by the naming conventions specified by GetPCHOutputFiles.
+ //
+ // order_only_dep are the dependencies that must be run before doing any
+ // compiles.
+ //
+ // The files produced by the compiler will be added to two output vectors.
+ void WriteSources(const std::vector<OutputFile>& pch_deps,
+ const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ const std::vector<ModuleDep>& module_dep_info,
+ std::vector<OutputFile>* object_files,
+ std::vector<SourceFile>* other_files);
+ void WriteSwiftSources(const std::vector<OutputFile>& input_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files);
+
+ void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
+ const std::vector<SourceFile>& other_files,
+ const std::vector<OutputFile>& input_deps);
+ void WriteOutputSubstitutions();
+ void WriteLibsList(const std::string& label,
+ const std::vector<OutputFile>& libs);
+
+ // Writes the implicit dependencies for the link or stamp line. This is
+ // the "||" and everything following it on the ninja line.
+ //
+ // The order-only dependencies are the non-linkable deps passed in as an
+ // argument, plus the data file depdencies in the target.
+ void WriteOrderOnlyDependencies(
+ const UniqueVector<const Target*>& non_linkable_deps);
+
+ // Checks for duplicates in the given list of output files. If any duplicates
+ // are found, throws an error and return false.
+ bool CheckForDuplicateObjectFiles(const std::vector<OutputFile>& files) const;
+
+ const CTool* tool_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaCBinaryTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_c_binary_target_writer_unittest.cc b/gn/src/gn/ninja_c_binary_target_writer_unittest.cc
new file mode 100644
index 00000000000..99961dafdd8
--- /dev/null
+++ b/gn/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -0,0 +1,1922 @@
+// Copyright 2019 The Chromium 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 "gn/ninja_c_binary_target_writer.h"
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "gn/config.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/scheduler.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+using NinjaCBinaryTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaCBinaryTargetWriterTest, SourceSet) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ // Also test object files, which should be just passed through to the
+ // dependents to link.
+ target.sources().push_back(SourceFile("//foo/input3.o"));
+ target.sources().push_back(SourceFile("//foo/input4.obj"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.source_types_used().Set(SourceFile::SOURCE_O);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Source set itself.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // A shared library that depends on the source set.
+ Target shlib_target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ shlib_target.set_output_type(Target::SHARED_LIBRARY);
+ shlib_target.public_deps().push_back(LabelTargetPair(&target));
+ shlib_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(shlib_target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&shlib_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libshlib\n"
+ "\n"
+ "\n"
+ // Ordering of the obj files here should come out in the order
+ // specified, with the target's first, followed by the source set's, in
+ // order.
+ "build ./libshlib.so: solink obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
+ "|| obj/foo/bar.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // A static library that depends on the source set (should not link it).
+ Target stlib_target(setup.settings(), Label(SourceDir("//foo/"), "stlib"));
+ stlib_target.set_output_type(Target::STATIC_LIBRARY);
+ stlib_target.public_deps().push_back(LabelTargetPair(&target));
+ stlib_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(stlib_target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&stlib_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libstlib\n"
+ "\n"
+ "\n"
+ // There are no sources so there are no params to alink. (In practice
+ // this will probably fail in the archive tool.)
+ "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // Make the static library 'complete', which means it should be linked.
+ stlib_target.set_complete_static_lib(true);
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&stlib_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libstlib\n"
+ "\n"
+ "\n"
+ // Ordering of the obj files here should come out in the order
+ // specified, with the target's first, followed by the source set's, in
+ // order.
+ "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
+ "|| obj/foo/bar.stamp\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, EscapeDefines) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.config_values().defines().push_back("BOOL_DEF");
+ target.config_values().defines().push_back("INT_DEF=123");
+ target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expectedSubstr[] =
+#if defined(OS_WIN)
+ "defines = -DBOOL_DEF -DINT_DEF=123 \"-DSTR_DEF=\\\"ABCD-1\\\"\"";
+#else
+ "defines = -DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\"ABCD-1\\\"";
+#endif
+ std::string out_str = out.str();
+ EXPECT_TRUE(out_str.find(expectedSubstr) != std::string::npos);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, StaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().arflags().push_back("--asdf");
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
+ " arflags = --asdf\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().arflags().push_back("--asdf");
+ target.set_complete_static_lib(true);
+
+ TestTarget baz(setup, "//foo:baz", Target::STATIC_LIBRARY);
+ baz.sources().push_back(SourceFile("//foo/input2.cc"));
+ baz.source_types_used().Set(SourceFile::SOURCE_CPP);
+
+ target.public_deps().push_back(LabelTargetPair(&baz));
+
+ ASSERT_TRUE(target.OnResolved(&err));
+ ASSERT_TRUE(baz.OnResolved(&err));
+
+ // A complete static library that depends on an incomplete static library
+ // should link in the dependent object files as if the dependent target
+ // were a source set.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
+ "obj/foo/libbaz.input2.o || obj/foo/libbaz.a\n"
+ " arflags = --asdf\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // Make the dependent static library complete.
+ baz.set_complete_static_lib(true);
+
+ // Dependent complete static libraries should not be linked directly.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
+ "|| obj/foo/libbaz.a\n"
+ " arflags = --asdf\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+// This tests that output extension and output dir overrides apply, and input
+// dependencies are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
+ Err err;
+ TestWithScope setup;
+
+ // An action for our library to depend on.
+ Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
+ action.set_output_type(Target::ACTION_FOREACH);
+ action.visibility().SetPublic();
+ action.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ // A shared library w/ the output_extension set to a custom value.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.set_output_extension(std::string("so.6"));
+ target.set_output_dir(SourceDir("//out/Debug/foo/"));
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.public_deps().push_back(LabelTargetPair(&action));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libshlib\n"
+ "\n"
+ "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
+ " || obj/foo/action.stamp\n"
+ "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
+ " || obj/foo/action.stamp\n"
+ "\n"
+ "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
+ // The order-only dependency here is stricly unnecessary since the
+ // sources list this as an order-only dep. See discussion in the code
+ // that writes this.
+ "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so.6\n"
+ " output_dir = foo\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
+ Err err;
+ TestWithScope setup;
+
+ SourceFile generated_file("//out/Debug/generated.cc");
+
+ // An action does code generation.
+ Target action(setup.settings(), Label(SourceDir("//foo/"), "generate"));
+ action.set_output_type(Target::ACTION);
+ action.visibility().SetPublic();
+ action.SetToolchain(setup.toolchain());
+ action.set_output_dir(SourceDir("//out/Debug/foo/"));
+ action.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/generated.cc");
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ // A source set compiling geneated code, this target does not publicize any
+ // headers.
+ Target gen_obj(setup.settings(), Label(SourceDir("//foo/"), "gen_obj"));
+ gen_obj.set_output_type(Target::SOURCE_SET);
+ gen_obj.set_output_dir(SourceDir("//out/Debug/foo/"));
+ gen_obj.sources().push_back(generated_file);
+ gen_obj.source_types_used().Set(SourceFile::SOURCE_CPP);
+ gen_obj.visibility().SetPublic();
+ gen_obj.private_deps().push_back(LabelTargetPair(&action));
+ gen_obj.set_all_headers_public(false);
+ gen_obj.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(gen_obj.OnResolved(&err));
+
+ std::ostringstream obj_out;
+ NinjaCBinaryTargetWriter obj_writer(&gen_obj, obj_out);
+ obj_writer.Run();
+
+ const char obj_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = gen_obj\n"
+ "\n"
+ "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
+ " || obj/foo/generate.stamp\n"
+ "\n"
+ "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/gen_obj.generated.o"
+ // The order-only dependency here is strictly unnecessary since the
+ // sources list this as an order-only dep.
+ " || obj/foo/generate.stamp\n";
+
+ std::string obj_str = obj_out.str();
+ EXPECT_EQ(obj_expected, obj_str);
+
+ // A shared library depends on gen_obj, having corresponding header for
+ // generated obj.
+ Target gen_lib(setup.settings(), Label(SourceDir("//foo/"), "gen_lib"));
+ gen_lib.set_output_type(Target::SHARED_LIBRARY);
+ gen_lib.set_output_dir(SourceDir("//out/Debug/foo/"));
+ gen_lib.sources().push_back(SourceFile("//foor/generated.h"));
+ gen_lib.source_types_used().Set(SourceFile::SOURCE_H);
+ gen_lib.visibility().SetPublic();
+ gen_lib.private_deps().push_back(LabelTargetPair(&gen_obj));
+ gen_lib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(gen_lib.OnResolved(&err));
+
+ std::ostringstream lib_out;
+ NinjaCBinaryTargetWriter lib_writer(&gen_lib, lib_out);
+ lib_writer.Run();
+
+ const char lib_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libgen_lib\n"
+ "\n"
+ "\n"
+ "build ./libgen_lib.so: solink obj/out/Debug/gen_obj.generated.o"
+ // The order-only dependency here is strictly unnecessary since
+ // obj/out/Debug/gen_obj.generated.o has dependency to
+ // obj/foo/gen_obj.stamp
+ " || obj/foo/gen_obj.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = foo\n";
+
+ std::string lib_str = lib_out.str();
+ EXPECT_EQ(lib_expected, lib_str);
+
+ // An executable depends on gen_lib.
+ Target executable(setup.settings(),
+ Label(SourceDir("//foo/"), "final_target"));
+ executable.set_output_type(Target::EXECUTABLE);
+ executable.set_output_dir(SourceDir("//out/Debug/foo/"));
+ executable.sources().push_back(SourceFile("//foo/main.cc"));
+ executable.source_types_used().Set(SourceFile::SOURCE_CPP);
+ executable.private_deps().push_back(LabelTargetPair(&gen_lib));
+ executable.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
+
+ std::ostringstream final_out;
+ NinjaCBinaryTargetWriter final_writer(&executable, final_out);
+ final_writer.Run();
+
+ // There is no order only dependency to action target.
+ const char final_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = final_target\n"
+ "\n"
+ "build obj/foo/final_target.main.o: cxx ../../foo/main.cc\n"
+ "\n"
+ "build ./final_target: link obj/foo/final_target.main.o"
+ " ./libgen_lib.so\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = foo\n";
+
+ std::string final_str = final_out.str();
+ EXPECT_EQ(final_expected, final_str);
+}
+
+// Tests libs are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, LibsAndLibDirs) {
+ Err err;
+ TestWithScope setup;
+
+ // A shared library w/ libs and lib_dirs.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.config_values().libs().push_back(LibFile(SourceFile("//foo/lib1.a")));
+ target.config_values().libs().push_back(LibFile("foo"));
+ target.config_values().lib_dirs().push_back(SourceDir("//foo/bar/"));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libshlib\n"
+ "\n"
+ "\n"
+ "build ./libshlib.so: solink | ../../foo/lib1.a\n"
+ " ldflags = -L../../foo/bar\n"
+ " libs = ../../foo/lib1.a -lfoo\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+// Tests frameworks are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, FrameworksAndFrameworkDirs) {
+ Err err;
+ TestWithScope setup;
+
+ // A config that force linking with the framework.
+ Config framework_config(setup.settings(),
+ Label(SourceDir("//bar"), "framework_config"));
+ framework_config.visibility().SetPublic();
+ framework_config.own_values().frameworks().push_back("Bar.framework");
+ framework_config.own_values().framework_dirs().push_back(
+ SourceDir("//out/Debug/"));
+ ASSERT_TRUE(framework_config.OnResolved(&err));
+
+ // A target creating a framework bundle.
+ Target framework(setup.settings(), Label(SourceDir("//bar"), "framework"));
+ framework.set_output_type(Target::CREATE_BUNDLE);
+ framework.bundle_data().product_type() = "com.apple.product-type.framework";
+ framework.public_configs().push_back(LabelConfigPair(&framework_config));
+ framework.SetToolchain(setup.toolchain());
+ framework.visibility().SetPublic();
+ ASSERT_TRUE(framework.OnResolved(&err));
+
+ // A shared library w/ libs and lib_dirs.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.config_values().frameworks().push_back("System.framework");
+ target.config_values().weak_frameworks().push_back("Whizbang.framework");
+ target.private_deps().push_back(LabelTargetPair(&framework));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libshlib\n"
+ "\n"
+ "\n"
+ "build ./libshlib.so: solink | obj/bar/framework.stamp\n"
+ " ldflags = -F.\n"
+ " libs =\n"
+ " frameworks = -framework System -framework Bar "
+ "-weak_framework Whizbang\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, EmptyOutputExtension) {
+ Err err;
+ TestWithScope setup;
+
+ // This test is the same as OutputExtensionAndInputDeps, except that we call
+ // set_output_extension("") and ensure that we get an empty one and override
+ // the output prefix so that the name matches the target exactly.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.set_output_prefix_override(true);
+ target.set_output_extension(std::string());
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = shlib\n"
+ "\n"
+ "build obj/foo/shlib.input1.o: cxx ../../foo/input1.cc\n"
+ "build obj/foo/shlib.input2.o: cxx ../../foo/input2.cc\n"
+ "\n"
+ "build ./shlib: solink obj/foo/shlib.input1.o "
+ "obj/foo/shlib.input2.o\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, SourceSetDataDeps) {
+ Err err;
+ TestWithScope setup;
+
+ // This target is a data (runtime) dependency of the intermediate target.
+ Target data(setup.settings(), Label(SourceDir("//foo/"), "data_target"));
+ data.set_output_type(Target::EXECUTABLE);
+ data.visibility().SetPublic();
+ data.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(data.OnResolved(&err));
+
+ // Intermediate source set target.
+ Target inter(setup.settings(), Label(SourceDir("//foo/"), "inter"));
+ inter.set_output_type(Target::SOURCE_SET);
+ inter.visibility().SetPublic();
+ inter.data_deps().push_back(LabelTargetPair(&data));
+ inter.SetToolchain(setup.toolchain());
+ inter.sources().push_back(SourceFile("//foo/inter.cc"));
+ inter.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
+
+ // Write out the intermediate target.
+ std::ostringstream inter_out;
+ NinjaCBinaryTargetWriter inter_writer(&inter, inter_out);
+ inter_writer.Run();
+
+ // The intermediate source set will be a stamp file that depends on the
+ // object files, and will have an order-only dependency on its data dep and
+ // data file.
+ const char inter_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = inter\n"
+ "\n"
+ "build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
+ "\n"
+ "build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
+ "./data_target\n";
+ EXPECT_EQ(inter_expected, inter_out.str());
+
+ // Final target.
+ Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
+ exe.set_output_type(Target::EXECUTABLE);
+ exe.public_deps().push_back(LabelTargetPair(&inter));
+ exe.SetToolchain(setup.toolchain());
+ exe.sources().push_back(SourceFile("//foo/final.cc"));
+ exe.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(exe.OnResolved(&err));
+
+ std::ostringstream final_out;
+ NinjaCBinaryTargetWriter final_writer(&exe, final_out);
+ final_writer.Run();
+
+ // The final output depends on both object files (one from the final target,
+ // one from the source set) and has an order-only dependency on the source
+ // set's stamp file and the final target's data file. The source set stamp
+ // dependency will create an implicit order-only dependency on the data
+ // target.
+ const char final_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
+ "\n"
+ "build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
+ "obj/foo/inter.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ EXPECT_EQ(final_expected, final_out.str());
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, SharedLibraryModuleDefinitionFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target shared_lib(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ shared_lib.set_output_type(Target::SHARED_LIBRARY);
+ shared_lib.SetToolchain(setup.toolchain());
+ shared_lib.sources().push_back(SourceFile("//foo/sources.cc"));
+ shared_lib.sources().push_back(SourceFile("//foo/bar.def"));
+ shared_lib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ shared_lib.source_types_used().Set(SourceFile::SOURCE_DEF);
+ ASSERT_TRUE(shared_lib.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&shared_lib, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+ "\n"
+ "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
+ " ldflags = /DEF:../../foo/bar.def\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+ EXPECT_EQ(expected, out.str());
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, LoadableModule) {
+ Err err;
+ TestWithScope setup;
+
+ Target loadable_module(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ loadable_module.set_output_type(Target::LOADABLE_MODULE);
+ loadable_module.visibility().SetPublic();
+ loadable_module.SetToolchain(setup.toolchain());
+ loadable_module.sources().push_back(SourceFile("//foo/sources.cc"));
+ loadable_module.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&loadable_module, out);
+ writer.Run();
+
+ const char loadable_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+ "\n"
+ "build ./libbar.so: solink_module obj/foo/libbar.sources.o\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+ EXPECT_EQ(loadable_expected, out.str());
+
+ // Final target.
+ Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
+ exe.set_output_type(Target::EXECUTABLE);
+ exe.public_deps().push_back(LabelTargetPair(&loadable_module));
+ exe.SetToolchain(setup.toolchain());
+ exe.sources().push_back(SourceFile("//foo/final.cc"));
+ exe.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
+
+ std::ostringstream final_out;
+ NinjaCBinaryTargetWriter final_writer(&exe, final_out);
+ final_writer.Run();
+
+ // The final output depends on the loadable module so should have an
+ // order-only dependency on the loadable modules's output file.
+ const char final_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
+ "\n"
+ "build ./exe: link obj/foo/exe.final.o || ./libbar.so\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ EXPECT_EQ(final_expected, final_out.str());
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, WinPrecompiledHeaders) {
+ Err err;
+
+ // This setup's toolchain does not have precompiled headers defined.
+ TestWithScope setup;
+
+ // A precompiled header toolchain.
+ Settings pch_settings(setup.build_settings(), "withpch/");
+ Toolchain pch_toolchain(&pch_settings,
+ Label(SourceDir("//toolchain/"), "withpch"));
+ pch_settings.set_toolchain_label(pch_toolchain.label());
+ pch_settings.set_default_toolchain_label(setup.toolchain()->label());
+
+ // Declare a C++ compiler that supports PCH.
+ std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cxx));
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
+ TestWithScope::SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool);
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cc));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // This target doesn't specify precompiled headers.
+ {
+ Target no_pch_target(&pch_settings,
+ Label(SourceDir("//foo/"), "no_pch_target"));
+ no_pch_target.set_output_type(Target::SOURCE_SET);
+ no_pch_target.visibility().SetPublic();
+ no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ no_pch_target.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&no_pch_target, out);
+ writer.Run();
+
+ const char no_pch_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_c = -std=c99\n"
+ "cflags_cc =\n"
+ "target_output_name = no_pch_target\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.input1.o: "
+ "withpch_cxx ../../foo/input1.cc\n"
+ "build withpch/obj/foo/no_pch_target.input2.o: "
+ "withpch_cc ../../foo/input2.c\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.stamp: "
+ "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
+ "withpch/obj/foo/no_pch_target.input2.o\n";
+ EXPECT_EQ(no_pch_expected, out.str());
+ }
+
+ // This target specifies PCH.
+ {
+ Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
+ pch_target.config_values().set_precompiled_header("build/precompile.h");
+ pch_target.config_values().set_precompiled_source(
+ SourceFile("//build/precompile.cc"));
+ pch_target.set_output_type(Target::SOURCE_SET);
+ pch_target.visibility().SetPublic();
+ pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&pch_target, out);
+ writer.Run();
+
+ const char pch_win_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ // It should output language-specific pch files.
+ "cflags_c = /Fpwithpch/obj/foo/pch_target_c.pch "
+ "/Yubuild/precompile.h\n"
+ "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
+ "/Yubuild/precompile.h\n"
+ "target_output_name = pch_target\n"
+ "\n"
+ // Compile the precompiled source files with /Yc.
+ "build withpch/obj/build/pch_target.precompile.c.o: "
+ "withpch_cc ../../build/precompile.cc\n"
+ " cflags_c = ${cflags_c} /Ycbuild/precompile.h\n"
+ "\n"
+ "build withpch/obj/build/pch_target.precompile.cc.o: "
+ "withpch_cxx ../../build/precompile.cc\n"
+ " cflags_cc = ${cflags_cc} /Ycbuild/precompile.h\n"
+ "\n"
+ "build withpch/obj/foo/pch_target.input1.o: "
+ "withpch_cxx ../../foo/input1.cc | "
+ // Explicit dependency on the PCH build step.
+ "withpch/obj/build/pch_target.precompile.cc.o\n"
+ "build withpch/obj/foo/pch_target.input2.o: "
+ "withpch_cc ../../foo/input2.c | "
+ // Explicit dependency on the PCH build step.
+ "withpch/obj/build/pch_target.precompile.c.o\n"
+ "\n"
+ "build withpch/obj/foo/pch_target.stamp: withpch_stamp "
+ "withpch/obj/foo/pch_target.input1.o "
+ "withpch/obj/foo/pch_target.input2.o "
+ // The precompiled object files were added to the outputs.
+ "withpch/obj/build/pch_target.precompile.c.o "
+ "withpch/obj/build/pch_target.precompile.cc.o\n";
+ EXPECT_EQ(pch_win_expected, out.str());
+ }
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, GCCPrecompiledHeaders) {
+ Err err;
+
+ // This setup's toolchain does not have precompiled headers defined.
+ TestWithScope setup;
+
+ // A precompiled header toolchain.
+ Settings pch_settings(setup.build_settings(), "withpch/");
+ Toolchain pch_toolchain(&pch_settings,
+ Label(SourceDir("//toolchain/"), "withpch"));
+ pch_settings.set_toolchain_label(pch_toolchain.label());
+ pch_settings.set_default_toolchain_label(setup.toolchain()->label());
+
+ // Declare a C++ compiler that supports PCH.
+ std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cxx));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
+ TestWithScope::SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool);
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cc));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // This target doesn't specify precompiled headers.
+ {
+ Target no_pch_target(&pch_settings,
+ Label(SourceDir("//foo/"), "no_pch_target"));
+ no_pch_target.set_output_type(Target::SOURCE_SET);
+ no_pch_target.visibility().SetPublic();
+ no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ no_pch_target.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&no_pch_target, out);
+ writer.Run();
+
+ const char no_pch_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_c = -std=c99\n"
+ "cflags_cc =\n"
+ "target_output_name = no_pch_target\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.input1.o: "
+ "withpch_cxx ../../foo/input1.cc\n"
+ "build withpch/obj/foo/no_pch_target.input2.o: "
+ "withpch_cc ../../foo/input2.c\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.stamp: "
+ "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
+ "withpch/obj/foo/no_pch_target.input2.o\n";
+ EXPECT_EQ(no_pch_expected, out.str());
+ }
+
+ // This target specifies PCH.
+ {
+ Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
+ pch_target.config_values().set_precompiled_source(
+ SourceFile("//build/precompile.h"));
+ pch_target.config_values().cflags_c().push_back("-std=c99");
+ pch_target.set_output_type(Target::SOURCE_SET);
+ pch_target.visibility().SetPublic();
+ pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&pch_target, out);
+ writer.Run();
+
+ const char pch_gcc_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_c = -std=c99 "
+ "-include withpch/obj/build/pch_target.precompile.h-c\n"
+ "cflags_cc = -include withpch/obj/build/pch_target.precompile.h-cc\n"
+ "target_output_name = pch_target\n"
+ "\n"
+ // Compile the precompiled sources with -x <lang>.
+ "build withpch/obj/build/pch_target.precompile.h-c.gch: "
+ "withpch_cc ../../build/precompile.h\n"
+ " cflags_c = -std=c99 -x c-header\n"
+ "\n"
+ "build withpch/obj/build/pch_target.precompile.h-cc.gch: "
+ "withpch_cxx ../../build/precompile.h\n"
+ " cflags_cc = -x c++-header\n"
+ "\n"
+ "build withpch/obj/foo/pch_target.input1.o: "
+ "withpch_cxx ../../foo/input1.cc | "
+ // Explicit dependency on the PCH build step.
+ "withpch/obj/build/pch_target.precompile.h-cc.gch\n"
+ "build withpch/obj/foo/pch_target.input2.o: "
+ "withpch_cc ../../foo/input2.c | "
+ // Explicit dependency on the PCH build step.
+ "withpch/obj/build/pch_target.precompile.h-c.gch\n"
+ "\n"
+ "build withpch/obj/foo/pch_target.stamp: "
+ "withpch_stamp withpch/obj/foo/pch_target.input1.o "
+ "withpch/obj/foo/pch_target.input2.o\n";
+ EXPECT_EQ(pch_gcc_expected, out.str());
+ }
+}
+
+// Should throw an error with the scheduler if a duplicate object file exists.
+// This is dependent on the toolchain's object file mapping.
+TEST_F(NinjaCBinaryTargetWriterTest, DupeObjFileError) {
+ TestWithScope setup;
+ TestTarget target(setup, "//foo:bar", Target::EXECUTABLE);
+ target.sources().push_back(SourceFile("//a.cc"));
+ target.sources().push_back(SourceFile("//a.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+
+ EXPECT_FALSE(scheduler().is_failed());
+
+ scheduler().SuppressOutputForTesting(true);
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ scheduler().SuppressOutputForTesting(false);
+
+ // Should have issued an error.
+ EXPECT_TRUE(scheduler().is_failed());
+}
+
+// This tests that output extension and output dir overrides apply, and input
+// dependencies are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
+ Err err;
+ TestWithScope setup;
+
+ // This target has one input.
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
+ " | ../../foo/input.data\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
+ " | ../../foo/input.data\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o\n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+
+ // This target has one input but no source files.
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.visibility().SetPublic();
+ target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "\n"
+ "build ./libbar.so: solink | ../../foo/input.data\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+
+ // This target has multiple inputs.
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input2.data"));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.inputs.stamp: stamp"
+ " ../../foo/input1.data ../../foo/input2.data\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o\n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+
+ // This target has one input itself, one from an immediate config, and one
+ // from a config tacked on to said config.
+ {
+ Config far_config(setup.settings(), Label(SourceDir("//foo/"), "qux"));
+ far_config.own_values().inputs().push_back(SourceFile("//foo/input3.data"));
+ ASSERT_TRUE(far_config.OnResolved(&err));
+
+ Config config(setup.settings(), Label(SourceDir("//foo/"), "baz"));
+ config.visibility().SetPublic();
+ config.own_values().inputs().push_back(SourceFile("//foo/input2.data"));
+ config.configs().push_back(LabelConfigPair(&far_config));
+ ASSERT_TRUE(config.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
+ target.configs().push_back(LabelConfigPair(&config));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.inputs.stamp: stamp"
+ " ../../foo/input1.data ../../foo/input2.data ../../foo/input3.data\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o\n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+}
+
+// Test linking of Rust dependencies into C targets.
+TEST_F(NinjaCBinaryTargetWriterTest, RustDeps) {
+ Err err;
+ TestWithScope setup;
+
+ {
+ Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ library_target.set_output_type(Target::STATIC_LIBRARY);
+ library_target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ library_target.sources().push_back(lib);
+ library_target.source_types_used().Set(SourceFile::SOURCE_RS);
+ library_target.rust_values().set_crate_root(lib);
+ library_target.rust_values().crate_name() = "foo";
+ library_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(library_target.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//bar/bar.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&library_target));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
+ "\n"
+ "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ {
+ Target rlib_target(setup.settings(), Label(SourceDir("//baz/"), "lib"));
+ rlib_target.set_output_type(Target::RUST_LIBRARY);
+ rlib_target.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ rlib_target.sources().push_back(bazlib);
+ rlib_target.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib_target.rust_values().set_crate_root(bazlib);
+ rlib_target.rust_values().crate_name() = "lib";
+ rlib_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib_target.OnResolved(&err));
+
+ Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ library_target.set_output_type(Target::STATIC_LIBRARY);
+ library_target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ library_target.sources().push_back(lib);
+ library_target.source_types_used().Set(SourceFile::SOURCE_RS);
+ library_target.rust_values().set_crate_root(lib);
+ library_target.rust_values().crate_name() = "foo";
+ library_target.public_deps().push_back(LabelTargetPair(&rlib_target));
+ library_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(library_target.OnResolved(&err));
+
+ Target rlib_target2(setup.settings(), Label(SourceDir("//qux/"), "lib2"));
+ rlib_target2.set_output_type(Target::RUST_LIBRARY);
+ rlib_target2.visibility().SetPublic();
+ SourceFile quxlib("//qux/lib.rs");
+ rlib_target2.sources().push_back(quxlib);
+ rlib_target2.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib_target2.rust_values().set_crate_root(quxlib);
+ rlib_target2.rust_values().crate_name() = "lib2";
+ rlib_target2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib_target2.OnResolved(&err));
+
+ Target rlib_target3(setup.settings(), Label(SourceDir("//quxqux/"), "lib3"));
+ rlib_target3.set_output_type(Target::RUST_LIBRARY);
+ rlib_target3.visibility().SetPublic();
+ SourceFile quxquxlib("//quxqux/lib.rs");
+ rlib_target3.sources().push_back(quxquxlib);
+ rlib_target3.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib_target3.rust_values().set_crate_root(quxlib);
+ rlib_target3.rust_values().crate_name() = "lib3";
+ rlib_target3.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib_target3.OnResolved(&err));
+
+ Target procmacro(setup.settings(),
+ Label(SourceDir("//quuxmacro/"), "procmacro"));
+ procmacro.set_output_type(Target::RUST_PROC_MACRO);
+ procmacro.visibility().SetPublic();
+ SourceFile procmacrolib("//procmacro/lib.rs");
+ procmacro.sources().push_back(procmacrolib);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.public_deps().push_back(LabelTargetPair(&rlib_target2));
+ procmacro.public_deps().push_back(LabelTargetPair(&rlib_target3));
+ procmacro.rust_values().set_crate_root(procmacrolib);
+ procmacro.rust_values().crate_name() = "procmacro";
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ Target rlib_target4(setup.settings(), Label(SourceDir("//quux/"), "lib4"));
+ rlib_target4.set_output_type(Target::RUST_LIBRARY);
+ rlib_target4.visibility().SetPublic();
+ SourceFile quuxlib("//quux/lib.rs");
+ rlib_target4.sources().push_back(quuxlib);
+ rlib_target4.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib_target4.public_deps().push_back(LabelTargetPair(&rlib_target2));
+ // Transitive proc macros should not impact C++ targets; we're
+ // adding one to ensure the ninja instructions below are unaffected.
+ rlib_target4.public_deps().push_back(LabelTargetPair(&procmacro));
+ rlib_target4.rust_values().set_crate_root(quuxlib);
+ rlib_target4.rust_values().crate_name() = "lib4";
+ rlib_target4.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib_target4.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//bar/bar.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&library_target));
+ target.private_deps().push_back(LabelTargetPair(&rlib_target4));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
+ "\n"
+ "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a | "
+ "obj/baz/lib.rlib obj/quux/lib4.rlib obj/qux/lib2.rlib\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/baz/lib.rlib obj/quux/lib4.rlib obj/qux/lib2.rlib\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ {
+ Target procmacro(setup.settings(), Label(SourceDir("//baz/"), "macro"));
+ procmacro.set_output_type(Target::LOADABLE_MODULE);
+ procmacro.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ procmacro.sources().push_back(bazlib);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.rust_values().set_crate_root(bazlib);
+ procmacro.rust_values().crate_name() = "macro";
+ procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ library_target.set_output_type(Target::STATIC_LIBRARY);
+ library_target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ library_target.sources().push_back(lib);
+ library_target.source_types_used().Set(SourceFile::SOURCE_RS);
+ library_target.rust_values().set_crate_root(lib);
+ library_target.rust_values().crate_name() = "foo";
+ library_target.public_deps().push_back(LabelTargetPair(&procmacro));
+ library_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(library_target.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//bar/bar.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&library_target));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
+ "\n"
+ "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, ModuleMapInStaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ std::unique_ptr<Tool> cxx_module_tool =
+ Tool::CreateTool(CTool::kCToolCxxModule);
+ cxx_module_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.pcm"));
+ setup.toolchain()->SetTool(std::move(cxx_module_tool));
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/bar.cc"));
+ target.sources().push_back(SourceFile("//foo/bar.modulemap"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.bar.o: cxx ../../foo/bar.cc | obj/foo/libbar.bar.pcm\n"
+ "build obj/foo/libbar.bar.pcm: cxx_module ../../foo/bar.modulemap\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.bar.o\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+// Test linking of targets containing Swift modules.
+TEST_F(NinjaCBinaryTargetWriterTest, SwiftModule) {
+ Err err;
+ TestWithScope setup;
+
+ // A single Swift module.
+ Target foo_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ foo_target.set_output_type(Target::SOURCE_SET);
+ foo_target.visibility().SetPublic();
+ foo_target.sources().push_back(SourceFile("//foo/file1.swift"));
+ foo_target.sources().push_back(SourceFile("//foo/file2.swift"));
+ foo_target.source_types_used().Set(SourceFile::SOURCE_SWIFT);
+ foo_target.swift_values().module_name() = "Foo";
+ foo_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(foo_target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&foo_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "module_name = Foo\n"
+ "module_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = foo\n"
+ "\n"
+ "build obj/foo/Foo.swiftmodule: swift"
+ " ../../foo/file1.swift ../../foo/file2.swift\n"
+ "\n"
+ "build obj/foo/file1.o obj/foo/file2.o: stamp obj/foo/Foo.swiftmodule\n"
+ "\n"
+ "build obj/foo/foo.stamp: stamp"
+ " obj/foo/file1.o obj/foo/file2.o\n";
+
+ const std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // Swift module_dirs correctly set if dependency between Swift modules.
+ {
+ Target bar_target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ bar_target.set_output_type(Target::SOURCE_SET);
+ bar_target.visibility().SetPublic();
+ bar_target.sources().push_back(SourceFile("//bar/bar.swift"));
+ bar_target.source_types_used().Set(SourceFile::SOURCE_SWIFT);
+ bar_target.swift_values().module_name() = "Bar";
+ bar_target.private_deps().push_back(LabelTargetPair(&foo_target));
+ bar_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(bar_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&bar_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "module_name = Bar\n"
+ "module_dirs = -Iobj/foo\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/Bar.swiftmodule: swift ../../bar/bar.swift"
+ " || obj/foo/foo.stamp\n"
+ "\n"
+ "build obj/bar/bar.o: stamp obj/bar/Bar.swiftmodule"
+ " || obj/foo/foo.stamp\n"
+ "\n"
+ "build obj/bar/bar.stamp: stamp obj/bar/bar.o "
+ "|| obj/foo/foo.stamp\n";
+
+ const std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // Swift module_dirs correctly set if dependency between Swift modules,
+ // even if the dependency is indirect (via public_deps).
+ {
+ Target group(setup.settings(), Label(SourceDir("//bar/"), "group"));
+ group.set_output_type(Target::GROUP);
+ group.visibility().SetPublic();
+ group.public_deps().push_back(LabelTargetPair(&foo_target));
+ group.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(group.OnResolved(&err));
+
+ Target bar_target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ bar_target.set_output_type(Target::SOURCE_SET);
+ bar_target.visibility().SetPublic();
+ bar_target.sources().push_back(SourceFile("//bar/bar.swift"));
+ bar_target.source_types_used().Set(SourceFile::SOURCE_SWIFT);
+ bar_target.swift_values().module_name() = "Bar";
+ bar_target.private_deps().push_back(LabelTargetPair(&group));
+ bar_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(bar_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&bar_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "module_name = Bar\n"
+ "module_dirs = -Iobj/foo\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/Bar.swiftmodule: swift ../../bar/bar.swift"
+ " || obj/foo/foo.stamp\n"
+ "\n"
+ "build obj/bar/bar.o: stamp obj/bar/Bar.swiftmodule"
+ " || obj/foo/foo.stamp\n"
+ "\n"
+ "build obj/bar/bar.stamp: stamp obj/bar/bar.o "
+ "|| obj/bar/group.stamp obj/foo/foo.stamp\n";
+
+ const std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ // C target links with module.
+ {
+ Target bar_target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ bar_target.set_output_type(Target::EXECUTABLE);
+ bar_target.visibility().SetPublic();
+ bar_target.private_deps().push_back(LabelTargetPair(&foo_target));
+ bar_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(bar_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&bar_target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "\n"
+ "build ./bar: link obj/foo/file1.o obj/foo/file2.o "
+ "| obj/foo/Foo.swiftmodule "
+ "|| obj/foo/foo.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules = obj/foo/Foo.swiftmodule\n"
+ " output_extension = \n"
+ " output_dir = \n";
+
+ const std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, DependOnModule) {
+ TestWithScope setup;
+ Err err;
+
+ // There's no cxx_module or flags in the test toolchain, set up a
+ // custom one here.
+ Settings module_settings(setup.build_settings(), "withmodules/");
+ Toolchain module_toolchain(&module_settings,
+ Label(SourceDir("//toolchain/"), "withmodules"));
+ module_settings.set_toolchain_label(module_toolchain.label());
+ module_settings.set_default_toolchain_label(module_toolchain.label());
+
+ std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{module_deps}} "
+ "{{defines}} {{include_dirs}} -o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ module_toolchain.SetTool(std::move(cxx));
+
+ std::unique_ptr<Tool> cxx_module_tool =
+ Tool::CreateTool(CTool::kCToolCxxModule);
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{module_deps_no_self}} "
+ "{{defines}} {{include_dirs}} -fmodule-name={{label}} -c -x c++ "
+ "-Xclang -emit-module -o {{output}}",
+ cxx_module_tool.get());
+ cxx_module_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.pcm"));
+ module_toolchain.SetTool(std::move(cxx_module_tool));
+
+ std::unique_ptr<Tool> alink = Tool::CreateTool(CTool::kCToolAlink);
+ CTool* alink_tool = alink->AsC();
+ TestWithScope::SetCommandForTool("ar {{output}} {{source}}", alink_tool);
+ alink_tool->set_lib_switch("-l");
+ alink_tool->set_lib_dir_switch("-L");
+ alink_tool->set_output_prefix("lib");
+ alink_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{target_output_name}}.a"));
+ module_toolchain.SetTool(std::move(alink));
+
+ std::unique_ptr<Tool> link = Tool::CreateTool(CTool::kCToolLink);
+ CTool* link_tool = link->AsC();
+ TestWithScope::SetCommandForTool(
+ "ld -o {{target_output_name}} {{source}} "
+ "{{ldflags}} {{libs}}",
+ link_tool);
+ link_tool->set_lib_switch("-l");
+ link_tool->set_lib_dir_switch("-L");
+ link_tool->set_outputs(
+ SubstitutionList::MakeForTest("{{root_out_dir}}/{{target_output_name}}"));
+ module_toolchain.SetTool(std::move(link));
+
+ module_toolchain.ToolchainSetupComplete();
+
+ Target target(&module_settings, Label(SourceDir("//blah/"), "a"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//blah/a.modulemap"));
+ target.sources().push_back(SourceFile("//blah/a.cc"));
+ target.sources().push_back(SourceFile("//blah/a.h"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
+ target.SetToolchain(&module_toolchain);
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // The library first.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] = R"(defines =
+include_dirs =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
+cflags =
+cflags_cc =
+label = //blah$:a
+root_out_dir = withmodules
+target_out_dir = obj/blah
+target_output_name = liba
+
+build obj/blah/liba.a.pcm: cxx_module ../../blah/a.modulemap
+build obj/blah/liba.a.o: cxx ../../blah/a.cc | obj/blah/liba.a.pcm
+
+build obj/blah/liba.a: alink obj/blah/liba.a.o
+ arflags =
+ output_extension =
+ output_dir =
+)";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target target2(&module_settings, Label(SourceDir("//stuff/"), "b"));
+ target2.set_output_type(Target::STATIC_LIBRARY);
+ target2.visibility().SetPublic();
+ target2.sources().push_back(SourceFile("//stuff/b.modulemap"));
+ target2.sources().push_back(SourceFile("//stuff/b.cc"));
+ target2.sources().push_back(SourceFile("//stuff/b.h"));
+ target2.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target2.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
+ target2.SetToolchain(&module_toolchain);
+ ASSERT_TRUE(target2.OnResolved(&err));
+
+ // A second library to make sure the depender includes both.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target2, out);
+ writer.Run();
+
+ const char expected[] = R"(defines =
+include_dirs =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
+cflags =
+cflags_cc =
+label = //stuff$:b
+root_out_dir = withmodules
+target_out_dir = obj/stuff
+target_output_name = libb
+
+build obj/stuff/libb.b.pcm: cxx_module ../../stuff/b.modulemap
+build obj/stuff/libb.b.o: cxx ../../stuff/b.cc | obj/stuff/libb.b.pcm
+
+build obj/stuff/libb.a: alink obj/stuff/libb.b.o
+ arflags =
+ output_extension =
+ output_dir =
+)";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target target3(&module_settings, Label(SourceDir("//things/"), "c"));
+ target3.set_output_type(Target::STATIC_LIBRARY);
+ target3.visibility().SetPublic();
+ target3.sources().push_back(SourceFile("//stuff/c.modulemap"));
+ target3.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
+ target3.private_deps().push_back(LabelTargetPair(&target));
+ target3.SetToolchain(&module_toolchain);
+ ASSERT_TRUE(target3.OnResolved(&err));
+
+ // A third library that depends on one of the previous static libraries, to
+ // check module_deps_no_self.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target3, out);
+ writer.Run();
+
+ const char expected[] = R"(defines =
+include_dirs =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libc.c.pcm -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
+cflags =
+cflags_cc =
+label = //things$:c
+root_out_dir = withmodules
+target_out_dir = obj/things
+target_output_name = libc
+
+build obj/stuff/libc.c.pcm: cxx_module ../../stuff/c.modulemap | obj/blah/liba.a.pcm
+
+build obj/things/libc.a: alink || obj/blah/liba.a
+ arflags =
+ output_extension =
+ output_dir =
+)";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target depender(&module_settings, Label(SourceDir("//zap/"), "c"));
+ depender.set_output_type(Target::EXECUTABLE);
+ depender.sources().push_back(SourceFile("//zap/x.cc"));
+ depender.sources().push_back(SourceFile("//zap/y.cc"));
+ depender.source_types_used().Set(SourceFile::SOURCE_CPP);
+ depender.private_deps().push_back(LabelTargetPair(&target));
+ depender.private_deps().push_back(LabelTargetPair(&target2));
+ depender.SetToolchain(&module_toolchain);
+ ASSERT_TRUE(depender.OnResolved(&err));
+
+ // Then the executable that depends on it.
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&depender, out);
+ writer.Run();
+
+ const char expected[] = R"(defines =
+include_dirs =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
+cflags =
+cflags_cc =
+label = //zap$:c
+root_out_dir = withmodules
+target_out_dir = obj/zap
+target_output_name = c
+
+build obj/zap/c.x.o: cxx ../../zap/x.cc | obj/blah/liba.a.pcm obj/stuff/libb.b.pcm
+build obj/zap/c.y.o: cxx ../../zap/y.cc | obj/blah/liba.a.pcm obj/stuff/libb.b.pcm
+
+build withmodules/c: link obj/zap/c.x.o obj/zap/c.y.o obj/blah/liba.a obj/stuff/libb.a
+ ldflags =
+ libs =
+ frameworks =
+ swiftmodules =
+ output_extension =
+ output_dir =
+)";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
diff --git a/gn/src/gn/ninja_copy_target_writer.cc b/gn/src/gn/ninja_copy_target_writer.cc
new file mode 100644
index 00000000000..92992236df2
--- /dev/null
+++ b/gn/src/gn/ninja_copy_target_writer.cc
@@ -0,0 +1,124 @@
+// 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 "gn/ninja_copy_target_writer.h"
+
+#include "base/strings/string_util.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_utils.h"
+#include "gn/output_file.h"
+#include "gn/scheduler.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/toolchain.h"
+
+NinjaCopyTargetWriter::NinjaCopyTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+NinjaCopyTargetWriter::~NinjaCopyTargetWriter() = default;
+
+void NinjaCopyTargetWriter::Run() {
+ const Tool* copy_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolCopy);
+ if (!copy_tool) {
+ g_scheduler->FailWithError(Err(
+ nullptr, "Copy tool not defined",
+ "The toolchain " +
+ target_->toolchain()->label().GetUserVisibleName(false) +
+ "\n used by target " + target_->label().GetUserVisibleName(false) +
+ "\n doesn't define a \"copy\" tool."));
+ return;
+ }
+
+ const Tool* stamp_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolStamp);
+ if (!stamp_tool) {
+ g_scheduler->FailWithError(Err(
+ nullptr, "Copy tool not defined",
+ "The toolchain " +
+ target_->toolchain()->label().GetUserVisibleName(false) +
+ "\n used by target " + target_->label().GetUserVisibleName(false) +
+ "\n doesn't define a \"stamp\" tool."));
+ return;
+ }
+
+ // Figure out the substitutions used by the copy and stamp tools.
+ SubstitutionBits required_bits = copy_tool->substitution_bits();
+ required_bits.MergeFrom(stamp_tool->substitution_bits());
+
+ // General target-related substitutions needed by both tools.
+ WriteSharedVars(required_bits);
+
+ std::vector<OutputFile> output_files;
+ WriteCopyRules(&output_files);
+ out_ << std::endl;
+ WriteStampForTarget(output_files, std::vector<OutputFile>());
+}
+
+void NinjaCopyTargetWriter::WriteCopyRules(
+ std::vector<OutputFile>* output_files) {
+ CHECK(target_->action_values().outputs().list().size() == 1);
+ const SubstitutionList& output_subst_list =
+ target_->action_values().outputs();
+ CHECK_EQ(1u, output_subst_list.list().size())
+ << "Should have one entry exactly.";
+ const SubstitutionPattern& output_subst = output_subst_list.list()[0];
+
+ std::string tool_name = GetNinjaRulePrefixForToolchain(settings_) +
+ GeneralTool::kGeneralToolCopy;
+
+ size_t num_stamp_uses = target_->sources().size();
+ std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), num_stamp_uses);
+
+ std::vector<OutputFile> data_outs;
+ for (const auto& dep : target_->data_deps())
+ data_outs.push_back(dep.ptr->dependency_output_file());
+
+ // Note that we don't write implicit deps for copy steps. "copy" only
+ // depends on the output files themselves, rather than having includes
+ // (the possibility of generated #includes is the main reason for implicit
+ // dependencies).
+ //
+ // It would seem that specifying implicit dependencies on the deps of the
+ // copy command would still be harmless. But Chrome implements copy tools
+ // as hard links (much faster) which don't change the timestamp. If the
+ // ninja rule looks like this:
+ // output: copy input | foo.stamp
+ // The copy will not make a new timestamp on the output file, but the
+ // foo.stamp file generated from a previous step will have a new timestamp.
+ // The copy rule will therefore look out-of-date to Ninja and the rule will
+ // get rebuilt.
+ //
+ // If this copy is copying a generated file, not listing the implicit
+ // dependency will be fine as long as the input to the copy is properly
+ // listed as the output from the step that generated it.
+ //
+ // Moreover, doing this assumes that the copy step is always a simple
+ // locally run command, so there is no need for a toolchain dependency.
+ //
+ // Note that there is the need in some cases for order-only dependencies
+ // where a command might need to make sure something else runs before it runs
+ // to avoid conflicts. This is also needed for data_deps on a copy target.
+ // Such cases should be avoided where possible, but sometimes that's not
+ // possible.
+ for (const auto& input_file : target_->sources()) {
+ OutputFile output_file =
+ SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
+ target_, target_->settings(), output_subst, input_file);
+ output_files->push_back(output_file);
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, output_file);
+ out_ << ": " << tool_name << " ";
+ path_output_.WriteFile(out_, input_file);
+ if (!input_deps.empty() || !data_outs.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, input_deps);
+ path_output_.WriteFiles(out_, data_outs);
+ }
+ out_ << std::endl;
+ }
+}
diff --git a/gn/src/gn/ninja_copy_target_writer.h b/gn/src/gn/ninja_copy_target_writer.h
new file mode 100644
index 00000000000..c4919c677fd
--- /dev/null
+++ b/gn/src/gn/ninja_copy_target_writer.h
@@ -0,0 +1,27 @@
+// 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 TOOLS_GN_NINJA_COPY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_COPY_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/ninja_target_writer.h"
+
+// Writes a .ninja file for a copy target type.
+class NinjaCopyTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaCopyTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaCopyTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ // Writes the rules top copy the file(s), putting the computed output file
+ // name(s) into the given vector.
+ void WriteCopyRules(std::vector<OutputFile>* output_files);
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaCopyTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_COPY_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_copy_target_writer_unittest.cc b/gn/src/gn/ninja_copy_target_writer_unittest.cc
new file mode 100644
index 00000000000..f641ffa9ec3
--- /dev/null
+++ b/gn/src/gn/ninja_copy_target_writer_unittest.cc
@@ -0,0 +1,126 @@
+// 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 <algorithm>
+#include <sstream>
+
+#include "gn/ninja_copy_target_writer.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+// Tests multiple files with an output pattern and no toolchain dependency.
+TEST(NinjaCopyTargetWriter, Run) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::COPY_FILES);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.sources().push_back(SourceFile("//foo/input2.txt"));
+
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCopyTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "build input1.out: copy ../../foo/input1.txt\n"
+ "build input2.out: copy ../../foo/input2.txt\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected_linux, out_str);
+}
+
+// Tests a single file with no output pattern.
+TEST(NinjaCopyTargetWriter, ToolchainDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::COPY_FILES);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/output.out");
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCopyTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "build output.out: copy ../../foo/input1.txt\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp output.out\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected_linux, out_str);
+}
+
+TEST(NinjaCopyTargetWriter, OrderOnlyDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::COPY_FILES);
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+ target.config_values().inputs().push_back(SourceFile("//foo/script.py"));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCopyTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "build input1.out: copy ../../foo/input1.txt || ../../foo/script.py\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp input1.out\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected_linux, out_str);
+}
+
+TEST(NinjaCopyTargetWriter, DataDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::COPY_FILES);
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ Target data_dep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
+ data_dep.set_output_type(Target::ACTION);
+ data_dep.visibility().SetPublic();
+ data_dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(data_dep.OnResolved(&err));
+
+ target.data_deps().push_back(LabelTargetPair(&data_dep));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCopyTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected_linux[] =
+ "build input1.out: copy ../../foo/input1.txt || obj/foo/datadep.stamp\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp input1.out\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected_linux, out_str);
+}
diff --git a/gn/src/gn/ninja_create_bundle_target_writer.cc b/gn/src/gn/ninja_create_bundle_target_writer.cc
new file mode 100644
index 00000000000..9f000f319b9
--- /dev/null
+++ b/gn/src/gn/ninja_create_bundle_target_writer.cc
@@ -0,0 +1,373 @@
+// Copyright 2016 The Chromium 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 "gn/ninja_create_bundle_target_writer.h"
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "gn/filesystem_utils.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_utils.h"
+#include "gn/output_file.h"
+#include "gn/scheduler.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/toolchain.h"
+
+namespace {
+
+bool TargetRequireAssetCatalogCompilation(const Target* target) {
+ return !target->bundle_data().assets_catalog_sources().empty() ||
+ !target->bundle_data().partial_info_plist().is_null();
+}
+
+void FailWithMissingToolError(const char* tool_name, const Target* target) {
+ g_scheduler->FailWithError(
+ Err(nullptr, std::string(tool_name) + " tool not defined",
+ "The toolchain " +
+ target->toolchain()->label().GetUserVisibleName(false) +
+ "\n"
+ "used by target " +
+ target->label().GetUserVisibleName(false) +
+ "\n"
+ "doesn't define a \"" +
+ tool_name + "\" tool."));
+}
+
+bool EnsureAllToolsAvailable(const Target* target) {
+ const char* kRequiredTools[] = {
+ GeneralTool::kGeneralToolCopyBundleData,
+ GeneralTool::kGeneralToolStamp,
+ };
+
+ for (size_t i = 0; i < std::size(kRequiredTools); ++i) {
+ if (!target->toolchain()->GetTool(kRequiredTools[i])) {
+ FailWithMissingToolError(kRequiredTools[i], target);
+ return false;
+ }
+ }
+
+ // The compile_xcassets tool is only required if the target has asset
+ // catalog resources to compile.
+ if (TargetRequireAssetCatalogCompilation(target)) {
+ if (!target->toolchain()->GetTool(
+ GeneralTool::kGeneralToolCompileXCAssets)) {
+ FailWithMissingToolError(GeneralTool::kGeneralToolCompileXCAssets,
+ target);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+NinjaCreateBundleTargetWriter::NinjaCreateBundleTargetWriter(
+ const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+NinjaCreateBundleTargetWriter::~NinjaCreateBundleTargetWriter() = default;
+
+void NinjaCreateBundleTargetWriter::Run() {
+ if (!EnsureAllToolsAvailable(target_))
+ return;
+
+ // Stamp users are CopyBundleData, CompileAssetsCatalog, CodeSigning and
+ // StampForTarget.
+ size_t num_stamp_uses = 4;
+ std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), num_stamp_uses);
+
+ std::string code_signing_rule_name = WriteCodeSigningRuleDefinition();
+
+ std::vector<OutputFile> output_files;
+ WriteCopyBundleDataSteps(order_only_deps, &output_files);
+ WriteCompileAssetsCatalogStep(order_only_deps, &output_files);
+ WriteCodeSigningStep(code_signing_rule_name, order_only_deps, &output_files);
+
+ for (const auto& pair : target_->data_deps())
+ order_only_deps.push_back(pair.ptr->dependency_output_file());
+ WriteStampForTarget(output_files, order_only_deps);
+
+ // Write a phony target for the outer bundle directory. This allows other
+ // targets to treat the entire bundle as a single unit, even though it is
+ // a directory, so that it can be depended upon as a discrete build edge.
+ out_ << "build ";
+ path_output_.WriteFile(
+ out_,
+ OutputFile(settings_->build_settings(),
+ target_->bundle_data().GetBundleRootDirOutput(settings_)));
+ out_ << ": phony " << target_->dependency_output_file().value();
+ out_ << std::endl;
+}
+
+std::string NinjaCreateBundleTargetWriter::WriteCodeSigningRuleDefinition() {
+ if (target_->bundle_data().code_signing_script().is_null())
+ return std::string();
+
+ std::string target_label = target_->label().GetUserVisibleName(true);
+ std::string custom_rule_name(target_label);
+ base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
+ custom_rule_name.append("_code_signing_rule");
+
+ out_ << "rule " << custom_rule_name << std::endl;
+ out_ << " command = ";
+ path_output_.WriteFile(out_, settings_->build_settings()->python_path());
+ out_ << " ";
+ path_output_.WriteFile(out_, target_->bundle_data().code_signing_script());
+
+ const SubstitutionList& args = target_->bundle_data().code_signing_args();
+ EscapeOptions args_escape_options;
+ args_escape_options.mode = ESCAPE_NINJA_COMMAND;
+
+ for (const auto& arg : args.list()) {
+ out_ << " ";
+ SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
+ }
+ out_ << std::endl;
+ out_ << " description = CODE SIGNING " << target_label << std::endl;
+ out_ << " restat = 1" << std::endl;
+ out_ << std::endl;
+
+ return custom_rule_name;
+}
+
+void NinjaCreateBundleTargetWriter::WriteCopyBundleDataSteps(
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files) {
+ for (const BundleFileRule& file_rule : target_->bundle_data().file_rules())
+ WriteCopyBundleFileRuleSteps(file_rule, order_only_deps, output_files);
+}
+
+void NinjaCreateBundleTargetWriter::WriteCopyBundleFileRuleSteps(
+ const BundleFileRule& file_rule,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files) {
+ // Note that we don't write implicit deps for copy steps. "copy_bundle_data"
+ // steps as this is most likely implemented using hardlink in the common case.
+ // See NinjaCopyTargetWriter::WriteCopyRules() for a detailed explanation.
+ for (const SourceFile& source_file : file_rule.sources()) {
+ // There is no need to check for errors here as the substitution will have
+ // been performed when computing the list of output of the target during
+ // the Target::OnResolved phase earlier.
+ OutputFile expanded_output_file;
+ file_rule.ApplyPatternToSourceAsOutputFile(
+ settings_, target_, target_->bundle_data(), source_file,
+ &expanded_output_file,
+ /*err=*/nullptr);
+ output_files->push_back(expanded_output_file);
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, expanded_output_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolCopyBundleData << " ";
+ path_output_.WriteFile(out_, source_file);
+
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+
+ out_ << std::endl;
+ }
+}
+
+void NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogStep(
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files) {
+ if (!TargetRequireAssetCatalogCompilation(target_))
+ return;
+
+ OutputFile compiled_catalog;
+ if (!target_->bundle_data().assets_catalog_sources().empty()) {
+ compiled_catalog =
+ OutputFile(settings_->build_settings(),
+ target_->bundle_data().GetCompiledAssetCatalogPath());
+ output_files->push_back(compiled_catalog);
+ }
+
+ OutputFile partial_info_plist;
+ if (!target_->bundle_data().partial_info_plist().is_null()) {
+ partial_info_plist =
+ OutputFile(settings_->build_settings(),
+ target_->bundle_data().partial_info_plist());
+
+ output_files->push_back(partial_info_plist);
+ }
+
+ // If there are no asset catalog to compile but the "partial_info_plist" is
+ // non-empty, then add a target to generate an empty file (to avoid breaking
+ // code that depends on this file existence).
+ if (target_->bundle_data().assets_catalog_sources().empty()) {
+ DCHECK(!target_->bundle_data().partial_info_plist().is_null());
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, partial_info_plist);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolStamp;
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+ out_ << std::endl;
+ return;
+ }
+
+ OutputFile input_dep = WriteCompileAssetsCatalogInputDepsStamp(
+ target_->bundle_data().assets_catalog_deps());
+ DCHECK(!input_dep.value().empty());
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, compiled_catalog);
+ if (partial_info_plist != OutputFile()) {
+ // If "partial_info_plist" is non-empty, then add it to list of implicit
+ // outputs of the asset catalog compilation, so that target can use it
+ // without getting the ninja error "'foo', needed by 'bar', missing and
+ // no known rule to make it".
+ out_ << " | ";
+ path_output_.WriteFile(out_, partial_info_plist);
+ }
+
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolCompileXCAssets;
+
+ SourceFileSet asset_catalog_bundles;
+ for (const auto& source : target_->bundle_data().assets_catalog_sources()) {
+ out_ << " ";
+ path_output_.WriteFile(out_, source);
+ asset_catalog_bundles.insert(source);
+ }
+
+ out_ << " | ";
+ path_output_.WriteFile(out_, input_dep);
+
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+
+ out_ << std::endl;
+
+ out_ << " product_type = " << target_->bundle_data().product_type()
+ << std::endl;
+
+ if (partial_info_plist != OutputFile()) {
+ out_ << " partial_info_plist = ";
+ path_output_.WriteFile(out_, partial_info_plist);
+ out_ << std::endl;
+ }
+
+ const std::vector<SubstitutionPattern>& flags =
+ target_->bundle_data().xcasset_compiler_flags().list();
+ if (!flags.empty()) {
+ out_ << " " << SubstitutionXcassetsCompilerFlags.ninja_name << " =";
+ EscapeOptions args_escape_options;
+ args_escape_options.mode = ESCAPE_NINJA_COMMAND;
+ for (const auto& flag : flags) {
+ out_ << " ";
+ SubstitutionWriter::WriteWithNinjaVariables(
+ flag, args_escape_options, out_);
+ }
+ out_ << std::endl;
+ }
+}
+
+OutputFile
+NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogInputDepsStamp(
+ const std::vector<const Target*>& dependencies) {
+ DCHECK(!dependencies.empty());
+ if (dependencies.size() == 1)
+ return dependencies[0]->dependency_output_file();
+
+ OutputFile xcassets_input_stamp_file =
+ GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
+ xcassets_input_stamp_file.value().append(target_->label().name());
+ xcassets_input_stamp_file.value().append(".xcassets.inputdeps.stamp");
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, xcassets_input_stamp_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolStamp;
+
+ for (const Target* target : dependencies) {
+ out_ << " ";
+ path_output_.WriteFile(out_, target->dependency_output_file());
+ }
+ out_ << std::endl;
+ return xcassets_input_stamp_file;
+}
+
+void NinjaCreateBundleTargetWriter::WriteCodeSigningStep(
+ const std::string& code_signing_rule_name,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files) {
+ if (code_signing_rule_name.empty())
+ return;
+
+ OutputFile code_signing_input_stamp_file =
+ WriteCodeSigningInputDepsStamp(order_only_deps, output_files);
+ DCHECK(!code_signing_input_stamp_file.value().empty());
+
+ out_ << "build";
+ std::vector<OutputFile> code_signing_output_files;
+ SubstitutionWriter::GetListAsOutputFiles(
+ settings_, target_->bundle_data().code_signing_outputs(),
+ &code_signing_output_files);
+ path_output_.WriteFiles(out_, code_signing_output_files);
+
+ // Since the code signature step depends on all the files from the bundle,
+ // the create_bundle stamp can just depends on the output of the signature
+ // script (dependencies are transitive).
+ *output_files = std::move(code_signing_output_files);
+
+ out_ << ": " << code_signing_rule_name;
+ out_ << " | ";
+ path_output_.WriteFile(out_, code_signing_input_stamp_file);
+ out_ << std::endl;
+}
+
+OutputFile NinjaCreateBundleTargetWriter::WriteCodeSigningInputDepsStamp(
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files) {
+ std::vector<SourceFile> code_signing_input_files;
+ code_signing_input_files.push_back(
+ target_->bundle_data().code_signing_script());
+ code_signing_input_files.insert(
+ code_signing_input_files.end(),
+ target_->bundle_data().code_signing_sources().begin(),
+ target_->bundle_data().code_signing_sources().end());
+ for (const OutputFile& output_file : *output_files) {
+ code_signing_input_files.push_back(
+ output_file.AsSourceFile(settings_->build_settings()));
+ }
+
+ DCHECK(!code_signing_input_files.empty());
+ if (code_signing_input_files.size() == 1 && order_only_deps.empty())
+ return OutputFile(settings_->build_settings(), code_signing_input_files[0]);
+
+ OutputFile code_signing_input_stamp_file =
+ GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
+ code_signing_input_stamp_file.value().append(target_->label().name());
+ code_signing_input_stamp_file.value().append(".codesigning.inputdeps.stamp");
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, code_signing_input_stamp_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolStamp;
+
+ for (const SourceFile& source : code_signing_input_files) {
+ out_ << " ";
+ path_output_.WriteFile(out_, source);
+ }
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+ out_ << std::endl;
+ return code_signing_input_stamp_file;
+}
diff --git a/gn/src/gn/ninja_create_bundle_target_writer.h b/gn/src/gn/ninja_create_bundle_target_writer.h
new file mode 100644
index 00000000000..6d4e2248865
--- /dev/null
+++ b/gn/src/gn/ninja_create_bundle_target_writer.h
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_NINJA_CREATE_BUNDLE_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_CREATE_BUNDLE_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/ninja_target_writer.h"
+
+class BundleFileRule;
+
+// Writes a .ninja file for a bundle_data target type.
+class NinjaCreateBundleTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaCreateBundleTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaCreateBundleTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ // Writes the Ninja rule for invoking the code signing script.
+ //
+ // Returns the name of the custom rule generated for the code signing step if
+ // defined, otherwise returns an empty string.
+ std::string WriteCodeSigningRuleDefinition();
+
+ // Writes the steps to copy files into the bundle.
+ //
+ // The list of newly created files will be added to |output_files|.
+ void WriteCopyBundleDataSteps(const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files);
+
+ // Writes the step to copy files BundleFileRule into the bundle.
+ //
+ // The list of newly created files will be added to |output_files|.
+ void WriteCopyBundleFileRuleSteps(
+ const BundleFileRule& file_rule,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files);
+
+ // Writes the step to compile assets catalogs.
+ //
+ // The list of newly created files will be added to |output_files|.
+ void WriteCompileAssetsCatalogStep(
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files);
+
+ // Writes the stamp file for the assets catalog compilation input
+ // dependencies.
+ OutputFile WriteCompileAssetsCatalogInputDepsStamp(
+ const std::vector<const Target*>& dependencies);
+
+ // Writes the code signing step (if a script is defined).
+ //
+ // The list of newly created files will be added to |output_files|. As the
+ // code signing may depends on the full bundle structure, this step will
+ // depends on all files generated via other rules.
+ void WriteCodeSigningStep(const std::string& code_signing_rule_name,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files);
+
+ // Writes the stamp file for the code signing input dependencies.
+ OutputFile WriteCodeSigningInputDepsStamp(
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* output_files);
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaCreateBundleTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_CREATE_BUNDLE_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_create_bundle_target_writer_unittest.cc b/gn/src/gn/ninja_create_bundle_target_writer_unittest.cc
new file mode 100644
index 00000000000..d2c5c2420da
--- /dev/null
+++ b/gn/src/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -0,0 +1,487 @@
+// Copyright 2016 The Chromium 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 "gn/ninja_create_bundle_target_writer.h"
+
+#include <algorithm>
+#include <memory>
+#include <sstream>
+
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+void SetupBundleDataDir(BundleData* bundle_data, const std::string& root_dir) {
+ std::string bundle_root_dir = root_dir + "/bar.bundle";
+ bundle_data->root_dir() = SourceDir(bundle_root_dir);
+ bundle_data->contents_dir() = SourceDir(bundle_root_dir + "/Contents");
+ bundle_data->resources_dir() =
+ SourceDir(bundle_data->contents_dir().value() + "/Resources");
+ bundle_data->executable_dir() =
+ SourceDir(bundle_data->contents_dir().value() + "/MacOS");
+}
+
+std::unique_ptr<Target> NewAction(const TestWithScope& setup) {
+ Err err;
+ auto action = std::make_unique<Target>(setup.settings(),
+ Label(SourceDir("//foo/"), "bar"));
+ action->set_output_type(Target::ACTION);
+ action->visibility().SetPublic();
+ action->action_values().set_script(SourceFile("//foo/script.py"));
+
+ action->action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+
+ action->SetToolchain(setup.toolchain());
+ return action;
+}
+
+} // namespace
+
+// Tests multiple files with an output pattern.
+TEST(NinjaCreateBundleTargetWriter, Run) {
+ Err err;
+ TestWithScope setup;
+
+ std::unique_ptr<Target> action = NewAction(setup);
+ ASSERT_TRUE(action->OnResolved(&err)) << err.message();
+
+ Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
+ bundle_data.set_output_type(Target::BUNDLE_DATA);
+ bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
+ bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
+ bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data.SetToolchain(setup.toolchain());
+ bundle_data.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data.OnResolved(&err));
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
+ create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
+ "obj/foo/data.stamp\n"
+ "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
+ "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
+ "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build obj/baz/bar.stamp: stamp "
+ "bar.bundle/Contents/Resources/input1.txt "
+ "bar.bundle/Contents/Resources/input2.txt"
+ " || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+// Tests creating a bundle in a sub-directory of $root_out_dir.
+TEST(NinjaCreateBundleTargetWriter, InSubDirectory) {
+ Err err;
+ TestWithScope setup;
+
+ std::unique_ptr<Target> action = NewAction(setup);
+ ASSERT_TRUE(action->OnResolved(&err)) << err.message();
+
+ Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
+ bundle_data.set_output_type(Target::BUNDLE_DATA);
+ bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
+ bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
+ bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data.SetToolchain(setup.toolchain());
+ bundle_data.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data.OnResolved(&err));
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug/gen");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
+ create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
+ "obj/foo/data.stamp\n"
+ "build gen/bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
+ "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build gen/bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
+ "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build obj/baz/bar.stamp: stamp "
+ "gen/bar.bundle/Contents/Resources/input1.txt "
+ "gen/bar.bundle/Contents/Resources/input2.txt || "
+ "obj/baz/bar.inputdeps.stamp\n"
+ "build gen/bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+// Tests empty asset catalog with partial_info_plist property defined.
+TEST(NinjaCreateBundleTargetWriter, JustPartialInfoPlist) {
+ Err err;
+ TestWithScope setup;
+
+ std::unique_ptr<Target> action = NewAction(setup);
+ ASSERT_TRUE(action->OnResolved(&err)) << err.message();
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
+ create_bundle.bundle_data().product_type().assign("com.apple.product-type");
+ create_bundle.bundle_data().set_partial_info_plist(
+ SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build baz/bar/bar_partial_info.plist: stamp || obj/foo/bar.stamp\n"
+ "build obj/baz/bar.stamp: stamp "
+ "baz/bar/bar_partial_info.plist || obj/foo/bar.stamp\n"
+ "build bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+// Tests multiple files from asset catalog.
+TEST(NinjaCreateBundleTargetWriter, AssetCatalog) {
+ Err err;
+ TestWithScope setup;
+
+ std::unique_ptr<Target> action = NewAction(setup);
+ ASSERT_TRUE(action->OnResolved(&err)) << err.message();
+
+ Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
+ bundle_data.set_output_type(Target::BUNDLE_DATA);
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.dataset/Contents.json"));
+ bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.dataset/FooScript.js"));
+ bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data.SetToolchain(setup.toolchain());
+ bundle_data.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data.OnResolved(&err));
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
+ create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
+ create_bundle.bundle_data().product_type().assign("com.apple.product-type");
+ create_bundle.bundle_data().xcasset_compiler_flags() =
+ SubstitutionList::MakeForTest("--app-icon", "foo");
+
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
+ "obj/foo/data.stamp\n"
+ "build bar.bundle/Contents/Resources/Assets.car: compile_xcassets "
+ "../../foo/Foo.xcassets | obj/foo/data.stamp || "
+ "obj/baz/bar.inputdeps.stamp\n"
+ " product_type = com.apple.product-type\n"
+ " xcasset_compiler_flags = --app-icon foo\n"
+ "build obj/baz/bar.stamp: stamp "
+ "bar.bundle/Contents/Resources/Assets.car || "
+ "obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+// Tests that the phony target for the top-level bundle directory is generated
+// correctly.
+TEST(NinjaCreateBundleTargetWriter, PhonyTarget) {
+ Err err;
+ TestWithScope setup;
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/baz/bar.stamp: stamp\n"
+ "build bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+// Tests complex target with multiple bundle_data sources, including
+// some asset catalog.
+TEST(NinjaCreateBundleTargetWriter, Complex) {
+ Err err;
+ TestWithScope setup;
+
+ std::unique_ptr<Target> action = NewAction(setup);
+ ASSERT_TRUE(action->OnResolved(&err)) << err.message();
+
+ Target bundle_data0(setup.settings(),
+ Label(SourceDir("//qux/"), "info_plist"));
+ bundle_data0.set_output_type(Target::BUNDLE_DATA);
+ bundle_data0.sources().push_back(SourceFile("//qux/qux-Info.plist"));
+ bundle_data0.action_values().outputs() =
+ SubstitutionList::MakeForTest("{{bundle_contents_dir}}/Info.plist");
+ bundle_data0.SetToolchain(setup.toolchain());
+ bundle_data0.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data0.OnResolved(&err));
+
+ Target bundle_data1(setup.settings(), Label(SourceDir("//foo/"), "data"));
+ bundle_data1.set_output_type(Target::BUNDLE_DATA);
+ bundle_data1.sources().push_back(SourceFile("//foo/input1.txt"));
+ bundle_data1.sources().push_back(SourceFile("//foo/input2.txt"));
+ bundle_data1.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data1.SetToolchain(setup.toolchain());
+ bundle_data1.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data1.OnResolved(&err));
+
+ Target bundle_data2(setup.settings(), Label(SourceDir("//foo/"), "assets"));
+ bundle_data2.set_output_type(Target::BUNDLE_DATA);
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/Contents.json"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.dataset/Contents.json"));
+ bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.dataset/FooScript.js"));
+ bundle_data2.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data2.SetToolchain(setup.toolchain());
+ bundle_data2.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data2.OnResolved(&err));
+
+ Target bundle_data3(setup.settings(), Label(SourceDir("//quz/"), "assets"));
+ bundle_data3.set_output_type(Target::BUNDLE_DATA);
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/Contents.json"));
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/quz.imageset/Contents.json"));
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29.png"));
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@2x.png"));
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@3x.png"));
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/quz.dataset/Contents.json"));
+ bundle_data3.sources().push_back(
+ SourceFile("//quz/Quz.xcassets/quz.dataset/QuzScript.js"));
+ bundle_data3.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data3.SetToolchain(setup.toolchain());
+ bundle_data3.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data3.OnResolved(&err));
+
+ Target bundle_data4(setup.settings(), Label(SourceDir("//biz/"), "assets"));
+ bundle_data4.set_output_type(Target::BUNDLE_DATA);
+ bundle_data4.sources().push_back(
+ SourceFile("//biz/Biz.xcassets/Contents.json"));
+ bundle_data4.sources().push_back(
+ SourceFile("//biz/Biz.xcassets/biz.colorset/Contents.json"));
+ bundle_data4.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data4.SetToolchain(setup.toolchain());
+ bundle_data4.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data4.OnResolved(&err));
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data0));
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data1));
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data2));
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data3));
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data4));
+ create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
+ create_bundle.bundle_data().product_type().assign("com.apple.product-type");
+ create_bundle.bundle_data().set_partial_info_plist(
+ SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/baz/bar.inputdeps.stamp: stamp obj/biz/assets.stamp "
+ "obj/foo/assets.stamp obj/foo/bar.stamp obj/foo/data.stamp "
+ "obj/qux/info_plist.stamp obj/quz/assets.stamp\n"
+ "build bar.bundle/Contents/Info.plist: copy_bundle_data "
+ "../../qux/qux-Info.plist || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
+ "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
+ "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build obj/baz/bar.xcassets.inputdeps.stamp: stamp "
+ "obj/foo/assets.stamp "
+ "obj/quz/assets.stamp obj/biz/assets.stamp\n"
+ "build bar.bundle/Contents/Resources/Assets.car | "
+ "baz/bar/bar_partial_info.plist: compile_xcassets "
+ "../../foo/Foo.xcassets ../../quz/Quz.xcassets "
+ "../../biz/Biz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || "
+ "obj/baz/bar.inputdeps.stamp\n"
+ " product_type = com.apple.product-type\n"
+ " partial_info_plist = baz/bar/bar_partial_info.plist\n"
+ "build obj/baz/bar.stamp: stamp "
+ "bar.bundle/Contents/Info.plist "
+ "bar.bundle/Contents/Resources/input1.txt "
+ "bar.bundle/Contents/Resources/input2.txt "
+ "bar.bundle/Contents/Resources/Assets.car "
+ "baz/bar/bar_partial_info.plist || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+// Tests code signing steps.
+TEST(NinjaCreateBundleTargetWriter, CodeSigning) {
+ Err err;
+ TestWithScope setup;
+
+ std::unique_ptr<Target> action = NewAction(setup);
+ ASSERT_TRUE(action->OnResolved(&err)) << err.message();
+
+ Target executable(setup.settings(), Label(SourceDir("//baz/"), "quz"));
+ executable.set_output_type(Target::EXECUTABLE);
+ executable.sources().push_back(SourceFile("//baz/quz.c"));
+ executable.SetToolchain(setup.toolchain());
+ executable.visibility().SetPublic();
+ ASSERT_TRUE(executable.OnResolved(&err));
+
+ Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
+ bundle_data.set_output_type(Target::BUNDLE_DATA);
+ bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
+ bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
+ bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data.SetToolchain(setup.toolchain());
+ bundle_data.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data.OnResolved(&err));
+
+ Target create_bundle(
+ setup.settings(),
+ Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
+ create_bundle.set_output_type(Target::CREATE_BUNDLE);
+ create_bundle.bundle_data().set_code_signing_script(
+ SourceFile("//build/codesign.py"));
+ create_bundle.bundle_data().code_signing_sources().push_back(
+ SourceFile("//out/Debug/quz"));
+ create_bundle.bundle_data().code_signing_outputs() =
+ SubstitutionList::MakeForTest(
+ "//out/Debug/bar.bundle/Contents/quz",
+ "//out/Debug/bar.bundle/_CodeSignature/CodeResources");
+ create_bundle.bundle_data().code_signing_args() =
+ SubstitutionList::MakeForTest("-b=quz", "bar.bundle");
+ create_bundle.public_deps().push_back(LabelTargetPair(&executable));
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
+ create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
+ create_bundle.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(create_bundle.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCreateBundleTargetWriter writer(&create_bundle, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/baz/bar.inputdeps.stamp: stamp ./quz obj/foo/bar.stamp "
+ "obj/foo/data.stamp\n"
+ "rule __baz_bar___toolchain_default__code_signing_rule\n"
+ " command = ../../build/codesign.py -b=quz bar.bundle\n"
+ " description = CODE SIGNING //baz:bar(//toolchain:default)\n"
+ " restat = 1\n"
+ "\n"
+ "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
+ "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
+ "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
+ "build obj/baz/bar.codesigning.inputdeps.stamp: stamp "
+ "../../build/codesign.py "
+ "quz "
+ "bar.bundle/Contents/Resources/input1.txt "
+ "bar.bundle/Contents/Resources/input2.txt || "
+ "obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: "
+ "__baz_bar___toolchain_default__code_signing_rule "
+ "| obj/baz/bar.codesigning.inputdeps.stamp\n"
+ "build obj/baz/bar.stamp: stamp "
+ "bar.bundle/Contents/quz "
+ "bar.bundle/_CodeSignature/CodeResources || obj/baz/bar.inputdeps.stamp\n"
+ "build bar.bundle: phony obj/baz/bar.stamp\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
diff --git a/gn/src/gn/ninja_generated_file_target_writer.cc b/gn/src/gn/ninja_generated_file_target_writer.cc
new file mode 100644
index 00000000000..afe1e08a4b6
--- /dev/null
+++ b/gn/src/gn/ninja_generated_file_target_writer.cc
@@ -0,0 +1,97 @@
+// Copyright 2018 The Chromium 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 "gn/ninja_generated_file_target_writer.h"
+
+#include "base/strings/string_util.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/output_conversion.h"
+#include "gn/output_file.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/string_utils.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+
+NinjaGeneratedFileTargetWriter::NinjaGeneratedFileTargetWriter(
+ const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+NinjaGeneratedFileTargetWriter::~NinjaGeneratedFileTargetWriter() = default;
+
+void NinjaGeneratedFileTargetWriter::Run() {
+ // Write the file.
+ GenerateFile();
+
+ // A generated_file target should generate a stamp file with dependencies
+ // on each of the deps and data_deps in the target. The actual collection is
+ // done at gen time, and so ninja doesn't need to know about it.
+ std::vector<OutputFile> output_files;
+ std::vector<OutputFile> data_output_files;
+ for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
+ if (pair.ptr->IsDataOnly()) {
+ data_output_files.push_back(pair.ptr->dependency_output_file());
+ } else {
+ output_files.push_back(pair.ptr->dependency_output_file());
+ }
+ }
+
+ const LabelTargetVector& data_deps = target_->data_deps();
+ for (const auto& pair : data_deps)
+ data_output_files.push_back(pair.ptr->dependency_output_file());
+
+ WriteStampForTarget(output_files, data_output_files);
+}
+
+void NinjaGeneratedFileTargetWriter::GenerateFile() {
+ Err err;
+
+ // If this is a metadata target, populate the write value with the appropriate
+ // data.
+ Value contents;
+ if (target_->contents().type() == Value::NONE) {
+ // Origin is set to the outputs location, so that errors with this value
+ // get flagged on the right target.
+ CHECK(target_->action_values().outputs().list().size() == 1U);
+ contents = Value(target_->action_values().outputs().list()[0].origin(),
+ Value::LIST);
+ std::set<const Target*> targets_walked;
+ if (!target_->GetMetadata(target_->data_keys(), target_->walk_keys(),
+ target_->rebase(), /*deps_only = */ true,
+ &contents.list_value(), &targets_walked, &err)) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+ } else {
+ contents = target_->contents();
+ }
+
+ std::vector<SourceFile> outputs_as_sources;
+ target_->action_values().GetOutputsAsSourceFiles(target_,
+ &outputs_as_sources);
+ CHECK(outputs_as_sources.size() == 1);
+
+ base::FilePath output =
+ settings_->build_settings()->GetFullPath(outputs_as_sources[0]);
+ ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, outputs_as_sources[0].value());
+
+ // Compute output.
+ std::ostringstream out;
+ ConvertValueToOutput(settings_, contents, target_->output_conversion(), out,
+ &err);
+
+ if (err.has_error()) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ WriteFileIfChanged(output, out.str(), &err);
+
+ if (err.has_error()) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+}
diff --git a/gn/src/gn/ninja_generated_file_target_writer.h b/gn/src/gn/ninja_generated_file_target_writer.h
new file mode 100644
index 00000000000..43231a6d4e1
--- /dev/null
+++ b/gn/src/gn/ninja_generated_file_target_writer.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/ninja_target_writer.h"
+
+// Writes a .ninja file for a group target type.
+class NinjaGeneratedFileTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaGeneratedFileTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaGeneratedFileTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ void GenerateFile();
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaGeneratedFileTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_generated_file_target_writer_unittest.cc b/gn/src/gn/ninja_generated_file_target_writer_unittest.cc
new file mode 100644
index 00000000000..676ba01a17d
--- /dev/null
+++ b/gn/src/gn/ninja_generated_file_target_writer_unittest.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 "gn/ninja_generated_file_target_writer.h"
+
+#include "gn/source_file.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using NinjaGeneratedFileTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaGeneratedFileTargetWriterTest, Run) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::GENERATED_FILE);
+ target.visibility().SetPublic();
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.json");
+ target.set_contents(Value(nullptr, true));
+ target.set_output_conversion(Value(nullptr, "json"));
+
+ Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
+ dep.set_output_type(Target::ACTION);
+ dep.visibility().SetPublic();
+ dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target dep2(setup.settings(), Label(SourceDir("//foo/"), "dep2"));
+ dep2.set_output_type(Target::ACTION);
+ dep2.visibility().SetPublic();
+ dep2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep2.OnResolved(&err));
+
+ Target bundle_data_dep(setup.settings(),
+ Label(SourceDir("//foo/"), "bundle_data_dep"));
+ bundle_data_dep.sources().push_back(SourceFile("//foo/some_data.txt"));
+ bundle_data_dep.set_output_type(Target::BUNDLE_DATA);
+ bundle_data_dep.visibility().SetPublic();
+ bundle_data_dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(bundle_data_dep.OnResolved(&err));
+
+ Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
+ datadep.set_output_type(Target::ACTION);
+ datadep.visibility().SetPublic();
+ datadep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ target.public_deps().push_back(LabelTargetPair(&dep));
+ target.public_deps().push_back(LabelTargetPair(&dep2));
+ target.public_deps().push_back(LabelTargetPair(&bundle_data_dep));
+ target.data_deps().push_back(LabelTargetPair(&datadep));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err)) << err.message();
+
+ std::ostringstream out;
+ NinjaGeneratedFileTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/foo/bar.stamp: stamp obj/foo/dep.stamp obj/foo/dep2.stamp || "
+ "obj/foo/bundle_data_dep.stamp obj/foo/datadep.stamp\n";
+ EXPECT_EQ(expected, out.str());
+}
diff --git a/gn/src/gn/ninja_group_target_writer.cc b/gn/src/gn/ninja_group_target_writer.cc
new file mode 100644
index 00000000000..beeb54ac902
--- /dev/null
+++ b/gn/src/gn/ninja_group_target_writer.cc
@@ -0,0 +1,37 @@
+// 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 "gn/ninja_group_target_writer.h"
+
+#include "base/strings/string_util.h"
+#include "gn/deps_iterator.h"
+#include "gn/output_file.h"
+#include "gn/string_utils.h"
+#include "gn/target.h"
+
+NinjaGroupTargetWriter::NinjaGroupTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+NinjaGroupTargetWriter::~NinjaGroupTargetWriter() = default;
+
+void NinjaGroupTargetWriter::Run() {
+ // A group rule just generates a stamp file with dependencies on each of
+ // the deps and data_deps in the group.
+ std::vector<OutputFile> output_files;
+ std::vector<OutputFile> data_output_files;
+ for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
+ if (pair.ptr->IsDataOnly()) {
+ data_output_files.push_back(pair.ptr->dependency_output_file());
+ } else {
+ output_files.push_back(pair.ptr->dependency_output_file());
+ }
+ }
+
+ const LabelTargetVector& data_deps = target_->data_deps();
+ for (const auto& pair : data_deps)
+ data_output_files.push_back(pair.ptr->dependency_output_file());
+
+ WriteStampForTarget(output_files, data_output_files);
+}
diff --git a/gn/src/gn/ninja_group_target_writer.h b/gn/src/gn/ninja_group_target_writer.h
new file mode 100644
index 00000000000..8abb20a5887
--- /dev/null
+++ b/gn/src/gn/ninja_group_target_writer.h
@@ -0,0 +1,23 @@
+// 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 TOOLS_GN_NINJA_GROUP_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_GROUP_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/ninja_target_writer.h"
+
+// Writes a .ninja file for a group target type.
+class NinjaGroupTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaGroupTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaGroupTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NinjaGroupTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_GROUP_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_group_target_writer_unittest.cc b/gn/src/gn/ninja_group_target_writer_unittest.cc
new file mode 100644
index 00000000000..1fe51ca1b92
--- /dev/null
+++ b/gn/src/gn/ninja_group_target_writer_unittest.cc
@@ -0,0 +1,60 @@
+// 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 "gn/ninja_group_target_writer.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(NinjaGroupTargetWriter, Run) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::GROUP);
+ target.visibility().SetPublic();
+
+ Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
+ dep.set_output_type(Target::ACTION);
+ dep.visibility().SetPublic();
+ dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target dep2(setup.settings(), Label(SourceDir("//foo/"), "dep2"));
+ dep2.set_output_type(Target::ACTION);
+ dep2.visibility().SetPublic();
+ dep2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep2.OnResolved(&err));
+
+ Target bundle_data_dep(setup.settings(),
+ Label(SourceDir("//foo/"), "bundle_data_dep"));
+ bundle_data_dep.sources().push_back(SourceFile("//foo/some_data.txt"));
+ bundle_data_dep.set_output_type(Target::BUNDLE_DATA);
+ bundle_data_dep.visibility().SetPublic();
+ bundle_data_dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(bundle_data_dep.OnResolved(&err));
+
+ Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
+ datadep.set_output_type(Target::ACTION);
+ datadep.visibility().SetPublic();
+ datadep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ target.public_deps().push_back(LabelTargetPair(&dep));
+ target.public_deps().push_back(LabelTargetPair(&dep2));
+ target.public_deps().push_back(LabelTargetPair(&bundle_data_dep));
+ target.data_deps().push_back(LabelTargetPair(&datadep));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaGroupTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/foo/bar.stamp: stamp obj/foo/dep.stamp obj/foo/dep2.stamp || "
+ "obj/foo/bundle_data_dep.stamp obj/foo/datadep.stamp\n";
+ EXPECT_EQ(expected, out.str());
+}
diff --git a/gn/src/gn/ninja_rust_binary_target_writer.cc b/gn/src/gn/ninja_rust_binary_target_writer.cc
new file mode 100644
index 00000000000..38f46864856
--- /dev/null
+++ b/gn/src/gn/ninja_rust_binary_target_writer.cc
@@ -0,0 +1,323 @@
+// Copyright 2019 The Chromium 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 "gn/ninja_rust_binary_target_writer.h"
+
+#include <sstream>
+
+#include "base/strings/string_util.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/ninja_utils.h"
+#include "gn/rust_substitution_type.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+
+namespace {
+
+// Returns the proper escape options for writing compiler and linker flags.
+EscapeOptions GetFlagOptions() {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ return opts;
+}
+
+void WriteVar(const char* name,
+ const std::string& value,
+ EscapeOptions opts,
+ std::ostream& out) {
+ out << name << " = ";
+ EscapeStringToStream(out, value, opts);
+ out << std::endl;
+}
+
+void WriteCrateVars(const Target* target,
+ const Tool* tool,
+ EscapeOptions opts,
+ std::ostream& out) {
+ WriteVar(kRustSubstitutionCrateName.ninja_name,
+ target->rust_values().crate_name(), opts, out);
+
+ std::string crate_type;
+ switch (target->rust_values().crate_type()) {
+ // Auto-select the crate type for executables, static libraries, and rlibs.
+ case RustValues::CRATE_AUTO: {
+ switch (target->output_type()) {
+ case Target::EXECUTABLE:
+ crate_type = "bin";
+ break;
+ case Target::STATIC_LIBRARY:
+ crate_type = "staticlib";
+ break;
+ case Target::RUST_LIBRARY:
+ crate_type = "rlib";
+ break;
+ case Target::RUST_PROC_MACRO:
+ crate_type = "proc-macro";
+ break;
+ default:
+ NOTREACHED();
+ }
+ break;
+ }
+ case RustValues::CRATE_BIN:
+ crate_type = "bin";
+ break;
+ case RustValues::CRATE_CDYLIB:
+ crate_type = "cdylib";
+ break;
+ case RustValues::CRATE_DYLIB:
+ crate_type = "dylib";
+ break;
+ case RustValues::CRATE_PROC_MACRO:
+ crate_type = "proc-macro";
+ break;
+ case RustValues::CRATE_RLIB:
+ crate_type = "rlib";
+ break;
+ case RustValues::CRATE_STATICLIB:
+ crate_type = "staticlib";
+ break;
+ default:
+ NOTREACHED();
+ }
+ WriteVar(kRustSubstitutionCrateType.ninja_name, crate_type, opts, out);
+
+ WriteVar(SubstitutionOutputExtension.ninja_name,
+ SubstitutionWriter::GetLinkerSubstitution(
+ target, tool, &SubstitutionOutputExtension),
+ opts, out);
+ WriteVar(SubstitutionOutputDir.ninja_name,
+ SubstitutionWriter::GetLinkerSubstitution(target, tool,
+ &SubstitutionOutputDir),
+ opts, out);
+}
+
+} // namespace
+
+NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaBinaryTargetWriter(target, out),
+ tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {}
+
+NinjaRustBinaryTargetWriter::~NinjaRustBinaryTargetWriter() = default;
+
+// TODO(juliehockett): add inherited library support? and IsLinkable support?
+// for c-cross-compat
+void NinjaRustBinaryTargetWriter::Run() {
+ DCHECK(target_->output_type() != Target::SOURCE_SET);
+
+ size_t num_stamp_uses = target_->sources().size();
+
+ std::vector<OutputFile> input_deps = WriteInputsStampAndGetDep(
+ num_stamp_uses);
+
+ WriteCompilerVars();
+
+ // Classify our dependencies.
+ ClassifiedDeps classified_deps = GetClassifiedDeps();
+
+ // The input dependencies will be an order-only dependency. This will cause
+ // Ninja to make sure the inputs are up to date before compiling this source,
+ // but changes in the inputs deps won't cause the file to be recompiled. See
+ // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation.
+ std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), num_stamp_uses);
+ std::copy(input_deps.begin(), input_deps.end(),
+ std::back_inserter(order_only_deps));
+
+ // Build lists which will go into different bits of the rustc command line.
+ // Public rust_library deps go in a --extern rlibs, public non-rust deps go in
+ // -Ldependency. Also assemble a list of extra (i.e. implicit) deps
+ // for ninja dependency tracking.
+ UniqueVector<OutputFile> implicit_deps;
+ AppendSourcesAndInputsToImplicitDeps(&implicit_deps);
+ implicit_deps.Append(classified_deps.extra_object_files.begin(),
+ classified_deps.extra_object_files.end());
+
+ std::vector<OutputFile> rustdeps;
+ std::vector<OutputFile> nonrustdeps;
+ nonrustdeps.insert(nonrustdeps.end(),
+ classified_deps.extra_object_files.begin(),
+ classified_deps.extra_object_files.end());
+ for (const auto* framework_dep : classified_deps.framework_deps) {
+ order_only_deps.push_back(framework_dep->dependency_output_file());
+ }
+ for (const auto* non_linkable_dep : classified_deps.non_linkable_deps) {
+ if (non_linkable_dep->source_types_used().RustSourceUsed() &&
+ non_linkable_dep->output_type() != Target::SOURCE_SET) {
+ rustdeps.push_back(non_linkable_dep->dependency_output_file());
+ }
+ order_only_deps.push_back(non_linkable_dep->dependency_output_file());
+ }
+ for (const auto* linkable_dep : classified_deps.linkable_deps) {
+ if (linkable_dep->source_types_used().RustSourceUsed()) {
+ rustdeps.push_back(linkable_dep->link_output_file());
+ } else {
+ nonrustdeps.push_back(linkable_dep->link_output_file());
+ }
+ implicit_deps.push_back(linkable_dep->dependency_output_file());
+ }
+
+ // Rust libraries specified by paths.
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ const ConfigValues& cur = iter.cur();
+ for (const auto& e : cur.externs()) {
+ if (e.second.is_source_file()) {
+ implicit_deps.push_back(
+ OutputFile(settings_->build_settings(), e.second.source_file()));
+ }
+ }
+ }
+
+ // Bubble up the full list of transitive rlib dependencies.
+ std::vector<OutputFile> transitive_rustlibs;
+ for (const auto* dep :
+ target_->rust_values().transitive_libs().GetOrdered()) {
+ if (dep->source_types_used().RustSourceUsed()) {
+ transitive_rustlibs.push_back(dep->dependency_output_file());
+ }
+ }
+
+ std::vector<OutputFile> tool_outputs;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ target_, tool_, tool_->outputs(), &tool_outputs);
+ WriteCompilerBuildLine({target_->rust_values().crate_root()},
+ implicit_deps.vector(), order_only_deps, tool_->name(),
+ tool_outputs);
+
+ std::vector<const Target*> extern_deps(
+ classified_deps.linkable_deps.vector());
+ std::copy(classified_deps.non_linkable_deps.begin(),
+ classified_deps.non_linkable_deps.end(),
+ std::back_inserter(extern_deps));
+ WriteExterns(extern_deps);
+ WriteRustdeps(transitive_rustlibs, rustdeps, nonrustdeps);
+ WriteSourcesAndInputs();
+}
+
+void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
+ const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
+ EscapeOptions opts = GetFlagOptions();
+ WriteCrateVars(target_, tool_, opts, out_);
+
+ WriteOneFlag(target_, &kRustSubstitutionRustFlags, false, Tool::kToolNone,
+ &ConfigValues::rustflags, opts, path_output_, out_);
+
+ WriteOneFlag(target_, &kRustSubstitutionRustEnv, false, Tool::kToolNone,
+ &ConfigValues::rustenv, opts, path_output_, out_);
+
+ WriteSharedVars(subst);
+}
+
+void NinjaRustBinaryTargetWriter::AppendSourcesAndInputsToImplicitDeps(
+ UniqueVector<OutputFile>* deps) const {
+ // Only the crate_root file needs to be given to rustc as input.
+ // Any other 'sources' are just implicit deps.
+ // Most Rust targets won't bother specifying the "sources =" line
+ // because it is handled sufficiently by crate_root and the generation
+ // of depfiles by rustc. But for those which do...
+ for (const auto& source : target_->sources()) {
+ deps->push_back(OutputFile(settings_->build_settings(), source));
+ }
+ for (const auto& data : target_->config_values().inputs()) {
+ deps->push_back(OutputFile(settings_->build_settings(), data));
+ }
+}
+
+void NinjaRustBinaryTargetWriter::WriteSourcesAndInputs() {
+ out_ << " sources =";
+ for (const auto& source : target_->sources()) {
+ out_ << " ";
+ path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), source));
+ }
+ for (const auto& data : target_->config_values().inputs()) {
+ out_ << " ";
+ path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), data));
+ }
+ out_ << std::endl;
+}
+
+void NinjaRustBinaryTargetWriter::WriteExterns(
+ const std::vector<const Target*>& deps) {
+ out_ << " externs =";
+
+ for (const Target* target : deps) {
+ if (target->output_type() == Target::RUST_LIBRARY ||
+ target->output_type() == Target::RUST_PROC_MACRO) {
+ out_ << " --extern ";
+ const auto& renamed_dep =
+ target_->rust_values().aliased_deps().find(target->label());
+ if (renamed_dep != target_->rust_values().aliased_deps().end()) {
+ out_ << renamed_dep->second << "=";
+ } else {
+ out_ << std::string(target->rust_values().crate_name()) << "=";
+ }
+ path_output_.WriteFile(out_, target->dependency_output_file());
+ }
+ }
+
+ EscapeOptions extern_escape_opts;
+ extern_escape_opts.mode = ESCAPE_NINJA_COMMAND;
+
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ const ConfigValues& cur = iter.cur();
+ for (const auto& e : cur.externs()) {
+ out_ << " --extern " << std::string(e.first) << "=";
+ if (e.second.is_source_file()) {
+ path_output_.WriteFile(out_, e.second.source_file());
+ } else {
+ EscapeStringToStream(out_, e.second.value(), extern_escape_opts);
+ }
+ }
+ }
+
+ out_ << std::endl;
+}
+
+void NinjaRustBinaryTargetWriter::WriteRustdeps(
+ const std::vector<OutputFile>& transitive_rustdeps,
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps) {
+ out_ << " rustdeps =";
+
+ // Rust dependencies.
+ UniqueVector<SourceDir> transitive_rustdep_dirs;
+ for (const auto& rustdep : transitive_rustdeps) {
+ // TODO switch to using --extern priv: after stabilization
+ transitive_rustdep_dirs.push_back(
+ rustdep.AsSourceFile(settings_->build_settings()).GetDir());
+ }
+ for (const auto& rustdepdir : transitive_rustdep_dirs) {
+ out_ << " -Ldependency=";
+ path_output_.WriteDir(out_, rustdepdir, PathOutput::DIR_NO_LAST_SLASH);
+ }
+
+ EscapeOptions lib_escape_opts;
+ lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
+
+ // Non-Rust native dependencies.
+ UniqueVector<SourceDir> nonrustdep_dirs;
+ for (const auto& nonrustdep : nonrustdeps) {
+ nonrustdep_dirs.push_back(
+ nonrustdep.AsSourceFile(settings_->build_settings()).GetDir());
+ }
+ // First -Lnative to specify the search directories.
+ // This is necessary for #[link(...)] directives to work properly.
+ for (const auto& nonrustdep_dir : nonrustdep_dirs) {
+ out_ << " -Lnative=";
+ path_output_.WriteDir(out_, nonrustdep_dir, PathOutput::DIR_NO_LAST_SLASH);
+ }
+ for (const auto& nonrustdep : nonrustdeps) {
+ out_ << " -Clink-arg=";
+ path_output_.WriteFile(out_, nonrustdep);
+ }
+
+ WriteLinkerFlags(out_, tool_, nullptr);
+ WriteLibs(out_, tool_);
+ out_ << std::endl;
+}
diff --git a/gn/src/gn/ninja_rust_binary_target_writer.h b/gn/src/gn/ninja_rust_binary_target_writer.h
new file mode 100644
index 00000000000..bd060aba27d
--- /dev/null
+++ b/gn/src/gn/ninja_rust_binary_target_writer.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "gn/ninja_binary_target_writer.h"
+#include "gn/rust_tool.h"
+
+struct EscapeOptions;
+
+// Writes a .ninja file for a binary target type (an executable, a shared
+// library, or a static library).
+class NinjaRustBinaryTargetWriter : public NinjaBinaryTargetWriter {
+ public:
+ NinjaRustBinaryTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaRustBinaryTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ void WriteCompilerVars();
+ void WriteSources(const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps);
+ void WriteExterns(const std::vector<const Target*>& deps);
+ void WriteRustdeps(const std::vector<OutputFile>& transitive_rustdeps,
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps);
+ // Unlike C/C++, Rust compiles all sources of a crate in one command.
+ // Write a ninja variable `sources` that contains all sources and input files.
+ void WriteSourcesAndInputs();
+ void WriteEdition();
+ void AppendSourcesAndInputsToImplicitDeps(UniqueVector<OutputFile>* deps) const;
+
+ const RustTool* tool_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaRustBinaryTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc b/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc
new file mode 100644
index 00000000000..6840b721b54
--- /dev/null
+++ b/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -0,0 +1,892 @@
+// Copyright 2019 The Chromium 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 "gn/ninja_rust_binary_target_writer.h"
+
+#include "gn/config.h"
+#include "gn/rust_values.h"
+#include "gn/scheduler.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+using NinjaRustBinaryTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustSourceSet) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.rs"));
+ target.sources().push_back(SourceFile("//foo/main.rs"));
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_FALSE(target.OnResolved(&err));
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustExecutable) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/input3.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/input3.rs "
+ "../../foo/main.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " sources = ../../foo/input3.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RlibDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
+ rlib.sources().push_back(barlib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(barlib);
+ rlib.rust_values().crate_name() = "mylib";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mylib\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = libmylib\n"
+ "\n"
+ "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
+ another_rlib.set_output_type(Target::RUST_LIBRARY);
+ another_rlib.visibility().SetPublic();
+ SourceFile lib("//foo/main.rs");
+ another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
+ another_rlib.sources().push_back(lib);
+ another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ another_rlib.rust_values().set_crate_root(lib);
+ another_rlib.rust_values().crate_name() = "direct";
+ another_rlib.SetToolchain(setup.toolchain());
+ another_rlib.public_deps().push_back(LabelTargetPair(&rlib));
+ ASSERT_TRUE(another_rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/foo/libdirect.rlib\n"
+ " externs = --extern direct=obj/foo/libdirect.rlib\n"
+ " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RlibDepsAcrossGroups) {
+ Err err;
+ TestWithScope setup;
+
+ Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
+ procmacro.set_output_type(Target::RUST_PROC_MACRO);
+ procmacro.visibility().SetPublic();
+ SourceFile barproc("//bar/lib.rs");
+ procmacro.sources().push_back(SourceFile("//bar/mylib.rs"));
+ procmacro.sources().push_back(barproc);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.rust_values().set_crate_root(barproc);
+ procmacro.rust_values().crate_name() = "mymacro";
+ procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&procmacro, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mymacro\n"
+ "crate_type = proc-macro\n"
+ "output_extension = .so\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = libmymacro\n"
+ "\n"
+ "build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
+ group.set_output_type(Target::GROUP);
+ group.visibility().SetPublic();
+ group.public_deps().push_back(LabelTargetPair(&procmacro));
+ group.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(group.OnResolved(&err));
+
+ Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
+ rlib.sources().push_back(barlib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(barlib);
+ rlib.rust_values().crate_name() = "mylib";
+ rlib.SetToolchain(setup.toolchain());
+ rlib.public_deps().push_back(LabelTargetPair(&group));
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mylib\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = libmylib\n"
+ "\n"
+ "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs obj/bar/libmymacro.so || "
+ "obj/baz/group.stamp\n"
+ " externs = --extern mymacro=obj/bar/libmymacro.so\n"
+ " rustdeps = -Ldependency=obj/bar\n"
+ " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.private_deps().push_back(LabelTargetPair(&rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | "
+ "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ " rustdeps = -Ldependency=obj/bar\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RenamedDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
+ another_rlib.set_output_type(Target::RUST_LIBRARY);
+ another_rlib.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
+ another_rlib.sources().push_back(lib);
+ another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ another_rlib.rust_values().set_crate_root(lib);
+ another_rlib.rust_values().crate_name() = "direct";
+ another_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(another_rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.rust_values().aliased_deps()[another_rlib.label()] = "direct_renamed";
+ target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/foo/libdirect.rlib\n"
+ " externs = --extern direct_renamed=obj/foo/libdirect.rlib\n"
+ " rustdeps = -Ldependency=obj/foo\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target staticlib(setup.settings(), Label(SourceDir("//foo/"), "static"));
+ staticlib.set_output_type(Target::STATIC_LIBRARY);
+ staticlib.visibility().SetPublic();
+ staticlib.sources().push_back(SourceFile("//foo/static.cpp"));
+ staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(staticlib.OnResolved(&err));
+
+ Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
+ rlib.sources().push_back(barlib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(barlib);
+ rlib.rust_values().crate_name() = "mylib";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target sharedlib(setup.settings(), Label(SourceDir("//foo/"), "shared"));
+ sharedlib.set_output_type(Target::SHARED_LIBRARY);
+ sharedlib.visibility().SetPublic();
+ sharedlib.sources().push_back(SourceFile("//foo/static.cpp"));
+ sharedlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ sharedlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(sharedlib.OnResolved(&err));
+
+ Target csourceset(setup.settings(), Label(SourceDir("//baz/"), "sourceset"));
+ csourceset.set_output_type(Target::SOURCE_SET);
+ csourceset.visibility().SetPublic();
+ csourceset.sources().push_back(SourceFile("//baz/csourceset.cpp"));
+ csourceset.source_types_used().Set(SourceFile::SOURCE_CPP);
+ csourceset.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(csourceset.OnResolved(&err));
+
+ Toolchain toolchain_with_toc(
+ setup.settings(), Label(SourceDir("//toolchain_with_toc/"), "with_toc"));
+ TestWithScope::SetupToolchain(&toolchain_with_toc, true);
+ Target sharedlib_with_toc(setup.settings(),
+ Label(SourceDir("//foo/"), "shared_with_toc"));
+ sharedlib_with_toc.set_output_type(Target::SHARED_LIBRARY);
+ sharedlib_with_toc.visibility().SetPublic();
+ sharedlib_with_toc.sources().push_back(SourceFile("//foo/static.cpp"));
+ sharedlib_with_toc.source_types_used().Set(SourceFile::SOURCE_CPP);
+ sharedlib_with_toc.SetToolchain(&toolchain_with_toc);
+ ASSERT_TRUE(sharedlib_with_toc.OnResolved(&err));
+
+ Target nonrust(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ nonrust.set_output_type(Target::EXECUTABLE);
+ nonrust.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ nonrust.sources().push_back(SourceFile("//foo/source.rs"));
+ nonrust.sources().push_back(main);
+ nonrust.source_types_used().Set(SourceFile::SOURCE_RS);
+ nonrust.rust_values().set_crate_root(main);
+ nonrust.rust_values().crate_name() = "foo_bar";
+ nonrust.private_deps().push_back(LabelTargetPair(&rlib));
+ nonrust.private_deps().push_back(LabelTargetPair(&staticlib));
+ nonrust.private_deps().push_back(LabelTargetPair(&sharedlib));
+ nonrust.private_deps().push_back(LabelTargetPair(&csourceset));
+ nonrust.private_deps().push_back(LabelTargetPair(&sharedlib_with_toc));
+ nonrust.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(nonrust.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&nonrust, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/baz/sourceset.csourceset.o "
+ "obj/bar/libmylib.rlib "
+ "obj/foo/libstatic.a ./libshared.so ./libshared_with_toc.so.TOC "
+ "|| obj/baz/sourceset.stamp\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ " rustdeps = -Ldependency=obj/bar "
+ "-Lnative=obj/baz -Lnative=obj/foo -Lnative=. "
+ "-Clink-arg=obj/baz/sourceset.csourceset.o "
+ "-Clink-arg=obj/foo/libstatic.a -Clink-arg=./libshared.so "
+ "-Clink-arg=./libshared_with_toc.so\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target nonrust_only(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ nonrust_only.set_output_type(Target::EXECUTABLE);
+ nonrust_only.visibility().SetPublic();
+ nonrust_only.sources().push_back(SourceFile("//foo/source.rs"));
+ nonrust_only.sources().push_back(main);
+ nonrust_only.source_types_used().Set(SourceFile::SOURCE_RS);
+ nonrust_only.rust_values().set_crate_root(main);
+ nonrust_only.rust_values().crate_name() = "foo_bar";
+ nonrust_only.private_deps().push_back(LabelTargetPair(&staticlib));
+ nonrust_only.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(nonrust_only.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&nonrust_only, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/foo/libstatic.a\n"
+ " externs =\n"
+ " rustdeps = -Lnative=obj/foo -Clink-arg=obj/foo/libstatic.a\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target rstaticlib(setup.settings(), Label(SourceDir("//baz/"), "baz"));
+ rstaticlib.set_output_type(Target::STATIC_LIBRARY);
+ rstaticlib.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ rstaticlib.sources().push_back(bazlib);
+ rstaticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rstaticlib.rust_values().set_crate_root(bazlib);
+ rstaticlib.rust_values().crate_name() = "baz";
+ rstaticlib.private_deps().push_back(LabelTargetPair(&staticlib));
+ rstaticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rstaticlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rstaticlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = baz\n"
+ "crate_type = staticlib\n"
+ "output_extension = .a\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/baz\n"
+ "target_output_name = libbaz\n"
+ "\n"
+ "build obj/baz/libbaz.a: rust_staticlib ../../baz/lib.rs | "
+ "../../baz/lib.rs "
+ "obj/foo/libstatic.a\n"
+ " externs =\n"
+ " rustdeps = -Lnative=obj/foo -Clink-arg=obj/foo/libstatic.a\n"
+ " sources = ../../baz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustOutputExtensionAndDir) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/input3.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.set_output_extension(std::string("exe"));
+ target.set_output_dir(SourceDir("//out/Debug/foo/"));
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = .exe\n"
+ "output_dir = foo\n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar.exe: rust_bin ../../foo/main.rs | ../../foo/input3.rs "
+ "../../foo/main.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " sources = ../../foo/input3.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, LibsAndLibDirs) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/input.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.set_output_dir(SourceDir("//out/Debug/foo/"));
+ target.config_values().libs().push_back(LibFile("quux"));
+ target.config_values().lib_dirs().push_back(SourceDir("//baz/"));
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = foo\n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/input.rs "
+ "../../foo/main.rs\n"
+ " externs =\n"
+ " rustdeps = -Lnative=../../baz -lquux\n"
+ " sources = ../../foo/input.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
+ Err err;
+ TestWithScope setup;
+
+ Target procmacrodep(setup.settings(),
+ Label(SourceDir("//baz/"), "mymacrodep"));
+ procmacrodep.set_output_type(Target::RUST_LIBRARY);
+ procmacrodep.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ procmacrodep.sources().push_back(SourceFile("//baz/mylib.rs"));
+ procmacrodep.sources().push_back(bazlib);
+ procmacrodep.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacrodep.rust_values().set_crate_root(bazlib);
+ procmacrodep.rust_values().crate_name() = "mymacrodep";
+ procmacrodep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacrodep.OnResolved(&err));
+
+ Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
+ procmacro.set_output_type(Target::RUST_PROC_MACRO);
+ procmacro.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ procmacro.sources().push_back(SourceFile("//bar/mylib.rs"));
+ procmacro.sources().push_back(barlib);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.rust_values().set_crate_root(barlib);
+ procmacro.rust_values().crate_name() = "mymacro";
+ procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ // Add a dependency to the procmacro so we can be sure its output
+ // directory is not propagated downstream beyond the proc macro.
+ procmacro.private_deps().push_back(LabelTargetPair(&procmacrodep));
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&procmacro, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mymacro\n"
+ "crate_type = proc-macro\n"
+ "output_extension = .so\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = libmymacro\n"
+ "\n"
+ "build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs obj/baz/libmymacrodep.rlib\n"
+ " externs = --extern mymacrodep=obj/baz/libmymacrodep.rlib\n"
+ " rustdeps = -Ldependency=obj/baz\n"
+ " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.private_deps().push_back(LabelTargetPair(&procmacro));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/bar/libmymacro.so\n"
+ " externs = --extern mymacro=obj/bar/libmymacro.so\n"
+ " rustdeps = -Ldependency=obj/bar\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, GroupDeps) {
+ Err err;
+ TestWithScope setup;
+
+ Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
+ rlib.sources().push_back(barlib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(barlib);
+ rlib.rust_values().crate_name() = "mylib";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mylib\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = libmylib\n"
+ "\n"
+ "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
+ group.set_output_type(Target::GROUP);
+ group.visibility().SetPublic();
+ group.public_deps().push_back(LabelTargetPair(&rlib));
+ group.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(group.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.private_deps().push_back(LabelTargetPair(&group));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/bar/libmylib.rlib || obj/baz/group.stamp\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ " rustdeps = -Ldependency=obj/bar\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, Externs) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.config_values().externs().push_back(
+ std::pair("lib1", LibFile(SourceFile("//foo/lib1.rlib"))));
+ target.config_values().externs().push_back(
+ std::pair("lib2", LibFile("lib2.rlib")));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs ../../foo/lib1.rlib\n"
+ " externs = --extern lib1=../../foo/lib1.rlib --extern "
+ "lib2=lib2.rlib\n"
+ " rustdeps =\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, Inputs) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/source.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.config_values().inputs().push_back(SourceFile("//foo/config.json"));
+ target.config_values().inputs().push_back(SourceFile("//foo/template.h"));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/foo/bar.inputs.stamp: stamp ../../foo/config.json ../../foo/template.h\n"
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs ../../foo/config.json ../../foo/template.h "
+ "|| obj/foo/bar.inputs.stamp\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " sources = ../../foo/source.rs ../../foo/main.rs "
+ "../../foo/config.json ../../foo/template.h\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
diff --git a/gn/src/gn/ninja_target_command_util.cc b/gn/src/gn/ninja_target_command_util.cc
new file mode 100644
index 00000000000..05f4ccdf8ce
--- /dev/null
+++ b/gn/src/gn/ninja_target_command_util.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 The Chromium 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 "gn/ninja_target_command_util.h"
+
+#include <string.h>
+
+#include "gn/c_tool.h"
+#include "gn/substitution_writer.h"
+
+namespace {
+
+// Returns the language-specific suffix for precompiled header files.
+const char* GetPCHLangSuffixForToolType(const char* name) {
+ if (name == CTool::kCToolCc)
+ return "c";
+ if (name == CTool::kCToolCxx)
+ return "cc";
+ if (name == CTool::kCToolObjC)
+ return "m";
+ if (name == CTool::kCToolObjCxx)
+ return "mm";
+ NOTREACHED() << "Not a valid PCH tool type: " << name;
+ return "";
+}
+
+} // namespace
+
+// Returns the computed name of the Windows .pch file for the given
+// tool type. The tool must support precompiled headers.
+OutputFile GetWindowsPCHFile(const Target* target, const char* tool_name) {
+ // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
+ // looking like "obj/chrome/browser/browser_cc.pch"
+ OutputFile ret = GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ);
+ ret.value().append(target->label().name());
+ ret.value().push_back('_');
+ ret.value().append(GetPCHLangSuffixForToolType(tool_name));
+ ret.value().append(".pch");
+
+ return ret;
+}
+
+void WriteOneFlag(const Target* target,
+ const Substitution* subst_enum,
+ bool has_precompiled_headers,
+ const char* tool_name,
+ const std::vector<std::string>& (ConfigValues::*getter)()
+ const,
+ EscapeOptions flag_escape_options,
+ PathOutput& path_output,
+ std::ostream& out,
+ bool write_substitution) {
+ if (!target->toolchain()->substitution_bits().used.count(subst_enum))
+ return;
+
+ if (write_substitution)
+ out << subst_enum->ninja_name << " =";
+
+ if (has_precompiled_headers) {
+ const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ // Name the .pch file.
+ out << " /Fp";
+ path_output.WriteFile(out, GetWindowsPCHFile(target, tool_name));
+
+ // Enables precompiled headers and names the .h file. It's a string
+ // rather than a file name (so no need to rebase or use path_output).
+ out << " /Yu" << target->config_values().precompiled_header();
+ RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
+ out);
+ } else if (tool && tool->precompiled_header_type() == CTool::PCH_GCC) {
+ // The targets to build the .gch files should omit the -include flag
+ // below. To accomplish this, each substitution flag is overwritten in
+ // the target rule and these values are repeated. The -include flag is
+ // omitted in place of the required -x <header lang> flag for .gch
+ // targets.
+ RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
+ out);
+
+ // Compute the gch file (it will be language-specific).
+ std::vector<OutputFile> outputs;
+ GetPCHOutputFiles(target, tool_name, &outputs);
+ if (!outputs.empty()) {
+ // Trim the .gch suffix for the -include flag.
+ // e.g. for gch file foo/bar/target.precompiled.h.gch:
+ // -include foo/bar/target.precompiled.h
+ std::string pch_file = outputs[0].value();
+ pch_file.erase(pch_file.length() - 4);
+ out << " -include " << pch_file;
+ }
+ } else {
+ RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
+ out);
+ }
+ } else {
+ RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
+ out);
+ }
+
+ if (write_substitution)
+ out << std::endl;
+}
+
+void GetPCHOutputFiles(const Target* target,
+ const char* tool_name,
+ std::vector<OutputFile>* outputs) {
+ outputs->clear();
+
+ // Compute the tool. This must use the tool type passed in rather than the
+ // detected file type of the precompiled source file since the same
+ // precompiled source file will be used for separate C/C++ compiles.
+ const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
+ if (!tool)
+ return;
+ SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+ target, target->config_values().precompiled_source(), tool->outputs(),
+ outputs);
+
+ if (outputs->empty())
+ return;
+ if (outputs->size() > 1)
+ outputs->resize(1); // Only link the first output from the compiler tool.
+
+ std::string& output_value = (*outputs)[0].value();
+ size_t extension_offset = FindExtensionOffset(output_value);
+ if (extension_offset == std::string::npos) {
+ // No extension found.
+ return;
+ }
+ DCHECK(extension_offset >= 1);
+ DCHECK(output_value[extension_offset - 1] == '.');
+
+ std::string output_extension;
+ CTool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
+ switch (header_type) {
+ case CTool::PCH_MSVC:
+ output_extension = GetWindowsPCHObjectExtension(
+ tool_name, output_value.substr(extension_offset - 1));
+ break;
+ case CTool::PCH_GCC:
+ output_extension = GetGCCPCHOutputExtension(tool_name);
+ break;
+ case CTool::PCH_NONE:
+ NOTREACHED() << "No outputs for no PCH type.";
+ break;
+ }
+ output_value.replace(extension_offset - 1, std::string::npos,
+ output_extension);
+}
+
+std::string GetGCCPCHOutputExtension(const char* tool_name) {
+ const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
+ std::string result = ".";
+ // For GCC, the output name must have a .gch suffix and be annotated with
+ // the language type. For example:
+ // obj/foo/target_name.header.h ->
+ // obj/foo/target_name.header.h-cc.gch
+ // In order for the compiler to pick it up, the output name (minus the .gch
+ // suffix MUST match whatever is passed to the -include flag).
+ result += "h-";
+ result += lang_suffix;
+ result += ".gch";
+ return result;
+}
+
+std::string GetWindowsPCHObjectExtension(const char* tool_name,
+ const std::string& obj_extension) {
+ const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
+ std::string result = ".";
+ // For MSVC, annotate the obj files with the language type. For example:
+ // obj/foo/target_name.precompile.obj ->
+ // obj/foo/target_name.precompile.cc.obj
+ result += lang_suffix;
+ result += obj_extension;
+ return result;
+}
diff --git a/gn/src/gn/ninja_target_command_util.h b/gn/src/gn/ninja_target_command_util.h
new file mode 100644
index 00000000000..d9d30bc29d9
--- /dev/null
+++ b/gn/src/gn/ninja_target_command_util.h
@@ -0,0 +1,114 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
+#define TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
+
+#include "base/json/string_escape.h"
+#include "gn/config_values_extractors.h"
+#include "gn/escape.h"
+#include "gn/filesystem_utils.h"
+#include "gn/frameworks_utils.h"
+#include "gn/path_output.h"
+#include "gn/target.h"
+#include "gn/toolchain.h"
+#include "gn/variables.h"
+
+struct DefineWriter {
+ DefineWriter() { options.mode = ESCAPE_NINJA_COMMAND; }
+ DefineWriter(EscapingMode mode) { options.mode = mode; }
+
+ void operator()(const std::string& s, std::ostream& out) const {
+ out << " ";
+ EscapeStringToStream(out, "-D" + s, options);
+ }
+
+ EscapeOptions options;
+};
+
+struct FrameworkDirsWriter {
+ FrameworkDirsWriter(PathOutput& path_output, const std::string& tool_switch)
+ : path_output_(path_output), tool_switch_(tool_switch) {}
+
+ ~FrameworkDirsWriter() = default;
+
+ void operator()(const SourceDir& d, std::ostream& out) const {
+ std::ostringstream path_out;
+ path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
+ const std::string& path = path_out.str();
+ if (path[0] == '"')
+ out << " \"" << tool_switch_ << path.substr(1);
+ else
+ out << " " << tool_switch_ << path;
+ }
+
+ PathOutput& path_output_;
+ std::string tool_switch_;
+};
+
+struct FrameworksWriter {
+ explicit FrameworksWriter(const std::string& tool_switch)
+ : FrameworksWriter(ESCAPE_NINJA_COMMAND, tool_switch) {}
+ FrameworksWriter(EscapingMode mode, const std::string& tool_switch)
+ : tool_switch_(tool_switch) {
+ options_.mode = mode;
+ }
+
+ void operator()(const std::string& s, std::ostream& out) const {
+ out << " " << tool_switch_;
+ std::string_view framework_name = GetFrameworkName(s);
+ EscapeStringToStream(out, framework_name, options_);
+ }
+
+ EscapeOptions options_;
+ std::string tool_switch_;
+};
+
+struct IncludeWriter {
+ explicit IncludeWriter(PathOutput& path_output) : path_output_(path_output) {}
+ ~IncludeWriter() = default;
+
+ void operator()(const SourceDir& d, std::ostream& out) const {
+ std::ostringstream path_out;
+ path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
+ const std::string& path = path_out.str();
+ if (path[0] == '"')
+ out << " \"-I" << path.substr(1);
+ else
+ out << " -I" << path;
+ }
+
+ PathOutput& path_output_;
+};
+
+// has_precompiled_headers is set when this substitution matches a tool type
+// that supports precompiled headers, and this target supports precompiled
+// headers. It doesn't indicate if the tool has precompiled headers (this
+// will be looked up by this function).
+//
+// The tool_type indicates the corresponding tool for flags that are
+// tool-specific (e.g. "cflags_c"). For non-tool-specific flags (e.g.
+// "defines") tool_type should be TYPE_NONE.
+void WriteOneFlag(const Target* target,
+ const Substitution* subst_enum,
+ bool has_precompiled_headers,
+ const char* tool_name,
+ const std::vector<std::string>& (ConfigValues::*getter)()
+ const,
+ EscapeOptions flag_escape_options,
+ PathOutput& path_output,
+ std::ostream& out,
+ bool write_substitution = true);
+
+// Fills |outputs| with the object or gch file for the precompiled header of the
+// given type (flag type and tool type must match).
+void GetPCHOutputFiles(const Target* target,
+ const char* tool_name,
+ std::vector<OutputFile>* outputs);
+
+std::string GetGCCPCHOutputExtension(const char* tool_name);
+std::string GetWindowsPCHObjectExtension(const char* tool_name,
+ const std::string& obj_extension);
+
+#endif // TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
diff --git a/gn/src/gn/ninja_target_command_util_unittest.cc b/gn/src/gn/ninja_target_command_util_unittest.cc
new file mode 100644
index 00000000000..8da62d322fb
--- /dev/null
+++ b/gn/src/gn/ninja_target_command_util_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright 2020 The Chromium 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 "gn/ninja_target_command_util.h"
+
+#include <algorithm>
+#include <sstream>
+
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+namespace {
+
+// Helper function that uses a "Writer" to format a list of strings and return
+// the generated output as a string.
+template <typename Writer, typename Item>
+std::string FormatWithWriter(Writer writer, std::vector<Item> items) {
+ std::ostringstream out;
+ for (const Item& item : items) {
+ writer(item, out);
+ }
+ return out.str();
+}
+
+// Helper function running to test "Writer" by formatting N "Items" and
+// comparing the resulting string to an expected output.
+template <typename Writer, typename Item>
+void TestWriter(Writer writer,
+ std::string expected,
+ std::initializer_list<Item> items) {
+ std::string formatted =
+ FormatWithWriter(writer, std::vector<Item>(std::move(items)));
+
+ // Manually implement the check and the formatting of the error message to
+ // see the difference in the error message (by default the error message
+ // would just be "formatted == expected").
+ if (formatted != expected) {
+ std::ostringstream stream;
+ stream << '"' << expected << "\" == \"" << formatted << '"';
+ std::string message = stream.str();
+
+ ::testing::TestResult result(false, message.c_str());
+ ::testing::AssertHelper(__FILE__, __LINE__, result) = ::testing::Message();
+ }
+}
+
+} // anonymous namespace
+
+TEST(NinjaTargetCommandUtil, DefineWriter) {
+ TestWriter(DefineWriter(),
+// Escaping is different between Windows and Posix.
+#if defined(OS_WIN)
+ " -DFOO -DBAR=1 \"-DBAZ=\\\"Baz\\\"\"",
+#else
+ " -DFOO -DBAR=1 -DBAZ=\\\"Baz\\\"",
+#endif
+ {"FOO", "BAR=1", "BAZ=\"Baz\""});
+
+ TestWriter(DefineWriter(ESCAPE_COMPILATION_DATABASE),
+ // Escaping is different between Windows and Posix.
+ " -DFOO -DBAR=1 \"-DBAZ=\\\"Baz\\\"\"",
+ {"FOO", "BAR=1", "BAZ=\"Baz\""});
+}
+
+TEST(NinjaTargetCommandUtil, FrameworkDirsWriter) {
+ PathOutput ninja_path_output(SourceDir("//out"), "", ESCAPE_NINJA_COMMAND);
+ TestWriter(FrameworkDirsWriter(ninja_path_output, "-F"),
+// Escaping is different between Windows and Posix.
+#if defined(OS_WIN)
+ " -F. \"-FPath$ With$ Spaces\"",
+#else
+ " -F. -FPath\\$ With\\$ Spaces",
+#endif
+ {SourceDir("//out"), SourceDir("//out/Path With Spaces")});
+
+ PathOutput space_path_output(SourceDir("//out"), "", ESCAPE_SPACE);
+ TestWriter(FrameworkDirsWriter(space_path_output, "-F"),
+ " -F. -FPath\\ With\\ Spaces",
+ {SourceDir("//out"), SourceDir("//out/Path With Spaces")});
+}
+
+TEST(NinjaTargetCommandUtil, FrameworksWriter) {
+ TestWriter(FrameworksWriter("-framework "),
+// Escaping is different between Windows and Posix.
+#if defined(OS_WIN)
+ " -framework Foundation -framework \"Name$ With$ Spaces\"",
+#else
+ " -framework Foundation -framework Name\\$ With\\$ Spaces",
+#endif
+ {"Foundation.framework", "Name With Spaces.framework"});
+
+ TestWriter(FrameworksWriter(ESCAPE_SPACE, "-framework "),
+ " -framework Foundation -framework Name\\ With\\ Spaces",
+ {"Foundation.framework", "Name With Spaces.framework"});
+}
diff --git a/gn/src/gn/ninja_target_writer.cc b/gn/src/gn/ninja_target_writer.cc
new file mode 100644
index 00000000000..7eefc1229f7
--- /dev/null
+++ b/gn/src/gn/ninja_target_writer.cc
@@ -0,0 +1,341 @@
+// 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 "gn/ninja_target_writer.h"
+
+#include <sstream>
+
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "gn/config_values_extractors.h"
+#include "gn/err.h"
+#include "gn/escape.h"
+#include "gn/filesystem_utils.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_action_target_writer.h"
+#include "gn/ninja_binary_target_writer.h"
+#include "gn/ninja_bundle_data_target_writer.h"
+#include "gn/ninja_copy_target_writer.h"
+#include "gn/ninja_create_bundle_target_writer.h"
+#include "gn/ninja_generated_file_target_writer.h"
+#include "gn/ninja_group_target_writer.h"
+#include "gn/ninja_utils.h"
+#include "gn/output_file.h"
+#include "gn/scheduler.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+
+NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
+ : settings_(target->settings()),
+ target_(target),
+ out_(out),
+ path_output_(settings_->build_settings()->build_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA) {}
+
+NinjaTargetWriter::~NinjaTargetWriter() = default;
+
+// static
+std::string NinjaTargetWriter::RunAndWriteFile(const Target* target) {
+ const Settings* settings = target->settings();
+
+ ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
+ target->label().GetUserVisibleName(false));
+ trace.SetToolchain(settings->toolchain_label());
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Computing", target->label().GetUserVisibleName(true));
+
+ // It's ridiculously faster to write to a string and then write that to
+ // disk in one operation than to use an fstream here.
+ std::stringstream rules;
+
+ // Call out to the correct sub-type of writer. Binary targets need to be
+ // written to separate files for compiler flag scoping, but other target
+ // types can have their rules coalesced.
+ //
+ // In ninja, if a rule uses a variable (like $include_dirs) it will use
+ // the value set by indenting it under the build line or it takes the value
+ // from the end of the invoking scope (otherwise the current file). It does
+ // not copy the value from what it was when the build line was encountered.
+ // To avoid writing lots of duplicate rules for defines and cflags, etc. on
+ // each source file build line, we use separate .ninja files with the shared
+ // variables set at the top.
+ //
+ // Groups and actions don't use this type of flag, they make unique rules
+ // or write variables scoped under each build line. As a result, they don't
+ // need the separate files.
+ bool needs_file_write = false;
+ if (target->output_type() == Target::BUNDLE_DATA) {
+ NinjaBundleDataTargetWriter writer(target, rules);
+ writer.Run();
+ } else if (target->output_type() == Target::CREATE_BUNDLE) {
+ NinjaCreateBundleTargetWriter writer(target, rules);
+ writer.Run();
+ } else if (target->output_type() == Target::COPY_FILES) {
+ NinjaCopyTargetWriter writer(target, rules);
+ writer.Run();
+ } else if (target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH) {
+ NinjaActionTargetWriter writer(target, rules);
+ writer.Run();
+ } else if (target->output_type() == Target::GROUP) {
+ NinjaGroupTargetWriter writer(target, rules);
+ writer.Run();
+ } else if (target->output_type() == Target::GENERATED_FILE) {
+ NinjaGeneratedFileTargetWriter writer(target, rules);
+ writer.Run();
+ } else if (target->IsBinary()) {
+ needs_file_write = true;
+ NinjaBinaryTargetWriter writer(target, rules);
+ writer.Run();
+ } else {
+ CHECK(0) << "Output type of target not handled.";
+ }
+
+ if (needs_file_write) {
+ // Write the ninja file.
+ SourceFile ninja_file = GetNinjaFileForTarget(target);
+ base::FilePath full_ninja_file =
+ settings->build_settings()->GetFullPath(ninja_file);
+ base::CreateDirectory(full_ninja_file.DirName());
+ WriteFileIfChanged(full_ninja_file, rules.str(), nullptr);
+
+ EscapeOptions options;
+ options.mode = ESCAPE_NINJA;
+
+ // Return the subninja command to load the rules file.
+ std::string result = "subninja ";
+ result.append(EscapeString(
+ OutputFile(target->settings()->build_settings(), ninja_file).value(),
+ options, nullptr));
+ result.push_back('\n');
+ return result;
+ }
+
+ // No separate file required, just return the rules.
+ return rules.str();
+}
+
+void NinjaTargetWriter::WriteEscapedSubstitution(const Substitution* type) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA;
+
+ out_ << type->ninja_name << " = ";
+ EscapeStringToStream(
+ out_, SubstitutionWriter::GetTargetSubstitution(target_, type), opts);
+ out_ << std::endl;
+}
+
+void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
+ bool written_anything = false;
+
+ // Target label.
+ if (bits.used.count(&SubstitutionLabel)) {
+ WriteEscapedSubstitution(&SubstitutionLabel);
+ written_anything = true;
+ }
+
+ // Target label name.
+ if (bits.used.count(&SubstitutionLabelName)) {
+ WriteEscapedSubstitution(&SubstitutionLabelName);
+ written_anything = true;
+ }
+
+ // Target label name without toolchain.
+ if (bits.used.count(&SubstitutionLabelNoToolchain)) {
+ WriteEscapedSubstitution(&SubstitutionLabelNoToolchain);
+ written_anything = true;
+ }
+
+ // Root gen dir.
+ if (bits.used.count(&SubstitutionRootGenDir)) {
+ WriteEscapedSubstitution(&SubstitutionRootGenDir);
+ written_anything = true;
+ }
+
+ // Root out dir.
+ if (bits.used.count(&SubstitutionRootOutDir)) {
+ WriteEscapedSubstitution(&SubstitutionRootOutDir);
+ written_anything = true;
+ }
+
+ // Target gen dir.
+ if (bits.used.count(&SubstitutionTargetGenDir)) {
+ WriteEscapedSubstitution(&SubstitutionTargetGenDir);
+ written_anything = true;
+ }
+
+ // Target out dir.
+ if (bits.used.count(&SubstitutionTargetOutDir)) {
+ WriteEscapedSubstitution(&SubstitutionTargetOutDir);
+ written_anything = true;
+ }
+
+ // Target output name.
+ if (bits.used.count(&SubstitutionTargetOutputName)) {
+ WriteEscapedSubstitution(&SubstitutionTargetOutputName);
+ written_anything = true;
+ }
+
+ // If we wrote any vars, separate them from the rest of the file that follows
+ // with a blank line.
+ if (written_anything)
+ out_ << std::endl;
+}
+
+std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
+ const std::vector<const Target*>& extra_hard_deps,
+ size_t num_stamp_uses) const {
+ CHECK(target_->toolchain()) << "Toolchain not set on target "
+ << target_->label().GetUserVisibleName(true);
+
+ // ----------
+ // Collect all input files that are input deps of this target. Knowing the
+ // number before writing allows us to either skip writing the input deps
+ // stamp or optimize it. Use pointers to avoid copies here.
+ std::vector<const SourceFile*> input_deps_sources;
+ input_deps_sources.reserve(32);
+
+ // Actions get implicit dependencies on the script itself.
+ if (target_->output_type() == Target::ACTION ||
+ target_->output_type() == Target::ACTION_FOREACH)
+ input_deps_sources.push_back(&target_->action_values().script());
+
+ // Input files are only considered for non-binary targets which use an
+ // implicit dependency instead. The implicit depedency in this case is
+ // handled separately by the binary target writer.
+ if (!target_->IsBinary()) {
+ for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
+ for (const auto& input : iter.cur().inputs())
+ input_deps_sources.push_back(&input);
+ }
+ }
+
+ // For an action (where we run a script only once) the sources are the same
+ // as the inputs. For action_foreach, the sources will be operated on
+ // separately so don't handle them here.
+ if (target_->output_type() == Target::ACTION) {
+ for (const auto& source : target_->sources())
+ input_deps_sources.push_back(&source);
+ }
+
+ // ----------
+ // Collect all target input dependencies of this target as was done for the
+ // files above.
+ std::vector<const Target*> input_deps_targets;
+ input_deps_targets.reserve(32);
+
+ // Hard dependencies that are direct or indirect dependencies.
+ // These are large (up to 100s), hence why we check other
+ const std::set<const Target*>& hard_deps(target_->recursive_hard_deps());
+ for (const Target* target : hard_deps) {
+ // BUNDLE_DATA should normally be treated as a data-only dependency
+ // (see Target::IsDataOnly()). Only the CREATE_BUNDLE target, that actually
+ // consumes this data, needs to have the BUNDLE_DATA as an input dependency.
+ if (target->output_type() != Target::BUNDLE_DATA ||
+ target_->output_type() == Target::CREATE_BUNDLE)
+ input_deps_targets.push_back(target);
+ }
+
+ // Extra hard dependencies passed in. These are usually empty or small, and
+ // we don't want to duplicate the explicit hard deps of the target.
+ for (const Target* target : extra_hard_deps) {
+ if (!hard_deps.count(target))
+ input_deps_targets.push_back(target);
+ }
+
+ // Toolchain dependencies. These must be resolved before doing anything.
+ // This just writes all toolchain deps for simplicity. If we find that
+ // toolchains often have more than one dependency, we could consider writing
+ // a toolchain-specific stamp file and only include the stamp here.
+ // Note that these are usually empty/small.
+ const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
+ for (const auto& toolchain_dep : toolchain_deps) {
+ // This could theoretically duplicate dependencies already in the list,
+ // but it shouldn't happen in practice, is inconvenient to check for,
+ // and only results in harmless redundant dependencies listed.
+ input_deps_targets.push_back(toolchain_dep.ptr);
+ }
+
+ // ---------
+ // Write the outputs.
+
+ if (input_deps_sources.size() + input_deps_targets.size() == 0)
+ return std::vector<OutputFile>(); // No input dependencies.
+
+ // If we're only generating one input dependency, return it directly instead
+ // of writing a stamp file for it.
+ if (input_deps_sources.size() == 1 && input_deps_targets.size() == 0)
+ return std::vector<OutputFile>{
+ OutputFile(settings_->build_settings(), *input_deps_sources[0])};
+ if (input_deps_sources.size() == 0 && input_deps_targets.size() == 1) {
+ const OutputFile& dep = input_deps_targets[0]->dependency_output_file();
+ DCHECK(!dep.value().empty());
+ return std::vector<OutputFile>{dep};
+ }
+
+ std::vector<OutputFile> outs;
+ // File input deps.
+ for (const SourceFile* source : input_deps_sources)
+ outs.push_back(OutputFile(settings_->build_settings(), *source));
+ // Target input deps. Sort by label so the output is deterministic (otherwise
+ // some of the targets will have gone through std::sets which will have
+ // sorted them by pointer).
+ std::sort(
+ input_deps_targets.begin(), input_deps_targets.end(),
+ [](const Target* a, const Target* b) { return a->label() < b->label(); });
+ for (auto* dep : input_deps_targets) {
+ DCHECK(!dep->dependency_output_file().value().empty());
+ outs.push_back(dep->dependency_output_file());
+ }
+
+ // If there are multiple inputs, but the stamp file would be referenced only
+ // once, don't write it but depend on the inputs directly.
+ if (num_stamp_uses == 1u)
+ return outs;
+
+ // Make a stamp file.
+ OutputFile input_stamp_file =
+ GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
+ input_stamp_file.value().append(target_->label().name());
+ input_stamp_file.value().append(".inputdeps.stamp");
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, input_stamp_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolStamp;
+ path_output_.WriteFiles(out_, outs);
+
+ out_ << "\n";
+ return std::vector<OutputFile>{input_stamp_file};
+}
+
+void NinjaTargetWriter::WriteStampForTarget(
+ const std::vector<OutputFile>& files,
+ const std::vector<OutputFile>& order_only_deps) {
+ const OutputFile& stamp_file = target_->dependency_output_file();
+
+ // First validate that the target's dependency is a stamp file. Otherwise,
+ // we shouldn't have gotten here!
+ CHECK(base::EndsWith(stamp_file.value(), ".stamp",
+ base::CompareCase::INSENSITIVE_ASCII))
+ << "Output should end in \".stamp\" for stamp file output. Instead got: "
+ << "\"" << stamp_file.value() << "\"";
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, stamp_file);
+
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << GeneralTool::kGeneralToolStamp;
+ path_output_.WriteFiles(out_, files);
+
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+ out_ << std::endl;
+}
diff --git a/gn/src/gn/ninja_target_writer.h b/gn/src/gn/ninja_target_writer.h
new file mode 100644
index 00000000000..f4c9eaea6b9
--- /dev/null
+++ b/gn/src/gn/ninja_target_writer.h
@@ -0,0 +1,72 @@
+// 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 TOOLS_GN_NINJA_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_TARGET_WRITER_H_
+
+#include <iosfwd>
+
+#include "base/macros.h"
+#include "gn/path_output.h"
+#include "gn/substitution_type.h"
+
+class OutputFile;
+class Settings;
+class Target;
+struct SubstitutionBits;
+
+// Generates one target's ".ninja" file. The toplevel "build.ninja" file is
+// generated by the NinjaBuildWriter.
+class NinjaTargetWriter {
+ public:
+ NinjaTargetWriter(const Target* target, std::ostream& out);
+ virtual ~NinjaTargetWriter();
+
+ // Returns the build line to be written to the toolchain build file.
+ //
+ // Some targets have their rules written to separate files, and some can have
+ // their rules coalesced in the main build file. For the coalesced case, this
+ // function will return the rules as a string. For the separate file case,
+ // the separate ninja file will be written and the return string will be the
+ // subninja command to load that file.
+ static std::string RunAndWriteFile(const Target* target);
+
+ virtual void Run() = 0;
+
+ protected:
+ // Writes out the substitution values that are shared between the different
+ // types of tools (target gen dir, target label, etc.). Only the substitutions
+ // identified by the given bits will be written.
+ void WriteSharedVars(const SubstitutionBits& bits);
+
+ // Writes to the output stream a stamp rule for input dependencies, and
+ // returns the file to be appended to source rules that encodes the
+ // order-only dependencies for the current target.
+ // If num_stamp_uses is small, this might return all input dependencies
+ // directly, without writing a stamp file.
+ // If there are no implicit dependencies and no extra target dependencies
+ // are passed in, this returns an empty vector.
+ std::vector<OutputFile> WriteInputDepsStampAndGetDep(
+ const std::vector<const Target*>& extra_hard_deps,
+ size_t num_stamp_uses) const;
+
+ // Writes to the output file a final stamp rule for the target that stamps
+ // the given list of files. This function assumes the stamp is for the target
+ // as a whole so the stamp file is set as the target's dependency output.
+ void WriteStampForTarget(const std::vector<OutputFile>& deps,
+ const std::vector<OutputFile>& order_only_deps);
+
+ const Settings* settings_; // Non-owning.
+ const Target* target_; // Non-owning.
+ std::ostream& out_;
+ PathOutput path_output_;
+
+ private:
+ void WriteCopyRules();
+ void WriteEscapedSubstitution(const Substitution* type);
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_TARGET_WRITER_H_
diff --git a/gn/src/gn/ninja_target_writer_unittest.cc b/gn/src/gn/ninja_target_writer_unittest.cc
new file mode 100644
index 00000000000..1b19159c63c
--- /dev/null
+++ b/gn/src/gn/ninja_target_writer_unittest.cc
@@ -0,0 +1,163 @@
+// 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 <sstream>
+
+#include "gn/ninja_action_target_writer.h"
+#include "gn/ninja_target_writer.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+class TestingNinjaTargetWriter : public NinjaTargetWriter {
+ public:
+ TestingNinjaTargetWriter(const Target* target,
+ const Toolchain* toolchain,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+ void Run() override {}
+
+ // Make this public so the test can call it.
+ std::vector<OutputFile> WriteInputDepsStampAndGetDep(
+ const std::vector<const Target*>& extra_hard_deps,
+ size_t num_stamp_uses) {
+ return NinjaTargetWriter::WriteInputDepsStampAndGetDep(extra_hard_deps,
+ num_stamp_uses);
+ }
+};
+
+} // namespace
+
+TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
+ TestWithScope setup;
+ Err err;
+
+ // Make a base target that's a hard dep (action).
+ Target base_target(setup.settings(), Label(SourceDir("//foo/"), "base"));
+ base_target.set_output_type(Target::ACTION);
+ base_target.visibility().SetPublic();
+ base_target.SetToolchain(setup.toolchain());
+ base_target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ // Dependent target that also includes a source prerequisite (should get
+ // included) and a source (should not be included).
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.SetToolchain(setup.toolchain());
+ target.config_values().inputs().push_back(SourceFile("//foo/input.txt"));
+ target.sources().push_back(SourceFile("//foo/source.txt"));
+ target.public_deps().push_back(LabelTargetPair(&base_target));
+
+ // Dependent action to test that action sources will be treated the same as
+ // inputs.
+ Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
+ action.set_output_type(Target::ACTION);
+ action.visibility().SetPublic();
+ action.SetToolchain(setup.toolchain());
+ action.action_values().set_script(SourceFile("//foo/script.py"));
+ action.sources().push_back(SourceFile("//foo/action_source.txt"));
+ action.public_deps().push_back(LabelTargetPair(&target));
+
+ ASSERT_TRUE(base_target.OnResolved(&err));
+ ASSERT_TRUE(target.OnResolved(&err));
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ // Input deps for the base (should be only the script itself).
+ {
+ std::ostringstream stream;
+ TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream);
+ std::vector<OutputFile> dep =
+ writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
+
+ // Since there is only one dependency, it should just be returned and
+ // nothing written to the stream.
+ ASSERT_EQ(1u, dep.size());
+ EXPECT_EQ("../../foo/script.py", dep[0].value());
+ EXPECT_EQ("", stream.str());
+ }
+
+ // Input deps for the target (should depend on the base).
+ {
+ std::ostringstream stream;
+ TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
+ std::vector<OutputFile> dep =
+ writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
+
+ // Since there is only one dependency, a stamp file will be returned
+ // directly without writing any additional rules.
+ ASSERT_EQ(1u, dep.size());
+ EXPECT_EQ("obj/foo/base.stamp", dep[0].value());
+ }
+
+ {
+ std::ostringstream stream;
+ NinjaActionTargetWriter writer(&action, stream);
+ writer.Run();
+ EXPECT_EQ(
+ "rule __foo_action___rule\n"
+ " command = ../../foo/script.py\n"
+ " description = ACTION //foo:action()\n"
+ " restat = 1\n"
+ "\n"
+ "build: __foo_action___rule | ../../foo/script.py"
+ " ../../foo/action_source.txt ./target\n"
+ "\n"
+ "build obj/foo/action.stamp: stamp\n",
+ stream.str());
+ }
+
+ // Input deps for action which should depend on the base since its a hard dep
+ // that is a (indirect) dependency, as well as the the action source.
+ {
+ std::ostringstream stream;
+ TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream);
+ std::vector<OutputFile> dep =
+ writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
+
+ ASSERT_EQ(1u, dep.size());
+ EXPECT_EQ("obj/foo/action.inputdeps.stamp", dep[0].value());
+ EXPECT_EQ(
+ "build obj/foo/action.inputdeps.stamp: stamp ../../foo/script.py "
+ "../../foo/action_source.txt ./target\n",
+ stream.str());
+ }
+}
+
+// Tests WriteInputDepsStampAndGetDep when toolchain deps are present.
+TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) {
+ TestWithScope setup;
+ Err err;
+
+ // Toolchain dependency. Here we make a target in the same toolchain for
+ // simplicity, but in real life (using the Builder) this would be rejected
+ // because it would be a circular dependency (the target depends on its
+ // toolchain, and the toolchain depends on this target).
+ Target toolchain_dep_target(setup.settings(),
+ Label(SourceDir("//foo/"), "setup"));
+ toolchain_dep_target.set_output_type(Target::ACTION);
+ toolchain_dep_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(toolchain_dep_target.OnResolved(&err));
+ setup.toolchain()->deps().push_back(LabelTargetPair(&toolchain_dep_target));
+
+ // Make a binary target
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
+ std::vector<OutputFile> dep =
+ writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
+
+ // Since there is more than one dependency, a stamp file will be returned
+ // and the rule for the stamp file will be written to the stream.
+ ASSERT_EQ(1u, dep.size());
+ EXPECT_EQ("obj/foo/setup.stamp", dep[0].value());
+ EXPECT_EQ("", stream.str());
+}
diff --git a/gn/src/gn/ninja_toolchain_writer.cc b/gn/src/gn/ninja_toolchain_writer.cc
new file mode 100644
index 00000000000..7be771932c7
--- /dev/null
+++ b/gn/src/gn/ninja_toolchain_writer.cc
@@ -0,0 +1,141 @@
+// 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 "gn/ninja_toolchain_writer.h"
+
+#include <fstream>
+
+#include "base/files/file_util.h"
+#include "base/strings/stringize_macros.h"
+#include "gn/build_settings.h"
+#include "gn/c_tool.h"
+#include "gn/filesystem_utils.h"
+#include "gn/general_tool.h"
+#include "gn/ninja_utils.h"
+#include "gn/pool.h"
+#include "gn/settings.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/toolchain.h"
+#include "gn/trace.h"
+
+namespace {
+
+const char kIndent[] = " ";
+
+} // namespace
+
+NinjaToolchainWriter::NinjaToolchainWriter(const Settings* settings,
+ const Toolchain* toolchain,
+ std::ostream& out)
+ : settings_(settings),
+ toolchain_(toolchain),
+ out_(out),
+ path_output_(settings_->build_settings()->build_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA) {}
+
+NinjaToolchainWriter::~NinjaToolchainWriter() = default;
+
+void NinjaToolchainWriter::Run(
+ const std::vector<NinjaWriter::TargetRulePair>& rules) {
+ std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
+
+ for (const auto& tool : toolchain_->tools()) {
+ if (tool.second->name() == GeneralTool::kGeneralToolAction)
+ continue;
+ WriteToolRule(tool.second.get(), rule_prefix);
+ }
+ out_ << std::endl;
+
+ for (const auto& pair : rules)
+ out_ << pair.second;
+}
+
+// static
+bool NinjaToolchainWriter::RunAndWriteFile(
+ const Settings* settings,
+ const Toolchain* toolchain,
+ const std::vector<NinjaWriter::TargetRulePair>& rules) {
+ base::FilePath ninja_file(settings->build_settings()->GetFullPath(
+ GetNinjaFileForToolchain(settings)));
+ ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, FilePathToUTF8(ninja_file));
+
+ base::CreateDirectory(ninja_file.DirName());
+
+ std::ofstream file;
+ file.open(FilePathToUTF8(ninja_file).c_str(),
+ std::ios_base::out | std::ios_base::binary);
+ if (file.fail())
+ return false;
+
+ NinjaToolchainWriter gen(settings, toolchain, file);
+ gen.Run(rules);
+ return true;
+}
+
+void NinjaToolchainWriter::WriteToolRule(Tool* tool,
+ const std::string& rule_prefix) {
+ out_ << "rule " << rule_prefix << tool->name() << std::endl;
+
+ // Rules explicitly include shell commands, so don't try to escape.
+ EscapeOptions options;
+ options.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
+
+ WriteCommandRulePattern("command", tool->command_launcher(), tool->command(),
+ options);
+
+ WriteRulePattern("description", tool->description(), options);
+ WriteRulePattern("rspfile", tool->rspfile(), options);
+ WriteRulePattern("rspfile_content", tool->rspfile_content(), options);
+
+ if (CTool* c_tool = tool->AsC()) {
+ if (c_tool->depsformat() == CTool::DEPS_GCC) {
+ // GCC-style deps require a depfile.
+ if (!c_tool->depfile().empty()) {
+ WriteRulePattern("depfile", tool->depfile(), options);
+ out_ << kIndent << "deps = gcc" << std::endl;
+ }
+ } else if (c_tool->depsformat() == CTool::DEPS_MSVC) {
+ // MSVC deps don't have a depfile.
+ out_ << kIndent << "deps = msvc" << std::endl;
+ }
+ } else if (!tool->depfile().empty()) {
+ WriteRulePattern("depfile", tool->depfile(), options);
+ out_ << kIndent << "deps = gcc" << std::endl;
+ }
+
+ // Use pool is specified.
+ if (tool->pool().ptr) {
+ std::string pool_name =
+ tool->pool().ptr->GetNinjaName(settings_->default_toolchain_label());
+ out_ << kIndent << "pool = " << pool_name << std::endl;
+ }
+
+ if (tool->restat())
+ out_ << kIndent << "restat = 1" << std::endl;
+}
+
+void NinjaToolchainWriter::WriteRulePattern(const char* name,
+ const SubstitutionPattern& pattern,
+ const EscapeOptions& options) {
+ if (pattern.empty())
+ return;
+ out_ << kIndent << name << " = ";
+ SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out_);
+ out_ << std::endl;
+}
+
+void NinjaToolchainWriter::WriteCommandRulePattern(
+ const char* name,
+ const std::string& launcher,
+ const SubstitutionPattern& command,
+ const EscapeOptions& options) {
+ CHECK(!command.empty()) << "Command should not be empty";
+ out_ << kIndent << name << " = " ;
+ if (!launcher.empty())
+ out_ << launcher << " ";
+ SubstitutionWriter::WriteWithNinjaVariables(command, options, out_);
+ out_ << std::endl;
+}
diff --git a/gn/src/gn/ninja_toolchain_writer.h b/gn/src/gn/ninja_toolchain_writer.h
new file mode 100644
index 00000000000..97ec481fc74
--- /dev/null
+++ b/gn/src/gn/ninja_toolchain_writer.h
@@ -0,0 +1,61 @@
+// 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 TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
+#define TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
+
+#include <iosfwd>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "gn/ninja_writer.h"
+#include "gn/path_output.h"
+#include "gn/toolchain.h"
+
+struct EscapeOptions;
+class Settings;
+class Tool;
+
+class NinjaToolchainWriter {
+ public:
+ // Takes the settings for the toolchain, as well as the list of all targets
+ // associated with the toolchain.
+ static bool RunAndWriteFile(
+ const Settings* settings,
+ const Toolchain* toolchain,
+ const std::vector<NinjaWriter::TargetRulePair>& rules);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRule);
+ FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRuleWithLauncher);
+
+ NinjaToolchainWriter(const Settings* settings,
+ const Toolchain* toolchain,
+ std::ostream& out);
+ ~NinjaToolchainWriter();
+
+ void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
+
+ void WriteRules();
+ void WriteToolRule(Tool* tool, const std::string& rule_prefix);
+ void WriteRulePattern(const char* name,
+ const SubstitutionPattern& pattern,
+ const EscapeOptions& options);
+ void WriteCommandRulePattern(const char* name,
+ const std::string& launcher,
+ const SubstitutionPattern& command,
+ const EscapeOptions& options);
+
+ const Settings* settings_;
+ const Toolchain* toolchain_;
+ std::ostream& out_;
+ PathOutput path_output_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaToolchainWriter);
+};
+
+#endif // TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
diff --git a/gn/src/gn/ninja_toolchain_writer_unittest.cc b/gn/src/gn/ninja_toolchain_writer_unittest.cc
new file mode 100644
index 00000000000..12eeb00a302
--- /dev/null
+++ b/gn/src/gn/ninja_toolchain_writer_unittest.cc
@@ -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.
+
+#include <sstream>
+
+#include "gn/ninja_toolchain_writer.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(NinjaToolchainWriter, WriteToolRule) {
+ TestWithScope setup;
+
+ std::ostringstream stream;
+ NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
+ writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCc),
+ std::string("prefix_"));
+
+ EXPECT_EQ(
+ "rule prefix_cc\n"
+ " command = cc ${in} ${cflags} ${cflags_c} ${defines} ${include_dirs} "
+ "-o ${out}\n",
+ stream.str());
+}
+
+TEST(NinjaToolchainWriter, WriteToolRuleWithLauncher) {
+ TestWithScope setup;
+
+ std::ostringstream stream;
+ NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
+ writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCxx),
+ std::string("prefix_"));
+
+ EXPECT_EQ(
+ "rule prefix_cxx\n"
+ " command = launcher c++ ${in} ${cflags} ${cflags_cc} ${defines} ${include_dirs} "
+ "-o ${out}\n",
+ stream.str());
+}
diff --git a/gn/src/gn/ninja_tools.cc b/gn/src/gn/ninja_tools.cc
new file mode 100644
index 00000000000..2eaf31cca36
--- /dev/null
+++ b/gn/src/gn/ninja_tools.cc
@@ -0,0 +1,82 @@
+// Copyright 2020 The Chromium 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 "gn/ninja_tools.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "gn/err.h"
+#include "gn/exec_process.h"
+#include "gn/filesystem_utils.h"
+
+namespace {
+
+base::CommandLine CreateNinjaToolCommandLine(const base::FilePath& ninja_executable,
+ const std::string& tool) {
+ base::CommandLine cmdline(ninja_executable);
+ cmdline.SetParseSwitches(false);
+ cmdline.AppendArg("-t");
+ cmdline.AppendArg(tool);
+ return cmdline;
+}
+
+bool RunNinja(const base::CommandLine& cmdline,
+ const base::FilePath& startup_dir,
+ std::string* output,
+ Err* err) {
+ std::string stderr_output;
+
+ int exit_code = 0;
+ if (!internal::ExecProcess(cmdline, startup_dir, output, &stderr_output,
+ &exit_code)) {
+ *err = Err(Location(), "Could not execute Ninja.",
+ "I was trying to execute \"" +
+ FilePathToUTF8(cmdline.GetProgram()) + "\".");
+ return false;
+ }
+
+ if (exit_code != 0) {
+ *err = Err(Location(), "Ninja has quit with exit code " +
+ base::IntToString(exit_code) + ".");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool InvokeNinjaRestatTool(const base::FilePath& ninja_executable,
+ const base::FilePath& build_dir,
+ const std::vector<base::FilePath>& files_to_restat,
+ Err* err) {
+ base::CommandLine cmdline =
+ CreateNinjaToolCommandLine(ninja_executable, "restat");
+ for (const base::FilePath& file : files_to_restat) {
+ cmdline.AppendArgPath(file);
+ }
+ std::string output;
+ return RunNinja(cmdline, build_dir, &output, err);
+}
+
+bool InvokeNinjaCleanDeadTool(const base::FilePath& ninja_executable,
+ const base::FilePath& build_dir,
+ Err* err) {
+ base::CommandLine cmdline =
+ CreateNinjaToolCommandLine(ninja_executable, "cleandead");
+ std::string output;
+ return RunNinja(cmdline, build_dir, &output, err);
+}
+
+bool InvokeNinjaRecompactTool(const base::FilePath& ninja_executable,
+ const base::FilePath& build_dir,
+ Err* err) {
+ base::CommandLine cmdline =
+ CreateNinjaToolCommandLine(ninja_executable, "recompact");
+ std::string output;
+ return RunNinja(cmdline, build_dir, &output, err);
+}
diff --git a/gn/src/gn/ninja_tools.h b/gn/src/gn/ninja_tools.h
new file mode 100644
index 00000000000..97212969b71
--- /dev/null
+++ b/gn/src/gn/ninja_tools.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_NINJA_TOOLS_H_
+#define TOOLS_GN_NINJA_TOOLS_H_
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "gn/err.h"
+
+// Invokes the ninja restat tool (ie, ninja -C build_dir -t restat). This tool
+// tells ninja that it should check the mtime of the provided files and update
+// the .ninja_log accordingly. This is useful when GN knows that an output file
+// in the ninja graph has been updated without invoking ninja.
+//
+// The best example of this is after gn gen runs, we know that build.ninja has
+// been potentially updated, but ninja will still use the mtime from the
+// .ninja_log and could trigger another re-gen. By telling ninja to restat
+// build.ninja, we can eliminate the extra re-gen.
+//
+// If files_to_restat is empty, ninja will restat all files that have an entry
+// in the .ninja_log.
+bool InvokeNinjaRestatTool(const base::FilePath& ninja_executable,
+ const base::FilePath& build_dir,
+ const std::vector<base::FilePath>& files_to_restat,
+ Err* err);
+
+// Invokes the ninja cleandead tool (ie, ninja -C build_dir -t cleandead). This
+// tool removes files produced by previous builds that are no longer in the
+// build file.
+bool InvokeNinjaCleanDeadTool(const base::FilePath& ninja_executable,
+ const base::FilePath& build_dir,
+ Err* err);
+
+// Invokes the ninja recompact tool (ie, ninja -C build_dir -t recompact). This
+// tool prunes the .ninja_log and .ninja_deps entries that are no longer part of
+// the build graph.
+bool InvokeNinjaRecompactTool(const base::FilePath& ninja_executable,
+ const base::FilePath& build_dir,
+ Err* err);
+
+#endif // TOOLS_GN_NINJA_TOOLS_H_
diff --git a/gn/src/gn/ninja_utils.cc b/gn/src/gn/ninja_utils.cc
new file mode 100644
index 00000000000..95cf3a1ee02
--- /dev/null
+++ b/gn/src/gn/ninja_utils.cc
@@ -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.
+
+#include "gn/ninja_utils.h"
+
+#include "gn/filesystem_utils.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+
+SourceFile GetNinjaFileForTarget(const Target* target) {
+ return SourceFile(
+ GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ).value() +
+ target->label().name() + ".ninja");
+}
+
+SourceFile GetNinjaFileForToolchain(const Settings* settings) {
+ return SourceFile(GetBuildDirAsSourceDir(BuildDirContext(settings),
+ BuildDirType::TOOLCHAIN_ROOT)
+ .value() +
+ "toolchain.ninja");
+}
+
+std::string GetNinjaRulePrefixForToolchain(const Settings* settings) {
+ // Don't prefix the default toolchain so it looks prettier, prefix everything
+ // else.
+ if (settings->is_default())
+ return std::string(); // Default toolchain has no prefix.
+ return settings->toolchain_label().name() + "_";
+}
diff --git a/gn/tools/gn/ninja_utils.h b/gn/src/gn/ninja_utils.h
index 60ae6b27a66..60ae6b27a66 100644
--- a/gn/tools/gn/ninja_utils.h
+++ b/gn/src/gn/ninja_utils.h
diff --git a/gn/src/gn/ninja_writer.cc b/gn/src/gn/ninja_writer.cc
new file mode 100644
index 00000000000..999718a1463
--- /dev/null
+++ b/gn/src/gn/ninja_writer.cc
@@ -0,0 +1,51 @@
+// 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 "gn/ninja_writer.h"
+
+#include "gn/builder.h"
+#include "gn/loader.h"
+#include "gn/location.h"
+#include "gn/ninja_build_writer.h"
+#include "gn/ninja_toolchain_writer.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+
+NinjaWriter::NinjaWriter(const Builder& builder) : builder_(builder) {}
+
+NinjaWriter::~NinjaWriter() = default;
+
+// static
+bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ const PerToolchainRules& per_toolchain_rules,
+ Err* err) {
+ NinjaWriter writer(builder);
+
+ if (!writer.WriteToolchains(per_toolchain_rules, err))
+ return false;
+ return NinjaBuildWriter::RunAndWriteFile(build_settings, builder, err);
+}
+
+bool NinjaWriter::WriteToolchains(const PerToolchainRules& per_toolchain_rules,
+ Err* err) {
+ if (per_toolchain_rules.empty()) {
+ *err = Err(Location(), "No targets.",
+ "I could not find any targets to write, so I'm doing nothing.");
+ return false;
+ }
+
+ for (const auto& i : per_toolchain_rules) {
+ const Toolchain* toolchain = i.first;
+ const Settings* settings =
+ builder_.loader()->GetToolchainSettings(toolchain->label());
+ if (!NinjaToolchainWriter::RunAndWriteFile(settings, toolchain, i.second)) {
+ *err =
+ Err(Location(), "Couldn't open toolchain buildfile(s) for writing");
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/gn/tools/gn/ninja_writer.h b/gn/src/gn/ninja_writer.h
index b9b93a4ef2c..b9b93a4ef2c 100644
--- a/gn/tools/gn/ninja_writer.h
+++ b/gn/src/gn/ninja_writer.h
diff --git a/gn/src/gn/operators.cc b/gn/src/gn/operators.cc
new file mode 100644
index 00000000000..7c04dd3a2bd
--- /dev/null
+++ b/gn/src/gn/operators.cc
@@ -0,0 +1,744 @@
+// 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 "gn/operators.h"
+
+#include <stddef.h>
+#include <algorithm>
+
+#include "base/strings/string_number_conversions.h"
+#include "gn/err.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/token.h"
+#include "gn/value.h"
+
+namespace {
+
+// Helper class used for assignment operations: =, +=, and -= to generalize
+// writing to various types of destinations.
+class ValueDestination {
+ public:
+ ValueDestination();
+
+ bool Init(Scope* exec_scope,
+ const ParseNode* dest,
+ const BinaryOpNode* op_node,
+ Err* err);
+
+ // Returns the value in the destination scope if it already exists, or null
+ // if it doesn't. This is for validation and does not count as a "use".
+ // Other nested scopes will be searched.
+ const Value* GetExistingValue() const;
+
+ // Returns an existing version of the output if it can be modified. This will
+ // not search nested scopes since writes only go into the current scope.
+ // Returns null if the value does not exist, or is not in the current scope
+ // (meaning assignments won't go to this value and it's not mutable). This
+ // is for implementing += and -=.
+ //
+ // If it exists, this will mark the origin of the value to be the passed-in
+ // node, and the value will be also marked unused (if possible) under the
+ // assumption that it will be modified in-place.
+ Value* GetExistingMutableValueIfExists(const ParseNode* origin);
+
+ // Returns a pointer to the value that was set.
+ Value* SetValue(Value value, const ParseNode* set_node);
+
+ // Fills the Err with an undefined value error appropriate for modification
+ // operators: += and -= (where the source is also the dest).
+ void MakeUndefinedIdentifierForModifyError(Err* err);
+
+ private:
+ enum Type { UNINITIALIZED, SCOPE, LIST };
+
+ Type type_;
+
+ // Valid when type_ == SCOPE.
+ Scope* scope_;
+ const Token* name_token_;
+
+ // Valid when type_ == LIST.
+ Value* list_;
+ size_t index_; // Guaranteed in-range when Init() succeeds.
+};
+
+ValueDestination::ValueDestination()
+ : type_(UNINITIALIZED),
+ scope_(nullptr),
+ name_token_(nullptr),
+ list_(nullptr),
+ index_(0) {}
+
+bool ValueDestination::Init(Scope* exec_scope,
+ const ParseNode* dest,
+ const BinaryOpNode* op_node,
+ Err* err) {
+ // Check for standard variable set.
+ const IdentifierNode* dest_identifier = dest->AsIdentifier();
+ if (dest_identifier) {
+ type_ = SCOPE;
+ scope_ = exec_scope;
+ name_token_ = &dest_identifier->value();
+ return true;
+ }
+
+ // Check for array and scope accesses. The base (array or scope variable
+ // name) must always be defined ahead of time.
+ const AccessorNode* dest_accessor = dest->AsAccessor();
+ if (!dest_accessor) {
+ *err = Err(op_node, "Assignment requires a lvalue.",
+ "This thing on the left is not an identifier or accessor.");
+ err->AppendRange(dest->GetRange());
+ return false;
+ }
+
+ // Known to be an accessor.
+ std::string_view base_str = dest_accessor->base().value();
+ Value* base =
+ exec_scope->GetMutableValue(base_str, Scope::SEARCH_CURRENT, false);
+ if (!base) {
+ // Base is either undefined or it's defined but not in the current scope.
+ // Make a good error message.
+ if (exec_scope->GetValue(base_str, false)) {
+ *err = Err(
+ dest_accessor->base(), "Suspicious in-place modification.",
+ "This variable exists in a containing scope. Normally, writing to it "
+ "would\nmake a copy of it into the current scope with the modified "
+ "version. But\nhere you're modifying only an element of a scope or "
+ "list object. It's unlikely\nyou meant to copy the entire thing just "
+ "to modify this part of it.\n"
+ "\n"
+ "If you really wanted to do this, do:\n"
+ " " +
+ std::string(base_str) + " = " + std::string(base_str) +
+ "\n"
+ "to copy it into the current scope before doing this operation.");
+ } else {
+ *err = Err(dest_accessor->base(), "Undefined identifier.");
+ }
+ return false;
+ }
+
+ if (dest_accessor->subscript()) {
+ // List access with an index.
+ if (!base->VerifyTypeIs(Value::LIST, err)) {
+ // Errors here will confusingly refer to the variable declaration (since
+ // that's all Value knows) rather than the list access. So rewrite the
+ // error location to refer to the base value's location.
+ *err = Err(dest_accessor->base(), err->message(), err->help_text());
+ return false;
+ }
+
+ type_ = LIST;
+ list_ = base;
+ return dest_accessor->ComputeAndValidateListIndex(
+ exec_scope, base->list_value().size(), &index_, err);
+ }
+
+ // Scope access with a dot.
+ if (!base->VerifyTypeIs(Value::SCOPE, err)) {
+ // As the for the list index case above, rewrite the error location.
+ *err = Err(dest_accessor->base(), err->message(), err->help_text());
+ return false;
+ }
+ type_ = SCOPE;
+ scope_ = base->scope_value();
+ name_token_ = &dest_accessor->member()->value();
+ return true;
+}
+
+const Value* ValueDestination::GetExistingValue() const {
+ if (type_ == SCOPE)
+ return scope_->GetValue(name_token_->value(), true);
+ else if (type_ == LIST)
+ return &list_->list_value()[index_];
+ return nullptr;
+}
+
+Value* ValueDestination::GetExistingMutableValueIfExists(
+ const ParseNode* origin) {
+ if (type_ == SCOPE) {
+ Value* value = scope_->GetMutableValue(name_token_->value(),
+ Scope::SEARCH_CURRENT, false);
+ if (value) {
+ // The value will be written to, reset its tracking information.
+ value->set_origin(origin);
+ scope_->MarkUnused(name_token_->value());
+ }
+ }
+ if (type_ == LIST)
+ return &list_->list_value()[index_];
+ return nullptr;
+}
+
+Value* ValueDestination::SetValue(Value value, const ParseNode* set_node) {
+ if (type_ == SCOPE) {
+ return scope_->SetValue(name_token_->value(), std::move(value), set_node);
+ } else if (type_ == LIST) {
+ Value* dest = &list_->list_value()[index_];
+ *dest = std::move(value);
+ return dest;
+ }
+ return nullptr;
+}
+
+void ValueDestination::MakeUndefinedIdentifierForModifyError(Err* err) {
+ // When Init() succeeds, the base of any accessor has already been resolved
+ // and that list indices are in-range. This means any undefined identifiers
+ // are for scope accesses.
+ DCHECK(type_ == SCOPE);
+ *err = Err(*name_token_, "Undefined identifier.");
+}
+
+// Computes an error message for overwriting a nonempty list/scope with another.
+Err MakeOverwriteError(const BinaryOpNode* op_node, const Value& old_value) {
+ std::string type_name;
+ std::string empty_def;
+
+ if (old_value.type() == Value::LIST) {
+ type_name = "list";
+ empty_def = "[]";
+ } else if (old_value.type() == Value::SCOPE) {
+ type_name = "scope";
+ empty_def = "{}";
+ } else {
+ NOTREACHED();
+ }
+
+ Err result(op_node->left()->GetRange(),
+ "Replacing nonempty " + type_name + ".",
+ "This overwrites a previously-defined nonempty " + type_name +
+ " with another nonempty " + type_name + ".");
+ result.AppendSubErr(Err(
+ old_value, "for previous definition",
+ "Did you mean to append/modify instead? If you really want to overwrite, "
+ "do:\n"
+ " foo = " +
+ empty_def + "\nbefore reassigning."));
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+Err MakeIncompatibleTypeError(const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right) {
+ std::string msg = std::string("You can't do <") +
+ Value::DescribeType(left.type()) + "> " +
+ std::string(op_node->op().value()) + " <" +
+ Value::DescribeType(right.type()) + ">.";
+ if (left.type() == Value::LIST) {
+ // Append extra hint for list stuff.
+ msg +=
+ "\n\nHint: If you're attempting to add or remove a single item from "
+ " a list, use \"foo + [ bar ]\".";
+ }
+ return Err(op_node, "Incompatible types for binary operator.", msg);
+}
+
+Value GetValueOrFillError(const BinaryOpNode* op_node,
+ const ParseNode* node,
+ const char* name,
+ Scope* scope,
+ Err* err) {
+ Value value = node->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (value.type() == Value::NONE) {
+ *err = Err(op_node->op(), "Operator requires a value.",
+ "This thing on the " + std::string(name) +
+ " does not evaluate to a value.");
+ err->AppendRange(node->GetRange());
+ return Value();
+ }
+ return value;
+}
+
+void RemoveMatchesFromList(const BinaryOpNode* op_node,
+ Value* list,
+ const Value& to_remove,
+ Err* err) {
+ std::vector<Value>& v = list->list_value();
+ switch (to_remove.type()) {
+ case Value::BOOLEAN:
+ case Value::INTEGER: // Filter out the individual int/string.
+ case Value::STRING:
+ case Value::SCOPE: {
+ bool found_match = false;
+ for (size_t i = 0; i < v.size(); /* nothing */) {
+ if (v[i] == to_remove) {
+ found_match = true;
+ v.erase(v.begin() + i);
+ } else {
+ i++;
+ }
+ }
+ if (!found_match) {
+ *err = Err(to_remove.origin()->GetRange(), "Item not found",
+ "You were trying to remove " + to_remove.ToString(true) +
+ "\nfrom the list but it wasn't there.");
+ }
+ break;
+ }
+
+ case Value::LIST: // Filter out each individual thing.
+ for (const auto& elem : to_remove.list_value()) {
+ // TODO(brettw) if the nested item is a list, we may want to search
+ // for the literal list rather than remote the items in it.
+ RemoveMatchesFromList(op_node, list, elem, err);
+ if (err->has_error())
+ return;
+ }
+ break;
+
+ case Value::NONE:
+ break;
+ }
+}
+
+// Assignment -----------------------------------------------------------------
+
+// We return a null value from this rather than the result of doing the append.
+// See ValuePlusEquals for rationale.
+Value ExecuteEquals(Scope* exec_scope,
+ const BinaryOpNode* op_node,
+ ValueDestination* dest,
+ Value right,
+ Err* err) {
+ const Value* old_value = dest->GetExistingValue();
+ if (old_value) {
+ // Check for overwriting nonempty scopes or lists with other nonempty
+ // scopes or lists. This prevents mistakes that clobber a value rather than
+ // appending to it. For cases where a user meant to clear a value, allow
+ // overwriting a nonempty list/scope with an empty one, which can then be
+ // modified.
+ if (old_value->type() == Value::LIST && right.type() == Value::LIST &&
+ !old_value->list_value().empty() && !right.list_value().empty()) {
+ *err = MakeOverwriteError(op_node, *old_value);
+ return Value();
+ } else if (old_value->type() == Value::SCOPE &&
+ right.type() == Value::SCOPE &&
+ old_value->scope_value()->HasValues(Scope::SEARCH_CURRENT) &&
+ right.scope_value()->HasValues(Scope::SEARCH_CURRENT)) {
+ *err = MakeOverwriteError(op_node, *old_value);
+ return Value();
+ }
+ }
+
+ dest->SetValue(std::move(right), op_node->right());
+ return Value();
+}
+
+// Plus/minus ------------------------------------------------------------------
+
+// allow_left_type_conversion indicates if we're allowed to change the type of
+// the left value. This is set to true when doing +, and false when doing +=.
+Value ExecutePlus(const BinaryOpNode* op_node,
+ Value left,
+ Value right,
+ bool allow_left_type_conversion,
+ Err* err) {
+ // Left-hand-side integer.
+ if (left.type() == Value::INTEGER) {
+ if (right.type() == Value::INTEGER) {
+ // Int + int -> addition.
+ return Value(op_node, left.int_value() + right.int_value());
+ } else if (right.type() == Value::STRING && allow_left_type_conversion) {
+ // Int + string -> string concat.
+ return Value(op_node, base::Int64ToString(left.int_value()) +
+ right.string_value());
+ }
+ *err = MakeIncompatibleTypeError(op_node, left, right);
+ return Value();
+ }
+
+ // Left-hand-side string.
+ if (left.type() == Value::STRING) {
+ if (right.type() == Value::INTEGER) {
+ // String + int -> string concat.
+ return Value(op_node, left.string_value() +
+ base::Int64ToString(right.int_value()));
+ } else if (right.type() == Value::STRING) {
+ // String + string -> string concat. Since the left is passed by copy
+ // we can avoid realloc if there is enough buffer by appending to left
+ // and assigning.
+ left.string_value().append(right.string_value());
+ return left; // FIXME(brettw) des this copy?
+ }
+ *err = MakeIncompatibleTypeError(op_node, left, right);
+ return Value();
+ }
+
+ // Left-hand-side list. The only valid thing is to add another list.
+ if (left.type() == Value::LIST && right.type() == Value::LIST) {
+ // Since left was passed by copy, avoid realloc by destructively appending
+ // to it and using that as the result.
+ for (Value& value : right.list_value())
+ left.list_value().push_back(std::move(value));
+ return left; // FIXME(brettw) does this copy?
+ }
+
+ *err = MakeIncompatibleTypeError(op_node, left, right);
+ return Value();
+}
+
+// Left is passed by value because it will be modified in-place and returned
+// for the list case.
+Value ExecuteMinus(const BinaryOpNode* op_node,
+ Value left,
+ const Value& right,
+ Err* err) {
+ // Left-hand-side int. The only thing to do is subtract another int.
+ if (left.type() == Value::INTEGER && right.type() == Value::INTEGER) {
+ // Int - int -> subtraction.
+ return Value(op_node, left.int_value() - right.int_value());
+ }
+
+ // Left-hand-side list. The only thing to do is subtract another list.
+ if (left.type() == Value::LIST && right.type() == Value::LIST) {
+ // In-place modify left and return it.
+ RemoveMatchesFromList(op_node, &left, right, err);
+ return left;
+ }
+
+ *err = MakeIncompatibleTypeError(op_node, left, right);
+ return Value();
+}
+
+// In-place plus/minus ---------------------------------------------------------
+
+void ExecutePlusEquals(Scope* exec_scope,
+ const BinaryOpNode* op_node,
+ ValueDestination* dest,
+ Value right,
+ Err* err) {
+ // There are several cases. Some things we can convert "foo += bar" to
+ // "foo = foo + bar". Some cases we can't (the 'sources' variable won't
+ // get the right filtering on the list). Some cases we don't want to (lists
+ // and strings will get unnecessary copying so we can to optimize these).
+ //
+ // - Value is already mutable in the current scope:
+ // 1. List/string append: use it.
+ // 2. Other types: fall back to "foo = foo + bar"
+ //
+ // - Value is not mutable in the current scope:
+ // 3. List/string append: copy into current scope and append to that.
+ // 4. Other types: fall back to "foo = foo + bar"
+ //
+ // The common case is to use += for list and string appends in the local
+ // scope, so this is written to avoid multiple variable lookups in that case.
+ Value* mutable_dest = dest->GetExistingMutableValueIfExists(op_node);
+ if (!mutable_dest) {
+ const Value* existing_value = dest->GetExistingValue();
+ if (!existing_value) {
+ // Undefined left-hand-size for +=.
+ dest->MakeUndefinedIdentifierForModifyError(err);
+ return;
+ }
+
+ if (existing_value->type() != Value::STRING &&
+ existing_value->type() != Value::LIST) {
+ // Case #4 above.
+ dest->SetValue(
+ ExecutePlus(op_node, *existing_value, std::move(right), false, err),
+ op_node);
+ return;
+ }
+
+ // Case #3 above, copy to current scope and fall-through to appending.
+ mutable_dest = dest->SetValue(*existing_value, op_node);
+ } else if (mutable_dest->type() != Value::STRING &&
+ mutable_dest->type() != Value::LIST) {
+ // Case #2 above.
+ dest->SetValue(
+ ExecutePlus(op_node, *mutable_dest, std::move(right), false, err),
+ op_node);
+ return;
+ } // "else" is case #1 above.
+
+ if (mutable_dest->type() == Value::STRING) {
+ if (right.type() == Value::INTEGER) {
+ // String + int -> string concat.
+ mutable_dest->string_value().append(
+ base::Int64ToString(right.int_value()));
+ } else if (right.type() == Value::STRING) {
+ // String + string -> string concat.
+ mutable_dest->string_value().append(right.string_value());
+ } else {
+ *err = MakeIncompatibleTypeError(op_node, *mutable_dest, right);
+ }
+ } else if (mutable_dest->type() == Value::LIST) {
+ // List concat.
+ if (right.type() == Value::LIST) {
+ // Normal list concat. This is a destructive move.
+ for (Value& value : right.list_value())
+ mutable_dest->list_value().push_back(std::move(value));
+ } else {
+ *err = Err(op_node->op(), "Incompatible types to add.",
+ "To append a single item to a list do \"foo += [ bar ]\".");
+ }
+ }
+}
+
+void ExecuteMinusEquals(const BinaryOpNode* op_node,
+ ValueDestination* dest,
+ const Value& right,
+ Err* err) {
+ // Like the += case, we can convert "foo -= bar" to "foo = foo - bar". Since
+ // there is no sources filtering, this is always semantically valid. The
+ // only case we don't do it is for lists in the current scope which is the
+ // most common case, and also the one that can be optimized the most by
+ // doing it in-place.
+ Value* mutable_dest = dest->GetExistingMutableValueIfExists(op_node);
+ if (!mutable_dest ||
+ (mutable_dest->type() != Value::LIST || right.type() != Value::LIST)) {
+ const Value* existing_value = dest->GetExistingValue();
+ if (!existing_value) {
+ // Undefined left-hand-size for -=.
+ dest->MakeUndefinedIdentifierForModifyError(err);
+ return;
+ }
+ dest->SetValue(ExecuteMinus(op_node, *existing_value, right, err), op_node);
+ return;
+ }
+
+ // In-place removal of items from "right".
+ RemoveMatchesFromList(op_node, mutable_dest, right, err);
+}
+
+// Comparison -----------------------------------------------------------------
+
+Value ExecuteEqualsEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left == right)
+ return Value(op_node, true);
+ return Value(op_node, false);
+}
+
+Value ExecuteNotEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ // Evaluate in terms of ==.
+ Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
+ result.boolean_value() = !result.boolean_value();
+ return result;
+}
+
+Value FillNeedsTwoIntegersError(const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ *err = Err(op_node, "Comparison requires two integers.",
+ "This operator can only compare two integers.");
+ err->AppendRange(left.origin()->GetRange());
+ err->AppendRange(right.origin()->GetRange());
+ return Value();
+}
+
+Value ExecuteLessEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsTwoIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() <= right.int_value());
+}
+
+Value ExecuteGreaterEquals(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsTwoIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() >= right.int_value());
+}
+
+Value ExecuteGreater(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsTwoIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() > right.int_value());
+}
+
+Value ExecuteLess(Scope* scope,
+ const BinaryOpNode* op_node,
+ const Value& left,
+ const Value& right,
+ Err* err) {
+ if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
+ return FillNeedsTwoIntegersError(op_node, left, right, err);
+ return Value(op_node, left.int_value() < right.int_value());
+}
+
+// Binary ----------------------------------------------------------------------
+
+Value ExecuteOr(Scope* scope,
+ const BinaryOpNode* op_node,
+ const ParseNode* left_node,
+ const ParseNode* right_node,
+ Err* err) {
+ Value left = GetValueOrFillError(op_node, left_node, "left", scope, err);
+ if (err->has_error())
+ return Value();
+ if (left.type() != Value::BOOLEAN) {
+ *err = Err(op_node->left(), "Left side of || operator is not a boolean.",
+ "Type is \"" + std::string(Value::DescribeType(left.type())) +
+ "\" instead.");
+ return Value();
+ }
+ if (left.boolean_value())
+ return Value(op_node, left.boolean_value());
+
+ Value right = GetValueOrFillError(op_node, right_node, "right", scope, err);
+ if (err->has_error())
+ return Value();
+ if (right.type() != Value::BOOLEAN) {
+ *err = Err(op_node->right(), "Right side of || operator is not a boolean.",
+ "Type is \"" + std::string(Value::DescribeType(right.type())) +
+ "\" instead.");
+ return Value();
+ }
+
+ return Value(op_node, left.boolean_value() || right.boolean_value());
+}
+
+Value ExecuteAnd(Scope* scope,
+ const BinaryOpNode* op_node,
+ const ParseNode* left_node,
+ const ParseNode* right_node,
+ Err* err) {
+ Value left = GetValueOrFillError(op_node, left_node, "left", scope, err);
+ if (err->has_error())
+ return Value();
+ if (left.type() != Value::BOOLEAN) {
+ *err = Err(op_node->left(), "Left side of && operator is not a boolean.",
+ "Type is \"" + std::string(Value::DescribeType(left.type())) +
+ "\" instead.");
+ return Value();
+ }
+ if (!left.boolean_value())
+ return Value(op_node, left.boolean_value());
+
+ Value right = GetValueOrFillError(op_node, right_node, "right", scope, err);
+ if (err->has_error())
+ return Value();
+ if (right.type() != Value::BOOLEAN) {
+ *err = Err(op_node->right(), "Right side of && operator is not a boolean.",
+ "Type is \"" + std::string(Value::DescribeType(right.type())) +
+ "\" instead.");
+ return Value();
+ }
+ return Value(op_node, left.boolean_value() && right.boolean_value());
+}
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+Value ExecuteUnaryOperator(Scope* scope,
+ const UnaryOpNode* op_node,
+ const Value& expr,
+ Err* err) {
+ DCHECK(op_node->op().type() == Token::BANG);
+
+ if (expr.type() != Value::BOOLEAN) {
+ *err = Err(op_node, "Operand of ! operator is not a boolean.",
+ "Type is \"" + std::string(Value::DescribeType(expr.type())) +
+ "\" instead.");
+ return Value();
+ }
+ // TODO(scottmg): Why no unary minus?
+ return Value(op_node, !expr.boolean_value());
+}
+
+Value ExecuteBinaryOperator(Scope* scope,
+ const BinaryOpNode* op_node,
+ const ParseNode* left,
+ const ParseNode* right,
+ Err* err) {
+ const Token& op = op_node->op();
+
+ // First handle the ones that take an lvalue.
+ if (op.type() == Token::EQUAL || op.type() == Token::PLUS_EQUALS ||
+ op.type() == Token::MINUS_EQUALS) {
+ // Compute the left side.
+ ValueDestination dest;
+ if (!dest.Init(scope, left, op_node, err))
+ return Value();
+
+ // Compute the right side.
+ Value right_value = right->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (right_value.type() == Value::NONE) {
+ *err = Err(op, "Operator requires a rvalue.",
+ "This thing on the right does not evaluate to a value.");
+ err->AppendRange(right->GetRange());
+ return Value();
+ }
+
+ // "foo += bar" (same for "-=") is converted to "foo = foo + bar" here, but
+ // we pass the original value of "foo" by pointer to avoid a copy.
+ if (op.type() == Token::EQUAL) {
+ ExecuteEquals(scope, op_node, &dest, std::move(right_value), err);
+ } else if (op.type() == Token::PLUS_EQUALS) {
+ ExecutePlusEquals(scope, op_node, &dest, std::move(right_value), err);
+ } else if (op.type() == Token::MINUS_EQUALS) {
+ ExecuteMinusEquals(op_node, &dest, right_value, err);
+ } else {
+ NOTREACHED();
+ }
+ return Value();
+ }
+
+ // ||, &&. Passed the node instead of the value so that they can avoid
+ // evaluating the RHS on early-out.
+ if (op.type() == Token::BOOLEAN_OR)
+ return ExecuteOr(scope, op_node, left, right, err);
+ if (op.type() == Token::BOOLEAN_AND)
+ return ExecuteAnd(scope, op_node, left, right, err);
+
+ // Everything else works on the evaluated left and right values.
+ Value left_value = GetValueOrFillError(op_node, left, "left", scope, err);
+ if (err->has_error())
+ return Value();
+ Value right_value = GetValueOrFillError(op_node, right, "right", scope, err);
+ if (err->has_error())
+ return Value();
+
+ // +, -.
+ if (op.type() == Token::MINUS)
+ return ExecuteMinus(op_node, std::move(left_value), right_value, err);
+ if (op.type() == Token::PLUS) {
+ return ExecutePlus(op_node, std::move(left_value), std::move(right_value),
+ true, err);
+ }
+
+ // Comparisons.
+ if (op.type() == Token::EQUAL_EQUAL)
+ return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
+ if (op.type() == Token::NOT_EQUAL)
+ return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
+ if (op.type() == Token::GREATER_EQUAL)
+ return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
+ if (op.type() == Token::LESS_EQUAL)
+ return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
+ if (op.type() == Token::GREATER_THAN)
+ return ExecuteGreater(scope, op_node, left_value, right_value, err);
+ if (op.type() == Token::LESS_THAN)
+ return ExecuteLess(scope, op_node, left_value, right_value, err);
+
+ return Value();
+}
diff --git a/gn/tools/gn/operators.h b/gn/src/gn/operators.h
index 82ff68e7578..82ff68e7578 100644
--- a/gn/tools/gn/operators.h
+++ b/gn/src/gn/operators.h
diff --git a/gn/src/gn/operators_unittest.cc b/gn/src/gn/operators_unittest.cc
new file mode 100644
index 00000000000..415596c978f
--- /dev/null
+++ b/gn/src/gn/operators_unittest.cc
@@ -0,0 +1,413 @@
+// 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 "gn/operators.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "gn/parse_tree.h"
+#include "gn/pattern.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+bool IsValueIntegerEqualing(const Value& v, int64_t i) {
+ if (v.type() != Value::INTEGER)
+ return false;
+ return v.int_value() == i;
+}
+
+bool IsValueStringEqualing(const Value& v, const char* s) {
+ if (v.type() != Value::STRING)
+ return false;
+ return v.string_value() == s;
+}
+
+// This parse node is for passing to tests. It returns a canned value for
+// Execute().
+class TestParseNode : public ParseNode {
+ public:
+ TestParseNode(const Value& v) : value_(v) {}
+
+ Value Execute(Scope* scope, Err* err) const override { return value_; }
+ LocationRange GetRange() const override { return LocationRange(); }
+ Err MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const override {
+ return Err(this, msg);
+ }
+ base::Value GetJSONNode() const override {
+ return base::Value();
+ }
+
+ private:
+ Value value_;
+};
+
+// Sets up a BinaryOpNode for testing.
+class TestBinaryOpNode : public BinaryOpNode {
+ public:
+ // Input token value string must outlive class.
+ TestBinaryOpNode(Token::Type op_token_type, const char* op_token_value)
+ : BinaryOpNode(),
+ op_token_ownership_(Location(), op_token_type, op_token_value) {
+ set_op(op_token_ownership_);
+ }
+
+ void SetLeftToValue(const Value& value) {
+ set_left(std::make_unique<TestParseNode>(value));
+ }
+
+ // Sets the left-hand side of the operator to an identifier node, this is
+ // used for testing assignments. Input string must outlive class.
+ void SetLeftToIdentifier(const char* identifier) {
+ left_identifier_token_ownership_ =
+ Token(Location(), Token::IDENTIFIER, identifier);
+ set_left(
+ std::make_unique<IdentifierNode>(left_identifier_token_ownership_));
+ }
+
+ void SetRightToValue(const Value& value) {
+ set_right(std::make_unique<TestParseNode>(value));
+ }
+ void SetRightToListOfValue(const Value& value) {
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(value);
+ set_right(std::make_unique<TestParseNode>(list));
+ }
+ void SetRightToListOfValue(const Value& value1, const Value& value2) {
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(value1);
+ list.list_value().push_back(value2);
+ set_right(std::make_unique<TestParseNode>(list));
+ }
+
+ private:
+ // The base class takes the Token by reference, this manages the lifetime.
+ Token op_token_ownership_;
+
+ // When setting the left to an identifier, this manages the lifetime of
+ // the identifier token.
+ Token left_identifier_token_ownership_;
+};
+
+} // namespace
+
+TEST(Operators, SourcesAppend) {
+ Err err;
+ TestWithScope setup;
+
+ // Set up "sources" with an empty list.
+ const char sources[] = "sources";
+ setup.scope()->SetValue(sources, Value(nullptr, Value::LIST), nullptr);
+
+ // Set up the operator.
+ TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
+ node.SetLeftToIdentifier(sources);
+
+ // Append an integer.
+ node.SetRightToListOfValue(Value(nullptr, static_cast<int64_t>(5)));
+ node.Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Append a string that doesn't match the pattern, it should get appended.
+ const char string1[] = "good";
+ node.SetRightToListOfValue(Value(nullptr, string1));
+ node.Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Append a list with the two strings from above.
+ node.SetRightToListOfValue(Value(nullptr, string1));
+ node.Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // The sources variable in the scope should now have: [ 5, "good", "good" ]
+ const Value* value = setup.scope()->GetValue(sources);
+ ASSERT_TRUE(value);
+ ASSERT_EQ(Value::LIST, value->type());
+ ASSERT_EQ(3u, value->list_value().size());
+ EXPECT_TRUE(IsValueIntegerEqualing(value->list_value()[0], 5));
+ EXPECT_TRUE(IsValueStringEqualing(value->list_value()[1], "good"));
+ EXPECT_TRUE(IsValueStringEqualing(value->list_value()[2], "good"));
+}
+
+// Note that the SourcesAppend test above tests the basic list + list features,
+// this test handles the other cases.
+TEST(Operators, ListAppend) {
+ Err err;
+ TestWithScope setup;
+
+ // Set up "foo" with an empty list.
+ const char foo[] = "foo";
+ setup.scope()->SetValue(foo, Value(nullptr, Value::LIST), nullptr);
+
+ // Set up the operator to append to "foo".
+ TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
+ node.SetLeftToIdentifier(foo);
+
+ // Append a list with a list, the result should be a nested list.
+ Value inner_list(nullptr, Value::LIST);
+ inner_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(12)));
+ node.SetRightToListOfValue(inner_list);
+
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Return from the operator should always be "none", it should update the
+ // value only.
+ EXPECT_EQ(Value::NONE, ret.type());
+
+ // The value should be updated with "[ [ 12 ] ]"
+ Value result = *setup.scope()->GetValue(foo);
+ ASSERT_EQ(Value::LIST, result.type());
+ ASSERT_EQ(1u, result.list_value().size());
+ ASSERT_EQ(Value::LIST, result.list_value()[0].type());
+ ASSERT_EQ(1u, result.list_value()[0].list_value().size());
+ ASSERT_EQ(Value::INTEGER, result.list_value()[0].list_value()[0].type());
+ ASSERT_EQ(12, result.list_value()[0].list_value()[0].int_value());
+
+ // Try to append an integer and a string directly (e.g. foo += "hi").
+ // This should fail.
+ const char str_str[] = "\"hi\"";
+ Token str(Location(), Token::STRING, str_str);
+ node.set_right(std::make_unique<LiteralNode>(str));
+ ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+
+ node.SetRightToValue(Value(nullptr, static_cast<int64_t>(12)));
+ ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(Operators, ListRemove) {
+ Err err;
+ TestWithScope setup;
+
+ const char foo_str[] = "foo";
+ const char bar_str[] = "bar";
+ Value test_list(nullptr, Value::LIST);
+ test_list.list_value().push_back(Value(nullptr, foo_str));
+ test_list.list_value().push_back(Value(nullptr, bar_str));
+ test_list.list_value().push_back(Value(nullptr, foo_str));
+
+ // Set up "var" with an the test list.
+ const char var[] = "var";
+ setup.scope()->SetValue(var, test_list, nullptr);
+
+ TestBinaryOpNode node(Token::MINUS_EQUALS, "-=");
+ node.SetLeftToIdentifier(var);
+
+ // Subtract a list consisting of "foo".
+ node.SetRightToListOfValue(Value(nullptr, foo_str));
+ Value result = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // -= returns an empty value to reduce the possibility of writing confusing
+ // cases like foo = bar += 1.
+ EXPECT_EQ(Value::NONE, result.type());
+
+ // The "var" variable should have been updated. Both instances of "foo" are
+ // deleted.
+ const Value* new_value = setup.scope()->GetValue(var);
+ ASSERT_TRUE(new_value);
+ ASSERT_EQ(Value::LIST, new_value->type());
+ ASSERT_EQ(1u, new_value->list_value().size());
+ ASSERT_EQ(Value::STRING, new_value->list_value()[0].type());
+ EXPECT_EQ("bar", new_value->list_value()[0].string_value());
+}
+
+TEST(Operators, ListSubtractWithScope) {
+ Err err;
+ TestWithScope setup;
+
+ Scope* scope_a = new Scope(setup.settings());
+ Value scopeval_a(nullptr, std::unique_ptr<Scope>(scope_a));
+ scope_a->SetValue("a", Value(nullptr, "foo"), nullptr);
+
+ Scope* scope_b = new Scope(setup.settings());
+ Value scopeval_b(nullptr, std::unique_ptr<Scope>(scope_b));
+ scope_b->SetValue("b", Value(nullptr, "bar"), nullptr);
+
+ Value lval(nullptr, Value::LIST);
+ lval.list_value().push_back(scopeval_a);
+ lval.list_value().push_back(scopeval_b);
+
+ Scope* scope_a_other = new Scope(setup.settings());
+ Value scopeval_a_other(nullptr, std::unique_ptr<Scope>(scope_a_other));
+ scope_a_other->SetValue("a", Value(nullptr, "foo"), nullptr);
+
+ Value rval(nullptr, Value::LIST);
+ rval.list_value().push_back(scopeval_a_other);
+
+ TestBinaryOpNode node(Token::MINUS, "-");
+ node.SetLeftToValue(lval);
+ node.SetRightToValue(rval);
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(Value::LIST, ret.type());
+
+ std::vector<Value> expected;
+ Scope* scope_expected = new Scope(setup.settings());
+ Value scopeval_expected(nullptr, std::unique_ptr<Scope>(scope_expected));
+ scope_expected->SetValue("b", Value(nullptr, "bar"), nullptr);
+ expected.push_back(scopeval_expected);
+ EXPECT_EQ(expected, ret.list_value());
+}
+
+TEST(Operators, IntegerAdd) {
+ Err err;
+ TestWithScope setup;
+
+ TestBinaryOpNode node(Token::PLUS, "+");
+ node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
+ node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(Value::INTEGER, ret.type());
+ EXPECT_EQ(579, ret.int_value());
+}
+
+TEST(Operators, IntegerSubtract) {
+ Err err;
+ TestWithScope setup;
+
+ TestBinaryOpNode node(Token::MINUS, "-");
+ node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
+ node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(Value::INTEGER, ret.type());
+ EXPECT_EQ(-333, ret.int_value());
+}
+
+TEST(Operators, ShortCircuitAnd) {
+ Err err;
+ TestWithScope setup;
+
+ // Set a && operator with the left to false.
+ TestBinaryOpNode node(Token::BOOLEAN_AND, "&&");
+ node.SetLeftToValue(Value(nullptr, false));
+
+ // Set right as foo, but don't define a value for it.
+ const char foo[] = "foo";
+ Token identifier_token(Location(), Token::IDENTIFIER, foo);
+ node.set_right(std::make_unique<IdentifierNode>(identifier_token));
+
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ EXPECT_FALSE(err.has_error());
+}
+
+TEST(Operators, ShortCircuitOr) {
+ Err err;
+ TestWithScope setup;
+
+ // Set a || operator with the left to true.
+ TestBinaryOpNode node(Token::BOOLEAN_OR, "||");
+ node.SetLeftToValue(Value(nullptr, true));
+
+ // Set right as foo, but don't define a value for it.
+ const char foo[] = "foo";
+ Token identifier_token(Location(), Token::IDENTIFIER, foo);
+ node.set_right(std::make_unique<IdentifierNode>(identifier_token));
+
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ EXPECT_FALSE(err.has_error());
+}
+
+// Overwriting nonempty lists and scopes with other nonempty lists and scopes
+// should be disallowed.
+TEST(Operators, NonemptyOverwriting) {
+ Err err;
+ TestWithScope setup;
+
+ // Set up "foo" with a nonempty list.
+ const char foo[] = "foo";
+ Value old_value(nullptr, Value::LIST);
+ old_value.list_value().push_back(Value(nullptr, "string"));
+ setup.scope()->SetValue(foo, old_value, nullptr);
+
+ TestBinaryOpNode node(Token::EQUAL, "=");
+ node.SetLeftToIdentifier(foo);
+
+ // Assigning a nonempty list should fail.
+ node.SetRightToListOfValue(Value(nullptr, "string"));
+ node.Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Replacing nonempty list.", err.message());
+ err = Err();
+
+ // Assigning an empty list should succeed.
+ node.SetRightToValue(Value(nullptr, Value::LIST));
+ node.Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error());
+ const Value* new_value = setup.scope()->GetValue(foo);
+ ASSERT_TRUE(new_value);
+ ASSERT_EQ(Value::LIST, new_value->type());
+ ASSERT_TRUE(new_value->list_value().empty());
+
+ // Set up "foo" with a nonempty scope.
+ const char bar[] = "bar";
+ old_value = Value(nullptr, std::make_unique<Scope>(setup.settings()));
+ old_value.scope_value()->SetValue(bar, Value(nullptr, "bar"), nullptr);
+ setup.scope()->SetValue(foo, old_value, nullptr);
+
+ // Assigning a nonempty scope should fail (re-use old_value copy).
+ node.SetRightToValue(old_value);
+ node.Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Replacing nonempty scope.", err.message());
+ err = Err();
+
+ // Assigning an empty list should succeed.
+ node.SetRightToValue(
+ Value(nullptr, std::make_unique<Scope>(setup.settings())));
+ node.Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error());
+ new_value = setup.scope()->GetValue(foo);
+ ASSERT_TRUE(new_value);
+ ASSERT_EQ(Value::SCOPE, new_value->type());
+ ASSERT_FALSE(new_value->scope_value()->HasValues(Scope::SEARCH_CURRENT));
+}
+
+// Tests this case:
+// foo = 1
+// target(...) {
+// foo += 1
+//
+// This should mark the outer "foo" as used, and the inner "foo" as unused.
+TEST(Operators, PlusEqualsUsed) {
+ Err err;
+ TestWithScope setup;
+
+ // Outer "foo" definition, it should be unused.
+ const char foo[] = "foo";
+ Value old_value(nullptr, static_cast<int64_t>(1));
+ setup.scope()->SetValue(foo, old_value, nullptr);
+ EXPECT_TRUE(setup.scope()->IsSetButUnused(foo));
+
+ // Nested scope.
+ Scope nested(setup.scope());
+
+ // Run "foo += 1".
+ TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
+ node.SetLeftToIdentifier(foo);
+ node.SetRightToValue(Value(nullptr, static_cast<int64_t>(1)));
+ node.Execute(&nested, &err);
+ ASSERT_FALSE(err.has_error());
+
+ // Outer foo should be used, inner foo should not be.
+ EXPECT_FALSE(setup.scope()->IsSetButUnused(foo));
+ EXPECT_TRUE(nested.IsSetButUnused(foo));
+}
diff --git a/gn/tools/gn/ordered_set.h b/gn/src/gn/ordered_set.h
index fda4e121f5c..fda4e121f5c 100644
--- a/gn/tools/gn/ordered_set.h
+++ b/gn/src/gn/ordered_set.h
diff --git a/gn/src/gn/output_conversion.cc b/gn/src/gn/output_conversion.cc
new file mode 100644
index 00000000000..04017dd4e2b
--- /dev/null
+++ b/gn/src/gn/output_conversion.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 The Chromium 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 "gn/output_conversion.h"
+
+#include "gn/settings.h"
+#include "gn/value.h"
+
+namespace {
+
+void ToString(const Value& output, std::ostream& out) {
+ out << output.ToString(false);
+}
+
+void ToStringQuoted(const Value& output, std::ostream& out) {
+ out << "\"" << output.ToString(false) << "\"";
+}
+
+void Indent(int indent, std::ostream& out) {
+ for (int i = 0; i < indent; ++i)
+ out << " ";
+}
+
+// Forward declare so it can be used recursively.
+void RenderScopeToJSON(const Value& output, std::ostream& out, int indent);
+
+void RenderListToJSON(const Value& output, std::ostream& out, int indent) {
+ assert(indent > 0);
+ bool first = true;
+ out << "[\n";
+ for (const auto& value : output.list_value()) {
+ if (!first)
+ out << ",\n";
+ Indent(indent, out);
+ if (value.type() == Value::SCOPE)
+ RenderScopeToJSON(value, out, indent + 1);
+ else if (value.type() == Value::LIST)
+ RenderListToJSON(value, out, indent + 1);
+ else
+ out << value.ToString(true);
+ first = false;
+ }
+ out << "\n";
+ Indent(indent - 1, out);
+ out << "]";
+}
+
+void RenderScopeToJSON(const Value& output, std::ostream& out, int indent) {
+ assert(indent > 0);
+ Scope::KeyValueMap scope_values;
+ output.scope_value()->GetCurrentScopeValues(&scope_values);
+ bool first = true;
+ out << "{\n";
+ for (const auto& pair : scope_values) {
+ if (!first)
+ out << ",\n";
+ Indent(indent, out);
+ out << "\"" << pair.first << "\": ";
+ if (pair.second.type() == Value::SCOPE)
+ RenderScopeToJSON(pair.second, out, indent + 1);
+ else if (pair.second.type() == Value::LIST)
+ RenderListToJSON(pair.second, out, indent + 1);
+ else
+ out << pair.second.ToString(true);
+ first = false;
+ }
+ out << "\n";
+ Indent(indent - 1, out);
+ out << "}";
+}
+
+void OutputListLines(const Value& output, std::ostream& out) {
+ assert(output.type() == Value::LIST);
+ const std::vector<Value>& list = output.list_value();
+ for (const auto& cur : list)
+ out << cur.ToString(false) << "\n";
+}
+
+void OutputString(const Value& output, std::ostream& out) {
+ if (output.type() == Value::NONE)
+ return;
+ if (output.type() == Value::STRING) {
+ ToString(output, out);
+ return;
+ }
+ ToStringQuoted(output, out);
+}
+
+void OutputValue(const Value& output, std::ostream& out) {
+ if (output.type() == Value::NONE)
+ return;
+ if (output.type() == Value::STRING) {
+ ToStringQuoted(output, out);
+ return;
+ }
+ ToString(output, out);
+}
+
+// The direct Value::ToString call wraps the scope in '{}', which we don't want
+// here for the top-level scope being output.
+void OutputScope(const Value& output, std::ostream& out) {
+ Scope::KeyValueMap scope_values;
+ output.scope_value()->GetCurrentScopeValues(&scope_values);
+ for (const auto& pair : scope_values) {
+ out << " " << pair.first << " = " << pair.second.ToString(true) << "\n";
+ }
+}
+
+void OutputDefault(const Value& output, std::ostream& out) {
+ if (output.type() == Value::LIST)
+ OutputListLines(output, out);
+ else
+ ToString(output, out);
+}
+
+void OutputJSON(const Value& output, std::ostream& out) {
+ if (output.type() == Value::SCOPE) {
+ RenderScopeToJSON(output, out, /*indent=*/1);
+ return;
+ }
+ if (output.type() == Value::LIST) {
+ RenderListToJSON(output, out, /*indent=*/1);
+ return;
+ }
+ ToStringQuoted(output, out);
+}
+
+void DoConvertValueToOutput(const Value& output,
+ const std::string& output_conversion,
+ const Value& original_output_conversion,
+ std::ostream& out,
+ Err* err) {
+ if (output_conversion == "") {
+ OutputDefault(output, out);
+ } else if (output_conversion == "list lines") {
+ if (output.type() != Value::LIST) {
+ *err = Err(original_output_conversion, "Not a valid list.");
+ return;
+ }
+ OutputListLines(output, out);
+ } else if (output_conversion == "string") {
+ OutputString(output, out);
+ } else if (output_conversion == "value") {
+ OutputValue(output, out);
+ } else if (output_conversion == "json") {
+ OutputJSON(output, out);
+ } else if (output_conversion == "scope") {
+ if (output.type() != Value::SCOPE) {
+ *err = Err(original_output_conversion, "Not a valid scope.");
+ return;
+ }
+ OutputScope(output, out);
+ } else {
+ // If we make it here, we didn't match any of the valid options.
+ *err = Err(original_output_conversion, "Not a valid output_conversion.",
+ "Run gn help output_conversion to see your options.");
+ }
+}
+
+} // namespace
+
+void ConvertValueToOutput(const Settings* settings,
+ const Value& output,
+ const Value& output_conversion,
+ std::ostream& out,
+ Err* err) {
+ if (output_conversion.type() == Value::NONE) {
+ OutputDefault(output, out);
+ return;
+ }
+ if (!output_conversion.VerifyTypeIs(Value::STRING, err))
+ return;
+
+ DoConvertValueToOutput(output, output_conversion.string_value(),
+ output_conversion, out, err);
+}
diff --git a/gn/src/gn/output_conversion.h b/gn/src/gn/output_conversion.h
new file mode 100644
index 00000000000..09ca2548b87
--- /dev/null
+++ b/gn/src/gn/output_conversion.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_OUTPUT_CONVERSION_H_
+#define TOOLS_GN_OUTPUT_CONVERSION_H_
+
+#include <iosfwd>
+#include <string>
+
+class Err;
+class Settings;
+class Value;
+
+// Converts the given input Value to an output string (to be written to a file).
+// Conversions as specified in the output_conversion string will be performed.
+// The given ostream will be used for writing the resulting string.
+//
+// If the conversion string is invalid, the error will be set.
+void ConvertValueToOutput(const Settings* settings,
+ const Value& output,
+ const Value& output_conversion_value,
+ std::ostream& out,
+ Err* err);
+
+#endif // TOOLS_GN_OUTPUT_CONVERSION_H_
diff --git a/gn/src/gn/output_conversion_unittest.cc b/gn/src/gn/output_conversion_unittest.cc
new file mode 100644
index 00000000000..43fdad55a17
--- /dev/null
+++ b/gn/src/gn/output_conversion_unittest.cc
@@ -0,0 +1,351 @@
+// Copyright 2018 The Chromium 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 "gn/output_conversion.h"
+
+#include <sstream>
+
+#include "gn/err.h"
+#include "gn/input_conversion.h"
+#include "gn/scope.h"
+#include "gn/template.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+namespace {
+
+// InputConversion needs a global scheduler object.
+class OutputConversionTest : public TestWithScheduler {
+ public:
+ OutputConversionTest() = default;
+
+ const Settings* settings() { return setup_.settings(); }
+ Scope* scope() { return setup_.scope(); }
+
+ private:
+ TestWithScope setup_;
+};
+
+} // namespace
+
+TEST_F(OutputConversionTest, ListLines) {
+ Err err;
+ Value output(nullptr, Value::LIST);
+ output.list_value().push_back(Value(nullptr, ""));
+ output.list_value().push_back(Value(nullptr, "foo"));
+ output.list_value().push_back(Value(nullptr, ""));
+ output.list_value().push_back(Value(nullptr, "bar"));
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "list lines"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ("\nfoo\n\nbar\n", result.str());
+}
+
+TEST_F(OutputConversionTest, String) {
+ Err err;
+ Value output(nullptr, "foo bar");
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "foo bar");
+}
+
+TEST_F(OutputConversionTest, StringInt) {
+ Err err;
+ Value output(nullptr, int64_t(6));
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "\"6\"");
+}
+
+TEST_F(OutputConversionTest, StringBool) {
+ Err err;
+ Value output(nullptr, true);
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "\"true\"");
+}
+
+TEST_F(OutputConversionTest, StringList) {
+ Err err;
+ Value output(nullptr, Value::LIST);
+ output.list_value().push_back(Value(nullptr, "foo"));
+ output.list_value().push_back(Value(nullptr, "bar"));
+ output.list_value().push_back(Value(nullptr, int64_t(6)));
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "\"[\"foo\", \"bar\", 6]\"");
+}
+
+TEST_F(OutputConversionTest, StringScope) {
+ Err err;
+
+ auto new_scope = std::make_unique<Scope>(settings());
+ // Add some values to the scope.
+ Value value(nullptr, "hello");
+ new_scope->SetValue("v", value, nullptr);
+ std::string_view private_var_name("_private");
+ new_scope->SetValue(private_var_name, value, nullptr);
+
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
+ Value(nullptr, "string"), result, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "\"{\n _private = \"hello\"\n v = \"hello\"\n}\"");
+}
+
+TEST_F(OutputConversionTest, ValueString) {
+ Err err;
+ Value output(nullptr, "foo bar");
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "\"foo bar\"");
+}
+
+TEST_F(OutputConversionTest, ValueInt) {
+ Err err;
+ Value output(nullptr, int64_t(6));
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "6");
+}
+
+TEST_F(OutputConversionTest, ValueBool) {
+ Err err;
+ Value output(nullptr, true);
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "true");
+}
+
+TEST_F(OutputConversionTest, ValueList) {
+ Err err;
+ Value output(nullptr, Value::LIST);
+ output.list_value().push_back(Value(nullptr, "foo"));
+ output.list_value().push_back(Value(nullptr, "bar"));
+ output.list_value().push_back(Value(nullptr, int64_t(6)));
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
+ &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "[\"foo\", \"bar\", 6]");
+}
+
+TEST_F(OutputConversionTest, ValueScope) {
+ Err err;
+
+ auto new_scope = std::make_unique<Scope>(settings());
+ // Add some values to the scope.
+ Value value(nullptr, "hello");
+ new_scope->SetValue("v", value, nullptr);
+ std::string_view private_var_name("_private");
+ new_scope->SetValue(private_var_name, value, nullptr);
+
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
+ Value(nullptr, "value"), result, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "{\n _private = \"hello\"\n v = \"hello\"\n}");
+}
+
+TEST_F(OutputConversionTest, JSON) {
+ Err err;
+ auto new_scope = std::make_unique<Scope>(settings());
+ // Add some values to the scope.
+ Value a_value(nullptr, "foo");
+ new_scope->SetValue("a", a_value, nullptr);
+ Value b_value(nullptr, int64_t(6));
+ new_scope->SetValue("b", b_value, nullptr);
+
+ auto c_scope = std::make_unique<Scope>(settings());
+ Value e_value(nullptr, Value::LIST);
+ e_value.list_value().push_back(Value(nullptr, "bar"));
+
+ auto e_value_scope = std::make_unique<Scope>(settings());
+ Value f_value(nullptr, "baz");
+ e_value_scope->SetValue("f", f_value, nullptr);
+ e_value.list_value().push_back(Value(nullptr, std::move(e_value_scope)));
+
+ c_scope->SetValue("e", e_value, nullptr);
+
+ new_scope->SetValue("c", Value(nullptr, std::move(c_scope)), nullptr);
+
+ std::string expected(R"*({
+ "a": "foo",
+ "b": 6,
+ "c": {
+ "e": [
+ "bar",
+ {
+ "f": "baz"
+ }
+ ]
+ }
+})*");
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
+ Value(nullptr, "json"), result, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), expected);
+}
+
+TEST_F(OutputConversionTest, ValueEmpty) {
+ Err err;
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), Value(), Value(nullptr, ""), result, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "<void>");
+}
+
+TEST_F(OutputConversionTest, DefaultValue) {
+ Err err;
+ Value output(nullptr, "foo bar");
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, ""), result, &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(result.str(), "foo bar");
+}
+
+TEST_F(OutputConversionTest, DefaultListLines) {
+ Err err;
+ Value output(nullptr, Value::LIST);
+ output.list_value().push_back(Value(nullptr, ""));
+ output.list_value().push_back(Value(nullptr, "foo"));
+ output.list_value().push_back(Value(nullptr, ""));
+ output.list_value().push_back(Value(nullptr, "bar"));
+ std::ostringstream result;
+ ConvertValueToOutput(settings(), output, Value(nullptr, ""), result, &err);
+
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ("\nfoo\n\nbar\n", result.str());
+}
+
+TEST_F(OutputConversionTest, ReverseString) {
+ Err err;
+ std::string input("foo bar");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "string"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "string"), reverse,
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), input)
+ << "actual: " << reverse.str() << "expected:" << input;
+}
+
+TEST_F(OutputConversionTest, ReverseListLines) {
+ Err err;
+ std::string input("\nfoo\nbar\n\n");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "list lines"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "list lines"),
+ reverse, &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), input)
+ << "actual: " << reverse.str() << "expected:" << input;
+}
+
+TEST_F(OutputConversionTest, ReverseValueString) {
+ Err err;
+ std::string input("\"str\"");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), input)
+ << "actual: " << reverse.str() << "expected:" << input;
+}
+
+TEST_F(OutputConversionTest, ReverseValueInt) {
+ Err err;
+ std::string input("6");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), input)
+ << "actual: " << reverse.str() << "expected:" << input;
+}
+
+TEST_F(OutputConversionTest, ReverseValueList) {
+ Err err;
+ std::string input("[\"a\", 5]");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), input)
+ << "actual: " << reverse.str() << "expected:" << input;
+}
+
+TEST_F(OutputConversionTest, ReverseValueDict) {
+ Err err;
+ std::string input(" a = 5\n b = \"foo\"\n c = 7\n");
+ Value result = ConvertInputToValue(settings(), input, nullptr,
+ Value(nullptr, "scope"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "scope"), reverse,
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), input)
+ << "actual: " << reverse.str() << "expected:" << input;
+}
+
+TEST_F(OutputConversionTest, ReverseValueEmpty) {
+ Err err;
+ Value result = ConvertInputToValue(settings(), "", nullptr,
+ Value(nullptr, "value"), &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::ostringstream reverse;
+ ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
+ &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(reverse.str(), "");
+}
diff --git a/gn/src/gn/output_file.cc b/gn/src/gn/output_file.cc
new file mode 100644
index 00000000000..ad25afa643c
--- /dev/null
+++ b/gn/src/gn/output_file.cc
@@ -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.
+
+#include "gn/output_file.h"
+
+#include "gn/filesystem_utils.h"
+#include "gn/source_file.h"
+
+OutputFile::OutputFile(std::string&& v) : value_(std::move(v)) {}
+
+OutputFile::OutputFile(const std::string& v) : value_(v) {}
+
+OutputFile::OutputFile(const BuildSettings* build_settings,
+ const SourceFile& source_file)
+ : value_(RebasePath(source_file.value(),
+ build_settings->build_dir(),
+ build_settings->root_path_utf8())) {}
+
+SourceFile OutputFile::AsSourceFile(const BuildSettings* build_settings) const {
+ DCHECK(!value_.empty());
+ DCHECK(value_[value_.size() - 1] != '/');
+
+ std::string path = build_settings->build_dir().value();
+ path.append(value_);
+ return SourceFile(std::move(path));
+}
+
+SourceDir OutputFile::AsSourceDir(const BuildSettings* build_settings) const {
+ if (!value_.empty()) {
+ // Empty means the root build dir. Otherwise, we expect it to end in a
+ // slash.
+ DCHECK(value_[value_.size() - 1] == '/');
+ }
+ std::string path = build_settings->build_dir().value();
+ path.append(value_);
+ NormalizePath(&path);
+ return SourceDir(std::move(path));
+}
diff --git a/gn/src/gn/output_file.h b/gn/src/gn/output_file.h
new file mode 100644
index 00000000000..d8d69ff9bf1
--- /dev/null
+++ b/gn/src/gn/output_file.h
@@ -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.
+
+#ifndef TOOLS_GN_OUTPUT_FILE_H_
+#define TOOLS_GN_OUTPUT_FILE_H_
+
+#include <stddef.h>
+
+#include <string>
+
+class BuildSettings;
+class SourceDir;
+class SourceFile;
+
+// A simple wrapper around a string that indicates the string is a path
+// relative to the output directory.
+class OutputFile {
+ public:
+ OutputFile() = default;
+
+ explicit OutputFile(std::string&& v);
+ explicit OutputFile(const std::string& v);
+
+ OutputFile(const BuildSettings* build_settings,
+ const SourceFile& source_file);
+
+ std::string& value() { return value_; }
+ const std::string& value() const { return value_; }
+
+ // Converts to a SourceFile by prepending the build directory to the file.
+ // The *Dir version requires that the current OutputFile ends in a slash, and
+ // the *File version is the opposite.
+ SourceFile AsSourceFile(const BuildSettings* build_settings) const;
+ SourceDir AsSourceDir(const BuildSettings* build_settings) const;
+
+ bool operator==(const OutputFile& other) const {
+ return value_ == other.value_;
+ }
+ bool operator!=(const OutputFile& other) const {
+ return value_ != other.value_;
+ }
+ bool operator<(const OutputFile& other) const {
+ return value_ < other.value_;
+ }
+
+ private:
+ std::string value_;
+};
+
+namespace std {
+
+template <>
+struct hash<OutputFile> {
+ std::size_t operator()(const OutputFile& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+
+} // namespace std
+
+#endif // TOOLS_GN_OUTPUT_FILE_H_
diff --git a/gn/src/gn/parse_node_value_adapter.cc b/gn/src/gn/parse_node_value_adapter.cc
new file mode 100644
index 00000000000..a8adb174f7f
--- /dev/null
+++ b/gn/src/gn/parse_node_value_adapter.cc
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium 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 "gn/parse_node_value_adapter.h"
+
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+
+ParseNodeValueAdapter::ParseNodeValueAdapter() : ref_(nullptr) {}
+
+ParseNodeValueAdapter::~ParseNodeValueAdapter() = default;
+
+bool ParseNodeValueAdapter::Init(Scope* scope,
+ const ParseNode* node,
+ Err* err) {
+ const IdentifierNode* identifier = node->AsIdentifier();
+ if (identifier) {
+ ref_ = scope->GetValue(identifier->value().value(), true);
+ if (!ref_) {
+ identifier->MakeErrorDescribing("Undefined identifier");
+ return false;
+ }
+ return true;
+ }
+
+ temporary_ = node->Execute(scope, err);
+ return !err->has_error();
+}
+
+bool ParseNodeValueAdapter::InitForType(Scope* scope,
+ const ParseNode* node,
+ Value::Type type,
+ Err* err) {
+ if (!Init(scope, node, err))
+ return false;
+ if (get().VerifyTypeIs(type, err))
+ return true;
+
+ // Fix up the error range (see class comment in the header file) to be the
+ // identifier node rather than the original value.
+ *err = Err(node, err->message(), err->help_text());
+ return false;
+}
diff --git a/gn/src/gn/parse_node_value_adapter.h b/gn/src/gn/parse_node_value_adapter.h
new file mode 100644
index 00000000000..e724bb66037
--- /dev/null
+++ b/gn/src/gn/parse_node_value_adapter.h
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_PARSE_NODE_VALUE_ADAPTER_H_
+#define TOOLS_GN_PARSE_NODE_VALUE_ADAPTER_H_
+
+#include "base/macros.h"
+#include "gn/value.h"
+
+class ParseNode;
+
+// Provides a means to convert a parse node to a value without causing a copy
+// in the common case of an "Identifier" node. Normally to get a value from a
+// parse node you have to call Execute(), and when an identifier is executed
+// it just returns the current value of itself as a copy. But some variables
+// are very large (lists of many strings for example).
+//
+// The reason you might not want to do this is that in the case of an
+// identifier where the copy is optimized away, the origin will still be the
+// original value. The result can be confusing because it will reference the
+// original value rather than the place where the value was dereferenced, e.g.
+// for a function call. The InitForType() function will verify type information
+// and will fix up the origin so it's not confusing.
+class ParseNodeValueAdapter {
+ public:
+ ParseNodeValueAdapter();
+ ~ParseNodeValueAdapter();
+
+ const Value& get() {
+ if (ref_)
+ return *ref_;
+ return temporary_;
+ }
+
+ // Initializes the adapter for the result of the given expression. Returns
+ // truen on success.
+ bool Init(Scope* scope, const ParseNode* node, Err* err);
+
+ // Like Init() but additionally verifies that the type of the result matches.
+ bool InitForType(Scope* scope,
+ const ParseNode* node,
+ Value::Type type,
+ Err* err);
+
+ private:
+ // Holds either a reference to an existing item, or a temporary as a copy.
+ // If ref is non-null, it's valid, otherwise the temporary is used.
+ const Value* ref_;
+ Value temporary_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParseNodeValueAdapter);
+};
+
+#endif // TOOLS_GN_PARSE_NODE_VALUE_ADAPTER_H_
diff --git a/gn/src/gn/parse_tree.cc b/gn/src/gn/parse_tree.cc
new file mode 100644
index 00000000000..9ab28b5d2d2
--- /dev/null
+++ b/gn/src/gn/parse_tree.cc
@@ -0,0 +1,1257 @@
+// 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 "gn/parse_tree.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "base/json/string_escape.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "gn/functions.h"
+#include "gn/operators.h"
+#include "gn/scope.h"
+#include "gn/string_utils.h"
+
+// Dictionary keys used for JSON-formatted tree dump.
+const char kJsonNodeChild[] = "child";
+const char kJsonNodeType[] = "type";
+const char kJsonNodeValue[] = "value";
+const char kJsonBeforeComment[] = "before_comment";
+const char kJsonSuffixComment[] = "suffix_comment";
+const char kJsonAfterComment[] = "after_comment";
+const char kJsonLocation[] = "location";
+const char kJsonLocationBeginLine[] = "begin_line";
+const char kJsonLocationBeginColumn[] = "begin_column";
+const char kJsonLocationEndLine[] = "end_line";
+const char kJsonLocationEndColumn[] = "end_column";
+
+// Used by Block and List.
+const char kJsonBeginToken[] = "begin_token";
+const char kJsonEnd[] = "end";
+
+namespace {
+
+enum DepsCategory {
+ DEPS_CATEGORY_LOCAL,
+ DEPS_CATEGORY_RELATIVE,
+ DEPS_CATEGORY_ABSOLUTE,
+ DEPS_CATEGORY_OTHER,
+};
+
+DepsCategory GetDepsCategory(std::string_view deps) {
+ if (deps.length() < 2 || deps[0] != '"' || deps[deps.size() - 1] != '"')
+ return DEPS_CATEGORY_OTHER;
+
+ if (deps[1] == ':')
+ return DEPS_CATEGORY_LOCAL;
+
+ if (deps[1] == '/')
+ return DEPS_CATEGORY_ABSOLUTE;
+
+ return DEPS_CATEGORY_RELATIVE;
+}
+
+std::tuple<std::string_view, std::string_view> SplitAtFirst(
+ std::string_view str,
+ char c) {
+ if (!base::StartsWith(str, "\"", base::CompareCase::SENSITIVE) ||
+ !base::EndsWith(str, "\"", base::CompareCase::SENSITIVE))
+ return std::make_tuple(str, std::string_view());
+
+ str = str.substr(1, str.length() - 2);
+ size_t index_of_first = str.find(c);
+ return std::make_tuple(str.substr(0, index_of_first),
+ index_of_first != std::string_view::npos
+ ? str.substr(index_of_first + 1)
+ : std::string_view());
+}
+
+bool IsSortRangeSeparator(const ParseNode* node, const ParseNode* prev) {
+ // If it's a block comment, or has an attached comment with a blank line
+ // before it, then we break the range at this point.
+ return node->AsBlockComment() != nullptr ||
+ (prev && node->comments() && !node->comments()->before().empty() &&
+ (node->GetRange().begin().line_number() >
+ prev->GetRange().end().line_number() +
+ static_cast<int>(node->comments()->before().size() + 1)));
+}
+
+std::string_view GetStringRepresentation(const ParseNode* node) {
+ DCHECK(node->AsLiteral() || node->AsIdentifier() || node->AsAccessor());
+ if (node->AsLiteral())
+ return node->AsLiteral()->value().value();
+ else if (node->AsIdentifier())
+ return node->AsIdentifier()->value().value();
+ else if (node->AsAccessor())
+ return node->AsAccessor()->base().value();
+ return std::string_view();
+}
+
+void AddLocationJSONNodes(base::Value* dict, LocationRange location) {
+ base::Value loc(base::Value::Type::DICTIONARY);
+ loc.SetKey(kJsonLocationBeginLine,
+ base::Value(location.begin().line_number()));
+ loc.SetKey(kJsonLocationBeginColumn,
+ base::Value(location.begin().column_number()));
+ loc.SetKey(kJsonLocationEndLine, base::Value(location.end().line_number()));
+ loc.SetKey(kJsonLocationEndColumn,
+ base::Value(location.end().column_number()));
+ dict->SetKey(kJsonLocation, std::move(loc));
+}
+
+Location GetBeginLocationFromJSON(const base::Value& value) {
+ int line =
+ value.FindKey(kJsonLocation)->FindKey(kJsonLocationBeginLine)->GetInt();
+ int column =
+ value.FindKey(kJsonLocation)->FindKey(kJsonLocationBeginColumn)->GetInt();
+ return Location(nullptr, line, column, 0);
+}
+
+void GetCommentsFromJSON(ParseNode* node, const base::Value& value) {
+ Comments* comments = node->comments_mutable();
+
+ Location loc = GetBeginLocationFromJSON(value);
+
+ auto loc_for = [&loc](int line) {
+ return Location(nullptr, loc.line_number() + line, loc.column_number(), 0);
+ };
+
+ if (value.FindKey(kJsonBeforeComment)) {
+ int line = 0;
+ for (const auto& c : value.FindKey(kJsonBeforeComment)->GetList()) {
+ comments->append_before(
+ Token::ClassifyAndMake(loc_for(line), c.GetString()));
+ ++line;
+ }
+ }
+
+ if (value.FindKey(kJsonSuffixComment)) {
+ int line = 0;
+ for (const auto& c : value.FindKey(kJsonSuffixComment)->GetList()) {
+ comments->append_suffix(
+ Token::ClassifyAndMake(loc_for(line), c.GetString()));
+ ++line;
+ }
+ }
+
+ if (value.FindKey(kJsonAfterComment)) {
+ int line = 0;
+ for (const auto& c : value.FindKey(kJsonAfterComment)->GetList()) {
+ comments->append_after(
+ Token::ClassifyAndMake(loc_for(line), c.GetString()));
+ ++line;
+ }
+ }
+}
+
+Token TokenFromValue(const base::Value& value) {
+ return Token::ClassifyAndMake(GetBeginLocationFromJSON(value),
+ value.FindKey(kJsonNodeValue)->GetString());
+}
+
+} // namespace
+
+Comments::Comments() = default;
+
+Comments::~Comments() = default;
+
+void Comments::ReverseSuffix() {
+ for (int i = 0, j = static_cast<int>(suffix_.size() - 1); i < j; ++i, --j)
+ std::swap(suffix_[i], suffix_[j]);
+}
+
+ParseNode::ParseNode() = default;
+
+ParseNode::~ParseNode() = default;
+
+const AccessorNode* ParseNode::AsAccessor() const {
+ return nullptr;
+}
+const BinaryOpNode* ParseNode::AsBinaryOp() const {
+ return nullptr;
+}
+const BlockCommentNode* ParseNode::AsBlockComment() const {
+ return nullptr;
+}
+const BlockNode* ParseNode::AsBlock() const {
+ return nullptr;
+}
+const ConditionNode* ParseNode::AsCondition() const {
+ return nullptr;
+}
+const EndNode* ParseNode::AsEnd() const {
+ return nullptr;
+}
+const FunctionCallNode* ParseNode::AsFunctionCall() const {
+ return nullptr;
+}
+const IdentifierNode* ParseNode::AsIdentifier() const {
+ return nullptr;
+}
+const ListNode* ParseNode::AsList() const {
+ return nullptr;
+}
+const LiteralNode* ParseNode::AsLiteral() const {
+ return nullptr;
+}
+const UnaryOpNode* ParseNode::AsUnaryOp() const {
+ return nullptr;
+}
+
+Comments* ParseNode::comments_mutable() {
+ if (!comments_)
+ comments_ = std::make_unique<Comments>();
+ return comments_.get();
+}
+
+base::Value ParseNode::CreateJSONNode(const char* type,
+ LocationRange location) const {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey(kJsonNodeType, base::Value(type));
+ AddLocationJSONNodes(&dict, location);
+ AddCommentsJSONNodes(&dict);
+ return dict;
+}
+
+base::Value ParseNode::CreateJSONNode(const char* type,
+ const std::string_view& value,
+ LocationRange location) const {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey(kJsonNodeType, base::Value(type));
+ dict.SetKey(kJsonNodeValue, base::Value(value));
+ AddLocationJSONNodes(&dict, location);
+ AddCommentsJSONNodes(&dict);
+ return dict;
+}
+
+void ParseNode::AddCommentsJSONNodes(base::Value* out_value) const {
+ if (comments_) {
+ if (comments_->before().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->before())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonBeforeComment, std::move(comment_values));
+ }
+ if (comments_->suffix().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->suffix())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonSuffixComment, std::move(comment_values));
+ }
+ if (comments_->after().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->after())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonAfterComment, std::move(comment_values));
+ }
+ }
+}
+
+// static
+std::unique_ptr<ParseNode> ParseNode::BuildFromJSON(const base::Value& value) {
+ const std::string& str_type = value.FindKey(kJsonNodeType)->GetString();
+
+#define RETURN_IF_MATCHES_NAME(t) \
+ if (str_type == t::kDumpNodeName) { \
+ return t::NewFromJSON(value); \
+ }
+
+ RETURN_IF_MATCHES_NAME(AccessorNode);
+ RETURN_IF_MATCHES_NAME(BinaryOpNode);
+ RETURN_IF_MATCHES_NAME(BlockCommentNode);
+ RETURN_IF_MATCHES_NAME(BlockNode);
+ RETURN_IF_MATCHES_NAME(ConditionNode);
+ RETURN_IF_MATCHES_NAME(EndNode);
+ RETURN_IF_MATCHES_NAME(FunctionCallNode);
+ RETURN_IF_MATCHES_NAME(IdentifierNode);
+ RETURN_IF_MATCHES_NAME(ListNode);
+ RETURN_IF_MATCHES_NAME(LiteralNode);
+ RETURN_IF_MATCHES_NAME(UnaryOpNode);
+
+#undef RETURN_IF_MATCHES_NAME
+
+ NOTREACHED() << str_type;
+ return std::unique_ptr<ParseNode>();
+}
+
+// AccessorNode ---------------------------------------------------------------
+
+AccessorNode::AccessorNode() = default;
+
+AccessorNode::~AccessorNode() = default;
+
+const AccessorNode* AccessorNode::AsAccessor() const {
+ return this;
+}
+
+Value AccessorNode::Execute(Scope* scope, Err* err) const {
+ if (subscript_)
+ return ExecuteSubscriptAccess(scope, err);
+ else if (member_)
+ return ExecuteScopeAccess(scope, err);
+ NOTREACHED();
+ return Value();
+}
+
+LocationRange AccessorNode::GetRange() const {
+ if (subscript_)
+ return LocationRange(base_.location(), subscript_->GetRange().end());
+ else if (member_)
+ return LocationRange(base_.location(), member_->GetRange().end());
+ NOTREACHED();
+ return LocationRange();
+}
+
+Err AccessorNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(GetRange(), msg, help);
+}
+
+base::Value AccessorNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode(kDumpNodeName, base_.value(), GetRange()));
+ base::Value child(base::Value::Type::LIST);
+ if (subscript_) {
+ child.GetList().push_back(subscript_->GetJSONNode());
+ dict.SetKey(kDumpAccessorKind, base::Value(kDumpAccessorKindSubscript));
+ } else if (member_) {
+ child.GetList().push_back(member_->GetJSONNode());
+ dict.SetKey(kDumpAccessorKind, base::Value(kDumpAccessorKindMember));
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
+}
+
+#define DECLARE_CHILD_AS_LIST_OR_FAIL() \
+ const base::Value* child = value.FindKey(kJsonNodeChild); \
+ if (!child || !child->is_list()) { \
+ return nullptr; \
+ }
+
+// static
+std::unique_ptr<AccessorNode> AccessorNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<AccessorNode>();
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ ret->base_ = TokenFromValue(value);
+ const base::Value::ListStorage& children = child->GetList();
+ const std::string& kind = value.FindKey(kDumpAccessorKind)->GetString();
+ if (kind == kDumpAccessorKindSubscript) {
+ ret->subscript_ = ParseNode::BuildFromJSON(children[0]);
+ } else if (kind == kDumpAccessorKindMember) {
+ ret->member_ = IdentifierNode::NewFromJSON(children[0]);
+ }
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+Value AccessorNode::ExecuteSubscriptAccess(Scope* scope, Err* err) const {
+ const Value* base_value = scope->GetValue(base_.value(), true);
+ if (!base_value) {
+ *err = MakeErrorDescribing("Undefined identifier.");
+ return Value();
+ }
+ if (base_value->type() == Value::LIST) {
+ return ExecuteArrayAccess(scope, base_value, err);
+ } else if (base_value->type() == Value::SCOPE) {
+ return ExecuteScopeSubscriptAccess(scope, base_value, err);
+ } else {
+ *err = MakeErrorDescribing(
+ std::string("Expecting either a list or a scope for subscript, got ") +
+ Value::DescribeType(base_value->type()) + ".");
+ return Value();
+ }
+}
+
+Value AccessorNode::ExecuteArrayAccess(Scope* scope,
+ const Value* base_value,
+ Err* err) const {
+ size_t index = 0;
+ if (!ComputeAndValidateListIndex(scope, base_value->list_value().size(),
+ &index, err))
+ return Value();
+ return base_value->list_value()[index];
+}
+
+Value AccessorNode::ExecuteScopeSubscriptAccess(Scope* scope,
+ const Value* base_value,
+ Err* err) const {
+ Value key_value = subscript_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (!key_value.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const Value* result =
+ base_value->scope_value()->GetValue(key_value.string_value());
+ if (!result) {
+ *err =
+ Err(subscript_.get(), "No value named \"" + key_value.string_value() +
+ "\" in scope \"" + base_.value() + "\"");
+ return Value();
+ }
+ return *result;
+}
+
+Value AccessorNode::ExecuteScopeAccess(Scope* scope, Err* err) const {
+ // We jump through some hoops here since ideally a.b will count "b" as
+ // accessed in the given scope. The value "a" might be in some normal nested
+ // scope and we can modify it, but it might also be inherited from the
+ // readonly root scope and we can't do used variable tracking on it. (It's
+ // not legal to const cast it away since the root scope will be in readonly
+ // mode and being accessed from multiple threads without locking.) So this
+ // code handles both cases.
+ const Value* result = nullptr;
+
+ // Look up the value in the scope named by "base_".
+ Value* mutable_base_value =
+ scope->GetMutableValue(base_.value(), Scope::SEARCH_NESTED, true);
+ if (mutable_base_value) {
+ // Common case: base value is mutable so we can track variable accesses
+ // for unused value warnings.
+ if (!mutable_base_value->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+ result = mutable_base_value->scope_value()->GetValue(
+ member_->value().value(), true);
+ } else {
+ // Fall back to see if the value is on a read-only scope.
+ const Value* const_base_value = scope->GetValue(base_.value(), true);
+ if (const_base_value) {
+ // Read only value, don't try to mark the value access as a "used" one.
+ if (!const_base_value->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+ result =
+ const_base_value->scope_value()->GetValue(member_->value().value());
+ } else {
+ *err = Err(base_, "Undefined identifier.");
+ return Value();
+ }
+ }
+
+ if (!result) {
+ *err = Err(member_.get(), "No value named \"" + member_->value().value() +
+ "\" in scope \"" + base_.value() + "\"");
+ return Value();
+ }
+ return *result;
+}
+
+void AccessorNode::SetNewLocation(int line_number) {
+ Location old = base_.location();
+ base_.set_location(
+ Location(old.file(), line_number, old.column_number(), old.byte()));
+}
+
+bool AccessorNode::ComputeAndValidateListIndex(Scope* scope,
+ size_t max_len,
+ size_t* computed_index,
+ Err* err) const {
+ Value index_value = subscript_->Execute(scope, err);
+ if (err->has_error())
+ return false;
+ if (!index_value.VerifyTypeIs(Value::INTEGER, err))
+ return false;
+
+ int64_t index_int = index_value.int_value();
+ if (index_int < 0) {
+ *err = Err(subscript_->GetRange(), "Negative array subscript.",
+ "You gave me " + base::Int64ToString(index_int) + ".");
+ return false;
+ }
+ if (max_len == 0) {
+ *err = Err(subscript_->GetRange(), "Array subscript out of range.",
+ "You gave me " + base::Int64ToString(index_int) + " but the " +
+ "array has no elements.");
+ return false;
+ }
+ size_t index_sizet = static_cast<size_t>(index_int);
+ if (index_sizet >= max_len) {
+ *err = Err(subscript_->GetRange(), "Array subscript out of range.",
+ "You gave me " + base::Int64ToString(index_int) +
+ " but I was expecting something from 0 to " +
+ base::NumberToString(max_len - 1) + ", inclusive.");
+ return false;
+ }
+
+ *computed_index = index_sizet;
+ return true;
+}
+
+// BinaryOpNode ---------------------------------------------------------------
+
+BinaryOpNode::BinaryOpNode() = default;
+
+BinaryOpNode::~BinaryOpNode() = default;
+
+const BinaryOpNode* BinaryOpNode::AsBinaryOp() const {
+ return this;
+}
+
+Value BinaryOpNode::Execute(Scope* scope, Err* err) const {
+ return ExecuteBinaryOperator(scope, this, left_.get(), right_.get(), err);
+}
+
+LocationRange BinaryOpNode::GetRange() const {
+ return left_->GetRange().Union(right_->GetRange());
+}
+
+Err BinaryOpNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(op_, msg, help);
+}
+
+base::Value BinaryOpNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode(kDumpNodeName, op_.value(), GetRange()));
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(left_->GetJSONNode());
+ child.GetList().push_back(right_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
+}
+
+// static
+std::unique_ptr<BinaryOpNode> BinaryOpNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<BinaryOpNode>();
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ const base::Value::ListStorage& children = child->GetList();
+ ret->left_ = ParseNode::BuildFromJSON(children[0]);
+ ret->right_ = ParseNode::BuildFromJSON(children[1]);
+ ret->op_ = TokenFromValue(value);
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+// BlockNode ------------------------------------------------------------------
+
+BlockNode::BlockNode(ResultMode result_mode) : result_mode_(result_mode) {}
+
+BlockNode::~BlockNode() = default;
+
+const BlockNode* BlockNode::AsBlock() const {
+ return this;
+}
+
+Value BlockNode::Execute(Scope* enclosing_scope, Err* err) const {
+ std::unique_ptr<Scope> nested_scope; // May be null.
+
+ Scope* execution_scope; // Either the enclosing_scope or nested_scope.
+ if (result_mode_ == RETURNS_SCOPE) {
+ // Create a nested scope to save the values for returning.
+ nested_scope = std::make_unique<Scope>(enclosing_scope);
+ execution_scope = nested_scope.get();
+ } else {
+ // Use the enclosing scope. Modifications will go into this also (for
+ // example, if conditions and loops).
+ execution_scope = enclosing_scope;
+ }
+
+ for (size_t i = 0; i < statements_.size() && !err->has_error(); i++) {
+ // Check for trying to execute things with no side effects in a block.
+ //
+ // A BlockNode here means that somebody has a free-floating { }.
+ // Technically this can have side effects since it could generated targets,
+ // but we don't want to allow this since it creates ambiguity when
+ // immediately following a function call that takes no block. By not
+ // allowing free-floating blocks that aren't passed anywhere or assigned to
+ // anything, this ambiguity is resolved.
+ const ParseNode* cur = statements_[i].get();
+ if (cur->AsList() || cur->AsLiteral() || cur->AsUnaryOp() ||
+ cur->AsIdentifier() || cur->AsBlock()) {
+ *err = cur->MakeErrorDescribing(
+ "This statement has no effect.",
+ "Either delete it or do something with the result.");
+ return Value();
+ }
+ cur->Execute(execution_scope, err);
+ }
+
+ if (result_mode_ == RETURNS_SCOPE) {
+ // Clear the reference to the containing scope. This will be passed in
+ // a value whose lifetime will not be related to the enclosing_scope passed
+ // to this function.
+ nested_scope->DetachFromContaining();
+ return Value(this, std::move(nested_scope));
+ }
+ return Value();
+}
+
+LocationRange BlockNode::GetRange() const {
+ if (begin_token_.type() != Token::INVALID &&
+ end_->value().type() != Token::INVALID) {
+ return begin_token_.range().Union(end_->value().range());
+ } else if (!statements_.empty()) {
+ return statements_[0]->GetRange().Union(
+ statements_[statements_.size() - 1]->GetRange());
+ }
+ return LocationRange();
+}
+
+Err BlockNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(GetRange(), msg, help);
+}
+
+base::Value BlockNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode(kDumpNodeName, GetRange()));
+ base::Value statements(base::Value::Type::LIST);
+ for (const auto& statement : statements_)
+ statements.GetList().push_back(statement->GetJSONNode());
+ if (end_)
+ dict.SetKey(kJsonEnd, end_->GetJSONNode());
+
+ dict.SetKey(kJsonNodeChild, std::move(statements));
+
+ if (result_mode_ == BlockNode::RETURNS_SCOPE) {
+ dict.SetKey(kDumpResultMode, base::Value(kDumpResultModeReturnsScope));
+ } else if (result_mode_ == BlockNode::DISCARDS_RESULT) {
+ dict.SetKey(kDumpResultMode, base::Value(kDumpResultModeDiscardsResult));
+ } else {
+ NOTREACHED();
+ }
+
+ dict.SetKey(kJsonBeginToken, base::Value(begin_token_.value()));
+
+ return dict;
+}
+
+// static
+std::unique_ptr<BlockNode> BlockNode::NewFromJSON(const base::Value& value) {
+ const std::string& result_mode = value.FindKey(kDumpResultMode)->GetString();
+ std::unique_ptr<BlockNode> ret;
+
+ if (result_mode == kDumpResultModeReturnsScope) {
+ ret.reset(new BlockNode(BlockNode::RETURNS_SCOPE));
+ } else if (result_mode == kDumpResultModeDiscardsResult) {
+ ret.reset(new BlockNode(BlockNode::DISCARDS_RESULT));
+ } else {
+ NOTREACHED();
+ }
+
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ for (const auto& elem : child->GetList()) {
+ ret->statements_.push_back(ParseNode::BuildFromJSON(elem));
+ }
+
+ ret->begin_token_ =
+ Token::ClassifyAndMake(GetBeginLocationFromJSON(value),
+ value.FindKey(kJsonBeginToken)->GetString());
+ if (value.FindKey(kJsonEnd)) {
+ ret->end_ = EndNode::NewFromJSON(*value.FindKey(kJsonEnd));
+ }
+
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+// ConditionNode --------------------------------------------------------------
+
+ConditionNode::ConditionNode() = default;
+
+ConditionNode::~ConditionNode() = default;
+
+const ConditionNode* ConditionNode::AsCondition() const {
+ return this;
+}
+
+Value ConditionNode::Execute(Scope* scope, Err* err) const {
+ Value condition_result = condition_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ if (condition_result.type() != Value::BOOLEAN) {
+ *err = condition_->MakeErrorDescribing(
+ "Condition does not evaluate to a boolean value.",
+ std::string("This is a value of type \"") +
+ Value::DescribeType(condition_result.type()) + "\" instead.");
+ err->AppendRange(if_token_.range());
+ return Value();
+ }
+
+ if (condition_result.boolean_value()) {
+ if_true_->Execute(scope, err);
+ } else if (if_false_) {
+ // The else block is optional.
+ if_false_->Execute(scope, err);
+ }
+
+ return Value();
+}
+
+LocationRange ConditionNode::GetRange() const {
+ if (if_false_)
+ return if_token_.range().Union(if_false_->GetRange());
+ return if_token_.range().Union(if_true_->GetRange());
+}
+
+Err ConditionNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(if_token_, msg, help);
+}
+
+base::Value ConditionNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode(kDumpNodeName, GetRange());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(condition_->GetJSONNode());
+ child.GetList().push_back(if_true_->GetJSONNode());
+ if (if_false_) {
+ child.GetList().push_back(if_false_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
+}
+
+// static
+std::unique_ptr<ConditionNode> ConditionNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<ConditionNode>();
+
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ const base::Value::ListStorage& children = child->GetList();
+
+ ret->if_token_ =
+ Token::ClassifyAndMake(GetBeginLocationFromJSON(value), "if");
+ ret->condition_ = ParseNode::BuildFromJSON(children[0]);
+ ret->if_true_ = BlockNode::NewFromJSON(children[1]);
+ if (children.size() > 2) {
+ ret->if_false_ = ParseNode::BuildFromJSON(children[2]);
+ }
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+// FunctionCallNode -----------------------------------------------------------
+
+FunctionCallNode::FunctionCallNode() = default;
+
+FunctionCallNode::~FunctionCallNode() = default;
+
+const FunctionCallNode* FunctionCallNode::AsFunctionCall() const {
+ return this;
+}
+
+Value FunctionCallNode::Execute(Scope* scope, Err* err) const {
+ return functions::RunFunction(scope, this, args_.get(), block_.get(), err);
+}
+
+LocationRange FunctionCallNode::GetRange() const {
+ if (function_.type() == Token::INVALID)
+ return LocationRange(); // This will be null in some tests.
+ if (block_)
+ return function_.range().Union(block_->GetRange());
+ return function_.range().Union(args_->GetRange());
+}
+
+Err FunctionCallNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(function_, msg, help);
+}
+
+base::Value FunctionCallNode::GetJSONNode() const {
+ base::Value dict =
+ CreateJSONNode(kDumpNodeName, function_.value(), GetRange());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(args_->GetJSONNode());
+ if (block_) {
+ child.GetList().push_back(block_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
+}
+
+// static
+std::unique_ptr<FunctionCallNode> FunctionCallNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<FunctionCallNode>();
+
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ const base::Value::ListStorage& children = child->GetList();
+ ret->function_ = TokenFromValue(value);
+ ret->args_ = ListNode::NewFromJSON(children[0]);
+ if (children.size() > 1)
+ ret->block_ = BlockNode::NewFromJSON(children[1]);
+
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+void FunctionCallNode::SetNewLocation(int line_number) {
+ Location func_old_loc = function_.location();
+ Location func_new_loc =
+ Location(func_old_loc.file(), line_number, func_old_loc.column_number(),
+ func_old_loc.byte());
+ function_.set_location(func_new_loc);
+
+ Location args_old_loc = args_->Begin().location();
+ Location args_new_loc =
+ Location(args_old_loc.file(), line_number, args_old_loc.column_number(),
+ args_old_loc.byte());
+ const_cast<Token&>(args_->Begin()).set_location(args_new_loc);
+ const_cast<Token&>(args_->End()->value()).set_location(args_new_loc);
+}
+
+// IdentifierNode --------------------------------------------------------------
+
+IdentifierNode::IdentifierNode() = default;
+
+IdentifierNode::IdentifierNode(const Token& token) : value_(token) {}
+
+IdentifierNode::~IdentifierNode() = default;
+
+const IdentifierNode* IdentifierNode::AsIdentifier() const {
+ return this;
+}
+
+Value IdentifierNode::Execute(Scope* scope, Err* err) const {
+ const Scope* found_in_scope = nullptr;
+ const Value* value =
+ scope->GetValueWithScope(value_.value(), true, &found_in_scope);
+ Value result;
+ if (!value) {
+ *err = MakeErrorDescribing("Undefined identifier");
+ return result;
+ }
+
+ if (!EnsureNotReadingFromSameDeclareArgs(this, scope, found_in_scope, err))
+ return result;
+
+ result = *value;
+ result.set_origin(this);
+ return result;
+}
+
+LocationRange IdentifierNode::GetRange() const {
+ return value_.range();
+}
+
+Err IdentifierNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(value_, msg, help);
+}
+
+base::Value IdentifierNode::GetJSONNode() const {
+ return CreateJSONNode(kDumpNodeName, value_.value(), GetRange());
+}
+
+// static
+std::unique_ptr<IdentifierNode> IdentifierNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<IdentifierNode>();
+ ret->set_value(TokenFromValue(value));
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+void IdentifierNode::SetNewLocation(int line_number) {
+ Location old = value_.location();
+ value_.set_location(
+ Location(old.file(), line_number, old.column_number(), old.byte()));
+}
+
+// ListNode -------------------------------------------------------------------
+
+ListNode::ListNode() {}
+
+ListNode::~ListNode() = default;
+
+const ListNode* ListNode::AsList() const {
+ return this;
+}
+
+Value ListNode::Execute(Scope* scope, Err* err) const {
+ Value result_value(this, Value::LIST);
+ std::vector<Value>& results = result_value.list_value();
+ results.reserve(contents_.size());
+
+ for (const auto& cur : contents_) {
+ if (cur->AsBlockComment())
+ continue;
+ results.push_back(cur->Execute(scope, err));
+ if (err->has_error())
+ return Value();
+ if (results.back().type() == Value::NONE) {
+ *err = cur->MakeErrorDescribing("This does not evaluate to a value.",
+ "I can't do something with nothing.");
+ return Value();
+ }
+ }
+ return result_value;
+}
+
+LocationRange ListNode::GetRange() const {
+ return LocationRange(begin_token_.location(), end_->value().location());
+}
+
+Err ListNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(begin_token_, msg, help);
+}
+
+base::Value ListNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode(kDumpNodeName, GetRange()));
+ base::Value child(base::Value::Type::LIST);
+ for (const auto& cur : contents_) {
+ child.GetList().push_back(cur->GetJSONNode());
+ }
+ if (end_)
+ dict.SetKey(kJsonEnd, end_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ dict.SetKey(kJsonBeginToken, base::Value(begin_token_.value()));
+ return dict;
+}
+
+// static
+std::unique_ptr<ListNode> ListNode::NewFromJSON(const base::Value& value) {
+ auto ret = std::make_unique<ListNode>();
+
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ for (const auto& elem : child->GetList()) {
+ ret->contents_.push_back(ParseNode::BuildFromJSON(elem));
+ }
+ ret->begin_token_ =
+ Token::ClassifyAndMake(GetBeginLocationFromJSON(value),
+ value.FindKey(kJsonBeginToken)->GetString());
+ if (value.FindKey(kJsonEnd)) {
+ ret->end_ = EndNode::NewFromJSON(*value.FindKey(kJsonEnd));
+ }
+
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+template <typename Comparator>
+void ListNode::SortList(Comparator comparator) {
+ // Partitions first on BlockCommentNodes and sorts each partition separately.
+ for (auto sr : GetSortRanges()) {
+ bool skip = false;
+ for (size_t i = sr.begin; i != sr.end; ++i) {
+ // Bails out if any of the nodes are unsupported.
+ const ParseNode* node = contents_[i].get();
+ if (!node->AsLiteral() && !node->AsIdentifier() && !node->AsAccessor()) {
+ skip = true;
+ continue;
+ }
+ }
+ if (skip)
+ continue;
+ // Save the original line number so that we can re-assign ranges. We assume
+ // they're contiguous lines because GetSortRanges() does so above. We need
+ // to re-assign these line numbers primiarily because `gn format` uses them
+ // to determine whether two nodes were initially separated by a blank line
+ // or not.
+ int start_line = contents_[sr.begin]->GetRange().begin().line_number();
+ const ParseNode* original_first = contents_[sr.begin].get();
+ std::sort(contents_.begin() + sr.begin, contents_.begin() + sr.end,
+ [&comparator](const std::unique_ptr<const ParseNode>& a,
+ const std::unique_ptr<const ParseNode>& b) {
+ return comparator(a.get(), b.get());
+ });
+ // If the beginning of the range had before comments, and the first node
+ // moved during the sort, then move its comments to the new head of the
+ // range.
+ if (original_first->comments() &&
+ contents_[sr.begin].get() != original_first) {
+ for (const auto& hc : original_first->comments()->before()) {
+ const_cast<ParseNode*>(contents_[sr.begin].get())
+ ->comments_mutable()
+ ->append_before(hc);
+ }
+ const_cast<ParseNode*>(original_first)
+ ->comments_mutable()
+ ->clear_before();
+ }
+ const ParseNode* prev = nullptr;
+ for (size_t i = sr.begin; i != sr.end; ++i) {
+ const ParseNode* node = contents_[i].get();
+ DCHECK(node->AsLiteral() || node->AsIdentifier() || node->AsAccessor());
+ int line_number =
+ prev ? prev->GetRange().end().line_number() + 1 : start_line;
+ if (node->AsLiteral()) {
+ const_cast<LiteralNode*>(node->AsLiteral())
+ ->SetNewLocation(line_number);
+ } else if (node->AsIdentifier()) {
+ const_cast<IdentifierNode*>(node->AsIdentifier())
+ ->SetNewLocation(line_number);
+ } else if (node->AsAccessor()) {
+ const_cast<AccessorNode*>(node->AsAccessor())
+ ->SetNewLocation(line_number);
+ }
+ prev = node;
+ }
+ }
+}
+
+void ListNode::SortAsStringsList() {
+ // Sorts alphabetically.
+ SortList([](const ParseNode* a, const ParseNode* b) {
+ std::string_view astr = GetStringRepresentation(a);
+ std::string_view bstr = GetStringRepresentation(b);
+ return astr < bstr;
+ });
+}
+
+void ListNode::SortAsTargetsList() {
+ // Sorts first relative targets, then absolute, each group is sorted
+ // alphabetically.
+ SortList([](const ParseNode* a, const ParseNode* b) {
+ std::string_view astr = GetStringRepresentation(a);
+ std::string_view bstr = GetStringRepresentation(b);
+ return std::make_pair(GetDepsCategory(astr), SplitAtFirst(astr, ':')) <
+ std::make_pair(GetDepsCategory(bstr), SplitAtFirst(bstr, ':'));
+ });
+}
+
+// Breaks the ParseNodes of |contents| up by ranges that should be separately
+// sorted. In particular, we break at a block comment, or an item that has an
+// attached "before" comment and is separated by a blank line from the item
+// before it. The assumption is that both of these indicate a separate 'section'
+// of a sources block across which items should not be inter-sorted.
+std::vector<ListNode::SortRange> ListNode::GetSortRanges() const {
+ std::vector<SortRange> ranges;
+ const ParseNode* prev = nullptr;
+ size_t begin = 0;
+ for (size_t i = begin; i < contents_.size(); prev = contents_[i++].get()) {
+ if (IsSortRangeSeparator(contents_[i].get(), prev)) {
+ if (i > begin) {
+ ranges.push_back(SortRange(begin, i));
+ // If |i| is an item with an attached comment, then we start the next
+ // range at that point, because we want to include it in the sort.
+ // Otherwise, it's a block comment which we skip over entirely because
+ // we don't want to move or include it in the sort. The two cases are:
+ //
+ // sources = [
+ // "a",
+ // "b",
+ //
+ // #
+ // # This is a block comment.
+ // #
+ //
+ // "c",
+ // "d",
+ // ]
+ //
+ // which contains 5 elements, and for which the ranges would be { [0,
+ // 2), [3, 5) } (notably excluding 2, the block comment), and:
+ //
+ // sources = [
+ // "a",
+ // "b",
+ //
+ // # This is a header comment.
+ // "c",
+ // "d",
+ // ]
+ //
+ // which contains 4 elements, index 2 containing an attached 'before'
+ // comments, and the ranges should be { [0, 2), [2, 4) }.
+ if (!contents_[i]->AsBlockComment())
+ begin = i;
+ else
+ begin = i + 1;
+ } else {
+ // If it was a one item range, just skip over it.
+ begin = i + 1;
+ }
+ }
+ }
+ if (begin != contents_.size())
+ ranges.push_back(SortRange(begin, contents_.size()));
+ return ranges;
+}
+
+// LiteralNode -----------------------------------------------------------------
+
+LiteralNode::LiteralNode() = default;
+
+LiteralNode::LiteralNode(const Token& token) : value_(token) {}
+
+LiteralNode::~LiteralNode() = default;
+
+const LiteralNode* LiteralNode::AsLiteral() const {
+ return this;
+}
+
+Value LiteralNode::Execute(Scope* scope, Err* err) const {
+ switch (value_.type()) {
+ case Token::TRUE_TOKEN:
+ return Value(this, true);
+ case Token::FALSE_TOKEN:
+ return Value(this, false);
+ case Token::INTEGER: {
+ std::string_view s = value_.value();
+ if ((base::StartsWith(s, "0", base::CompareCase::SENSITIVE) &&
+ s.size() > 1) ||
+ base::StartsWith(s, "-0", base::CompareCase::SENSITIVE)) {
+ if (s == "-0")
+ *err = MakeErrorDescribing("Negative zero doesn't make sense");
+ else
+ *err = MakeErrorDescribing("Leading zeros not allowed");
+ return Value();
+ }
+ int64_t result_int;
+ if (!base::StringToInt64(s, &result_int)) {
+ *err = MakeErrorDescribing("This does not look like an integer");
+ return Value();
+ }
+ return Value(this, result_int);
+ }
+ case Token::STRING: {
+ Value v(this, Value::STRING);
+ ExpandStringLiteral(scope, value_, &v, err);
+ return v;
+ }
+ default:
+ NOTREACHED();
+ return Value();
+ }
+}
+
+LocationRange LiteralNode::GetRange() const {
+ return value_.range();
+}
+
+Err LiteralNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(value_, msg, help);
+}
+
+base::Value LiteralNode::GetJSONNode() const {
+ return CreateJSONNode(kDumpNodeName, value_.value(), GetRange());
+}
+
+// static
+std::unique_ptr<LiteralNode> LiteralNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<LiteralNode>();
+ ret->value_ = TokenFromValue(value);
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+void LiteralNode::SetNewLocation(int line_number) {
+ Location old = value_.location();
+ value_.set_location(
+ Location(old.file(), line_number, old.column_number(), old.byte()));
+}
+
+// UnaryOpNode ----------------------------------------------------------------
+
+UnaryOpNode::UnaryOpNode() = default;
+
+UnaryOpNode::~UnaryOpNode() = default;
+
+const UnaryOpNode* UnaryOpNode::AsUnaryOp() const {
+ return this;
+}
+
+Value UnaryOpNode::Execute(Scope* scope, Err* err) const {
+ Value operand_value = operand_->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ return ExecuteUnaryOperator(scope, this, operand_value, err);
+}
+
+LocationRange UnaryOpNode::GetRange() const {
+ return op_.range().Union(operand_->GetRange());
+}
+
+Err UnaryOpNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(op_, msg, help);
+}
+
+base::Value UnaryOpNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode(kDumpNodeName, op_.value(), GetRange());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(operand_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
+}
+
+// static
+std::unique_ptr<UnaryOpNode> UnaryOpNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<UnaryOpNode>();
+ ret->op_ = TokenFromValue(value);
+ DECLARE_CHILD_AS_LIST_OR_FAIL();
+ ret->operand_ = ParseNode::BuildFromJSON(child->GetList()[0]);
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+// BlockCommentNode ------------------------------------------------------------
+
+BlockCommentNode::BlockCommentNode() = default;
+
+BlockCommentNode::~BlockCommentNode() = default;
+
+const BlockCommentNode* BlockCommentNode::AsBlockComment() const {
+ return this;
+}
+
+Value BlockCommentNode::Execute(Scope* scope, Err* err) const {
+ return Value();
+}
+
+LocationRange BlockCommentNode::GetRange() const {
+ return comment_.range();
+}
+
+Err BlockCommentNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(comment_, msg, help);
+}
+
+base::Value BlockCommentNode::GetJSONNode() const {
+ std::string escaped;
+ return CreateJSONNode(kDumpNodeName, comment_.value(), GetRange());
+}
+
+// static
+std::unique_ptr<BlockCommentNode> BlockCommentNode::NewFromJSON(
+ const base::Value& value) {
+ auto ret = std::make_unique<BlockCommentNode>();
+ ret->comment_ = Token(GetBeginLocationFromJSON(value), Token::BLOCK_COMMENT,
+ value.FindKey(kJsonNodeValue)->GetString());
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
+
+// EndNode ---------------------------------------------------------------------
+
+EndNode::EndNode(const Token& token) : value_(token) {}
+
+EndNode::~EndNode() = default;
+
+const EndNode* EndNode::AsEnd() const {
+ return this;
+}
+
+Value EndNode::Execute(Scope* scope, Err* err) const {
+ return Value();
+}
+
+LocationRange EndNode::GetRange() const {
+ return value_.range();
+}
+
+Err EndNode::MakeErrorDescribing(const std::string& msg,
+ const std::string& help) const {
+ return Err(value_, msg, help);
+}
+
+base::Value EndNode::GetJSONNode() const {
+ return CreateJSONNode(kDumpNodeName, value_.value(), GetRange());
+}
+
+// static
+std::unique_ptr<EndNode> EndNode::NewFromJSON(const base::Value& value) {
+ auto ret = std::make_unique<EndNode>(TokenFromValue(value));
+ GetCommentsFromJSON(ret.get(), value);
+ return ret;
+}
diff --git a/gn/src/gn/parse_tree.h b/gn/src/gn/parse_tree.h
new file mode 100644
index 00000000000..9ffacc20798
--- /dev/null
+++ b/gn/src/gn/parse_tree.h
@@ -0,0 +1,613 @@
+// 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 TOOLS_GN_PARSE_TREE_H_
+#define TOOLS_GN_PARSE_TREE_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "gn/err.h"
+#include "gn/token.h"
+#include "gn/value.h"
+
+class AccessorNode;
+class BinaryOpNode;
+class BlockCommentNode;
+class BlockNode;
+class ConditionNode;
+class EndNode;
+class FunctionCallNode;
+class IdentifierNode;
+class ListNode;
+class LiteralNode;
+class Scope;
+class UnaryOpNode;
+
+// Dictionary keys used for JSON-formatted tree dump.
+extern const char kJsonNodeChild[];
+extern const char kJsonNodeType[];
+extern const char kJsonNodeValue[];
+extern const char kJsonBeforeComment[];
+extern const char kJsonSuffixComment[];
+extern const char kJsonAfterComment[];
+
+class Comments {
+ public:
+ Comments();
+ virtual ~Comments();
+
+ const std::vector<Token>& before() const { return before_; }
+ void append_before(Token c) { before_.push_back(c); }
+ void clear_before() { before_.clear(); }
+
+ const std::vector<Token>& suffix() const { return suffix_; }
+ void append_suffix(Token c) { suffix_.push_back(c); }
+ // Reverse the order of the suffix comments. When walking the tree in
+ // post-order we append suffix comments in reverse order, so this fixes them
+ // up.
+ void ReverseSuffix();
+
+ const std::vector<Token>& after() const { return after_; }
+ void append_after(Token c) { after_.push_back(c); }
+
+ private:
+ // Whole line comments before the expression.
+ std::vector<Token> before_;
+
+ // End-of-line comments after this expression.
+ std::vector<Token> suffix_;
+
+ // For top-level expressions only, after_ lists whole-line comments
+ // following the expression.
+ std::vector<Token> after_;
+
+ DISALLOW_COPY_AND_ASSIGN(Comments);
+};
+
+// ParseNode -------------------------------------------------------------------
+
+// A node in the AST.
+class ParseNode {
+ public:
+ ParseNode();
+ virtual ~ParseNode();
+
+ virtual const AccessorNode* AsAccessor() const;
+ virtual const BinaryOpNode* AsBinaryOp() const;
+ virtual const BlockCommentNode* AsBlockComment() const;
+ virtual const BlockNode* AsBlock() const;
+ virtual const ConditionNode* AsCondition() const;
+ virtual const EndNode* AsEnd() const;
+ virtual const FunctionCallNode* AsFunctionCall() const;
+ virtual const IdentifierNode* AsIdentifier() const;
+ virtual const ListNode* AsList() const;
+ virtual const LiteralNode* AsLiteral() const;
+ virtual const UnaryOpNode* AsUnaryOp() const;
+
+ virtual Value Execute(Scope* scope, Err* err) const = 0;
+
+ virtual LocationRange GetRange() const = 0;
+
+ // Returns an error with the given messages and the range set to something
+ // that indicates this node.
+ virtual Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const = 0;
+
+ // Generates a representation of this node in base::Value, to be used for
+ // exporting the tree as a JSON or formatted text with indents.
+ virtual base::Value GetJSONNode() const = 0;
+
+ const Comments* comments() const { return comments_.get(); }
+ Comments* comments_mutable();
+
+ static std::unique_ptr<ParseNode> BuildFromJSON(const base::Value& value);
+
+ protected:
+ // Helper functions for GetJSONNode. Creates and fills a Value object with
+ // given type (and value).
+ base::Value CreateJSONNode(const char* type, LocationRange location) const;
+ base::Value CreateJSONNode(const char* type,
+ const std::string_view& value,
+ LocationRange location) const;
+
+ private:
+ // Helper function for CreateJSONNode.
+ void AddCommentsJSONNodes(base::Value* out_value) const;
+
+ std::unique_ptr<Comments> comments_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParseNode);
+};
+
+// AccessorNode ----------------------------------------------------------------
+
+// Access an array or scope element.
+//
+// Currently, such values are only read-only. In that you can do:
+// a = obj1.a
+// b = obj2[0]
+// But not
+// obj1.a = 5
+// obj2[0] = 6
+//
+// In the current design where the dot operator is used only for templates, we
+// explicitly don't want to allow you to do "invoker.foo = 5", so if we added
+// support for accessors to be lvalues, we would also need to add some concept
+// of a constant scope. Supporting this would also add a lot of complications
+// to the operator= implementation, since some accessors might return values
+// in the const root scope that shouldn't be modified. Without a strong
+// use-case for this, it seems simpler to just disallow it.
+//
+// Additionally, the left-hand-side of the accessor must currently be an
+// identifier. So you can't do things like:
+// function_call()[1]
+// a = b.c.d
+// These are easier to implement if we needed them but given the very limited
+// use cases for this, it hasn't seemed worth the bother.
+class AccessorNode : public ParseNode {
+ public:
+ AccessorNode();
+ ~AccessorNode() override;
+
+ const AccessorNode* AsAccessor() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<AccessorNode> NewFromJSON(const base::Value& value);
+
+ // Base is the thing on the left of the [] or dot, currently always required
+ // to be an identifier token.
+ const Token& base() const { return base_; }
+ void set_base(const Token& b) { base_ = b; }
+
+ // Subscript is the expression inside the []. Will be null if member is set.
+ const ParseNode* subscript() const { return subscript_.get(); }
+ void set_subscript(std::unique_ptr<ParseNode> key) {
+ subscript_ = std::move(key);
+ }
+
+ // The member is the identifier on the right hand side of the dot. Will be
+ // null if the index is set.
+ const IdentifierNode* member() const { return member_.get(); }
+ void set_member(std::unique_ptr<IdentifierNode> i) { member_ = std::move(i); }
+
+ void SetNewLocation(int line_number);
+
+ // Evaluates the index for list accessor operations and range checks it
+ // against the max length of the list. If the index is OK, sets
+ // |*computed_index| and returns true. Otherwise sets the |*err| and returns
+ // false.
+ bool ComputeAndValidateListIndex(Scope* scope,
+ size_t max_len,
+ size_t* computed_index,
+ Err* err) const;
+
+ static constexpr const char* kDumpNodeName = "ACCESSOR";
+
+ private:
+ Value ExecuteSubscriptAccess(Scope* scope, Err* err) const;
+ Value ExecuteArrayAccess(Scope* scope,
+ const Value* base_value,
+ Err* err) const;
+ Value ExecuteScopeSubscriptAccess(Scope* scope,
+ const Value* base_value,
+ Err* err) const;
+ Value ExecuteScopeAccess(Scope* scope, Err* err) const;
+
+ static constexpr const char* kDumpAccessorKind = "accessor_kind";
+ static constexpr const char* kDumpAccessorKindSubscript = "subscript";
+ static constexpr const char* kDumpAccessorKindMember = "member";
+
+ Token base_;
+
+ // Either index or member will be set according to what type of access this
+ // is.
+ std::unique_ptr<ParseNode> subscript_;
+ std::unique_ptr<IdentifierNode> member_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessorNode);
+};
+
+// BinaryOpNode ----------------------------------------------------------------
+
+class BinaryOpNode : public ParseNode {
+ public:
+ BinaryOpNode();
+ ~BinaryOpNode() override;
+
+ const BinaryOpNode* AsBinaryOp() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<BinaryOpNode> NewFromJSON(const base::Value& value);
+
+ const Token& op() const { return op_; }
+ void set_op(const Token& t) { op_ = t; }
+
+ const ParseNode* left() const { return left_.get(); }
+ void set_left(std::unique_ptr<ParseNode> left) { left_ = std::move(left); }
+
+ const ParseNode* right() const { return right_.get(); }
+ void set_right(std::unique_ptr<ParseNode> right) {
+ right_ = std::move(right);
+ }
+
+ static constexpr const char* kDumpNodeName = "BINARY";
+
+ private:
+ std::unique_ptr<ParseNode> left_;
+ Token op_;
+ std::unique_ptr<ParseNode> right_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryOpNode);
+};
+
+// BlockNode -------------------------------------------------------------------
+
+class BlockNode : public ParseNode {
+ public:
+ // How Execute manages the scopes and results.
+ enum ResultMode {
+ // Creates a new scope for the execution of this block and returns it as
+ // a Value from Execute().
+ RETURNS_SCOPE,
+
+ // Executes in the context of the calling scope (variables set will go
+ // into the invoking scope) and Execute will return an empty Value.
+ DISCARDS_RESULT
+ };
+
+ BlockNode(ResultMode result_mode);
+ ~BlockNode() override;
+
+ const BlockNode* AsBlock() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<BlockNode> NewFromJSON(const base::Value& value);
+
+ void set_begin_token(const Token& t) { begin_token_ = t; }
+ void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
+ const EndNode* End() const { return end_.get(); }
+
+ ResultMode result_mode() const { return result_mode_; }
+
+ const std::vector<std::unique_ptr<ParseNode>>& statements() const {
+ return statements_;
+ }
+ void append_statement(std::unique_ptr<ParseNode> s) {
+ statements_.push_back(std::move(s));
+ }
+
+ static constexpr const char* kDumpNodeName = "BLOCK";
+
+ private:
+ static constexpr const char* kDumpResultMode = "result_mode";
+ static constexpr const char* kDumpResultModeReturnsScope = "returns_scope";
+ static constexpr const char* kDumpResultModeDiscardsResult =
+ "discards_result";
+
+ const ResultMode result_mode_;
+
+ // Tokens corresponding to { and }, if any (may be NULL). The end is stored
+ // in a custom parse node so that it can have comments hung off of it.
+ Token begin_token_;
+ std::unique_ptr<EndNode> end_;
+
+ std::vector<std::unique_ptr<ParseNode>> statements_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlockNode);
+};
+
+// ConditionNode ---------------------------------------------------------------
+
+class ConditionNode : public ParseNode {
+ public:
+ ConditionNode();
+ ~ConditionNode() override;
+
+ const ConditionNode* AsCondition() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<ConditionNode> NewFromJSON(const base::Value& value);
+
+ void set_if_token(const Token& token) { if_token_ = token; }
+
+ const ParseNode* condition() const { return condition_.get(); }
+ void set_condition(std::unique_ptr<ParseNode> c) {
+ condition_ = std::move(c);
+ }
+
+ const BlockNode* if_true() const { return if_true_.get(); }
+ void set_if_true(std::unique_ptr<BlockNode> t) { if_true_ = std::move(t); }
+
+ // This is either empty, a block (for the else clause), or another
+ // condition.
+ const ParseNode* if_false() const { return if_false_.get(); }
+ void set_if_false(std::unique_ptr<ParseNode> f) { if_false_ = std::move(f); }
+
+ static constexpr const char* kDumpNodeName = "CONDITION";
+
+ private:
+ // Token corresponding to the "if" string.
+ Token if_token_;
+
+ std::unique_ptr<ParseNode> condition_; // Always non-null.
+ std::unique_ptr<BlockNode> if_true_; // Always non-null.
+ std::unique_ptr<ParseNode> if_false_; // May be null.
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionNode);
+};
+
+// FunctionCallNode ------------------------------------------------------------
+
+class FunctionCallNode : public ParseNode {
+ public:
+ FunctionCallNode();
+ ~FunctionCallNode() override;
+
+ const FunctionCallNode* AsFunctionCall() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<FunctionCallNode> NewFromJSON(
+ const base::Value& value);
+
+ const Token& function() const { return function_; }
+ void set_function(Token t) { function_ = t; }
+
+ const ListNode* args() const { return args_.get(); }
+ void set_args(std::unique_ptr<ListNode> a) { args_ = std::move(a); }
+
+ const BlockNode* block() const { return block_.get(); }
+ void set_block(std::unique_ptr<BlockNode> b) { block_ = std::move(b); }
+
+ void SetNewLocation(int line_number);
+
+ static constexpr const char* kDumpNodeName = "FUNCTION";
+
+ private:
+ Token function_;
+ std::unique_ptr<ListNode> args_;
+ std::unique_ptr<BlockNode> block_; // May be null.
+
+ DISALLOW_COPY_AND_ASSIGN(FunctionCallNode);
+};
+
+// IdentifierNode --------------------------------------------------------------
+
+class IdentifierNode : public ParseNode {
+ public:
+ IdentifierNode();
+ explicit IdentifierNode(const Token& token);
+ ~IdentifierNode() override;
+
+ const IdentifierNode* AsIdentifier() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<IdentifierNode> NewFromJSON(const base::Value& value);
+
+ const Token& value() const { return value_; }
+ void set_value(const Token& t) { value_ = t; }
+
+ void SetNewLocation(int line_number);
+
+ static constexpr const char* kDumpNodeName = "IDENTIFIER";
+
+ private:
+ Token value_;
+
+ DISALLOW_COPY_AND_ASSIGN(IdentifierNode);
+};
+
+// ListNode --------------------------------------------------------------------
+
+class ListNode : public ParseNode {
+ public:
+ ListNode();
+ ~ListNode() override;
+
+ const ListNode* AsList() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<ListNode> NewFromJSON(const base::Value& value);
+
+ void set_begin_token(const Token& t) { begin_token_ = t; }
+ const Token& Begin() const { return begin_token_; }
+ void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
+ const EndNode* End() const { return end_.get(); }
+
+ void append_item(std::unique_ptr<ParseNode> s) {
+ contents_.push_back(std::move(s));
+ }
+ const std::vector<std::unique_ptr<const ParseNode>>& contents() const {
+ return contents_;
+ }
+
+ void SortAsStringsList();
+ void SortAsTargetsList();
+
+ struct SortRange {
+ size_t begin;
+ size_t end;
+ SortRange(size_t begin, size_t end) : begin(begin), end(end) {}
+ };
+ // Only public for testing.
+ std::vector<SortRange> GetSortRanges() const;
+
+ static constexpr const char* kDumpNodeName = "LIST";
+
+ private:
+ template <typename Comparator>
+ void SortList(Comparator comparator);
+
+ // Tokens corresponding to the [ and ]. The end token is stored in inside an
+ // custom parse node so that it can have comments hung off of it.
+ Token begin_token_;
+ std::unique_ptr<EndNode> end_;
+
+ std::vector<std::unique_ptr<const ParseNode>> contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListNode);
+};
+
+// LiteralNode -----------------------------------------------------------------
+
+class LiteralNode : public ParseNode {
+ public:
+ LiteralNode();
+ explicit LiteralNode(const Token& token);
+ ~LiteralNode() override;
+
+ const LiteralNode* AsLiteral() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<LiteralNode> NewFromJSON(const base::Value& value);
+
+ const Token& value() const { return value_; }
+ void set_value(const Token& t) { value_ = t; }
+
+ void SetNewLocation(int line_number);
+
+ static constexpr const char* kDumpNodeName = "LITERAL";
+
+ private:
+ Token value_;
+
+ DISALLOW_COPY_AND_ASSIGN(LiteralNode);
+};
+
+// UnaryOpNode -----------------------------------------------------------------
+
+class UnaryOpNode : public ParseNode {
+ public:
+ UnaryOpNode();
+ ~UnaryOpNode() override;
+
+ const UnaryOpNode* AsUnaryOp() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<UnaryOpNode> NewFromJSON(const base::Value& value);
+
+ const Token& op() const { return op_; }
+ void set_op(const Token& t) { op_ = t; }
+
+ const ParseNode* operand() const { return operand_.get(); }
+ void set_operand(std::unique_ptr<ParseNode> operand) {
+ operand_ = std::move(operand);
+ }
+
+ static constexpr const char* kDumpNodeName = "UNARY";
+
+ private:
+ Token op_;
+ std::unique_ptr<ParseNode> operand_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnaryOpNode);
+};
+
+// BlockCommentNode ------------------------------------------------------------
+
+// This node type is only used for standalone comments (that is, those not
+// specifically attached to another syntax element. The most common of these
+// is a standard header block. This node contains only the last line of such
+// a comment block as the anchor, and other lines of the block comment are
+// hung off of it as Before comments, similar to other syntax elements.
+class BlockCommentNode : public ParseNode {
+ public:
+ BlockCommentNode();
+ ~BlockCommentNode() override;
+
+ const BlockCommentNode* AsBlockComment() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<BlockCommentNode> NewFromJSON(
+ const base::Value& value);
+
+ const Token& comment() const { return comment_; }
+ void set_comment(const Token& t) { comment_ = t; }
+
+ static constexpr const char* kDumpNodeName = "BLOCK_COMMENT";
+
+ private:
+ Token comment_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlockCommentNode);
+};
+
+// EndNode ---------------------------------------------------------------------
+
+// This node type is used as the end_ object for lists and blocks (rather than
+// just the end ']', '}', or ')' token). This is so that during formatting
+// traversal there is a node that appears at the end of the block to which
+// comments can be attached.
+class EndNode : public ParseNode {
+ public:
+ explicit EndNode(const Token& token);
+ ~EndNode() override;
+
+ const EndNode* AsEnd() const override;
+ Value Execute(Scope* scope, Err* err) const override;
+ LocationRange GetRange() const override;
+ Err MakeErrorDescribing(
+ const std::string& msg,
+ const std::string& help = std::string()) const override;
+ base::Value GetJSONNode() const override;
+ static std::unique_ptr<EndNode> NewFromJSON(const base::Value& value);
+
+ const Token& value() const { return value_; }
+ void set_value(const Token& t) { value_ = t; }
+
+ static constexpr const char* kDumpNodeName = "END";
+
+ private:
+ Token value_;
+
+ DISALLOW_COPY_AND_ASSIGN(EndNode);
+};
+
+#endif // TOOLS_GN_PARSE_TREE_H_
diff --git a/gn/src/gn/parse_tree_unittest.cc b/gn/src/gn/parse_tree_unittest.cc
new file mode 100644
index 00000000000..ed28b54bcc1
--- /dev/null
+++ b/gn/src/gn/parse_tree_unittest.cc
@@ -0,0 +1,307 @@
+// 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 "gn/parse_tree.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "gn/input_file.h"
+#include "gn/scope.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(ParseTree, Accessor) {
+ TestWithScope setup;
+
+ // Make a pretend parse node with proper tracking that we can blame for the
+ // given value.
+ InputFile input_file(SourceFile("//foo"));
+ Token base_token(Location(&input_file, 1, 1, 1), Token::IDENTIFIER, "a");
+ Token member_token(Location(&input_file, 1, 1, 1), Token::IDENTIFIER, "b");
+
+ AccessorNode accessor;
+ accessor.set_base(base_token);
+
+ std::unique_ptr<IdentifierNode> member_identifier =
+ std::make_unique<IdentifierNode>(member_token);
+ accessor.set_member(std::move(member_identifier));
+
+ // The access should fail because a is not defined.
+ Err err;
+ Value result = accessor.Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+ EXPECT_EQ(Value::NONE, result.type());
+
+ // Define a as a Scope. It should still fail because b isn't defined.
+ err = Err();
+ setup.scope()->SetValue(
+ "a", Value(nullptr, std::make_unique<Scope>(setup.scope())), nullptr);
+ result = accessor.Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(Value::NONE, result.type());
+
+ // Define b, accessor should succeed now.
+ const int64_t kBValue = 42;
+ err = Err();
+ setup.scope()
+ ->GetMutableValue("a", Scope::SEARCH_NESTED, false)
+ ->scope_value()
+ ->SetValue("b", Value(nullptr, kBValue), nullptr);
+ result = accessor.Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(Value::INTEGER, result.type());
+ EXPECT_EQ(kBValue, result.int_value());
+}
+
+TEST(ParseTree, SubscriptedAccess) {
+ TestWithScope setup;
+ Err err;
+ TestParseInput values(
+ "list = [ 2, 3 ]\n"
+ "scope = {\n"
+ " foo = 5\n"
+ " bar = 8\n"
+ "}\n"
+ "bar_key = \"bar\""
+ "second_element_idx = 1");
+ values.parsed()->Execute(setup.scope(), &err);
+
+ EXPECT_FALSE(err.has_error());
+
+ Value first = setup.ExecuteExpression("list[0]", &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(first.type(), Value::INTEGER);
+ EXPECT_EQ(first.int_value(), 2);
+
+ Value second = setup.ExecuteExpression("list[second_element_idx]", &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(second.type(), Value::INTEGER);
+ EXPECT_EQ(second.int_value(), 3);
+
+ Value foo = setup.ExecuteExpression("scope[\"foo\"]", &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(foo.type(), Value::INTEGER);
+ EXPECT_EQ(foo.int_value(), 5);
+
+ Value bar = setup.ExecuteExpression("scope[bar_key]", &err);
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(bar.type(), Value::INTEGER);
+ EXPECT_EQ(bar.int_value(), 8);
+
+ Value invalid1 = setup.ExecuteExpression("scope[second_element_idx]", &err);
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+ EXPECT_EQ(invalid1.type(), Value::NONE);
+
+ Value invalid2 = setup.ExecuteExpression("list[bar_key]", &err);
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+ EXPECT_EQ(invalid2.type(), Value::NONE);
+
+ Value invalid3 = setup.ExecuteExpression("scope[\"baz\"]", &err);
+ EXPECT_TRUE(err.has_error());
+ err = Err();
+ EXPECT_EQ(invalid3.type(), Value::NONE);
+}
+
+TEST(ParseTree, BlockUnusedVars) {
+ TestWithScope setup;
+
+ // Printing both values should be OK.
+ //
+ // The crazy template definition here is a way to execute a block without
+ // defining a target. Templates require that both the target_name and the
+ // invoker be used, which is what the assertion statement inside the template
+ // does.
+ TestParseInput input_all_used(
+ "template(\"foo\") { assert(target_name != 0 && invoker != 0) }\n"
+ "foo(\"a\") {\n"
+ " a = 12\n"
+ " b = 13\n"
+ " print(\"$a $b\")\n"
+ "}");
+ EXPECT_FALSE(input_all_used.has_error());
+
+ Err err;
+ input_all_used.parsed()->Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Skipping one should throw an unused var error.
+ TestParseInput input_unused(
+ "foo(\"a\") {\n"
+ " a = 12\n"
+ " b = 13\n"
+ " print(\"$a\")\n"
+ "}");
+ EXPECT_FALSE(input_unused.has_error());
+
+ input_unused.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+
+ // Also verify that the unused variable has the correct origin set. The
+ // origin will point to the value assigned to the variable (in this case, the
+ // "13" assigned to "b".
+ EXPECT_EQ(3, err.location().line_number());
+ EXPECT_EQ(7, err.location().column_number());
+}
+
+TEST(ParseTree, OriginForDereference) {
+ TestWithScope setup;
+ TestParseInput input(
+ "a = 6\n"
+ "get_target_outputs(a)");
+ EXPECT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+
+ // The origin for the "not a string" error message should be where the value
+ // was dereferenced (the "a" on the second line).
+ EXPECT_EQ(2, err.location().line_number());
+ EXPECT_EQ(20, err.location().column_number());
+}
+
+TEST(ParseTree, SortRangeExtraction) {
+ TestWithScope setup;
+
+ // Ranges are [begin, end).
+
+ {
+ TestParseInput input(
+ "sources = [\n"
+ " \"a\",\n"
+ " \"b\",\n"
+ " \n"
+ " #\n"
+ " # Block\n"
+ " #\n"
+ " \n"
+ " \"c\","
+ " \"d\","
+ "]\n");
+ EXPECT_FALSE(input.has_error());
+ ASSERT_TRUE(input.parsed()->AsBlock());
+ ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
+ const BinaryOpNode* binop =
+ input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
+ ASSERT_TRUE(binop->right()->AsList());
+ const ListNode* list = binop->right()->AsList();
+ EXPECT_EQ(5u, list->contents().size());
+ auto ranges = list->GetSortRanges();
+ ASSERT_EQ(2u, ranges.size());
+ EXPECT_EQ(0u, ranges[0].begin);
+ EXPECT_EQ(2u, ranges[0].end);
+ EXPECT_EQ(3u, ranges[1].begin);
+ EXPECT_EQ(5u, ranges[1].end);
+ }
+
+ {
+ TestParseInput input(
+ "sources = [\n"
+ " \"a\",\n"
+ " \"b\",\n"
+ " \n"
+ " # Attached comment.\n"
+ " \"c\","
+ " \"d\","
+ "]\n");
+ EXPECT_FALSE(input.has_error());
+ ASSERT_TRUE(input.parsed()->AsBlock());
+ ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
+ const BinaryOpNode* binop =
+ input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
+ ASSERT_TRUE(binop->right()->AsList());
+ const ListNode* list = binop->right()->AsList();
+ EXPECT_EQ(4u, list->contents().size());
+ auto ranges = list->GetSortRanges();
+ ASSERT_EQ(2u, ranges.size());
+ EXPECT_EQ(0u, ranges[0].begin);
+ EXPECT_EQ(2u, ranges[0].end);
+ EXPECT_EQ(2u, ranges[1].begin);
+ EXPECT_EQ(4u, ranges[1].end);
+ }
+
+ {
+ TestParseInput input(
+ "sources = [\n"
+ " # At end of list.\n"
+ " \"zzzzzzzzzzz.cc\","
+ "]\n");
+ EXPECT_FALSE(input.has_error());
+ ASSERT_TRUE(input.parsed()->AsBlock());
+ ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
+ const BinaryOpNode* binop =
+ input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
+ ASSERT_TRUE(binop->right()->AsList());
+ const ListNode* list = binop->right()->AsList();
+ EXPECT_EQ(1u, list->contents().size());
+ auto ranges = list->GetSortRanges();
+ ASSERT_EQ(1u, ranges.size());
+ EXPECT_EQ(0u, ranges[0].begin);
+ EXPECT_EQ(1u, ranges[0].end);
+ }
+
+ {
+ TestParseInput input(
+ "sources = [\n"
+ " # Block at start.\n"
+ " \n"
+ " \"z.cc\","
+ " \"y.cc\","
+ "]\n");
+ EXPECT_FALSE(input.has_error());
+ ASSERT_TRUE(input.parsed()->AsBlock());
+ ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
+ const BinaryOpNode* binop =
+ input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
+ ASSERT_TRUE(binop->right()->AsList());
+ const ListNode* list = binop->right()->AsList();
+ EXPECT_EQ(3u, list->contents().size());
+ auto ranges = list->GetSortRanges();
+ ASSERT_EQ(1u, ranges.size());
+ EXPECT_EQ(1u, ranges[0].begin);
+ EXPECT_EQ(3u, ranges[0].end);
+ }
+}
+
+TEST(ParseTree, Integers) {
+ static const char* const kGood[] = {
+ "0",
+ "10",
+ "-54321",
+ "9223372036854775807", // INT64_MAX
+ "-9223372036854775808", // INT64_MIN
+ };
+ for (auto* s : kGood) {
+ TestParseInput input(std::string("x = ") + s);
+ EXPECT_FALSE(input.has_error());
+
+ TestWithScope setup;
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ EXPECT_FALSE(err.has_error());
+ }
+
+ static const char* const kBad[] = {
+ "-0",
+ "010",
+ "-010",
+ "9223372036854775808", // INT64_MAX + 1
+ "-9223372036854775809", // INT64_MIN - 1
+ };
+ for (auto* s : kBad) {
+ TestParseInput input(std::string("x = ") + s);
+ EXPECT_FALSE(input.has_error());
+
+ TestWithScope setup;
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+ }
+}
diff --git a/gn/src/gn/parser.cc b/gn/src/gn/parser.cc
new file mode 100644
index 00000000000..07e9ba326c6
--- /dev/null
+++ b/gn/src/gn/parser.cc
@@ -0,0 +1,941 @@
+// 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 "gn/parser.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "gn/functions.h"
+#include "gn/operators.h"
+#include "gn/token.h"
+
+const char kGrammar_Help[] =
+ R"*(Language and grammar for GN build files
+
+Tokens
+
+ GN build files are read as sequences of tokens. While splitting the file
+ into tokens, the next token is the longest sequence of characters that form a
+ valid token.
+
+White space and comments
+
+ White space is comprised of spaces (U+0020), horizontal tabs (U+0009),
+ carriage returns (U+000D), and newlines (U+000A).
+
+ Comments start at the character "#" and stop at the next newline.
+
+ White space and comments are ignored except that they may separate tokens
+ that would otherwise combine into a single token.
+
+Identifiers
+
+ Identifiers name variables and functions.
+
+ identifier = letter { letter | digit } .
+ letter = "A" ... "Z" | "a" ... "z" | "_" .
+ digit = "0" ... "9" .
+
+Keywords
+
+ The following keywords are reserved and may not be used as identifiers:
+
+ else false if true
+
+Integer literals
+
+ An integer literal represents a decimal integer value.
+
+ integer = [ "-" ] digit { digit } .
+
+ Leading zeros and negative zero are disallowed.
+
+String literals
+
+ A string literal represents a string value consisting of the quoted
+ characters with possible escape sequences and variable expansions.
+
+ string = `"` { char | escape | expansion } `"` .
+ escape = `\` ( "$" | `"` | char ) .
+ BracketExpansion = "{" ( identifier | ArrayAccess | ScopeAccess ) "}" .
+ Hex = "0x" [0-9A-Fa-f][0-9A-Fa-f]
+ expansion = "$" ( identifier | BracketExpansion | Hex ) .
+ char = /* any character except "$", `"`, or newline */ .
+
+ After a backslash, certain sequences represent special characters:
+
+ \" U+0022 quotation mark
+ \$ U+0024 dollar sign
+ \\ U+005C backslash
+
+ All other backslashes represent themselves.
+
+ To insert an arbitrary byte value, use $0xFF. For example, to insert a
+ newline character: "Line one$0x0ALine two".
+
+ An expansion will evaluate the variable following the '$' and insert a
+ stringified version of it into the result. For example, to concat two path
+ components with a slash separating them:
+ "$var_one/$var_two"
+ Use the "${var_one}" format to be explicitly deliniate the variable for
+ otherwise-ambiguous cases.
+
+Punctuation
+
+ The following character sequences represent punctuation:
+
+ + += == != ( )
+ - -= < <= [ ]
+ ! = > >= { }
+ && || . ,
+
+Grammar
+
+ The input tokens form a syntax tree following a context-free grammar:
+
+ File = StatementList .
+
+ Statement = Assignment | Call | Condition .
+ LValue = identifier | ArrayAccess | ScopeAccess .
+ Assignment = LValue AssignOp Expr .
+ Call = identifier "(" [ ExprList ] ")" [ Block ] .
+ Condition = "if" "(" Expr ")" Block
+ [ "else" ( Condition | Block ) ] .
+ Block = "{" StatementList "}" .
+ StatementList = { Statement } .
+
+ ArrayAccess = identifier "[" Expr "]" .
+ ScopeAccess = identifier "." identifier .
+ Expr = UnaryExpr | Expr BinaryOp Expr .
+ UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .
+ PrimaryExpr = identifier | integer | string | Call
+ | ArrayAccess | ScopeAccess | Block
+ | "(" Expr ")"
+ | "[" [ ExprList [ "," ] ] "]" .
+ ExprList = Expr { "," Expr } .
+
+ AssignOp = "=" | "+=" | "-=" .
+ UnaryOp = "!" .
+ BinaryOp = "+" | "-" // highest priority
+ | "<" | "<=" | ">" | ">="
+ | "==" | "!="
+ | "&&"
+ | "||" . // lowest priority
+
+ All binary operators are left-associative.
+
+Types
+
+ The GN language is dynamically typed. The following types are used:
+
+ - Boolean: Uses the keywords "true" and "false". There is no implicit
+ conversion between booleans and integers.
+
+ - Integers: All numbers in GN are signed 64-bit integers.
+
+ - Strings: Strings are 8-bit with no enforced encoding. When a string is
+ used to interact with other systems with particular encodings (like the
+ Windows and Mac filesystems) it is assumed to be UTF-8. See "String
+ literals" above for more.
+
+ - Lists: Lists are arbitrary-length ordered lists of values. See "Lists"
+ below for more.
+
+ - Scopes: Scopes are like dictionaries that use variable names for keys. See
+ "Scopes" below for more.
+
+Lists
+
+ Lists are created with [] and using commas to separate items:
+
+ mylist = [ 0, 1, 2, "some string" ]
+
+ A comma after the last item is optional. Lists are dereferenced using 0-based
+ indexing:
+
+ mylist[0] += 1
+ var = mylist[2]
+
+ Lists can be concatenated using the '+' and '+=' operators. Bare values can
+ not be concatenated with lists, to add a single item, it must be put into a
+ list of length one.
+
+ Items can be removed from lists using the '-' and '-=' operators. This will
+ remove all occurrences of every item in the right-hand list from the
+ left-hand list. It is an error to remove an item not in the list. This is to
+ prevent common typos and to detect dead code that is removing things that no
+ longer apply.
+
+ It is an error to use '=' to replace a nonempty list with another nonempty
+ list. This is to prevent accidentally overwriting data when in most cases
+ '+=' was intended. To overwrite a list on purpose, first assign it to the
+ empty list:
+
+ mylist = []
+ mylist = otherlist
+
+Scopes
+
+ All execution happens in the context of a scope which holds the current state
+ (like variables). With the exception of loops and conditions, '{' introduces
+ a new scope that has a parent reference to the old scope.
+
+ Variable reads recursively search all nested scopes until the variable is
+ found or there are no more scopes. Variable writes always go into the current
+ scope. This means that after the closing '}' (again excepting loops and
+ conditions), all local variables will be restored to the previous values.
+ This also means that "foo = foo" can do useful work by copying a variable
+ into the current scope that was defined in a containing scope.
+
+ Scopes can also be assigned to variables. Such scopes can be created by
+ functions like exec_script, when invoking a template (the template code
+ refers to the variables set by the invoking code by the implicitly-created
+ "invoker" scope), or explicitly like:
+
+ empty_scope = {}
+ myvalues = {
+ foo = 21
+ bar = "something"
+ }
+
+ Inside such a scope definition can be any GN code including conditionals and
+ function calls. After the close of the scope, it will contain all variables
+ explicitly set by the code contained inside it. After this, the values can be
+ read, modified, or added to:
+
+ myvalues.foo += 2
+ empty_scope.new_thing = [ 1, 2, 3 ]
+
+ Scope equality is defined as single-level scopes identical within the current
+ scope. That is, all values in the first scope must be present and identical
+ within the second, and vice versa. Note that this means inherited scopes are
+ always unequal by definition.
+)*";
+
+// Precedence constants.
+//
+// Currently all operators are left-associative so this list is sequential. To
+// implement a right-assocative operators in a Pratt parser we would leave gaps
+// in between the constants, and right-associative operators get a precedence
+// of "<left-associated-precedence> - 1".
+enum Precedence {
+ PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence.
+ PRECEDENCE_OR = 2,
+ PRECEDENCE_AND = 3,
+ PRECEDENCE_EQUALITY = 4,
+ PRECEDENCE_RELATION = 5,
+ PRECEDENCE_SUM = 6,
+ PRECEDENCE_PREFIX = 7,
+ PRECEDENCE_CALL = 8,
+ PRECEDENCE_DOT = 9, // Highest precedence.
+};
+
+// The top-level for blocks/ifs is recursive descent, the expression parser is
+// a Pratt parser. The basic idea there is to have the precedences (and
+// associativities) encoded relative to each other and only parse up until you
+// hit something of that precedence. There's a dispatch table in expressions_
+// at the top of parser.cc that describes how each token dispatches if it's
+// seen as either a prefix or infix operator, and if it's infix, what its
+// precedence is.
+//
+// References:
+// http://javascript.crockford.com/tdop/tdop.html
+// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
+
+// Indexed by Token::Type.
+ParserHelper Parser::expressions_[] = {
+ {nullptr, nullptr, -1}, // INVALID
+ {&Parser::Literal, nullptr, -1}, // INTEGER
+ {&Parser::Literal, nullptr, -1}, // STRING
+ {&Parser::Literal, nullptr, -1}, // TRUE_TOKEN
+ {&Parser::Literal, nullptr, -1}, // FALSE_TOKEN
+ {nullptr, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // EQUAL
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_SUM}, // PLUS
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_SUM}, // MINUS
+ {nullptr, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // PLUS_EQUALS
+ {nullptr, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // MINUS_EQUALS
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // EQUAL_EQUAL
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // NOT_EQUAL
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_EQUAL
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_EQUAL
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND
+ {nullptr, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR
+ {&Parser::Not, nullptr, -1}, // BANG
+ {nullptr, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT
+ {&Parser::Group, nullptr, -1}, // LEFT_PAREN
+ {nullptr, nullptr, -1}, // RIGHT_PAREN
+ {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET
+ {nullptr, nullptr, -1}, // RIGHT_BRACKET
+ {&Parser::Block, nullptr, -1}, // LEFT_BRACE
+ {nullptr, nullptr, -1}, // RIGHT_BRACE
+ {nullptr, nullptr, -1}, // IF
+ {nullptr, nullptr, -1}, // ELSE
+ {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER
+ {nullptr, nullptr, -1}, // COMMA
+ {nullptr, nullptr, -1}, // UNCLASSIFIED_COMMENT
+ {nullptr, nullptr, -1}, // LINE_COMMENT
+ {nullptr, nullptr, -1}, // SUFFIX_COMMENT
+ {&Parser::BlockComment, nullptr, -1}, // BLOCK_COMMENT
+};
+
+Parser::Parser(const std::vector<Token>& tokens, Err* err)
+ : invalid_token_(Location(), Token::INVALID, std::string_view()),
+ err_(err),
+ cur_(0) {
+ for (const auto& token : tokens) {
+ switch (token.type()) {
+ case Token::LINE_COMMENT:
+ line_comment_tokens_.push_back(token);
+ break;
+ case Token::SUFFIX_COMMENT:
+ suffix_comment_tokens_.push_back(token);
+ break;
+ default:
+ // Note that BLOCK_COMMENTs (top-level standalone comments) are passed
+ // through the real parser.
+ tokens_.push_back(token);
+ break;
+ }
+ }
+}
+
+Parser::~Parser() = default;
+
+// static
+std::unique_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
+ Err* err) {
+ Parser p(tokens, err);
+ return p.ParseFile();
+}
+
+// static
+std::unique_ptr<ParseNode> Parser::ParseExpression(
+ const std::vector<Token>& tokens,
+ Err* err) {
+ Parser p(tokens, err);
+ std::unique_ptr<ParseNode> expr = p.ParseExpression();
+ if (!p.at_end() && !err->has_error()) {
+ *err = Err(p.cur_token(), "Trailing garbage");
+ return nullptr;
+ }
+ return expr;
+}
+
+// static
+std::unique_ptr<ParseNode> Parser::ParseValue(const std::vector<Token>& tokens,
+ Err* err) {
+ for (const Token& token : tokens) {
+ switch (token.type()) {
+ case Token::INTEGER:
+ case Token::STRING:
+ case Token::TRUE_TOKEN:
+ case Token::FALSE_TOKEN:
+ case Token::LEFT_BRACKET:
+ case Token::RIGHT_BRACKET:
+ case Token::COMMA:
+ continue;
+ default:
+ *err = Err(token, "Invalid token in literal value");
+ return nullptr;
+ }
+ }
+
+ return ParseExpression(tokens, err);
+}
+
+bool Parser::IsAssignment(const ParseNode* node) const {
+ return node && node->AsBinaryOp() &&
+ (node->AsBinaryOp()->op().type() == Token::EQUAL ||
+ node->AsBinaryOp()->op().type() == Token::PLUS_EQUALS ||
+ node->AsBinaryOp()->op().type() == Token::MINUS_EQUALS);
+}
+
+bool Parser::IsStatementBreak(Token::Type token_type) const {
+ switch (token_type) {
+ case Token::IDENTIFIER:
+ case Token::LEFT_BRACE:
+ case Token::RIGHT_BRACE:
+ case Token::IF:
+ case Token::ELSE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Parser::LookAhead(Token::Type type) {
+ if (at_end())
+ return false;
+ return cur_token().type() == type;
+}
+
+bool Parser::Match(Token::Type type) {
+ if (!LookAhead(type))
+ return false;
+ Consume();
+ return true;
+}
+
+const Token& Parser::Consume(Token::Type type, const char* error_message) {
+ Token::Type types[1] = {type};
+ return Consume(types, 1, error_message);
+}
+
+const Token& Parser::Consume(Token::Type* types,
+ size_t num_types,
+ const char* error_message) {
+ if (has_error()) {
+ // Don't overwrite current error, but make progress through tokens so that
+ // a loop that's expecting a particular token will still terminate.
+ if (!at_end())
+ cur_++;
+ return invalid_token_;
+ }
+ if (at_end()) {
+ const char kEOFMsg[] = "I hit EOF instead.";
+ if (tokens_.empty())
+ *err_ = Err(Location(), error_message, kEOFMsg);
+ else
+ *err_ = Err(tokens_[tokens_.size() - 1], error_message, kEOFMsg);
+ return invalid_token_;
+ }
+
+ for (size_t i = 0; i < num_types; ++i) {
+ if (cur_token().type() == types[i])
+ return Consume();
+ }
+ *err_ = Err(cur_token(), error_message);
+ return invalid_token_;
+}
+
+const Token& Parser::Consume() {
+ return tokens_[cur_++];
+}
+
+std::unique_ptr<ParseNode> Parser::ParseExpression() {
+ return ParseExpression(0);
+}
+
+std::unique_ptr<ParseNode> Parser::ParseExpression(int precedence) {
+ if (at_end())
+ return std::unique_ptr<ParseNode>();
+
+ const Token& token = Consume();
+ PrefixFunc prefix = expressions_[token.type()].prefix;
+
+ if (prefix == nullptr) {
+ *err_ = Err(token, "Unexpected token '" + std::string(token.value()) + "'");
+ return std::unique_ptr<ParseNode>();
+ }
+
+ std::unique_ptr<ParseNode> left = (this->*prefix)(token);
+ if (has_error())
+ return left;
+
+ while (!at_end() && !IsStatementBreak(cur_token().type()) &&
+ precedence <= expressions_[cur_token().type()].precedence) {
+ const Token& next_token = Consume();
+ InfixFunc infix = expressions_[next_token.type()].infix;
+ if (infix == nullptr) {
+ *err_ = Err(next_token,
+ "Unexpected token '" + std::string(next_token.value()) + "'");
+ return std::unique_ptr<ParseNode>();
+ }
+ left = (this->*infix)(std::move(left), next_token);
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+ }
+
+ return left;
+}
+
+std::unique_ptr<ParseNode> Parser::Block(const Token& token) {
+ // This entrypoint into ParseBlock means it's part of an expression and we
+ // always want the result.
+ return ParseBlock(token, BlockNode::RETURNS_SCOPE);
+}
+
+std::unique_ptr<ParseNode> Parser::Literal(const Token& token) {
+ return std::make_unique<LiteralNode>(token);
+}
+
+std::unique_ptr<ParseNode> Parser::Name(const Token& token) {
+ return IdentifierOrCall(std::unique_ptr<ParseNode>(), token);
+}
+
+std::unique_ptr<ParseNode> Parser::BlockComment(const Token& token) {
+ std::unique_ptr<BlockCommentNode> comment =
+ std::make_unique<BlockCommentNode>();
+ comment->set_comment(token);
+ return std::move(comment);
+}
+
+std::unique_ptr<ParseNode> Parser::Group(const Token& token) {
+ std::unique_ptr<ParseNode> expr = ParseExpression();
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+ Consume(Token::RIGHT_PAREN, "Expected ')'");
+ return expr;
+}
+
+std::unique_ptr<ParseNode> Parser::Not(const Token& token) {
+ std::unique_ptr<ParseNode> expr = ParseExpression(PRECEDENCE_PREFIX + 1);
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+ if (!expr) {
+ if (!has_error())
+ *err_ = Err(token, "Expected right-hand side for '!'.");
+ return std::unique_ptr<ParseNode>();
+ }
+ std::unique_ptr<UnaryOpNode> unary_op = std::make_unique<UnaryOpNode>();
+ unary_op->set_op(token);
+ unary_op->set_operand(std::move(expr));
+ return std::move(unary_op);
+}
+
+std::unique_ptr<ParseNode> Parser::List(const Token& node) {
+ std::unique_ptr<ParseNode> list(ParseList(node, Token::RIGHT_BRACKET, true));
+ if (!has_error() && !at_end())
+ Consume(Token::RIGHT_BRACKET, "Expected ']'");
+ return list;
+}
+
+std::unique_ptr<ParseNode> Parser::BinaryOperator(
+ std::unique_ptr<ParseNode> left,
+ const Token& token) {
+ std::unique_ptr<ParseNode> right =
+ ParseExpression(expressions_[token.type()].precedence + 1);
+ if (!right) {
+ if (!has_error()) {
+ *err_ = Err(token, "Expected right-hand side for '" +
+ std::string(token.value()) + "'");
+ }
+ return std::unique_ptr<ParseNode>();
+ }
+ std::unique_ptr<BinaryOpNode> binary_op = std::make_unique<BinaryOpNode>();
+ binary_op->set_op(token);
+ binary_op->set_left(std::move(left));
+ binary_op->set_right(std::move(right));
+ return std::move(binary_op);
+}
+
+std::unique_ptr<ParseNode> Parser::IdentifierOrCall(
+ std::unique_ptr<ParseNode> left,
+ const Token& token) {
+ std::unique_ptr<ListNode> list = std::make_unique<ListNode>();
+ list->set_begin_token(token);
+ list->set_end(std::make_unique<EndNode>(token));
+ std::unique_ptr<BlockNode> block;
+ bool has_arg = false;
+ if (LookAhead(Token::LEFT_PAREN)) {
+ const Token& start_token = Consume();
+ // Parsing a function call.
+ has_arg = true;
+ if (Match(Token::RIGHT_PAREN)) {
+ // Nothing, just an empty call.
+ } else {
+ list = ParseList(start_token, Token::RIGHT_PAREN, false);
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+ Consume(Token::RIGHT_PAREN, "Expected ')' after call");
+ }
+ // Optionally with a scope.
+ if (LookAhead(Token::LEFT_BRACE)) {
+ block = ParseBlock(Consume(), BlockNode::DISCARDS_RESULT);
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+ }
+ }
+
+ if (!left && !has_arg) {
+ // Not a function call, just a standalone identifier.
+ return std::make_unique<IdentifierNode>(token);
+ }
+ std::unique_ptr<FunctionCallNode> func_call =
+ std::make_unique<FunctionCallNode>();
+ func_call->set_function(token);
+ func_call->set_args(std::move(list));
+ if (block)
+ func_call->set_block(std::move(block));
+ return std::move(func_call);
+}
+
+std::unique_ptr<ParseNode> Parser::Assignment(std::unique_ptr<ParseNode> left,
+ const Token& token) {
+ if (left->AsIdentifier() == nullptr && left->AsAccessor() == nullptr) {
+ *err_ = Err(left.get(),
+ "The left-hand side of an assignment must be an identifier, "
+ "scope access, or array access.");
+ return std::unique_ptr<ParseNode>();
+ }
+ std::unique_ptr<ParseNode> value = ParseExpression(PRECEDENCE_ASSIGNMENT);
+ if (!value) {
+ if (!has_error())
+ *err_ = Err(token, "Expected right-hand side for assignment.");
+ return std::unique_ptr<ParseNode>();
+ }
+ std::unique_ptr<BinaryOpNode> assign = std::make_unique<BinaryOpNode>();
+ assign->set_op(token);
+ assign->set_left(std::move(left));
+ assign->set_right(std::move(value));
+ return std::move(assign);
+}
+
+std::unique_ptr<ParseNode> Parser::Subscript(std::unique_ptr<ParseNode> left,
+ const Token& token) {
+ // TODO: Maybe support more complex expressions like a[0][0]. This would
+ // require work on the evaluator too.
+ if (left->AsIdentifier() == nullptr) {
+ *err_ = Err(
+ left.get(), "May only subscript identifiers.",
+ "The thing on the left hand side of the [] must be an identifier\n"
+ "and not an expression. If you need this, you'll have to assign the\n"
+ "value to a temporary before subscripting. Sorry.");
+ return std::unique_ptr<ParseNode>();
+ }
+ std::unique_ptr<ParseNode> value = ParseExpression();
+ Consume(Token::RIGHT_BRACKET, "Expecting ']' after subscript.");
+ std::unique_ptr<AccessorNode> accessor = std::make_unique<AccessorNode>();
+ accessor->set_base(left->AsIdentifier()->value());
+ accessor->set_subscript(std::move(value));
+ return std::move(accessor);
+}
+
+std::unique_ptr<ParseNode> Parser::DotOperator(std::unique_ptr<ParseNode> left,
+ const Token& token) {
+ if (left->AsIdentifier() == nullptr) {
+ *err_ = Err(
+ left.get(), "May only use \".\" for identifiers.",
+ "The thing on the left hand side of the dot must be an identifier\n"
+ "and not an expression. If you need this, you'll have to assign the\n"
+ "value to a temporary first. Sorry.");
+ return std::unique_ptr<ParseNode>();
+ }
+
+ std::unique_ptr<ParseNode> right = ParseExpression(PRECEDENCE_DOT);
+ if (!right || !right->AsIdentifier()) {
+ *err_ = Err(
+ token, "Expected identifier for right-hand-side of \".\"",
+ "Good: a.cookies\nBad: a.42\nLooks good but still bad: a.cookies()");
+ return std::unique_ptr<ParseNode>();
+ }
+
+ std::unique_ptr<AccessorNode> accessor = std::make_unique<AccessorNode>();
+ accessor->set_base(left->AsIdentifier()->value());
+ accessor->set_member(std::unique_ptr<IdentifierNode>(
+ static_cast<IdentifierNode*>(right.release())));
+ return std::move(accessor);
+}
+
+// Does not Consume the start or end token.
+std::unique_ptr<ListNode> Parser::ParseList(const Token& start_token,
+ Token::Type stop_before,
+ bool allow_trailing_comma) {
+ std::unique_ptr<ListNode> list = std::make_unique<ListNode>();
+ list->set_begin_token(start_token);
+ bool just_got_comma = false;
+ bool first_time = true;
+ while (!LookAhead(stop_before)) {
+ if (!first_time) {
+ if (!just_got_comma) {
+ // Require commas separate things in lists.
+ *err_ = Err(cur_token(), "Expected comma between items.");
+ return std::unique_ptr<ListNode>();
+ }
+ }
+ first_time = false;
+
+ // Why _OR? We're parsing things that are higher precedence than the ,
+ // that separates the items of the list. , should appear lower than
+ // boolean expressions (the lowest of which is OR), but above assignments.
+ list->append_item(ParseExpression(PRECEDENCE_OR));
+ if (has_error())
+ return std::unique_ptr<ListNode>();
+ if (at_end()) {
+ *err_ =
+ Err(tokens_[tokens_.size() - 1], "Unexpected end of file in list.");
+ return std::unique_ptr<ListNode>();
+ }
+ if (list->contents().back()->AsBlockComment()) {
+ // If there was a comment inside the list, we don't need a comma to the
+ // next item, so pretend we got one, if we're expecting one.
+ just_got_comma = allow_trailing_comma;
+ } else {
+ just_got_comma = Match(Token::COMMA);
+ }
+ }
+ if (just_got_comma && !allow_trailing_comma) {
+ *err_ = Err(cur_token(), "Trailing comma");
+ return std::unique_ptr<ListNode>();
+ }
+ list->set_end(std::make_unique<EndNode>(cur_token()));
+ return list;
+}
+
+std::unique_ptr<ParseNode> Parser::ParseFile() {
+ std::unique_ptr<BlockNode> file =
+ std::make_unique<BlockNode>(BlockNode::DISCARDS_RESULT);
+ for (;;) {
+ if (at_end())
+ break;
+ std::unique_ptr<ParseNode> statement = ParseStatement();
+ if (!statement)
+ break;
+ file->append_statement(std::move(statement));
+ }
+ if (!at_end() && !has_error())
+ *err_ = Err(cur_token(), "Unexpected here, should be newline.");
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+
+ // TODO(scottmg): If this is measurably expensive, it could be done only
+ // when necessary (when reformatting, or during tests). Comments are
+ // separate from the parse tree at this point, so downstream code can remain
+ // ignorant of them.
+ AssignComments(file.get());
+
+ return std::move(file);
+}
+
+std::unique_ptr<ParseNode> Parser::ParseStatement() {
+ if (LookAhead(Token::IF)) {
+ return ParseCondition();
+ } else if (LookAhead(Token::BLOCK_COMMENT)) {
+ return BlockComment(Consume());
+ } else {
+ // TODO(scottmg): Is this too strict? Just drop all the testing if we want
+ // to allow "pointless" expressions and return ParseExpression() directly.
+ std::unique_ptr<ParseNode> stmt = ParseExpression();
+ if (stmt) {
+ if (stmt->AsFunctionCall() || IsAssignment(stmt.get()))
+ return stmt;
+ }
+ if (!has_error()) {
+ const Token& token = cur_or_last_token();
+ *err_ = Err(token, "Expecting assignment or function call.");
+ }
+ return std::unique_ptr<ParseNode>();
+ }
+}
+
+std::unique_ptr<BlockNode> Parser::ParseBlock(
+ const Token& begin_brace,
+ BlockNode::ResultMode result_mode) {
+ if (has_error())
+ return std::unique_ptr<BlockNode>();
+ std::unique_ptr<BlockNode> block = std::make_unique<BlockNode>(result_mode);
+ block->set_begin_token(begin_brace);
+
+ for (;;) {
+ if (LookAhead(Token::RIGHT_BRACE)) {
+ block->set_end(std::make_unique<EndNode>(Consume()));
+ break;
+ }
+
+ std::unique_ptr<ParseNode> statement = ParseStatement();
+ if (!statement)
+ return std::unique_ptr<BlockNode>();
+ block->append_statement(std::move(statement));
+ }
+ return block;
+}
+
+std::unique_ptr<ParseNode> Parser::ParseCondition() {
+ std::unique_ptr<ConditionNode> condition = std::make_unique<ConditionNode>();
+ condition->set_if_token(Consume(Token::IF, "Expected 'if'"));
+ Consume(Token::LEFT_PAREN, "Expected '(' after 'if'.");
+ condition->set_condition(ParseExpression());
+ if (IsAssignment(condition->condition()))
+ *err_ = Err(condition->condition(), "Assignment not allowed in 'if'.");
+ Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'.");
+ condition->set_if_true(ParseBlock(
+ Consume(Token::LEFT_BRACE, "Expected '{' to start 'if' block."),
+ BlockNode::DISCARDS_RESULT));
+ if (Match(Token::ELSE)) {
+ if (LookAhead(Token::LEFT_BRACE)) {
+ condition->set_if_false(
+ ParseBlock(Consume(), BlockNode::DISCARDS_RESULT));
+ } else if (LookAhead(Token::IF)) {
+ condition->set_if_false(ParseStatement());
+ } else {
+ *err_ = Err(cur_or_last_token(), "Expected '{' or 'if' after 'else'.");
+ return std::unique_ptr<ParseNode>();
+ }
+ }
+ if (has_error())
+ return std::unique_ptr<ParseNode>();
+ return std::move(condition);
+}
+
+void Parser::TraverseOrder(const ParseNode* root,
+ std::vector<const ParseNode*>* pre,
+ std::vector<const ParseNode*>* post) {
+ if (root) {
+ pre->push_back(root);
+
+ if (const AccessorNode* accessor = root->AsAccessor()) {
+ TraverseOrder(accessor->subscript(), pre, post);
+ TraverseOrder(accessor->member(), pre, post);
+ } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
+ TraverseOrder(binop->left(), pre, post);
+ TraverseOrder(binop->right(), pre, post);
+ } else if (const BlockNode* block = root->AsBlock()) {
+ for (const auto& statement : block->statements())
+ TraverseOrder(statement.get(), pre, post);
+ TraverseOrder(block->End(), pre, post);
+ } else if (const ConditionNode* condition = root->AsCondition()) {
+ TraverseOrder(condition->condition(), pre, post);
+ TraverseOrder(condition->if_true(), pre, post);
+ TraverseOrder(condition->if_false(), pre, post);
+ } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
+ TraverseOrder(func_call->args(), pre, post);
+ TraverseOrder(func_call->block(), pre, post);
+ } else if (root->AsIdentifier()) {
+ // Nothing.
+ } else if (const ListNode* list = root->AsList()) {
+ for (const auto& node : list->contents())
+ TraverseOrder(node.get(), pre, post);
+ TraverseOrder(list->End(), pre, post);
+ } else if (root->AsLiteral()) {
+ // Nothing.
+ } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
+ TraverseOrder(unaryop->operand(), pre, post);
+ } else if (root->AsBlockComment()) {
+ // Nothing.
+ } else if (root->AsEnd()) {
+ // Nothing.
+ } else {
+ CHECK(false) << "Unhandled case in TraverseOrder.";
+ }
+
+ post->push_back(root);
+ }
+}
+
+void Parser::AssignComments(ParseNode* file) {
+ // Start by generating a pre- and post- order traversal of the tree so we
+ // can determine what's before and after comments.
+ std::vector<const ParseNode*> pre;
+ std::vector<const ParseNode*> post;
+ TraverseOrder(file, &pre, &post);
+
+ // Assign line comments to syntax immediately following.
+ int cur_comment = 0;
+ for (auto* node : pre) {
+ if (node->GetRange().is_null()) {
+ CHECK_EQ(node, file) << "Only expected on top file node";
+ continue;
+ }
+ const Location start = node->GetRange().begin();
+ while (cur_comment < static_cast<int>(line_comment_tokens_.size())) {
+ if (start.byte() >= line_comment_tokens_[cur_comment].location().byte()) {
+ const_cast<ParseNode*>(node)->comments_mutable()->append_before(
+ line_comment_tokens_[cur_comment]);
+ ++cur_comment;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Remaining line comments go at end of file.
+ for (; cur_comment < static_cast<int>(line_comment_tokens_.size());
+ ++cur_comment)
+ file->comments_mutable()->append_after(line_comment_tokens_[cur_comment]);
+
+ // Assign suffix to syntax immediately before.
+ cur_comment = static_cast<int>(suffix_comment_tokens_.size() - 1);
+ for (std::vector<const ParseNode*>::const_reverse_iterator i = post.rbegin();
+ i != post.rend(); ++i) {
+ // Don't assign suffix comments to the function, list, or block, but instead
+ // to the last thing inside.
+ if ((*i)->AsFunctionCall() || (*i)->AsList() || (*i)->AsBlock())
+ continue;
+
+ Location start = (*i)->GetRange().begin();
+ Location end = (*i)->GetRange().end();
+
+ // Don't assign suffix comments to something that starts on an earlier
+ // line, so that in:
+ //
+ // sources = [ "a",
+ // "b" ] # comment
+ //
+ // it's attached to "b", not sources = [ ... ].
+ if (start.line_number() != end.line_number())
+ continue;
+
+ while (cur_comment >= 0) {
+ if (end.byte() <= suffix_comment_tokens_[cur_comment].location().byte()) {
+ const_cast<ParseNode*>(*i)->comments_mutable()->append_suffix(
+ suffix_comment_tokens_[cur_comment]);
+ --cur_comment;
+ } else {
+ break;
+ }
+ }
+
+ // Suffix comments were assigned in reverse, so if there were multiple on
+ // the same node, they need to be reversed.
+ if ((*i)->comments() && !(*i)->comments()->suffix().empty())
+ const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix();
+ }
+}
+
+std::string IndentFor(int value) {
+ return std::string(value, ' ');
+}
+
+void RenderToText(const base::Value& node,
+ int indent_level,
+ std::ostringstream& os) {
+ const base::Value* child = node.FindKey(std::string("child"));
+ std::string node_type(node.FindKey("type")->GetString());
+ if (node_type == "ACCESSOR") {
+ // AccessorNode is a bit special, in that it holds a Token, not a ParseNode
+ // for the base.
+ os << IndentFor(indent_level) << node_type << std::endl;
+ os << IndentFor(indent_level + 1) << node.FindKey("value")->GetString()
+ << std::endl;
+ } else {
+ os << IndentFor(indent_level) << node_type;
+ if (node.FindKey("value")) {
+ os << "(" << node.FindKey("value")->GetString() << ")";
+ }
+ os << std::endl;
+ }
+ if (node.FindKey(kJsonBeforeComment)) {
+ for (auto& v : node.FindKey(kJsonBeforeComment)->GetList()) {
+ os << IndentFor(indent_level + 1) << "+BEFORE_COMMENT(\"" << v.GetString()
+ << "\")\n";
+ }
+ }
+ if (node.FindKey(kJsonSuffixComment)) {
+ for (auto& v : node.FindKey(kJsonSuffixComment)->GetList()) {
+ os << IndentFor(indent_level + 1) << "+SUFFIX_COMMENT(\"" << v.GetString()
+ << "\")\n";
+ }
+ }
+ if (node.FindKey(kJsonAfterComment)) {
+ for (auto& v : node.FindKey(kJsonAfterComment)->GetList()) {
+ os << IndentFor(indent_level + 1) << "+AFTER_COMMENT(\"" << v.GetString()
+ << "\")\n";
+ }
+ }
+ if (child) {
+ for (const base::Value& n : child->GetList()) {
+ RenderToText(n, indent_level + 1, os);
+ }
+ }
+ const base::Value* end = node.FindKey("end");
+ if (end &&
+ (end->FindKey(kJsonBeforeComment) || end->FindKey(kJsonSuffixComment) ||
+ end->FindKey(kJsonAfterComment))) {
+ RenderToText(*end, indent_level + 1, os);
+ }
+}
diff --git a/gn/src/gn/parser.h b/gn/src/gn/parser.h
new file mode 100644
index 00000000000..1f1cf1abdfa
--- /dev/null
+++ b/gn/src/gn/parser.h
@@ -0,0 +1,158 @@
+// 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 TOOLS_GN_PARSER_H_
+#define TOOLS_GN_PARSER_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "gn/err.h"
+#include "gn/parse_tree.h"
+
+extern const char kGrammar_Help[];
+
+struct ParserHelper;
+
+// Parses a series of tokens. The resulting AST will refer to the tokens passed
+// to the input, so the tokens an the file data they refer to must outlive your
+// use of the ParseNode.
+class Parser {
+ public:
+ // Will return a null pointer and set the err on error.
+ static std::unique_ptr<ParseNode> Parse(const std::vector<Token>& tokens,
+ Err* err);
+
+ // Alternative to parsing that assumes the input is an expression.
+ static std::unique_ptr<ParseNode> ParseExpression(
+ const std::vector<Token>& tokens,
+ Err* err);
+
+ // Alternative to parsing that assumes the input is a literal value.
+ static std::unique_ptr<ParseNode> ParseValue(const std::vector<Token>& tokens,
+ Err* err);
+
+ private:
+ // Vector must be valid for lifetime of call.
+ Parser(const std::vector<Token>& tokens, Err* err);
+ ~Parser();
+
+ std::unique_ptr<ParseNode> ParseExpression();
+
+ // Parses an expression with the given precedence or higher.
+ std::unique_ptr<ParseNode> ParseExpression(int precedence);
+
+ // |PrefixFunc|s used in parsing expressions.
+ std::unique_ptr<ParseNode> Block(const Token& token);
+ std::unique_ptr<ParseNode> Literal(const Token& token);
+ std::unique_ptr<ParseNode> Name(const Token& token);
+ std::unique_ptr<ParseNode> Group(const Token& token);
+ std::unique_ptr<ParseNode> Not(const Token& token);
+ std::unique_ptr<ParseNode> List(const Token& token);
+ std::unique_ptr<ParseNode> BlockComment(const Token& token);
+
+ // |InfixFunc|s used in parsing expressions.
+ std::unique_ptr<ParseNode> BinaryOperator(std::unique_ptr<ParseNode> left,
+ const Token& token);
+ std::unique_ptr<ParseNode> IdentifierOrCall(std::unique_ptr<ParseNode> left,
+ const Token& token);
+ std::unique_ptr<ParseNode> Assignment(std::unique_ptr<ParseNode> left,
+ const Token& token);
+ std::unique_ptr<ParseNode> Subscript(std::unique_ptr<ParseNode> left,
+ const Token& token);
+ std::unique_ptr<ParseNode> DotOperator(std::unique_ptr<ParseNode> left,
+ const Token& token);
+
+ // Helper to parse a comma separated list, optionally allowing trailing
+ // commas (allowed in [] lists, not in function calls).
+ std::unique_ptr<ListNode> ParseList(const Token& start_token,
+ Token::Type stop_before,
+ bool allow_trailing_comma);
+
+ std::unique_ptr<ParseNode> ParseFile();
+ std::unique_ptr<ParseNode> ParseStatement();
+ // Expects to be passed the token corresponding to the '{' and that the
+ // current token is the one following the '{'.
+ std::unique_ptr<BlockNode> ParseBlock(const Token& begin_brace,
+ BlockNode::ResultMode result_mode);
+ std::unique_ptr<ParseNode> ParseCondition();
+
+ // Generates a pre- and post-order traversal of the tree.
+ void TraverseOrder(const ParseNode* root,
+ std::vector<const ParseNode*>* pre,
+ std::vector<const ParseNode*>* post);
+
+ // Attach comments to nearby syntax.
+ void AssignComments(ParseNode* file);
+
+ bool IsAssignment(const ParseNode* node) const;
+ bool IsStatementBreak(Token::Type token_type) const;
+
+ bool LookAhead(Token::Type type);
+ bool Match(Token::Type type);
+ const Token& Consume(Token::Type type, const char* error_message);
+ const Token& Consume(Token::Type* types,
+ size_t num_types,
+ const char* error_message);
+ const Token& Consume();
+
+ // Call this only if !at_end().
+ const Token& cur_token() const { return tokens_[cur_]; }
+
+ const Token& cur_or_last_token() const {
+ return at_end() ? tokens_[tokens_.size() - 1] : cur_token();
+ }
+
+ bool done() const { return at_end() || has_error(); }
+ bool at_end() const { return cur_ >= tokens_.size(); }
+ bool has_error() const { return err_->has_error(); }
+
+ std::vector<Token> tokens_;
+ std::vector<Token> line_comment_tokens_;
+ std::vector<Token> suffix_comment_tokens_;
+
+ static ParserHelper expressions_[Token::NUM_TYPES];
+
+ Token invalid_token_;
+ Err* err_;
+
+ // Current index into the tokens.
+ size_t cur_;
+
+ FRIEND_TEST_ALL_PREFIXES(Parser, BinaryOp);
+ FRIEND_TEST_ALL_PREFIXES(Parser, Block);
+ FRIEND_TEST_ALL_PREFIXES(Parser, Condition);
+ FRIEND_TEST_ALL_PREFIXES(Parser, Expression);
+ FRIEND_TEST_ALL_PREFIXES(Parser, FunctionCall);
+ FRIEND_TEST_ALL_PREFIXES(Parser, List);
+ FRIEND_TEST_ALL_PREFIXES(Parser, ParenExpression);
+ FRIEND_TEST_ALL_PREFIXES(Parser, UnaryOp);
+
+ DISALLOW_COPY_AND_ASSIGN(Parser);
+};
+
+using PrefixFunc = std::unique_ptr<ParseNode> (Parser::*)(const Token& token);
+using InfixFunc = std::unique_ptr<ParseNode> (
+ Parser::*)(std::unique_ptr<ParseNode> left, const Token& token);
+
+struct ParserHelper {
+ PrefixFunc prefix;
+ InfixFunc infix;
+
+ // Used only for infix operators.
+ int precedence;
+};
+
+// Renders parse subtree as a formatted text, indenting by the given number of
+// spaces.
+void RenderToText(const base::Value& node,
+ int indent_level,
+ std::ostringstream& os);
+
+#endif // TOOLS_GN_PARSER_H_
diff --git a/gn/src/gn/parser_unittest.cc b/gn/src/gn/parser_unittest.cc
new file mode 100644
index 00000000000..8cfdb8dea78
--- /dev/null
+++ b/gn/src/gn/parser_unittest.cc
@@ -0,0 +1,734 @@
+// 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 <sstream>
+
+#include "gn/input_file.h"
+#include "gn/parser.h"
+#include "gn/tokenizer.h"
+#include "util/test/test.h"
+
+namespace {
+
+bool GetTokens(const InputFile* input, std::vector<Token>* result) {
+ result->clear();
+ Err err;
+ *result = Tokenizer::Tokenize(input, &err);
+ return !err.has_error();
+}
+
+void DoParserPrintTest(const char* input, const char* expected) {
+ std::vector<Token> tokens;
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+ ASSERT_TRUE(GetTokens(&input_file, &tokens));
+
+ Err err;
+ std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+ if (!result)
+ err.PrintToStdout();
+ ASSERT_TRUE(result);
+
+ std::ostringstream collector;
+ RenderToText(result->GetJSONNode(), 0, collector);
+
+ EXPECT_EQ(expected, collector.str());
+}
+
+void DoExpressionPrintTest(const char* input, const char* expected) {
+ std::vector<Token> tokens;
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+ ASSERT_TRUE(GetTokens(&input_file, &tokens));
+
+ Err err;
+ std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
+ ASSERT_TRUE(result);
+
+ std::ostringstream collector;
+ RenderToText(result->GetJSONNode(), 0, collector);
+
+ EXPECT_EQ(expected, collector.str());
+}
+
+// Expects the tokenizer or parser to identify an error at the given line and
+// character.
+void DoParserErrorTest(const char* input, int err_line, int err_char) {
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+
+ Err err;
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
+ if (!err.has_error()) {
+ std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+ ASSERT_FALSE(result);
+ ASSERT_TRUE(err.has_error());
+ }
+
+ EXPECT_EQ(err_line, err.location().line_number());
+ EXPECT_EQ(err_char, err.location().column_number());
+}
+
+// Expects the tokenizer or parser to identify an error at the given line and
+// character.
+void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+
+ Err err;
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
+ if (!err.has_error()) {
+ std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
+ ASSERT_FALSE(result);
+ ASSERT_TRUE(err.has_error());
+ }
+
+ EXPECT_EQ(err_line, err.location().line_number());
+ EXPECT_EQ(err_char, err.location().column_number());
+}
+
+} // namespace
+
+TEST(Parser, Literal) {
+ DoExpressionPrintTest("5", "LITERAL(5)\n");
+ DoExpressionPrintTest("\"stuff\"", "LITERAL(\"stuff\")\n");
+}
+
+TEST(Parser, BinaryOp) {
+ // TODO(scottmg): The tokenizer is dumb, and treats "5-1" as two integers,
+ // not a binary operator between two positive integers.
+ DoExpressionPrintTest("5 - 1",
+ "BINARY(-)\n"
+ " LITERAL(5)\n"
+ " LITERAL(1)\n");
+ DoExpressionPrintTest("5+1",
+ "BINARY(+)\n"
+ " LITERAL(5)\n"
+ " LITERAL(1)\n");
+ DoExpressionPrintTest("5 - 1 - 2",
+ "BINARY(-)\n"
+ " BINARY(-)\n"
+ " LITERAL(5)\n"
+ " LITERAL(1)\n"
+ " LITERAL(2)\n");
+}
+
+TEST(Parser, FunctionCall) {
+ DoExpressionPrintTest("foo()",
+ "FUNCTION(foo)\n"
+ " LIST\n");
+ DoExpressionPrintTest("blah(1, 2)",
+ "FUNCTION(blah)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " LITERAL(2)\n");
+ DoExpressionErrorTest("foo(1, 2,)", 1, 10);
+ DoExpressionErrorTest("foo(1 2)", 1, 7);
+}
+
+TEST(Parser, ParenExpression) {
+ const char* input = "(foo(1)) + (a + (b - c) + d)";
+ const char* expected =
+ "BINARY(+)\n"
+ " FUNCTION(foo)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " BINARY(+)\n"
+ " BINARY(+)\n"
+ " IDENTIFIER(a)\n"
+ " BINARY(-)\n"
+ " IDENTIFIER(b)\n"
+ " IDENTIFIER(c)\n"
+ " IDENTIFIER(d)\n";
+ DoExpressionPrintTest(input, expected);
+ DoExpressionErrorTest("(a +", 1, 4);
+}
+
+TEST(Parser, OrderOfOperationsLeftAssociative) {
+ const char* input = "5 - 1 - 2\n";
+ const char* expected =
+ "BINARY(-)\n"
+ " BINARY(-)\n"
+ " LITERAL(5)\n"
+ " LITERAL(1)\n"
+ " LITERAL(2)\n";
+ DoExpressionPrintTest(input, expected);
+}
+
+TEST(Parser, OrderOfOperationsEqualityBoolean) {
+ const char* input =
+ "if (a == \"b\" && is_stuff) {\n"
+ " print(\"hai\")\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " CONDITION\n"
+ " BINARY(&&)\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(\"b\")\n"
+ " IDENTIFIER(is_stuff)\n"
+ " BLOCK\n"
+ " FUNCTION(print)\n"
+ " LIST\n"
+ " LITERAL(\"hai\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, UnaryOp) {
+ DoExpressionPrintTest("!foo",
+ "UNARY(!)\n"
+ " IDENTIFIER(foo)\n");
+
+ // No contents for binary operator.
+ DoExpressionErrorTest("a = !", 1, 5);
+}
+
+TEST(Parser, List) {
+ DoExpressionPrintTest("[]", "LIST\n");
+ DoExpressionPrintTest("[1,asd,]",
+ "LIST\n"
+ " LITERAL(1)\n"
+ " IDENTIFIER(asd)\n");
+ DoExpressionPrintTest("[1, 2+3 - foo]",
+ "LIST\n"
+ " LITERAL(1)\n"
+ " BINARY(-)\n"
+ " BINARY(+)\n"
+ " LITERAL(2)\n"
+ " LITERAL(3)\n"
+ " IDENTIFIER(foo)\n");
+ DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
+ "LIST\n"
+ " LITERAL(1)\n"
+ " LITERAL(2)\n"
+ " LITERAL(3)\n"
+ " LITERAL(4)\n");
+
+ DoExpressionErrorTest("[a, 2+,]", 1, 7);
+ DoExpressionErrorTest("[,]", 1, 2);
+ DoExpressionErrorTest("[a,,]", 1, 4);
+}
+
+TEST(Parser, Assignment) {
+ DoParserPrintTest("a=2",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(2)\n");
+
+ DoExpressionErrorTest("a = ", 1, 3);
+}
+
+TEST(Parser, Accessor) {
+ // Accessor indexing.
+ DoParserPrintTest("a=b[c+2]",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " ACCESSOR\n"
+ " b\n" // AccessorNode is a bit weird in that it holds
+ // a Token, not a ParseNode for the base.
+ " BINARY(+)\n"
+ " IDENTIFIER(c)\n"
+ " LITERAL(2)\n");
+ DoParserErrorTest("a = b[1][0]", 1, 5);
+
+ // Member accessors.
+ DoParserPrintTest("a=b.c+2",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " BINARY(+)\n"
+ " ACCESSOR\n"
+ " b\n"
+ " IDENTIFIER(c)\n"
+ " LITERAL(2)\n");
+ DoParserPrintTest("a.b = 5",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " ACCESSOR\n"
+ " a\n"
+ " IDENTIFIER(b)\n"
+ " LITERAL(5)\n");
+ DoParserErrorTest("a = b.c.d", 1, 6); // Can't nest accessors (currently).
+
+ // Error at the bad dot in the RHS, not the + operator (crbug.com/472038).
+ DoParserErrorTest("foo(a + b.c.d)", 1, 10);
+}
+
+TEST(Parser, Condition) {
+ DoParserPrintTest("if(1) { a = 2 }",
+ "BLOCK\n"
+ " CONDITION\n"
+ " LITERAL(1)\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(2)\n");
+
+ DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
+ "BLOCK\n"
+ " CONDITION\n"
+ " LITERAL(1)\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(2)\n"
+ " CONDITION\n"
+ " LITERAL(0)\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(3)\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(4)\n");
+}
+
+TEST(Parser, OnlyCallAndAssignInBody) {
+ DoParserErrorTest("[]", 1, 2);
+ DoParserErrorTest("3 + 4", 1, 5);
+ DoParserErrorTest("6 - 7", 1, 5);
+ DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
+}
+
+TEST(Parser, NoAssignmentInCondition) {
+ DoParserErrorTest("if (a=2) {}", 1, 5);
+}
+
+TEST(Parser, CompleteFunction) {
+ const char* input =
+ "cc_test(\"foo\") {\n"
+ " sources = [\n"
+ " \"foo.cc\",\n"
+ " \"foo.h\"\n"
+ " ]\n"
+ " dependencies = [\n"
+ " \"base\"\n"
+ " ]\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"foo.cc\")\n"
+ " LITERAL(\"foo.h\")\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(dependencies)\n"
+ " LIST\n"
+ " LITERAL(\"base\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, FunctionWithConditional) {
+ const char* input =
+ "cc_test(\"foo\") {\n"
+ " sources = [\"foo.cc\"]\n"
+ " if (OS == \"mac\") {\n"
+ " sources += \"bar.cc\"\n"
+ " } else if (OS == \"win\") {\n"
+ " sources -= [\"asd.cc\", \"foo.cc\"]\n"
+ " } else {\n"
+ " dependencies += [\"bar.cc\"]\n"
+ " }\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"foo.cc\")\n"
+ " CONDITION\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(OS)\n"
+ " LITERAL(\"mac\")\n"
+ " BLOCK\n"
+ " BINARY(+=)\n"
+ " IDENTIFIER(sources)\n"
+ " LITERAL(\"bar.cc\")\n"
+ " CONDITION\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(OS)\n"
+ " LITERAL(\"win\")\n"
+ " BLOCK\n"
+ " BINARY(-=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"asd.cc\")\n"
+ " LITERAL(\"foo.cc\")\n"
+ " BLOCK\n"
+ " BINARY(+=)\n"
+ " IDENTIFIER(dependencies)\n"
+ " LIST\n"
+ " LITERAL(\"bar.cc\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, UnterminatedBlock) {
+ DoParserErrorTest("stuff() {", 1, 9);
+}
+
+TEST(Parser, BadlyTerminatedNumber) {
+ DoParserErrorTest("1234z", 1, 5);
+}
+
+TEST(Parser, NewlinesInUnusualPlaces) {
+ DoParserPrintTest(
+ "if\n"
+ "(\n"
+ "a\n"
+ ")\n"
+ "{\n"
+ "}\n",
+ "BLOCK\n"
+ " CONDITION\n"
+ " IDENTIFIER(a)\n"
+ " BLOCK\n");
+}
+
+TEST(Parser, NewlinesInUnusualPlaces2) {
+ DoParserPrintTest("a\n=\n2\n",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(2)\n");
+ DoParserPrintTest("x =\ny if\n(1\n) {}",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(x)\n"
+ " IDENTIFIER(y)\n"
+ " CONDITION\n"
+ " LITERAL(1)\n"
+ " BLOCK\n");
+ DoParserPrintTest("x = 3\n+2",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(x)\n"
+ " BINARY(+)\n"
+ " LITERAL(3)\n"
+ " LITERAL(2)\n");
+}
+
+TEST(Parser, NewlineBeforeSubscript) {
+ const char* input = "a = b[1]";
+ const char* input_with_newline = "a = b\n[1]";
+ const char* expected =
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " ACCESSOR\n"
+ " b\n"
+ " LITERAL(1)\n";
+ DoParserPrintTest(input, expected);
+ DoParserPrintTest(input_with_newline, expected);
+}
+
+TEST(Parser, SequenceOfExpressions) {
+ DoParserPrintTest("a = 1 b = 2",
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(1)\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(b)\n"
+ " LITERAL(2)\n");
+}
+
+TEST(Parser, BlockAfterFunction) {
+ const char* input = "func(\"stuff\") {\n}";
+ // TODO(scottmg): Do we really want these to mean different things?
+ const char* input_with_newline = "func(\"stuff\")\n{\n}";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(func)\n"
+ " LIST\n"
+ " LITERAL(\"stuff\")\n"
+ " BLOCK\n";
+ DoParserPrintTest(input, expected);
+ DoParserPrintTest(input_with_newline, expected);
+}
+
+TEST(Parser, LongExpression) {
+ const char* input = "a = b + c && d || e";
+ const char* expected =
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " BINARY(||)\n"
+ " BINARY(&&)\n"
+ " BINARY(+)\n"
+ " IDENTIFIER(b)\n"
+ " IDENTIFIER(c)\n"
+ " IDENTIFIER(d)\n"
+ " IDENTIFIER(e)\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsStandalone) {
+ const char* input =
+ "# Toplevel comment.\n"
+ "\n"
+ "executable(\"wee\") {}\n";
+ const char* expected =
+ "BLOCK\n"
+ " BLOCK_COMMENT(# Toplevel comment.)\n"
+ " FUNCTION(executable)\n"
+ " LIST\n"
+ " LITERAL(\"wee\")\n"
+ " BLOCK\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsStandaloneEof) {
+ const char* input =
+ "executable(\"wee\") {}\n"
+ "# EOF comment.\n";
+ const char* expected =
+ "BLOCK\n"
+ " +AFTER_COMMENT(\"# EOF comment.\")\n"
+ " FUNCTION(executable)\n"
+ " LIST\n"
+ " LITERAL(\"wee\")\n"
+ " BLOCK\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsLineAttached) {
+ const char* input =
+ "executable(\"wee\") {\n"
+ " # Some sources.\n"
+ " sources = [\n"
+ " \"stuff.cc\",\n"
+ " \"things.cc\",\n"
+ " # This file is special or something.\n"
+ " \"another.cc\",\n"
+ " ]\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(executable)\n"
+ " LIST\n"
+ " LITERAL(\"wee\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " +BEFORE_COMMENT(\"# Some sources.\")\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"stuff.cc\")\n"
+ " LITERAL(\"things.cc\")\n"
+ " LITERAL(\"another.cc\")\n"
+ " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsSuffix) {
+ const char* input =
+ "executable(\"wee\") { # This is some stuff.\n"
+ "sources = [ \"a.cc\" # And another comment here.\n"
+ "] }";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(executable)\n"
+ " LIST\n"
+ " LITERAL(\"wee\")\n"
+ " END())\n"
+ " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"a.cc\")\n"
+ " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsSuffixDifferentLine) {
+ const char* input =
+ "executable(\"wee\") {\n"
+ " sources = [ \"a\",\n"
+ " \"b\" ] # Comment\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(executable)\n"
+ " LIST\n"
+ " LITERAL(\"wee\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"a\")\n"
+ " LITERAL(\"b\")\n"
+ " END(])\n"
+ " +SUFFIX_COMMENT(\"# Comment\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsSuffixMultiple) {
+ const char* input =
+ "executable(\"wee\") {\n"
+ " sources = [\n"
+ " \"a\", # This is a comment,\n"
+ " # and some more,\n" // Note that this is aligned with above.
+ " # then the end.\n"
+ " ]\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(executable)\n"
+ " LIST\n"
+ " LITERAL(\"wee\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"a\")\n"
+ " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
+ " +SUFFIX_COMMENT(\"# and some more,\")\n"
+ " +SUFFIX_COMMENT(\"# then the end.\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsConnectedInList) {
+ const char* input =
+ "defines = [\n"
+ "\n"
+ " # Connected comment.\n"
+ " \"WEE\",\n"
+ " \"BLORPY\",\n"
+ "]\n";
+ const char* expected =
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(defines)\n"
+ " LIST\n"
+ " LITERAL(\"WEE\")\n"
+ " +BEFORE_COMMENT(\"# Connected comment.\")\n"
+ " LITERAL(\"BLORPY\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, CommentsAtEndOfBlock) {
+ const char* input =
+ "if (is_win) {\n"
+ " sources = [\"a.cc\"]\n"
+ " # Some comment at end.\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " CONDITION\n"
+ " IDENTIFIER(is_win)\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"a.cc\")\n"
+ " END(})\n"
+ " +BEFORE_COMMENT(\"# Some comment at end.\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+// TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
+// which thing this comment is intended to be attached to.
+TEST(Parser, CommentsEndOfBlockSingleLine) {
+ const char* input =
+ "defines = [ # EOL defines.\n"
+ "]\n";
+ const char* expected =
+ "BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(defines)\n"
+ " +SUFFIX_COMMENT(\"# EOL defines.\")\n"
+ " LIST\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, HangingIf) {
+ DoParserErrorTest("if", 1, 1);
+}
+
+TEST(Parser, NegatingList) {
+ DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);
+}
+
+TEST(Parser, ConditionNoBracesIf) {
+ DoParserErrorTest(
+ "if (true)\n"
+ " foreach(foo, []) {}\n"
+ "else {\n"
+ " foreach(bar, []) {}\n"
+ "}\n",
+ 2, 3);
+}
+
+TEST(Parser, ConditionNoBracesElse) {
+ DoParserErrorTest(
+ "if (true) {\n"
+ " foreach(foo, []) {}\n"
+ "} else\n"
+ " foreach(bar, []) {}\n",
+ 4, 3);
+}
+
+TEST(Parser, ConditionNoBracesElseIf) {
+ DoParserErrorTest(
+ "if (true) {\n"
+ " foreach(foo, []) {}\n"
+ "} else if (true)\n"
+ " foreach(bar, []) {}\n",
+ 4, 3);
+}
+
+// Disallow standalone {} for introducing new scopes. These are ambiguous with
+// target declarations (e.g. is:
+// foo("bar") {}
+// a function with an associated block, or a standalone function with a
+// freestanding block.
+TEST(Parser, StandaloneBlock) {
+ // The error is reported at the end of the block when nothing is done
+ // with it. If we had said "a = { ..." then it would have been OK.
+ DoParserErrorTest(
+ "if (true) {\n"
+ "}\n"
+ "{\n"
+ " assert(false)\n"
+ "}\n",
+ 5, 1);
+}
+
+TEST(Parser, BlockValues) {
+ const char* input =
+ "print({a = 1 b = 2}, 3)\n"
+ "a = { b = \"asd\" }";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(print)\n"
+ " LIST\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(1)\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(b)\n"
+ " LITERAL(2)\n"
+ " LITERAL(3)\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(b)\n"
+ " LITERAL(\"asd\")\n";
+ DoParserPrintTest(input, expected);
+}
diff --git a/gn/src/gn/path_output.cc b/gn/src/gn/path_output.cc
new file mode 100644
index 00000000000..c89e061c3d4
--- /dev/null
+++ b/gn/src/gn/path_output.cc
@@ -0,0 +1,176 @@
+// 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 "gn/path_output.h"
+
+#include "base/strings/string_util.h"
+#include "gn/filesystem_utils.h"
+#include "gn/output_file.h"
+#include "gn/string_utils.h"
+#include "util/build_config.h"
+
+PathOutput::PathOutput(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ EscapingMode escaping)
+ : current_dir_(current_dir) {
+ inverse_current_dir_ = RebasePath("//", current_dir, source_root);
+ if (!EndsWithSlash(inverse_current_dir_))
+ inverse_current_dir_.push_back('/');
+ options_.mode = escaping;
+}
+
+PathOutput::~PathOutput() = default;
+
+void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const {
+ WritePathStr(out, file.value());
+}
+
+void PathOutput::WriteDir(std::ostream& out,
+ const SourceDir& dir,
+ DirSlashEnding slash_ending) const {
+ if (dir.value() == "/") {
+ // Writing system root is always a slash (this will normally only come up
+ // on Posix systems).
+ if (slash_ending == DIR_NO_LAST_SLASH)
+ out << "/.";
+ else
+ out << "/";
+ } else if (dir.value() == "//") {
+ // Writing out the source root.
+ if (slash_ending == DIR_NO_LAST_SLASH) {
+ // The inverse_current_dir_ will contain a [back]slash at the end, so we
+ // can't just write it out.
+ if (inverse_current_dir_.empty()) {
+ out << ".";
+ } else {
+ out.write(inverse_current_dir_.c_str(),
+ inverse_current_dir_.size() - 1);
+ }
+ } else {
+ if (inverse_current_dir_.empty())
+ out << "./";
+ else
+ out << inverse_current_dir_;
+ }
+ } else if (dir == current_dir_) {
+ // Writing the same directory. This needs special handling here since
+ // we need to output something else other than the input.
+ if (slash_ending == DIR_INCLUDE_LAST_SLASH)
+ out << "./";
+ else
+ out << ".";
+ } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) {
+ WritePathStr(out, dir.value());
+ } else {
+ // DIR_NO_LAST_SLASH mode, just trim the last char.
+ WritePathStr(out,
+ std::string_view(dir.value().data(), dir.value().size() - 1));
+ }
+}
+
+void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const {
+ // Here we assume that the path is already preprocessed.
+ EscapeStringToStream(out, file.value(), options_);
+}
+
+void PathOutput::WriteFiles(std::ostream& out,
+ const std::vector<SourceFile>& files) const {
+ for (const auto& file : files) {
+ out << " ";
+ WriteFile(out, file);
+ }
+}
+
+void PathOutput::WriteFiles(std::ostream& out,
+ const std::vector<OutputFile>& files) const {
+ for (const auto& file : files) {
+ out << " ";
+ WriteFile(out, file);
+ }
+}
+
+void PathOutput::WriteFiles(std::ostream& out,
+ const UniqueVector<OutputFile>& files) const {
+ for (const auto& file : files) {
+ out << " ";
+ WriteFile(out, file);
+ }
+}
+
+void PathOutput::WriteDir(std::ostream& out,
+ const OutputFile& file,
+ DirSlashEnding slash_ending) const {
+ DCHECK(file.value().empty() || file.value()[file.value().size() - 1] == '/');
+
+ switch (slash_ending) {
+ case DIR_INCLUDE_LAST_SLASH:
+ EscapeStringToStream(out, file.value(), options_);
+ break;
+ case DIR_NO_LAST_SLASH:
+ if (!file.value().empty() &&
+ file.value()[file.value().size() - 1] == '/') {
+ // Trim trailing slash.
+ EscapeStringToStream(
+ out, std::string_view(file.value().data(), file.value().size() - 1),
+ options_);
+ } else {
+ // Doesn't end with a slash, write the whole thing.
+ EscapeStringToStream(out, file.value(), options_);
+ }
+ break;
+ }
+}
+
+void PathOutput::WriteFile(std::ostream& out,
+ const base::FilePath& file) const {
+ // Assume native file paths are always absolute.
+ EscapeStringToStream(out, FilePathToUTF8(file), options_);
+}
+
+void PathOutput::WriteSourceRelativeString(std::ostream& out,
+ const std::string_view& str) const {
+ if (options_.mode == ESCAPE_NINJA_COMMAND) {
+ // Shell escaping needs an intermediate string since it may end up
+ // quoting the whole thing.
+ std::string intermediate;
+ intermediate.reserve(inverse_current_dir_.size() + str.size());
+ intermediate.assign(inverse_current_dir_.c_str(),
+ inverse_current_dir_.size());
+ intermediate.append(str.data(), str.size());
+
+ EscapeStringToStream(
+ out, std::string_view(intermediate.c_str(), intermediate.size()),
+ options_);
+ } else {
+ // Ninja (and none) escaping can avoid the intermediate string and
+ // reprocessing of the inverse_current_dir_.
+ out << inverse_current_dir_;
+ EscapeStringToStream(out, str, options_);
+ }
+}
+
+void PathOutput::WritePathStr(std::ostream& out,
+ const std::string_view& str) const {
+ DCHECK(str.size() > 0 && str[0] == '/');
+
+ if (str.substr(0, current_dir_.value().size()) ==
+ std::string_view(current_dir_.value())) {
+ // The current dir is a prefix of the output file, so we can strip the
+ // prefix and write out the result.
+ EscapeStringToStream(out, str.substr(current_dir_.value().size()),
+ options_);
+ } else if (str.size() >= 2 && str[1] == '/') {
+ WriteSourceRelativeString(out, str.substr(2));
+ } else {
+// Input begins with one slash, don't write the current directory since
+// it's system-absolute.
+#if defined(OS_WIN)
+ // On Windows, trim the leading slash, since the input for absolute
+ // paths will look like "/C:/foo/bar.txt".
+ EscapeStringToStream(out, str.substr(1), options_);
+#else
+ EscapeStringToStream(out, str, options_);
+#endif
+ }
+}
diff --git a/gn/src/gn/path_output.h b/gn/src/gn/path_output.h
new file mode 100644
index 00000000000..729ba9864fa
--- /dev/null
+++ b/gn/src/gn/path_output.h
@@ -0,0 +1,93 @@
+// 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 TOOLS_GN_PATH_OUTPUT_H_
+#define TOOLS_GN_PATH_OUTPUT_H_
+
+#include <iosfwd>
+#include <string>
+#include <string_view>
+
+#include "base/macros.h"
+#include "gn/escape.h"
+#include "gn/source_dir.h"
+#include "gn/unique_vector.h"
+
+class OutputFile;
+class SourceFile;
+
+namespace base {
+class FilePath;
+}
+
+// Writes file names to streams assuming a certain input directory and
+// escaping rules. This gives us a central place for managing this state.
+class PathOutput {
+ public:
+ // Controls whether writing directory names include the trailing slash.
+ // Often we don't want the trailing slash when writing out to a command line,
+ // especially on Windows where it's a backslash and might be interpreted as
+ // escaping the thing following it.
+ enum DirSlashEnding {
+ DIR_INCLUDE_LAST_SLASH,
+ DIR_NO_LAST_SLASH,
+ };
+
+ PathOutput(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ EscapingMode escaping);
+ ~PathOutput();
+
+ // Read-only since inverse_current_dir_ is computed depending on this.
+ EscapingMode escaping_mode() const { return options_.mode; }
+
+ const SourceDir& current_dir() const { return current_dir_; }
+
+ // Getter/setters for flags inside the escape options.
+ bool inhibit_quoting() const { return options_.inhibit_quoting; }
+ void set_inhibit_quoting(bool iq) { options_.inhibit_quoting = iq; }
+ void set_escape_platform(EscapingPlatform p) { options_.platform = p; }
+
+ void WriteFile(std::ostream& out, const SourceFile& file) const;
+ void WriteFile(std::ostream& out, const OutputFile& file) const;
+ void WriteFile(std::ostream& out, const base::FilePath& file) const;
+
+ // Writes the given SourceFiles/OutputFiles with spaces separating them. This
+ // will also write an initial space before the first item.
+ void WriteFiles(std::ostream& out, const std::vector<SourceFile>& file) const;
+ void WriteFiles(std::ostream& out,
+ const std::vector<OutputFile>& files) const;
+ void WriteFiles(std::ostream& out,
+ const UniqueVector<OutputFile>& files) const;
+
+ // This variant assumes the dir ends in a trailing slash or is empty.
+ void WriteDir(std::ostream& out,
+ const SourceDir& dir,
+ DirSlashEnding slash_ending) const;
+
+ void WriteDir(std::ostream& out,
+ const OutputFile& file,
+ DirSlashEnding slash_ending) const;
+
+ // Backend for WriteFile and WriteDir. This appends the given file or
+ // directory string to the file.
+ void WritePathStr(std::ostream& out, const std::string_view& str) const;
+
+ private:
+ // Takes the given string and writes it out, appending to the inverse
+ // current dir. This assumes leading slashes have been trimmed.
+ void WriteSourceRelativeString(std::ostream& out,
+ const std::string_view& str) const;
+
+ SourceDir current_dir_;
+
+ // Uses system slashes if convert_slashes_to_system_.
+ std::string inverse_current_dir_;
+
+ // Since the inverse_current_dir_ depends on some of these, we don't expose
+ // this directly to modification.
+ EscapeOptions options_;
+};
+
+#endif // TOOLS_GN_PATH_OUTPUT_H_
diff --git a/gn/src/gn/path_output_unittest.cc b/gn/src/gn/path_output_unittest.cc
new file mode 100644
index 00000000000..1debb64d3e0
--- /dev/null
+++ b/gn/src/gn/path_output_unittest.cc
@@ -0,0 +1,276 @@
+// 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 <sstream>
+
+#include "base/files/file_path.h"
+#include "gn/output_file.h"
+#include "gn/path_output.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(PathOutput, Basic) {
+ SourceDir build_dir("//out/Debug/");
+ std::string_view source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NONE);
+ {
+ // Normal source-root path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+ EXPECT_EQ("../../foo/bar.cc", out.str());
+ }
+ {
+ // File in the root dir.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo.cc"));
+ EXPECT_EQ("../../foo.cc", out.str());
+ }
+ {
+ // Files in the output dir.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//out/Debug/foo.cc"));
+ out << " ";
+ writer.WriteFile(out, SourceFile("//out/Debug/bar/baz.cc"));
+ EXPECT_EQ("foo.cc bar/baz.cc", out.str());
+ }
+#if defined(OS_WIN)
+ {
+ // System-absolute path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("/C:/foo/bar.cc"));
+ EXPECT_EQ("C:/foo/bar.cc", out.str());
+ }
+#else
+ {
+ // System-absolute path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("/foo/bar.cc"));
+ EXPECT_EQ("/foo/bar.cc", out.str());
+ }
+#endif
+}
+
+// Same as basic but the output dir is the root.
+TEST(PathOutput, BasicInRoot) {
+ SourceDir build_dir("//");
+ std::string_view source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NONE);
+ {
+ // Normal source-root path.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/bar.cc"));
+ EXPECT_EQ("foo/bar.cc", out.str());
+ }
+ {
+ // File in the root dir.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo.cc"));
+ EXPECT_EQ("foo.cc", out.str());
+ }
+}
+
+TEST(PathOutput, NinjaEscaping) {
+ SourceDir build_dir("//out/Debug/");
+ std::string_view source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
+ {
+ // Spaces and $ in filenames.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar$.cc"));
+ EXPECT_EQ("../../foo/foo$ bar$$.cc", out.str());
+ }
+ {
+ // Not other weird stuff
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/\"foo\".cc"));
+ EXPECT_EQ("../../foo/\"foo\".cc", out.str());
+ }
+}
+
+TEST(PathOutput, NinjaForkEscaping) {
+ SourceDir build_dir("//out/Debug/");
+ std::string_view source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA_COMMAND);
+
+ // Spaces in filenames should get quoted on Windows.
+ writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+ EXPECT_EQ("\"../../foo/foo$ bar.cc\"", out.str());
+ }
+
+ // Spaces in filenames should get escaped on Posix.
+ writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+ EXPECT_EQ("../../foo/foo\\$ bar.cc", out.str());
+ }
+
+ // Quotes should get blackslash-escaped on Windows and Posix.
+ writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
+ // Our Windows code currently quotes the whole thing in this case for
+ // code simplicity, even though it's strictly unnecessary. This might
+ // change in the future.
+ EXPECT_EQ("\"../../foo/\\\"foobar\\\".cc\"", out.str());
+ }
+ writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
+ EXPECT_EQ("../../foo/\\\"foobar\\\".cc", out.str());
+ }
+
+ // Backslashes should get escaped on non-Windows and preserved on Windows.
+ writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, OutputFile("foo\\bar.cc"));
+ EXPECT_EQ("foo\\bar.cc", out.str());
+ }
+ writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
+ {
+ std::ostringstream out;
+ writer.WriteFile(out, OutputFile("foo\\bar.cc"));
+ EXPECT_EQ("foo\\\\bar.cc", out.str());
+ }
+}
+
+TEST(PathOutput, InhibitQuoting) {
+ SourceDir build_dir("//out/Debug/");
+ std::string_view source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA_COMMAND);
+ writer.set_inhibit_quoting(true);
+
+ writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
+ {
+ // We should get unescaped spaces in the output with no quotes.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+ EXPECT_EQ("../../foo/foo$ bar.cc", out.str());
+ }
+
+ writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
+ {
+ // Escapes the space.
+ std::ostringstream out;
+ writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
+ EXPECT_EQ("../../foo/foo\\$ bar.cc", out.str());
+ }
+}
+
+TEST(PathOutput, WriteDir) {
+ {
+ SourceDir build_dir("//out/Debug/");
+ std::string_view source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//foo/bar/"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("../../foo/bar/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//foo/bar/"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("../../foo/bar", out.str());
+ }
+
+ // Output source root dir.
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("../../", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("../..", out.str());
+ }
+
+ // Output system root dir.
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("/.", out.str());
+ }
+
+ // Output inside current dir.
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//out/Debug/"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("./", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//out/Debug/"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ(".", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//out/Debug/foo/"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("foo/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, SourceDir("//out/Debug/foo/"),
+ PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("foo", out.str());
+ }
+
+ // WriteDir using an OutputFile.
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, OutputFile("foo/"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("foo/", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, OutputFile("foo/"), PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ("foo", out.str());
+ }
+ {
+ std::ostringstream out;
+ writer.WriteDir(out, OutputFile(), PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("", out.str());
+ }
+ }
+ {
+ // Empty build dir writer.
+ std::string_view source_root("/source/root");
+ PathOutput root_writer(SourceDir("//"), source_root, ESCAPE_NINJA);
+ {
+ std::ostringstream out;
+ root_writer.WriteDir(out, SourceDir("//"),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ EXPECT_EQ("./", out.str());
+ }
+ {
+ std::ostringstream out;
+ root_writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_NO_LAST_SLASH);
+ EXPECT_EQ(".", out.str());
+ }
+ }
+}
diff --git a/gn/src/gn/pattern.cc b/gn/src/gn/pattern.cc
new file mode 100644
index 00000000000..cf03bab9166
--- /dev/null
+++ b/gn/src/gn/pattern.cc
@@ -0,0 +1,223 @@
+// 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 "gn/pattern.h"
+
+#include "gn/value.h"
+
+const char kFilePattern_Help[] =
+ R"*(File patterns
+
+ File patterns are VERY limited regular expressions. They must match the
+ entire input string to be counted as a match. In regular expression parlance,
+ there is an implicit "^...$" surrounding your input. If you want to match a
+ substring, you need to use wildcards at the beginning and end.
+
+ There are only two special tokens understood by the pattern matcher.
+ Everything else is a literal.
+
+ - "*" Matches zero or more of any character. It does not depend on the
+ preceding character (in regular expression parlance it is equivalent to
+ ".*").
+
+ - "\b" Matches a path boundary. This will match the beginning or end of a
+ string, or a slash.
+
+Pattern examples
+
+ "*asdf*"
+ Matches a string containing "asdf" anywhere.
+
+ "asdf"
+ Matches only the exact string "asdf".
+
+ "*.cc"
+ Matches strings ending in the literal ".cc".
+
+ "\bwin/*"
+ Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+
+)*";
+
+namespace {
+
+void ParsePattern(const std::string& s, std::vector<Pattern::Subrange>* out) {
+ // Set when the last subrange is a literal so we can just append when we
+ // find another literal.
+ Pattern::Subrange* last_literal = nullptr;
+
+ for (size_t i = 0; i < s.size(); i++) {
+ if (s[i] == '*') {
+ // Don't allow two **.
+ if (out->size() == 0 ||
+ (*out)[out->size() - 1].type != Pattern::Subrange::ANYTHING)
+ out->push_back(Pattern::Subrange(Pattern::Subrange::ANYTHING));
+ last_literal = nullptr;
+ } else if (s[i] == '\\') {
+ if (i < s.size() - 1 && s[i + 1] == 'b') {
+ // "\b" means path boundary.
+ i++;
+ out->push_back(Pattern::Subrange(Pattern::Subrange::PATH_BOUNDARY));
+ last_literal = nullptr;
+ } else {
+ // Backslash + anything else means that literal char.
+ if (!last_literal) {
+ out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
+ last_literal = &(*out)[out->size() - 1];
+ }
+ if (i < s.size() - 1) {
+ i++;
+ last_literal->literal.push_back(s[i]);
+ } else {
+ // Single backslash at end, use literal backslash.
+ last_literal->literal.push_back('\\');
+ }
+ }
+ } else {
+ if (!last_literal) {
+ out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
+ last_literal = &(*out)[out->size() - 1];
+ }
+ last_literal->literal.push_back(s[i]);
+ }
+ }
+}
+
+} // namespace
+
+Pattern::Pattern(const std::string& s) {
+ ParsePattern(s, &subranges_);
+ is_suffix_ =
+ (subranges_.size() == 2 && subranges_[0].type == Subrange::ANYTHING &&
+ subranges_[1].type == Subrange::LITERAL);
+}
+
+Pattern::Pattern(const Pattern& other) = default;
+
+Pattern::~Pattern() = default;
+
+bool Pattern::MatchesString(const std::string& s) const {
+ // Empty pattern matches only empty string.
+ if (subranges_.empty())
+ return s.empty();
+
+ if (is_suffix_) {
+ const std::string& suffix = subranges_[1].literal;
+ if (suffix.size() > s.size())
+ return false; // Too short.
+ return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
+ }
+
+ return RecursiveMatch(s, 0, 0, true);
+}
+
+// We assume the number of ranges is small so recursive is always reasonable.
+// Could be optimized to only be recursive for *.
+bool Pattern::RecursiveMatch(const std::string& s,
+ size_t begin_char,
+ size_t subrange_index,
+ bool allow_implicit_path_boundary) const {
+ if (subrange_index >= subranges_.size()) {
+ // Hit the end of our subranges, the text should also be at the end for a
+ // match.
+ return begin_char == s.size();
+ }
+
+ const Subrange& sr = subranges_[subrange_index];
+ switch (sr.type) {
+ case Subrange::LITERAL: {
+ if (s.size() - begin_char < sr.literal.size())
+ return false; // Not enough room.
+ if (s.compare(begin_char, sr.literal.size(), sr.literal) != 0)
+ return false; // Literal doesn't match.
+
+ // Recursively check the next one.
+ return RecursiveMatch(s, begin_char + sr.literal.size(),
+ subrange_index + 1, true);
+ }
+
+ case Subrange::PATH_BOUNDARY: {
+ // When we can accept an implicit path boundary, we have to check both
+ // a match of the literal and the implicit one.
+ if (allow_implicit_path_boundary &&
+ (begin_char == 0 || begin_char == s.size())) {
+ // At implicit path boundary, see if the rest of the pattern matches.
+ if (RecursiveMatch(s, begin_char, subrange_index + 1, false))
+ return true;
+ }
+
+ // Check for a literal "/".
+ if (begin_char < s.size() && s[begin_char] == '/') {
+ // At explicit boundary, see if the rest of the pattern matches.
+ if (RecursiveMatch(s, begin_char + 1, subrange_index + 1, true))
+ return true;
+ }
+ return false;
+ }
+
+ case Subrange::ANYTHING: {
+ if (subrange_index == subranges_.size() - 1)
+ return true; // * at the end, consider it matching.
+
+ size_t min_next_size = sr.MinSize();
+
+ // We don't care about exactly what matched as long as there was a match,
+ // so we can do this front-to-back. If we needed the match, we would
+ // normally want "*" to be greedy so would work backwards.
+ for (size_t i = begin_char; i < s.size() - min_next_size; i++) {
+ // Note: this could probably be faster by detecting the type of the
+ // next match in advance and checking for a match in this loop rather
+ // than doing a full recursive call for each character.
+ if (RecursiveMatch(s, i, subrange_index + 1, true))
+ return true;
+ }
+ return false;
+ }
+
+ default:
+ NOTREACHED();
+ }
+
+ return false;
+}
+
+PatternList::PatternList() = default;
+
+PatternList::PatternList(const PatternList& other) = default;
+
+PatternList::~PatternList() = default;
+
+void PatternList::Append(const Pattern& pattern) {
+ patterns_.push_back(pattern);
+}
+
+void PatternList::SetFromValue(const Value& v, Err* err) {
+ patterns_.clear();
+
+ if (v.type() != Value::LIST) {
+ *err = Err(v.origin(), "This value must be a list.");
+ return;
+ }
+
+ const std::vector<Value>& list = v.list_value();
+ for (const auto& elem : list) {
+ if (!elem.VerifyTypeIs(Value::STRING, err))
+ return;
+ patterns_.push_back(Pattern(elem.string_value()));
+ }
+}
+
+bool PatternList::MatchesString(const std::string& s) const {
+ for (const auto& pattern : patterns_) {
+ if (pattern.MatchesString(s))
+ return true;
+ }
+ return false;
+}
+
+bool PatternList::MatchesValue(const Value& v) const {
+ if (v.type() == Value::STRING)
+ return MatchesString(v.string_value());
+ return false;
+}
diff --git a/gn/src/gn/pattern.h b/gn/src/gn/pattern.h
new file mode 100644
index 00000000000..f833ca4d63e
--- /dev/null
+++ b/gn/src/gn/pattern.h
@@ -0,0 +1,92 @@
+// 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 TOOLS_GN_PATTERN_H_
+#define TOOLS_GN_PATTERN_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "gn/value.h"
+
+extern const char kFilePattern_Help[];
+
+class Pattern {
+ public:
+ struct Subrange {
+ enum Type {
+ LITERAL, // Matches exactly the contents of the string.
+ ANYTHING, // * (zero or more chars).
+ PATH_BOUNDARY // '/' or beginning of string.
+ };
+
+ explicit Subrange(Type t, const std::string& l = std::string())
+ : type(t), literal(l) {}
+
+ // Returns the minimum number of chars that this subrange requires.
+ size_t MinSize() const {
+ switch (type) {
+ case LITERAL:
+ return literal.size();
+ case ANYTHING:
+ return 0;
+ case PATH_BOUNDARY:
+ return 0; // Can match beginning or end of string, which is 0 len.
+ default:
+ return 0;
+ }
+ }
+
+ Type type;
+
+ // When type == LITERAL this is the text to match.
+ std::string literal;
+ };
+
+ explicit Pattern(const std::string& s);
+ Pattern(const Pattern& other);
+ ~Pattern();
+
+ // Returns true if the current pattern matches the given string.
+ bool MatchesString(const std::string& s) const;
+
+ private:
+ // allow_implicit_path_boundary determines if a path boundary should accept
+ // matches at the beginning or end of the string.
+ bool RecursiveMatch(const std::string& s,
+ size_t begin_char,
+ size_t subrange_index,
+ bool allow_implicit_path_boundary) const;
+
+ std::vector<Subrange> subranges_;
+
+ // Set to true when the subranges are "*foo" ("ANYTHING" followed by a
+ // literal). This covers most patterns so we optimize for this.
+ bool is_suffix_;
+};
+
+class PatternList {
+ public:
+ PatternList();
+ PatternList(const PatternList& other);
+ ~PatternList();
+
+ bool is_empty() const { return patterns_.empty(); }
+
+ void Append(const Pattern& pattern);
+
+ // Initializes the pattern list from a give list of pattern strings. Sets
+ // |*err| on failure.
+ void SetFromValue(const Value& v, Err* err);
+
+ bool MatchesString(const std::string& s) const;
+ bool MatchesValue(const Value& v) const;
+
+ private:
+ std::vector<Pattern> patterns_;
+};
+
+#endif // TOOLS_GN_PATTERN_H_
diff --git a/gn/src/gn/pattern_unittest.cc b/gn/src/gn/pattern_unittest.cc
new file mode 100644
index 00000000000..d796289a0d0
--- /dev/null
+++ b/gn/src/gn/pattern_unittest.cc
@@ -0,0 +1,66 @@
+// 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 <stddef.h>
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "gn/pattern.h"
+#include "util/test/test.h"
+
+namespace {
+
+struct Case {
+ const char* pattern;
+ const char* candidate;
+ bool expected_match;
+};
+
+} // namespace
+
+TEST(Pattern, Matches) {
+ Case pattern_cases[] = {
+ // Empty pattern matches only empty string.
+ {"", "", true},
+ {"", "foo", false},
+ // Exact matches.
+ {"foo", "foo", true},
+ {"foo", "bar", false},
+ // Path boundaries.
+ {"\\b", "", true},
+ {"\\b", "/", true},
+ {"\\b\\b", "/", true},
+ {"\\b\\b\\b", "", false},
+ {"\\b\\b\\b", "/", true},
+ {"\\b", "//", false},
+ {"\\bfoo\\b", "foo", true},
+ {"\\bfoo\\b", "/foo/", true},
+ {"\\b\\bfoo", "/foo", true},
+ // *
+ {"*", "", true},
+ {"*", "foo", true},
+ {"*foo", "foo", true},
+ {"*foo", "gagafoo", true},
+ {"*foo", "gagafoob", false},
+ {"foo*bar", "foobar", true},
+ {"foo*bar", "foo-bar", true},
+ {"foo*bar", "foolalalalabar", true},
+ {"foo*bar", "foolalalalabaz", false},
+ {"*a*b*c*d*", "abcd", true},
+ {"*a*b*c*d*", "1a2b3c4d5", true},
+ {"*a*b*c*d*", "1a2b3c45", false},
+ {"*\\bfoo\\b*", "foo", true},
+ {"*\\bfoo\\b*", "/foo/", true},
+ {"*\\bfoo\\b*", "foob", false},
+ {"*\\bfoo\\b*", "lala/foo/bar/baz", true},
+ };
+ for (size_t i = 0; i < std::size(pattern_cases); i++) {
+ const Case& c = pattern_cases[i];
+ Pattern pattern(c.pattern);
+ bool result = pattern.MatchesString(c.candidate);
+ EXPECT_EQ(c.expected_match, result)
+ << i << ": \"" << c.pattern << "\", \"" << c.candidate << "\"";
+ }
+}
diff --git a/gn/src/gn/pool.cc b/gn/src/gn/pool.cc
new file mode 100644
index 00000000000..60049a1ee53
--- /dev/null
+++ b/gn/src/gn/pool.cc
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium 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 "gn/pool.h"
+
+#include <sstream>
+
+#include "base/logging.h"
+
+Pool::~Pool() = default;
+
+Pool* Pool::AsPool() {
+ return this;
+}
+
+const Pool* Pool::AsPool() const {
+ return this;
+}
+
+std::string Pool::GetNinjaName(const Label& default_toolchain) const {
+ bool include_toolchain = label().toolchain_dir() != default_toolchain.dir() ||
+ label().toolchain_name() != default_toolchain.name();
+ return GetNinjaName(include_toolchain);
+}
+
+std::string Pool::GetNinjaName(bool include_toolchain) const {
+ std::ostringstream buffer;
+ if (include_toolchain) {
+ DCHECK(label().toolchain_dir().is_source_absolute());
+ std::string toolchain_dir = label().toolchain_dir().value();
+ for (std::string::size_type i = 2; i < toolchain_dir.size(); ++i) {
+ buffer << (toolchain_dir[i] == '/' ? '_' : toolchain_dir[i]);
+ }
+ buffer << label().toolchain_name() << "_";
+ }
+
+ DCHECK(label().dir().is_source_absolute());
+ std::string label_dir = label().dir().value();
+ for (std::string::size_type i = 2; i < label_dir.size(); ++i) {
+ buffer << (label_dir[i] == '/' ? '_' : label_dir[i]);
+ }
+ buffer << label().name();
+ return buffer.str();
+}
diff --git a/gn/src/gn/pool.h b/gn/src/gn/pool.h
new file mode 100644
index 00000000000..3d6f8f39b7c
--- /dev/null
+++ b/gn/src/gn/pool.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_POOL_H_
+#define TOOLS_GN_POOL_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "gn/item.h"
+
+// Represents a named pool in the dependency graph.
+//
+// A pool is used to limit the parallelism of task invocation in the
+// generated ninja build. Pools are referenced by toolchains.
+class Pool : public Item {
+ public:
+ using Item::Item;
+ ~Pool() override;
+
+ // Item implementation.
+ Pool* AsPool() override;
+ const Pool* AsPool() const override;
+
+ // The pool depth (number of task to run simultaneously).
+ int64_t depth() const { return depth_; }
+ void set_depth(int64_t depth) { depth_ = depth; }
+
+ // The pool name in generated ninja files.
+ std::string GetNinjaName(const Label& default_toolchain) const;
+
+ private:
+ std::string GetNinjaName(bool include_toolchain) const;
+
+ int64_t depth_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Pool);
+};
+
+#endif // TOOLS_GN_POOL_H_
diff --git a/gn/src/gn/qt_creator_writer.cc b/gn/src/gn/qt_creator_writer.cc
new file mode 100644
index 00000000000..f5c997a0fab
--- /dev/null
+++ b/gn/src/gn/qt_creator_writer.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2016 The Chromium 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 "gn/qt_creator_writer.h"
+
+#include <optional>
+#include <set>
+#include <sstream>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+#include "gn/builder.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/label.h"
+#include "gn/loader.h"
+
+namespace {
+base::FilePath::CharType kProjectDirName[] =
+ FILE_PATH_LITERAL("qtcreator_project");
+base::FilePath::CharType kProjectName[] = FILE_PATH_LITERAL("all");
+base::FilePath::CharType kMainProjectFileSuffix[] =
+ FILE_PATH_LITERAL(".creator");
+base::FilePath::CharType kSourcesFileSuffix[] = FILE_PATH_LITERAL(".files");
+base::FilePath::CharType kIncludesFileSuffix[] = FILE_PATH_LITERAL(".includes");
+base::FilePath::CharType kDefinesFileSuffix[] = FILE_PATH_LITERAL(".config");
+} // namespace
+
+// static
+bool QtCreatorWriter::RunAndWriteFile(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err,
+ const std::string& root_target) {
+ base::FilePath project_dir =
+ build_settings->GetFullPath(build_settings->build_dir())
+ .Append(kProjectDirName);
+ if (!base::DirectoryExists(project_dir)) {
+ base::File::Error error;
+ if (!base::CreateDirectoryAndGetError(project_dir, &error)) {
+ *err =
+ Err(Location(), "Could not create the QtCreator project directory '" +
+ FilePathToUTF8(project_dir) +
+ "': " + base::File::ErrorToString(error));
+ return false;
+ }
+ }
+
+ base::FilePath project_prefix = project_dir.Append(kProjectName);
+ QtCreatorWriter gen(build_settings, builder, project_prefix, root_target);
+ gen.Run();
+ if (gen.err_.has_error()) {
+ *err = gen.err_;
+ return false;
+ }
+ return true;
+}
+
+QtCreatorWriter::QtCreatorWriter(const BuildSettings* build_settings,
+ const Builder& builder,
+ const base::FilePath& project_prefix,
+ const std::string& root_target_name)
+ : build_settings_(build_settings),
+ builder_(builder),
+ project_prefix_(project_prefix),
+ root_target_name_(root_target_name) {}
+
+QtCreatorWriter::~QtCreatorWriter() = default;
+
+void QtCreatorWriter::CollectDeps(const Target* target) {
+ for (const auto& dep : target->GetDeps(Target::DEPS_ALL)) {
+ const Target* dep_target = dep.ptr;
+ if (targets_.count(dep_target))
+ continue;
+ targets_.insert(dep_target);
+ CollectDeps(dep_target);
+ }
+}
+
+bool QtCreatorWriter::DiscoverTargets() {
+ auto all_targets = builder_.GetAllResolvedTargets();
+
+ if (root_target_name_.empty()) {
+ targets_ = std::set<const Target*>(all_targets.begin(), all_targets.end());
+ return true;
+ }
+
+ const Target* root_target = nullptr;
+ for (const Target* target : all_targets) {
+ if (target->label().name() == root_target_name_) {
+ root_target = target;
+ break;
+ }
+ }
+
+ if (!root_target) {
+ err_ = Err(Location(), "Target '" + root_target_name_ + "' not found.");
+ return false;
+ }
+
+ targets_.insert(root_target);
+ CollectDeps(root_target);
+ return true;
+}
+
+void QtCreatorWriter::AddToSources(const Target::FileList& files) {
+ for (const SourceFile& file : files) {
+ const std::string& file_path =
+ FilePathToUTF8(build_settings_->GetFullPath(file));
+ sources_.insert(file_path);
+ }
+}
+
+namespace QtCreatorWriterUtils {
+
+enum class CVersion {
+ C99,
+ C11,
+};
+
+enum class CxxVersion {
+ CXX98,
+ CXX03,
+ CXX11,
+ CXX14,
+ CXX17,
+};
+
+std::string ToMacro(CVersion version) {
+ const std::string s = "__STDC_VERSION__";
+
+ switch (version) {
+ case CVersion::C99:
+ return s + " 199901L";
+ case CVersion::C11:
+ return s + " 201112L";
+ }
+
+ return std::string();
+}
+
+std::string ToMacro(CxxVersion version) {
+ const std::string name = "__cplusplus";
+
+ switch (version) {
+ case CxxVersion::CXX98:
+ case CxxVersion::CXX03:
+ return name + " 199711L";
+ case CxxVersion::CXX11:
+ return name + " 201103L";
+ case CxxVersion::CXX14:
+ return name + " 201402L";
+ case CxxVersion::CXX17:
+ return name + " 201703L";
+ }
+
+ return std::string();
+}
+
+const std::map<std::string, CVersion> kFlagToCVersion{
+ {"-std=gnu99", CVersion::C99},
+ {"-std=c99", CVersion::C99},
+ {"-std=gnu11", CVersion::C11},
+ {"-std=c11", CVersion::C11}};
+
+const std::map<std::string, CxxVersion> kFlagToCxxVersion{
+ {"-std=gnu++11", CxxVersion::CXX11}, {"-std=c++11", CxxVersion::CXX11},
+ {"-std=gnu++98", CxxVersion::CXX98}, {"-std=c++98", CxxVersion::CXX98},
+ {"-std=gnu++03", CxxVersion::CXX03}, {"-std=c++03", CxxVersion::CXX03},
+ {"-std=gnu++14", CxxVersion::CXX14}, {"-std=c++14", CxxVersion::CXX14},
+ {"-std=c++1y", CxxVersion::CXX14}, {"-std=gnu++17", CxxVersion::CXX17},
+ {"-std=c++17", CxxVersion::CXX17}, {"-std=c++1z", CxxVersion::CXX17},
+};
+
+template <typename Enum>
+struct CompVersion {
+ bool operator()(Enum a, Enum b) {
+ return static_cast<int>(a) < static_cast<int>(b);
+ }
+};
+
+struct CompilerOptions {
+ std::optional<CVersion> c_version_;
+ std::optional<CxxVersion> cxx_version_;
+
+ void SetCVersion(CVersion ver) { SetVersionImpl(c_version_, ver); }
+
+ void SetCxxVersion(CxxVersion ver) { SetVersionImpl(cxx_version_, ver); }
+
+ private:
+ template <typename Version>
+ void SetVersionImpl(std::optional<Version>& cur_ver, Version ver) {
+ if (cur_ver)
+ cur_ver = std::max(*cur_ver, ver, CompVersion<Version>{});
+ else
+ cur_ver = ver;
+ }
+};
+
+void ParseCompilerOption(const std::string& flag, CompilerOptions* options) {
+ auto c_ver = kFlagToCVersion.find(flag);
+ if (c_ver != kFlagToCVersion.end())
+ options->SetCVersion(c_ver->second);
+
+ auto cxx_ver = kFlagToCxxVersion.find(flag);
+ if (cxx_ver != kFlagToCxxVersion.end())
+ options->SetCxxVersion(cxx_ver->second);
+}
+
+void ParseCompilerOptions(const std::vector<std::string>& cflags,
+ CompilerOptions* options) {
+ for (const std::string& flag : cflags)
+ ParseCompilerOption(flag, options);
+}
+
+} // namespace QtCreatorWriterUtils
+
+void QtCreatorWriter::HandleTarget(const Target* target) {
+ using namespace QtCreatorWriterUtils;
+
+ SourceFile build_file = builder_.loader()->BuildFileForLabel(target->label());
+ sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(build_file)));
+ AddToSources(target->settings()->import_manager().GetImportedFiles());
+
+ AddToSources(target->sources());
+ AddToSources(target->public_headers());
+
+ for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
+ for (const auto& input : it.cur().inputs())
+ sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(input)));
+
+ SourceFile precompiled_source = it.cur().precompiled_source();
+ if (!precompiled_source.is_null()) {
+ sources_.insert(
+ FilePathToUTF8(build_settings_->GetFullPath(precompiled_source)));
+ }
+
+ for (const SourceDir& include_dir : it.cur().include_dirs()) {
+ includes_.insert(
+ FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
+ }
+
+ static constexpr const char* define_str = "#define ";
+ for (std::string define : it.cur().defines()) {
+ size_t equal_pos = define.find('=');
+ if (equal_pos != std::string::npos)
+ define[equal_pos] = ' ';
+ define.insert(0, define_str);
+ defines_.insert(define);
+ }
+
+ CompilerOptions options;
+ ParseCompilerOptions(it.cur().cflags(), &options);
+ ParseCompilerOptions(it.cur().cflags_c(), &options);
+ ParseCompilerOptions(it.cur().cflags_cc(), &options);
+
+ auto add_define_version = [this](auto& ver) {
+ if (ver)
+ defines_.insert(define_str + ToMacro(*ver));
+ };
+ add_define_version(options.c_version_);
+ add_define_version(options.cxx_version_);
+ }
+}
+
+void QtCreatorWriter::GenerateFile(const base::FilePath::CharType* suffix,
+ const std::set<std::string>& items) {
+ const base::FilePath file_path = project_prefix_.AddExtension(suffix);
+ std::ostringstream output;
+ for (const std::string& item : items)
+ output << item << std::endl;
+ WriteFileIfChanged(file_path, output.str(), &err_);
+}
+
+void QtCreatorWriter::Run() {
+ if (!DiscoverTargets())
+ return;
+
+ for (const Target* target : targets_) {
+ if (target->toolchain()->label() !=
+ builder_.loader()->GetDefaultToolchain())
+ continue;
+ HandleTarget(target);
+ }
+
+ std::set<std::string> empty_list;
+
+ GenerateFile(kMainProjectFileSuffix, empty_list);
+ GenerateFile(kSourcesFileSuffix, sources_);
+ GenerateFile(kIncludesFileSuffix, includes_);
+ GenerateFile(kDefinesFileSuffix, defines_);
+}
diff --git a/gn/src/gn/qt_creator_writer.h b/gn/src/gn/qt_creator_writer.h
new file mode 100644
index 00000000000..c85a3223e05
--- /dev/null
+++ b/gn/src/gn/qt_creator_writer.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2016 The Chromium 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 TOOLS_GN_QT_CREATOR_WRITER_H_
+#define TOOLS_GN_QT_CREATOR_WRITER_H_
+
+#include <set>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gn/err.h"
+#include "gn/target.h"
+
+class Builder;
+class BuildSettings;
+
+class QtCreatorWriter {
+ public:
+ static bool RunAndWriteFile(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err,
+ const std::string& root_target);
+
+ private:
+ QtCreatorWriter(const BuildSettings* build_settings,
+ const Builder& builder,
+ const base::FilePath& project_prefix,
+ const std::string& root_target_name);
+ ~QtCreatorWriter();
+
+ void Run();
+
+ bool DiscoverTargets();
+ void HandleTarget(const Target* target);
+
+ void CollectDeps(const Target* target);
+ void AddToSources(const Target::FileList& files);
+ void GenerateFile(const base::FilePath::CharType* suffix,
+ const std::set<std::string>& items);
+
+ const BuildSettings* build_settings_;
+ const Builder& builder_;
+ base::FilePath project_prefix_;
+ std::string root_target_name_;
+ std::set<const Target*> targets_;
+ std::set<std::string> sources_;
+ std::set<std::string> includes_;
+ std::set<std::string> defines_;
+ Err err_;
+
+ DISALLOW_COPY_AND_ASSIGN(QtCreatorWriter);
+};
+
+#endif // TOOLS_GN_QT_CREATOR_WRITER_H_
diff --git a/gn/src/gn/runtime_deps.cc b/gn/src/gn/runtime_deps.cc
new file mode 100644
index 00000000000..3b6d683febe
--- /dev/null
+++ b/gn/src/gn/runtime_deps.cc
@@ -0,0 +1,319 @@
+// 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 "gn/runtime_deps.h"
+
+#include <map>
+#include <set>
+#include <sstream>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_split.h"
+#include "gn/build_settings.h"
+#include "gn/builder.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/loader.h"
+#include "gn/output_file.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/switches.h"
+#include "gn/target.h"
+#include "gn/trace.h"
+
+namespace {
+
+using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>;
+
+// Adds the given file to the deps list if it hasn't already been listed in
+// the found_files list. Updates the list.
+void AddIfNew(const OutputFile& output_file,
+ const Target* source,
+ RuntimeDepsVector* deps,
+ std::set<OutputFile>* found_file) {
+ if (found_file->find(output_file) != found_file->end())
+ return; // Already there.
+ deps->push_back(std::make_pair(output_file, source));
+}
+
+// Automatically converts a string that looks like a source to an OutputFile.
+void AddIfNew(const std::string& str,
+ const Target* source,
+ RuntimeDepsVector* deps,
+ std::set<OutputFile>* found_file) {
+ OutputFile output_file(
+ RebasePath(str, source->settings()->build_settings()->build_dir(),
+ source->settings()->build_settings()->root_path_utf8()));
+ AddIfNew(output_file, source, deps, found_file);
+}
+
+// To avoid duplicate traversals of targets, or duplicating output files that
+// might be listed by more than one target, the set of targets and output files
+// that have been found so far is passed. The "value" of the seen_targets map
+// is a boolean indicating if the seen dep was a data dep (true = data_dep).
+// data deps add more stuff, so we will want to revisit a target if it's a
+// data dependency and we've previously only seen it as a regular dep.
+void RecursiveCollectRuntimeDeps(const Target* target,
+ bool is_target_data_dep,
+ RuntimeDepsVector* deps,
+ std::map<const Target*, bool>* seen_targets,
+ std::set<OutputFile>* found_files) {
+ const auto& found_seen_target = seen_targets->find(target);
+ if (found_seen_target != seen_targets->end()) {
+ // Already visited.
+ if (found_seen_target->second || !is_target_data_dep) {
+ // Already visited as a data dep, or the current dep is not a data
+ // dep so visiting again will be a no-op.
+ return;
+ }
+ // In the else case, the previously seen target was a regular dependency
+ // and we'll now process it as a data dependency.
+ }
+ (*seen_targets)[target] = is_target_data_dep;
+
+ // Add the main output file for executables, shared libraries, and
+ // loadable modules.
+ if (target->output_type() == Target::EXECUTABLE ||
+ target->output_type() == Target::LOADABLE_MODULE ||
+ target->output_type() == Target::SHARED_LIBRARY) {
+ for (const auto& runtime_output : target->runtime_outputs())
+ AddIfNew(runtime_output, target, deps, found_files);
+ }
+
+ // Add all data files.
+ for (const auto& file : target->data())
+ AddIfNew(file, target, deps, found_files);
+
+ // Actions/copy have all outputs considered when the're a data dep.
+ if (is_target_data_dep && (target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH ||
+ target->output_type() == Target::COPY_FILES)) {
+ std::vector<SourceFile> outputs;
+ target->action_values().GetOutputsAsSourceFiles(target, &outputs);
+ for (const auto& output_file : outputs)
+ AddIfNew(output_file.value(), target, deps, found_files);
+ }
+
+ // Data dependencies.
+ for (const auto& dep_pair : target->data_deps()) {
+ RecursiveCollectRuntimeDeps(dep_pair.ptr, true, deps, seen_targets,
+ found_files);
+ }
+
+ // Do not recurse into bundle targets. A bundle's dependencies should be
+ // copied into the bundle itself for run-time access.
+ if (target->output_type() == Target::CREATE_BUNDLE) {
+ SourceDir bundle_root_dir =
+ target->bundle_data().GetBundleRootDirOutputAsDir(target->settings());
+ AddIfNew(bundle_root_dir.value(), target, deps, found_files);
+ return;
+ }
+
+ // Non-data dependencies (both public and private).
+ for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) {
+ if (dep_pair.ptr->output_type() == Target::EXECUTABLE)
+ continue; // Skip executables that aren't data deps.
+ if (dep_pair.ptr->output_type() == Target::SHARED_LIBRARY &&
+ (target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH)) {
+ // Skip shared libraries that action depends on,
+ // unless it were listed in data deps.
+ continue;
+ }
+ RecursiveCollectRuntimeDeps(dep_pair.ptr, false, deps, seen_targets,
+ found_files);
+ }
+}
+
+bool CollectRuntimeDepsFromFlag(const BuildSettings* build_settings,
+ const Builder& builder,
+ RuntimeDepsVector* files_to_write,
+ Err* err) {
+ std::string deps_target_list_file =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kRuntimeDepsListFile);
+
+ if (deps_target_list_file.empty())
+ return true;
+
+ std::string list_contents;
+ ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file);
+ if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file),
+ &list_contents)) {
+ *err = Err(Location(),
+ std::string("File for --") + switches::kRuntimeDepsListFile +
+ " doesn't exist.",
+ "The file given was \"" + deps_target_list_file + "\"");
+ return false;
+ }
+ load_trace.Done();
+
+ SourceDir root_dir("//");
+ Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
+ for (const auto& line : base::SplitString(
+ list_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ if (line.empty())
+ continue;
+ Label label =
+ Label::Resolve(root_dir, build_settings->root_path_utf8(),
+ default_toolchain_label, Value(nullptr, line), err);
+ if (err->has_error())
+ return false;
+
+ const Item* item = builder.GetItem(label);
+ const Target* target = item ? item->AsTarget() : nullptr;
+ if (!target) {
+ *err =
+ Err(Location(),
+ "The label \"" + label.GetUserVisibleName(true) +
+ "\" isn't a target.",
+ "When reading the line:\n " + line +
+ "\n"
+ "from the --" +
+ switches::kRuntimeDepsListFile + "=" + deps_target_list_file);
+ return false;
+ }
+
+ OutputFile output_file;
+ const char extension[] = ".runtime_deps";
+ if (target->output_type() == Target::SHARED_LIBRARY ||
+ target->output_type() == Target::LOADABLE_MODULE) {
+ // Force the first output for shared-library-type linker outputs since
+ // the dependency output files might not be the main output.
+ CHECK(!target->computed_outputs().empty());
+ output_file =
+ OutputFile(target->computed_outputs()[0].value() + extension);
+ } else {
+ output_file =
+ OutputFile(target->dependency_output_file().value() + extension);
+ }
+ files_to_write->push_back(std::make_pair(output_file, target));
+ }
+ return true;
+}
+
+bool WriteRuntimeDepsFile(const OutputFile& output_file,
+ const Target* target,
+ Err* err) {
+ SourceFile output_as_source =
+ output_file.AsSourceFile(target->settings()->build_settings());
+ base::FilePath data_deps_file =
+ target->settings()->build_settings()->GetFullPath(output_as_source);
+
+ std::stringstream contents;
+ for (const auto& pair : ComputeRuntimeDeps(target))
+ contents << pair.first.value() << std::endl;
+
+ ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, output_as_source.value());
+ return WriteFileIfChanged(data_deps_file, contents.str(), err);
+}
+
+} // namespace
+
+const char kRuntimeDeps_Help[] =
+ R"(Runtime dependencies
+
+ Runtime dependencies of a target are exposed via the "runtime_deps" category
+ of "gn desc" (see "gn help desc") or they can be written at build generation
+ time via write_runtime_deps(), or --runtime-deps-list-file (see "gn help
+ --runtime-deps-list-file").
+
+ To a first approximation, the runtime dependencies of a target are the set of
+ "data" files, data directories, and the shared libraries from all transitive
+ dependencies. Executables, shared libraries, and loadable modules are
+ considered runtime dependencies of themselves.
+
+Executables
+
+ Executable targets and those executable targets' transitive dependencies are
+ not considered unless that executable is listed in "data_deps". Otherwise, GN
+ assumes that the executable (and everything it requires) is a build-time
+ dependency only.
+
+Actions and copies
+
+ Action and copy targets that are listed as "data_deps" will have all of their
+ outputs and data files considered as runtime dependencies. Action and copy
+ targets that are "deps" or "public_deps" will have only their data files
+ considered as runtime dependencies. These targets can list an output file in
+ both the "outputs" and "data" lists to force an output file as a runtime
+ dependency in all cases.
+
+ The different rules for deps and data_deps are to express build-time (deps)
+ vs. run-time (data_deps) outputs. If GN counted all build-time copy steps as
+ data dependencies, there would be a lot of extra stuff, and if GN counted all
+ run-time dependencies as regular deps, the build's parallelism would be
+ unnecessarily constrained.
+
+ This rule can sometimes lead to unintuitive results. For example, given the
+ three targets:
+ A --[data_deps]--> B --[deps]--> ACTION
+ GN would say that A does not have runtime deps on the result of the ACTION,
+ which is often correct. But the purpose of the B target might be to collect
+ many actions into one logic unit, and the "data"-ness of A's dependency is
+ lost. Solutions:
+
+ - List the outputs of the action in its data section (if the results of
+ that action are always runtime files).
+ - Have B list the action in data_deps (if the outputs of the actions are
+ always runtime files).
+ - Have B list the action in both deps and data deps (if the outputs might be
+ used in both contexts and you don't care about unnecessary entries in the
+ list of files required at runtime).
+ - Split B into run-time and build-time versions with the appropriate "deps"
+ for each.
+
+Static libraries and source sets
+
+ The results of static_library or source_set targets are not considered
+ runtime dependencies since these are assumed to be intermediate targets only.
+ If you need to list a static library as a runtime dependency, you can
+ manually compute the .a/.lib file name for the current platform and list it
+ in the "data" list of a target (possibly on the static library target
+ itself).
+
+Multiple outputs
+
+ Linker tools can specify which of their outputs should be considered when
+ computing the runtime deps by setting runtime_outputs. If this is unset on
+ the tool, the default will be the first output only.
+)";
+
+RuntimeDepsVector ComputeRuntimeDeps(const Target* target) {
+ RuntimeDepsVector result;
+ std::map<const Target*, bool> seen_targets;
+ std::set<OutputFile> found_files;
+
+ // The initial target is not considered a data dependency so that actions's
+ // outputs (if the current target is an action) are not automatically
+ // considered data deps.
+ RecursiveCollectRuntimeDeps(target, false, &result, &seen_targets,
+ &found_files);
+ return result;
+}
+
+bool WriteRuntimeDepsFilesIfNecessary(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err) {
+ RuntimeDepsVector files_to_write;
+ if (!CollectRuntimeDepsFromFlag(build_settings, builder, &files_to_write,
+ err))
+ return false;
+
+ // Files scheduled by write_runtime_deps.
+ for (const Target* target : g_scheduler->GetWriteRuntimeDepsTargets()) {
+ files_to_write.push_back(
+ std::make_pair(target->write_runtime_deps_output(), target));
+ }
+
+ for (const auto& entry : files_to_write) {
+ // Currently this writes all runtime deps files sequentially. We generally
+ // expect few of these. We can run this on the worker pool if it looks
+ // like it's talking a long time.
+ if (!WriteRuntimeDepsFile(entry.first, entry.second, err))
+ return false;
+ }
+ return true;
+}
diff --git a/gn/src/gn/runtime_deps.h b/gn/src/gn/runtime_deps.h
new file mode 100644
index 00000000000..82e0f10cbe9
--- /dev/null
+++ b/gn/src/gn/runtime_deps.h
@@ -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.
+
+#ifndef TOOLS_GN_RUNTIME_DEPS_H
+#define TOOLS_GN_RUNTIME_DEPS_H
+
+#include <utility>
+#include <vector>
+
+class Builder;
+class BuildSettings;
+class Err;
+class OutputFile;
+class Target;
+
+extern const char kRuntimeDeps_Help[];
+
+// Computes the runtime dependencies of the given target. The result is a list
+// of pairs listing the runtime dependency and the target that the runtime
+// dependency is from (for blaming).
+std::vector<std::pair<OutputFile, const Target*>> ComputeRuntimeDeps(
+ const Target* target);
+
+// Writes all runtime deps files requested on the command line, or does nothing
+// if no files were specified.
+bool WriteRuntimeDepsFilesIfNecessary(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err);
+
+#endif // TOOLS_GN_RUNTIME_DEPS_H
diff --git a/gn/src/gn/runtime_deps_unittest.cc b/gn/src/gn/runtime_deps_unittest.cc
new file mode 100644
index 00000000000..e79d7497706
--- /dev/null
+++ b/gn/src/gn/runtime_deps_unittest.cc
@@ -0,0 +1,448 @@
+// 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 <stddef.h>
+
+#include "base/stl_util.h"
+#include "gn/runtime_deps.h"
+#include "gn/scheduler.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+void InitTargetWithType(TestWithScope& setup,
+ Target* target,
+ Target::OutputType type) {
+ target->set_output_type(type);
+ target->visibility().SetPublic();
+ target->SetToolchain(setup.toolchain());
+}
+
+// Convenience function to make the correct kind of pair.
+std::pair<OutputFile, const Target*> MakePair(const char* str,
+ const Target* t) {
+ return std::pair<OutputFile, const Target*>(OutputFile(str), t);
+}
+
+std::string GetVectorDescription(
+ const std::vector<std::pair<OutputFile, const Target*>>& v) {
+ std::string result;
+ for (size_t i = 0; i < v.size(); i++) {
+ if (i != 0)
+ result.append(", ");
+ result.append("\"" + v[i].first.value() + "\"");
+ }
+ return result;
+}
+
+} // namespace
+
+using RuntimeDeps = TestWithScheduler;
+
+// Tests an exe depending on different types of libraries.
+TEST_F(RuntimeDeps, Libs) {
+ TestWithScope setup;
+ Err err;
+
+ // Dependency hierarchy: main(exe) -> static library
+ // -> shared library
+ // -> loadable module
+ // -> source set
+
+ Target stat(setup.settings(), Label(SourceDir("//"), "stat"));
+ InitTargetWithType(setup, &stat, Target::STATIC_LIBRARY);
+ stat.data().push_back("//stat.dat");
+ ASSERT_TRUE(stat.OnResolved(&err));
+
+ Target shared(setup.settings(), Label(SourceDir("//"), "shared"));
+ InitTargetWithType(setup, &shared, Target::SHARED_LIBRARY);
+ shared.data().push_back("//shared.dat");
+ ASSERT_TRUE(shared.OnResolved(&err));
+
+ Target loadable(setup.settings(), Label(SourceDir("//"), "loadable"));
+ InitTargetWithType(setup, &loadable, Target::LOADABLE_MODULE);
+ loadable.data().push_back("//loadable.dat");
+ ASSERT_TRUE(loadable.OnResolved(&err));
+
+ Target set(setup.settings(), Label(SourceDir("//"), "set"));
+ InitTargetWithType(setup, &set, Target::SOURCE_SET);
+ set.data().push_back("//set.dat");
+ ASSERT_TRUE(set.OnResolved(&err));
+
+ Target main(setup.settings(), Label(SourceDir("//"), "main"));
+ InitTargetWithType(setup, &main, Target::EXECUTABLE);
+ main.private_deps().push_back(LabelTargetPair(&stat));
+ main.private_deps().push_back(LabelTargetPair(&shared));
+ main.private_deps().push_back(LabelTargetPair(&loadable));
+ main.private_deps().push_back(LabelTargetPair(&set));
+ main.data().push_back("//main.dat");
+ ASSERT_TRUE(main.OnResolved(&err));
+
+ std::vector<std::pair<OutputFile, const Target*>> result =
+ ComputeRuntimeDeps(&main);
+
+ // The result should have deps of main, all 5 dat files, libshared.so, and
+ // libloadable.so.
+ ASSERT_EQ(8u, result.size()) << GetVectorDescription(result);
+
+ // The first one should always be the main exe.
+ EXPECT_TRUE(MakePair("./main", &main) == result[0]);
+
+ // The rest of the ordering is undefined. First the data files.
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("../../stat.dat", &stat)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../shared.dat", &shared)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../loadable.dat", &loadable)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("../../set.dat", &set)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("../../main.dat", &main)))
+ << GetVectorDescription(result);
+
+ // Check the static library and loadable module.
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("./libshared.so", &shared)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("./libloadable.so", &loadable)))
+ << GetVectorDescription(result);
+}
+
+// Tests that executables that aren't listed as data deps aren't included in
+// the output, but executables that are data deps are included.
+TEST_F(RuntimeDeps, ExeDataDep) {
+ TestWithScope setup;
+ Err err;
+
+ // Dependency hierarchy: main(exe) -> datadep(exe) -> final_in(source set)
+ // -> dep(exe) -> final_out(source set)
+ // The final_in/out targets each have data files. final_in's should be
+ // included, final_out's should not be.
+
+ Target final_in(setup.settings(), Label(SourceDir("//"), "final_in"));
+ InitTargetWithType(setup, &final_in, Target::SOURCE_SET);
+ final_in.data().push_back("//final_in.dat");
+ ASSERT_TRUE(final_in.OnResolved(&err));
+
+ Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
+ InitTargetWithType(setup, &datadep, Target::EXECUTABLE);
+ datadep.private_deps().push_back(LabelTargetPair(&final_in));
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ Target final_out(setup.settings(), Label(SourceDir("//"), "final_out"));
+ InitTargetWithType(setup, &final_out, Target::SOURCE_SET);
+ final_out.data().push_back("//final_out.dat");
+ ASSERT_TRUE(final_out.OnResolved(&err));
+
+ Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
+ InitTargetWithType(setup, &dep, Target::EXECUTABLE);
+ dep.private_deps().push_back(LabelTargetPair(&final_out));
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target main(setup.settings(), Label(SourceDir("//"), "main"));
+ InitTargetWithType(setup, &main, Target::EXECUTABLE);
+ main.private_deps().push_back(LabelTargetPair(&dep));
+ main.data_deps().push_back(LabelTargetPair(&datadep));
+ ASSERT_TRUE(main.OnResolved(&err));
+
+ std::vector<std::pair<OutputFile, const Target*>> result =
+ ComputeRuntimeDeps(&main);
+
+ // The result should have deps of main, datadep, final_in.dat
+ ASSERT_EQ(3u, result.size()) << GetVectorDescription(result);
+
+ // The first one should always be the main exe.
+ EXPECT_TRUE(MakePair("./main", &main) == result[0]);
+
+ // The rest of the ordering is undefined.
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("./datadep", &datadep)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../final_in.dat", &final_in)))
+ << GetVectorDescription(result);
+}
+
+TEST_F(RuntimeDeps, ActionSharedLib) {
+ TestWithScope setup;
+ Err err;
+
+ // Dependency hierarchy: main(exe) -> action -> datadep(shared library)
+ // -> dep(shared library)
+ // Datadep should be included, dep should not be.
+
+ Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
+ InitTargetWithType(setup, &dep, Target::SHARED_LIBRARY);
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
+ InitTargetWithType(setup, &datadep, Target::SHARED_LIBRARY);
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ Target action(setup.settings(), Label(SourceDir("//"), "action"));
+ InitTargetWithType(setup, &action, Target::ACTION);
+ action.private_deps().push_back(LabelTargetPair(&dep));
+ action.data_deps().push_back(LabelTargetPair(&datadep));
+ action.action_values().outputs() =
+ SubstitutionList::MakeForTest("//action.output");
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ Target main(setup.settings(), Label(SourceDir("//"), "main"));
+ InitTargetWithType(setup, &main, Target::EXECUTABLE);
+ main.private_deps().push_back(LabelTargetPair(&action));
+ ASSERT_TRUE(main.OnResolved(&err));
+
+ std::vector<std::pair<OutputFile, const Target*>> result =
+ ComputeRuntimeDeps(&main);
+
+ // The result should have deps of main and data_dep.
+ ASSERT_EQ(2u, result.size()) << GetVectorDescription(result);
+
+ // The first one should always be the main exe.
+ EXPECT_TRUE(MakePair("./main", &main) == result[0]);
+ EXPECT_TRUE(MakePair("./libdatadep.so", &datadep) == result[1]);
+}
+
+// Tests that action and copy outputs are considered if they're data deps, but
+// not if they're regular deps. Action and copy "data" files are always
+// included.
+TEST_F(RuntimeDeps, ActionOutputs) {
+ TestWithScope setup;
+ Err err;
+
+ // Dependency hierarchy: main(exe) -> datadep (action)
+ // -> datadep_copy (copy)
+ // -> dep (action)
+ // -> dep_copy (copy)
+
+ Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
+ InitTargetWithType(setup, &datadep, Target::ACTION);
+ datadep.data().push_back("//datadep.data");
+ datadep.action_values().outputs() =
+ SubstitutionList::MakeForTest("//datadep.output");
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ Target datadep_copy(setup.settings(), Label(SourceDir("//"), "datadep_copy"));
+ InitTargetWithType(setup, &datadep_copy, Target::COPY_FILES);
+ datadep_copy.sources().push_back(SourceFile("//input"));
+ datadep_copy.data().push_back("//datadep_copy.data");
+ datadep_copy.action_values().outputs() =
+ SubstitutionList::MakeForTest("//datadep_copy.output");
+ ASSERT_TRUE(datadep_copy.OnResolved(&err));
+
+ Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
+ InitTargetWithType(setup, &dep, Target::ACTION);
+ dep.data().push_back("//dep.data");
+ dep.action_values().outputs() = SubstitutionList::MakeForTest("//dep.output");
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target dep_copy(setup.settings(), Label(SourceDir("//"), "dep_copy"));
+ InitTargetWithType(setup, &dep_copy, Target::COPY_FILES);
+ dep_copy.sources().push_back(SourceFile("//input"));
+ dep_copy.data().push_back("//dep_copy/data/"); // Tests a directory.
+ dep_copy.action_values().outputs() =
+ SubstitutionList::MakeForTest("//dep_copy.output");
+ ASSERT_TRUE(dep_copy.OnResolved(&err));
+
+ Target main(setup.settings(), Label(SourceDir("//"), "main"));
+ InitTargetWithType(setup, &main, Target::EXECUTABLE);
+ main.private_deps().push_back(LabelTargetPair(&dep));
+ main.private_deps().push_back(LabelTargetPair(&dep_copy));
+ main.data_deps().push_back(LabelTargetPair(&datadep));
+ main.data_deps().push_back(LabelTargetPair(&datadep_copy));
+ ASSERT_TRUE(main.OnResolved(&err));
+
+ std::vector<std::pair<OutputFile, const Target*>> result =
+ ComputeRuntimeDeps(&main);
+
+ // The result should have deps of main, both datadeps files, but only
+ // the data file from dep.
+ ASSERT_EQ(7u, result.size()) << GetVectorDescription(result);
+
+ // The first one should always be the main exe.
+ EXPECT_TRUE(MakePair("./main", &main) == result[0]);
+
+ // The rest of the ordering is undefined.
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../datadep.data", &datadep)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(
+ result, MakePair("../../datadep_copy.data", &datadep_copy)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../datadep.output", &datadep)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(
+ result, MakePair("../../datadep_copy.output", &datadep_copy)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("../../dep.data", &dep)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../dep_copy/data/", &dep_copy)))
+ << GetVectorDescription(result);
+
+ // Explicitly asking for the runtime deps of an action target only includes
+ // the data and not all outputs.
+ result = ComputeRuntimeDeps(&dep);
+ ASSERT_EQ(1u, result.size());
+ EXPECT_TRUE(MakePair("../../dep.data", &dep) == result[0]);
+}
+
+// Tests that the search for dependencies terminates at a bundle target,
+// ignoring any shared libraries or loadable modules that get copied into the
+// bundle.
+TEST_F(RuntimeDeps, CreateBundle) {
+ TestWithScope setup;
+ Err err;
+
+ // Dependency hierarchy:
+ // main(exe) -> dep(bundle) -> dep(shared_library) -> dep(source set)
+ // -> dep(bundle_data) -> dep(loadable_module)
+ // -> data(lm.data)
+ // -> datadep(datadep) -> data(dd.data)
+
+ const SourceDir source_dir("//");
+ const std::string& build_dir = setup.build_settings()->build_dir().value();
+
+ Target loadable_module(setup.settings(),
+ Label(source_dir, "loadable_module"));
+ InitTargetWithType(setup, &loadable_module, Target::LOADABLE_MODULE);
+ loadable_module.data().push_back("//lm.data");
+ ASSERT_TRUE(loadable_module.OnResolved(&err));
+
+ Target module_data(setup.settings(), Label(source_dir, "module_data"));
+ InitTargetWithType(setup, &module_data, Target::BUNDLE_DATA);
+ module_data.private_deps().push_back(LabelTargetPair(&loadable_module));
+ module_data.bundle_data().file_rules().push_back(BundleFileRule(
+ nullptr,
+ std::vector<SourceFile>{SourceFile(build_dir + "loadable_module.so")},
+ SubstitutionPattern::MakeForTest("{{bundle_resources_dir}}")));
+ ASSERT_TRUE(module_data.OnResolved(&err));
+
+ Target source_set(setup.settings(), Label(source_dir, "sources"));
+ InitTargetWithType(setup, &source_set, Target::SOURCE_SET);
+ source_set.sources().push_back(SourceFile(source_dir.value() + "foo.cc"));
+ ASSERT_TRUE(source_set.OnResolved(&err));
+
+ Target dylib(setup.settings(), Label(source_dir, "dylib"));
+ dylib.set_output_prefix_override(true);
+ dylib.set_output_extension("");
+ dylib.set_output_name("Bundle");
+ InitTargetWithType(setup, &dylib, Target::SHARED_LIBRARY);
+ dylib.private_deps().push_back(LabelTargetPair(&source_set));
+ ASSERT_TRUE(dylib.OnResolved(&err));
+
+ Target dylib_data(setup.settings(), Label(source_dir, "dylib_data"));
+ InitTargetWithType(setup, &dylib_data, Target::BUNDLE_DATA);
+ dylib_data.private_deps().push_back(LabelTargetPair(&dylib));
+ dylib_data.bundle_data().file_rules().push_back(BundleFileRule(
+ nullptr, std::vector<SourceFile>{SourceFile(build_dir + "dylib")},
+ SubstitutionPattern::MakeForTest("{{bundle_executable_dir}}")));
+ ASSERT_TRUE(dylib_data.OnResolved(&err));
+
+ Target data_dep(setup.settings(), Label(source_dir, "datadep"));
+ InitTargetWithType(setup, &data_dep, Target::EXECUTABLE);
+ data_dep.data().push_back("//dd.data");
+ ASSERT_TRUE(data_dep.OnResolved(&err));
+
+ Target bundle(setup.settings(), Label(source_dir, "bundle"));
+ InitTargetWithType(setup, &bundle, Target::CREATE_BUNDLE);
+ const std::string root_dir(build_dir + "Bundle.framework/");
+ const std::string contents_dir(root_dir + "Versions/A/");
+ bundle.bundle_data().root_dir() = SourceDir(root_dir);
+ bundle.bundle_data().contents_dir() = SourceDir(contents_dir);
+ bundle.bundle_data().resources_dir() = SourceDir(contents_dir + "Resources");
+ bundle.bundle_data().executable_dir() = SourceDir(contents_dir + "MacOS");
+ bundle.private_deps().push_back(LabelTargetPair(&dylib_data));
+ bundle.private_deps().push_back(LabelTargetPair(&module_data));
+ bundle.data_deps().push_back(LabelTargetPair(&data_dep));
+ bundle.data().push_back("//b.data");
+ ASSERT_TRUE(bundle.OnResolved(&err));
+
+ Target main(setup.settings(), Label(source_dir, "main"));
+ InitTargetWithType(setup, &main, Target::EXECUTABLE);
+ main.data_deps().push_back(LabelTargetPair(&bundle));
+ ASSERT_TRUE(main.OnResolved(&err));
+
+ std::vector<std::pair<OutputFile, const Target*>> result =
+ ComputeRuntimeDeps(&main);
+
+ // The result should have deps of main, datadep, final_in.dat
+ ASSERT_EQ(5u, result.size()) << GetVectorDescription(result);
+
+ // The first one should always be the main exe.
+ EXPECT_EQ(MakePair("./main", &main), result[0]);
+
+ // The rest of the ordering is undefined.
+
+ // The framework bundle's internal dependencies should not be incldued.
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("Bundle.framework/", &bundle)))
+ << GetVectorDescription(result);
+ // But direct data and data dependencies should be.
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("./datadep", &data_dep)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("../../dd.data", &data_dep)))
+ << GetVectorDescription(result);
+ EXPECT_TRUE(base::ContainsValue(result, MakePair("../../b.data", &bundle)))
+ << GetVectorDescription(result);
+}
+
+// Tests that a dependency duplicated in regular and data deps is processed
+// as a data dep.
+TEST_F(RuntimeDeps, Dupe) {
+ TestWithScope setup;
+ Err err;
+
+ Target action(setup.settings(), Label(SourceDir("//"), "action"));
+ InitTargetWithType(setup, &action, Target::ACTION);
+ action.action_values().outputs() =
+ SubstitutionList::MakeForTest("//action.output");
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//"), "foo"));
+ InitTargetWithType(setup, &target, Target::EXECUTABLE);
+ target.private_deps().push_back(LabelTargetPair(&action));
+ target.data_deps().push_back(LabelTargetPair(&action));
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // The results should be the executable and the copy output.
+ std::vector<std::pair<OutputFile, const Target*>> result =
+ ComputeRuntimeDeps(&target);
+ EXPECT_TRUE(
+ base::ContainsValue(result, MakePair("../../action.output", &action)))
+ << GetVectorDescription(result);
+}
+
+// Tests that actions can't have output substitutions.
+TEST_F(RuntimeDeps, WriteRuntimeDepsVariable) {
+ TestWithScope setup;
+ Err err;
+
+ // Should refuse to write files outside of the output dir.
+ EXPECT_FALSE(setup.ExecuteSnippet(
+ "group(\"foo\") { write_runtime_deps = \"//foo.txt\" }", &err));
+
+ // Should fail for garbage inputs.
+ err = Err();
+ EXPECT_FALSE(
+ setup.ExecuteSnippet("group(\"foo\") { write_runtime_deps = 0 }", &err));
+
+ // Should be able to write inside the out dir, and shouldn't write the one
+ // in the else clause.
+ err = Err();
+ EXPECT_TRUE(setup.ExecuteSnippet(
+ "if (true) {\n"
+ " group(\"foo\") { write_runtime_deps = \"//out/Debug/foo.txt\" }\n"
+ "} else {\n"
+ " group(\"bar\") { write_runtime_deps = \"//out/Debug/bar.txt\" }\n"
+ "}",
+ &err));
+ EXPECT_EQ(1U, setup.items().size());
+ EXPECT_EQ(1U, scheduler().GetWriteRuntimeDepsTargets().size());
+}
diff --git a/gn/src/gn/rust_project_writer.cc b/gn/src/gn/rust_project_writer.cc
new file mode 100644
index 00000000000..9134cd0801b
--- /dev/null
+++ b/gn/src/gn/rust_project_writer.cc
@@ -0,0 +1,451 @@
+// Copyright 2020 The Chromium 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 "gn/rust_project_writer.h"
+
+#include <fstream>
+#include <optional>
+#include <sstream>
+#include <tuple>
+
+#include "base/json/string_escape.h"
+#include "gn/builder.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/ninja_target_command_util.h"
+#include "gn/rust_project_writer_helpers.h"
+#include "gn/rust_tool.h"
+#include "gn/source_file.h"
+#include "gn/string_output_buffer.h"
+#include "gn/tool.h"
+
+#if defined(OS_WINDOWS)
+#define NEWLINE "\r\n"
+#else
+#define NEWLINE "\n"
+#endif
+
+// Current structure of rust-project.json output file
+//
+// {
+// "roots": [
+// "some/source/root" // each crate's source root
+// ],
+// "crates": [
+// {
+// "deps": [
+// {
+// "crate": 1, // index into crate array
+// "name": "alloc" // extern name of dependency
+// },
+// ],
+// "edition": "2018", // edition of crate
+// "cfg": [
+// "unix", // "atomic" value config options
+// "rust_panic=\"abort\""", // key="value" config options
+// ]
+// "root_module": "absolute path to crate",
+// "label": "//path/target:value", // GN target for the crate
+// "target": "x86_64-unknown-linux" // optional rustc target
+// },
+// }
+//
+
+bool RustProjectWriter::RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ const std::string& file_name,
+ bool quiet,
+ Err* err) {
+ SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
+ Value(nullptr, file_name), err);
+ if (output_file.is_null())
+ return false;
+
+ base::FilePath output_path = build_settings->GetFullPath(output_file);
+
+ std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
+
+ StringOutputBuffer out_buffer;
+ std::ostream out(&out_buffer);
+
+ RenderJSON(build_settings, all_targets, out);
+
+ if (out_buffer.ContentsEqual(output_path)) {
+ return true;
+ }
+
+ return out_buffer.WriteToFile(output_path, err);
+}
+
+// Map of Targets to their index in the crates list (for linking dependencies to
+// their indexes).
+using TargetIndexMap = std::unordered_map<const Target*, uint32_t>;
+
+// A collection of Targets.
+using TargetsVector = UniqueVector<const Target*>;
+
+// Get the Rust deps for a target, recursively expanding OutputType::GROUPS
+// that are present in the GN structure. This will return a flattened list of
+// deps from the groups, but will not expand a Rust lib dependency to find any
+// transitive Rust dependencies.
+void GetRustDeps(const Target* target, TargetsVector* rust_deps) {
+ for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
+ const Target* dep = pair.ptr;
+
+ if (dep->source_types_used().RustSourceUsed()) {
+ // Include any Rust dep.
+ rust_deps->push_back(dep);
+ } else if (dep->output_type() == Target::OutputType::GROUP) {
+ // Inspect (recursively) any group to see if it contains Rust deps.
+ GetRustDeps(dep, rust_deps);
+ }
+ }
+}
+TargetsVector GetRustDeps(const Target* target) {
+ TargetsVector deps;
+ GetRustDeps(target, &deps);
+ return deps;
+}
+
+std::vector<std::string> ExtractCompilerArgs(const Target* target) {
+ std::vector<std::string> args;
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ auto rustflags = iter.cur().rustflags();
+ for (auto flag_iter = rustflags.begin(); flag_iter != rustflags.end();
+ flag_iter++) {
+ args.push_back(*flag_iter);
+ }
+ }
+ return args;
+}
+
+std::optional<std::string> FindArgValue(const char* arg,
+ const std::vector<std::string>& args) {
+ for (auto current = args.begin(); current != args.end();) {
+ // capture the current value
+ auto previous = *current;
+ // and increment
+ current++;
+
+ // if the previous argument matches `arg`, and after the above increment the
+ // end hasn't been reached, this current argument is the desired value.
+ if (previous == arg && current != args.end()) {
+ return std::make_optional(*current);
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string> FindArgValueAfterPrefix(
+ const std::string& prefix,
+ const std::vector<std::string>& args) {
+ for (auto arg : args) {
+ if (!arg.compare(0, prefix.size(), prefix)) {
+ auto value = arg.substr(prefix.size());
+ return std::make_optional(value);
+ }
+ }
+ return std::nullopt;
+}
+
+std::vector<std::string> FindAllArgValuesAfterPrefix(
+ const std::string& prefix,
+ const std::vector<std::string>& args) {
+ std::vector<std::string> values;
+ for (auto arg : args) {
+ if (!arg.compare(0, prefix.size(), prefix)) {
+ auto value = arg.substr(prefix.size());
+ values.push_back(value);
+ }
+ }
+ return values;
+}
+
+// TODO(bwb) Parse sysroot structure from toml files. This is fragile and
+// might break if upstream changes the dependency structure.
+const std::string_view sysroot_crates[] = {"std",
+ "core",
+ "alloc",
+ "panic_unwind",
+ "proc_macro",
+ "test",
+ "panic_abort",
+ "unwind"};
+
+// Multiple sysroot crates have dependenices on each other. This provides a
+// mechanism for specifiying that in an extendible manner.
+const std::unordered_map<std::string_view, std::vector<std::string_view>>
+ sysroot_deps_map = {{"alloc", {"core"}},
+ {"std", {"alloc", "core", "panic_abort", "unwind"}}};
+
+// Add each of the crates a sysroot has, including their dependencies.
+void AddSysrootCrate(const BuildSettings* build_settings,
+ const std::string_view crate,
+ const std::string_view current_sysroot,
+ SysrootCrateIndexMap& sysroot_crate_lookup,
+ CrateList& crate_list) {
+ if (sysroot_crate_lookup.find(crate) != sysroot_crate_lookup.end()) {
+ // If this sysroot crate is already in the lookup, we don't add it again.
+ return;
+ }
+
+ // Add any crates that this sysroot crate depends on.
+ auto deps_lookup = sysroot_deps_map.find(crate);
+ if (deps_lookup != sysroot_deps_map.end()) {
+ auto deps = (*deps_lookup).second;
+ for (auto dep : deps) {
+ AddSysrootCrate(build_settings, dep, current_sysroot,
+ sysroot_crate_lookup, crate_list);
+ }
+ }
+
+ size_t crate_index = crate_list.size();
+ sysroot_crate_lookup.insert(std::make_pair(crate, crate_index));
+
+ base::FilePath rebased_out_dir =
+ build_settings->GetFullPath(build_settings->build_dir());
+ auto crate_path =
+ FilePathToUTF8(rebased_out_dir) + std::string(current_sysroot) +
+ "/lib/rustlib/src/rust/library/" + std::string(crate) + "/src/lib.rs";
+
+ Crate sysroot_crate =
+ Crate(SourceFile(crate_path), crate_index, std::string(crate), "2018");
+
+ sysroot_crate.AddConfigItem("debug_assertions");
+
+ if (deps_lookup != sysroot_deps_map.end()) {
+ auto deps = (*deps_lookup).second;
+ for (auto dep : deps) {
+ auto idx = sysroot_crate_lookup[dep];
+ sysroot_crate.AddDependency(idx, std::string(dep));
+ }
+ }
+
+ crate_list.push_back(sysroot_crate);
+}
+
+// Add the given sysroot to the project, if it hasn't already been added.
+void AddSysroot(const BuildSettings* build_settings,
+ const std::string_view sysroot,
+ SysrootIndexMap& sysroot_lookup,
+ CrateList& crate_list) {
+ // If this sysroot is already in the lookup, we don't add it again.
+ if (sysroot_lookup.find(sysroot) != sysroot_lookup.end()) {
+ return;
+ }
+
+ // Otherwise, add all of its crates
+ for (auto crate : sysroot_crates) {
+ AddSysrootCrate(build_settings, crate, sysroot, sysroot_lookup[sysroot],
+ crate_list);
+ }
+}
+
+void AddSysrootDependencyToCrate(Crate* crate,
+ const SysrootCrateIndexMap& sysroot,
+ const std::string_view crate_name) {
+ if (const auto crate_idx = sysroot.find(crate_name);
+ crate_idx != sysroot.end()) {
+ crate->AddDependency(crate_idx->second, std::string(crate_name));
+ }
+}
+
+void AddTarget(const BuildSettings* build_settings,
+ const Target* target,
+ TargetIndexMap& lookup,
+ SysrootIndexMap& sysroot_lookup,
+ CrateList& crate_list) {
+ if (lookup.find(target) != lookup.end()) {
+ // If target is already in the lookup, we don't add it again.
+ return;
+ }
+
+ auto compiler_args = ExtractCompilerArgs(target);
+ auto compiler_target = FindArgValue("--target", compiler_args);
+
+ // Check what sysroot this target needs. Add it to the crate list if it
+ // hasn't already been added.
+ auto rust_tool =
+ target->toolchain()->GetToolForTargetFinalOutputAsRust(target);
+ auto current_sysroot = rust_tool->GetSysroot();
+ if (current_sysroot != "" && sysroot_lookup.count(current_sysroot) == 0) {
+ AddSysroot(build_settings, current_sysroot, sysroot_lookup, crate_list);
+ }
+
+ auto crate_deps = GetRustDeps(target);
+
+ // Add all dependencies of this crate, before this crate.
+ for (const auto& dep : crate_deps) {
+ AddTarget(build_settings, dep, lookup, sysroot_lookup, crate_list);
+ }
+
+ // The index of a crate is its position (0-based) in the list of crates.
+ size_t crate_id = crate_list.size();
+
+ // Add the target to the crate lookup.
+ lookup.insert(std::make_pair(target, crate_id));
+
+ SourceFile crate_root = target->rust_values().crate_root();
+ std::string crate_label = target->label().GetUserVisibleName(false);
+
+ auto edition =
+ FindArgValueAfterPrefix(std::string("--edition="), compiler_args);
+ if (!edition.has_value()) {
+ edition = FindArgValue("--edition", compiler_args);
+ }
+
+ Crate crate =
+ Crate(crate_root, crate_id, crate_label, edition.value_or("2015"));
+
+ crate.SetCompilerArgs(compiler_args);
+ if (compiler_target.has_value())
+ crate.SetCompilerTarget(compiler_target.value());
+
+ ConfigList cfgs =
+ FindAllArgValuesAfterPrefix(std::string("--cfg="), compiler_args);
+
+ crate.AddConfigItem("test");
+ crate.AddConfigItem("debug_assertions");
+
+ for (auto& cfg : cfgs) {
+ crate.AddConfigItem(cfg);
+ }
+
+ // Add the sysroot dependencies, if there is one.
+ if (current_sysroot != "") {
+ const auto& sysroot = sysroot_lookup[current_sysroot];
+ AddSysrootDependencyToCrate(&crate, sysroot, "core");
+ AddSysrootDependencyToCrate(&crate, sysroot, "alloc");
+ AddSysrootDependencyToCrate(&crate, sysroot, "std");
+
+ // Proc macros have the proc_macro crate as a direct dependency
+ if (std::string_view(rust_tool->name()) ==
+ std::string_view(RustTool::kRsToolMacro)) {
+ AddSysrootDependencyToCrate(&crate, sysroot, "proc_macro");
+ }
+ }
+
+ // Add the rest of the crate dependencies.
+ for (const auto& dep : crate_deps) {
+ auto idx = lookup[dep];
+ crate.AddDependency(idx, dep->rust_values().crate_name());
+ }
+
+ crate_list.push_back(crate);
+}
+
+void WriteCrates(const BuildSettings* build_settings,
+ CrateList& crate_list,
+ std::ostream& rust_project) {
+ // produce a de-duplicated set of source roots:
+ std::set<std::string> roots;
+ for (auto& crate : crate_list) {
+ roots.insert(
+ FilePathToUTF8(build_settings->GetFullPath(crate.root().GetDir())));
+ }
+
+ rust_project << "{" NEWLINE;
+ rust_project << " \"roots\": [";
+ bool first_root = true;
+ for (auto& root : roots) {
+ if (!first_root)
+ rust_project << ",";
+ first_root = false;
+
+ rust_project << NEWLINE " \"" << root << "\"";
+ }
+ rust_project << NEWLINE " ]," NEWLINE;
+ rust_project << " \"crates\": [";
+ bool first_crate = true;
+ for (auto& crate : crate_list) {
+ if (!first_crate)
+ rust_project << ",";
+ first_crate = false;
+
+ auto crate_module =
+ FilePathToUTF8(build_settings->GetFullPath(crate.root()));
+
+ rust_project << NEWLINE << " {" NEWLINE
+ << " \"crate_id\": " << crate.index() << "," NEWLINE
+ << " \"root_module\": \"" << crate_module << "\"," NEWLINE
+ << " \"label\": \"" << crate.label() << "\"," NEWLINE;
+
+ auto compiler_target = crate.CompilerTarget();
+ if (compiler_target.has_value()) {
+ rust_project << " \"target\": \"" << compiler_target.value()
+ << "\"," NEWLINE;
+ }
+
+ auto compiler_args = crate.CompilerArgs();
+ if (!compiler_args.empty()) {
+ rust_project << " \"compiler_args\": [";
+ bool first_arg = true;
+ for (auto& arg : crate.CompilerArgs()) {
+ if (!first_arg)
+ rust_project << ", ";
+ first_arg = false;
+
+ std::string escaped_arg;
+ base::EscapeJSONString(arg, false, &escaped_arg);
+
+ rust_project << "\"" << escaped_arg << "\"";
+ }
+ rust_project << "]," << NEWLINE;
+ }
+
+ rust_project << " \"deps\": [";
+ bool first_dep = true;
+ for (auto& dep : crate.dependencies()) {
+ if (!first_dep)
+ rust_project << ",";
+ first_dep = false;
+
+ rust_project << NEWLINE << " {" NEWLINE
+ << " \"crate\": " << dep.first << "," NEWLINE
+ << " \"name\": \"" << dep.second << "\"" NEWLINE
+ << " }";
+ }
+ rust_project << NEWLINE " ]," NEWLINE; // end dep list
+
+ rust_project << " \"edition\": \"" << crate.edition() << "\"," NEWLINE;
+
+ rust_project << " \"cfg\": [";
+ bool first_cfg = true;
+ for (const auto& cfg : crate.configs()) {
+ if (!first_cfg)
+ rust_project << ",";
+ first_cfg = false;
+
+ std::string escaped_config;
+ base::EscapeJSONString(cfg, false, &escaped_config);
+
+ rust_project << NEWLINE;
+ rust_project << " \"" << escaped_config << "\"";
+ }
+ rust_project << NEWLINE;
+ rust_project << " ]" NEWLINE; // end cfgs
+
+ rust_project << " }"; // end crate
+ }
+ rust_project << NEWLINE " ]" NEWLINE; // end crate list
+ rust_project << "}" NEWLINE;
+}
+
+void RustProjectWriter::RenderJSON(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets,
+ std::ostream& rust_project) {
+ TargetIndexMap lookup;
+ SysrootIndexMap sysroot_lookup;
+ CrateList crate_list;
+
+ // All the crates defined in the project.
+ for (const auto* target : all_targets) {
+ if (!target->IsBinary() || !target->source_types_used().RustSourceUsed())
+ continue;
+
+ AddTarget(build_settings, target, lookup, sysroot_lookup, crate_list);
+ }
+
+ WriteCrates(build_settings, crate_list, rust_project);
+}
diff --git a/gn/src/gn/rust_project_writer.h b/gn/src/gn/rust_project_writer.h
new file mode 100644
index 00000000000..8140e062531
--- /dev/null
+++ b/gn/src/gn/rust_project_writer.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_RUST_PROJECT_WRITER_H_
+#define TOOLS_GN_RUST_PROJECT_WRITER_H_
+
+#include "gn/err.h"
+#include "gn/target.h"
+
+class Builder;
+class BuildSettings;
+
+// rust-project.json is an output format describing the rust build graph. It is
+// used by rust-analyzer (a LSP server), similiar to compile-commands.json.
+//
+// an example output is in rust_project_writer.cc
+class RustProjectWriter {
+ public:
+ // Write Rust build graph into a json file located by parameter file_name.
+ //
+ // Parameter quiet is not used.
+ static bool RunAndWriteFiles(const BuildSettings* build_setting,
+ const Builder& builder,
+ const std::string& file_name,
+ bool quiet,
+ Err* err);
+ static void RenderJSON(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets,
+ std::ostream& rust_project);
+
+ private:
+ // This fuction visits the deps graph of a target in a DFS fashion.
+ static void VisitDeps(const Target* target, std::set<const Target*>* visited);
+};
+
+#endif // TOOLS_GN_RUST_PROJECT_WRITER_H_
diff --git a/gn/src/gn/rust_project_writer_helpers.h b/gn/src/gn/rust_project_writer_helpers.h
new file mode 100644
index 00000000000..0020e2cfce0
--- /dev/null
+++ b/gn/src/gn/rust_project_writer_helpers.h
@@ -0,0 +1,140 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
+#define TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
+
+#include <fstream>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "build_settings.h"
+#include "gn/source_file.h"
+#include "gn/target.h"
+
+// These are internal types and helper functions for RustProjectWriter that have
+// been extracted for easier testability.
+
+// Crate Index in the generated file
+using CrateIndex = size_t;
+
+using ConfigList = std::vector<std::string>;
+using Dependency = std::pair<CrateIndex, std::string>;
+using DependencyList = std::vector<Dependency>;
+
+// This class represents a crate to be serialized out as part of the
+// rust-project.json file. This is used to separate the generating
+// of the data that needs to be in the file, from the file itself.
+class Crate {
+ public:
+ Crate(SourceFile root,
+ CrateIndex index,
+ std::string label,
+ std::string edition)
+ : root_(root), index_(index), label_(label), edition_(edition) {}
+
+ ~Crate() = default;
+
+ // Add a config item to the crate.
+ void AddConfigItem(std::string cfg_item) { configs_.push_back(cfg_item); }
+
+ // Add another crate as a dependency of this one.
+ void AddDependency(CrateIndex index, std::string name) {
+ deps_.push_back(std::make_pair(index, name));
+ }
+
+ // Set the compiler arguments used to invoke the compilation of this crate
+ void SetCompilerArgs(std::vector<std::string> args) { compiler_args_ = args; }
+
+ // Set the compiler target ("e.g. x86_64-linux-kernel")
+ void SetCompilerTarget(std::string target) { compiler_target_ = target; }
+
+ // Returns the root file for the crate.
+ SourceFile& root() { return root_; }
+
+ // Returns the crate index.
+ CrateIndex index() { return index_; };
+
+ // Returns the displayable crate label.
+ const std::string& label() { return label_; }
+
+ // Returns the Rust Edition this crate uses.
+ const std::string& edition() { return edition_; }
+
+ // Return the set of config items for this crate.
+ ConfigList& configs() { return configs_; }
+
+ // Return the set of dependencies for this crate.
+ DependencyList& dependencies() { return deps_; }
+
+ // Return the compiler arguments used to invoke the compilation of this crate
+ const std::vector<std::string>& CompilerArgs() { return compiler_args_; }
+
+ // Return the compiler target "triple" from the compiler args
+ const std::optional<std::string>& CompilerTarget() {
+ return compiler_target_;
+ }
+
+ private:
+ SourceFile root_;
+ CrateIndex index_;
+ std::string label_;
+ std::string edition_;
+ ConfigList configs_;
+ DependencyList deps_;
+ std::optional<std::string> compiler_target_;
+ std::vector<std::string> compiler_args_;
+};
+
+using CrateList = std::vector<Crate>;
+
+// Mapping of a sysroot crate (path) to it's index in the crates list.
+using SysrootCrateIndexMap = std::unordered_map<std::string_view, CrateIndex>;
+
+// Mapping of a sysroot (path) to the mapping of each of the sysroot crates to
+// their index in the crates list.
+using SysrootIndexMap =
+ std::unordered_map<std::string_view, SysrootCrateIndexMap>;
+
+// Add all of the crates for a sysroot (path) to the rust_project ostream.
+// Add the given sysroot to the project, if it hasn't already been added.
+void AddSysroot(const BuildSettings* build_settings,
+ const std::string_view sysroot,
+ SysrootIndexMap& sysroot_lookup,
+ CrateList& crate_list);
+
+// Write the entire rust-project.json file contents into the given stream, based
+// on the the given crates list.
+void WriteCrates(const BuildSettings* build_settings,
+ CrateList& crate_list,
+ std::ostream& rust_project);
+
+// Assemble the compiler arguments for the given GN Target.
+std::vector<std::string> ExtractCompilerArgs(const Target* target);
+
+// Find the value of an argument that's passed to the compiler as two
+// consecutive strings in the list of arguments: ["arg", "value"]
+std::optional<std::string> FindArgValue(const char* arg,
+ const std::vector<std::string>& args);
+
+// Find the first argument that matches the prefix, returning the value after
+// the prefix. e.g. ˝--arg=value", is returned as "value" if the prefix
+// "--arg=" is used.
+std::optional<std::string> FindArgValueAfterPrefix(
+ const std::string& prefix,
+ const std::vector<std::string>& args);
+
+// Find all arguments that match the given prefix, returning the value after
+// the prefix for each one. e.g. "--cfg=value" is returned as "value" if the
+// prefix "--cfg=" is used.
+std::vector<std::string> FindAllArgValuesAfterPrefix(
+ const std::string& prefix,
+ const std::vector<std::string>& args);
+
+
+#endif // TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
diff --git a/gn/src/gn/rust_project_writer_helpers_unittest.cc b/gn/src/gn/rust_project_writer_helpers_unittest.cc
new file mode 100644
index 00000000000..7ff3b487533
--- /dev/null
+++ b/gn/src/gn/rust_project_writer_helpers_unittest.cc
@@ -0,0 +1,359 @@
+// Copyright 2020 The Chromium 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 "gn/rust_project_writer_helpers.h"
+
+#include "base/strings/string_util.h"
+#include "gn/filesystem_utils.h"
+#include "gn/string_output_buffer.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+static void ExpectEqOrShowDiff(const char* expected,
+ const std::string& actual) {
+ if (expected != actual) {
+ printf("\nExpected: >>>\n%s<<<\n", expected);
+ printf(" Actual: >>>\n%s<<<\n", actual.c_str());
+ }
+ EXPECT_EQ(expected, actual);
+}
+
+using RustProjectWriterHelper = TestWithScheduler;
+
+TEST_F(RustProjectWriterHelper, WriteCrates) {
+ TestWithScope setup;
+
+ CrateList crates;
+ Crate dep =
+ Crate(SourceFile("/root/tortoise/lib.rs"), 0, "//tortoise:bar", "2015");
+ Crate target =
+ Crate(SourceFile("/root/hare/lib.rs"), 1, "//hare:bar", "2015");
+ target.AddDependency(0, "tortoise");
+ target.AddConfigItem("unix");
+ target.AddConfigItem("feature=\"test\"");
+
+ crates.push_back(dep);
+ crates.push_back(target);
+
+ std::ostringstream stream;
+ WriteCrates(setup.build_settings(), crates, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"/root/hare/\",\n"
+ " \"/root/tortoise/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"/root/tortoise/lib.rs\",\n"
+ " \"label\": \"//tortoise:bar\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 1,\n"
+ " \"root_module\": \"/root/hare/lib.rs\",\n"
+ " \"label\": \"//hare:bar\",\n"
+ " \"deps\": [\n"
+ " {\n"
+ " \"crate\": 0,\n"
+ " \"name\": \"tortoise\"\n"
+ " }\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"unix\",\n"
+ " \"feature=\\\"test\\\"\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectWriterHelper, SysrootDepsAreCorrect) {
+ TestWithScope setup;
+ setup.build_settings()->SetRootPath(UTF8ToFilePath("/root"));
+
+ SysrootIndexMap sysroot_lookup;
+ CrateList crates;
+
+ AddSysroot(setup.build_settings(), "sysroot", sysroot_lookup, crates);
+
+ std::ostringstream stream;
+ WriteCrates(setup.build_settings(), crates, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/alloc/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/core/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_abort/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_unwind/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/proc_macro/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/std/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/test/src/\",\n"
+ " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/unwind/src/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/core/src/lib.rs\",\n"
+ " \"label\": \"core\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 1,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/alloc/src/lib.rs\",\n"
+ " \"label\": \"alloc\",\n"
+ " \"deps\": [\n"
+ " {\n"
+ " \"crate\": 0,\n"
+ " \"name\": \"core\"\n"
+ " }\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 2,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_abort/src/lib.rs\",\n"
+ " \"label\": \"panic_abort\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 3,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/unwind/src/lib.rs\",\n"
+ " \"label\": \"unwind\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 4,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/std/src/lib.rs\",\n"
+ " \"label\": \"std\",\n"
+ " \"deps\": [\n"
+ " {\n"
+ " \"crate\": 1,\n"
+ " \"name\": \"alloc\"\n"
+ " },\n"
+ " {\n"
+ " \"crate\": 0,\n"
+ " \"name\": \"core\"\n"
+ " },\n"
+ " {\n"
+ " \"crate\": 2,\n"
+ " \"name\": \"panic_abort\"\n"
+ " },\n"
+ " {\n"
+ " \"crate\": 3,\n"
+ " \"name\": \"unwind\"\n"
+ " }\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 5,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_unwind/src/lib.rs\",\n"
+ " \"label\": \"panic_unwind\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 6,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/proc_macro/src/lib.rs\",\n"
+ " \"label\": \"proc_macro\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 7,\n"
+ " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/test/src/lib.rs\",\n"
+ " \"label\": \"test\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleSimple) {
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.config_values().rustflags().push_back("--target");
+ target.config_values().rustflags().push_back("x86-someos");
+ target.config_values().rustflags().push_back("--edition=2018");
+
+ auto args = ExtractCompilerArgs(&target);
+ std::optional<std::string> result = FindArgValue("--target", args);
+ auto expected = std::optional<std::string>{"x86-someos"};
+ EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleMissing) {
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back(
+ "--cfg=featur4e=\"foo_enabled\"");
+ target.config_values().rustflags().push_back("x86-someos");
+ target.config_values().rustflags().push_back("--edition=2018");
+
+ auto args = ExtractCompilerArgs(&target);
+ std::optional<std::string> result = FindArgValue("--target", args);
+ auto expected = std::nullopt;
+ EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleDontFallOffEnd) {
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.config_values().rustflags().push_back("--edition=2018");
+ target.config_values().rustflags().push_back("--target");
+
+ auto args = ExtractCompilerArgs(&target);
+ std::optional<std::string> result = FindArgValue("--target", args);
+ auto expected = std::nullopt;
+ EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractFirstArgValueWithPrefixMissing) {
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.config_values().rustflags().push_back("--edition=2018");
+ target.config_values().rustflags().push_back("--target");
+
+ auto args = ExtractCompilerArgs(&target);
+ std::optional<std::string> result =
+ FindArgValueAfterPrefix("--missing", args);
+ auto expected = std::nullopt;
+ EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractFirstArgValueWithPrefix) {
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.config_values().rustflags().push_back("--edition=2018");
+ target.config_values().rustflags().push_back("--target");
+
+ auto args = ExtractCompilerArgs(&target);
+ std::optional<std::string> result =
+ FindArgValueAfterPrefix("--edition=", args);
+ auto expected = std::optional<std::string>{"2018"};
+ EXPECT_EQ(expected, result);
+}
+
+TEST_F(RustProjectWriterHelper, ExtractAllArgValueWithPrefix) {
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.config_values().rustflags().push_back("--edition=2018");
+ target.config_values().rustflags().push_back("--cfg=feature=\"bar_enabled\"");
+ target.config_values().rustflags().push_back("--target");
+
+ auto args = ExtractCompilerArgs(&target);
+ std::vector<std::string> result = FindAllArgValuesAfterPrefix("--cfg=", args);
+ std::vector<std::string> expected = {"feature=\"foo_enabled\"",
+ "feature=\"bar_enabled\""};
+ EXPECT_EQ(expected, result);
+} \ No newline at end of file
diff --git a/gn/src/gn/rust_project_writer_unittest.cc b/gn/src/gn/rust_project_writer_unittest.cc
new file mode 100644
index 00000000000..675f9a6a387
--- /dev/null
+++ b/gn/src/gn/rust_project_writer_unittest.cc
@@ -0,0 +1,524 @@
+// Copyright 2020 The Chromium 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 "gn/rust_project_writer.h"
+#include "base/strings/string_util.h"
+#include "base/files/file_path.h"
+#include "gn/filesystem_utils.h"
+#include "gn/substitution_list.h"
+#include "gn/target.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+
+static void ExpectEqOrShowDiff(const char* expected, const std::string& actual) {
+ if(expected != actual) {
+ printf("\nExpected: >>>\n%s<<<\n", expected);
+ printf(" Actual: >>>\n%s<<<\n", actual.c_str());
+ }
+ EXPECT_EQ(expected, actual);
+}
+
+using RustProjectJSONWriter = TestWithScheduler;
+
+TEST_F(RustProjectJSONWriter, OneRustTarget) {
+ Err err;
+ TestWithScope setup;
+ setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"path/foo/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"path/foo/lib.rs\",\n"
+ " \"label\": \"//foo:bar\",\n"
+ " \"compiler_args\": [\"--cfg=feature=\\\"foo_enabled\\\"\"],\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\",\n"
+ " \"feature=\\\"foo_enabled\\\"\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, RustTargetDep) {
+ Err err;
+ TestWithScope setup;
+
+ Target dep(setup.settings(), Label(SourceDir("//tortoise/"), "bar"));
+ dep.set_output_type(Target::RUST_LIBRARY);
+ dep.visibility().SetPublic();
+ SourceFile tlib("//tortoise/lib.rs");
+ dep.sources().push_back(tlib);
+ dep.source_types_used().Set(SourceFile::SOURCE_RS);
+ dep.rust_values().set_crate_root(tlib);
+ dep.rust_values().crate_name() = "tortoise";
+ dep.SetToolchain(setup.toolchain());
+
+ Target target(setup.settings(), Label(SourceDir("//hare/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile harelib("//hare/lib.rs");
+ target.sources().push_back(harelib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(harelib);
+ target.rust_values().crate_name() = "hare";
+ target.public_deps().push_back(LabelTargetPair(&dep));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"hare/\",\n"
+ " \"tortoise/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"tortoise/lib.rs\",\n"
+ " \"label\": \"//tortoise:bar\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 1,\n"
+ " \"root_module\": \"hare/lib.rs\",\n"
+ " \"label\": \"//hare:bar\",\n"
+ " \"deps\": [\n"
+ " {\n"
+ " \"crate\": 0,\n"
+ " \"name\": \"tortoise\"\n"
+ " }\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, RustTargetDepTwo) {
+ Err err;
+ TestWithScope setup;
+
+ Target dep(setup.settings(), Label(SourceDir("//tortoise/"), "bar"));
+ dep.set_output_type(Target::RUST_LIBRARY);
+ dep.visibility().SetPublic();
+ SourceFile tlib("//tortoise/lib.rs");
+ dep.sources().push_back(tlib);
+ dep.source_types_used().Set(SourceFile::SOURCE_RS);
+ dep.rust_values().set_crate_root(tlib);
+ dep.rust_values().crate_name() = "tortoise";
+ dep.SetToolchain(setup.toolchain());
+
+ Target dep2(setup.settings(), Label(SourceDir("//achilles/"), "bar"));
+ dep2.set_output_type(Target::RUST_LIBRARY);
+ dep2.visibility().SetPublic();
+ SourceFile alib("//achilles/lib.rs");
+ dep2.sources().push_back(alib);
+ dep2.source_types_used().Set(SourceFile::SOURCE_RS);
+ dep2.rust_values().set_crate_root(alib);
+ dep2.rust_values().crate_name() = "achilles";
+ dep2.SetToolchain(setup.toolchain());
+
+ Target target(setup.settings(), Label(SourceDir("//hare/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile harelib("//hare/lib.rs");
+ target.sources().push_back(harelib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(harelib);
+ target.rust_values().crate_name() = "hare";
+ target.public_deps().push_back(LabelTargetPair(&dep));
+ target.public_deps().push_back(LabelTargetPair(&dep2));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"achilles/\",\n"
+ " \"hare/\",\n"
+ " \"tortoise/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"tortoise/lib.rs\",\n"
+ " \"label\": \"//tortoise:bar\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 1,\n"
+ " \"root_module\": \"achilles/lib.rs\",\n"
+ " \"label\": \"//achilles:bar\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 2,\n"
+ " \"root_module\": \"hare/lib.rs\",\n"
+ " \"label\": \"//hare:bar\",\n"
+ " \"deps\": [\n"
+ " {\n"
+ " \"crate\": 0,\n"
+ " \"name\": \"tortoise\"\n"
+ " },\n"
+ " {\n"
+ " \"crate\": 1,\n"
+ " \"name\": \"achilles\"\n"
+ " }\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+// Test that when outputting dependencies, only Rust deps are returned,
+// and that any groups are inspected to see if they include Rust deps.
+TEST_F(RustProjectJSONWriter, RustTargetGetDepRustOnly) {
+ Err err;
+ TestWithScope setup;
+
+ Target dep(setup.settings(), Label(SourceDir("//tortoise/"), "bar"));
+ dep.set_output_type(Target::RUST_LIBRARY);
+ dep.visibility().SetPublic();
+ SourceFile tlib("//tortoise/lib.rs");
+ dep.sources().push_back(tlib);
+ dep.source_types_used().Set(SourceFile::SOURCE_RS);
+ dep.rust_values().set_crate_root(tlib);
+ dep.rust_values().crate_name() = "tortoise";
+ dep.SetToolchain(setup.toolchain());
+
+ Target dep2(setup.settings(), Label(SourceDir("//achilles/"), "bar"));
+ dep2.set_output_type(Target::STATIC_LIBRARY);
+ dep2.visibility().SetPublic();
+ SourceFile alib("//achilles/lib.o");
+ dep2.SetToolchain(setup.toolchain());
+
+ Target dep3(setup.settings(), Label(SourceDir("//achilles/"), "group"));
+ dep3.set_output_type(Target::GROUP);
+ dep3.visibility().SetPublic();
+ dep3.public_deps().push_back(LabelTargetPair(&dep));
+ dep3.SetToolchain(setup.toolchain());
+
+ Target dep4(setup.settings(), Label(SourceDir("//tortoise/"), "macro"));
+ dep4.set_output_type(Target::RUST_PROC_MACRO);
+ dep4.visibility().SetPublic();
+ SourceFile tmlib("//tortoise/macro/lib.rs");
+ dep4.sources().push_back(tmlib);
+ dep4.source_types_used().Set(SourceFile::SOURCE_RS);
+ dep4.rust_values().set_crate_root(tmlib);
+ dep4.rust_values().crate_name() = "tortoise_macro";
+ dep4.SetToolchain(setup.toolchain());
+
+ Target target(setup.settings(), Label(SourceDir("//hare/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile harelib("//hare/lib.rs");
+ target.sources().push_back(harelib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(harelib);
+ target.rust_values().crate_name() = "hare";
+ target.public_deps().push_back(LabelTargetPair(&dep));
+ target.public_deps().push_back(LabelTargetPair(&dep3));
+ target.public_deps().push_back(LabelTargetPair(&dep4));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"hare/\",\n"
+ " \"tortoise/\",\n"
+ " \"tortoise/macro/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"tortoise/lib.rs\",\n"
+ " \"label\": \"//tortoise:bar\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 1,\n"
+ " \"root_module\": \"tortoise/macro/lib.rs\",\n"
+ " \"label\": \"//tortoise:macro\",\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " },\n"
+ " {\n"
+ " \"crate_id\": 2,\n"
+ " \"root_module\": \"hare/lib.rs\",\n"
+ " \"label\": \"//hare:bar\",\n"
+ " \"deps\": [\n"
+ " {\n"
+ " \"crate\": 0,\n"
+ " \"name\": \"tortoise\"\n"
+ " },\n"
+ " {\n"
+ " \"crate\": 1,\n"
+ " \"name\": \"tortoise_macro\"\n"
+ " }\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, OneRustTargetWithRustcTargetSet) {
+ Err err;
+ TestWithScope setup;
+ setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--target");
+ target.config_values().rustflags().push_back("x86-64_unknown");
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"path/foo/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"path/foo/lib.rs\",\n"
+ " \"label\": \"//foo:bar\",\n"
+ " \"target\": \"x86-64_unknown\",\n"
+ " \"compiler_args\": [\"--target\", \"x86-64_unknown\"],\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSet) {
+ Err err;
+ TestWithScope setup;
+ setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--edition=2018");
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"path/foo/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"path/foo/lib.rs\",\n"
+ " \"label\": \"//foo:bar\",\n"
+ " \"compiler_args\": [\"--edition=2018\"],\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
+
+TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSetAlternate) {
+ Err err;
+ TestWithScope setup;
+ setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_LIBRARY);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--edition");
+ target.config_values().rustflags().push_back("2018");
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"roots\": [\n"
+ " \"path/foo/\"\n"
+ " ],\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"path/foo/lib.rs\",\n"
+ " \"label\": \"//foo:bar\",\n"
+ " \"compiler_args\": [\"--edition\", \"2018\"],\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2018\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\"\n"
+ " ]\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+} \ No newline at end of file
diff --git a/gn/src/gn/rust_substitution_type.cc b/gn/src/gn/rust_substitution_type.cc
new file mode 100644
index 00000000000..c4170196e92
--- /dev/null
+++ b/gn/src/gn/rust_substitution_type.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium 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 "gn/rust_substitution_type.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "gn/err.h"
+#include "gn/substitution_type.h"
+
+const SubstitutionTypes RustSubstitutions = {
+ &kRustSubstitutionCrateName, &kRustSubstitutionCrateType,
+ &kRustSubstitutionRustDeps, &kRustSubstitutionRustFlags,
+ &kRustSubstitutionRustEnv, &kRustSubstitutionExterns,
+ &kRustSubstitutionSources,
+};
+
+// Valid for Rust tools.
+const Substitution kRustSubstitutionCrateName = {"{{crate_name}}",
+ "crate_name"};
+const Substitution kRustSubstitutionCrateType = {"{{crate_type}}",
+ "crate_type"};
+const Substitution kRustSubstitutionExterns = {"{{externs}}", "externs"};
+const Substitution kRustSubstitutionRustDeps = {"{{rustdeps}}", "rustdeps"};
+const Substitution kRustSubstitutionRustEnv = {"{{rustenv}}", "rustenv"};
+const Substitution kRustSubstitutionRustFlags = {"{{rustflags}}", "rustflags"};
+const Substitution kRustSubstitutionSources = {"{{sources}}", "sources"};
+
+bool IsValidRustSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
+ type == &SubstitutionOutputDir ||
+ type == &SubstitutionOutputExtension ||
+ type == &kRustSubstitutionCrateName ||
+ type == &kRustSubstitutionCrateType ||
+ type == &kRustSubstitutionExterns ||
+ type == &kRustSubstitutionRustDeps ||
+ type == &kRustSubstitutionRustEnv ||
+ type == &kRustSubstitutionRustFlags ||
+ type == &kRustSubstitutionSources;
+}
diff --git a/gn/src/gn/rust_substitution_type.h b/gn/src/gn/rust_substitution_type.h
new file mode 100644
index 00000000000..2eeb7dc763a
--- /dev/null
+++ b/gn/src/gn/rust_substitution_type.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
+#define TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
+
+#include <set>
+#include <vector>
+
+#include "gn/substitution_type.h"
+
+// The set of substitutions available to Rust tools.
+extern const SubstitutionTypes RustSubstitutions;
+
+// Valid for Rust tools.
+extern const Substitution kRustSubstitutionCrateName;
+extern const Substitution kRustSubstitutionCrateType;
+extern const Substitution kRustSubstitutionExterns;
+extern const Substitution kRustSubstitutionRustDeps;
+extern const Substitution kRustSubstitutionRustEnv;
+extern const Substitution kRustSubstitutionRustFlags;
+extern const Substitution kRustSubstitutionSources;
+
+bool IsValidRustSubstitution(const Substitution* type);
+
+#endif // TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
diff --git a/gn/src/gn/rust_tool.cc b/gn/src/gn/rust_tool.cc
new file mode 100644
index 00000000000..0b8921f08f6
--- /dev/null
+++ b/gn/src/gn/rust_tool.cc
@@ -0,0 +1,123 @@
+// Copyright 2019 The Chromium 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 "gn/rust_tool.h"
+
+#include "gn/rust_substitution_type.h"
+#include "gn/target.h"
+
+const char* RustTool::kRsToolBin = "rust_bin";
+const char* RustTool::kRsToolCDylib = "rust_cdylib";
+const char* RustTool::kRsToolDylib = "rust_dylib";
+const char* RustTool::kRsToolMacro = "rust_macro";
+const char* RustTool::kRsToolRlib = "rust_rlib";
+const char* RustTool::kRsToolStaticlib = "rust_staticlib";
+
+RustTool::RustTool(const char* n) : Tool(n) {
+ CHECK(ValidateName(n));
+ // TODO: should these be settable in toolchain definition?
+ set_framework_switch("-lframework=");
+ set_lib_dir_switch("-Lnative=");
+ set_lib_switch("-l");
+ set_linker_arg("-Clink-arg=");
+}
+
+RustTool::~RustTool() = default;
+
+RustTool* RustTool::AsRust() {
+ return this;
+}
+const RustTool* RustTool::AsRust() const {
+ return this;
+}
+
+bool RustTool::ValidateName(const char* name) const {
+ return name == kRsToolBin || name == kRsToolCDylib || name == kRsToolDylib ||
+ name == kRsToolMacro || name == kRsToolRlib ||
+ name == kRsToolStaticlib;
+}
+
+void RustTool::SetComplete() {
+ SetToolComplete();
+}
+
+std::string_view RustTool::GetSysroot() const {
+ return rust_sysroot_;
+}
+
+bool RustTool::SetOutputExtension(const Value* value,
+ std::string* var,
+ Err* err) {
+ DCHECK(!complete_);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+ if (value->string_value().empty())
+ return true;
+
+ *var = std::move(value->string_value());
+ return true;
+}
+
+bool RustTool::ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ SubstitutionList list;
+ if (!list.Parse(*value, err))
+ return false;
+
+ // Validate the right kinds of patterns are used.
+ if (list.list().empty()) {
+ *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
+ return false;
+ }
+
+ for (const auto& cur_type : list.required_types()) {
+ if (!IsValidRustSubstitution(cur_type)) {
+ *err = Err(*value, "Pattern not valid here.",
+ "You used the pattern " + std::string(cur_type->name) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+
+ *field = std::move(list);
+ return true;
+}
+
+bool RustTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ if (!Tool::InitTool(scope, toolchain, err)) {
+ return false;
+ }
+
+ // All Rust tools should have outputs.
+ if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
+ return false;
+ }
+
+ // Check for a sysroot. Sets an empty string when not explicitly set.
+ if (!ReadString(scope, "rust_sysroot", &rust_sysroot_, err)) {
+ return false;
+ }
+ return true;
+}
+
+bool RustTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kRsToolBin || name_ == kRsToolCDylib || name_ == kRsToolDylib ||
+ name_ == kRsToolMacro || name_ == kRsToolRlib ||
+ name_ == kRsToolStaticlib)
+ return IsValidRustSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
diff --git a/gn/src/gn/rust_tool.h b/gn/src/gn/rust_tool.h
new file mode 100644
index 00000000000..6a4fdf7bf91
--- /dev/null
+++ b/gn/src/gn/rust_tool.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_TOOL_H_
+#define TOOLS_GN_RUST_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/label.h"
+#include "gn/label_ptr.h"
+#include "gn/rust_values.h"
+#include "gn/source_file.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+#include "gn/target.h"
+#include "gn/tool.h"
+
+class RustTool : public Tool {
+ public:
+ // Rust tools
+ static const char* kRsToolBin;
+ static const char* kRsToolCDylib;
+ static const char* kRsToolDylib;
+ static const char* kRsToolMacro;
+ static const char* kRsToolRlib;
+ static const char* kRsToolStaticlib;
+
+ explicit RustTool(const char* n);
+ ~RustTool();
+
+ // Manual RTTI and required functions ---------------------------------------
+
+ bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+ bool ValidateName(const char* name) const override;
+ void SetComplete() override;
+ bool ValidateSubstitution(const Substitution* sub_type) const override;
+
+ RustTool* AsRust() override;
+ const RustTool* AsRust() const override;
+
+ std::string_view GetSysroot() const;
+
+ private:
+ std::string rust_sysroot_;
+
+ bool SetOutputExtension(const Value* value, std::string* var, Err* err);
+ bool ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err);
+
+ DISALLOW_COPY_AND_ASSIGN(RustTool);
+};
+
+#endif // TOOLS_GN_RUST_TOOL_H_
diff --git a/gn/src/gn/rust_values.cc b/gn/src/gn/rust_values.cc
new file mode 100644
index 00000000000..ed314ebfb87
--- /dev/null
+++ b/gn/src/gn/rust_values.cc
@@ -0,0 +1,9 @@
+// Copyright 2019 The Chromium 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 "gn/rust_values.h"
+
+RustValues::RustValues() : crate_type_(RustValues::CRATE_AUTO) {}
+
+RustValues::~RustValues() = default; \ No newline at end of file
diff --git a/gn/src/gn/rust_values.h b/gn/src/gn/rust_values.h
new file mode 100644
index 00000000000..8f7fff62c6d
--- /dev/null
+++ b/gn/src/gn/rust_values.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_TARGET_VALUES_H_
+#define TOOLS_GN_RUST_TARGET_VALUES_H_
+
+#include <map>
+
+#include "base/containers/flat_map.h"
+#include "gn/inherited_libraries.h"
+#include "gn/label.h"
+#include "gn/source_file.h"
+
+// Holds the values (outputs, args, script name, etc.) for either an action or
+// an action_foreach target.
+class RustValues {
+ public:
+ RustValues();
+ ~RustValues();
+
+ // Library crate types are specified here. Shared library crate types must be
+ // specified, all other crate types can be automatically deduced from the
+ // target type (e.g. executables use crate_type = "bin", static_libraries use
+ // crate_type = "staticlib") unless explicitly set.
+ enum CrateType {
+ CRATE_AUTO = 0,
+ CRATE_BIN,
+ CRATE_CDYLIB,
+ CRATE_DYLIB,
+ CRATE_PROC_MACRO,
+ CRATE_RLIB,
+ CRATE_STATICLIB,
+ };
+
+ // Name of this crate.
+ std::string& crate_name() { return crate_name_; }
+ const std::string& crate_name() const { return crate_name_; }
+
+ // Main source file for this crate.
+ const SourceFile& crate_root() const { return crate_root_; }
+ void set_crate_root(SourceFile& s) { crate_root_ = s; }
+
+ // Crate type for compilation.
+ CrateType crate_type() const { return crate_type_; }
+ void set_crate_type(CrateType s) { crate_type_ = s; }
+
+ // Any renamed dependencies for the `extern` flags.
+ const std::map<Label, std::string>& aliased_deps() const {
+ return aliased_deps_;
+ }
+ std::map<Label, std::string>& aliased_deps() { return aliased_deps_; }
+
+ // Transitive closure of libraries that are depended on by this target
+ InheritedLibraries& transitive_libs() { return rust_libs_; }
+ const InheritedLibraries& transitive_libs() const { return rust_libs_; }
+
+ private:
+ std::string crate_name_;
+ SourceFile crate_root_;
+ CrateType crate_type_ = CRATE_AUTO;
+ std::map<Label, std::string> aliased_deps_;
+ InheritedLibraries rust_libs_;
+
+ DISALLOW_COPY_AND_ASSIGN(RustValues);
+};
+
+#endif // TOOLS_GN_RUST_TARGET_VALUES_H_
diff --git a/gn/src/gn/rust_values_generator.cc b/gn/src/gn/rust_values_generator.cc
new file mode 100644
index 00000000000..e71aff68b5c
--- /dev/null
+++ b/gn/src/gn/rust_values_generator.cc
@@ -0,0 +1,191 @@
+// Copyright 2019 The Chromium 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 "gn/rust_values_generator.h"
+
+#include "gn/config_values_generator.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/rust_variables.h"
+#include "gn/scope.h"
+#include "gn/target.h"
+#include "gn/value_extractors.h"
+
+static const char* kRustSupportedCrateTypesError =
+ "\"crate_type\" must be one of \"bin\", \"cdylib\", \"dylib\", or "
+ "\"proc-macro\", \"rlib\", \"staticlib\".";
+
+RustValuesGenerator::RustValuesGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : target_(target),
+ scope_(scope),
+ function_call_(function_call),
+ err_(err) {}
+
+RustValuesGenerator::~RustValuesGenerator() = default;
+
+void RustValuesGenerator::Run() {
+ // source_set targets don't need any special Rust handling.
+ if (target_->output_type() == Target::SOURCE_SET)
+ return;
+
+ // Check that this type of target is Rust-supported.
+ if (target_->output_type() != Target::EXECUTABLE &&
+ target_->output_type() != Target::SHARED_LIBRARY &&
+ target_->output_type() != Target::RUST_LIBRARY &&
+ target_->output_type() != Target::RUST_PROC_MACRO &&
+ target_->output_type() != Target::STATIC_LIBRARY &&
+ target_->output_type() != Target::LOADABLE_MODULE) {
+ // Only valid rust output types.
+ *err_ = Err(function_call_,
+ "Target type \"" +
+ std::string(Target::GetStringForOutputType(
+ target_->output_type())) +
+ "\" is not supported for Rust compilation.",
+ "Supported target types are \"executable\", \"loadable_module\""
+ "\"shared_library\", \"static_library\", or \"source_set\".");
+ return;
+ }
+
+ if (!FillCrateName())
+ return;
+
+ if (!FillCrateType())
+ return;
+
+ if (!FillCrateRoot())
+ return;
+
+ if (!FillAliasedDeps())
+ return;
+}
+
+bool RustValuesGenerator::FillCrateName() {
+ const Value* value = scope_->GetValue(variables::kRustCrateName, true);
+ if (!value) {
+ // The target name will be used.
+ target_->rust_values().crate_name() = target_->label().name();
+ return true;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ target_->rust_values().crate_name() = std::move(value->string_value());
+ return true;
+}
+
+bool RustValuesGenerator::FillCrateType() {
+ const Value* value = scope_->GetValue(variables::kRustCrateType, true);
+ if (!value) {
+ // Require shared_library and loadable_module targets to tell us what
+ // they want.
+ if (target_->output_type() == Target::SHARED_LIBRARY ||
+ target_->output_type() == Target::LOADABLE_MODULE) {
+ *err_ = Err(function_call_,
+ "Must set \"crate_type\" on a Rust \"shared_library\".",
+ kRustSupportedCrateTypesError);
+ return false;
+ }
+
+ return true;
+ }
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ if (value->string_value() == "bin") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_BIN);
+ return true;
+ }
+ if (value->string_value() == "cdylib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
+ return true;
+ }
+ if (value->string_value() == "dylib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_DYLIB);
+ return true;
+ }
+ if (value->string_value() == "proc-macro") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ return true;
+ }
+ if (value->string_value() == "rlib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_RLIB);
+ return true;
+ }
+ if (value->string_value() == "staticlib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_STATICLIB);
+ return true;
+ }
+
+ *err_ = Err(value->origin(),
+ "Inadmissible crate type \"" + value->string_value() + "\".",
+ kRustSupportedCrateTypesError);
+ return false;
+}
+
+bool RustValuesGenerator::FillCrateRoot() {
+ const Value* value = scope_->GetValue(variables::kRustCrateRoot, true);
+ if (!value) {
+ // If there's only one source, use that.
+ if (target_->sources().size() == 1) {
+ target_->rust_values().set_crate_root(target_->sources()[0]);
+ return true;
+ }
+ // Otherwise, see if "lib.rs" or "main.rs" (as relevant) are in sources.
+ std::string to_find =
+ target_->output_type() == Target::EXECUTABLE ? "main.rs" : "lib.rs";
+ for (auto& source : target_->sources()) {
+ if (source.GetName() == to_find) {
+ target_->rust_values().set_crate_root(source);
+ return true;
+ }
+ }
+ *err_ = Err(function_call_, "Missing \"crate_root\" and missing \"" +
+ to_find + "\" in sources.");
+ return false;
+ }
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ SourceFile dest;
+ if (!ExtractRelativeFile(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &dest, err_))
+ return false;
+
+ target_->rust_values().set_crate_root(dest);
+ return true;
+}
+
+bool RustValuesGenerator::FillAliasedDeps() {
+ const Value* value = scope_->GetValue(variables::kRustAliasedDeps, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::SCOPE, err_))
+ return false;
+
+ Scope::KeyValueMap aliased_deps;
+ value->scope_value()->GetCurrentScopeValues(&aliased_deps);
+ for (const auto& pair : aliased_deps) {
+ Label dep_label =
+ Label::Resolve(scope_->GetSourceDir(),
+ scope_->settings()->build_settings()->root_path_utf8(),
+ ToolchainLabelForScope(scope_), pair.second, err_);
+
+ if (err_->has_error())
+ return false;
+
+ // Insert into the aliased_deps map.
+ target_->rust_values().aliased_deps().emplace(std::move(dep_label),
+ pair.first);
+ }
+
+ return true;
+}
diff --git a/gn/src/gn/rust_values_generator.h b/gn/src/gn/rust_values_generator.h
new file mode 100644
index 00000000000..d902978a12d
--- /dev/null
+++ b/gn/src/gn/rust_values_generator.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_VALUES_GENERATOR_H_
+#define TOOLS_GN_RUST_VALUES_GENERATOR_H_
+
+#include "base/macros.h"
+#include "gn/target.h"
+
+class FunctionCallNode;
+
+// Collects and writes specified data.
+class RustValuesGenerator {
+ public:
+ RustValuesGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ ~RustValuesGenerator();
+
+ void Run();
+
+ private:
+ bool FillCrateName();
+ bool FillCrateRoot();
+ bool FillCrateType();
+ bool FillEdition();
+ bool FillAliasedDeps();
+
+ Target* target_;
+ Scope* scope_;
+ const FunctionCallNode* function_call_;
+ Err* err_;
+
+ DISALLOW_COPY_AND_ASSIGN(RustValuesGenerator);
+};
+
+#endif // TOOLS_GN_RUST_VALUES_GENERATOR_H_
diff --git a/gn/src/gn/rust_variables.cc b/gn/src/gn/rust_variables.cc
new file mode 100644
index 00000000000..651eb075944
--- /dev/null
+++ b/gn/src/gn/rust_variables.cc
@@ -0,0 +1,113 @@
+// Copyright 2019 The Chromium 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 "gn/rust_variables.h"
+
+namespace variables {
+
+// Rust target variables ------------------------------------------------------
+
+const char kRustAliasedDeps[] = "aliased_deps";
+const char kRustAliasedDeps_HelpShort[] =
+ "aliased_deps: [scope] Set of crate-dependency pairs.";
+const char kRustAliasedDeps_Help[] =
+ R"(aliased_deps: [scope] Set of crate-dependency pairs.
+
+ Valid for `rust_library` targets and `executable`, `static_library`, and
+ `shared_library` targets that contain Rust sources.
+
+ A scope, each key indicating the renamed crate and the corresponding value
+ specifying the label of the dependency producing the relevant binary.
+
+ All dependencies listed in this field *must* be listed as deps of the target.
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ deps = [ "//bar" ]
+ }
+
+ This target would compile the `foo` crate with the following `extern` flag:
+ `rustc ...command... --extern bar=<build_out_dir>/obj/bar`
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ deps = [ ":bar" ]
+ aliased_deps = {
+ bar_renamed = ":bar"
+ }
+ }
+
+ With the addition of `aliased_deps`, above target would instead compile with:
+ `rustc ...command... --extern bar_renamed=<build_out_dir>/obj/bar`
+)";
+
+const char kRustCrateName[] = "crate_name";
+const char kRustCrateName_HelpShort[] =
+ "crate_name: [string] The name for the compiled crate.";
+const char kRustCrateName_Help[] =
+ R"(crate_name: [string] The name for the compiled crate.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ If crate_name is not set, then this rule will use the target name.
+)";
+
+const char kRustCrateType[] = "crate_type";
+const char kRustCrateType_HelpShort[] =
+ "crate_type: [string] The type of linkage to use on a shared_library.";
+const char kRustCrateType_Help[] =
+ R"(crate_type: [string] The type of linkage to use on a shared_library.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ Options for this field are "cdylib", "staticlib", "proc-macro", and "dylib".
+ This field sets the `crate-type` attribute for the `rustc` tool on static
+ libraries, as well as the appropriate output extension in the
+ `rust_output_extension` attribute. Since outputs must be explicit, the `lib`
+ crate type (where the Rust compiler produces what it thinks is the
+ appropriate library type) is not supported.
+
+ It should be noted that the "dylib" crate type in Rust is unstable in the set
+ of symbols it exposes, and most usages today are potentially wrong and will
+ be broken in the future.
+
+ Static libraries, rust libraries, and executables have this field set
+ automatically.
+)";
+
+const char kRustCrateRoot[] = "crate_root";
+const char kRustCrateRoot_HelpShort[] =
+ "crate_root: [string] The root source file for a binary or library.";
+const char kRustCrateRoot_Help[] =
+ R"(crate_root: [string] The root source file for a binary or library.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ This file is usually the `main.rs` or `lib.rs` for binaries and libraries,
+ respectively.
+
+ If crate_root is not set, then this rule will look for a lib.rs file (or
+ main.rs for executable) or a single file in sources, if sources contains
+ only one file.
+)";
+
+void InsertRustVariables(VariableInfoMap* info_map) {
+ info_map->insert(std::make_pair(
+ kRustAliasedDeps,
+ VariableInfo(kRustAliasedDeps_HelpShort, kRustAliasedDeps_Help)));
+ info_map->insert(std::make_pair(
+ kRustCrateName,
+ VariableInfo(kRustCrateName_HelpShort, kRustCrateName_Help)));
+ info_map->insert(std::make_pair(
+ kRustCrateType,
+ VariableInfo(kRustCrateType_HelpShort, kRustCrateType_Help)));
+ info_map->insert(std::make_pair(
+ kRustCrateRoot,
+ VariableInfo(kRustCrateRoot_HelpShort, kRustCrateRoot_Help)));
+}
+
+} // namespace variables
diff --git a/gn/src/gn/rust_variables.h b/gn/src/gn/rust_variables.h
new file mode 100644
index 00000000000..ea2da6d3783
--- /dev/null
+++ b/gn/src/gn/rust_variables.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_VARIABLES_H_
+#define TOOLS_GN_RUST_VARIABLES_H_
+
+#include "gn/variables.h"
+
+namespace variables {
+
+// Rust target vars ------------------------------------------------------
+
+extern const char kRustAliasedDeps[];
+extern const char kRustAliasedDeps_HelpShort[];
+extern const char kRustAliasedDeps_Help[];
+
+extern const char kRustCrateName[];
+extern const char kRustCrateName_HelpShort[];
+extern const char kRustCrateName_Help[];
+
+extern const char kRustCrateType[];
+extern const char kRustCrateType_HelpShort[];
+extern const char kRustCrateType_Help[];
+
+extern const char kRustCrateRoot[];
+extern const char kRustCrateRoot_HelpShort[];
+extern const char kRustCrateRoot_Help[];
+
+extern const char kRustEdition[];
+extern const char kRustEdition_HelpShort[];
+extern const char kRustEdition_Help[];
+
+void InsertRustVariables(VariableInfoMap* info_map);
+
+} // namespace variables
+
+#endif // TOOLS_GN_RUST_VARIABLES_H_ \ No newline at end of file
diff --git a/gn/src/gn/scheduler.cc b/gn/src/gn/scheduler.cc
new file mode 100644
index 00000000000..6525f33ec3a
--- /dev/null
+++ b/gn/src/gn/scheduler.cc
@@ -0,0 +1,183 @@
+// 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 "gn/scheduler.h"
+
+#include <algorithm>
+
+#include "gn/standard_out.h"
+#include "gn/target.h"
+
+namespace {} // namespace
+
+Scheduler* g_scheduler = nullptr;
+
+Scheduler::Scheduler()
+ : main_thread_run_loop_(MsgLoop::Current()),
+ input_file_manager_(new InputFileManager) {
+ g_scheduler = this;
+}
+
+Scheduler::~Scheduler() {
+ WaitForPoolTasks();
+ g_scheduler = nullptr;
+}
+
+bool Scheduler::Run() {
+ main_thread_run_loop_->Run();
+ bool local_is_failed;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ local_is_failed = is_failed();
+ has_been_shutdown_ = true;
+ }
+ // Don't do this while holding |lock_|, since it will block on the workers,
+ // which may be in turn waiting on the lock.
+ WaitForPoolTasks();
+ return !local_is_failed;
+}
+
+void Scheduler::Log(const std::string& verb, const std::string& msg) {
+ task_runner()->PostTask([this, verb, msg]() { LogOnMainThread(verb, msg); });
+}
+
+void Scheduler::FailWithError(const Err& err) {
+ DCHECK(err.has_error());
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ if (is_failed_ || has_been_shutdown_)
+ return; // Ignore errors once we see one.
+ is_failed_ = true;
+ }
+
+ task_runner()->PostTask([this, err]() { FailWithErrorOnMainThread(err); });
+}
+
+void Scheduler::ScheduleWork(std::function<void()> work) {
+ IncrementWorkCount();
+ pool_work_count_.Increment();
+ worker_pool_.PostTask([this, work = std::move(work)]() {
+ work();
+ DecrementWorkCount();
+ if (!pool_work_count_.Decrement()) {
+ std::unique_lock<std::mutex> auto_lock(pool_work_count_lock_);
+ pool_work_count_cv_.notify_one();
+ }
+ });
+}
+
+void Scheduler::AddGenDependency(const base::FilePath& file) {
+ std::lock_guard<std::mutex> lock(lock_);
+ gen_dependencies_.push_back(file);
+}
+
+std::vector<base::FilePath> Scheduler::GetGenDependencies() const {
+ std::lock_guard<std::mutex> lock(lock_);
+ return gen_dependencies_;
+}
+
+void Scheduler::AddWrittenFile(const SourceFile& file) {
+ std::lock_guard<std::mutex> lock(lock_);
+ written_files_.push_back(file);
+}
+
+void Scheduler::AddUnknownGeneratedInput(const Target* target,
+ const SourceFile& file) {
+ std::lock_guard<std::mutex> lock(lock_);
+ unknown_generated_inputs_.insert(std::make_pair(file, target));
+}
+
+void Scheduler::AddWriteRuntimeDepsTarget(const Target* target) {
+ std::lock_guard<std::mutex> lock(lock_);
+ write_runtime_deps_targets_.push_back(target);
+}
+
+std::vector<const Target*> Scheduler::GetWriteRuntimeDepsTargets() const {
+ std::lock_guard<std::mutex> lock(lock_);
+ return write_runtime_deps_targets_;
+}
+
+bool Scheduler::IsFileGeneratedByWriteRuntimeDeps(
+ const OutputFile& file) const {
+ std::lock_guard<std::mutex> lock(lock_);
+ // Number of targets should be quite small, so brute-force search is fine.
+ for (const Target* target : write_runtime_deps_targets_) {
+ if (file == target->write_runtime_deps_output()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Scheduler::AddGeneratedFile(const SourceFile& entry) {
+ std::lock_guard<std::mutex> lock(lock_);
+ generated_files_.insert(std::make_pair(entry, true));
+}
+
+bool Scheduler::IsFileGeneratedByTarget(const SourceFile& file) const {
+ std::lock_guard<std::mutex> lock(lock_);
+ return generated_files_.find(file) != generated_files_.end();
+}
+
+std::multimap<SourceFile, const Target*> Scheduler::GetUnknownGeneratedInputs()
+ const {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ // Remove all unknown inputs that were written files. These are OK as inputs
+ // to build steps since they were written as a side-effect of running GN.
+ //
+ // It's assumed that this function is called once during cleanup to check for
+ // errors, so performing this work in the lock doesn't matter.
+ std::multimap<SourceFile, const Target*> filtered = unknown_generated_inputs_;
+ for (const SourceFile& file : written_files_)
+ filtered.erase(file);
+
+ return filtered;
+}
+
+void Scheduler::ClearUnknownGeneratedInputsAndWrittenFiles() {
+ std::lock_guard<std::mutex> lock(lock_);
+ unknown_generated_inputs_.clear();
+ written_files_.clear();
+}
+
+void Scheduler::IncrementWorkCount() {
+ work_count_.Increment();
+}
+
+void Scheduler::DecrementWorkCount() {
+ if (!work_count_.Decrement()) {
+ task_runner()->PostTask([this]() { OnComplete(); });
+ }
+}
+
+void Scheduler::SuppressOutputForTesting(bool suppress) {
+ std::lock_guard<std::mutex> lock(lock_);
+ suppress_output_for_testing_ = suppress;
+}
+
+void Scheduler::LogOnMainThread(const std::string& verb,
+ const std::string& msg) {
+ OutputString(verb, DECORATION_YELLOW);
+ OutputString(" " + msg + "\n");
+}
+
+void Scheduler::FailWithErrorOnMainThread(const Err& err) {
+ if (!suppress_output_for_testing_)
+ err.PrintToStdout();
+ task_runner()->PostQuit();
+}
+
+void Scheduler::OnComplete() {
+ // Should be called on the main thread.
+ DCHECK(task_runner() == MsgLoop::Current());
+ task_runner()->PostQuit();
+}
+
+void Scheduler::WaitForPoolTasks() {
+ std::unique_lock<std::mutex> lock(pool_work_count_lock_);
+ while (!pool_work_count_.IsZero())
+ pool_work_count_cv_.wait(lock);
+}
diff --git a/gn/src/gn/scheduler.h b/gn/src/gn/scheduler.h
new file mode 100644
index 00000000000..4ea4c02aaab
--- /dev/null
+++ b/gn/src/gn/scheduler.h
@@ -0,0 +1,156 @@
+// 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 TOOLS_GN_SCHEDULER_H_
+#define TOOLS_GN_SCHEDULER_H_
+
+#include <condition_variable>
+#include <functional>
+#include <map>
+#include <mutex>
+
+#include "base/atomic_ref_count.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gn/input_file_manager.h"
+#include "gn/label.h"
+#include "gn/source_file.h"
+#include "gn/token.h"
+#include "util/msg_loop.h"
+#include "util/worker_pool.h"
+
+class Target;
+
+// Maintains the thread pool and error state.
+class Scheduler {
+ public:
+ Scheduler();
+ ~Scheduler();
+
+ bool Run();
+
+ MsgLoop* task_runner() {
+ DCHECK(main_thread_run_loop_);
+ return main_thread_run_loop_;
+ }
+
+ InputFileManager* input_file_manager() { return input_file_manager_.get(); }
+
+ bool verbose_logging() const { return verbose_logging_; }
+ void set_verbose_logging(bool v) { verbose_logging_ = v; }
+
+ // TODO(brettw) data race on this access (benign?).
+ bool is_failed() const { return is_failed_; }
+
+ void Log(const std::string& verb, const std::string& msg);
+ void FailWithError(const Err& err);
+
+ void ScheduleWork(std::function<void()> work);
+
+ void Shutdown();
+
+ // Declares that the given file was read and affected the build output.
+ //
+ // Some consumers expect provided path to be absolute.kk
+ //
+ // TODO(brettw) this is global rather than per-BuildSettings. If we
+ // start using >1 build settings, then we probably want this to take a
+ // BuildSettings object so we know the depdency on a per-build basis.
+ // If moved, most of the Add/Get functions below should move as well.
+ void AddGenDependency(const base::FilePath& file);
+ std::vector<base::FilePath> GetGenDependencies() const;
+
+ // Tracks calls to write_file for resolving with the unknown generated
+ // inputs (see AddUnknownGeneratedInput below).
+ void AddWrittenFile(const SourceFile& file);
+
+ // Schedules a file to be written due to a target setting write_runtime_deps.
+ void AddWriteRuntimeDepsTarget(const Target* entry);
+ std::vector<const Target*> GetWriteRuntimeDepsTargets() const;
+ bool IsFileGeneratedByWriteRuntimeDeps(const OutputFile& file) const;
+
+ // Tracks generated_file calls.
+ void AddGeneratedFile(const SourceFile& entry);
+ bool IsFileGeneratedByTarget(const SourceFile& file) const;
+
+ // Unknown generated inputs are files that a target declares as an input
+ // in the output directory, but which aren't generated by any dependency.
+ //
+ // Some of these files will be files written by write_file and will be
+ // GenDependencies (see AddWrittenFile above). There are OK and include
+ // things like response files for scripts. Others cases will be ones where
+ // the file is generated by a target that's not a dependency.
+ //
+ // In order to distinguish these two cases, the checking for these input
+ // files needs to be done after all targets are complete. This also has the
+ // nice side effect that if a target generates the file we can find it and
+ // tell the user which dependency is missing.
+ //
+ // The result returned by GetUnknownGeneratedInputs will not count any files
+ // that were written by write_file during execution.
+ void AddUnknownGeneratedInput(const Target* target, const SourceFile& file);
+ std::multimap<SourceFile, const Target*> GetUnknownGeneratedInputs() const;
+ void ClearUnknownGeneratedInputsAndWrittenFiles(); // For testing.
+
+ // We maintain a count of the things we need to do that works like a
+ // refcount. When this reaches 0, the program exits.
+ void IncrementWorkCount();
+ void DecrementWorkCount();
+
+ void SuppressOutputForTesting(bool suppress);
+
+ private:
+ void LogOnMainThread(const std::string& verb, const std::string& msg);
+ void FailWithErrorOnMainThread(const Err& err);
+
+ void DoTargetFileWrite(const Target* target);
+
+ void OnComplete();
+
+ // Waits for tasks scheduled via ScheduleWork() to complete their execution.
+ void WaitForPoolTasks();
+
+ MsgLoop* main_thread_run_loop_;
+
+ scoped_refptr<InputFileManager> input_file_manager_;
+
+ bool verbose_logging_ = false;
+
+ base::AtomicRefCount work_count_;
+
+ // Number of tasks scheduled by ScheduleWork() that haven't completed their
+ // execution.
+ base::AtomicRefCount pool_work_count_;
+
+ // Lock for |pool_work_count_cv_|.
+ std::mutex pool_work_count_lock_;
+
+ // Condition variable signaled when |pool_work_count_| reaches zero.
+ std::condition_variable pool_work_count_cv_;
+
+ WorkerPool worker_pool_;
+
+ mutable std::mutex lock_;
+ bool is_failed_ = false;
+
+ bool suppress_output_for_testing_ = false;
+
+ // Used to track whether the worker pool has been shutdown. This is necessary
+ // to clean up after tests that make a scheduler but don't run the message
+ // loop.
+ bool has_been_shutdown_ = false;
+
+ // Protected by the lock. See the corresponding Add/Get functions above.
+ std::vector<base::FilePath> gen_dependencies_;
+ std::vector<SourceFile> written_files_;
+ std::vector<const Target*> write_runtime_deps_targets_;
+ std::multimap<SourceFile, const Target*> unknown_generated_inputs_;
+ std::map<SourceFile, bool> generated_files_;
+
+ DISALLOW_COPY_AND_ASSIGN(Scheduler);
+};
+
+extern Scheduler* g_scheduler;
+
+#endif // TOOLS_GN_SCHEDULER_H_
diff --git a/gn/src/gn/scope.cc b/gn/src/gn/scope.cc
new file mode 100644
index 00000000000..a1cf1796b4d
--- /dev/null
+++ b/gn/src/gn/scope.cc
@@ -0,0 +1,573 @@
+// 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 "gn/scope.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "gn/parse_tree.h"
+#include "gn/source_file.h"
+#include "gn/template.h"
+
+namespace {
+
+// FLags set in the mode_flags_ of a scope. If a bit is set, it applies
+// recursively to all dependent scopes.
+const unsigned kProcessingBuildConfigFlag = 1;
+const unsigned kProcessingImportFlag = 2;
+
+// Returns true if this variable name should be considered private. Private
+// values start with an underscore, and are not imported from "gni" files
+// when processing an import.
+bool IsPrivateVar(const std::string_view& name) {
+ return name.empty() || name[0] == '_';
+}
+
+} // namespace
+
+// Defaults to all false, which are the things least likely to cause errors.
+Scope::MergeOptions::MergeOptions()
+ : clobber_existing(false),
+ skip_private_vars(false),
+ mark_dest_used(false) {}
+
+Scope::MergeOptions::~MergeOptions() = default;
+
+Scope::ProgrammaticProvider::~ProgrammaticProvider() {
+ scope_->RemoveProvider(this);
+}
+
+Scope::Scope(const Settings* settings)
+ : const_containing_(nullptr),
+ mutable_containing_(nullptr),
+ settings_(settings),
+ mode_flags_(0),
+ item_collector_(nullptr) {}
+
+Scope::Scope(Scope* parent)
+ : const_containing_(nullptr),
+ mutable_containing_(parent),
+ settings_(parent->settings()),
+ mode_flags_(0),
+ item_collector_(nullptr),
+ build_dependency_files_(parent->build_dependency_files_) {}
+
+Scope::Scope(const Scope* parent)
+ : const_containing_(parent),
+ mutable_containing_(nullptr),
+ settings_(parent->settings()),
+ mode_flags_(0),
+ item_collector_(nullptr),
+ build_dependency_files_(parent->build_dependency_files_) {}
+
+Scope::~Scope() = default;
+
+void Scope::DetachFromContaining() {
+ const_containing_ = nullptr;
+ mutable_containing_ = nullptr;
+}
+
+bool Scope::HasValues(SearchNested search_nested) const {
+ DCHECK(search_nested == SEARCH_CURRENT);
+ return !values_.empty();
+}
+
+const Value* Scope::GetValue(const std::string_view& ident,
+ bool counts_as_used) {
+ const Scope* found_in_scope = nullptr;
+ return GetValueWithScope(ident, counts_as_used, &found_in_scope);
+}
+
+const Value* Scope::GetValueWithScope(const std::string_view& ident,
+ bool counts_as_used,
+ const Scope** found_in_scope) {
+ // First check for programmatically-provided values.
+ for (auto* provider : programmatic_providers_) {
+ const Value* v = provider->GetProgrammaticValue(ident);
+ if (v) {
+ *found_in_scope = nullptr;
+ return v;
+ }
+ }
+
+ RecordMap::iterator found = values_.find(ident);
+ if (found != values_.end()) {
+ if (counts_as_used)
+ found->second.used = true;
+ *found_in_scope = this;
+ return &found->second.value;
+ }
+
+ // Search in the parent scope.
+ if (const_containing_)
+ return const_containing_->GetValueWithScope(ident, found_in_scope);
+ if (mutable_containing_) {
+ return mutable_containing_->GetValueWithScope(ident, counts_as_used,
+ found_in_scope);
+ }
+ return nullptr;
+}
+
+Value* Scope::GetMutableValue(const std::string_view& ident,
+ SearchNested search_mode,
+ bool counts_as_used) {
+ // Don't do programmatic values, which are not mutable.
+ RecordMap::iterator found = values_.find(ident);
+ if (found != values_.end()) {
+ if (counts_as_used)
+ found->second.used = true;
+ return &found->second.value;
+ }
+
+ // Search in the parent mutable scope if requested, but not const one.
+ if (search_mode == SEARCH_NESTED && mutable_containing_) {
+ return mutable_containing_->GetMutableValue(ident, Scope::SEARCH_NESTED,
+ counts_as_used);
+ }
+ return nullptr;
+}
+
+std::string_view Scope::GetStorageKey(const std::string_view& ident) const {
+ RecordMap::const_iterator found = values_.find(ident);
+ if (found != values_.end())
+ return found->first;
+
+ // Search in parent scope.
+ if (containing())
+ return containing()->GetStorageKey(ident);
+ return std::string_view();
+}
+
+const Value* Scope::GetValue(const std::string_view& ident) const {
+ const Scope* found_in_scope = nullptr;
+ return GetValueWithScope(ident, &found_in_scope);
+}
+
+const Value* Scope::GetValueWithScope(const std::string_view& ident,
+ const Scope** found_in_scope) const {
+ RecordMap::const_iterator found = values_.find(ident);
+ if (found != values_.end()) {
+ *found_in_scope = this;
+ return &found->second.value;
+ }
+ if (containing())
+ return containing()->GetValueWithScope(ident, found_in_scope);
+ return nullptr;
+}
+
+Value* Scope::SetValue(const std::string_view& ident,
+ Value v,
+ const ParseNode* set_node) {
+ Record& r = values_[ident]; // Clears any existing value.
+ r.value = std::move(v);
+ r.value.set_origin(set_node);
+ return &r.value;
+}
+
+void Scope::RemoveIdentifier(const std::string_view& ident) {
+ RecordMap::iterator found = values_.find(ident);
+ if (found != values_.end())
+ values_.erase(found);
+}
+
+void Scope::RemovePrivateIdentifiers() {
+ // Do it in two phases to avoid mutating while iterating. Our hash map is
+ // currently backed by several different vendor-specific implementations and
+ // I'm not sure if all of them support mutating while iterating. Since this
+ // is not perf-critical, do the safe thing.
+ std::vector<std::string_view> to_remove;
+ for (const auto& cur : values_) {
+ if (IsPrivateVar(cur.first))
+ to_remove.push_back(cur.first);
+ }
+
+ for (const auto& cur : to_remove)
+ values_.erase(cur);
+}
+
+bool Scope::AddTemplate(const std::string& name, const Template* templ) {
+ if (GetTemplate(name))
+ return false;
+ templates_[name] = templ;
+ return true;
+}
+
+const Template* Scope::GetTemplate(const std::string& name) const {
+ TemplateMap::const_iterator found = templates_.find(name);
+ if (found != templates_.end())
+ return found->second.get();
+ if (containing())
+ return containing()->GetTemplate(name);
+ return nullptr;
+}
+
+void Scope::MarkUsed(const std::string_view& ident) {
+ RecordMap::iterator found = values_.find(ident);
+ if (found == values_.end()) {
+ NOTREACHED();
+ return;
+ }
+ found->second.used = true;
+}
+
+void Scope::MarkAllUsed() {
+ for (auto& cur : values_)
+ cur.second.used = true;
+}
+
+void Scope::MarkAllUsed(const std::set<std::string>& excluded_values) {
+ for (auto& cur : values_) {
+ if (!excluded_values.empty() &&
+ excluded_values.find(std::string(cur.first)) != excluded_values.end()) {
+ continue; // Skip this excluded value.
+ }
+ cur.second.used = true;
+ }
+}
+
+void Scope::MarkUnused(const std::string_view& ident) {
+ RecordMap::iterator found = values_.find(ident);
+ if (found == values_.end()) {
+ NOTREACHED();
+ return;
+ }
+ found->second.used = false;
+}
+
+bool Scope::IsSetButUnused(const std::string_view& ident) const {
+ RecordMap::const_iterator found = values_.find(ident);
+ if (found != values_.end()) {
+ if (!found->second.used) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Scope::CheckForUnusedVars(Err* err) const {
+ for (const auto& pair : values_) {
+ if (!pair.second.used) {
+ std::string help =
+ "You set the variable \"" + std::string(pair.first) +
+ "\" here and it was unused before it went\nout of scope.";
+
+ const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
+ if (binary && binary->op().type() == Token::EQUAL) {
+ // Make a nicer error message for normal var sets.
+ *err =
+ Err(binary->left()->GetRange(), "Assignment had no effect.", help);
+ } else {
+ // This will happen for internally-generated variables.
+ *err =
+ Err(pair.second.value.origin(), "Assignment had no effect.", help);
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
+ for (const auto& pair : values_)
+ (*output)[pair.first] = pair.second.value;
+}
+
+bool Scope::CheckCurrentScopeValuesEqual(const Scope* other) const {
+ // If there are containing scopes, equality shouldn't work.
+ if (containing()) {
+ return false;
+ }
+ if (values_.size() != other->values_.size()) {
+ return false;
+ }
+ for (const auto& pair : values_) {
+ const Value* v = other->GetValue(pair.first);
+ if (!v || *v != pair.second.value) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Scope::NonRecursiveMergeTo(Scope* dest,
+ const MergeOptions& options,
+ const ParseNode* node_for_err,
+ const char* desc_for_err,
+ Err* err) const {
+ // Values.
+ for (const auto& pair : values_) {
+ const std::string_view& current_name = pair.first;
+ if (options.skip_private_vars && IsPrivateVar(current_name))
+ continue; // Skip this private var.
+ if (!options.excluded_values.empty() &&
+ options.excluded_values.find(std::string(current_name)) !=
+ options.excluded_values.end()) {
+ continue; // Skip this excluded value.
+ }
+
+ const Value& new_value = pair.second.value;
+ if (!options.clobber_existing) {
+ const Value* existing_value = dest->GetValue(current_name);
+ if (existing_value && new_value != *existing_value) {
+ // Value present in both the source and the dest.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Value collision.",
+ "This " + desc_string + " contains \"" +
+ std::string(current_name) + "\"");
+ err->AppendSubErr(
+ Err(pair.second.value, "defined here.",
+ "Which would clobber the one in your current scope"));
+ err->AppendSubErr(
+ Err(*existing_value, "defined here.",
+ "Executing " + desc_string +
+ " should not conflict with anything "
+ "in the current\nscope unless the values are identical."));
+ return false;
+ }
+ }
+ dest->values_[current_name] = pair.second;
+
+ if (options.mark_dest_used)
+ dest->MarkUsed(current_name);
+ }
+
+ // Target defaults are owning pointers.
+ for (const auto& pair : target_defaults_) {
+ const std::string& current_name = pair.first;
+ if (!options.excluded_values.empty() &&
+ options.excluded_values.find(current_name) !=
+ options.excluded_values.end()) {
+ continue; // Skip the excluded value.
+ }
+
+ if (!options.clobber_existing) {
+ const Scope* dest_defaults = dest->GetTargetDefaults(current_name);
+ if (dest_defaults) {
+ if (RecordMapValuesEqual(pair.second->values_,
+ dest_defaults->values_)) {
+ // Values of the two defaults are equivalent, just ignore the
+ // collision.
+ continue;
+ } else {
+ // TODO(brettw) it would be nice to know the origin of a
+ // set_target_defaults so we can give locations for the colliding
+ // target defaults.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Target defaults collision.",
+ "This " + desc_string +
+ " contains target defaults for\n"
+ "\"" +
+ current_name +
+ "\" which would clobber one for the\n"
+ "same target type in your current scope. It's "
+ "unfortunate that "
+ "I'm too stupid\nto tell you the location of where "
+ "the target "
+ "defaults were set. Usually\nthis happens in the "
+ "BUILDCONFIG.gn "
+ "file or in a related .gni file.\n");
+ return false;
+ }
+ }
+ }
+
+ std::unique_ptr<Scope>& dest_scope = dest->target_defaults_[current_name];
+ dest_scope = std::make_unique<Scope>(settings_);
+ pair.second->NonRecursiveMergeTo(dest_scope.get(), options, node_for_err,
+ "<SHOULDN'T HAPPEN>", err);
+ }
+
+ // Templates.
+ for (const auto& pair : templates_) {
+ const std::string& current_name = pair.first;
+ if (options.skip_private_vars && IsPrivateVar(current_name))
+ continue; // Skip this private template.
+ if (!options.excluded_values.empty() &&
+ options.excluded_values.find(current_name) !=
+ options.excluded_values.end()) {
+ continue; // Skip the excluded value.
+ }
+
+ if (!options.clobber_existing) {
+ const Template* existing_template = dest->GetTemplate(current_name);
+ // Since templates are refcounted, we can check if it's the same one by
+ // comparing pointers.
+ if (existing_template && pair.second.get() != existing_template) {
+ // Rule present in both the source and the dest, and they're not the
+ // same one.
+ std::string desc_string(desc_for_err);
+ *err = Err(node_for_err, "Template collision.",
+ "This " + desc_string + " contains a template \"" +
+ current_name + "\"");
+ err->AppendSubErr(
+ Err(pair.second->GetDefinitionRange(), "defined here.",
+ "Which would clobber the one in your current scope"));
+ err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
+ "defined here.",
+ "Executing " + desc_string +
+ " should not conflict with anything "
+ "in the current\nscope."));
+ return false;
+ }
+ }
+
+ // Be careful to delete any pointer we're about to clobber.
+ dest->templates_[current_name] = pair.second;
+ }
+
+ // Propogate build dependency files,
+ dest->AddBuildDependencyFiles(build_dependency_files_);
+
+ return true;
+}
+
+std::unique_ptr<Scope> Scope::MakeClosure() const {
+ std::unique_ptr<Scope> result;
+ if (const_containing_) {
+ // We reached the top of the mutable scope stack. The result scope just
+ // references the const scope (which will never change).
+ result = std::make_unique<Scope>(const_containing_);
+ } else if (mutable_containing_) {
+ // There are more nested mutable scopes. Recursively go up the stack to
+ // get the closure.
+ result = mutable_containing_->MakeClosure();
+ } else {
+ // This is a standalone scope, just copy it.
+ result = std::make_unique<Scope>(settings_);
+ }
+
+ // Want to clobber since we've flattened some nested scopes, and our parent
+ // scope may have a duplicate value set.
+ MergeOptions options;
+ options.clobber_existing = true;
+
+ // Add in our variables and we're done.
+ Err err;
+ NonRecursiveMergeTo(result.get(), options, nullptr, "<SHOULDN'T HAPPEN>",
+ &err);
+ DCHECK(!err.has_error());
+ return result;
+}
+
+Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
+ std::unique_ptr<Scope>& dest = target_defaults_[target_type];
+ dest = std::make_unique<Scope>(settings_);
+ return dest.get();
+}
+
+const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
+ NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
+ if (found != target_defaults_.end())
+ return found->second.get();
+ if (containing())
+ return containing()->GetTargetDefaults(target_type);
+ return nullptr;
+}
+
+void Scope::SetProcessingBuildConfig() {
+ DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
+ mode_flags_ |= kProcessingBuildConfigFlag;
+}
+
+void Scope::ClearProcessingBuildConfig() {
+ DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
+ mode_flags_ &= ~(kProcessingBuildConfigFlag);
+}
+
+bool Scope::IsProcessingBuildConfig() const {
+ if (mode_flags_ & kProcessingBuildConfigFlag)
+ return true;
+ if (containing())
+ return containing()->IsProcessingBuildConfig();
+ return false;
+}
+
+void Scope::SetProcessingImport() {
+ DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
+ mode_flags_ |= kProcessingImportFlag;
+}
+
+void Scope::ClearProcessingImport() {
+ DCHECK(mode_flags_ & kProcessingImportFlag);
+ mode_flags_ &= ~(kProcessingImportFlag);
+}
+
+bool Scope::IsProcessingImport() const {
+ if (mode_flags_ & kProcessingImportFlag)
+ return true;
+ if (containing())
+ return containing()->IsProcessingImport();
+ return false;
+}
+
+const SourceDir& Scope::GetSourceDir() const {
+ if (!source_dir_.is_null())
+ return source_dir_;
+ if (containing())
+ return containing()->GetSourceDir();
+ return source_dir_;
+}
+
+void Scope::AddBuildDependencyFile(const SourceFile& build_dependency_file) {
+ build_dependency_files_.insert(build_dependency_file);
+}
+
+void Scope::AddBuildDependencyFiles(
+ const SourceFileSet& build_dependency_files) {
+ build_dependency_files_.insert(build_dependency_files.begin(),
+ build_dependency_files.end());
+}
+
+Scope::ItemVector* Scope::GetItemCollector() {
+ if (item_collector_)
+ return item_collector_;
+ if (mutable_containing())
+ return mutable_containing()->GetItemCollector();
+ return nullptr;
+}
+
+void Scope::SetProperty(const void* key, void* value) {
+ if (!value) {
+ DCHECK(properties_.find(key) != properties_.end());
+ properties_.erase(key);
+ } else {
+ properties_[key] = value;
+ }
+}
+
+void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
+ PropertyMap::const_iterator found = properties_.find(key);
+ if (found != properties_.end()) {
+ if (found_on_scope)
+ *found_on_scope = this;
+ return found->second;
+ }
+ if (containing())
+ return containing()->GetProperty(key, found_on_scope);
+ return nullptr;
+}
+
+void Scope::AddProvider(ProgrammaticProvider* p) {
+ programmatic_providers_.insert(p);
+}
+
+void Scope::RemoveProvider(ProgrammaticProvider* p) {
+ DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
+ programmatic_providers_.erase(p);
+}
+
+// static
+bool Scope::RecordMapValuesEqual(const RecordMap& a, const RecordMap& b) {
+ if (a.size() != b.size())
+ return false;
+ for (const auto& pair : a) {
+ const auto& found_b = b.find(pair.first);
+ if (found_b == b.end())
+ return false; // Item in 'a' but not 'b'.
+ if (pair.second.value != found_b->second.value)
+ return false; // Values for variable in 'a' and 'b' are different.
+ }
+ return true;
+}
diff --git a/gn/src/gn/scope.h b/gn/src/gn/scope.h
new file mode 100644
index 00000000000..4621db514aa
--- /dev/null
+++ b/gn/src/gn/scope.h
@@ -0,0 +1,387 @@
+// 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 TOOLS_GN_SCOPE_H_
+#define TOOLS_GN_SCOPE_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "gn/err.h"
+#include "gn/pattern.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/value.h"
+
+class Item;
+class ParseNode;
+class Settings;
+class Template;
+
+// Scope for the script execution.
+//
+// Scopes are nested. Writing goes into the toplevel scope, reading checks
+// values resursively down the stack until a match is found or there are no
+// more containing scopes.
+//
+// A containing scope can be const or non-const. The const containing scope is
+// used primarily to refer to the master build config which is shared across
+// many invocations. A const containing scope, however, prevents us from
+// marking variables "used" which prevents us from issuing errors on unused
+// variables. So you should use a non-const containing scope whenever possible.
+class Scope {
+ public:
+ using KeyValueMap = std::map<std::string_view, Value>;
+ // Holds an owning list of Items.
+ using ItemVector = std::vector<std::unique_ptr<Item>>;
+
+ // A flag to indicate whether a function should recurse into nested scopes,
+ // or only operate on the current scope.
+ enum SearchNested { SEARCH_NESTED, SEARCH_CURRENT };
+
+ // Allows code to provide values for built-in variables. This class will
+ // automatically register itself on construction and deregister itself on
+ // destruction.
+ class ProgrammaticProvider {
+ public:
+ explicit ProgrammaticProvider(Scope* scope) : scope_(scope) {
+ scope_->AddProvider(this);
+ }
+ virtual ~ProgrammaticProvider();
+
+ // Returns a non-null value if the given value can be programmatically
+ // generated, or NULL if there is none.
+ virtual const Value* GetProgrammaticValue(
+ const std::string_view& ident) = 0;
+
+ protected:
+ Scope* scope_;
+ };
+
+ // Options for configuring scope merges.
+ struct MergeOptions {
+ MergeOptions();
+ ~MergeOptions();
+
+ // When set, all existing avlues in the destination scope will be
+ // overwritten.
+ //
+ // When false, it will be an error to merge a variable into another scope
+ // where a variable with the same name is already set. The exception is
+ // if both of the variables have the same value (which happens if you
+ // somehow multiply import the same file, for example). This case will be
+ // ignored since there is nothing getting lost.
+ bool clobber_existing;
+
+ // When true, private variables (names beginning with an underscore) will
+ // be copied to the destination scope. When false, private values will be
+ // skipped.
+ bool skip_private_vars;
+
+ // When set, values copied to the destination scope will be marked as used
+ // so won't trigger an unused variable warning. You want this when doing an
+ // import, for example, or files that don't need a variable from the .gni
+ // file will throw an error.
+ bool mark_dest_used;
+
+ // When set, those variables are not merged.
+ std::set<std::string> excluded_values;
+ };
+
+ // Creates an empty toplevel scope.
+ explicit Scope(const Settings* settings);
+
+ // Creates a dependent scope.
+ explicit Scope(Scope* parent);
+ explicit Scope(const Scope* parent);
+
+ ~Scope();
+
+ const Settings* settings() const { return settings_; }
+
+ // See the const_/mutable_containing_ var declarations below. Yes, it's a
+ // bit weird that we can have a const pointer to the "mutable" one.
+ Scope* mutable_containing() { return mutable_containing_; }
+ const Scope* mutable_containing() const { return mutable_containing_; }
+ const Scope* const_containing() const { return const_containing_; }
+ const Scope* containing() const {
+ return mutable_containing_ ? mutable_containing_ : const_containing_;
+ }
+
+ // Clears any references to containing scopes. This scope will now be
+ // self-sufficient.
+ void DetachFromContaining();
+
+ // Returns true if the scope has any values set. This does not check other
+ // things that may be set like templates or defaults.
+ //
+ // Currently this does not search nested scopes and this will assert if you
+ // want to search nested scopes. The enum is passed so the callers are
+ // unambiguous about nested scope handling. This can be added if needed.
+ bool HasValues(SearchNested search_nested) const;
+
+ // Returns NULL if there's no such value.
+ //
+ // counts_as_used should be set if the variable is being read in a way that
+ // should count for unused variable checking.
+ //
+ // found_in_scope is set to the scope that contains the definition of the
+ // ident. If the value was provided programmatically (like host_cpu),
+ // found_in_scope will be set to null.
+ const Value* GetValue(const std::string_view& ident, bool counts_as_used);
+ const Value* GetValue(const std::string_view& ident) const;
+ const Value* GetValueWithScope(const std::string_view& ident,
+ const Scope** found_in_scope) const;
+ const Value* GetValueWithScope(const std::string_view& ident,
+ bool counts_as_used,
+ const Scope** found_in_scope);
+
+ // Returns the requested value as a mutable one if possible. If the value
+ // is not found in a mutable scope, then returns null. Note that the value
+ // could still exist in a const scope, so GetValue() could still return
+ // non-null in this case.
+ //
+ // Say you have a local scope that then refers to the const root scope from
+ // the master build config. You can't change the values from the master
+ // build config (it's read-only so it can be read from multiple threads
+ // without locking). Read-only operations would work on values from the root
+ // scope, but write operations would only work on values in the derived
+ // scope(s).
+ //
+ // Be careful when calling this. It's not normally correct to modify values,
+ // but you should instead do a new Set each time.
+ //
+ // Consider this code:
+ // a = 5
+ // {
+ // a = 6
+ // }
+ // The 6 should get set on the nested scope rather than modify the value
+ // in the outer one.
+ Value* GetMutableValue(const std::string_view& ident,
+ SearchNested search_mode,
+ bool counts_as_used);
+
+ // Returns the std::string_view used to identify the value. This string piece
+ // will have the same contents as "ident" passed in, but may point to a
+ // different underlying buffer. This is useful because this std::string_view
+ // is static and won't be deleted for the life of the program, so it can be
+ // used as keys in places that may outlive a temporary. It will return an
+ // empty string for programmatic and nonexistant values.
+ std::string_view GetStorageKey(const std::string_view& ident) const;
+
+ // The set_node indicates the statement that caused the set, for displaying
+ // errors later. Returns a pointer to the value in the current scope (a copy
+ // is made for storage).
+ Value* SetValue(const std::string_view& ident,
+ Value v,
+ const ParseNode* set_node);
+
+ // Removes the value with the given identifier if it exists on the current
+ // scope. This does not search recursive scopes. Does nothing if not found.
+ void RemoveIdentifier(const std::string_view& ident);
+
+ // Removes from this scope all identifiers and templates that are considered
+ // private.
+ void RemovePrivateIdentifiers();
+
+ // Templates associated with this scope. A template can only be set once, so
+ // AddTemplate will fail and return false if a rule with that name already
+ // exists. GetTemplate returns NULL if the rule doesn't exist, and it will
+ // check all containing scoped rescursively.
+ bool AddTemplate(const std::string& name, const Template* templ);
+ const Template* GetTemplate(const std::string& name) const;
+
+ // Marks the given identifier as (un)used in the current scope.
+ void MarkUsed(const std::string_view& ident);
+ void MarkAllUsed();
+ void MarkAllUsed(const std::set<std::string>& excluded_values);
+ void MarkUnused(const std::string_view& ident);
+
+ // Checks to see if the scope has a var set that hasn't been used. This is
+ // called before replacing the var with a different one. It does not check
+ // containing scopes.
+ //
+ // If the identifier is present but hasnn't been used, return true.
+ bool IsSetButUnused(const std::string_view& ident) const;
+
+ // Checks the scope to see if any values were set but not used, and fills in
+ // the error and returns false if they were.
+ bool CheckForUnusedVars(Err* err) const;
+
+ // Returns all values set in the current scope, without going to the parent
+ // scopes.
+ void GetCurrentScopeValues(KeyValueMap* output) const;
+
+ // Returns true if the values in the current scope are the same as all
+ // values in the given scope, without going to the parent scopes. Returns
+ // false if not.
+ bool CheckCurrentScopeValuesEqual(const Scope* other) const;
+
+ // Copies this scope's values into the destination. Values from the
+ // containing scope(s) (normally shadowed into the current one) will not be
+ // copied, neither will the reference to the containing scope (this is why
+ // it's "non-recursive").
+ //
+ // This is used in different contexts. When generating the error, the given
+ // parse node will be blamed, and the given desc will be used to describe
+ // the operation that doesn't support doing this. For example, desc_for_err
+ // would be "import" when doing an import, and the error string would say
+ // something like "The import contains...".
+ bool NonRecursiveMergeTo(Scope* dest,
+ const MergeOptions& options,
+ const ParseNode* node_for_err,
+ const char* desc_for_err,
+ Err* err) const;
+
+ // Constructs a scope that is a copy of the current one. Nested scopes will
+ // be collapsed until we reach a const containing scope. Private values will
+ // be included. The resulting closure will reference the const containing
+ // scope as its containing scope (since we assume the const scope won't
+ // change, we don't have to copy its values).
+ std::unique_ptr<Scope> MakeClosure() const;
+
+ // Makes an empty scope with the given name. Overwrites any existing one.
+ Scope* MakeTargetDefaults(const std::string& target_type);
+
+ // Gets the scope associated with the given target name, or null if it hasn't
+ // been set.
+ const Scope* GetTargetDefaults(const std::string& target_type) const;
+
+ // Indicates if we're currently processing the build configuration file.
+ // This is true when processing the config file for any toolchain.
+ //
+ // To set or clear the flag, it must currently be in the opposite state in
+ // the current scope. Note that querying the state of the flag recursively
+ // checks all containing scopes until it reaches the top or finds the flag
+ // set.
+ void SetProcessingBuildConfig();
+ void ClearProcessingBuildConfig();
+ bool IsProcessingBuildConfig() const;
+
+ // Indicates if we're currently processing an import file.
+ //
+ // See SetProcessingBaseConfig for how flags work.
+ void SetProcessingImport();
+ void ClearProcessingImport();
+ bool IsProcessingImport() const;
+
+ // The source directory associated with this scope. This will check embedded
+ // scopes until it finds a nonempty source directory. This will default to
+ // an empty dir if no containing scope has a source dir set.
+ const SourceDir& GetSourceDir() const;
+ void set_source_dir(const SourceDir& d) { source_dir_ = d; }
+
+ // Set of files that may affect the execution of this scope. Note that this
+ // set is constructed conservatively, meanining that every file that can
+ // potentially affect this scope is included, but not necessarily every change
+ // to these files will affect this scope.
+ const SourceFileSet& build_dependency_files() const {
+ return build_dependency_files_;
+ }
+ void AddBuildDependencyFile(const SourceFile& build_dependency_file);
+ void AddBuildDependencyFiles(const SourceFileSet& build_dependency_files);
+
+ // The item collector is where Items (Targets, Configs, etc.) go that have
+ // been defined. If a scope can generate items, this non-owning pointer will
+ // point to the storage for such items. The creator of this scope will be
+ // responsible for setting up the collector and then dealing with the
+ // collected items once execution of the context is complete.
+ //
+ // The items in a scope are collected as we go and then dispatched at the end
+ // of execution of a scope so that we can query the previously-generated
+ // targets (like getting the outputs).
+ //
+ // This can be null if the current scope can not generate items (like for
+ // imports and such).
+ //
+ // When retrieving the collector, the non-const scopes are recursively
+ // queried. The collector is not copied for closures, etc.
+ void set_item_collector(ItemVector* collector) {
+ item_collector_ = collector;
+ }
+ ItemVector* GetItemCollector();
+
+ // Properties are opaque pointers that code can use to set state on a Scope
+ // that it can retrieve later.
+ //
+ // The key should be a pointer to some use-case-specific object (to avoid
+ // collisions, otherwise it doesn't matter). Memory management is up to the
+ // setter. Setting the value to NULL will delete the property.
+ //
+ // Getting a property recursively searches all scopes, and the optional
+ // |found_on_scope| variable will be filled with the actual scope containing
+ // the key (if the pointer is non-NULL).
+ void SetProperty(const void* key, void* value);
+ void* GetProperty(const void* key, const Scope** found_on_scope) const;
+
+ private:
+ friend class ProgrammaticProvider;
+
+ struct Record {
+ Record() : used(false) {}
+ explicit Record(const Value& v) : used(false), value(v) {}
+
+ bool used; // Set to true when the variable is used.
+ Value value;
+ };
+
+ using RecordMap = std::unordered_map<std::string_view, Record>;
+
+ void AddProvider(ProgrammaticProvider* p);
+ void RemoveProvider(ProgrammaticProvider* p);
+
+ // Returns true if the two RecordMaps contain the same values (the origins
+ // of the values may be different).
+ static bool RecordMapValuesEqual(const RecordMap& a, const RecordMap& b);
+
+ // Scopes can have no containing scope (both null), a mutable containing
+ // scope, or a const containing scope. The reason is that when we're doing
+ // a new target, we want to refer to the base_config scope which will be read
+ // by multiple threads at the same time, so we REALLY want it to be const.
+ // When you just do a nested {}, however, we sometimes want to be able to
+ // change things (especially marking unused vars).
+ const Scope* const_containing_;
+ Scope* mutable_containing_;
+
+ const Settings* settings_;
+
+ // Bits set for different modes. See the flag definitions in the .cc file
+ // for more.
+ unsigned mode_flags_;
+
+ RecordMap values_;
+
+ // Note that this can't use string pieces since the names are constructed from
+ // Values which might be deallocated before this goes out of scope.
+ using NamedScopeMap = std::unordered_map<std::string, std::unique_ptr<Scope>>;
+ NamedScopeMap target_defaults_;
+
+ // Owning pointers, must be deleted.
+ using TemplateMap = std::map<std::string, scoped_refptr<const Template>>;
+ TemplateMap templates_;
+
+ ItemVector* item_collector_;
+
+ // Opaque pointers. See SetProperty() above.
+ using PropertyMap = std::map<const void*, void*>;
+ PropertyMap properties_;
+
+ using ProviderSet = std::set<ProgrammaticProvider*>;
+ ProviderSet programmatic_providers_;
+
+ SourceDir source_dir_;
+
+ SourceFileSet build_dependency_files_;
+
+ DISALLOW_COPY_AND_ASSIGN(Scope);
+};
+
+#endif // TOOLS_GN_SCOPE_H_
diff --git a/gn/src/gn/scope_per_file_provider.cc b/gn/src/gn/scope_per_file_provider.cc
new file mode 100644
index 00000000000..3110a8f75b3
--- /dev/null
+++ b/gn/src/gn/scope_per_file_provider.cc
@@ -0,0 +1,128 @@
+// 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 "gn/scope_per_file_provider.h"
+
+#include <memory>
+
+#include "gn/filesystem_utils.h"
+#include "gn/settings.h"
+#include "gn/source_file.h"
+#include "gn/value.h"
+#include "gn/variables.h"
+
+#include "last_commit_position.h"
+
+ScopePerFileProvider::ScopePerFileProvider(Scope* scope, bool allow_target_vars)
+ : ProgrammaticProvider(scope), allow_target_vars_(allow_target_vars) {}
+
+ScopePerFileProvider::~ScopePerFileProvider() = default;
+
+const Value* ScopePerFileProvider::GetProgrammaticValue(
+ const std::string_view& ident) {
+ if (ident == variables::kCurrentToolchain)
+ return GetCurrentToolchain();
+ if (ident == variables::kDefaultToolchain)
+ return GetDefaultToolchain();
+ if (ident == variables::kGnVersion)
+ return GetGnVersion();
+ if (ident == variables::kPythonPath)
+ return GetPythonPath();
+
+ if (ident == variables::kRootBuildDir)
+ return GetRootBuildDir();
+ if (ident == variables::kRootGenDir)
+ return GetRootGenDir();
+ if (ident == variables::kRootOutDir)
+ return GetRootOutDir();
+
+ if (allow_target_vars_) {
+ if (ident == variables::kTargetGenDir)
+ return GetTargetGenDir();
+ if (ident == variables::kTargetOutDir)
+ return GetTargetOutDir();
+ }
+ return nullptr;
+}
+
+const Value* ScopePerFileProvider::GetCurrentToolchain() {
+ if (!current_toolchain_) {
+ current_toolchain_ = std::make_unique<Value>(
+ nullptr,
+ scope_->settings()->toolchain_label().GetUserVisibleName(false));
+ }
+ return current_toolchain_.get();
+}
+
+const Value* ScopePerFileProvider::GetDefaultToolchain() {
+ if (!default_toolchain_) {
+ default_toolchain_ = std::make_unique<Value>(
+ nullptr,
+ scope_->settings()->default_toolchain_label().GetUserVisibleName(
+ false));
+ }
+ return default_toolchain_.get();
+}
+
+const Value* ScopePerFileProvider::GetGnVersion() {
+ if (!gn_version_) {
+ gn_version_ = std::make_unique<Value>(
+ nullptr, static_cast<int64_t>(LAST_COMMIT_POSITION_NUM));
+ }
+ return gn_version_.get();
+}
+
+const Value* ScopePerFileProvider::GetPythonPath() {
+ if (!python_path_) {
+ python_path_ = std::make_unique<Value>(
+ nullptr,
+ FilePathToUTF8(scope_->settings()->build_settings()->python_path()));
+ }
+ return python_path_.get();
+}
+
+const Value* ScopePerFileProvider::GetRootBuildDir() {
+ if (!root_build_dir_) {
+ root_build_dir_ = std::make_unique<Value>(
+ nullptr, DirectoryWithNoLastSlash(
+ scope_->settings()->build_settings()->build_dir()));
+ }
+ return root_build_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRootGenDir() {
+ if (!root_gen_dir_) {
+ root_gen_dir_ = std::make_unique<Value>(
+ nullptr, DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
+ BuildDirContext(scope_), BuildDirType::GEN)));
+ }
+ return root_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetRootOutDir() {
+ if (!root_out_dir_) {
+ root_out_dir_ = std::make_unique<Value>(
+ nullptr, DirectoryWithNoLastSlash(GetScopeCurrentBuildDirAsSourceDir(
+ scope_, BuildDirType::TOOLCHAIN_ROOT)));
+ }
+ return root_out_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetGenDir() {
+ if (!target_gen_dir_) {
+ target_gen_dir_ = std::make_unique<Value>(
+ nullptr, DirectoryWithNoLastSlash(GetScopeCurrentBuildDirAsSourceDir(
+ scope_, BuildDirType::GEN)));
+ }
+ return target_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetOutDir() {
+ if (!target_out_dir_) {
+ target_out_dir_ = std::make_unique<Value>(
+ nullptr, DirectoryWithNoLastSlash(GetScopeCurrentBuildDirAsSourceDir(
+ scope_, BuildDirType::OBJ)));
+ }
+ return target_out_dir_.get();
+}
diff --git a/gn/src/gn/scope_per_file_provider.h b/gn/src/gn/scope_per_file_provider.h
new file mode 100644
index 00000000000..228bac3cd69
--- /dev/null
+++ b/gn/src/gn/scope_per_file_provider.h
@@ -0,0 +1,53 @@
+// 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 TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
+#define TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "gn/scope.h"
+
+// ProgrammaticProvider for a scope to provide it with per-file built-in
+// variable support.
+class ScopePerFileProvider : public Scope::ProgrammaticProvider {
+ public:
+ // allow_target_vars allows the target-related variables to get resolved.
+ // When allow_target_vars is unset, the target-related values will be
+ // undefined to GN script.
+ ScopePerFileProvider(Scope* scope, bool allow_target_vars);
+ ~ScopePerFileProvider() override;
+
+ // ProgrammaticProvider implementation.
+ const Value* GetProgrammaticValue(const std::string_view& ident) override;
+
+ private:
+ const Value* GetCurrentToolchain();
+ const Value* GetDefaultToolchain();
+ const Value* GetGnVersion();
+ const Value* GetPythonPath();
+ const Value* GetRootBuildDir();
+ const Value* GetRootGenDir();
+ const Value* GetRootOutDir();
+ const Value* GetTargetGenDir();
+ const Value* GetTargetOutDir();
+
+ bool allow_target_vars_;
+
+ // All values are lazily created.
+ std::unique_ptr<Value> current_toolchain_;
+ std::unique_ptr<Value> default_toolchain_;
+ std::unique_ptr<Value> gn_version_;
+ std::unique_ptr<Value> python_path_;
+ std::unique_ptr<Value> root_build_dir_;
+ std::unique_ptr<Value> root_gen_dir_;
+ std::unique_ptr<Value> root_out_dir_;
+ std::unique_ptr<Value> target_gen_dir_;
+ std::unique_ptr<Value> target_out_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopePerFileProvider);
+};
+
+#endif // TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
diff --git a/gn/src/gn/scope_per_file_provider_unittest.cc b/gn/src/gn/scope_per_file_provider_unittest.cc
new file mode 100644
index 00000000000..8e389031b15
--- /dev/null
+++ b/gn/src/gn/scope_per_file_provider_unittest.cc
@@ -0,0 +1,58 @@
+// 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 "gn/scope_per_file_provider.h"
+#include "gn/build_settings.h"
+#include "gn/settings.h"
+#include "gn/test_with_scope.h"
+#include "gn/toolchain.h"
+#include "gn/variables.h"
+#include "util/test/test.h"
+
+TEST(ScopePerFileProvider, Expected) {
+ TestWithScope test;
+
+// Prevent horrible wrapping of calls below.
+#define GPV(val) provider.GetProgrammaticValue(val)->string_value()
+
+ // Test the default toolchain.
+ {
+ Scope scope(test.settings());
+ scope.set_source_dir(SourceDir("//source/"));
+ ScopePerFileProvider provider(&scope, true);
+
+ EXPECT_EQ("//toolchain:default", GPV(variables::kCurrentToolchain));
+ // TODO(brettw) this test harness does not set up the Toolchain manager
+ // which is the source of this value, so we can't test this yet.
+ // EXPECT_EQ("//toolchain:default", GPV(variables::kDefaultToolchain));
+ EXPECT_EQ("//out/Debug", GPV(variables::kRootBuildDir));
+ EXPECT_EQ("//out/Debug/gen", GPV(variables::kRootGenDir));
+ EXPECT_EQ("//out/Debug", GPV(variables::kRootOutDir));
+ EXPECT_EQ("//out/Debug/gen/source", GPV(variables::kTargetGenDir));
+ EXPECT_EQ("//out/Debug/obj/source", GPV(variables::kTargetOutDir));
+
+ EXPECT_GE(provider.GetProgrammaticValue(variables::kGnVersion)->int_value(),
+ 0);
+ }
+
+ // Test some with an alternate toolchain.
+ {
+ Settings settings(test.build_settings(), "tc/");
+ Toolchain toolchain(&settings, Label(SourceDir("//toolchain/"), "tc"));
+ settings.set_toolchain_label(toolchain.label());
+
+ Scope scope(&settings);
+ scope.set_source_dir(SourceDir("//source/"));
+ ScopePerFileProvider provider(&scope, true);
+
+ EXPECT_EQ("//toolchain:tc", GPV(variables::kCurrentToolchain));
+ // See above.
+ // EXPECT_EQ("//toolchain:default", GPV(variables::kDefaultToolchain));
+ EXPECT_EQ("//out/Debug", GPV(variables::kRootBuildDir));
+ EXPECT_EQ("//out/Debug/tc/gen", GPV(variables::kRootGenDir));
+ EXPECT_EQ("//out/Debug/tc", GPV(variables::kRootOutDir));
+ EXPECT_EQ("//out/Debug/tc/gen/source", GPV(variables::kTargetGenDir));
+ EXPECT_EQ("//out/Debug/tc/obj/source", GPV(variables::kTargetOutDir));
+ }
+}
diff --git a/gn/src/gn/scope_unittest.cc b/gn/src/gn/scope_unittest.cc
new file mode 100644
index 00000000000..af39a9d0fd0
--- /dev/null
+++ b/gn/src/gn/scope_unittest.cc
@@ -0,0 +1,335 @@
+// 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 "gn/scope.h"
+
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/source_file.h"
+#include "gn/template.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+namespace {
+
+bool HasStringValueEqualTo(const Scope* scope,
+ const char* name,
+ const char* expected_value) {
+ const Value* value = scope->GetValue(name);
+ if (!value)
+ return false;
+ if (value->type() != Value::STRING)
+ return false;
+ return value->string_value() == expected_value;
+}
+
+bool ContainsBuildDependencyFile(const Scope* scope,
+ const SourceFile& source_file) {
+ const auto& build_dependency_files = scope->build_dependency_files();
+ return build_dependency_files.end() !=
+ build_dependency_files.find(source_file);
+}
+
+} // namespace
+
+TEST(Scope, InheritBuildDependencyFilesFromParent) {
+ TestWithScope setup;
+ SourceFile source_file = SourceFile("//a/BUILD.gn");
+ setup.scope()->AddBuildDependencyFile(source_file);
+
+ Scope new_scope(setup.scope());
+ EXPECT_EQ(1U, new_scope.build_dependency_files().size());
+ EXPECT_TRUE(ContainsBuildDependencyFile(&new_scope, source_file));
+}
+
+TEST(Scope, NonRecursiveMergeTo) {
+ TestWithScope setup;
+
+ // Make a pretend parse node with proper tracking that we can blame for the
+ // given value.
+ InputFile input_file(SourceFile("//foo"));
+ Token assignment_token(Location(&input_file, 1, 1, 1), Token::STRING,
+ "\"hello\"");
+ LiteralNode assignment;
+ assignment.set_value(assignment_token);
+
+ // Add some values to the scope.
+ Value old_value(&assignment, "hello");
+ setup.scope()->SetValue("v", old_value, &assignment);
+ std::string_view private_var_name("_private");
+ setup.scope()->SetValue(private_var_name, old_value, &assignment);
+
+ // Add some templates to the scope.
+ FunctionCallNode templ_definition;
+ scoped_refptr<Template> templ(new Template(setup.scope(), &templ_definition));
+ setup.scope()->AddTemplate("templ", templ.get());
+ scoped_refptr<Template> private_templ(
+ new Template(setup.scope(), &templ_definition));
+ setup.scope()->AddTemplate("_templ", private_templ.get());
+
+ // Detect collisions of values' values.
+ {
+ Scope new_scope(setup.settings());
+ Value new_value(&assignment, "goodbye");
+ new_scope.SetValue("v", new_value, &assignment);
+
+ Err err;
+ EXPECT_FALSE(setup.scope()->NonRecursiveMergeTo(
+ &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
+ EXPECT_TRUE(err.has_error());
+ }
+
+ // Template name collisions.
+ {
+ Scope new_scope(setup.settings());
+
+ scoped_refptr<Template> new_templ(
+ new Template(&new_scope, &templ_definition));
+ new_scope.AddTemplate("templ", new_templ.get());
+
+ Err err;
+ EXPECT_FALSE(setup.scope()->NonRecursiveMergeTo(
+ &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
+ EXPECT_TRUE(err.has_error());
+ }
+
+ // The clobber flag should just overwrite colliding values.
+ {
+ Scope new_scope(setup.settings());
+ Value new_value(&assignment, "goodbye");
+ new_scope.SetValue("v", new_value, &assignment);
+
+ Err err;
+ Scope::MergeOptions options;
+ options.clobber_existing = true;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
+ &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+
+ const Value* found_value = new_scope.GetValue("v");
+ ASSERT_TRUE(found_value);
+ EXPECT_TRUE(old_value == *found_value);
+ }
+
+ // Clobber flag for templates.
+ {
+ Scope new_scope(setup.settings());
+
+ scoped_refptr<Template> new_templ(
+ new Template(&new_scope, &templ_definition));
+ new_scope.AddTemplate("templ", new_templ.get());
+ Scope::MergeOptions options;
+ options.clobber_existing = true;
+
+ Err err;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
+ &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+
+ const Template* found_value = new_scope.GetTemplate("templ");
+ ASSERT_TRUE(found_value);
+ EXPECT_TRUE(templ.get() == found_value);
+ }
+
+ // Don't flag values that technically collide but have the same value.
+ {
+ Scope new_scope(setup.settings());
+ Value new_value(&assignment, "hello");
+ new_scope.SetValue("v", new_value, &assignment);
+
+ Err err;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
+ &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+ }
+
+ // Templates that technically collide but are the same.
+ {
+ Scope new_scope(setup.settings());
+
+ scoped_refptr<Template> new_templ(
+ new Template(&new_scope, &templ_definition));
+ new_scope.AddTemplate("templ", templ.get());
+
+ Err err;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
+ &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+ }
+
+ // Copy private values and templates.
+ {
+ Scope new_scope(setup.settings());
+
+ Err err;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
+ &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(new_scope.GetValue(private_var_name));
+ EXPECT_TRUE(new_scope.GetTemplate("_templ"));
+ }
+
+ // Skip private values and templates.
+ {
+ Scope new_scope(setup.settings());
+
+ Err err;
+ Scope::MergeOptions options;
+ options.skip_private_vars = true;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
+ &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_FALSE(new_scope.GetValue(private_var_name));
+ EXPECT_FALSE(new_scope.GetTemplate("_templ"));
+ }
+
+ // Don't mark used.
+ {
+ Scope new_scope(setup.settings());
+
+ Err err;
+ Scope::MergeOptions options;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
+ &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_FALSE(new_scope.CheckForUnusedVars(&err));
+ EXPECT_TRUE(err.has_error());
+ }
+
+ // Mark dest used.
+ {
+ Scope new_scope(setup.settings());
+
+ Err err;
+ Scope::MergeOptions options;
+ options.mark_dest_used = true;
+ EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
+ &assignment, "error", &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(new_scope.CheckForUnusedVars(&err));
+ EXPECT_FALSE(err.has_error());
+ }
+
+ // Build dependency files are merged.
+ {
+ Scope from_scope(setup.settings());
+ SourceFile source_file = SourceFile("//a/BUILD.gn");
+ from_scope.AddBuildDependencyFile(source_file);
+
+ Scope to_scope(setup.settings());
+ EXPECT_FALSE(ContainsBuildDependencyFile(&to_scope, source_file));
+
+ Scope::MergeOptions options;
+ Err err;
+ EXPECT_TRUE(from_scope.NonRecursiveMergeTo(&to_scope, options, &assignment,
+ "error", &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(1U, to_scope.build_dependency_files().size());
+ EXPECT_TRUE(ContainsBuildDependencyFile(&to_scope, source_file));
+ }
+}
+
+TEST(Scope, MakeClosure) {
+ // Create 3 nested scopes [const root from setup] <- nested1 <- nested2.
+ TestWithScope setup;
+
+ // Make a pretend parse node with proper tracking that we can blame for the
+ // given value.
+ InputFile input_file(SourceFile("//foo"));
+ Token assignment_token(Location(&input_file, 1, 1, 1), Token::STRING,
+ "\"hello\"");
+ LiteralNode assignment;
+ assignment.set_value(assignment_token);
+ setup.scope()->SetValue("on_root", Value(&assignment, "on_root"),
+ &assignment);
+
+ // Root scope should be const from the nested caller's perspective.
+ Scope nested1(static_cast<const Scope*>(setup.scope()));
+ nested1.SetValue("on_one", Value(&assignment, "on_one"), &assignment);
+
+ Scope nested2(&nested1);
+ nested2.SetValue("on_one", Value(&assignment, "on_two"), &assignment);
+ nested2.SetValue("on_two", Value(&assignment, "on_two2"), &assignment);
+
+ // Making a closure from the root scope.
+ std::unique_ptr<Scope> result = setup.scope()->MakeClosure();
+ EXPECT_FALSE(result->containing()); // Should have no containing scope.
+ EXPECT_TRUE(result->GetValue("on_root")); // Value should be copied.
+
+ // Making a closure from the second nested scope.
+ result = nested2.MakeClosure();
+ EXPECT_EQ(setup.scope(),
+ result->containing()); // Containing scope should be the root.
+ EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_root", "on_root"));
+ EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_one", "on_two"));
+ EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_two", "on_two2"));
+}
+
+TEST(Scope, GetMutableValue) {
+ TestWithScope setup;
+
+ // Make a pretend parse node with proper tracking that we can blame for the
+ // given value.
+ InputFile input_file(SourceFile("//foo"));
+ Token assignment_token(Location(&input_file, 1, 1, 1), Token::STRING,
+ "\"hello\"");
+ LiteralNode assignment;
+ assignment.set_value(assignment_token);
+
+ const char kOnConst[] = "on_const";
+ const char kOnMutable1[] = "on_mutable1";
+ const char kOnMutable2[] = "on_mutable2";
+
+ Value value(&assignment, "hello");
+
+ // Create a root scope with one value.
+ Scope root_scope(setup.settings());
+ root_scope.SetValue(kOnConst, value, &assignment);
+
+ // Create a first nested scope with a different value.
+ const Scope* const_root_scope = &root_scope;
+ Scope mutable_scope1(const_root_scope);
+ mutable_scope1.SetValue(kOnMutable1, value, &assignment);
+
+ // Create a second nested scope with a different value.
+ Scope mutable_scope2(&mutable_scope1);
+ mutable_scope2.SetValue(kOnMutable2, value, &assignment);
+
+ // Check getting root scope values.
+ EXPECT_TRUE(mutable_scope2.GetValue(kOnConst, true));
+ EXPECT_FALSE(
+ mutable_scope2.GetMutableValue(kOnConst, Scope::SEARCH_NESTED, true));
+
+ // Test reading a value from scope 1.
+ Value* mutable1_result =
+ mutable_scope2.GetMutableValue(kOnMutable1, Scope::SEARCH_NESTED, false);
+ ASSERT_TRUE(mutable1_result);
+ EXPECT_TRUE(*mutable1_result == value);
+
+ // Make sure CheckForUnusedVars works on scope1 (we didn't mark the value as
+ // used in the previous step).
+ Err err;
+ EXPECT_FALSE(mutable_scope1.CheckForUnusedVars(&err));
+ mutable1_result =
+ mutable_scope2.GetMutableValue(kOnMutable1, Scope::SEARCH_NESTED, true);
+ EXPECT_TRUE(mutable1_result);
+ err = Err();
+ EXPECT_TRUE(mutable_scope1.CheckForUnusedVars(&err));
+
+ // Test reading a value from scope 2.
+ Value* mutable2_result =
+ mutable_scope2.GetMutableValue(kOnMutable2, Scope::SEARCH_NESTED, true);
+ ASSERT_TRUE(mutable2_result);
+ EXPECT_TRUE(*mutable2_result == value);
+}
+
+TEST(Scope, RemovePrivateIdentifiers) {
+ TestWithScope setup;
+ setup.scope()->SetValue("a", Value(nullptr, true), nullptr);
+ setup.scope()->SetValue("_b", Value(nullptr, true), nullptr);
+
+ setup.scope()->RemovePrivateIdentifiers();
+ EXPECT_TRUE(setup.scope()->GetValue("a"));
+ EXPECT_FALSE(setup.scope()->GetValue("_b"));
+}
diff --git a/gn/src/gn/settings.cc b/gn/src/gn/settings.cc
new file mode 100644
index 00000000000..154e0af5eb2
--- /dev/null
+++ b/gn/src/gn/settings.cc
@@ -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.
+
+#include "gn/settings.h"
+
+#include "base/logging.h"
+#include "gn/filesystem_utils.h"
+#include "util/build_config.h"
+
+Settings::Settings(const BuildSettings* build_settings,
+ const std::string& output_subdir_name)
+ : build_settings_(build_settings), base_config_(this) {
+ if (output_subdir_name.empty()) {
+ toolchain_output_dir_ = build_settings->build_dir();
+ } else {
+ // We guarantee this ends in a slash.
+ DCHECK(output_subdir_name[output_subdir_name.size() - 1] == '/');
+ toolchain_output_subdir_.value().append(output_subdir_name);
+
+ DCHECK(!build_settings->build_dir().is_null());
+ toolchain_output_dir_ = SourceDir(build_settings->build_dir().value() +
+ toolchain_output_subdir_.value());
+ }
+ // The output dir will be null in some tests and when invoked to parsed
+ // one-off data without doing generation.
+ if (!toolchain_output_dir_.is_null())
+ toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/");
+}
diff --git a/gn/src/gn/settings.h b/gn/src/gn/settings.h
new file mode 100644
index 00000000000..1ae3d38b9e9
--- /dev/null
+++ b/gn/src/gn/settings.h
@@ -0,0 +1,116 @@
+// 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 TOOLS_GN_SETTINGS_H_
+#define TOOLS_GN_SETTINGS_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gn/import_manager.h"
+#include "gn/output_file.h"
+#include "gn/scope.h"
+#include "gn/source_dir.h"
+#include "gn/toolchain.h"
+
+class BuildSettings;
+
+// Holds the settings for one toolchain invocation. There will be one
+// Settings object for each toolchain type, each referring to the same
+// BuildSettings object for shared stuff.
+//
+// The Settings object is const once it is constructed, which allows us to
+// use it from multiple threads during target generation without locking (which
+// is important, because it gets used a lot).
+//
+// The Toolchain object holds the set of stuff that is set by the toolchain
+// declaration, which obviously needs to be set later when we actually parse
+// the file with the toolchain declaration in it.
+class Settings {
+ public:
+ // Constructs a toolchain settings.
+ //
+ // The output_subdir_name is the name we should use for the subdirectory in
+ // the build output directory for this toolchain's outputs. The default
+ // toolchain would use an empty string (it goes in the root build dir).
+ // Otherwise, it must end in a slash.
+ Settings(const BuildSettings* build_settings,
+ const std::string& output_subdir_name);
+
+ const BuildSettings* build_settings() const { return build_settings_; }
+
+ // The actual Toolchain object pointer is not available on the settings
+ // object because it might not be resolved yet. Code running after the
+ // load is complete can ask the Builder for the Toolchain corresponding to
+ // this label.
+ const Label& toolchain_label() const { return toolchain_label_; }
+ void set_toolchain_label(const Label& l) { toolchain_label_ = l; }
+
+ const Label& default_toolchain_label() const {
+ return default_toolchain_label_;
+ }
+ void set_default_toolchain_label(const Label& default_label) {
+ default_toolchain_label_ = default_label;
+ }
+
+ // Indicates if this corresponds to the default toolchain.
+ bool is_default() const {
+ return toolchain_label_ == default_toolchain_label_;
+ }
+
+ const OutputFile& toolchain_output_subdir() const {
+ return toolchain_output_subdir_;
+ }
+ const SourceDir& toolchain_output_dir() const {
+ return toolchain_output_dir_;
+ }
+
+ // Directory for generated files.
+ const SourceDir& toolchain_gen_dir() const { return toolchain_gen_dir_; }
+
+ // The import manager caches the result of executing imported files in the
+ // context of a given settings object.
+ //
+ // See the ItemTree getter in GlobalSettings for why this doesn't return a
+ // const pointer.
+ ImportManager& import_manager() const { return import_manager_; }
+
+ const Scope* base_config() const { return &base_config_; }
+ Scope* base_config() { return &base_config_; }
+
+ // Set to true when every target we encounter should be generated. False
+ // means that only targets that have a dependency from (directly or
+ // indirectly) some magic root node are actually generated. See the comments
+ // on ItemTree for more.
+ bool greedy_target_generation() const { return greedy_target_generation_; }
+ void set_greedy_target_generation(bool gtg) {
+ greedy_target_generation_ = gtg;
+ }
+
+ private:
+ const BuildSettings* build_settings_;
+
+ Label toolchain_label_;
+ Label default_toolchain_label_;
+
+ mutable ImportManager import_manager_;
+
+ // The subdirectory inside the build output for this toolchain. For the
+ // default toolchain, this will be empty (since the deafult toolchain's
+ // output directory is the same as the build directory). When nonempty, this
+ // is guaranteed to end in a slash.
+ OutputFile toolchain_output_subdir_;
+
+ // Full source file path to the toolchain output directory.
+ SourceDir toolchain_output_dir_;
+
+ SourceDir toolchain_gen_dir_;
+
+ Scope base_config_;
+
+ bool greedy_target_generation_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(Settings);
+};
+
+#endif // TOOLS_GN_SETTINGS_H_
diff --git a/gn/src/gn/setup.cc b/gn/src/gn/setup.cc
new file mode 100644
index 00000000000..b6fdee2b4d9
--- /dev/null
+++ b/gn/src/gn/setup.cc
@@ -0,0 +1,1028 @@
+// 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 "gn/setup.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/command_format.h"
+#include "gn/commands.h"
+#include "gn/exec_process.h"
+#include "gn/filesystem_utils.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/parser.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/standard_out.h"
+#include "gn/switches.h"
+#include "gn/tokenizer.h"
+#include "gn/trace.h"
+#include "gn/value.h"
+#include "gn/value_extractors.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/scoped_process_information.h"
+#include "base/win/win_util.h"
+#endif
+
+const char kDotfile_Help[] =
+ R"(.gn file
+
+ When gn starts, it will search the current directory and parent directories
+ for a file called ".gn". This indicates the source root. You can override
+ this detection by using the --root command-line argument
+
+ The .gn file in the source root will be executed. The syntax is the same as a
+ buildfile, but with very limited build setup-specific meaning.
+
+ If you specify --root, by default GN will look for the file .gn in that
+ directory. If you want to specify a different file, you can additionally pass
+ --dotfile:
+
+ gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn
+
+Variables
+
+ arg_file_template [optional]
+ Path to a file containing the text that should be used as the default
+ args.gn content when you run `gn args`.
+
+ buildconfig [required]
+ Path to the build config file. This file will be used to set up the
+ build file execution environment for each toolchain.
+
+ check_targets [optional]
+ A list of labels and label patterns that should be checked when running
+ "gn check" or "gn gen --check". If neither check_targets or
+ no_check_targets (see below) is specified, all targets will be checked.
+ It is an error to specify both check_targets and no_check_targets. If it
+ is the empty list, no targets will be checked. To bypass this list,
+ request an explicit check of targets, like "//*".
+
+ The format of this list is identical to that of "visibility" so see "gn
+ help visibility" for examples.
+
+ no_check_targets [optional]
+ A list of labels and label patterns that should *not* be checked when
+ running "gn check" or "gn gen --check". All other targets will be checked.
+ If neither check_targets (see above) or no_check_targets is specified, all
+ targets will be checked. It is an error to specify both check_targets and
+ no_check_targets.
+
+ The format of this list is identical to that of "visibility" so see "gn
+ help visibility" for examples.
+
+ check_system_includes [optional]
+ Boolean to control whether system style includes are checked by default
+ when running "gn check" or "gn gen --check". System style includes are
+ includes that use angle brackets <> instead of double quotes "". If this
+ setting is omitted or set to false, these includes will be ignored by
+ default. They can be checked explicitly by running
+ "gn check --check-system" or "gn gen --check=system"
+
+ exec_script_whitelist [optional]
+ A list of .gn/.gni files (not labels) that have permission to call the
+ exec_script function. If this list is defined, calls to exec_script will
+ be checked against this list and GN will fail if the current file isn't
+ in the list.
+
+ This is to allow the use of exec_script to be restricted since is easy to
+ use inappropriately. Wildcards are not supported. Files in the
+ secondary_source tree (if defined) should be referenced by ignoring the
+ secondary tree and naming them as if they are in the main tree.
+
+ If unspecified, the ability to call exec_script is unrestricted.
+
+ Example:
+ exec_script_whitelist = [
+ "//base/BUILD.gn",
+ "//build/my_config.gni",
+ ]
+
+ root [optional]
+ Label of the root build target. The GN build will start by loading the
+ build file containing this target name. This defaults to "//:" which will
+ cause the file //BUILD.gn to be loaded. Note that build_file_extension
+ applies to the default case as well.
+
+ The command-line switch --root-target will override this value (see "gn
+ help --root-target").
+
+ script_executable [optional]
+ Path to specific Python executable or other interpreter to use in
+ action targets and exec_script calls. By default GN searches the
+ PATH for Python to execute these scripts.
+
+ If set to the empty string, the path specified in action targets
+ and exec_script calls will be executed directly.
+
+ secondary_source [optional]
+ Label of an alternate directory tree to find input files. When searching
+ for a BUILD.gn file (or the build config file discussed above), the file
+ will first be looked for in the source root. If it's not found, the
+ secondary source root will be checked (which would contain a parallel
+ directory hierarchy).
+
+ This behavior is intended to be used when BUILD.gn files can't be checked
+ in to certain source directories for whatever reason.
+
+ The secondary source root must be inside the main source tree.
+
+ default_args [optional]
+ Scope containing the default overrides for declared arguments. These
+ overrides take precedence over the default values specified in the
+ declare_args() block, but can be overridden using --args or the
+ args.gn file.
+
+ This is intended to be used when subprojects declare arguments with
+ default values that need to be changed for whatever reason.
+
+ build_file_extension [optional]
+ If set to a non-empty string, this is added to the name of all build files
+ to load.
+ GN will look for build files named "BUILD.$build_file_extension.gn".
+ This is intended to be used during migrations or other situations where
+ there are two independent GN builds in the same directories.
+
+ ninja_required_version [optional]
+ When set specifies the minimum required version of Ninja. The default
+ required version is 1.7.2. Specifying a higher version might enable the
+ use of some of newer features that can make the build more efficient.
+
+Example .gn file contents
+
+ buildconfig = "//build/config/BUILDCONFIG.gn"
+
+ check_targets = [
+ "//doom_melon/*", # Check everything in this subtree.
+ "//tools:mind_controlling_ant", # Check this specific target.
+ ]
+
+ root = "//:root"
+
+ secondary_source = "//build/config/temporary_buildfiles/"
+
+ default_args = {
+ # Default to release builds for this project.
+ is_debug = false
+ is_component_build = false
+ }
+)";
+
+namespace {
+
+const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
+const char kDefaultArgsGn[] =
+ "# Set build arguments here. See `gn help buildargs`.";
+
+base::FilePath FindDotFile(const base::FilePath& current_dir) {
+ base::FilePath try_this_file = current_dir.Append(kGnFile);
+ if (base::PathExists(try_this_file))
+ return try_this_file;
+
+ base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
+ base::FilePath up_one_dir = with_no_slash.DirName();
+ if (up_one_dir == current_dir)
+ return base::FilePath(); // Got to the top.
+
+ return FindDotFile(up_one_dir);
+}
+
+// Called on any thread. Post the item to the builder on the main thread.
+void ItemDefinedCallback(MsgLoop* task_runner,
+ Builder* builder_call_on_main_thread_only,
+ std::unique_ptr<Item> item) {
+ DCHECK(item);
+
+ // Increment the work count for the duration of defining the item with the
+ // builder. Otherwise finishing this callback will race finishing loading
+ // files. If there is no other pending work at any point in the middle of
+ // this call completing on the main thread, the 'Complete' function will
+ // be signaled and we'll stop running with an incomplete build.
+ g_scheduler->IncrementWorkCount();
+
+ // Work around issue binding a unique_ptr with std::function by moving into a
+ // shared_ptr.
+ auto item_shared = std::make_shared<std::unique_ptr<Item>>(std::move(item));
+ task_runner->PostTask(
+ [builder_call_on_main_thread_only, item_shared]() mutable {
+ builder_call_on_main_thread_only->ItemDefined(std::move(*item_shared));
+ g_scheduler->DecrementWorkCount();
+ });
+}
+
+void DecrementWorkCount() {
+ g_scheduler->DecrementWorkCount();
+}
+
+#if defined(OS_WIN)
+
+std::u16string SysMultiByteTo16(std::string_view mb) {
+ if (mb.empty())
+ return std::u16string();
+
+ int mb_length = static_cast<int>(mb.length());
+ // Compute the length of the buffer.
+ int charcount = MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
+ if (charcount == 0)
+ return std::u16string();
+
+ std::u16string wide;
+ wide.resize(charcount);
+ MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, base::ToWCharT(&wide[0]),
+ charcount);
+
+ return wide;
+}
+
+// Given the path to a batch file that runs Python, extracts the name of the
+// executable actually implementing Python. Generally people write a batch file
+// to put something named "python" on the path, which then just redirects to
+// a python.exe somewhere else. This step decodes that setup. On failure,
+// returns empty path.
+base::FilePath PythonBatToExe(const base::FilePath& bat_path) {
+ // Note exciting double-quoting to allow spaces. The /c switch seems to check
+ // for quotes around the whole thing and then deletes them. If you want to
+ // quote the first argument in addition (to allow for spaces in the Python
+ // path, you need *another* set of quotes around that, likewise, we need
+ // two quotes at the end.
+ std::u16string command = u"cmd.exe /c \"\"";
+ command.append(bat_path.value());
+ command.append(u"\" -c \"import sys; print(sys.executable)\"\"");
+
+ std::string python_path;
+ std::string std_err;
+ int exit_code;
+ base::FilePath cwd;
+ GetCurrentDirectory(&cwd);
+ if (internal::ExecProcess(command, cwd, &python_path, &std_err, &exit_code) &&
+ exit_code == 0 && std_err.empty()) {
+ base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
+
+ // Python uses the system multibyte code page for sys.executable.
+ base::FilePath exe_path(SysMultiByteTo16(python_path));
+
+ // Check for reasonable output, cmd may have output an error message.
+ if (base::PathExists(exe_path))
+ return exe_path;
+ }
+ return base::FilePath();
+}
+
+// python_exe_name and python_bat_name can be empty but cannot be absolute
+// paths. They should be "python.exe" or "", etc., and "python.bat" or "", etc.
+base::FilePath FindWindowsPython(const base::FilePath& python_exe_name,
+ const base::FilePath& python_bat_name) {
+ char16_t current_directory[MAX_PATH];
+ ::GetCurrentDirectory(MAX_PATH, reinterpret_cast<LPWSTR>(current_directory));
+
+ // First search for python.exe in the current directory.
+ if (!python_exe_name.empty()) {
+ CHECK(python_exe_name.FinalExtension() == u".exe");
+ CHECK_EQ(python_exe_name.IsAbsolute(), false);
+ base::FilePath cur_dir_candidate_exe =
+ base::FilePath(current_directory).Append(python_exe_name);
+ if (base::PathExists(cur_dir_candidate_exe))
+ return cur_dir_candidate_exe;
+ }
+
+ // Get the path.
+ const char16_t kPathEnvVarName[] = u"Path";
+ DWORD path_length = ::GetEnvironmentVariable(
+ reinterpret_cast<LPCWSTR>(kPathEnvVarName), nullptr, 0);
+ if (path_length == 0)
+ return base::FilePath();
+ std::unique_ptr<char16_t[]> full_path(new char16_t[path_length]);
+ DWORD actual_path_length = ::GetEnvironmentVariable(
+ reinterpret_cast<LPCWSTR>(kPathEnvVarName),
+ reinterpret_cast<LPWSTR>(full_path.get()), path_length);
+ CHECK_EQ(path_length, actual_path_length + 1);
+
+ // Search for python.exe in the path.
+ for (const auto& component : base::SplitStringPiece(
+ std::u16string_view(full_path.get(), path_length), u";",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+ if (!python_exe_name.empty()) {
+ base::FilePath candidate_exe =
+ base::FilePath(component).Append(python_exe_name);
+ if (base::PathExists(candidate_exe))
+ return candidate_exe;
+ }
+
+ // Also allow python.bat, but convert into the .exe.
+ if (!python_bat_name.empty()) {
+ CHECK(python_bat_name.FinalExtension() == u".bat");
+ CHECK_EQ(python_bat_name.IsAbsolute(), false);
+ base::FilePath candidate_bat =
+ base::FilePath(component).Append(python_bat_name);
+ if (base::PathExists(candidate_bat)) {
+ base::FilePath python_exe = PythonBatToExe(candidate_bat);
+ if (!python_exe.empty())
+ return python_exe;
+ }
+ }
+ }
+ return base::FilePath();
+}
+#endif
+
+} // namespace
+
+const char Setup::kBuildArgFileName[] = "args.gn";
+
+Setup::Setup()
+ : build_settings_(),
+ loader_(new LoaderImpl(&build_settings_)),
+ builder_(loader_.get()),
+ dotfile_settings_(&build_settings_, std::string()),
+ dotfile_scope_(&dotfile_settings_) {
+ dotfile_settings_.set_toolchain_label(Label());
+
+ build_settings_.set_item_defined_callback(
+ [task_runner = scheduler_.task_runner(),
+ builder = &builder_](std::unique_ptr<Item> item) {
+ ItemDefinedCallback(task_runner, builder, std::move(item));
+ });
+
+ loader_->set_complete_callback(&DecrementWorkCount);
+ // The scheduler's task runner wasn't created when the Loader was created, so
+ // we need to set it now.
+ loader_->set_task_runner(scheduler_.task_runner());
+}
+
+bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
+ return DoSetup(build_dir, force_create,
+ *base::CommandLine::ForCurrentProcess());
+}
+
+bool Setup::DoSetup(const std::string& build_dir,
+ bool force_create,
+ const base::CommandLine& cmdline) {
+ Err err;
+ if (!DoSetupWithErr(build_dir, force_create, cmdline, &err)) {
+ err.PrintToStdout();
+ return false;
+ }
+ DCHECK(!err.has_error());
+ return true;
+}
+
+bool Setup::DoSetupWithErr(const std::string& build_dir,
+ bool force_create,
+ const base::CommandLine& cmdline,
+ Err* err) {
+ scheduler_.set_verbose_logging(cmdline.HasSwitch(switches::kVerbose));
+ if (cmdline.HasSwitch(switches::kTime) ||
+ cmdline.HasSwitch(switches::kTracelog))
+ EnableTracing();
+
+ ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
+
+ if (!FillSourceDir(cmdline, err))
+ return false;
+ if (!RunConfigFile(err))
+ return false;
+ if (!FillOtherConfig(cmdline, err))
+ return false;
+
+ // Must be after FillSourceDir to resolve.
+ if (!FillBuildDir(build_dir, !force_create, err))
+ return false;
+
+ // Apply project-specific default (if specified).
+ // Must happen before FillArguments().
+ if (default_args_) {
+ Scope::KeyValueMap overrides;
+ default_args_->GetCurrentScopeValues(&overrides);
+ build_settings_.build_args().AddDefaultArgOverrides(overrides);
+ }
+
+ if (fill_arguments_) {
+ if (!FillArguments(cmdline, err))
+ return false;
+ }
+ if (!FillPythonPath(cmdline, err))
+ return false;
+
+ // Check for unused variables in the .gn file.
+ if (!dotfile_scope_.CheckForUnusedVars(err)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Setup::Run() {
+ return Run(*base::CommandLine::ForCurrentProcess());
+}
+
+bool Setup::Run(const base::CommandLine& cmdline) {
+ RunPreMessageLoop();
+ if (!scheduler_.Run())
+ return false;
+ return RunPostMessageLoop(cmdline);
+}
+
+SourceFile Setup::GetBuildArgFile() const {
+ return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName);
+}
+
+void Setup::RunPreMessageLoop() {
+ // Will be decremented with the loader is drained.
+ g_scheduler->IncrementWorkCount();
+
+ // Load the root build file.
+ loader_->Load(root_build_file_, LocationRange(), Label());
+}
+
+bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
+ Err err;
+ if (!builder_.CheckForBadItems(&err)) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
+ if (cmdline.HasSwitch(switches::kFailOnUnusedArgs)) {
+ err.PrintToStdout();
+ return false;
+ }
+ err.PrintNonfatalToStdout();
+ OutputString(
+ "\nThe build continued as if that argument was "
+ "unspecified.\n\n");
+ // Nonfatal error.
+ }
+
+ if (check_public_headers_) {
+ std::vector<const Target*> all_targets = builder_.GetAllResolvedTargets();
+ std::vector<const Target*> to_check;
+ if (check_patterns()) {
+ commands::FilterTargetsByPatterns(all_targets, *check_patterns(),
+ &to_check);
+ } else if (no_check_patterns()) {
+ commands::FilterOutTargetsByPatterns(all_targets, *no_check_patterns(),
+ &to_check);
+ } else {
+ to_check = all_targets;
+ }
+
+ if (!commands::CheckPublicHeaders(&build_settings_, all_targets, to_check,
+ false, false, check_system_includes_)) {
+ return false;
+ }
+ }
+
+ // Write out tracing and timing if requested.
+ if (cmdline.HasSwitch(switches::kTime))
+ PrintLongHelp(SummarizeTraces());
+ if (cmdline.HasSwitch(switches::kTracelog))
+ SaveTraces(cmdline.GetSwitchValuePath(switches::kTracelog));
+
+ return true;
+}
+
+bool Setup::FillArguments(const base::CommandLine& cmdline, Err* err) {
+ // Use the args on the command line if specified, and save them. Do this even
+ // if the list is empty (this means clear any defaults).
+ // If --args is not set, args.gn file does not exist and gen_empty_args
+ // is set, generate an empty args.gn file with default comments.
+
+ base::FilePath build_arg_file =
+ build_settings_.GetFullPath(GetBuildArgFile());
+ auto switch_value = cmdline.GetSwitchValueASCII(switches::kArgs);
+ if (cmdline.HasSwitch(switches::kArgs) ||
+ (gen_empty_args_ && !PathExists(build_arg_file))) {
+ if (!FillArgsFromCommandLine(
+ switch_value.empty() ? kDefaultArgsGn : switch_value, err)) {
+ return false;
+ }
+ SaveArgsToFile();
+ return true;
+ }
+
+ // No command line args given, use the arguments from the build dir (if any).
+ return FillArgsFromFile(err);
+}
+
+bool Setup::FillArgsFromCommandLine(const std::string& args, Err* err) {
+ args_input_file_ = std::make_unique<InputFile>(SourceFile());
+ args_input_file_->SetContents(args);
+ args_input_file_->set_friendly_name("the command-line \"--args\"");
+ return FillArgsFromArgsInputFile(err);
+}
+
+bool Setup::FillArgsFromFile(Err* err) {
+ ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file");
+
+ SourceFile build_arg_source_file = GetBuildArgFile();
+ base::FilePath build_arg_file =
+ build_settings_.GetFullPath(build_arg_source_file);
+
+ std::string contents;
+ if (!base::ReadFileToString(build_arg_file, &contents))
+ return true; // File doesn't exist, continue with default args.
+
+ // Add a dependency on the build arguments file. If this changes, we want
+ // to re-generate the build.
+ g_scheduler->AddGenDependency(build_arg_file);
+
+ if (contents.empty())
+ return true; // Empty file, do nothing.
+
+ args_input_file_ = std::make_unique<InputFile>(build_arg_source_file);
+ args_input_file_->SetContents(contents);
+ args_input_file_->set_friendly_name(
+ "build arg file (use \"gn args <out_dir>\" to edit)");
+
+ setup_trace.Done(); // Only want to count the load as part of the trace.
+ return FillArgsFromArgsInputFile(err);
+}
+
+bool Setup::FillArgsFromArgsInputFile(Err* err) {
+ ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args");
+
+ args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), err);
+ if (err->has_error()) {
+ return false;
+ }
+
+ args_root_ = Parser::Parse(args_tokens_, err);
+ if (err->has_error()) {
+ return false;
+ }
+
+ Scope arg_scope(&dotfile_settings_);
+ // Set soure dir so relative imports in args work.
+ SourceDir root_source_dir =
+ SourceDirForCurrentDirectory(build_settings_.root_path());
+ arg_scope.set_source_dir(root_source_dir);
+ args_root_->Execute(&arg_scope, err);
+ if (err->has_error()) {
+ return false;
+ }
+
+ // Save the result of the command args.
+ Scope::KeyValueMap overrides;
+ arg_scope.GetCurrentScopeValues(&overrides);
+ build_settings_.build_args().AddArgOverrides(overrides);
+ build_settings_.build_args().set_build_args_dependency_files(
+ arg_scope.build_dependency_files());
+ return true;
+}
+
+bool Setup::SaveArgsToFile() {
+ ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file");
+
+ // For the first run, the build output dir might not be created yet, so do
+ // that so we can write a file into it. Ignore errors, we'll catch the error
+ // when we try to write a file to it below.
+ base::FilePath build_arg_file =
+ build_settings_.GetFullPath(GetBuildArgFile());
+ base::CreateDirectory(build_arg_file.DirName());
+
+ std::string contents = args_input_file_->contents();
+ commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive,
+ &contents, nullptr);
+#if defined(OS_WIN)
+ // Use Windows lineendings for this file since it will often open in
+ // Notepad which can't handle Unix ones.
+ base::ReplaceSubstringsAfterOffset(&contents, 0, "\n", "\r\n");
+#endif
+ if (base::WriteFile(build_arg_file, contents.c_str(),
+ static_cast<int>(contents.size())) == -1) {
+ Err(Location(), "Args file could not be written.",
+ "The file is \"" + FilePathToUTF8(build_arg_file) + "\"")
+ .PrintToStdout();
+ return false;
+ }
+
+ // Add a dependency on the build arguments file. If this changes, we want
+ // to re-generate the build.
+ g_scheduler->AddGenDependency(build_arg_file);
+
+ return true;
+}
+
+bool Setup::FillSourceDir(const base::CommandLine& cmdline, Err* err) {
+ // Find the .gn file.
+ base::FilePath root_path;
+
+ // Prefer the command line args to the config file.
+ base::FilePath relative_root_path =
+ cmdline.GetSwitchValuePath(switches::kRoot);
+ if (!relative_root_path.empty()) {
+ root_path = base::MakeAbsoluteFilePath(relative_root_path);
+ if (root_path.empty()) {
+ *err = Err(Location(), "Root source path not found.",
+ "The path \"" + FilePathToUTF8(relative_root_path) +
+ "\" doesn't exist.");
+ return false;
+ }
+
+ // When --root is specified, an alternate --dotfile can also be set.
+ // --dotfile should be a real file path and not a "//foo" source-relative
+ // path.
+ base::FilePath dotfile_path =
+ cmdline.GetSwitchValuePath(switches::kDotfile);
+ if (dotfile_path.empty()) {
+ dotfile_name_ = root_path.Append(kGnFile);
+ } else {
+ dotfile_name_ = base::MakeAbsoluteFilePath(dotfile_path);
+ if (dotfile_name_.empty()) {
+ *err = Err(Location(), "Could not load dotfile.",
+ "The file \"" + FilePathToUTF8(dotfile_path) +
+ "\" couldn't be loaded.");
+ return false;
+ }
+ // Only set dotfile_name if it was passed explicitly.
+ build_settings_.set_dotfile_name(dotfile_name_);
+ }
+ } else {
+ // In the default case, look for a dotfile and that also tells us where the
+ // source root is.
+ base::FilePath cur_dir;
+ base::GetCurrentDirectory(&cur_dir);
+ dotfile_name_ = FindDotFile(cur_dir);
+ if (dotfile_name_.empty()) {
+ *err = Err(
+ Location(), "Can't find source root.",
+ "I could not find a \".gn\" file in the current directory or any "
+ "parent,\nand the --root command-line argument was not specified.");
+ return false;
+ }
+ root_path = dotfile_name_.DirName();
+ }
+
+ base::FilePath root_realpath = base::MakeAbsoluteFilePath(root_path);
+ if (root_realpath.empty()) {
+ *err = Err(Location(), "Can't get the real root path.",
+ "I could not get the real path of \"" +
+ FilePathToUTF8(root_path) + "\".");
+ return false;
+ }
+ if (scheduler_.verbose_logging())
+ scheduler_.Log("Using source root", FilePathToUTF8(root_realpath));
+ build_settings_.SetRootPath(root_realpath);
+
+ return true;
+}
+
+bool Setup::FillBuildDir(const std::string& build_dir,
+ bool require_exists,
+ Err* err) {
+ SourceDir resolved =
+ SourceDirForCurrentDirectory(build_settings_.root_path())
+ .ResolveRelativeDir(Value(nullptr, build_dir), err,
+ build_settings_.root_path_utf8());
+ if (err->has_error()) {
+ return false;
+ }
+
+ base::FilePath build_dir_path = build_settings_.GetFullPath(resolved);
+ if (!base::CreateDirectory(build_dir_path)) {
+ *err = Err(Location(), "Can't create the build dir.",
+ "I could not create the build dir \"" +
+ FilePathToUTF8(build_dir_path) + "\".");
+ return false;
+ }
+ base::FilePath build_dir_realpath =
+ base::MakeAbsoluteFilePath(build_dir_path);
+ if (build_dir_realpath.empty()) {
+ *err = Err(Location(), "Can't get the real build dir path.",
+ "I could not get the real path of \"" +
+ FilePathToUTF8(build_dir_path) + "\".");
+ return false;
+ }
+ resolved = SourceDirForPath(build_settings_.root_path(), build_dir_realpath);
+
+ if (scheduler_.verbose_logging())
+ scheduler_.Log("Using build dir", resolved.value());
+
+ if (require_exists) {
+ if (!base::PathExists(
+ build_dir_path.Append(FILE_PATH_LITERAL("build.ninja")))) {
+ *err = Err(
+ Location(), "Not a build directory.",
+ "This command requires an existing build directory. I interpreted "
+ "your input\n\"" +
+ build_dir + "\" as:\n " + FilePathToUTF8(build_dir_path) +
+ "\nwhich doesn't seem to contain a previously-generated build.");
+ return false;
+ }
+ }
+
+ build_settings_.SetBuildDir(resolved);
+ return true;
+}
+
+// On Chromium repositories on Windows the Python executable can be specified as
+// python, python.bat, or python.exe (ditto for python3, and with or without a
+// full path specification). This handles all of these cases and returns a fully
+// specified path to a .exe file.
+// This is currently a NOP on other platforms.
+base::FilePath ProcessFileExtensions(base::FilePath script_executable) {
+#if defined(OS_WIN)
+ // If we have a relative path with no extension such as "python" or
+ // "python3" then do a path search on the name with .exe and .bat appended.
+ auto extension = script_executable.FinalExtension();
+ if (script_executable.IsAbsolute()) {
+ // Do translation from .bat to .exe but otherwise just pass through.
+ if (extension == u".bat")
+ script_executable = PythonBatToExe(script_executable);
+ } else {
+ if (extension == u"") {
+ // If no extension is specified then search the path for .exe and .bat
+ // variants.
+ script_executable =
+ FindWindowsPython(script_executable.ReplaceExtension(u".exe"),
+ script_executable.ReplaceExtension(u".bat"));
+ } else if (extension == u".bat") {
+ // Search the path just for the specified .bat.
+ script_executable =
+ FindWindowsPython(base::FilePath(), script_executable);
+ } else if (extension == u".exe") {
+ // Search the path just for the specified .exe.
+ script_executable =
+ FindWindowsPython(script_executable, base::FilePath());
+ }
+ }
+ script_executable = script_executable.NormalizePathSeparatorsTo('/');
+#endif
+ return script_executable;
+}
+
+bool Setup::FillPythonPath(const base::CommandLine& cmdline, Err* err) {
+ // Trace this since it tends to be a bit slow on Windows.
+ ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path");
+ const Value* value = dotfile_scope_.GetValue("script_executable", true);
+ if (cmdline.HasSwitch(switches::kScriptExecutable)) {
+ auto script_executable =
+ cmdline.GetSwitchValuePath(switches::kScriptExecutable);
+ build_settings_.set_python_path(ProcessFileExtensions(script_executable));
+ } else if (value) {
+ if (!value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+ build_settings_.set_python_path(
+ ProcessFileExtensions(UTF8ToFilePath(value->string_value())));
+ } else {
+#if defined(OS_WIN)
+ base::FilePath python_path =
+ ProcessFileExtensions(base::FilePath(u"python"));
+ if (!python_path.IsAbsolute()) {
+ scheduler_.Log("WARNING",
+ "Could not find python on path, using "
+ "just \"python.exe\"");
+ python_path = base::FilePath(u"python.exe");
+ }
+ build_settings_.set_python_path(python_path);
+#else
+ build_settings_.set_python_path(base::FilePath("python"));
+#endif
+ }
+ return true;
+}
+
+bool Setup::RunConfigFile(Err* err) {
+ if (scheduler_.verbose_logging())
+ scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
+
+ dotfile_input_file_ = std::make_unique<InputFile>(SourceFile("//.gn"));
+ if (!dotfile_input_file_->Load(dotfile_name_)) {
+ *err = Err(Location(), "Could not load dotfile.",
+ "The file \"" + FilePathToUTF8(dotfile_name_) +
+ "\" couldn't be loaded");
+ return false;
+ }
+
+ dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), err);
+ if (err->has_error()) {
+ return false;
+ }
+
+ dotfile_root_ = Parser::Parse(dotfile_tokens_, err);
+ if (err->has_error()) {
+ return false;
+ }
+
+ // Add a dependency on the build arguments file. If this changes, we want
+ // to re-generate the build. This causes the dotfile to make it into
+ // build.ninja.d.
+ g_scheduler->AddGenDependency(dotfile_name_);
+
+ // Also add a build dependency to the scope, which is used by `gn analyze`.
+ dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
+ dotfile_root_->Execute(&dotfile_scope_, err);
+ if (err->has_error()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Setup::FillOtherConfig(const base::CommandLine& cmdline, Err* err) {
+ SourceDir current_dir("//");
+ Label root_target_label(current_dir, "");
+
+ // Secondary source path, read from the config file if present.
+ // Read from the config file if present.
+ const Value* secondary_value =
+ dotfile_scope_.GetValue("secondary_source", true);
+ if (secondary_value) {
+ if (!secondary_value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+ build_settings_.SetSecondarySourcePath(
+ SourceDir(secondary_value->string_value()));
+ }
+
+ // Build file names.
+ const Value* build_file_extension_value =
+ dotfile_scope_.GetValue("build_file_extension", true);
+ if (build_file_extension_value) {
+ if (!build_file_extension_value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+
+ std::string extension = build_file_extension_value->string_value();
+ auto normalized_extension = UTF8ToFilePath(extension).value();
+ if (normalized_extension.find_first_of(base::FilePath::kSeparators) !=
+ base::FilePath::StringType::npos) {
+ *err = Err(Location(), "Build file extension '" + extension +
+ "' cannot " + "contain a path separator");
+ return false;
+ }
+ loader_->set_build_file_extension(extension);
+ }
+
+ // Ninja required version.
+ const Value* ninja_required_version_value =
+ dotfile_scope_.GetValue("ninja_required_version", true);
+ if (ninja_required_version_value) {
+ if (!ninja_required_version_value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+ std::optional<Version> version =
+ Version::FromString(ninja_required_version_value->string_value());
+ if (!version) {
+ Err(Location(), "Invalid Ninja version '" +
+ ninja_required_version_value->string_value() + "'")
+ .PrintToStdout();
+ return false;
+ }
+ build_settings_.set_ninja_required_version(*version);
+ }
+
+ // Root build file.
+ if (cmdline.HasSwitch(switches::kRootTarget)) {
+ auto switch_value = cmdline.GetSwitchValueASCII(switches::kRootTarget);
+ Value root_value(nullptr, switch_value);
+ root_target_label = Label::Resolve(current_dir, std::string_view(), Label(),
+ root_value, err);
+ if (err->has_error()) {
+ return false;
+ }
+ if (dotfile_scope_.GetValue("root", true)) {
+ // The "kRootTarget" switch overwrites the "root" variable in ".gn".
+ dotfile_scope_.MarkUsed("root");
+ }
+ } else {
+ const Value* root_value = dotfile_scope_.GetValue("root", true);
+ if (root_value) {
+ if (!root_value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+
+ root_target_label = Label::Resolve(current_dir, std::string_view(),
+ Label(), *root_value, err);
+ if (err->has_error()) {
+ return false;
+ }
+ }
+ }
+ // Set the root build file here in order to take into account the values of
+ // "build_file_extension" and "root".
+ root_build_file_ = loader_->BuildFileForLabel(root_target_label);
+ build_settings_.SetRootTargetLabel(root_target_label);
+
+ // Build config file.
+ const Value* build_config_value =
+ dotfile_scope_.GetValue("buildconfig", true);
+ if (!build_config_value) {
+ Err(Location(), "No build config file.",
+ "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
+ "\")\n"
+ "didn't specify a \"buildconfig\" value.")
+ .PrintToStdout();
+ return false;
+ } else if (!build_config_value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+ build_settings_.set_build_config_file(
+ SourceFile(build_config_value->string_value()));
+
+ // Targets to check.
+ const Value* check_targets_value =
+ dotfile_scope_.GetValue("check_targets", true);
+ if (check_targets_value) {
+ check_patterns_ = std::make_unique<std::vector<LabelPattern>>();
+ ExtractListOfLabelPatterns(&build_settings_, *check_targets_value,
+ current_dir, check_patterns_.get(), err);
+ if (err->has_error()) {
+ return false;
+ }
+ }
+
+ // Targets not to check.
+ const Value* no_check_targets_value =
+ dotfile_scope_.GetValue("no_check_targets", true);
+ if (no_check_targets_value) {
+ if (check_targets_value) {
+ Err(Location(), "Conflicting check settings.",
+ "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
+ "\")\n"
+ "specified both check_targets and no_check_targets and at most "
+ "one is allowed.")
+ .PrintToStdout();
+ return false;
+ }
+ no_check_patterns_ = std::make_unique<std::vector<LabelPattern>>();
+ ExtractListOfLabelPatterns(&build_settings_, *no_check_targets_value,
+ current_dir, no_check_patterns_.get(), err);
+ if (err->has_error()) {
+ return false;
+ }
+ }
+
+ const Value* check_system_includes_value =
+ dotfile_scope_.GetValue("check_system_includes", true);
+ if (check_system_includes_value) {
+ if (!check_system_includes_value->VerifyTypeIs(Value::BOOLEAN, err)) {
+ return false;
+ }
+ check_system_includes_ = check_system_includes_value->boolean_value();
+ }
+
+ // Fill exec_script_whitelist.
+ const Value* exec_script_whitelist_value =
+ dotfile_scope_.GetValue("exec_script_whitelist", true);
+ if (exec_script_whitelist_value) {
+ // Fill the list of targets to check.
+ if (!exec_script_whitelist_value->VerifyTypeIs(Value::LIST, err)) {
+ return false;
+ }
+ std::unique_ptr<SourceFileSet> whitelist =
+ std::make_unique<SourceFileSet>();
+ for (const auto& item : exec_script_whitelist_value->list_value()) {
+ if (!item.VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+ whitelist->insert(current_dir.ResolveRelativeFile(item, err));
+ if (err->has_error()) {
+ return false;
+ }
+ }
+ build_settings_.set_exec_script_whitelist(std::move(whitelist));
+ }
+
+ // Fill optional default_args.
+ const Value* default_args_value =
+ dotfile_scope_.GetValue("default_args", true);
+ if (default_args_value) {
+ if (!default_args_value->VerifyTypeIs(Value::SCOPE, err)) {
+ return false;
+ }
+
+ default_args_ = default_args_value->scope_value();
+ }
+
+ const Value* arg_file_template_value =
+ dotfile_scope_.GetValue("arg_file_template", true);
+ if (arg_file_template_value) {
+ if (!arg_file_template_value->VerifyTypeIs(Value::STRING, err)) {
+ return false;
+ }
+ SourceFile path(arg_file_template_value->string_value());
+ build_settings_.set_arg_file_template_path(path);
+ }
+
+ return true;
+}
diff --git a/gn/src/gn/setup.h b/gn/src/gn/setup.h
new file mode 100644
index 00000000000..1c5d280fe4c
--- /dev/null
+++ b/gn/src/gn/setup.h
@@ -0,0 +1,214 @@
+// 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 TOOLS_GN_SETUP_H_
+#define TOOLS_GN_SETUP_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gn/build_settings.h"
+#include "gn/builder.h"
+#include "gn/label_pattern.h"
+#include "gn/loader.h"
+#include "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/token.h"
+#include "gn/toolchain.h"
+
+class InputFile;
+class ParseNode;
+
+namespace base {
+class CommandLine;
+}
+
+extern const char kDotfile_Help[];
+
+// Helper class to set up the build settings and environment for the various
+// commands to run.
+class Setup {
+ public:
+ Setup();
+
+ // Configures the build for the current command line. On success returns
+ // true. On failure, prints the error and returns false.
+ //
+ // The parameter is the string the user specified for the build directory. We
+ // will try to interpret this as a SourceDir if possible, and will fail if is
+ // is malformed.
+ //
+ // With force_create = false, setup will fail if the build directory doesn't
+ // already exist with an args file in it. With force_create set to true, the
+ // directory will be created if necessary. Commands explicitly doing
+ // generation should set this to true to create it, but querying commands
+ // should set it to false to prevent creating oddly-named directories in case
+ // the user omits the build directory argument (which is easy to do).
+ //
+ // cmdline is the gn invocation command, with flags like --root and --dotfile.
+ // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
+ // is used.
+ bool DoSetup(const std::string& build_dir, bool force_create);
+ bool DoSetup(const std::string& build_dir,
+ bool force_create,
+ const base::CommandLine& cmdline);
+
+ // Same as DoSetup() but used for tests to capture error output.
+ bool DoSetupWithErr(const std::string& build_dir,
+ bool force_create,
+ const base::CommandLine& cmdline,
+ Err* err);
+
+ // Runs the load, returning true on success. On failure, prints the error
+ // and returns false. This includes both RunPreMessageLoop() and
+ // RunPostMessageLoop().
+ //
+ // cmdline is the gn invocation command, with flags like --root and --dotfile.
+ // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
+ // is used.
+ bool Run();
+ bool Run(const base::CommandLine& cmdline);
+
+ Scheduler& scheduler() { return scheduler_; }
+
+ // Returns the file used to store the build arguments. Note that the path
+ // might not exist.
+ SourceFile GetBuildArgFile() const;
+
+ // Sets whether the build arguments should be filled during setup from the
+ // command line/build argument file. This will be true by default. The use
+ // case for setting it to false is when editing build arguments, we don't
+ // want to rely on them being valid.
+ void set_fill_arguments(bool fa) { fill_arguments_ = fa; }
+
+ // After a successful run, setting this will additionally cause the public
+ // headers to be checked. Defaults to false.
+ void set_check_public_headers(bool s) { check_public_headers_ = s; }
+
+ // After a successful run, setting this will additionally cause system style
+ // includes to be checked. Defaults to false.
+ void set_check_system_includes(bool s) { check_system_includes_ = s; }
+
+ bool check_system_includes() const { return check_system_includes_; }
+
+ // Before DoSetup, setting this will generate an empty args.gn if
+ // it does not exist and set up correct dependencies for it.
+ void set_gen_empty_args(bool ge) { gen_empty_args_ = ge; }
+
+ // Read from the .gn file, these are the targets to check. If the .gn file
+ // does not specify anything, this will be null. If the .gn file specifies
+ // the empty list, this will be non-null but empty.
+ const std::vector<LabelPattern>* check_patterns() const {
+ return check_patterns_.get();
+ }
+
+ // Read from the .gn file, these are the targets *not* to check. If the .gn
+ // file does not specify anything, this will be null. If the .gn file
+ // specifies the empty list, this will be non-null but empty. At least one of
+ // check_patterns() and no_check_patterns() will be null.
+ const std::vector<LabelPattern>* no_check_patterns() const {
+ return no_check_patterns_.get();
+ }
+
+ BuildSettings& build_settings() { return build_settings_; }
+ Builder& builder() { return builder_; }
+ LoaderImpl* loader() { return loader_.get(); }
+
+ const SourceFile& GetDotFile() const { return dotfile_input_file_->name(); }
+
+ // Name of the file in the root build directory that contains the build
+ // arguments.
+ static const char kBuildArgFileName[];
+
+ private:
+ // Performs the two sets of operations to run the generation before and after
+ // the message loop is run.
+ void RunPreMessageLoop();
+ bool RunPostMessageLoop(const base::CommandLine& cmdline);
+
+ // Fills build arguments. Returns true on success.
+ bool FillArguments(const base::CommandLine& cmdline, Err* err);
+
+ // Fills the build arguments from the command line or from the build arg file.
+ bool FillArgsFromCommandLine(const std::string& args, Err* err);
+ bool FillArgsFromFile(Err* err);
+
+ // Given an already-loaded args_input_file_, parses and saves the resulting
+ // arguments. Backend for the different FillArgs variants.
+ bool FillArgsFromArgsInputFile(Err* err);
+
+ // Writes the build arguments to the build arg file.
+ bool SaveArgsToFile();
+
+ // Fills the root directory into the settings. Returns true on success, or
+ // |err| filled out.
+ bool FillSourceDir(const base::CommandLine& cmdline, Err* err);
+
+ // Fills the build directory given the value the user has specified.
+ // Must happen after FillSourceDir so we can resolve source-relative
+ // paths. If require_exists is false, it will fail if the dir doesn't exist.
+ bool FillBuildDir(const std::string& build_dir,
+ bool require_exists,
+ Err* err);
+
+ // Fills the python path portion of the command line. On failure, sets
+ // it to just "python".
+ bool FillPythonPath(const base::CommandLine& cmdline, Err* err);
+
+ // Run config file.
+ bool RunConfigFile(Err* err);
+
+ bool FillOtherConfig(const base::CommandLine& cmdline, Err* err);
+
+ BuildSettings build_settings_;
+ scoped_refptr<LoaderImpl> loader_;
+ Builder builder_;
+
+ SourceFile root_build_file_;
+
+ bool check_public_headers_ = false;
+ bool check_system_includes_ = false;
+
+ // See getter for info.
+ std::unique_ptr<std::vector<LabelPattern>> check_patterns_;
+ std::unique_ptr<std::vector<LabelPattern>> no_check_patterns_;
+
+ Scheduler scheduler_;
+
+ // These settings and toolchain are used to interpret the command line and
+ // dot file.
+ Settings dotfile_settings_;
+ Scope dotfile_scope_;
+
+ // State for invoking the dotfile.
+ base::FilePath dotfile_name_;
+ std::unique_ptr<InputFile> dotfile_input_file_;
+ std::vector<Token> dotfile_tokens_;
+ std::unique_ptr<ParseNode> dotfile_root_;
+
+ // Default overrides, specified in the dotfile.
+ // Owned by the Value (if it exists) in the dotfile_scope_.
+ const Scope* default_args_ = nullptr;
+
+ // Set to true when we should populate the build arguments from the command
+ // line or build argument file. See setter above.
+ bool fill_arguments_ = true;
+
+ // Generate an empty args.gn file if it does not exists.
+ bool gen_empty_args_ = false;
+
+ // State for invoking the command line args. We specifically want to keep
+ // this around for the entire run so that Values can blame to the command
+ // line when we issue errors about them.
+ std::unique_ptr<InputFile> args_input_file_;
+ std::vector<Token> args_tokens_;
+ std::unique_ptr<ParseNode> args_root_;
+
+ DISALLOW_COPY_AND_ASSIGN(Setup);
+};
+
+#endif // TOOLS_GN_SETUP_H_
diff --git a/gn/src/gn/setup_unittest.cc b/gn/src/gn/setup_unittest.cc
new file mode 100644
index 00000000000..af10a6eec94
--- /dev/null
+++ b/gn/src/gn/setup_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2018 The Chromium 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 "gn/setup.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "gn/filesystem_utils.h"
+#include "gn/switches.h"
+#include "gn/test_with_scheduler.h"
+#include "util/build_config.h"
+
+using SetupTest = TestWithScheduler;
+
+static void WriteFile(const base::FilePath& file, const std::string& data) {
+ CHECK_EQ(static_cast<int>(data.size()), // Way smaller than INT_MAX.
+ base::WriteFile(file, data.data(), data.size()));
+}
+
+TEST_F(SetupTest, DotGNFileIsGenDep) {
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+ // pass it as --root.
+ base::ScopedTempDir in_temp_dir;
+ ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+ base::FilePath in_path = in_temp_dir.GetPath();
+ base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+ WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n");
+ WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+ cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+
+ // Create another temp dir for writing the generated files to.
+ base::ScopedTempDir build_temp_dir;
+ ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+ // Run setup and check that the .gn file is in the scheduler's gen deps.
+ Setup setup;
+ EXPECT_TRUE(
+ setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline));
+ std::vector<base::FilePath> gen_deps = g_scheduler->GetGenDependencies();
+ ASSERT_EQ(1u, gen_deps.size());
+ EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
+}
+
+static void RunExtensionCheckTest(std::string extension,
+ bool success,
+ const std::string& expected_error_message) {
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+ // pass it as --root.
+ base::ScopedTempDir in_temp_dir;
+ ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+ base::FilePath in_path = in_temp_dir.GetPath();
+ base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+ WriteFile(dot_gn_name,
+ "buildconfig = \"//BUILDCONFIG.gn\"\n\
+ build_file_extension = \"" +
+ extension + "\"");
+ WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+ cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+
+ // Create another temp dir for writing the generated files to.
+ base::ScopedTempDir build_temp_dir;
+ ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+ // Run setup and check that its status.
+ Setup setup;
+ Err err;
+ EXPECT_EQ(success,
+ setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()), true,
+ cmdline, &err));
+ EXPECT_EQ(success, !err.has_error());
+ EXPECT_EQ(expected_error_message, err.message());
+}
+
+TEST_F(SetupTest, NoSeparatorInExtension) {
+ RunExtensionCheckTest(
+ "hello" + std::string(1, base::FilePath::kSeparators[0]) + "world", false,
+#if defined(OS_WIN)
+ "Build file extension 'hello\\world' cannot contain a path separator"
+#else
+ "Build file extension 'hello/world' cannot contain a path separator"
+#endif
+ );
+}
+
+TEST_F(SetupTest, Extension) {
+ RunExtensionCheckTest("yay", true, "");
+}
diff --git a/gn/src/gn/source_dir.cc b/gn/src/gn/source_dir.cc
new file mode 100644
index 00000000000..ce92cf22c78
--- /dev/null
+++ b/gn/src/gn/source_dir.cc
@@ -0,0 +1,155 @@
+// 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 "gn/source_dir.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "gn/filesystem_utils.h"
+#include "gn/source_file.h"
+#include "util/build_config.h"
+
+namespace {
+
+void AssertValueSourceDirString(const std::string_view s) {
+ if (!s.empty()) {
+#if defined(OS_WIN)
+ DCHECK(s[0] == '/' ||
+ (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
+#else
+ DCHECK(s[0] == '/');
+#endif
+ DCHECK(EndsWithSlash(s)) << s;
+ }
+}
+
+// Validates input value (input_value) and sets proper error message.
+// Note: Parameter blame_input is used only for generating error message.
+template <typename StringType>
+bool ValidateResolveInput(bool as_file,
+ const Value& blame_input_value,
+ const StringType& input_value,
+ Err* err) {
+ if (as_file) {
+ // It's an error to resolve an empty string or one that is a directory
+ // (indicated by a trailing slash) because this is the function that expects
+ // to return a file.
+ if (input_value.empty()) {
+ *err = Err(blame_input_value, "Empty file path.",
+ "You can't use empty strings as file paths.");
+ return false;
+ } else if (input_value[input_value.size() - 1] == '/') {
+ std::string help = "You specified the path\n ";
+ help.append(std::string(input_value));
+ help.append(
+ "\nand it ends in a slash, indicating you think it's a directory."
+ "\nBut here you're supposed to be listing a file.");
+ *err = Err(blame_input_value, "File path ends in a slash.", help);
+ return false;
+ }
+ } else if (input_value.empty()) {
+ *err = Err(blame_input_value, "Empty directory path.",
+ "You can't use empty strings as directories.");
+ return false;
+ }
+ return true;
+}
+
+static StringAtom SourceDirStringAtom(const std::string_view s) {
+ if (EndsWithSlash(s)) { // Avoid allocation when possible.
+ AssertValueSourceDirString(s);
+ return StringAtom(s);
+ }
+
+ std::string str;
+ str.reserve(s.size() + 1);
+ str += s;
+ str.push_back('/');
+ AssertValueSourceDirString(str);
+ return StringAtom(str);
+}
+
+} // namespace
+
+SourceDir::SourceDir(const std::string_view s)
+ : value_(SourceDirStringAtom(s)) {}
+
+template <typename StringType>
+std::string SourceDir::ResolveRelativeAs(
+ bool as_file,
+ const Value& blame_input_value,
+ const StringType& input_value,
+ Err* err,
+ const std::string_view& source_root) const {
+ if (!ValidateResolveInput<StringType>(as_file, blame_input_value, input_value,
+ err)) {
+ return std::string();
+ }
+ return ResolveRelative(input_value, value_.str(), as_file, source_root);
+}
+
+SourceFile SourceDir::ResolveRelativeFile(
+ const Value& p,
+ Err* err,
+ const std::string_view& source_root) const {
+ SourceFile ret;
+
+ if (!p.VerifyTypeIs(Value::STRING, err))
+ return ret;
+
+ const std::string& input_string = p.string_value();
+ if (!ValidateResolveInput<std::string>(true, p, input_string, err))
+ return ret;
+
+ ret.SetValue(ResolveRelative(input_string, value_.str(), true, source_root));
+ return ret;
+}
+
+std::string SourceDir::ResolveRelativeAs(bool as_file,
+ const Value& v,
+ Err* err,
+ const std::string_view& source_root,
+ const std::string* v_value) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return std::string();
+
+ if (!v_value) {
+ v_value = &v.string_value();
+ }
+ std::string result =
+ ResolveRelativeAs(as_file, v, *v_value, err, source_root);
+ if (!as_file)
+ AssertValueSourceDirString(result);
+ return result;
+}
+
+SourceDir SourceDir::ResolveRelativeDir(
+ const Value& v,
+ Err* err,
+ const std::string_view& source_root) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return SourceDir();
+
+ return ResolveRelativeDir<std::string>(v, v.string_value(), err, source_root);
+}
+
+base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
+ return ResolvePath(value_.str(), false, source_root);
+}
+
+// Explicit template instantiation
+template std::string SourceDir::ResolveRelativeAs(
+ bool as_file,
+ const Value& blame_input_value,
+ const std::string& input_value,
+ Err* err,
+ const std::string_view& source_root) const;
+
+template std::string SourceDir::ResolveRelativeAs(
+ bool as_file,
+ const Value& blame_input_value,
+ const std::string_view& input_value,
+ Err* err,
+ const std::string_view& source_root) const;
diff --git a/gn/src/gn/source_dir.h b/gn/src/gn/source_dir.h
new file mode 100644
index 00000000000..5c436f47bdc
--- /dev/null
+++ b/gn/src/gn/source_dir.h
@@ -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.
+
+#ifndef TOOLS_GN_SOURCE_DIR_H_
+#define TOOLS_GN_SOURCE_DIR_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <string>
+#include <string_view>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+
+#include "gn/string_atom.h"
+
+class Err;
+class SourceFile;
+class Value;
+
+// Represents a directory within the source tree. Source dirs begin and end in
+// slashes.
+//
+// If there is one slash at the beginning, it will mean a system-absolute file
+// path. On Windows, absolute system paths will be of the form "/C:/foo/bar".
+//
+// Two slashes at the beginning indicate a path relative to the source root.
+class SourceDir {
+ public:
+ SourceDir() = default;
+
+ SourceDir(const std::string_view s);
+
+ // Resolves a file or dir name (based on as_file parameter) relative
+ // to this source directory. Will return an empty string on error
+ // and set the give *err pointer (required). Empty input is always an error.
+ //
+ // Passed non null v_value will be used to resolve path (in cases where
+ // a substring has been extracted from the value, as with label resolution).
+ // In this use case parameter v is used to generate proper error.
+ //
+ // If source_root is supplied, these functions will additionally handle the
+ // case where the input is a system-absolute but still inside the source
+ // tree. This is the case for some external tools.
+ std::string ResolveRelativeAs(
+ bool as_file,
+ const Value& v,
+ Err* err,
+ const std::string_view& source_root = std::string_view(),
+ const std::string* v_value = nullptr) const;
+
+ // Like ResolveRelativeAs above, but allows one to produce result
+ // without overhead for string conversion (on input value).
+ template <typename StringType>
+ std::string ResolveRelativeAs(
+ bool as_file,
+ const Value& blame_input_value,
+ const StringType& input_value,
+ Err* err,
+ const std::string_view& source_root = std::string_view()) const;
+
+ // Wrapper for ResolveRelativeAs.
+ SourceFile ResolveRelativeFile(
+ const Value& p,
+ Err* err,
+ const std::string_view& source_root = std::string_view()) const;
+
+ // Wrapper for ResolveRelativeAs.
+ template <typename StringType>
+ SourceDir ResolveRelativeDir(
+ const Value& blame_input_value,
+ const StringType& input_value,
+ Err* err,
+ const std::string_view& source_root = std::string_view()) const {
+ SourceDir ret;
+ ret.value_ = StringAtom(ResolveRelativeAs<StringType>(
+ false, blame_input_value, input_value, err, source_root));
+ return ret;
+ }
+
+ // Wrapper for ResolveRelativeDir where input_value equals to
+ // v.string_value().
+ SourceDir ResolveRelativeDir(
+ const Value& v,
+ Err* err,
+ const std::string_view& source_root = std::string_view()) const;
+
+ // Resolves this source file relative to some given source root. Returns
+ // an empty file path on error.
+ base::FilePath Resolve(const base::FilePath& source_root) const;
+
+ bool is_null() const { return value_.empty(); }
+ const std::string& value() const { return value_.str(); }
+
+ // Returns true if this path starts with a "//" which indicates a path
+ // from the source root.
+ bool is_source_absolute() const {
+ const std::string& v = value_.str();
+ return v.size() >= 2 && v[0] == '/' && v[1] == '/';
+ }
+
+ // Returns true if this path starts with a single slash which indicates a
+ // system-absolute path.
+ bool is_system_absolute() const { return !is_source_absolute(); }
+
+ // Returns a source-absolute path starting with only one slash at the
+ // beginning (normally source-absolute paths start with two slashes to mark
+ // them as such). This is normally used when concatenating directories
+ // together.
+ //
+ // This function asserts that the directory is actually source-absolute. The
+ // return value points into our buffer.
+ std::string_view SourceAbsoluteWithOneSlash() const {
+ CHECK(is_source_absolute());
+ const std::string& v = value_.str();
+ return std::string_view(&v[1], v.size() - 1);
+ }
+
+ // Returns a path that does not end with a slash.
+ //
+ // This function simply returns the reference to the value if the path is a
+ // root, e.g. "/" or "//".
+ std::string_view SourceWithNoTrailingSlash() const {
+ const std::string& v = value_.str();
+ if (v.size() > 2)
+ return std::string_view(&v[0], v.size() - 1);
+ return std::string_view(v);
+ }
+
+ bool operator==(const SourceDir& other) const {
+ return value_.SameAs(other.value_);
+ }
+ bool operator!=(const SourceDir& other) const { return !operator==(other); }
+ bool operator<(const SourceDir& other) const { return value_ < other.value_; }
+
+ size_t hash() const { return value_.hash(); }
+
+ private:
+ friend class SourceFile;
+ StringAtom value_;
+};
+
+namespace std {
+
+template <>
+struct hash<SourceDir> {
+ std::size_t operator()(const SourceDir& v) const { return v.hash(); }
+};
+
+} // namespace std
+
+#endif // TOOLS_GN_SOURCE_DIR_H_
diff --git a/gn/src/gn/source_dir_unittest.cc b/gn/src/gn/source_dir_unittest.cc
new file mode 100644
index 00000000000..148ab92f325
--- /dev/null
+++ b/gn/src/gn/source_dir_unittest.cc
@@ -0,0 +1,208 @@
+// 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 "gn/source_dir.h"
+#include "gn/err.h"
+#include "gn/source_file.h"
+#include "gn/value.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(SourceDir, ResolveRelativeFile) {
+ Err err;
+ SourceDir base("//base/");
+#if defined(OS_WIN)
+ std::string_view source_root("C:/source/root");
+#else
+ std::string_view source_root("/source/root");
+#endif
+
+ // Empty input is an error.
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, std::string()), &err,
+ source_root) == SourceFile());
+ EXPECT_TRUE(err.has_error());
+
+ // These things are directories, so should be an error.
+ err = Err();
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//foo/bar/"), &err,
+ source_root) == SourceFile());
+ EXPECT_TRUE(err.has_error());
+
+ err = Err();
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "bar/"), &err,
+ source_root) == SourceFile());
+ EXPECT_TRUE(err.has_error());
+
+ // Absolute paths should be passed unchanged.
+ err = Err();
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//foo"), &err,
+ source_root) == SourceFile("//foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "/foo"), &err,
+ source_root) == SourceFile("/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ // Basic relative stuff.
+ EXPECT_TRUE(
+ base.ResolveRelativeFile(Value(nullptr, "foo"), &err, source_root) ==
+ SourceFile("//base/foo"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(
+ base.ResolveRelativeFile(Value(nullptr, "./foo"), &err, source_root) ==
+ SourceFile("//base/foo"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "../foo"), &err,
+ source_root) == SourceFile("//foo"));
+ EXPECT_FALSE(err.has_error());
+
+// If the given relative path points outside the source root, we
+// expect an absolute path.
+#if defined(OS_WIN)
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "../../foo"), &err,
+ source_root) ==
+ SourceFile("/C:/source/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(
+ base.ResolveRelativeFile(Value(nullptr, "//../foo"), &err, source_root) ==
+ SourceFile("/C:/source/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../root/foo"), &err,
+ source_root) ==
+ SourceFile("/C:/source/root/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../../../foo/bar"),
+ &err,
+ source_root) == SourceFile("/foo/bar"));
+ EXPECT_FALSE(err.has_error());
+#else
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "../../foo"), &err,
+ source_root) ==
+ SourceFile("/source/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(
+ base.ResolveRelativeFile(Value(nullptr, "//../foo"), &err, source_root) ==
+ SourceFile("/source/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../root/foo"), &err,
+ source_root) ==
+ SourceFile("/source/root/foo"));
+ EXPECT_FALSE(err.has_error());
+
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../../../foo/bar"),
+ &err,
+ source_root) == SourceFile("/foo/bar"));
+ EXPECT_FALSE(err.has_error());
+#endif
+
+#if defined(OS_WIN)
+ // Note that we don't canonicalize the backslashes to forward slashes.
+ // This could potentially be changed in the future which would mean we should
+ // just change the expected result.
+ EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "C:\\foo\\bar.txt"), &err,
+ source_root) ==
+ SourceFile("/C:/foo/bar.txt"));
+ EXPECT_FALSE(err.has_error());
+#endif
+}
+
+TEST(SourceDir, ResolveRelativeDir) {
+ Err err;
+ SourceDir base("//base/");
+#if defined(OS_WIN)
+ std::string_view source_root("C:/source/root");
+#else
+ std::string_view source_root("/source/root");
+#endif
+
+ // Empty input is an error.
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, std::string()), &err,
+ source_root) == SourceDir());
+ EXPECT_TRUE(err.has_error());
+
+ // Absolute paths should be passed unchanged.
+ err = Err();
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "//foo"), &err,
+ source_root) == SourceDir("//foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "/foo"), &err,
+ source_root) == SourceDir("/foo/"));
+ EXPECT_FALSE(err.has_error());
+
+ // Basic relative stuff.
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "foo"), &err,
+ source_root) == SourceDir("//base/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "./foo"), &err,
+ source_root) == SourceDir("//base/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "../foo"), &err,
+ source_root) == SourceDir("//foo/"));
+ EXPECT_FALSE(err.has_error());
+
+// If the given relative path points outside the source root, we
+// expect an absolute path.
+#if defined(OS_WIN)
+ EXPECT_TRUE(
+ base.ResolveRelativeDir(Value(nullptr, "../../foo"), &err, source_root) ==
+ SourceDir("/C:/source/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(
+ base.ResolveRelativeDir(Value(nullptr, "//../foo"), &err, source_root) ==
+ SourceDir("/C:/source/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "//.."), &err,
+ source_root) == SourceDir("/C:/source/"));
+ EXPECT_FALSE(err.has_error());
+#else
+ EXPECT_TRUE(
+ base.ResolveRelativeDir(Value(nullptr, "../../foo"), &err, source_root) ==
+ SourceDir("/source/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(
+ base.ResolveRelativeDir(Value(nullptr, "//../foo"), &err, source_root) ==
+ SourceDir("/source/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "//.."), &err,
+ source_root) == SourceDir("/source/"));
+ EXPECT_FALSE(err.has_error());
+#endif
+
+#if defined(OS_WIN)
+ // Canonicalize the existing backslashes to forward slashes and add a
+ // leading slash if necessary.
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "\\C:\\foo"), &err) ==
+ SourceDir("/C:/foo/"));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "C:\\foo"), &err) ==
+ SourceDir("/C:/foo/"));
+ EXPECT_FALSE(err.has_error());
+#endif
+}
+
+TEST(SourceDir, SourceWithNoTrailingSlash) {
+ Err err;
+ SourceDir base("//base/");
+ SourceDir base_no_slash("//base/");
+ EXPECT_EQ(base.SourceWithNoTrailingSlash(), "//base");
+ EXPECT_EQ(base_no_slash.SourceWithNoTrailingSlash(), "//base");
+
+ SourceDir relative_root("//");
+ EXPECT_EQ(relative_root.SourceWithNoTrailingSlash(), "//");
+
+#if defined(OS_WIN)
+ SourceDir root("C:/");
+ SourceDir root_no_slash("C:");
+ EXPECT_EQ(root.SourceWithNoTrailingSlash(), "C:");
+ EXPECT_EQ(root_no_slash.SourceWithNoTrailingSlash(), "C:");
+#else
+ SourceDir root("/");
+ EXPECT_EQ(root.SourceWithNoTrailingSlash(), "/");
+#endif
+}
diff --git a/gn/src/gn/source_file.cc b/gn/src/gn/source_file.cc
new file mode 100644
index 00000000000..52066d76f07
--- /dev/null
+++ b/gn/src/gn/source_file.cc
@@ -0,0 +1,140 @@
+// 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 "gn/source_file.h"
+
+#include "base/logging.h"
+#include "gn/filesystem_utils.h"
+#include "gn/source_dir.h"
+#include "util/build_config.h"
+
+namespace {
+
+void AssertValueSourceFileString(const std::string& s) {
+#if defined(OS_WIN)
+ DCHECK(s[0] == '/' ||
+ (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
+#else
+ DCHECK(s[0] == '/');
+#endif
+ DCHECK(!EndsWithSlash(s)) << s;
+}
+
+SourceFile::Type GetSourceFileType(const std::string& file) {
+ std::string_view extension = FindExtension(&file);
+ if (extension == "cc" || extension == "cpp" || extension == "cxx" ||
+ extension == "c++")
+ return SourceFile::SOURCE_CPP;
+ if (extension == "h" || extension == "hpp" || extension == "hxx" ||
+ extension == "hh" || extension == "inc" || extension == "ipp" ||
+ extension == "inl")
+ return SourceFile::SOURCE_H;
+ if (extension == "c")
+ return SourceFile::SOURCE_C;
+ if (extension == "m")
+ return SourceFile::SOURCE_M;
+ if (extension == "mm")
+ return SourceFile::SOURCE_MM;
+ if (extension == "modulemap")
+ return SourceFile::SOURCE_MODULEMAP;
+ if (extension == "rc")
+ return SourceFile::SOURCE_RC;
+ if (extension == "S" || extension == "s" || extension == "asm")
+ return SourceFile::SOURCE_S;
+ if (extension == "o" || extension == "obj")
+ return SourceFile::SOURCE_O;
+ if (extension == "def")
+ return SourceFile::SOURCE_DEF;
+ if (extension == "rs")
+ return SourceFile::SOURCE_RS;
+ if (extension == "go")
+ return SourceFile::SOURCE_GO;
+ if (extension == "swift")
+ return SourceFile::SOURCE_SWIFT;
+ if (extension == "swiftmodule")
+ return SourceFile::SOURCE_SWIFTMODULE;
+
+ return SourceFile::SOURCE_UNKNOWN;
+}
+
+std::string Normalized(std::string value) {
+ DCHECK(!value.empty());
+ AssertValueSourceFileString(value);
+ NormalizePath(&value);
+ return value;
+}
+
+} // namespace
+
+SourceFile::SourceFile(const std::string& value)
+ : SourceFile(StringAtom(Normalized(value))) {}
+
+SourceFile::SourceFile(std::string&& value)
+ : SourceFile(StringAtom(Normalized(std::move(value)))) {}
+
+SourceFile::SourceFile(StringAtom value) : value_(value) {
+ type_ = GetSourceFileType(value_.str());
+}
+
+std::string SourceFile::GetName() const {
+ if (is_null())
+ return std::string();
+
+ const std::string& value = value_.str();
+ DCHECK(value.find('/') != std::string::npos);
+ size_t last_slash = value.rfind('/');
+ return std::string(&value[last_slash + 1], value.size() - last_slash - 1);
+}
+
+SourceDir SourceFile::GetDir() const {
+ if (is_null())
+ return SourceDir();
+
+ const std::string& value = value_.str();
+ DCHECK(value.find('/') != std::string::npos);
+ size_t last_slash = value.rfind('/');
+ return SourceDir(value.substr(0, last_slash + 1));
+}
+
+base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const {
+ return ResolvePath(value_.str(), true, source_root);
+}
+
+void SourceFile::SetValue(const std::string& value) {
+ value_ = StringAtom(value);
+ type_ = GetSourceFileType(value);
+}
+
+SourceFileTypeSet::SourceFileTypeSet() : empty_(true) {
+ memset(flags_, 0,
+ sizeof(bool) * static_cast<int>(SourceFile::SOURCE_NUMTYPES));
+}
+
+bool SourceFileTypeSet::CSourceUsed() const {
+ return empty_ || Get(SourceFile::SOURCE_CPP) ||
+ Get(SourceFile::SOURCE_MODULEMAP) || Get(SourceFile::SOURCE_H) ||
+ Get(SourceFile::SOURCE_C) || Get(SourceFile::SOURCE_M) ||
+ Get(SourceFile::SOURCE_MM) || Get(SourceFile::SOURCE_RC) ||
+ Get(SourceFile::SOURCE_S) || Get(SourceFile::SOURCE_O) ||
+ Get(SourceFile::SOURCE_DEF);
+}
+
+bool SourceFileTypeSet::RustSourceUsed() const {
+ return Get(SourceFile::SOURCE_RS);
+}
+
+bool SourceFileTypeSet::GoSourceUsed() const {
+ return Get(SourceFile::SOURCE_GO);
+}
+
+bool SourceFileTypeSet::SwiftSourceUsed() const {
+ return Get(SourceFile::SOURCE_SWIFT);
+}
+
+bool SourceFileTypeSet::MixedSourceUsed() const {
+ return (1 << static_cast<int>(CSourceUsed())
+ << static_cast<int>(RustSourceUsed())
+ << static_cast<int>(GoSourceUsed())
+ << static_cast<int>(SwiftSourceUsed())) > 2;
+}
diff --git a/gn/src/gn/source_file.h b/gn/src/gn/source_file.h
new file mode 100644
index 00000000000..5ad8f6279b4
--- /dev/null
+++ b/gn/src/gn/source_file.h
@@ -0,0 +1,172 @@
+// 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 TOOLS_GN_SOURCE_FILE_H_
+#define TOOLS_GN_SOURCE_FILE_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <string>
+#include <string_view>
+
+#include "base/containers/flat_set.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+
+#include "gn/string_atom.h"
+
+class SourceDir;
+
+// Represents a file within the source tree. Always begins in a slash, never
+// ends in one.
+class SourceFile {
+ public:
+ // This should be sequential integers starting from 0 so they can be used as
+ // array indices.
+ enum Type {
+ SOURCE_UNKNOWN = 0,
+ SOURCE_ASM,
+ SOURCE_C,
+ SOURCE_CPP,
+ SOURCE_H,
+ SOURCE_M,
+ SOURCE_MM,
+ SOURCE_MODULEMAP,
+ SOURCE_S,
+ SOURCE_RC,
+ SOURCE_O, // Object files can be inputs, too. Also counts .obj.
+ SOURCE_DEF,
+
+ SOURCE_RS,
+ SOURCE_GO,
+ SOURCE_SWIFT,
+ SOURCE_SWIFTMODULE,
+
+ // Must be last.
+ SOURCE_NUMTYPES,
+ };
+
+ SourceFile() = default;
+
+ // Takes a known absolute source file. Always begins in a slash.
+ explicit SourceFile(const std::string& value);
+ explicit SourceFile(std::string&& value);
+ explicit SourceFile(StringAtom value);
+
+ ~SourceFile() = default;
+
+ bool is_null() const { return value_.empty(); }
+ const std::string& value() const { return value_.str(); }
+ Type type() const { return type_; }
+
+ // Returns everything after the last slash.
+ std::string GetName() const;
+ SourceDir GetDir() const;
+
+ // Resolves this source file relative to some given source root. Returns
+ // an empty file path on error.
+ base::FilePath Resolve(const base::FilePath& source_root) const;
+
+ // Returns true if this file starts with a "//" which indicates a path
+ // from the source root.
+ bool is_source_absolute() const {
+ return value().size() >= 2 && value()[0] == '/' && value()[1] == '/';
+ }
+
+ // Returns true if this file starts with a single slash which indicates a
+ // system-absolute path.
+ bool is_system_absolute() const { return !is_source_absolute(); }
+
+ // Returns a source-absolute path starting with only one slash at the
+ // beginning (normally source-absolute paths start with two slashes to mark
+ // them as such). This is normally used when concatenating names together.
+ //
+ // This function asserts that the file is actually source-absolute. The
+ // return value points into our buffer.
+ std::string_view SourceAbsoluteWithOneSlash() const {
+ CHECK(is_source_absolute());
+ return std::string_view(&value()[1], value().size() - 1);
+ }
+
+ bool operator==(const SourceFile& other) const {
+ return value_ == other.value_;
+ }
+ bool operator!=(const SourceFile& other) const { return !operator==(other); }
+ bool operator<(const SourceFile& other) const {
+ return value_ < other.value_;
+ }
+
+ struct PtrCompare {
+ bool operator()(const SourceFile& a, const SourceFile& b) const noexcept {
+ return StringAtom::PtrCompare()(a.value_, b.value_);
+ }
+ };
+ struct PtrHash {
+ size_t operator()(const SourceFile& s) const noexcept {
+ return StringAtom::PtrHash()(s.value_);
+ }
+ };
+
+ struct PtrEqual {
+ bool operator()(const SourceFile& a, const SourceFile& b) const noexcept {
+ return StringAtom::PtrEqual()(a.value_, b.value_);
+ }
+ };
+
+ private:
+ friend class SourceDir;
+
+ void SetValue(const std::string& value);
+
+ StringAtom value_;
+ Type type_ = SOURCE_UNKNOWN;
+};
+
+namespace std {
+
+template <>
+struct hash<SourceFile> {
+ std::size_t operator()(const SourceFile& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+
+} // namespace std
+
+// Represents a set of source files.
+// NOTE: In practice, this is much faster than using an std::set<> or
+// std::unordered_set<> container. E.g. for the Fuchsia Zircon build, the
+// overall difference in "gn gen" time is about 10%.
+using SourceFileSet = base::flat_set<SourceFile, SourceFile::PtrCompare>;
+
+// Represents a set of tool types.
+class SourceFileTypeSet {
+ public:
+ SourceFileTypeSet();
+
+ void Set(SourceFile::Type type) {
+ flags_[static_cast<int>(type)] = true;
+ empty_ = false;
+ }
+ bool Get(SourceFile::Type type) const {
+ return flags_[static_cast<int>(type)];
+ }
+
+ bool empty() const { return empty_; }
+
+ bool CSourceUsed() const;
+ bool RustSourceUsed() const;
+ bool GoSourceUsed() const;
+ bool SwiftSourceUsed() const;
+
+ bool MixedSourceUsed() const;
+
+ private:
+ bool empty_;
+ bool flags_[static_cast<int>(SourceFile::SOURCE_NUMTYPES)];
+};
+
+#endif // TOOLS_GN_SOURCE_FILE_H_
diff --git a/gn/src/gn/source_file_unittest.cc b/gn/src/gn/source_file_unittest.cc
new file mode 100644
index 00000000000..9d3a12333d1
--- /dev/null
+++ b/gn/src/gn/source_file_unittest.cc
@@ -0,0 +1,20 @@
+// 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 "gn/source_file.h"
+
+#include "util/test/test.h"
+
+// The SourceFile object should normalize the input passed to the constructor.
+// The normalizer unit test checks for all the weird edge cases for normalizing
+// so here just check that it gets called.
+TEST(SourceFile, Normalize) {
+ SourceFile a("//foo/../bar.cc");
+ EXPECT_EQ("//bar.cc", a.value());
+
+ std::string b_str("//foo/././../bar.cc");
+ SourceFile b(std::move(b_str));
+ EXPECT_TRUE(b_str.empty()); // Should have been swapped in.
+ EXPECT_EQ("//bar.cc", b.value());
+}
diff --git a/gn/src/gn/standard_out.cc b/gn/src/gn/standard_out.cc
new file mode 100644
index 00000000000..5564a0c0841
--- /dev/null
+++ b/gn/src/gn/standard_out.cc
@@ -0,0 +1,342 @@
+// 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 "gn/standard_out.h"
+
+#include <stddef.h>
+
+#include <string_view>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "gn/switches.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#else
+#include <stdio.h>
+#include <unistd.h>
+#endif
+
+namespace {
+
+bool initialized = false;
+
+#if defined(OS_WIN)
+HANDLE hstdout;
+WORD default_attributes;
+#endif
+bool is_console = false;
+
+bool is_markdown = false;
+
+// True while output is going into a markdown ```...``` code block.
+bool in_body = false;
+
+void EnsureInitialized() {
+ if (initialized)
+ return;
+ initialized = true;
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ if (cmdline->HasSwitch(switches::kMarkdown)) {
+ // Output help in Markdown's syntax, not color-highlighted.
+ is_markdown = true;
+ }
+
+ if (cmdline->HasSwitch(switches::kNoColor)) {
+ // Force color off.
+ is_console = false;
+ return;
+ }
+
+#if defined(OS_WIN)
+ // On Windows, we can't force the color on. If the output handle isn't a
+ // console, there's nothing we can do about it.
+ hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ is_console = !!::GetConsoleScreenBufferInfo(hstdout, &info);
+ default_attributes = info.wAttributes;
+#else
+ if (cmdline->HasSwitch(switches::kColor))
+ is_console = true;
+ else
+ is_console = isatty(fileno(stdout));
+#endif
+}
+
+#if !defined(OS_WIN)
+void WriteToStdOut(const std::string& output) {
+ size_t written_bytes = fwrite(output.data(), 1, output.size(), stdout);
+ DCHECK_EQ(output.size(), written_bytes);
+}
+#endif // !defined(OS_WIN)
+
+void OutputMarkdownDec(TextDecoration dec) {
+ // The markdown rendering turns "dim" text to italics and any
+ // other colored text to bold.
+
+#if defined(OS_WIN)
+ DWORD written = 0;
+ if (dec == DECORATION_DIM)
+ ::WriteFile(hstdout, "*", 1, &written, nullptr);
+ else if (dec != DECORATION_NONE)
+ ::WriteFile(hstdout, "**", 2, &written, nullptr);
+#else
+ if (dec == DECORATION_DIM)
+ WriteToStdOut("*");
+ else if (dec != DECORATION_NONE)
+ WriteToStdOut("**");
+#endif
+}
+
+} // namespace
+
+#if defined(OS_WIN)
+
+void OutputString(const std::string& output,
+ TextDecoration dec,
+ HtmlEscaping escaping) {
+ EnsureInitialized();
+ DWORD written = 0;
+
+ if (is_markdown) {
+ OutputMarkdownDec(dec);
+ } else if (is_console) {
+ switch (dec) {
+ case DECORATION_NONE:
+ break;
+ case DECORATION_DIM:
+ ::SetConsoleTextAttribute(hstdout, FOREGROUND_INTENSITY);
+ break;
+ case DECORATION_RED:
+ ::SetConsoleTextAttribute(hstdout,
+ FOREGROUND_RED | FOREGROUND_INTENSITY);
+ break;
+ case DECORATION_GREEN:
+ // Keep green non-bold.
+ ::SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN);
+ break;
+ case DECORATION_BLUE:
+ ::SetConsoleTextAttribute(hstdout,
+ FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ break;
+ case DECORATION_YELLOW:
+ ::SetConsoleTextAttribute(hstdout, FOREGROUND_RED | FOREGROUND_GREEN);
+ break;
+ }
+ }
+
+ std::string tmpstr = output;
+ if (is_markdown && dec == DECORATION_YELLOW) {
+ // https://code.google.com/p/gitiles/issues/detail?id=77
+ // Gitiles will replace "--" with an em dash in non-code text.
+ // Figuring out all instances of this might be difficult, but we can
+ // at least escape the instances where this shows up in a heading.
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
+ }
+ if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
+ // Markdown auto-escapes < and > in code sections (and converts &lt; to
+ // &amp;tl; there), but not elsewhere.
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
+ }
+ ::WriteFile(hstdout, tmpstr.c_str(), static_cast<DWORD>(tmpstr.size()),
+ &written, nullptr);
+
+ if (is_markdown) {
+ OutputMarkdownDec(dec);
+ } else if (is_console) {
+ ::SetConsoleTextAttribute(hstdout, default_attributes);
+ }
+}
+
+#else
+
+void OutputString(const std::string& output,
+ TextDecoration dec,
+ HtmlEscaping escaping) {
+ EnsureInitialized();
+ if (is_markdown) {
+ OutputMarkdownDec(dec);
+ } else if (is_console) {
+ switch (dec) {
+ case DECORATION_NONE:
+ break;
+ case DECORATION_DIM:
+ WriteToStdOut("\e[2m");
+ break;
+ case DECORATION_RED:
+ WriteToStdOut("\e[31m\e[1m");
+ break;
+ case DECORATION_GREEN:
+ WriteToStdOut("\e[32m");
+ break;
+ case DECORATION_BLUE:
+ WriteToStdOut("\e[34m\e[1m");
+ break;
+ case DECORATION_YELLOW:
+ WriteToStdOut("\e[33m");
+ break;
+ }
+ }
+
+ std::string tmpstr = output;
+ if (is_markdown && dec == DECORATION_YELLOW) {
+ // https://code.google.com/p/gitiles/issues/detail?id=77
+ // Gitiles will replace "--" with an em dash in non-code text.
+ // Figuring out all instances of this might be difficult, but we can
+ // at least escape the instances where this shows up in a heading.
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
+ }
+ if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
+ // Markdown auto-escapes < and > in code sections (and converts &lt; to
+ // &amp;tl; there), but not elsewhere.
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
+ }
+ WriteToStdOut(tmpstr.data());
+
+ if (is_markdown) {
+ OutputMarkdownDec(dec);
+ } else if (is_console && dec != DECORATION_NONE) {
+ WriteToStdOut("\e[0m");
+ }
+}
+
+#endif
+
+void PrintSectionHelp(const std::string& line,
+ const std::string& topic,
+ const std::string& tag) {
+ EnsureInitialized();
+
+ if (is_markdown) {
+ OutputString("* [" + line + "](#" + tag + ")\n");
+ } else if (topic.size()) {
+ OutputString("\n" + line + " (type \"gn help " + topic +
+ "\" for more help):\n");
+ } else {
+ OutputString("\n" + line + ":\n");
+ }
+}
+
+void PrintShortHelp(const std::string& line, const std::string& link_tag) {
+ EnsureInitialized();
+
+ if (is_markdown) {
+ if (link_tag.empty())
+ OutputString(" * " + line + "\n");
+ else
+ OutputString(" * [" + line + "](#" + link_tag + ")\n");
+ return;
+ }
+
+ size_t colon_offset = line.find(':');
+ size_t first_normal = 0;
+ if (colon_offset != std::string::npos) {
+ OutputString(" " + line.substr(0, colon_offset), DECORATION_YELLOW);
+ first_normal = colon_offset;
+ }
+
+ // See if the colon is followed by a " [" and if so, dim the contents of [ ].
+ if (first_normal > 0 && line.size() > first_normal + 2 &&
+ line[first_normal + 1] == ' ' && line[first_normal + 2] == '[') {
+ size_t begin_bracket = first_normal + 2;
+ OutputString(": ");
+ first_normal = line.find(']', begin_bracket);
+ if (first_normal == std::string::npos)
+ first_normal = line.size();
+ else
+ first_normal++;
+ OutputString(line.substr(begin_bracket, first_normal - begin_bracket),
+ DECORATION_DIM);
+ }
+
+ OutputString(line.substr(first_normal) + "\n");
+}
+
+void PrintLongHelp(const std::string& text, const std::string& tag) {
+ EnsureInitialized();
+
+ bool first_header = true;
+ in_body = false;
+ std::size_t empty_lines = 0;
+ for (const std::string& line : base::SplitString(
+ text, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ // Check for a heading line.
+ if (!line.empty() && line[0] != ' ') {
+ // New paragraph, just skip any trailing empty lines.
+ empty_lines = 0;
+
+ if (is_markdown) {
+ // GN's block-level formatting is converted to markdown as follows:
+ // * The first heading is treated as an H3.
+ // * Subsequent heading are treated as H4s.
+ // * Any other text is wrapped in a code block and displayed as-is.
+ //
+ // Span-level formatting (the decorations) is converted inside
+ // OutputString().
+ if (in_body) {
+ OutputString("```\n\n", DECORATION_NONE);
+ in_body = false;
+ }
+
+ if (first_header && !tag.empty()) {
+ OutputString("### <a name=\"" + tag + "\"></a>", DECORATION_NONE,
+ NO_ESCAPING);
+ first_header = false;
+ } else {
+ OutputString("#### ", DECORATION_NONE);
+ }
+ }
+
+ // Highlight up to the colon (if any).
+ size_t chars_to_highlight = line.find(':');
+ if (chars_to_highlight == std::string::npos)
+ chars_to_highlight = line.size();
+
+ OutputString(line.substr(0, chars_to_highlight), DECORATION_YELLOW);
+ OutputString(line.substr(chars_to_highlight));
+ OutputString("\n");
+ continue;
+ } else if (is_markdown && !line.empty() && !in_body) {
+ OutputString("```\n", DECORATION_NONE);
+ in_body = true;
+ }
+
+ // We buffer empty lines, so we can skip them if needed
+ // (i.e. new paragraph body, end of final paragraph body).
+ if (in_body && is_markdown) {
+ if (!line.empty() && empty_lines != 0) {
+ OutputString(std::string(empty_lines, '\n'));
+ empty_lines = 0;
+ } else if (line.empty()) {
+ ++empty_lines;
+ continue;
+ }
+ }
+
+ // Check for a comment.
+ TextDecoration dec = DECORATION_NONE;
+ for (const auto& elem : line) {
+ if (elem == '#' && !is_markdown) {
+ // Got a comment, draw dimmed.
+ dec = DECORATION_DIM;
+ break;
+ } else if (elem != ' ') {
+ break;
+ }
+ }
+
+ OutputString(line + "\n", dec);
+ }
+
+ if (is_markdown && in_body)
+ OutputString("```\n");
+}
diff --git a/gn/tools/gn/standard_out.h b/gn/src/gn/standard_out.h
index 5c0fcceabb1..5c0fcceabb1 100644
--- a/gn/tools/gn/standard_out.h
+++ b/gn/src/gn/standard_out.h
diff --git a/gn/src/gn/string_atom.cc b/gn/src/gn/string_atom.cc
new file mode 100644
index 00000000000..fe1ef7382cd
--- /dev/null
+++ b/gn/src/gn/string_atom.cc
@@ -0,0 +1,215 @@
+// Copyright (c) 2020 The Chromium 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 "gn/string_atom.h"
+
+#include <array>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "gn/hash_table_base.h"
+
+namespace {
+
+// Implementation note:
+//
+// StringAtomSet implements the global shared state, which is:
+//
+// - a group of std::string instances with a persistent address, allocated
+// through a fast slab allocator.
+//
+// - a set of string pointers, corresponding to the known strings in the
+// group.
+//
+// - a mutex to ensure correct thread-safety.
+//
+// - a find() method that takes an std::string_view argument, and uses it
+// to find a matching entry in the string tree. If none is available,
+// a new std::string is allocated and its address inserted into the tree
+// before being returned.
+//
+// Because the mutex is a large bottleneck, each thread implements
+// its own local string pointer cache, and will only call StringAtomSet::find()
+// in case of a lookup miss. This is critical for good performance.
+//
+
+static const std::string kEmptyString;
+
+using KeyType = const std::string*;
+
+// A HashTableBase node type that stores one hash value and one string pointer.
+struct KeyNode {
+ size_t hash;
+ KeyType key;
+
+ // The following methods are required by HashTableBase<>
+ bool is_valid() const { return !is_null(); }
+ bool is_null() const { return !key; }
+ size_t hash_value() const { return hash; }
+
+ // No deletion support means faster lookup code.
+ static constexpr bool is_tombstone() { return false; }
+};
+
+// This is a trivial hash table of string pointers, using open addressing.
+// It is faster in practice than using a standard container or even a
+// base::flat_set<>.
+//
+// Usage is the following:
+//
+// 1) Compute string hash value.
+//
+// 2) Call Lookup() with the hash value and the string_view key,
+// this always returns a mutable Node* pointer, say |node|.
+//
+// 3) If |node->key| is not nullptr, this is the key to use.
+// Otherwise, allocate a new string with persistent address,
+// and call Insert(), passing the |node|, |hash| and new string
+// address as arguments.
+//
+struct KeySet : public HashTableBase<KeyNode> {
+ using BaseType = HashTableBase<KeyNode>;
+ using Node = BaseType::Node;
+
+ // Compute hash for |str|. Replace with faster hash function if available.
+ static size_t Hash(std::string_view str) {
+ return std::hash<std::string_view>()(str);
+ }
+
+ // Lookup for |str| with specific |hash| value.
+ // Return a Node pointer. If the key was found, |node.key| is its value.
+ // Otherwise, the caller should create a new key value, then call Insert()
+ // below.
+ //
+ // NOTE: Even though this method is const, because it doesn't modify the
+ // state of the KeySet, it returns a *mutable* node pointer, to be
+ // passed to Insert() in case of a miss.
+ //
+ Node* Lookup(size_t hash, std::string_view str) const {
+ return BaseType::NodeLookup(hash, [hash, &str](const Node* node) {
+ // NOTE: Only is_valid() node pointers are passed to this function
+ // which means key won't be null, and there are no tombstone values
+ // in this derivation of HashTableBase<>.
+ return node->hash == hash && *node->key == str;
+ });
+ }
+
+ void Insert(Node* node, size_t hash, KeyType key) {
+ node->hash = hash;
+ node->key = key;
+ BaseType::UpdateAfterInsert();
+ }
+};
+
+class StringAtomSet {
+ public:
+ StringAtomSet() {
+ // Ensure kEmptyString is in our set while not being allocated
+ // from a slab. The end result is that find("") should always
+ // return this address.
+ //
+ // This allows the StringAtom() default initializer to use the same
+ // address directly, avoiding a table lookup.
+ //
+ size_t hash = set_.Hash("");
+ auto* node = set_.Lookup(hash, "");
+ set_.Insert(node, hash, &kEmptyString);
+ }
+
+ // Find the unique constant string pointer for |key|.
+ const std::string* find(std::string_view key) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ size_t hash = set_.Hash(key);
+ auto* node = set_.Lookup(hash, key);
+ if (node->key)
+ return node->key;
+
+ // Allocate new string, insert its address in the set.
+ if (slab_index_ >= kStringsPerSlab) {
+ slabs_.push_back(new Slab());
+ slab_index_ = 0;
+ }
+ std::string* result = slabs_.back()->init(slab_index_++, key);
+ set_.Insert(node, hash, result);
+ return result;
+ }
+
+ private:
+ static constexpr unsigned int kStringsPerSlab = 128;
+
+ // Each slab is allocated independently, has a fixed address and stores
+ // kStringsPerSlab items of type StringStorage. The latter has the same
+ // size and alignment as std::string, but doesn't need default-initialization.
+ // This is used to slightly speed up Slab allocation and string
+ // initialization. The drawback is that on process exit, allocated strings
+ // are leaked (but GN already leaks several hundred MiBs of memory anyway).
+
+ // A C++ union that can store an std::string but without default
+ // initialization and destruction.
+ union StringStorage {
+ StringStorage() {}
+ ~StringStorage() {}
+ char dummy;
+ std::string str;
+ };
+
+ // A fixed array of StringStorage items. Can be allocated cheaply.
+ class Slab {
+ public:
+ // Init the n-th string in the slab with |str|.
+ // Return its location as well.
+ std::string* init(size_t index, const std::string_view& str) {
+ std::string* result = &items_[index].str;
+ new (result) std::string(str);
+ return result;
+ }
+
+ private:
+ StringStorage items_[kStringsPerSlab];
+ };
+
+ std::mutex mutex_;
+ KeySet set_;
+ std::vector<Slab*> slabs_;
+ unsigned int slab_index_ = kStringsPerSlab;
+};
+
+StringAtomSet& GetStringAtomSet() {
+ static StringAtomSet s_string_atom_set;
+ return s_string_atom_set;
+}
+
+// Each thread maintains its own ThreadLocalCache to perform fast lookups
+// without taking any mutex in most cases.
+class ThreadLocalCache {
+ public:
+ // Find the unique constant string pointer for |key| in this cache,
+ // and fallback to the global one in case of a miss.
+ KeyType find(std::string_view key) {
+ size_t hash = local_set_.Hash(key);
+ auto* node = local_set_.Lookup(hash, key);
+ if (node->key)
+ return node->key;
+
+ KeyType result = GetStringAtomSet().find(key);
+ local_set_.Insert(node, hash, result);
+ return result;
+ }
+
+ private:
+ KeySet local_set_;
+};
+
+thread_local ThreadLocalCache s_local_cache;
+
+} // namespace
+
+StringAtom::StringAtom() : value_(kEmptyString) {}
+
+StringAtom::StringAtom(std::string_view str) noexcept
+ : value_(*s_local_cache.find(str)) {}
diff --git a/gn/src/gn/string_atom.h b/gn/src/gn/string_atom.h
new file mode 100644
index 00000000000..29d4e410125
--- /dev/null
+++ b/gn/src/gn/string_atom.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2020 The Chromium 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 TOOLS_GN_STRING_ATOM_H_
+#define TOOLS_GN_STRING_ATOM_H_
+
+#include <functional>
+#include <string>
+
+// A StringAtom models a pointer to a globally unique constant string.
+//
+// They are useful as key types for sets and map container types, especially
+// when a program uses multiple instances that tend to use the same strings
+// (as happen very frequently in GN).
+//
+// Note that default equality and comparison functions will compare the
+// string content, not the pointers, ensuring that the behaviour of
+// standard containers using StringAtom key types is the same as if
+// std::string was used.
+//
+// In addition, _ordered_ containers support heterogeneous lookups (i.e.
+// using an std::string_view, and by automatic conversion, a const char*
+// of const char[] literal) as a key type.
+//
+// Additionally, it is also possible to implement very fast _unordered_
+// containers by using the StringAtom::Fast{Hash,Equal,Compare} structs,
+// which will force containers to hash/compare pointer values instead,
+// for example:
+//
+// // A fast unordered set of unique strings.
+// //
+// // Implementation uses a hash table so performance will be bounded
+// // by the string hash function. Does not support heterogeneous lookups.
+// //
+// using FastStringSet = std::unordered_set<StringAtom,
+// StringAtom::PtrHash,
+// StringAtom::PtrEqual>;
+//
+// // A fast unordered set of unique strings.
+// //
+// // Implementation uses a balanced binary tree so performance will
+// // be bounded by string comparisons. Does support heterogeneous lookups,
+// // but not that this does not extend to the [] operator, only to the
+// // find() method.
+// //
+// using FastStringSet = std::set<StringAtom, StringAtom::PtrCompare>
+//
+// // A fast unordered { string -> VALUE } map.
+// //
+// // Implementation uses a balanced binary tree. Supports heterogeneous
+// // lookups.
+// template <typename VALUE>
+// using FastStringMap = std::map<StringAtom, VALUE, StringAtom::PtrCompare>
+//
+class StringAtom {
+ public:
+ // Default constructor. Value points to a globally unique empty string.
+ StringAtom();
+
+ // Destructor should do nothing at all.
+ ~StringAtom() = default;
+
+ // Non-explicit constructors.
+ StringAtom(std::string_view str) noexcept;
+
+ // Copy and move operations.
+ StringAtom(const StringAtom& other) noexcept : value_(other.value_) {}
+ StringAtom& operator=(const StringAtom& other) noexcept {
+ if (this != &other) {
+ this->~StringAtom(); // really a no-op
+ new (this) StringAtom(other);
+ }
+ return *this;
+ }
+
+ StringAtom(StringAtom&& other) noexcept : value_(other.value_) {}
+ StringAtom& operator=(const StringAtom&& other) noexcept {
+ if (this != &other) {
+ this->~StringAtom(); // really a no-op
+ new (this) StringAtom(std::move(other));
+ }
+ return *this;
+ }
+
+ bool empty() const { return value_.empty(); }
+
+ // Explicit conversions.
+ const std::string& str() const { return value_; }
+
+ // Implicit conversions.
+ operator std::string_view() const { return {value_}; }
+
+ // Returns true iff this is the same key.
+ // Note that the default comparison functions compare the value instead
+ // in order to use them in standard containers without surprises by
+ // default.
+ bool SameAs(const StringAtom& other) const {
+ return &value_ == &other.value_;
+ }
+
+ // Default comparison functions.
+ bool operator==(const StringAtom& other) const {
+ return value_ == other.value_;
+ }
+
+ bool operator!=(const StringAtom& other) const {
+ return value_ != other.value_;
+ }
+
+ bool operator<(const StringAtom& other) const {
+ // Avoid one un-necessary string comparison if values are equal.
+ if (SameAs(other))
+ return false;
+
+ return value_ < other.value_;
+ }
+
+ size_t hash() const { return std::hash<std::string>()(value_); }
+
+ // Use the following structs to implement containers that use StringAtom
+ // values as keys, but only compare/hash the pointer values for speed.
+ // E.g.:
+ // using FastSet = std::unordered_set<StringAtom, PtrHash, PtrEqual>;
+ // using FastMap = std::map<StringAtom, Value, PtrCompare>;
+ //
+ // IMPORTANT: Note that FastMap above is ordered based in the StringAtom
+ // pointer value, not the string content.
+ //
+ struct PtrHash {
+ size_t operator()(const StringAtom& key) const noexcept {
+ return std::hash<const std::string*>()(&key.value_);
+ }
+ };
+
+ struct PtrEqual {
+ bool operator()(const StringAtom& a, const StringAtom& b) const noexcept {
+ return &a.value_ == &b.value_;
+ }
+ };
+
+ struct PtrCompare {
+ bool operator()(const StringAtom& a, const StringAtom& b) const noexcept {
+ return &a.value_ < &b.value_;
+ }
+ };
+
+ protected:
+ const std::string& value_;
+};
+
+namespace std {
+
+// Ensure default heterogeneous lookups with other types like std::string_view.
+template <>
+struct less<StringAtom> {
+ using is_transparent = int;
+
+ bool operator()(const StringAtom& a, const StringAtom& b) const noexcept {
+ return a.str() < b.str();
+ }
+ template <typename U>
+ bool operator()(const StringAtom& a, const U& b) const noexcept {
+ return a.str() < b;
+ }
+ template <typename U>
+ bool operator()(const U& a, const StringAtom& b) const noexcept {
+ return a < b.str();
+ };
+};
+
+template <>
+struct hash<StringAtom> {
+ size_t operator()(const StringAtom& key) const noexcept { return key.hash(); }
+};
+
+} // namespace std
+
+#endif // TOOLS_GN_STRING_ATOM_H_
diff --git a/gn/src/gn/string_atom_unittest.cc b/gn/src/gn/string_atom_unittest.cc
new file mode 100644
index 00000000000..ba151e37ebe
--- /dev/null
+++ b/gn/src/gn/string_atom_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2020 The Chromium 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 "gn/string_atom.h"
+
+#include "util/test/test.h"
+
+#include <algorithm>
+#include <array>
+#include <set>
+#include <string>
+#include <vector>
+
+TEST(StringAtomTest, EmptyString) {
+ StringAtom key1;
+ StringAtom key2("");
+
+ ASSERT_STREQ(key1.str().c_str(), "");
+ ASSERT_STREQ(key2.str().c_str(), "");
+ ASSERT_EQ(&key1.str(), &key2.str());
+}
+
+TEST(StringAtomTest, Find) {
+ StringAtom empty;
+ EXPECT_EQ(empty.str(), std::string());
+
+ StringAtom foo("foo");
+ EXPECT_EQ(foo.str(), std::string("foo"));
+
+ StringAtom foo2("foo");
+ EXPECT_EQ(&foo.str(), &foo2.str());
+}
+
+// Default compare should always be ordered.
+TEST(StringAtomTest, DefaultCompare) {
+ auto foo = StringAtom("foo");
+ auto bar = StringAtom("bar");
+ auto zoo = StringAtom("zoo");
+
+ EXPECT_TRUE(bar < foo);
+ EXPECT_TRUE(foo < zoo);
+ EXPECT_TRUE(bar < zoo);
+}
+
+TEST(StringAtomTest, NormalSet) {
+ std::set<StringAtom> set;
+ auto foo_ret = set.insert(std::string_view("foo"));
+ auto bar_ret = set.insert(std::string_view("bar"));
+ auto zoo_ret = set.insert(std::string_view("zoo"));
+
+ StringAtom foo_key("foo");
+ EXPECT_EQ(*foo_ret.first, foo_key);
+
+ auto foo_it = set.find(foo_key);
+ EXPECT_NE(foo_it, set.end());
+ EXPECT_EQ(*foo_it, foo_key);
+
+ EXPECT_EQ(set.find(std::string_view("bar")), bar_ret.first);
+ EXPECT_EQ(set.find(std::string_view("zoo")), zoo_ret.first);
+
+ // Normal sets are always ordered according to the key value.
+ auto it = set.begin();
+ EXPECT_EQ(it, bar_ret.first);
+ ++it;
+
+ EXPECT_EQ(it, foo_ret.first);
+ ++it;
+
+ EXPECT_EQ(it, zoo_ret.first);
+ ++it;
+
+ EXPECT_EQ(it, set.end());
+}
+
+TEST(StringAtomTest, FastSet) {
+ std::set<StringAtom, StringAtom::PtrCompare> set;
+
+ auto foo_ret = set.insert(std::string_view("foo"));
+ auto bar_ret = set.insert(std::string_view("bar"));
+ auto zoo_ret = set.insert(std::string_view("zoo"));
+
+ auto atom_to_ptr = [](const StringAtom& atom) -> const std::string* {
+ return &atom.str();
+ };
+
+ EXPECT_TRUE(foo_ret.second);
+ EXPECT_TRUE(bar_ret.second);
+ EXPECT_TRUE(zoo_ret.second);
+
+ const std::string* foo_ptr = atom_to_ptr(*foo_ret.first);
+ const std::string* bar_ptr = atom_to_ptr(*bar_ret.first);
+ const std::string* zoo_ptr = atom_to_ptr(*zoo_ret.first);
+
+ StringAtom foo_key("foo");
+ EXPECT_EQ(foo_ptr, atom_to_ptr(foo_key));
+
+ auto foo_it = set.find(foo_key);
+ EXPECT_NE(foo_it, set.end());
+ EXPECT_EQ(*foo_it, foo_key);
+
+ EXPECT_EQ(set.find(std::string_view("bar")), bar_ret.first);
+ EXPECT_EQ(set.find(std::string_view("zoo")), zoo_ret.first);
+
+ // Fast sets are ordered according to the key pointer.
+ // Even though a bump allocator is used to allocate AtomString
+ // strings, there is no guarantee that the global StringAtom
+ // set was not already populated by a different test previously,
+ // which means the pointers value need to be sorted before
+ // iterating over the set for comparison.
+ std::array<const std::string*, 3> ptrs = {
+ foo_ptr,
+ bar_ptr,
+ zoo_ptr,
+ };
+ std::sort(ptrs.begin(), ptrs.end());
+
+ auto it = set.begin();
+ EXPECT_EQ(atom_to_ptr(*it), ptrs[0]);
+ ++it;
+
+ EXPECT_EQ(atom_to_ptr(*it), ptrs[1]);
+ ++it;
+
+ EXPECT_EQ(atom_to_ptr(*it), ptrs[2]);
+ ++it;
+
+ EXPECT_EQ(it, set.end());
+}
+
+TEST(StringAtom, AllocMoreThanASingleSlabOfKeys) {
+ // Verify that allocating more than 128 string keys works properly.
+ const size_t kMaxCount = 16384;
+ std::vector<StringAtom> keys;
+
+ // Small lambda to create a string for the n-th key.
+ auto string_for = [](size_t index) -> std::string {
+ return std::to_string(index) + "_key";
+ };
+
+ for (size_t nn = 0; nn < kMaxCount; ++nn) {
+ keys.push_back(StringAtom(string_for(nn)));
+ }
+
+ for (size_t nn = 0; nn < kMaxCount; ++nn) {
+ ASSERT_EQ(keys[nn].str(), string_for(nn));
+ }
+}
diff --git a/gn/src/gn/string_output_buffer.cc b/gn/src/gn/string_output_buffer.cc
new file mode 100644
index 00000000000..09913ce2232
--- /dev/null
+++ b/gn/src/gn/string_output_buffer.cc
@@ -0,0 +1,117 @@
+// Copyright 2020 The Chromium 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 "gn/string_output_buffer.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "gn/err.h"
+#include "gn/file_writer.h"
+#include "gn/filesystem_utils.h"
+
+#include <fstream>
+
+std::string StringOutputBuffer::str() const {
+ std::string result;
+ size_t data_size = size();
+ result.reserve(data_size);
+ for (size_t nn = 0; nn < pages_.size(); ++nn) {
+ size_t wanted_size = std::min(kPageSize, data_size - nn * kPageSize);
+ result.append(pages_[nn]->data(), wanted_size);
+ }
+ return result;
+}
+
+void StringOutputBuffer::Append(const char* str, size_t len) {
+ Append(std::string_view(str, len));
+}
+
+void StringOutputBuffer::Append(std::string_view str) {
+ while (str.size() > 0) {
+ if (page_free_size() == 0) {
+ // Allocate a new page.
+ pages_.push_back(std::make_unique<Page>());
+ pos_ = 0;
+ }
+ size_t size = std::min(page_free_size(), str.size());
+ memcpy(pages_.back()->data() + pos_, str.data(), size);
+ pos_ += size;
+ str.remove_prefix(size);
+ }
+}
+
+void StringOutputBuffer::Append(char c) {
+ if (page_free_size() == 0) {
+ // Allocate a new page.
+ pages_.push_back(std::make_unique<Page>());
+ pos_ = 0;
+ }
+ pages_.back()->data()[pos_] = c;
+ pos_ += 1;
+}
+
+bool StringOutputBuffer::ContentsEqual(const base::FilePath& file_path) const {
+ // Compare file and stream sizes first. Quick and will save us some time if
+ // they are different sizes.
+ size_t data_size = size();
+ int64_t file_size;
+ if (!base::GetFileSize(file_path, &file_size) ||
+ static_cast<size_t>(file_size) != data_size) {
+ return false;
+ }
+
+ // Open the file in binary mode.
+ std::ifstream file(file_path.As8Bit().c_str(), std::ios::binary);
+ if (!file.is_open())
+ return false;
+
+ size_t page_count = pages_.size();
+ Page file_page;
+ for (size_t nn = 0; nn < page_count; ++nn) {
+ size_t wanted_size = std::min(data_size - nn * kPageSize, kPageSize);
+ file.read(file_page.data(), wanted_size);
+ if (!file.good())
+ return false;
+
+ if (memcmp(file_page.data(), pages_[nn]->data(), wanted_size) != 0)
+ return false;
+ }
+ return true;
+}
+
+// Write the contents of this instance to a file at |file_path|.
+bool StringOutputBuffer::WriteToFile(const base::FilePath& file_path,
+ Err* err) const {
+ // Create the directory if necessary.
+ if (!base::CreateDirectory(file_path.DirName())) {
+ if (err) {
+ *err =
+ Err(Location(), "Unable to create directory.",
+ "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
+ }
+ return false;
+ }
+
+ size_t data_size = size();
+ size_t page_count = pages_.size();
+
+ FileWriter writer;
+ bool success = writer.Create(file_path);
+ if (success) {
+ for (size_t nn = 0; nn < page_count; ++nn) {
+ size_t wanted_size = std::min(data_size - nn * kPageSize, kPageSize);
+ success = writer.Write(std::string_view(pages_[nn]->data(), wanted_size));
+ if (!success)
+ break;
+ }
+ }
+ if (!writer.Close())
+ success = false;
+
+ if (!success && err) {
+ *err = Err(Location(), "Unable to write file.",
+ "I was writing \"" + FilePathToUTF8(file_path) + "\".");
+ }
+ return success;
+}
diff --git a/gn/src/gn/string_output_buffer.h b/gn/src/gn/string_output_buffer.h
new file mode 100644
index 00000000000..d2a42c9cf37
--- /dev/null
+++ b/gn/src/gn/string_output_buffer.h
@@ -0,0 +1,94 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_STRING_OUTPUT_BUFFER_H_
+#define TOOLS_GN_STRING_OUTPUT_BUFFER_H_
+
+#include <array>
+#include <memory>
+#include <streambuf>
+#include <string>
+#include <vector>
+
+namespace base {
+class FilePath;
+} // namespace base
+
+class Err;
+
+// An append-only very large storage area for string data. Useful for the parts
+// of GN that need to generate huge output files (e.g. --ide=json will create
+// a 139 MiB project.json file for the Fuchsia build).
+//
+// Usage is the following:
+//
+// 1) Create instance.
+//
+// 2) Use operator<<, or Append() to append data to the instance.
+//
+// 3) Alternatively, create an std::ostream that takes its address as
+// argument, then use the output stream as usual to append data to it.
+//
+// StringOutputBuffer storage;
+// std::ostream out(&storage);
+// out << "Hello world!";
+//
+// 4) Use ContentsEqual() to compare the instance's content with that of a
+// given file.
+//
+// 5) Use WriteToFile() to write the content to a given file.
+//
+class StringOutputBuffer : public std::streambuf {
+ public:
+ StringOutputBuffer() = default;
+
+ // Convert content to single std::string instance. Useful for unit-testing.
+ std::string str() const;
+
+ // Return the number of characters stored in this instance.
+ size_t size() const { return (pages_.size() - 1u) * kPageSize + pos_; }
+
+ // Append string to this instance.
+ void Append(const char* str, size_t len);
+ void Append(std::string_view str);
+ void Append(char c);
+
+ StringOutputBuffer& operator<<(std::string_view str) {
+ Append(str);
+ return *this;
+ }
+
+ // Compare the content of this instance with that of the file at |file_path|.
+ bool ContentsEqual(const base::FilePath& file_path) const;
+
+ // Write the contents of this instance to a file at |file_path|.
+ bool WriteToFile(const base::FilePath& file_path, Err* err) const;
+
+ static size_t GetPageSizeForTesting() { return kPageSize; }
+
+ protected:
+ // Called by std::ostream to write |n| chars from |s|.
+ std::streamsize xsputn(const char* s, std::streamsize n) override {
+ Append(s, static_cast<size_t>(n));
+ return n;
+ }
+
+ // Called by std::ostream to write a single character.
+ int_type overflow(int_type ch) override {
+ Append(static_cast<char>(ch));
+ return 1;
+ }
+
+ private:
+ // Return the number of free bytes in the current page.
+ size_t page_free_size() const { return kPageSize - pos_; }
+
+ static constexpr size_t kPageSize = 65536;
+ using Page = std::array<char, kPageSize>;
+
+ size_t pos_ = kPageSize;
+ std::vector<std::unique_ptr<Page>> pages_;
+};
+
+#endif // TOOLS_GN_STRING_OUTPUT_BUFFER_H_
diff --git a/gn/src/gn/string_output_buffer_unittest.cc b/gn/src/gn/string_output_buffer_unittest.cc
new file mode 100644
index 00000000000..6dcb741fa9b
--- /dev/null
+++ b/gn/src/gn/string_output_buffer_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright 2020 The Chromium 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 "gn/string_output_buffer.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "util/test/test.h"
+
+namespace {
+
+// Create a test string of |size| characters with pseudo-random ASCII content.
+std::string CreateTestString(size_t size, size_t seed = 0) {
+ std::string result;
+ result.resize(size);
+ for (size_t n = 0; n < size; ++n) {
+ int offset = (size + seed + n * 1337);
+ char ch = ' ' + offset % (127 - 32);
+ result[n] = ch;
+ }
+ return result;
+}
+
+} // namespace
+
+TEST(StringOutputBuffer, Append) {
+ const size_t data_size = 100000;
+ std::string data = CreateTestString(data_size);
+
+ const size_t num_spans = 50;
+ const size_t span_size = data_size / num_spans;
+
+ StringOutputBuffer buffer;
+
+ for (size_t n = 0; n < num_spans; ++n) {
+ size_t start_offset = n * span_size;
+ size_t end_offset = std::min(start_offset + span_size, data.size());
+ buffer.Append(&data[start_offset], end_offset - start_offset);
+ }
+
+ EXPECT_EQ(data.size(), buffer.size());
+ ASSERT_STREQ(data.c_str(), buffer.str().c_str());
+}
+
+TEST(StringOutputBuffer, AppendWithPageSizeMultiples) {
+ const size_t page_size = StringOutputBuffer::GetPageSizeForTesting();
+ const size_t page_count = 100;
+ const size_t data_size = page_size * page_count;
+ std::string data = CreateTestString(data_size);
+
+ StringOutputBuffer buffer;
+
+ for (size_t n = 0; n < page_count; ++n) {
+ size_t start_offset = n * page_size;
+ buffer.Append(&data[start_offset], page_size);
+ }
+
+ EXPECT_EQ(data.size(), buffer.size());
+ ASSERT_STREQ(data.c_str(), buffer.str().c_str());
+}
+
+TEST(StringOutput, WrappedByStdOstream) {
+ const size_t data_size = 100000;
+ std::string data = CreateTestString(data_size);
+
+ const size_t num_spans = 50;
+ const size_t span_size = data_size / num_spans;
+
+ StringOutputBuffer buffer;
+ std::ostream out(&buffer);
+
+ for (size_t n = 0; n < num_spans; ++n) {
+ size_t start_offset = n * span_size;
+ size_t end_offset = std::min(start_offset + span_size, data.size());
+ out << std::string_view(&data[start_offset], end_offset - start_offset);
+ }
+
+ EXPECT_EQ(data.size(), buffer.size());
+ ASSERT_STREQ(data.c_str(), buffer.str().c_str());
+}
+
+TEST(StringOutputBuffer, ContentsEqual) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const size_t data_size = 100000;
+ std::string data = CreateTestString(data_size);
+
+ base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt");
+ base::WriteFile(file_path, data.c_str(), static_cast<int>(data.size()));
+
+ {
+ StringOutputBuffer buffer;
+ buffer.Append(data);
+
+ EXPECT_TRUE(buffer.ContentsEqual(file_path));
+
+ // Different length and contents.
+ buffer << "extra";
+ EXPECT_FALSE(buffer.ContentsEqual(file_path));
+ }
+
+ // The same length, different contents.
+ {
+ StringOutputBuffer buffer;
+ buffer << CreateTestString(data_size, 1);
+
+ EXPECT_FALSE(buffer.ContentsEqual(file_path));
+ }
+}
+
+TEST(StringOutputBuffer, WriteToFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const size_t data_size = 100000;
+ std::string data = CreateTestString(data_size);
+
+ // Write if file doesn't exist. Also create directory.
+ base::FilePath file_path =
+ temp_dir.GetPath().AppendASCII("bar").AppendASCII("foo.txt");
+
+ StringOutputBuffer buffer;
+ buffer.Append(data);
+
+ EXPECT_TRUE(buffer.WriteToFile(file_path, nullptr));
+
+ // Verify file was created and has same content.
+ base::File::Info file_info;
+ ASSERT_TRUE(base::GetFileInfo(file_path, &file_info));
+ ASSERT_TRUE(buffer.ContentsEqual(file_path));
+}
diff --git a/gn/src/gn/string_utils.cc b/gn/src/gn/string_utils.cc
new file mode 100644
index 00000000000..b785e759e9a
--- /dev/null
+++ b/gn/src/gn/string_utils.cc
@@ -0,0 +1,356 @@
+// 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 "gn/string_utils.h"
+
+#include <stddef.h>
+#include <cctype>
+
+#include "base/strings/string_number_conversions.h"
+#include "gn/err.h"
+#include "gn/input_file.h"
+#include "gn/parser.h"
+#include "gn/scope.h"
+#include "gn/token.h"
+#include "gn/tokenizer.h"
+#include "gn/value.h"
+
+namespace {
+
+// Constructs an Err indicating a range inside a string. We assume that the
+// token has quotes around it that are not counted by the offset.
+Err ErrInsideStringToken(const Token& token,
+ size_t offset,
+ size_t size,
+ const std::string& msg,
+ const std::string& help = std::string()) {
+ // The "+1" is skipping over the " at the beginning of the token.
+ int int_offset = static_cast<int>(offset);
+ Location begin_loc(token.location().file(), token.location().line_number(),
+ token.location().column_number() + int_offset + 1,
+ token.location().byte() + int_offset + 1);
+ Location end_loc(
+ token.location().file(), token.location().line_number(),
+ token.location().column_number() + int_offset + 1 +
+ static_cast<int>(size),
+ token.location().byte() + int_offset + 1 + static_cast<int>(size));
+ return Err(LocationRange(begin_loc, end_loc), msg, help);
+}
+
+// Notes about expression interpolation. This is based loosly on Dart but is
+// slightly less flexible. In Dart, seeing the ${ in a string is something
+// the toplevel parser knows about, and it will recurse into the block
+// treating it as a first-class {...} block. So even things like this work:
+// "hello ${"foo}"*2+"bar"}" => "hello foo}foo}bar"
+// (you can see it did not get confused by the nested strings or the nested "}"
+// inside the block).
+//
+// This is cool but complicates the parser for almost no benefit for this
+// non-general-purpose programming language. The main reason expressions are
+// supported here at all are to support "${scope.variable}" and "${list[0]}",
+// neither of which have any of these edge-cases.
+//
+// In this simplified approach, we search for the terminating '}' and execute
+// the result. This means we can't support any expressions with embedded '}'
+// or '"'. To keep people from getting confusing about what's supported and
+// what's not, only identifier and accessor expressions are allowed (neither
+// of these run into any of these edge-cases).
+bool AppendInterpolatedExpression(Scope* scope,
+ const Token& token,
+ const char* input,
+ size_t begin_offset,
+ size_t end_offset,
+ std::string* output,
+ Err* err) {
+ SourceFile empty_source_file; // Prevent most vexing parse.
+ InputFile input_file(empty_source_file);
+ input_file.SetContents(
+ std::string(&input[begin_offset], end_offset - begin_offset));
+
+ // Tokenize.
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
+ if (err->has_error()) {
+ // The error will point into our temporary buffer, rewrite it to refer
+ // to the original token. This will make the location information less
+ // precise, but generally there won't be complicated things in string
+ // interpolations.
+ *err = ErrInsideStringToken(token, begin_offset, end_offset - begin_offset,
+ err->message(), err->help_text());
+ return false;
+ }
+
+ // Parse.
+ std::unique_ptr<ParseNode> node = Parser::ParseExpression(tokens, err);
+ if (err->has_error()) {
+ // Rewrite error as above.
+ *err = ErrInsideStringToken(token, begin_offset, end_offset - begin_offset,
+ err->message(), err->help_text());
+ return false;
+ }
+ if (!(node->AsIdentifier() || node->AsAccessor())) {
+ *err = ErrInsideStringToken(
+ token, begin_offset, end_offset - begin_offset,
+ "Invalid string interpolation.",
+ "The thing inside the ${} must be an identifier ${foo},\n"
+ "a scope access ${foo.bar}, or a list access ${foo[0]}.");
+ return false;
+ }
+
+ // Evaluate.
+ Value result = node->Execute(scope, err);
+ if (err->has_error()) {
+ // Rewrite error as above.
+ *err = ErrInsideStringToken(token, begin_offset, end_offset - begin_offset,
+ err->message(), err->help_text());
+ return false;
+ }
+
+ output->append(result.ToString(false));
+ return true;
+}
+
+bool AppendInterpolatedIdentifier(Scope* scope,
+ const Token& token,
+ const char* input,
+ size_t begin_offset,
+ size_t end_offset,
+ std::string* output,
+ Err* err) {
+ std::string_view identifier(&input[begin_offset], end_offset - begin_offset);
+ const Value* value = scope->GetValue(identifier, true);
+ if (!value) {
+ // We assume the input points inside the token.
+ *err = ErrInsideStringToken(
+ token, identifier.data() - token.value().data() - 1, identifier.size(),
+ "Undefined identifier in string expansion.",
+ std::string("\"") + identifier + "\" is not currently in scope.");
+ return false;
+ }
+
+ output->append(value->ToString(false));
+ return true;
+}
+
+// Handles string interpolations: $identifier and ${expression}
+//
+// |*i| is the index into |input| after the $. This will be updated to point to
+// the last character consumed on success. The token is the original string
+// to blame on failure.
+//
+// On failure, returns false and sets the error. On success, appends the
+// result of the interpolation to |*output|.
+bool AppendStringInterpolation(Scope* scope,
+ const Token& token,
+ const char* input,
+ size_t size,
+ size_t* i,
+ std::string* output,
+ Err* err) {
+ size_t dollars_index = *i - 1;
+
+ if (input[*i] == '{') {
+ // Bracketed expression.
+ (*i)++;
+ size_t begin_offset = *i;
+
+ // Find the closing } and check for non-identifier chars. Don't need to
+ // bother checking for the more-restricted first character of an identifier
+ // since the {} unambiguously denotes the range, and identifiers with
+ // invalid names just won't be found later.
+ bool has_non_ident_chars = false;
+ while (*i < size && input[*i] != '}') {
+ has_non_ident_chars |= Tokenizer::IsIdentifierContinuingChar(input[*i]);
+ (*i)++;
+ }
+ if (*i == size) {
+ *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index,
+ "Unterminated ${...");
+ return false;
+ }
+
+ // In the common case, the thing inside the {} will actually be a
+ // simple identifier. Avoid all the complicated parsing of accessors
+ // in this case.
+ if (!has_non_ident_chars) {
+ return AppendInterpolatedIdentifier(scope, token, input, begin_offset, *i,
+ output, err);
+ }
+ return AppendInterpolatedExpression(scope, token, input, begin_offset, *i,
+ output, err);
+ }
+
+ // Simple identifier.
+ // The first char of an identifier is more restricted.
+ if (!Tokenizer::IsIdentifierFirstChar(input[*i])) {
+ *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index + 1,
+ "$ not followed by an identifier char.",
+ "It you want a literal $ use \"\\$\".");
+ return false;
+ }
+ size_t begin_offset = *i;
+ (*i)++;
+
+ // Find the first non-identifier char following the string.
+ while (*i < size && Tokenizer::IsIdentifierContinuingChar(input[*i]))
+ (*i)++;
+ size_t end_offset = *i;
+ (*i)--; // Back up to mark the last character consumed.
+ return AppendInterpolatedIdentifier(scope, token, input, begin_offset,
+ end_offset, output, err);
+}
+
+// Handles a hex literal: $0xFF
+//
+// |*i| is the index into |input| after the $. This will be updated to point to
+// the last character consumed on success. The token is the original string
+// to blame on failure.
+//
+// On failure, returns false and sets the error. On success, appends the
+// char with the given hex value to |*output|.
+bool AppendHexByte(Scope* scope,
+ const Token& token,
+ const char* input,
+ size_t size,
+ size_t* i,
+ std::string* output,
+ Err* err) {
+ size_t dollars_index = *i - 1;
+ // "$0" is already known to exist.
+ if (*i + 3 >= size || input[*i + 1] != 'x' || !std::isxdigit(input[*i + 2]) ||
+ !std::isxdigit(input[*i + 3])) {
+ *err = ErrInsideStringToken(
+ token, dollars_index, *i - dollars_index + 1,
+ "Invalid hex character. Hex values must look like 0xFF.");
+ return false;
+ }
+ int value = 0;
+ if (!base::HexStringToInt(std::string_view(&input[*i + 2], 2), &value)) {
+ *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index + 1,
+ "Could not convert hex value.");
+ return false;
+ }
+ *i += 3;
+ output->push_back(value);
+ return true;
+}
+
+} // namespace
+
+bool ExpandStringLiteral(Scope* scope,
+ const Token& literal,
+ Value* result,
+ Err* err) {
+ DCHECK(literal.type() == Token::STRING);
+ DCHECK(literal.value().size() > 1); // Should include quotes.
+ DCHECK(result->type() == Value::STRING); // Should be already set.
+
+ // The token includes the surrounding quotes, so strip those off.
+ const char* input = &literal.value().data()[1];
+ size_t size = literal.value().size() - 2;
+
+ std::string& output = result->string_value();
+ output.reserve(size);
+ for (size_t i = 0; i < size; i++) {
+ if (input[i] == '\\') {
+ if (i < size - 1) {
+ switch (input[i + 1]) {
+ case '\\':
+ case '"':
+ case '$':
+ output.push_back(input[i + 1]);
+ i++;
+ continue;
+ default: // Everything else has no meaning: pass the literal.
+ break;
+ }
+ }
+ output.push_back(input[i]);
+ } else if (input[i] == '$') {
+ i++;
+ if (i == size) {
+ *err = ErrInsideStringToken(
+ literal, i - 1, 1, "$ at end of string.",
+ "I was expecting an identifier, 0xFF, or {...} after the $.");
+ return false;
+ }
+ if (input[i] == '0') {
+ if (!AppendHexByte(scope, literal, input, size, &i, &output, err))
+ return false;
+ } else if (!AppendStringInterpolation(scope, literal, input, size, &i,
+ &output, err))
+ return false;
+ } else {
+ output.push_back(input[i]);
+ }
+ }
+ return true;
+}
+
+size_t EditDistance(const std::string_view& s1,
+ const std::string_view& s2,
+ size_t max_edit_distance) {
+ // The algorithm implemented below is the "classic"
+ // dynamic-programming algorithm for computing the Levenshtein
+ // distance, which is described here:
+ //
+ // http://en.wikipedia.org/wiki/Levenshtein_distance
+ //
+ // Although the algorithm is typically described using an m x n
+ // array, only one row plus one element are used at a time, so this
+ // implementation just keeps one vector for the row. To update one entry,
+ // only the entries to the left, top, and top-left are needed. The left
+ // entry is in row[x-1], the top entry is what's in row[x] from the last
+ // iteration, and the top-left entry is stored in previous.
+ size_t m = s1.size();
+ size_t n = s2.size();
+
+ std::vector<size_t> row(n + 1);
+ for (size_t i = 1; i <= n; ++i)
+ row[i] = i;
+
+ for (size_t y = 1; y <= m; ++y) {
+ row[0] = y;
+ size_t best_this_row = row[0];
+
+ size_t previous = y - 1;
+ for (size_t x = 1; x <= n; ++x) {
+ size_t old_row = row[x];
+ row[x] = std::min(previous + (s1[y - 1] == s2[x - 1] ? 0u : 1u),
+ std::min(row[x - 1], row[x]) + 1u);
+ previous = old_row;
+ best_this_row = std::min(best_this_row, row[x]);
+ }
+
+ if (max_edit_distance && best_this_row > max_edit_distance)
+ return max_edit_distance + 1;
+ }
+
+ return row[n];
+}
+
+std::string_view SpellcheckString(const std::string_view& text,
+ const std::vector<std::string_view>& words) {
+ const size_t kMaxValidEditDistance = 3u;
+
+ size_t min_distance = kMaxValidEditDistance + 1u;
+ std::string_view result;
+ for (std::string_view word : words) {
+ size_t distance = EditDistance(word, text, kMaxValidEditDistance);
+ if (distance < min_distance) {
+ min_distance = distance;
+ result = word;
+ }
+ }
+ return result;
+}
+
+std::string ReadStdin() {
+ char buffer[4 << 10];
+ std::string result;
+ size_t len;
+ while ((len = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
+ result.append(buffer, len);
+ // TODO(thakis): Check ferror(stdin)?
+ return result;
+}
diff --git a/gn/src/gn/string_utils.h b/gn/src/gn/string_utils.h
new file mode 100644
index 00000000000..a98a4857edd
--- /dev/null
+++ b/gn/src/gn/string_utils.h
@@ -0,0 +1,56 @@
+// 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 TOOLS_GN_STRING_UTILS_H_
+#define TOOLS_GN_STRING_UTILS_H_
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+class Err;
+class Scope;
+class Token;
+class Value;
+
+inline std::string operator+(const std::string& a, const std::string_view& b) {
+ std::string ret;
+ ret.reserve(a.size() + b.size());
+ ret.assign(a);
+ ret.append(b.data(), b.size());
+ return ret;
+}
+
+inline std::string operator+(const std::string_view& a, const std::string& b) {
+ std::string ret;
+ ret.reserve(a.size() + b.size());
+ ret.assign(a.data(), a.size());
+ ret.append(b);
+ return ret;
+}
+
+// Unescapes and expands variables in the given literal, writing the result
+// to the given value. On error, sets |err| and returns false.
+bool ExpandStringLiteral(Scope* scope,
+ const Token& literal,
+ Value* result,
+ Err* err);
+
+// Returns the minimum number of inserts, deleted, and replacements of
+// characters needed to transform s1 to s2, or max_edit_distance + 1 if
+// transforming s1 into s2 isn't possible in at most max_edit_distance steps.
+size_t EditDistance(const std::string_view& s1,
+ const std::string_view& s2,
+ size_t max_edit_distance);
+
+// Given a string |text| and a vector of correctly-spelled strings |words|,
+// returns the first string in |words| closest to |text|, or an empty
+// std::string_view if none of the strings in |words| is close.
+std::string_view SpellcheckString(const std::string_view& text,
+ const std::vector<std::string_view>& words);
+
+// Reads stdin until end-of-data and returns what it read.
+std::string ReadStdin();
+
+#endif // TOOLS_GN_STRING_UTILS_H_
diff --git a/gn/src/gn/string_utils_unittest.cc b/gn/src/gn/string_utils_unittest.cc
new file mode 100644
index 00000000000..64fd0c27d9c
--- /dev/null
+++ b/gn/src/gn/string_utils_unittest.cc
@@ -0,0 +1,159 @@
+// 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 "gn/string_utils.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "gn/err.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/token.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+namespace {
+
+bool CheckExpansionCase(const char* input, const char* expected, bool success) {
+ Scope scope(static_cast<const Settings*>(nullptr));
+ int64_t one = 1;
+ scope.SetValue("one", Value(nullptr, one), nullptr);
+ scope.SetValue("onestring", Value(nullptr, "one"), nullptr);
+
+ // Nested scope called "onescope" with a value "one" inside it.
+ std::unique_ptr<Scope> onescope =
+ std::make_unique<Scope>(static_cast<const Settings*>(nullptr));
+ onescope->SetValue("one", Value(nullptr, one), nullptr);
+ scope.SetValue("onescope", Value(nullptr, std::move(onescope)), nullptr);
+
+ // List called "onelist" with one value that maps to 1.
+ Value onelist(nullptr, Value::LIST);
+ onelist.list_value().push_back(Value(nullptr, one));
+ scope.SetValue("onelist", onelist, nullptr);
+
+ // Construct the string token, which includes the quotes.
+ std::string literal_string;
+ literal_string.push_back('"');
+ literal_string.append(input);
+ literal_string.push_back('"');
+ Token literal(Location(), Token::STRING, literal_string);
+
+ Value result(nullptr, Value::STRING);
+ Err err;
+ bool ret = ExpandStringLiteral(&scope, literal, &result, &err);
+
+ // Err and return value should agree.
+ EXPECT_NE(ret, err.has_error());
+
+ if (ret != success)
+ return false;
+
+ if (!success)
+ return true; // Don't check result on failure.
+ return result.string_value() == expected;
+}
+
+} // namespace
+
+TEST(StringUtils, ExpandStringLiteralIdentifier) {
+ EXPECT_TRUE(CheckExpansionCase("", "", true));
+ EXPECT_TRUE(CheckExpansionCase("hello", "hello", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #$one", "hello #1", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #$one/two", "hello #1/two", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${one}", "hello #1", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${one}one", "hello #1one", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${one}$one", "hello #11", true));
+ EXPECT_TRUE(CheckExpansionCase("$onestring${one}$one", "one11", true));
+ EXPECT_TRUE(CheckExpansionCase("$onescope", "{\n one = 1\n}", true));
+ EXPECT_TRUE(CheckExpansionCase("$onelist", "[1]", true));
+
+ // Hex values
+ EXPECT_TRUE(CheckExpansionCase("$0x0AA",
+ "\x0A"
+ "A",
+ true));
+ EXPECT_TRUE(CheckExpansionCase("$0x0a$0xfF", "\x0A\xFF", true));
+
+ // Errors
+ EXPECT_TRUE(CheckExpansionCase("hello #$", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #$%", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #${", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #${}", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #$nonexistant", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hello #${unterminated", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hex truncated: $0", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hex truncated: $0x", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hex truncated: $0x0", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hex with bad char: $0a", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hex with bad char: $0x1z", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("hex with bad char: $0xz1", nullptr, false));
+
+ // Unknown backslash values aren't special.
+ EXPECT_TRUE(CheckExpansionCase("\\", "\\", true));
+ EXPECT_TRUE(CheckExpansionCase("\\b", "\\b", true));
+
+ // Backslashes escape some special things. \"\$\\ -> "$\ Note that gtest
+ // doesn't like this escape sequence so we have to put it out-of-line.
+ const char* in = "\\\"\\$\\\\";
+ const char* out = "\"$\\";
+ EXPECT_TRUE(CheckExpansionCase(in, out, true));
+}
+
+TEST(StringUtils, ExpandStringLiteralExpression) {
+ // Accessing the scope.
+ EXPECT_TRUE(CheckExpansionCase("hello #${onescope.one}", "hello #1", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${onescope.two}", nullptr, false));
+
+ // Accessing the list.
+ EXPECT_TRUE(CheckExpansionCase("hello #${onelist[0]}", "hello #1", true));
+ EXPECT_TRUE(CheckExpansionCase("hello #${onelist[1]}", nullptr, false));
+
+ // Trying some other (otherwise valid) expressions should fail.
+ EXPECT_TRUE(CheckExpansionCase("${1 + 2}", nullptr, false));
+ EXPECT_TRUE(CheckExpansionCase("${print(1)}", nullptr, false));
+}
+
+TEST(StringUtils, EditDistance) {
+ EXPECT_EQ(3u, EditDistance("doom melon", "dune melon", 100));
+ EXPECT_EQ(2u, EditDistance("doom melon", "dune melon", 1));
+
+ EXPECT_EQ(2u, EditDistance("ab", "ba", 100));
+ EXPECT_EQ(2u, EditDistance("ba", "ab", 100));
+
+ EXPECT_EQ(2u, EditDistance("ananas", "banana", 100));
+ EXPECT_EQ(2u, EditDistance("banana", "ananas", 100));
+
+ EXPECT_EQ(2u, EditDistance("unclear", "nuclear", 100));
+ EXPECT_EQ(2u, EditDistance("nuclear", "unclear", 100));
+
+ EXPECT_EQ(3u, EditDistance("chrome", "chromium", 100));
+ EXPECT_EQ(3u, EditDistance("chromium", "chrome", 100));
+
+ EXPECT_EQ(4u, EditDistance("", "abcd", 100));
+ EXPECT_EQ(4u, EditDistance("abcd", "", 100));
+
+ EXPECT_EQ(4u, EditDistance("xxx", "xxxxxxx", 100));
+ EXPECT_EQ(4u, EditDistance("xxxxxxx", "xxx", 100));
+
+ EXPECT_EQ(7u, EditDistance("yyy", "xxxxxxx", 100));
+ EXPECT_EQ(7u, EditDistance("xxxxxxx", "yyy", 100));
+}
+
+TEST(StringUtils, SpellcheckString) {
+ std::vector<std::string_view> words;
+ words.push_back("your");
+ words.push_back("bravado");
+ words.push_back("won\'t");
+ words.push_back("help");
+ words.push_back("you");
+ words.push_back("now");
+
+ EXPECT_EQ("help", SpellcheckString("halp", words));
+
+ // barbados has an edit distance of 4 from bravado, so there's no suggestion.
+ EXPECT_TRUE(SpellcheckString("barbados", words).empty());
+}
diff --git a/gn/src/gn/substitution_list.cc b/gn/src/gn/substitution_list.cc
new file mode 100644
index 00000000000..ecef4f54dc2
--- /dev/null
+++ b/gn/src/gn/substitution_list.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 "gn/substitution_list.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include "gn/value.h"
+
+SubstitutionList::SubstitutionList() = default;
+
+SubstitutionList::SubstitutionList(const SubstitutionList& other) = default;
+
+SubstitutionList::~SubstitutionList() = default;
+
+bool SubstitutionList::Parse(const Value& value, Err* err) {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ const std::vector<Value>& input_list = value.list_value();
+ list_.resize(input_list.size());
+ for (size_t i = 0; i < input_list.size(); i++) {
+ if (!list_[i].Parse(input_list[i], err))
+ return false;
+ }
+
+ SubstitutionBits bits;
+ FillRequiredTypes(&bits);
+ bits.FillVector(&required_types_);
+ return true;
+}
+
+bool SubstitutionList::Parse(const std::vector<std::string>& values,
+ const ParseNode* origin,
+ Err* err) {
+ list_.resize(values.size());
+ for (size_t i = 0; i < values.size(); i++) {
+ if (!list_[i].Parse(values[i], origin, err))
+ return false;
+ }
+
+ SubstitutionBits bits;
+ FillRequiredTypes(&bits);
+ bits.FillVector(&required_types_);
+ return true;
+}
+
+SubstitutionList SubstitutionList::MakeForTest(const char* a,
+ const char* b,
+ const char* c) {
+ std::vector<std::string> input_strings;
+ input_strings.push_back(a);
+ if (b)
+ input_strings.push_back(b);
+ if (c)
+ input_strings.push_back(c);
+
+ Err err;
+ SubstitutionList result;
+ result.Parse(input_strings, nullptr, &err);
+ return result;
+}
+
+void SubstitutionList::FillRequiredTypes(SubstitutionBits* bits) const {
+ for (const auto& item : list_)
+ item.FillRequiredTypes(bits);
+}
diff --git a/gn/src/gn/substitution_list.h b/gn/src/gn/substitution_list.h
new file mode 100644
index 00000000000..c1985bb5d30
--- /dev/null
+++ b/gn/src/gn/substitution_list.h
@@ -0,0 +1,46 @@
+// 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 TOOLS_GN_SUBSTITUTION_LIST_H_
+#define TOOLS_GN_SUBSTITUTION_LIST_H_
+
+#include <string>
+#include <vector>
+
+#include "gn/substitution_pattern.h"
+
+// Represents a list of strings with {{substitution_patterns}} in them.
+class SubstitutionList {
+ public:
+ SubstitutionList();
+ SubstitutionList(const SubstitutionList& other);
+ ~SubstitutionList();
+
+ bool Parse(const Value& value, Err* err);
+ bool Parse(const std::vector<std::string>& values,
+ const ParseNode* origin,
+ Err* err);
+
+ // Makes a SubstitutionList from the given hardcoded patterns.
+ static SubstitutionList MakeForTest(const char* a,
+ const char* b = nullptr,
+ const char* c = nullptr);
+
+ const std::vector<SubstitutionPattern>& list() const { return list_; }
+
+ // Returns a list of all substitution types used by the patterns in this
+ // list, with the exception of LITERAL.
+ const std::vector<const Substitution*>& required_types() const {
+ return required_types_;
+ }
+
+ void FillRequiredTypes(SubstitutionBits* bits) const;
+
+ private:
+ std::vector<SubstitutionPattern> list_;
+
+ std::vector<const Substitution*> required_types_;
+};
+
+#endif // TOOLS_GN_SUBSTITUTION_LIST_H_
diff --git a/gn/src/gn/substitution_pattern.cc b/gn/src/gn/substitution_pattern.cc
new file mode 100644
index 00000000000..a25d6312fb4
--- /dev/null
+++ b/gn/src/gn/substitution_pattern.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 "gn/substitution_pattern.h"
+
+#include <stddef.h>
+
+#include "base/strings/string_number_conversions.h"
+#include "gn/build_settings.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/value.h"
+
+SubstitutionPattern::Subrange::Subrange() : type(&SubstitutionLiteral) {}
+
+SubstitutionPattern::Subrange::Subrange(const Substitution* t,
+ const std::string& l)
+ : type(t), literal(l) {}
+
+SubstitutionPattern::Subrange::~Subrange() = default;
+
+SubstitutionPattern::SubstitutionPattern() : origin_(nullptr) {}
+
+SubstitutionPattern::SubstitutionPattern(const SubstitutionPattern& other) =
+ default;
+
+SubstitutionPattern::~SubstitutionPattern() = default;
+
+bool SubstitutionPattern::Parse(const Value& value, Err* err) {
+ if (!value.VerifyTypeIs(Value::STRING, err))
+ return false;
+ return Parse(value.string_value(), value.origin(), err);
+}
+
+bool SubstitutionPattern::Parse(const std::string& str,
+ const ParseNode* origin,
+ Err* err) {
+ DCHECK(ranges_.empty()); // Should only be called once.
+
+ size_t cur = 0;
+ while (true) {
+ size_t next = str.find("{{", cur);
+
+ // Pick up everything from the previous spot to here as a literal.
+ if (next == std::string::npos) {
+ if (cur != str.size())
+ ranges_.push_back(Subrange(&SubstitutionLiteral, str.substr(cur)));
+ break;
+ } else if (next > cur) {
+ ranges_.push_back(
+ Subrange(&SubstitutionLiteral, str.substr(cur, next - cur)));
+ }
+
+ // Find which specific pattern this corresponds to.
+ bool found_match = false;
+ for (const SubstitutionTypes* types : AllSubstitutions) {
+ for (const Substitution* sub : *types) {
+ const char* cur_pattern = sub->name;
+ size_t cur_len = strlen(cur_pattern);
+ if (str.compare(next, cur_len, cur_pattern) == 0) {
+ ranges_.push_back(Subrange(sub));
+ cur = next + cur_len;
+ found_match = true;
+ break;
+ }
+ }
+ }
+
+ // Expect all occurrances of {{ to resolve to a pattern.
+ if (!found_match) {
+ // Could make this error message more friendly if it comes up a lot. But
+ // most people will not be writing substitution patterns and the code
+ // to exactly indicate the error location is tricky.
+ *err = Err(origin, "Unknown substitution pattern",
+ "Found a {{ at offset " + base::NumberToString(next) +
+ " and did not find a known substitution following it.");
+ ranges_.clear();
+ return false;
+ }
+ }
+
+ origin_ = origin;
+
+ // Fill required types vector.
+ SubstitutionBits bits;
+ FillRequiredTypes(&bits);
+ bits.FillVector(&required_types_);
+ return true;
+}
+
+// static
+SubstitutionPattern SubstitutionPattern::MakeForTest(const char* str) {
+ Err err;
+ SubstitutionPattern pattern;
+ CHECK(pattern.Parse(str, nullptr, &err)) << err.message();
+ return pattern;
+}
+
+std::string SubstitutionPattern::AsString() const {
+ std::string result;
+ for (const auto& elem : ranges_) {
+ if (elem.type == &SubstitutionLiteral)
+ result.append(elem.literal);
+ else
+ result.append(elem.type->name);
+ }
+ return result;
+}
+
+void SubstitutionPattern::FillRequiredTypes(SubstitutionBits* bits) const {
+ for (const auto& elem : ranges_) {
+ if (elem.type != &SubstitutionLiteral)
+ bits->used.insert(elem.type);
+ }
+}
+
+bool SubstitutionPattern::IsInOutputDir(const BuildSettings* build_settings,
+ Err* err) const {
+ if (ranges_.empty()) {
+ *err = Err(origin_, "This is empty but I was expecting an output file.");
+ return false;
+ }
+
+ if (ranges_[0].type == &SubstitutionLiteral) {
+ // If the first thing is a literal, it must start with the output dir.
+ if (!EnsureStringIsInOutputDir(build_settings->build_dir(),
+ ranges_[0].literal, origin_, err))
+ return false;
+ } else {
+ // Otherwise, the first subrange must be a pattern that expands to
+ // something in the output directory.
+ if (!SubstitutionIsInOutputDir(ranges_[0].type)) {
+ *err =
+ Err(origin_, "File is not inside output directory.",
+ "The given file should be in the output directory. Normally you\n"
+ "would specify\n\"$target_out_dir/foo\" or "
+ "\"{{source_gen_dir}}/foo\".");
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/gn/src/gn/substitution_pattern.h b/gn/src/gn/substitution_pattern.h
new file mode 100644
index 00000000000..f6ad22eeb94
--- /dev/null
+++ b/gn/src/gn/substitution_pattern.h
@@ -0,0 +1,79 @@
+// 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 TOOLS_GN_SUBSTITUTION_PATTERN_H_
+#define TOOLS_GN_SUBSTITUTION_PATTERN_H_
+
+#include <string>
+#include <vector>
+
+#include "gn/substitution_type.h"
+
+class BuildSettings;
+class Err;
+class ParseNode;
+class Value;
+
+// Represents a string with {{substitution_patterns}} in them.
+class SubstitutionPattern {
+ public:
+ struct Subrange {
+ Subrange();
+ explicit Subrange(const Substitution* t, const std::string& l = std::string());
+ ~Subrange();
+
+ inline bool operator==(const Subrange& other) const {
+ return type == other.type && literal == other.literal;
+ }
+
+ const Substitution* type;
+
+ // When type_ == LITERAL, this specifies the literal.
+ std::string literal;
+ };
+
+ SubstitutionPattern();
+ SubstitutionPattern(const SubstitutionPattern& other);
+ ~SubstitutionPattern();
+
+ // Parses the given string and fills in the pattern. The pattern must only
+ // be initialized once. On failure, returns false and sets the error.
+ bool Parse(const Value& value, Err* err);
+ bool Parse(const std::string& str, const ParseNode* origin, Err* err);
+
+ // Makes a pattern given a hardcoded string. Will assert if the string is
+ // not a valid pattern.
+ static SubstitutionPattern MakeForTest(const char* str);
+
+ // Returns the pattern as a string with substitutions in them.
+ std::string AsString() const;
+
+ // Sets the bits in the given vector corresponding to the substitutions used
+ // by this pattern. SubstitutionLiteral is ignored.
+ void FillRequiredTypes(SubstitutionBits* bits) const;
+
+ // Checks whether this pattern resolves to something in the output directory
+ // for the given build settings. If not, returns false and fills in the given
+ // error.
+ bool IsInOutputDir(const BuildSettings* build_settings, Err* err) const;
+
+ // Returns a vector listing the substitutions used by this pattern, not
+ // counting SubstitutionLiteral.
+ const std::vector<const Substitution*>& required_types() const {
+ return required_types_;
+ }
+
+ const std::vector<Subrange>& ranges() const { return ranges_; }
+ bool empty() const { return ranges_.empty(); }
+
+ const ParseNode* origin() const { return origin_; }
+
+ private:
+ std::vector<Subrange> ranges_;
+ const ParseNode* origin_;
+
+ std::vector<const Substitution*> required_types_;
+};
+
+#endif // TOOLS_GN_SUBSTITUTION_PATTERN_H_
diff --git a/gn/src/gn/substitution_pattern_unittest.cc b/gn/src/gn/substitution_pattern_unittest.cc
new file mode 100644
index 00000000000..76e4dec7129
--- /dev/null
+++ b/gn/src/gn/substitution_pattern_unittest.cc
@@ -0,0 +1,73 @@
+// 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 "gn/substitution_pattern.h"
+
+#include "gn/err.h"
+#include "gn/rust_substitution_type.h"
+#include "util/test/test.h"
+
+TEST(SubstitutionPattern, ParseLiteral) {
+ SubstitutionPattern pattern;
+ Err err;
+ EXPECT_TRUE(pattern.Parse("This is a literal", nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(1u, pattern.ranges().size());
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
+ EXPECT_EQ("This is a literal", pattern.ranges()[0].literal);
+}
+
+TEST(SubstitutionPattern, ParseComplex) {
+ SubstitutionPattern pattern;
+ Err err;
+ EXPECT_TRUE(pattern.Parse(
+ "AA{{source}}{{source_name_part}}BB{{source_file_part}}", nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(5u, pattern.ranges().size());
+
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
+ EXPECT_EQ("AA", pattern.ranges()[0].literal);
+ EXPECT_EQ(&SubstitutionSource, pattern.ranges()[1].type);
+ EXPECT_EQ(&SubstitutionSourceNamePart, pattern.ranges()[2].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[3].type);
+ EXPECT_EQ("BB", pattern.ranges()[3].literal);
+ EXPECT_EQ(&SubstitutionSourceFilePart, pattern.ranges()[4].type);
+}
+
+TEST(SubstitutionPattern, ParseErrors) {
+ SubstitutionPattern pattern;
+ Err err;
+ EXPECT_FALSE(pattern.Parse("AA{{source", nullptr, &err));
+ EXPECT_TRUE(err.has_error());
+
+ err = Err();
+ EXPECT_FALSE(pattern.Parse("{{source_of_evil}}", nullptr, &err));
+ EXPECT_TRUE(err.has_error());
+
+ err = Err();
+ EXPECT_FALSE(pattern.Parse("{{source{{source}}", nullptr, &err));
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(SubstitutionPattern, ParseRust) {
+ SubstitutionPattern pattern;
+ Err err;
+ EXPECT_TRUE(pattern.Parse(
+ "AA{{rustflags}}{{rustenv}}BB{{crate_name}}{{rustdeps}}CC{{externs}}",
+ nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(8u, pattern.ranges().size());
+
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
+ EXPECT_EQ("AA", pattern.ranges()[0].literal);
+ EXPECT_EQ(&kRustSubstitutionRustFlags, pattern.ranges()[1].type);
+ EXPECT_EQ(&kRustSubstitutionRustEnv, pattern.ranges()[2].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[3].type);
+ EXPECT_EQ("BB", pattern.ranges()[3].literal);
+ EXPECT_EQ(&kRustSubstitutionCrateName, pattern.ranges()[4].type);
+ EXPECT_EQ(&kRustSubstitutionRustDeps, pattern.ranges()[5].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[6].type);
+ EXPECT_EQ("CC", pattern.ranges()[6].literal);
+ EXPECT_EQ(&kRustSubstitutionExterns, pattern.ranges()[7].type);
+} \ No newline at end of file
diff --git a/gn/src/gn/substitution_type.cc b/gn/src/gn/substitution_type.cc
new file mode 100644
index 00000000000..06c21b36ef1
--- /dev/null
+++ b/gn/src/gn/substitution_type.cc
@@ -0,0 +1,204 @@
+// 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 "gn/substitution_type.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "gn/c_substitution_type.h"
+#include "gn/err.h"
+#include "gn/rust_substitution_type.h"
+
+const std::vector<SubstitutionTypes*> AllSubstitutions = {
+ &GeneralSubstitutions, &CSubstitutions, &RustSubstitutions};
+
+const SubstitutionTypes GeneralSubstitutions = {
+ &SubstitutionLiteral,
+
+ &SubstitutionOutput,
+ &SubstitutionLabel,
+ &SubstitutionLabelName,
+ &SubstitutionLabelNoToolchain,
+ &SubstitutionRootGenDir,
+ &SubstitutionRootOutDir,
+ &SubstitutionOutputDir,
+ &SubstitutionOutputExtension,
+ &SubstitutionTargetGenDir,
+ &SubstitutionTargetOutDir,
+ &SubstitutionTargetOutputName,
+
+ &SubstitutionSource,
+ &SubstitutionSourceNamePart,
+ &SubstitutionSourceFilePart,
+ &SubstitutionSourceDir,
+ &SubstitutionSourceRootRelativeDir,
+ &SubstitutionSourceGenDir,
+ &SubstitutionSourceOutDir,
+ &SubstitutionSourceTargetRelative,
+
+ &SubstitutionBundleRootDir,
+ &SubstitutionBundleContentsDir,
+ &SubstitutionBundleResourcesDir,
+ &SubstitutionBundleExecutableDir,
+
+ &SubstitutionBundleProductType,
+ &SubstitutionBundlePartialInfoPlist,
+ &SubstitutionXcassetsCompilerFlags,
+
+ &SubstitutionRspFileName,
+};
+
+const Substitution SubstitutionLiteral = {"<<literal>>", nullptr};
+
+const Substitution SubstitutionSource = {"{{source}}", "in"};
+const Substitution SubstitutionOutput = {"{{output}}", "out"};
+
+const Substitution SubstitutionSourceNamePart = {"{{source_name_part}}",
+ "source_name_part"};
+const Substitution SubstitutionSourceFilePart = {"{{source_file_part}}",
+ "source_file_part"};
+const Substitution SubstitutionSourceDir = {"{{source_dir}}", "source_dir"};
+const Substitution SubstitutionSourceRootRelativeDir = {
+ "{{source_root_relative_dir}}", "source_root_relative_dir"};
+const Substitution SubstitutionSourceGenDir = {"{{source_gen_dir}}",
+ "source_gen_dir"};
+const Substitution SubstitutionSourceOutDir = {"{{source_out_dir}}",
+ "source_out_dir"};
+const Substitution SubstitutionSourceTargetRelative = {
+ "{{source_target_relative}}", "source_target_relative"};
+
+// Valid for all compiler and linker tools. These depend on the target and
+// do not vary on a per-file basis.
+const Substitution SubstitutionLabel = {"{{label}}", "label"};
+const Substitution SubstitutionLabelName = {"{{label_name}}", "label_name"};
+const Substitution SubstitutionLabelNoToolchain = {"{{label_no_toolchain}}",
+ "label_no_toolchain"};
+const Substitution SubstitutionRootGenDir = {"{{root_gen_dir}}",
+ "root_gen_dir"};
+const Substitution SubstitutionRootOutDir = {"{{root_out_dir}}",
+ "root_out_dir"};
+const Substitution SubstitutionOutputDir = {"{{output_dir}}", "output_dir"};
+const Substitution SubstitutionOutputExtension = {"{{output_extension}}",
+ "output_extension"};
+const Substitution SubstitutionTargetGenDir = {"{{target_gen_dir}}",
+ "target_gen_dir"};
+const Substitution SubstitutionTargetOutDir = {"{{target_out_dir}}",
+ "target_out_dir"};
+const Substitution SubstitutionTargetOutputName = {"{{target_output_name}}",
+ "target_output_name"};
+
+// Valid for bundle_data targets.
+const Substitution SubstitutionBundleRootDir = {"{{bundle_root_dir}}",
+ "bundle_root_dir"};
+const Substitution SubstitutionBundleContentsDir = {"{{bundle_contents_dir}}",
+ "bundle_contents_dir"};
+const Substitution SubstitutionBundleResourcesDir = {"{{bundle_resources_dir}}",
+ "bundle_resources_dir"};
+const Substitution SubstitutionBundleExecutableDir = {
+ "{{bundle_executable_dir}}", "bundle_executable_dir"};
+
+// Valid for compile_xcassets tool.
+const Substitution SubstitutionBundleProductType = {"{{bundle_product_type}}",
+ "product_type"};
+const Substitution SubstitutionBundlePartialInfoPlist = {
+ "{{bundle_partial_info_plist}}", "partial_info_plist"};
+const Substitution SubstitutionXcassetsCompilerFlags = {
+ "{{xcasset_compiler_flags}}", "xcasset_compiler_flags"};
+
+// Used only for the args of actions.
+const Substitution SubstitutionRspFileName = {"{{response_file_name}}",
+ "rspfile"};
+
+SubstitutionBits::SubstitutionBits() = default;
+
+void SubstitutionBits::MergeFrom(const SubstitutionBits& other) {
+ for (const Substitution* s : other.used)
+ used.insert(s);
+}
+
+void SubstitutionBits::FillVector(
+ std::vector<const Substitution*>* vect) const {
+ for (const Substitution* s : used) {
+ vect->push_back(s);
+ }
+}
+
+bool SubstitutionIsInOutputDir(const Substitution* type) {
+ return type == &SubstitutionSourceGenDir ||
+ type == &SubstitutionSourceOutDir || type == &SubstitutionRootGenDir ||
+ type == &SubstitutionRootOutDir || type == &SubstitutionTargetGenDir ||
+ type == &SubstitutionTargetOutDir;
+}
+
+bool SubstitutionIsInBundleDir(const Substitution* type) {
+ return type == &SubstitutionBundleRootDir ||
+ type == &SubstitutionBundleContentsDir ||
+ type == &SubstitutionBundleResourcesDir ||
+ type == &SubstitutionBundleExecutableDir;
+}
+
+bool IsValidBundleDataSubstitution(const Substitution* type) {
+ return type == &SubstitutionLiteral ||
+ type == &SubstitutionSourceTargetRelative ||
+ type == &SubstitutionSourceNamePart ||
+ type == &SubstitutionSourceFilePart ||
+ type == &SubstitutionSourceRootRelativeDir ||
+ type == &SubstitutionBundleRootDir ||
+ type == &SubstitutionBundleContentsDir ||
+ type == &SubstitutionBundleResourcesDir ||
+ type == &SubstitutionBundleExecutableDir;
+}
+
+bool IsValidSourceSubstitution(const Substitution* type) {
+ return type == &SubstitutionLiteral || type == &SubstitutionSource ||
+ type == &SubstitutionSourceNamePart ||
+ type == &SubstitutionSourceFilePart ||
+ type == &SubstitutionSourceDir ||
+ type == &SubstitutionSourceRootRelativeDir ||
+ type == &SubstitutionSourceGenDir ||
+ type == &SubstitutionSourceOutDir ||
+ type == &SubstitutionSourceTargetRelative;
+}
+
+bool IsValidScriptArgsSubstitution(const Substitution* type) {
+ return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName;
+}
+
+bool IsValidToolSubstitution(const Substitution* type) {
+ return type == &SubstitutionLiteral || type == &SubstitutionOutput ||
+ type == &SubstitutionLabel || type == &SubstitutionLabelName ||
+ type == &SubstitutionLabelNoToolchain ||
+ type == &SubstitutionRootGenDir || type == &SubstitutionRootOutDir ||
+ type == &SubstitutionTargetGenDir ||
+ type == &SubstitutionTargetOutDir ||
+ type == &SubstitutionTargetOutputName;
+}
+
+bool IsValidCopySubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &SubstitutionSource;
+}
+
+bool IsValidCompileXCassetsSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &CSubstitutionLinkerInputs ||
+ type == &SubstitutionBundleProductType ||
+ type == &SubstitutionBundlePartialInfoPlist ||
+ type == &SubstitutionXcassetsCompilerFlags;
+}
+
+bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
+ bool (*is_valid_subst)(const Substitution*),
+ const ParseNode* origin,
+ Err* err) {
+ for (const Substitution* type : types) {
+ if (!is_valid_subst(type)) {
+ *err = Err(origin, "Invalid substitution type.",
+ "The substitution " + std::string(type->name) +
+ " isn't valid for something\n"
+ "operating on a source file such as this.");
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/gn/src/gn/substitution_type.h b/gn/src/gn/substitution_type.h
new file mode 100644
index 00000000000..28f6c66b64a
--- /dev/null
+++ b/gn/src/gn/substitution_type.h
@@ -0,0 +1,120 @@
+// 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 TOOLS_GN_SUBSTITUTION_TYPE_H_
+#define TOOLS_GN_SUBSTITUTION_TYPE_H_
+
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+
+class Err;
+class ParseNode;
+
+// Each pair here represents the string representation of the substitution in GN
+// and in Ninja.
+struct Substitution {
+ const char* name;
+ const char* ninja_name;
+ DISALLOW_COPY_AND_ASSIGN(Substitution);
+};
+
+using SubstitutionTypes = const std::vector<const Substitution*>;
+
+// All possible substitutions, organized into logical sets.
+extern const std::vector<SubstitutionTypes*> AllSubstitutions;
+
+// The set of substitutions available to all tools.
+extern const SubstitutionTypes GeneralSubstitutions;
+
+// Types of substitutions.
+extern const Substitution SubstitutionLiteral;
+
+// Valid for all tools. These depend on the target and
+// do not vary on a per-file basis.
+extern const Substitution SubstitutionOutput;
+extern const Substitution SubstitutionLabel;
+extern const Substitution SubstitutionLabelName;
+extern const Substitution SubstitutionLabelNoToolchain;
+extern const Substitution SubstitutionRootGenDir;
+extern const Substitution SubstitutionRootOutDir;
+extern const Substitution SubstitutionOutputDir;
+extern const Substitution SubstitutionOutputExtension;
+extern const Substitution SubstitutionTargetGenDir;
+extern const Substitution SubstitutionTargetOutDir;
+extern const Substitution SubstitutionTargetOutputName;
+
+// Valid for all compiler tools.
+extern const Substitution SubstitutionSource;
+extern const Substitution SubstitutionSourceNamePart;
+extern const Substitution SubstitutionSourceFilePart;
+extern const Substitution SubstitutionSourceDir;
+extern const Substitution SubstitutionSourceRootRelativeDir;
+extern const Substitution SubstitutionSourceGenDir;
+extern const Substitution SubstitutionSourceOutDir;
+extern const Substitution SubstitutionSourceTargetRelative;
+
+// Valid for bundle_data targets.
+extern const Substitution SubstitutionBundleRootDir;
+extern const Substitution SubstitutionBundleContentsDir;
+extern const Substitution SubstitutionBundleResourcesDir;
+extern const Substitution SubstitutionBundleExecutableDir;
+
+// Valid for compile_xcassets tool.
+extern const Substitution SubstitutionBundleProductType;
+extern const Substitution SubstitutionBundlePartialInfoPlist;
+extern const Substitution SubstitutionXcassetsCompilerFlags;
+
+// Used only for the args of actions.
+extern const Substitution SubstitutionRspFileName;
+
+// A wrapper around an array if flags indicating whether a given substitution
+// type is required in some context. By convention, the LITERAL type bit is
+// not set.
+struct SubstitutionBits {
+ SubstitutionBits();
+
+ // Merges any bits set in the given "other" to this one. This object will
+ // then be the union of all bits in the two lists.
+ void MergeFrom(const SubstitutionBits& other);
+
+ // Converts the substitution type set to a vector of the types listed. Does
+ // not include SubstitutionLiteral.
+ void FillVector(std::vector<const Substitution*>* vect) const;
+
+ // This set depends on global uniqueness of pointers, and so all points in
+ // this set should be the Substitution* constants.
+ base::flat_set<const Substitution*> used;
+};
+
+// Returns true if the given substitution pattern references the output
+// directory. This is used to check strings that begin with a substitution to
+// verify that they produce a file in the output directory.
+bool SubstitutionIsInOutputDir(const Substitution* type);
+
+// Returns true if the given substitution pattern references the bundle
+// directory. This is used to check strings that begin with a substitution to
+// verify that they produce a file in the bundle directory.
+bool SubstitutionIsInBundleDir(const Substitution* type);
+
+// Returns true if the given substitution is valid for the named purpose.
+bool IsValidBundleDataSubstitution(const Substitution* type);
+bool IsValidSourceSubstitution(const Substitution* type);
+bool IsValidScriptArgsSubstitution(const Substitution* type);
+
+// Both compiler and linker tools.
+bool IsValidToolSubstitution(const Substitution* type);
+bool IsValidCopySubstitution(const Substitution* type);
+bool IsValidCompileXCassetsSubstitution(const Substitution* type);
+
+// Validates that each substitution type in the vector passes the given
+// is_valid_subst predicate. Returns true on success. On failure, fills in the
+// error object with an appropriate message and returns false.
+bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
+ bool (*is_valid_subst)(const Substitution*),
+ const ParseNode* origin,
+ Err* err);
+
+#endif // TOOLS_GN_SUBSTITUTION_TYPE_H_
diff --git a/gn/src/gn/substitution_writer.cc b/gn/src/gn/substitution_writer.cc
new file mode 100644
index 00000000000..c9624d75624
--- /dev/null
+++ b/gn/src/gn/substitution_writer.cc
@@ -0,0 +1,587 @@
+// 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 "gn/substitution_writer.h"
+
+#include "gn/build_settings.h"
+#include "gn/c_substitution_type.h"
+#include "gn/escape.h"
+#include "gn/filesystem_utils.h"
+#include "gn/output_file.h"
+#include "gn/rust_substitution_type.h"
+#include "gn/rust_tool.h"
+#include "gn/settings.h"
+#include "gn/source_file.h"
+#include "gn/string_utils.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+#include "gn/target.h"
+
+namespace {
+
+// Sets the given directory string to the destination, trimming any trailing
+// slash from the directory (SourceDirs and OutputFiles representing
+// directories will end in a trailing slash). If the directory is empty,
+// it will be replaced with a ".".
+void SetDirOrDotWithNoSlash(const std::string& dir, std::string* dest) {
+ if (!dir.empty() && dir[dir.size() - 1] == '/')
+ dest->assign(dir.data(), dir.size() - 1);
+ else
+ dest->assign(dir);
+
+ if (dest->empty())
+ dest->push_back('.');
+}
+
+} // namespace
+
+const char kSourceExpansion_Help[] =
+ R"(How Source Expansion Works
+
+ Source expansion is used for the action_foreach and copy target types to map
+ source file names to output file names or arguments.
+
+ To perform source expansion in the outputs, GN maps every entry in the
+ sources to every entry in the outputs list, producing the cross product of
+ all combinations, expanding placeholders (see below).
+
+ Source expansion in the args works similarly, but performing the placeholder
+ substitution produces a different set of arguments for each invocation of the
+ script.
+
+ If no placeholders are found, the outputs or args list will be treated as a
+ static list of literal file names that do not depend on the sources.
+
+ See "gn help copy" and "gn help action_foreach" for more on how this is
+ applied.
+
+Placeholders
+
+ This section discusses only placeholders for actions. There are other
+ placeholders used in the definition of tools. See "gn help tool" for those.
+
+ {{source}}
+ The name of the source file including directory (*). This will generally
+ be used for specifying inputs to a script in the "args" variable.
+ "//foo/bar/baz.txt" => "../../foo/bar/baz.txt"
+
+ {{source_file_part}}
+ The file part of the source including the extension.
+ "//foo/bar/baz.txt" => "baz.txt"
+
+ {{source_name_part}}
+ The filename part of the source file with no directory or extension. This
+ will generally be used for specifying a transformation from a source file
+ to a destination file with the same name but different extension.
+ "//foo/bar/baz.txt" => "baz"
+
+ {{source_dir}}
+ The directory (*) containing the source file with no trailing slash.
+ "//foo/bar/baz.txt" => "../../foo/bar"
+
+ {{source_root_relative_dir}}
+ The path to the source file's directory relative to the source root, with
+ no leading "//" or trailing slashes. If the path is system-absolute,
+ (beginning in a single slash) this will just return the path with no
+ trailing slash. This value will always be the same, regardless of whether
+ it appears in the "outputs" or "args" section.
+ "//foo/bar/baz.txt" => "foo/bar"
+
+ {{source_gen_dir}}
+ The generated file directory (*) corresponding to the source file's path.
+ This will be different than the target's generated file directory if the
+ source file is in a different directory than the BUILD.gn file.
+ "//foo/bar/baz.txt" => "gen/foo/bar"
+
+ {{source_out_dir}}
+ The object file directory (*) corresponding to the source file's path,
+ relative to the build directory. this us be different than the target's
+ out directory if the source file is in a different directory than the
+ build.gn file.
+ "//foo/bar/baz.txt" => "obj/foo/bar"
+
+ {{source_target_relative}}
+ The path to the source file relative to the target's directory. This will
+ generally be used for replicating the source directory layout in the
+ output directory. This can only be used in actions and bundle_data
+ targets. It is an error to use in process_file_template where there is no
+ "target".
+ "//foo/bar/baz.txt" => "baz.txt"
+
+(*) Note on directories
+
+ Paths containing directories (except the source_root_relative_dir) will be
+ different depending on what context the expansion is evaluated in. Generally
+ it should "just work" but it means you can't concatenate strings containing
+ these values with reasonable results.
+
+ Details: source expansions can be used in the "outputs" variable, the "args"
+ variable, and in calls to "process_file_template". The "args" are passed to a
+ script which is run from the build directory, so these directories will
+ relative to the build directory for the script to find. In the other cases,
+ the directories will be source- absolute (begin with a "//") because the
+ results of those expansions will be handled by GN internally.
+
+Examples
+
+ Non-varying outputs:
+ action("hardcoded_outputs") {
+ sources = [ "input1.idl", "input2.idl" ]
+ outputs = [ "$target_out_dir/output1.dat",
+ "$target_out_dir/output2.dat" ]
+ }
+ The outputs in this case will be the two literal files given.
+
+ Varying outputs:
+ action_foreach("varying_outputs") {
+ sources = [ "input1.idl", "input2.idl" ]
+ outputs = [ "{{source_gen_dir}}/{{source_name_part}}.h",
+ "{{source_gen_dir}}/{{source_name_part}}.cc" ]
+ }
+ Performing source expansion will result in the following output names:
+ //out/Debug/obj/mydirectory/input1.h
+ //out/Debug/obj/mydirectory/input1.cc
+ //out/Debug/obj/mydirectory/input2.h
+ //out/Debug/obj/mydirectory/input2.cc
+)";
+
+// static
+void SubstitutionWriter::WriteWithNinjaVariables(
+ const SubstitutionPattern& pattern,
+ const EscapeOptions& escape_options,
+ std::ostream& out) {
+ // The result needs to be quoted as if it was one string, but the $ for
+ // the inserted Ninja variables can't be escaped. So write to a buffer with
+ // no quoting, and then quote the whole thing if necessary.
+ EscapeOptions no_quoting(escape_options);
+ no_quoting.inhibit_quoting = true;
+
+ bool needs_quotes = false;
+ std::string result;
+ for (const auto& range : pattern.ranges()) {
+ if (range.type == &SubstitutionLiteral) {
+ result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
+ } else {
+ result.append("${");
+ result.append(range.type->ninja_name);
+ result.append("}");
+ }
+ }
+
+ if (needs_quotes && !escape_options.inhibit_quoting)
+ out << "\"" << result << "\"";
+ else
+ out << result;
+}
+
+// static
+void SubstitutionWriter::GetListAsSourceFiles(const SubstitutionList& list,
+ std::vector<SourceFile>* output) {
+ for (const auto& pattern : list.list()) {
+ CHECK(pattern.ranges().size() == 1 &&
+ pattern.ranges()[0].type == &SubstitutionLiteral)
+ << "The substitution pattern \"" << pattern.AsString()
+ << "\" was expected to be a literal with no {{substitutions}}.";
+ const std::string& literal = pattern.ranges()[0].literal;
+ CHECK(literal.size() >= 1 && literal[0] == '/')
+ << "The result of the pattern \"" << pattern.AsString()
+ << "\" was not an absolute path.";
+ output->push_back(SourceFile(literal));
+ }
+}
+
+// static
+void SubstitutionWriter::GetListAsOutputFiles(const Settings* settings,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ std::vector<SourceFile> output_as_sources;
+ GetListAsSourceFiles(list, &output_as_sources);
+ for (const auto& file : output_as_sources)
+ output->push_back(OutputFile(settings->build_settings(), file));
+}
+
+// static
+SourceFile SubstitutionWriter::ApplyPatternToSource(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionPattern& pattern,
+ const SourceFile& source) {
+ std::string result_value =
+ ApplyPatternToSourceAsString(target, settings, pattern, source);
+ CHECK(!result_value.empty() && result_value[0] == '/')
+ << "The result of the pattern \"" << pattern.AsString()
+ << "\" was not a path beginning in \"/\" or \"//\".";
+ return SourceFile(std::move(result_value));
+}
+
+// static
+std::string SubstitutionWriter::ApplyPatternToSourceAsString(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionPattern& pattern,
+ const SourceFile& source) {
+ std::string result_value;
+ for (const auto& subrange : pattern.ranges()) {
+ if (subrange.type == &SubstitutionLiteral) {
+ result_value.append(subrange.literal);
+ } else {
+ result_value.append(GetSourceSubstitution(target, settings, source,
+ subrange.type, OUTPUT_ABSOLUTE,
+ SourceDir()));
+ }
+ }
+ return result_value;
+}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionPattern& pattern,
+ const SourceFile& source) {
+ SourceFile result_as_source =
+ ApplyPatternToSource(target, settings, pattern, source);
+ return OutputFile(settings->build_settings(), result_as_source);
+}
+
+// static
+void SubstitutionWriter::ApplyListToSource(const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const SourceFile& source,
+ std::vector<SourceFile>* output) {
+ for (const auto& item : list.list())
+ output->push_back(ApplyPatternToSource(target, settings, item, source));
+}
+
+// static
+void SubstitutionWriter::ApplyListToSourceAsString(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const SourceFile& source,
+ std::vector<std::string>* output) {
+ for (const auto& item : list.list())
+ output->push_back(
+ ApplyPatternToSourceAsString(target, settings, item, source));
+}
+
+// static
+void SubstitutionWriter::ApplyListToSourceAsOutputFile(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const SourceFile& source,
+ std::vector<OutputFile>* output) {
+ for (const auto& item : list.list())
+ output->push_back(
+ ApplyPatternToSourceAsOutputFile(target, settings, item, source));
+}
+
+// static
+void SubstitutionWriter::ApplyListToSources(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const std::vector<SourceFile>& sources,
+ std::vector<SourceFile>* output) {
+ output->clear();
+ for (const auto& source : sources)
+ ApplyListToSource(target, settings, list, source, output);
+}
+
+// static
+void SubstitutionWriter::ApplyListToSourcesAsString(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const std::vector<SourceFile>& sources,
+ std::vector<std::string>* output) {
+ output->clear();
+ for (const auto& source : sources)
+ ApplyListToSourceAsString(target, settings, list, source, output);
+}
+
+// static
+void SubstitutionWriter::ApplyListToSourcesAsOutputFile(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const std::vector<SourceFile>& sources,
+ std::vector<OutputFile>* output) {
+ output->clear();
+ for (const auto& source : sources)
+ ApplyListToSourceAsOutputFile(target, settings, list, source, output);
+}
+
+// static
+void SubstitutionWriter::WriteNinjaVariablesForSource(
+ const Target* target,
+ const Settings* settings,
+ const SourceFile& source,
+ const std::vector<const Substitution*>& types,
+ const EscapeOptions& escape_options,
+ std::ostream& out) {
+ for (const auto& type : types) {
+ // Don't write SOURCE since that just maps to Ninja's $in variable, which
+ // is implicit in the rule. RESPONSE_FILE_NAME is written separately
+ // only when writing target rules since it can never be used in any
+ // other context (like process_file_template).
+ if (type != &SubstitutionSource && type != &SubstitutionRspFileName) {
+ out << " " << type->ninja_name << " = ";
+ EscapeStringToStream(
+ out,
+ GetSourceSubstitution(target, settings, source, type, OUTPUT_RELATIVE,
+ settings->build_settings()->build_dir()),
+ escape_options);
+ out << std::endl;
+ }
+ }
+}
+
+// static
+std::string SubstitutionWriter::GetSourceSubstitution(
+ const Target* target,
+ const Settings* settings,
+ const SourceFile& source,
+ const Substitution* type,
+ OutputStyle output_style,
+ const SourceDir& relative_to) {
+ std::string to_rebase;
+ if (type == &SubstitutionSource) {
+ if (source.is_system_absolute())
+ return source.value();
+ to_rebase = source.value();
+ } else if (type == &SubstitutionSourceNamePart) {
+ return std::string(FindFilenameNoExtension(&source.value()));
+ } else if (type == &SubstitutionSourceFilePart) {
+ return source.GetName();
+ } else if (type == &SubstitutionSourceDir) {
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ to_rebase = DirectoryWithNoLastSlash(source.GetDir());
+ } else if (type == &SubstitutionSourceRootRelativeDir) {
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ return RebasePath(DirectoryWithNoLastSlash(source.GetDir()),
+ SourceDir("//"),
+ settings->build_settings()->root_path_utf8());
+ } else if (type == &SubstitutionSourceGenDir) {
+ to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings), source.GetDir(), BuildDirType::GEN));
+ } else if (type == &SubstitutionSourceOutDir) {
+ to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ));
+ } else if (type == &SubstitutionSourceTargetRelative) {
+ if (target) {
+ return RebasePath(source.value(), target->label().dir(),
+ settings->build_settings()->root_path_utf8());
+ }
+ NOTREACHED() << "Cannot use substitution " << type->name
+ << " without target";
+ return std::string();
+ } else if (IsValidRustSubstitution(type)) {
+ to_rebase = source.value();
+ } else {
+ NOTREACHED() << "Unsupported substitution for this function: "
+ << type->name;
+ return std::string();
+ }
+
+ // If we get here, the result is a path that should be made relative or
+ // absolute according to the output_style. Other cases (just file name or
+ // extension extraction) will have been handled via early return above.
+ if (output_style == OUTPUT_ABSOLUTE)
+ return to_rebase;
+ return RebasePath(to_rebase, relative_to,
+ settings->build_settings()->root_path_utf8());
+}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionPattern& pattern) {
+ std::string result_value;
+ for (const auto& subrange : pattern.ranges()) {
+ if (subrange.type == &SubstitutionLiteral) {
+ result_value.append(subrange.literal);
+ } else {
+ std::string subst;
+ CHECK(GetTargetSubstitution(target, subrange.type, &subst));
+ result_value.append(subst);
+ }
+ }
+ return OutputFile(result_value);
+}
+
+// static
+void SubstitutionWriter::ApplyListToTargetAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ for (const auto& item : list.list())
+ output->push_back(ApplyPatternToTargetAsOutputFile(target, tool, item));
+}
+
+// static
+bool SubstitutionWriter::GetTargetSubstitution(const Target* target,
+ const Substitution* type,
+ std::string* result) {
+ if (type == &SubstitutionLabel) {
+ // Only include the toolchain for non-default toolchains.
+ *result =
+ target->label().GetUserVisibleName(!target->settings()->is_default());
+ } else if (type == &SubstitutionLabelName) {
+ *result = target->label().name();
+ } else if (type == &SubstitutionLabelNoToolchain) {
+ *result = target->label().GetUserVisibleName(false);
+ } else if (type == &SubstitutionRootGenDir) {
+ SetDirOrDotWithNoSlash(
+ GetBuildDirAsOutputFile(BuildDirContext(target), BuildDirType::GEN)
+ .value(),
+ result);
+ } else if (type == &SubstitutionRootOutDir) {
+ SetDirOrDotWithNoSlash(
+ target->settings()->toolchain_output_subdir().value(), result);
+ } else if (type == &SubstitutionTargetGenDir) {
+ SetDirOrDotWithNoSlash(
+ GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN).value(),
+ result);
+ } else if (type == &SubstitutionTargetOutDir) {
+ SetDirOrDotWithNoSlash(
+ GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ).value(),
+ result);
+ } else if (type == &SubstitutionTargetOutputName) {
+ *result = target->GetComputedOutputName();
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// static
+std::string SubstitutionWriter::GetTargetSubstitution(
+ const Target* target,
+ const Substitution* type) {
+ std::string result;
+ GetTargetSubstitution(target, type, &result);
+ return result;
+}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
+ const Target* target,
+ const SourceFile& source,
+ const SubstitutionPattern& pattern) {
+ OutputFile result;
+ for (const auto& subrange : pattern.ranges()) {
+ if (subrange.type == &SubstitutionLiteral) {
+ result.value().append(subrange.literal);
+ } else {
+ result.value().append(
+ GetCompilerSubstitution(target, source, subrange.type));
+ }
+ }
+ return result;
+}
+
+// static
+void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+ const Target* target,
+ const SourceFile& source,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ for (const auto& item : list.list())
+ output->push_back(ApplyPatternToCompilerAsOutputFile(target, source, item));
+}
+
+// static
+std::string SubstitutionWriter::GetCompilerSubstitution(
+ const Target* target,
+ const SourceFile& source,
+ const Substitution* type) {
+ // First try the common tool ones.
+ std::string result;
+ if (GetTargetSubstitution(target, type, &result))
+ return result;
+
+ // Fall-through to the source ones.
+ return GetSourceSubstitution(
+ target, target->settings(), source, type, OUTPUT_RELATIVE,
+ target->settings()->build_settings()->build_dir());
+}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionPattern& pattern) {
+ OutputFile result;
+ for (const auto& subrange : pattern.ranges()) {
+ if (subrange.type == &SubstitutionLiteral) {
+ result.value().append(subrange.literal);
+ } else {
+ result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
+ }
+ }
+ return result;
+}
+
+// static
+void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ for (const auto& item : list.list())
+ output->push_back(ApplyPatternToLinkerAsOutputFile(target, tool, item));
+}
+
+// static
+std::string SubstitutionWriter::GetLinkerSubstitution(
+ const Target* target,
+ const Tool* tool,
+ const Substitution* type) {
+ // First try the common tool ones.
+ std::string result;
+ if (GetTargetSubstitution(target, type, &result))
+ return result;
+
+ // Fall-through to the linker-specific ones.
+ if (type == &SubstitutionOutputDir) {
+ // Use the target's value if there is one (it will have no expansion
+ // patterns since it can directly use GN variables to compute whatever
+ // path it wants), or the tool's default (which will contain further
+ // expansions).
+ if (target->output_dir().is_null()) {
+ return ApplyPatternToLinkerAsOutputFile(target, tool,
+ tool->default_output_dir())
+ .value();
+ }
+ SetDirOrDotWithNoSlash(
+ RebasePath(target->output_dir().value(),
+ target->settings()->build_settings()->build_dir()),
+ &result);
+ return result;
+ } else if (type == &SubstitutionOutputExtension) {
+ // Use the extension provided on the target if specified, otherwise
+ // fall back on the default. Note that the target's output extension
+ // does not include the dot but the tool's does.
+ if (!target->output_extension_set())
+ return tool->default_output_extension();
+ if (target->output_extension().empty())
+ return std::string(); // Explicitly set to no extension.
+ return std::string(".") + target->output_extension();
+ } else if (type == &kRustSubstitutionCrateName) {
+ // Only include the toolchain for non-default toolchains.
+ return target->rust_values().crate_name();
+ } else if (type == &CSubstitutionSwiftModuleName) {
+ return target->swift_values().module_name();
+ } else {
+ NOTREACHED();
+ return std::string();
+ }
+}
diff --git a/gn/src/gn/substitution_writer.h b/gn/src/gn/substitution_writer.h
new file mode 100644
index 00000000000..95e4b94a022
--- /dev/null
+++ b/gn/src/gn/substitution_writer.h
@@ -0,0 +1,238 @@
+// 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 TOOLS_GN_SUBSTITUTION_WRITER_H_
+#define TOOLS_GN_SUBSTITUTION_WRITER_H_
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "gn/substitution_type.h"
+
+struct EscapeOptions;
+class OutputFile;
+class Settings;
+class SourceDir;
+class SourceFile;
+class SubstitutionList;
+class SubstitutionPattern;
+class Target;
+class Tool;
+
+// Help text for script source expansion.
+extern const char kSourceExpansion_Help[];
+
+// This class handles writing or applying substitution patterns to strings.
+//
+// There are several different uses:
+//
+// - Source substitutions: These are used to compute action_foreach
+// outputs and arguments. Functions are provided to expand these in terms
+// of both OutputFiles (for writing Ninja files) as well as SourceFiles
+// (for computing lists used by code).
+//
+// - Target substitutions: These are specific to the target+tool combination
+// and are shared between the compiler and linker ones. It includes things
+// like the target_gen_dir.
+//
+// - Compiler substitutions: These are used to compute compiler outputs.
+// It includes all source substitutions (since they depend on the various
+// parts of the source file) as well as the target substitutions.
+//
+// - Linker substitutions: These are used to compute linker outputs. It
+// includes the target substitutions.
+//
+// The compiler and linker specific substitutions do NOT include the various
+// cflags, ldflags, libraries, etc. These are written by the ninja target
+// writer since they depend on traversing the dependency tree.
+//
+// The methods which take a target as an argument can accept null target
+// pointer if there is no target context, in which case the substitutions
+// requiring target context will not work.
+class SubstitutionWriter {
+ public:
+ enum OutputStyle {
+ OUTPUT_ABSOLUTE, // Dirs will be absolute "//foo/bar".
+ OUTPUT_RELATIVE, // Dirs will be relative to a given directory.
+ };
+
+ // Writes the pattern to the given stream with no special handling, and with
+ // Ninja variables replacing the patterns.
+ static void WriteWithNinjaVariables(const SubstitutionPattern& pattern,
+ const EscapeOptions& escape_options,
+ std::ostream& out);
+
+ // NOP substitutions ---------------------------------------------------------
+
+ // Converts the given SubstitutionList to OutputFiles assuming there are
+ // no substitutions (it will assert if there are). This is used for cases
+ // like actions where the outputs are explicit, but the list is stored as
+ // a SubstitutionList.
+ static void GetListAsSourceFiles(const SubstitutionList& list,
+ std::vector<SourceFile>* output);
+ static void GetListAsOutputFiles(const Settings* settings,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output);
+
+ // Source substitutions -----------------------------------------------------
+
+ // Applies the substitution pattern to a source file, returning the result
+ // as either a string, a SourceFile or an OutputFile. If the result is
+ // expected to be a SourceFile or an OutputFile, this will CHECK if the
+ // result isn't in the correct directory. The caller should validate this
+ // first (see for example IsFileInOuputDir).
+ //
+ // The target can be null (see class comment above).
+ static SourceFile ApplyPatternToSource(const Target* target,
+ const Settings* settings,
+ const SubstitutionPattern& pattern,
+ const SourceFile& source);
+ static std::string ApplyPatternToSourceAsString(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionPattern& pattern,
+ const SourceFile& source);
+ static OutputFile ApplyPatternToSourceAsOutputFile(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionPattern& pattern,
+ const SourceFile& source);
+
+ // Applies the substitution list to a source, APPENDING the result to the
+ // given output vector. It works this way so one can call multiple times to
+ // apply to multiple files and create a list. The result can either be
+ // SourceFiles or OutputFiles.
+ //
+ // The target can be null (see class comment above).
+ static void ApplyListToSource(const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const SourceFile& source,
+ std::vector<SourceFile>* output);
+ static void ApplyListToSourceAsString(const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const SourceFile& source,
+ std::vector<std::string>* output);
+ static void ApplyListToSourceAsOutputFile(const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const SourceFile& source,
+ std::vector<OutputFile>* output);
+
+ // Like ApplyListToSource but applies the list to all sources and replaces
+ // rather than appends the output (this produces the complete output).
+ //
+ // The target can be null (see class comment above).
+ static void ApplyListToSources(const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const std::vector<SourceFile>& sources,
+ std::vector<SourceFile>* output);
+ static void ApplyListToSourcesAsString(const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const std::vector<SourceFile>& sources,
+ std::vector<std::string>* output);
+ static void ApplyListToSourcesAsOutputFile(
+ const Target* target,
+ const Settings* settings,
+ const SubstitutionList& list,
+ const std::vector<SourceFile>& sources,
+ std::vector<OutputFile>* output);
+
+ // Given a list of source replacement types used, writes the Ninja variable
+ // definitions for the given source file to use for those replacements. The
+ // variables will be indented two spaces. Since this is for writing to
+ // Ninja files, paths will be relative to the build dir, and no definition
+ // for {{source}} will be written since that maps to Ninja's implicit $in
+ // variable.
+ //
+ // The target can be null (see class comment above).
+ static void WriteNinjaVariablesForSource(
+ const Target* target,
+ const Settings* settings,
+ const SourceFile& source,
+ const std::vector<const Substitution*>& types,
+ const EscapeOptions& escape_options,
+ std::ostream& out);
+
+ // Extracts the given type of substitution related to a source file from the
+ // given source file. If output_style is OUTPUT_RELATIVE, relative_to
+ // indicates the directory that the relative directories should be relative
+ // to, otherwise it is ignored.
+ //
+ // The target can be null (see class comment above).
+ static std::string GetSourceSubstitution(const Target* target,
+ const Settings* settings,
+ const SourceFile& source,
+ const Substitution* type,
+ OutputStyle output_style,
+ const SourceDir& relative_to);
+
+ // Target substitutions ------------------------------------------------------
+ //
+ // Handles the target substitutions that apply to both compiler and linker
+ // tools.
+ static OutputFile ApplyPatternToTargetAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionPattern& pattern);
+ static void ApplyListToTargetAsOutputFile(const Target* target,
+ const Tool* tool,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output);
+
+ // This function is slightly different than the other substitution getters
+ // since it can handle failure (since it is designed to be used by the
+ // compiler and linker ones which will fall through if it's not a common tool
+ // one).
+ static bool GetTargetSubstitution(const Target* target,
+ const Substitution* type,
+ std::string* result);
+ static std::string GetTargetSubstitution(const Target* target,
+ const Substitution* type);
+
+ // Compiler substitutions ----------------------------------------------------
+ //
+ // A compiler substitution allows both source and tool substitutions. These
+ // are used to compute output names for compiler tools.
+
+ static OutputFile ApplyPatternToCompilerAsOutputFile(
+ const Target* target,
+ const SourceFile& source,
+ const SubstitutionPattern& pattern);
+ static void ApplyListToCompilerAsOutputFile(const Target* target,
+ const SourceFile& source,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output);
+
+ // Like GetSourceSubstitution but for strings based on the target or
+ // toolchain. This type of result will always be relative to the build
+ // directory.
+ static std::string GetCompilerSubstitution(const Target* target,
+ const SourceFile& source,
+ const Substitution* type);
+
+ // Linker substitutions ------------------------------------------------------
+
+ static OutputFile ApplyPatternToLinkerAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionPattern& pattern);
+ static void ApplyListToLinkerAsOutputFile(const Target* target,
+ const Tool* tool,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output);
+
+ // Like GetSourceSubstitution but for strings based on the target or
+ // toolchain. This type of result will always be relative to the build
+ // directory.
+ static std::string GetLinkerSubstitution(const Target* target,
+ const Tool* tool,
+ const Substitution* type);
+};
+
+#endif // TOOLS_GN_SUBSTITUTION_WRITER_H_
diff --git a/gn/src/gn/substitution_writer_unittest.cc b/gn/src/gn/substitution_writer_unittest.cc
new file mode 100644
index 00000000000..fc3c44692a7
--- /dev/null
+++ b/gn/src/gn/substitution_writer_unittest.cc
@@ -0,0 +1,325 @@
+// 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 <sstream>
+
+#include "gn/c_substitution_type.h"
+#include "gn/err.h"
+#include "gn/escape.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(SubstitutionWriter, GetListAs) {
+ TestWithScope setup;
+
+ SubstitutionList list =
+ SubstitutionList::MakeForTest("//foo/bar/a.cc", "//foo/bar/b.cc");
+
+ std::vector<SourceFile> sources;
+ SubstitutionWriter::GetListAsSourceFiles(list, &sources);
+ ASSERT_EQ(2u, sources.size());
+ EXPECT_EQ("//foo/bar/a.cc", sources[0].value());
+ EXPECT_EQ("//foo/bar/b.cc", sources[1].value());
+
+ std::vector<OutputFile> outputs;
+ SubstitutionWriter::GetListAsOutputFiles(setup.settings(), list, &outputs);
+ ASSERT_EQ(2u, outputs.size());
+ EXPECT_EQ("../../foo/bar/a.cc", outputs[0].value());
+ EXPECT_EQ("../../foo/bar/b.cc", outputs[1].value());
+}
+
+TEST(SubstitutionWriter, ApplyPatternToSource) {
+ TestWithScope setup;
+
+ SubstitutionPattern pattern;
+ Err err;
+ ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp",
+ nullptr, &err));
+
+ SourceFile result = SubstitutionWriter::ApplyPatternToSource(
+ nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
+ ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value());
+}
+
+TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) {
+ TestWithScope setup;
+
+ SubstitutionPattern pattern;
+ Err err;
+ ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp",
+ nullptr, &err));
+
+ OutputFile result = SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
+ nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
+ ASSERT_EQ("gen/foo/bar/myfile.tmp", result.value());
+}
+
+TEST(SubstitutionWriter, WriteNinjaVariablesForSource) {
+ TestWithScope setup;
+
+ std::vector<const Substitution*> types;
+ types.push_back(&SubstitutionSource);
+ types.push_back(&SubstitutionSourceNamePart);
+ types.push_back(&SubstitutionSourceDir);
+
+ EscapeOptions options;
+ options.mode = ESCAPE_NONE;
+
+ std::ostringstream out;
+ SubstitutionWriter::WriteNinjaVariablesForSource(
+ nullptr, setup.settings(), SourceFile("//foo/bar/baz.txt"), types,
+ options, out);
+
+ // The "source" should be skipped since that will expand to $in which is
+ // implicit.
+ EXPECT_EQ(
+ " source_name_part = baz\n"
+ " source_dir = ../../foo/bar\n",
+ out.str());
+}
+
+TEST(SubstitutionWriter, WriteWithNinjaVariables) {
+ Err err;
+ SubstitutionPattern pattern;
+ ASSERT_TRUE(pattern.Parse("-i {{source}} --out=bar\"{{source_name_part}}\".o",
+ nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+
+ EscapeOptions options;
+ options.mode = ESCAPE_NONE;
+
+ std::ostringstream out;
+ SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out);
+
+ EXPECT_EQ("-i ${in} --out=bar\"${source_name_part}\".o", out.str());
+}
+
+TEST(SubstitutionWriter, SourceSubstitutions) {
+ TestWithScope setup;
+ Err err;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+// Call to get substitutions relative to the build dir.
+#define GetRelSubst(str, what) \
+ SubstitutionWriter::GetSourceSubstitution( \
+ &target, setup.settings(), SourceFile(str), what, \
+ SubstitutionWriter::OUTPUT_RELATIVE, \
+ setup.settings()->build_settings()->build_dir())
+
+// Call to get absolute directory substitutions.
+#define GetAbsSubst(str, what) \
+ SubstitutionWriter::GetSourceSubstitution( \
+ &target, setup.settings(), SourceFile(str), what, \
+ SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir())
+
+ // Try all possible templates with a normal looking string.
+ EXPECT_EQ("../../foo/bar/baz.txt",
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSource));
+ EXPECT_EQ("//foo/bar/baz.txt",
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSource));
+
+ EXPECT_EQ("baz",
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
+ EXPECT_EQ("baz",
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
+
+ EXPECT_EQ("baz.txt",
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
+ EXPECT_EQ("baz.txt",
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
+
+ EXPECT_EQ("../../foo/bar",
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
+ EXPECT_EQ("//foo/bar",
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
+
+ EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt",
+ &SubstitutionSourceRootRelativeDir));
+ EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt",
+ &SubstitutionSourceRootRelativeDir));
+
+ EXPECT_EQ("gen/foo/bar",
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
+ EXPECT_EQ("//out/Debug/gen/foo/bar",
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
+
+ EXPECT_EQ("obj/foo/bar",
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
+ EXPECT_EQ("//out/Debug/obj/foo/bar",
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
+
+ // Operations on an absolute path.
+ EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", &SubstitutionSource));
+ EXPECT_EQ("/.", GetRelSubst("/baz.txt", &SubstitutionSourceDir));
+ EXPECT_EQ("gen/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceGenDir));
+ EXPECT_EQ("obj/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceOutDir));
+#if defined(OS_WIN)
+ EXPECT_EQ("gen/ABS_PATH/C",
+ GetRelSubst("/C:/baz.txt", &SubstitutionSourceGenDir));
+ EXPECT_EQ("obj/ABS_PATH/C",
+ GetRelSubst("/C:/baz.txt", &SubstitutionSourceOutDir));
+#endif
+
+ EXPECT_EQ(".", GetRelSubst("//baz.txt", &SubstitutionSourceRootRelativeDir));
+
+ EXPECT_EQ("baz.txt", GetRelSubst("//foo/bar/baz.txt",
+ &SubstitutionSourceTargetRelative));
+ EXPECT_EQ("baz.txt", GetAbsSubst("//foo/bar/baz.txt",
+ &SubstitutionSourceTargetRelative));
+
+#undef GetAbsSubst
+#undef GetRelSubst
+}
+
+TEST(SubstitutionWriter, TargetSubstitutions) {
+ TestWithScope setup;
+ Err err;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::string result;
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionLabel, &result));
+ EXPECT_EQ("//foo/bar:baz", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionLabelName, &result));
+ EXPECT_EQ("baz", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionLabelNoToolchain, &result));
+ EXPECT_EQ("//foo/bar:baz", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionRootGenDir, &result));
+ EXPECT_EQ("gen", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionRootOutDir, &result));
+ EXPECT_EQ(".", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionTargetGenDir, &result));
+ EXPECT_EQ("gen/foo/bar", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionTargetOutDir, &result));
+ EXPECT_EQ("obj/foo/bar", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, &SubstitutionTargetOutputName, &result));
+ EXPECT_EQ("libbaz", result);
+}
+
+TEST(SubstitutionWriter, CompilerSubstitutions) {
+ TestWithScope setup;
+ Err err;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // The compiler substitution is just source + target combined. So test one
+ // of each of those classes of things to make sure this is hooked up.
+ EXPECT_EQ("file", SubstitutionWriter::GetCompilerSubstitution(
+ &target, SourceFile("//foo/bar/file.txt"),
+ &SubstitutionSourceNamePart));
+ EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetCompilerSubstitution(
+ &target, SourceFile("//foo/bar/file.txt"),
+ &SubstitutionTargetGenDir));
+}
+
+TEST(SubstitutionWriter, LinkerSubstitutions) {
+ TestWithScope setup;
+ Err err;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target);
+
+ // The compiler substitution is just target + OUTPUT_EXTENSION combined. So
+ // test one target one plus the output extension.
+ EXPECT_EQ(".so", SubstitutionWriter::GetLinkerSubstitution(
+ &target, tool, &SubstitutionOutputExtension));
+ EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetLinkerSubstitution(
+ &target, tool, &SubstitutionTargetGenDir));
+
+ // Test that we handle paths that end up in the root build dir properly
+ // (no leading "./" or "/").
+ SubstitutionPattern pattern;
+ ASSERT_TRUE(pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so",
+ nullptr, &err));
+
+ OutputFile output = SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ &target, tool, pattern);
+ EXPECT_EQ("./libbaz.so", output.value());
+
+ // Output extensions can be overridden.
+ target.set_output_extension("extension");
+ EXPECT_EQ(".extension", SubstitutionWriter::GetLinkerSubstitution(
+ &target, tool, &SubstitutionOutputExtension));
+ target.set_output_extension("");
+ EXPECT_EQ("", SubstitutionWriter::GetLinkerSubstitution(
+ &target, tool, &SubstitutionOutputExtension));
+
+ // Output directory is tested in a separate test below.
+}
+
+TEST(SubstitutionWriter, OutputDir) {
+ TestWithScope setup;
+ Err err;
+
+ // This tool has an output directory pattern and uses that for the
+ // output name.
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolLink);
+ SubstitutionPattern out_dir_pattern;
+ ASSERT_TRUE(out_dir_pattern.Parse("{{root_out_dir}}/{{target_output_name}}",
+ nullptr, &err));
+ tool->set_default_output_dir(out_dir_pattern);
+ tool->SetComplete();
+
+ // Default target with no output dir overrides.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "baz"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // The output should expand the default from the patterns in the tool.
+ SubstitutionPattern output_name;
+ ASSERT_TRUE(output_name.Parse("{{output_dir}}/{{target_output_name}}.exe",
+ nullptr, &err));
+ EXPECT_EQ("./baz/baz.exe",
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ &target, tool.get(), output_name)
+ .value());
+
+ // Override the output name to the root build dir.
+ target.set_output_dir(SourceDir("//out/Debug/"));
+ EXPECT_EQ("./baz.exe", SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ &target, tool.get(), output_name)
+ .value());
+
+ // Override the output name to a new subdirectory.
+ target.set_output_dir(SourceDir("//out/Debug/foo/bar"));
+ EXPECT_EQ("foo/bar/baz.exe",
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ &target, tool.get(), output_name)
+ .value());
+}
diff --git a/gn/src/gn/swift_values.cc b/gn/src/gn/swift_values.cc
new file mode 100644
index 00000000000..c15e319bb5b
--- /dev/null
+++ b/gn/src/gn/swift_values.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 The Chromium 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 "gn/swift_values.h"
+
+#include "gn/deps_iterator.h"
+#include "gn/err.h"
+#include "gn/settings.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+
+SwiftValues::SwiftValues() = default;
+
+SwiftValues::~SwiftValues() = default;
+
+bool SwiftValues::OnTargetResolved(const Target* target, Err* err) {
+ if (!FillModuleOuputFile(target, err))
+ return false;
+
+ FillModuleDependencies(target);
+ return true;
+}
+
+void SwiftValues::FillModuleDependencies(const Target* target) {
+ for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
+ if (pair.ptr->toolchain() == target->toolchain() ||
+ pair.ptr->toolchain()->propagates_configs()) {
+ modules_.Append(pair.ptr->swift_values().public_modules().begin(),
+ pair.ptr->swift_values().public_modules().end());
+ }
+ }
+
+ for (const auto& pair : target->public_deps()) {
+ if (pair.ptr->toolchain() == target->toolchain() ||
+ pair.ptr->toolchain()->propagates_configs())
+ public_modules_.Append(pair.ptr->swift_values().public_modules().begin(),
+ pair.ptr->swift_values().public_modules().end());
+ }
+
+ if (builds_module())
+ public_modules_.push_back(target);
+}
+
+bool SwiftValues::FillModuleOuputFile(const Target* target, Err* err) {
+ if (!target->IsBinary() || !target->source_types_used().SwiftSourceUsed())
+ return true;
+
+ const Tool* tool =
+ target->toolchain()->GetToolForSourceType(SourceFile::SOURCE_SWIFT);
+ CHECK(tool->outputs().list().size() >= 1);
+
+ OutputFile module_output_file =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ target, tool, tool->outputs().list()[0]);
+
+ const SourceFile module_output_file_as_source =
+ module_output_file.AsSourceFile(target->settings()->build_settings());
+ if (module_output_file_as_source.type() != SourceFile::SOURCE_SWIFTMODULE) {
+ *err = Err(tool->defined_from(), "Incorrect outputs for tool",
+ "The first output of tool " + std::string(tool->name()) +
+ " must be a .swiftmodule file.");
+ return false;
+ }
+
+ module_output_file_ = std::move(module_output_file);
+ module_output_dir_ = module_output_file_as_source.GetDir();
+
+ return true;
+}
diff --git a/gn/src/gn/swift_values.h b/gn/src/gn/swift_values.h
new file mode 100644
index 00000000000..c52973843a9
--- /dev/null
+++ b/gn/src/gn/swift_values.h
@@ -0,0 +1,91 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_SWIFT_TARGET_VALUES_H_
+#define TOOLS_GN_SWIFT_TARGET_VALUES_H_
+
+#include <string>
+
+#include "gn/output_file.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/unique_vector.h"
+
+class Err;
+class Target;
+
+// Holds values specific to target that compile .swift files.
+class SwiftValues {
+ public:
+ SwiftValues();
+ ~SwiftValues();
+
+ SwiftValues(const SwiftValues&) = delete;
+ SwiftValues& operator=(const SwiftValues&) = delete;
+
+ // Called when the target is resolved.
+ bool OnTargetResolved(const Target* target, Err* err);
+
+ // Path of the bridging header.
+ SourceFile& bridge_header() { return bridge_header_; }
+ const SourceFile& bridge_header() const { return bridge_header_; }
+
+ // Name of the module.
+ std::string& module_name() { return module_name_; }
+ const std::string module_name() const { return module_name_; }
+
+ // Returns whether the target generates a .swiftmodule.
+ bool builds_module() const { return module_output_file_ != OutputFile(); }
+
+ // Name of the generated .swiftmodule file. Computed when the target
+ // is resolved.
+ const OutputFile& module_output_file() const { return module_output_file_; }
+
+ // Path of the directory containing the generated .swiftmodule file.
+ // Computed when the target is resolved.
+ const SourceDir& module_output_dir() const { return module_output_dir_; }
+
+ // List of dependent target that generate a .swiftmodule. The current target
+ // is assumed to depend on those modules, and will add them to the module
+ // search path.
+ const UniqueVector<const Target*>& modules() const { return modules_; }
+
+ // List of dependent target that generate a .swiftmodule that are publicly
+ // exported by the current target. This will include the current target if
+ // it generates a .swiftmodule.
+ const UniqueVector<const Target*>& public_modules() const {
+ return public_modules_;
+ }
+
+ private:
+ // Fill informations about .swiftmodule generated by this target.
+ bool FillModuleOuputFile(const Target* target, Err* err);
+
+ // Fill dependencies information on other target generating .swiftmodules.
+ void FillModuleDependencies(const Target* target);
+
+ // Name of the optional bridge header used to import Objective-C classes.
+ // Filled from the target, may be empty even if the target include .swift
+ // source files.
+ SourceFile bridge_header_;
+
+ // Name of the generate module for use by substitution.
+ std::string module_name_;
+
+ // Path to the .swiftmodule generated by this target. Will be empty if the
+ // target does not include .swift sources.
+ OutputFile module_output_file_;
+
+ // Path of the directory containing the .swiftmodule generated by this
+ // target. Will be null if the target does not include .swift sources.
+ SourceDir module_output_dir_;
+
+ // For modules() and public_modules() function. Will be filled when the
+ // target is resolved (can be non-empty even if the target does not build
+ // .swift sources due to transitive dependencies).
+ UniqueVector<const Target*> modules_;
+ UniqueVector<const Target*> public_modules_;
+};
+
+#endif // TOOLS_GN_SWIFT_TARGET_VALUES_H_
diff --git a/gn/src/gn/swift_values_generator.cc b/gn/src/gn/swift_values_generator.cc
new file mode 100644
index 00000000000..ba56e287167
--- /dev/null
+++ b/gn/src/gn/swift_values_generator.cc
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium 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 "gn/swift_values_generator.h"
+
+#include "gn/label.h"
+#include "gn/scope.h"
+#include "gn/settings.h"
+#include "gn/swift_values.h"
+#include "gn/swift_variables.h"
+#include "gn/target.h"
+#include "gn/value_extractors.h"
+
+SwiftValuesGenerator::SwiftValuesGenerator(Target* target,
+ Scope* scope,
+ Err* err)
+ : target_(target), scope_(scope), err_(err) {}
+
+SwiftValuesGenerator::~SwiftValuesGenerator() = default;
+
+void SwiftValuesGenerator::Run() {
+ if (!FillBridgeHeader())
+ return;
+
+ if (!FillModuleName())
+ return;
+}
+
+bool SwiftValuesGenerator::FillBridgeHeader() {
+ const Value* value = scope_->GetValue(variables::kSwiftBridgeHeader, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ SourceFile dest;
+ if (!ExtractRelativeFile(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &dest, err_))
+ return false;
+
+ target_->swift_values().bridge_header() = std::move(dest);
+ return true;
+}
+
+bool SwiftValuesGenerator::FillModuleName() {
+ const Value* value = scope_->GetValue(variables::kSwiftModuleName, true);
+ if (!value) {
+ // The target name will be used.
+ target_->swift_values().module_name() = target_->label().name();
+ return true;
+ }
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ target_->swift_values().module_name() = std::move(value->string_value());
+ return true;
+}
diff --git a/gn/src/gn/swift_values_generator.h b/gn/src/gn/swift_values_generator.h
new file mode 100644
index 00000000000..211c0592486
--- /dev/null
+++ b/gn/src/gn/swift_values_generator.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_SWIFT_TARGET_VALUES_GENERATOR_H_
+#define TOOLS_GN_SWIFT_TARGET_VALUES_GENERATOR_H_
+
+#include <string>
+
+class Err;
+class FunctionCallNode;
+class Scope;
+class Target;
+
+class SwiftValuesGenerator {
+ public:
+ SwiftValuesGenerator(Target* target, Scope* scope, Err* err);
+ ~SwiftValuesGenerator();
+
+ SwiftValuesGenerator(const SwiftValuesGenerator&) = delete;
+ SwiftValuesGenerator& operator=(const SwiftValuesGenerator&) = delete;
+
+ void Run();
+
+ private:
+ bool FillBridgeHeader();
+ bool FillModuleName();
+
+ Target* target_;
+ Scope* scope_;
+ Err* err_;
+};
+
+#endif // TOOLS_GN_SWIFT_TARGET_VALUES_GENERATOR_H_
diff --git a/gn/src/gn/swift_variables.cc b/gn/src/gn/swift_variables.cc
new file mode 100644
index 00000000000..cf9d3d48aaf
--- /dev/null
+++ b/gn/src/gn/swift_variables.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium 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 "gn/swift_variables.h"
+
+namespace variables {
+
+// Swift target vars -----------------------------------------------------
+
+const char kSwiftBridgeHeader[] = "bridge_header";
+const char kSwiftBridgeHeader_HelpShort[] =
+ "bridge_header: [string] Path to C/Objective-C compatibility header.";
+const char kSwiftBridgeHeader_Help[] =
+ R"(bridge_header: [string] Path to C/Objective-C compatibility header.
+
+ Valid for binary targets that contain Swift sources.
+
+ Path to an header that includes C/Objective-C functions and types that
+ needs to be made available to the Swift module.
+)";
+
+const char kSwiftModuleName[] = "module_name";
+const char kSwiftModuleName_HelpShort[] =
+ "module_name: [string] The name for the compiled module.";
+const char kSwiftModuleName_Help[] =
+ R"(module_name: [string] The name for the compiled module.
+
+ Valid for binary targets that contain Swift sources.
+
+ If module_name is not set, then this rule will use the target name.
+)";
+
+void InsertSwiftVariables(VariableInfoMap* info_map) {
+ info_map->insert(std::make_pair(
+ kSwiftBridgeHeader,
+ VariableInfo(kSwiftBridgeHeader_HelpShort, kSwiftBridgeHeader_Help)));
+
+ info_map->insert(std::make_pair(
+ kSwiftModuleName,
+ VariableInfo(kSwiftModuleName_HelpShort, kSwiftModuleName_Help)));
+}
+
+} // namespace variables
diff --git a/gn/src/gn/swift_variables.h b/gn/src/gn/swift_variables.h
new file mode 100644
index 00000000000..c43046872d0
--- /dev/null
+++ b/gn/src/gn/swift_variables.h
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_SWIFT_TARGET_VARIABLES_H_
+#define TOOLS_GN_SWIFT_TARGET_VARIABLES_H_
+
+#include "gn/variables.h"
+
+namespace variables {
+
+// Swift target vars -----------------------------------------------------
+
+extern const char kSwiftBridgeHeader[];
+extern const char kSwiftBridgeHeader_HelpShort[];
+extern const char kSwiftBridgeHeader_Help[];
+
+extern const char kSwiftModuleName[];
+extern const char kSwiftModuleName_HelpShort[];
+extern const char kSwiftModuleName_Help[];
+
+void InsertSwiftVariables(VariableInfoMap* info_map);
+
+} // namespace variables
+
+#endif // TOOLS_GN_SWIFT_TARGET_VARIABLES_H_
diff --git a/gn/src/gn/switches.cc b/gn/src/gn/switches.cc
new file mode 100644
index 00000000000..fb63b308ec1
--- /dev/null
+++ b/gn/src/gn/switches.cc
@@ -0,0 +1,356 @@
+// 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 "gn/switches.h"
+
+namespace switches {
+
+const char kArgs[] = "args";
+const char kArgs_HelpShort[] = "--args: Specifies build arguments overrides.";
+const char kArgs_Help[] =
+ R"(--args: Specifies build arguments overrides.
+
+ See "gn help buildargs" for an overview of how build arguments work.
+
+ Most operations take a build directory. The build arguments are taken from
+ the previous build done in that directory. If a command specifies --args, it
+ will override the previous arguments stored in the build directory, and use
+ the specified ones.
+
+ The args specified will be saved to the build directory for subsequent
+ commands. Specifying --args="" will clear all build arguments.
+
+Formatting
+
+ The value of the switch is interpreted in GN syntax. For typical usage of
+ string arguments, you will need to be careful about escaping of quotes.
+
+Examples
+
+ gn gen out/Default --args="foo=\"bar\""
+
+ gn gen out/Default --args='foo="bar" enable=true blah=7'
+
+ gn check out/Default --args=""
+ Clears existing build args from the directory.
+
+ gn desc out/Default --args="some_list=[1, false, \"foo\"]"
+)";
+
+#define COLOR_HELP_LONG \
+ "--[no]color: Forces colored output on or off.\n" \
+ "\n" \
+ " Normally GN will try to detect whether it is outputting to a terminal\n" \
+ " and will enable or disable color accordingly. Use of these switches\n" \
+ " will override the default.\n" \
+ "\n" \
+ "Examples\n" \
+ "\n" \
+ " gn gen out/Default --color\n" \
+ "\n" \
+ " gn gen out/Default --nocolor\n"
+const char kColor[] = "color";
+const char kColor_HelpShort[] = "--color: Force colored output.";
+const char kColor_Help[] = COLOR_HELP_LONG;
+
+const char kDotfile[] = "dotfile";
+const char kDotfile_HelpShort[] =
+ "--dotfile: Override the name of the \".gn\" file.";
+const char kDotfile_Help[] =
+ R"(--dotfile: Override the name of the ".gn" file.
+
+ Normally GN loads the ".gn" file from the source root for some basic
+ configuration (see "gn help dotfile"). This flag allows you to
+ use a different file.
+)";
+
+const char kFailOnUnusedArgs[] = "fail-on-unused-args";
+const char kFailOnUnusedArgs_HelpShort[] =
+ "--fail-on-unused-args: Treat unused build args as fatal errors.";
+const char kFailOnUnusedArgs_Help[] =
+ R"(--fail-on-unused-args: Treat unused build args as fatal errors.
+
+ If you set a value in a build's "gn args" and never use it in the build (in
+ a declare_args() block), GN will normally print an error but not fail the
+ build.
+
+ In many cases engineers would use build args to enable or disable features
+ that would sometimes get removed. It would by annoying to block work for
+ typically benign problems. In Chrome in particular, flags might be configured
+ for build bots in a separate infrastructure repository, or a declare_args
+ block might be changed in a third party repository. Treating these errors as
+ blocking forced complex multi- way patches to land what would otherwise be
+ simple changes.
+
+ In some cases, such concerns are not as important, and a mismatch in build
+ flags between the invoker of the build and the build files represents a
+ critical mismatch that should be immediately fixed. Such users can set this
+ flag to force GN to fail in that case.
+)";
+
+const char kMarkdown[] = "markdown";
+const char kMarkdown_HelpShort[] =
+ "--markdown: Write help output in the Markdown format.";
+const char kMarkdown_Help[] =
+ "--markdown: Write help output in the Markdown format.\n";
+
+const char kNoColor[] = "nocolor";
+const char kNoColor_HelpShort[] = "--nocolor: Force non-colored output.";
+const char kNoColor_Help[] = COLOR_HELP_LONG;
+
+const char kNinjaExecutable[] = "ninja-executable";
+const char kNinjaExecutable_HelpShort[] =
+ "--ninja-executable: Set the Ninja executable.";
+const char kNinjaExecutable_Help[] =
+ R"(--ninja-executable: Set the Ninja executable.
+
+ When set specifies the Ninja executable that will be used to perform some
+ post-processing on the generated files for more consistent builds.
+)";
+
+const char kScriptExecutable[] = "script-executable";
+const char kScriptExecutable_HelpShort[] =
+ "--script-executable: Set the executable used to execute scripts.";
+const char kScriptExecutable_Help[] =
+ R"(--script-executable: Set the executable used to execute scripts.
+
+ Path to specific Python executable or other interpreter to use in
+ action targets and exec_script calls. By default GN searches the
+ PATH for Python to execute these scripts.
+
+ If set to the empty string, the path specified in action targets
+ and exec_script calls will be executed directly.
+)";
+
+const char kMetaDataKeys[] = "data";
+const char kMetaDataKeys_HelpShort[] =
+ "--data: list of data keys to concatenate when collecting metadata.";
+const char kMetaDataKeys_Help[] =
+ R"(--data: list of data keys to concatenate when collecting metadata.
+
+ Data keys identify which variables in the given targets' `metadata`
+ scopes should be collected. At least one data key must be specified.
+)";
+
+const char kMetaWalkKeys[] = "walk";
+const char kMetaWalkKeys_HelpShort[] =
+ "--walk: list of walk keys to traverse when collecting metadata.";
+const char kMetaWalkKeys_Help[] =
+ R"(--walk: list of walk keys to traverse when collecting metadata.
+
+ Walk keys identify which variables in the given targets' `metadata`
+ scopes contain the list of dependencies to walk next. Absence of any
+ walk keys indicates that all deps and data_deps should be walked.
+)";
+
+const char kMetaRebaseFiles[] = "rebase-files";
+const char kMetaRebaseFiles_HelpShort[] =
+ "--rebase-files (boolean): whether to rebase the paths of the collected "
+ "metadata.";
+const char kMetaRebaseFiles_Help[] =
+ R"(--rebase-files: whether to rebase the paths of the collected metadata.
+
+ This flag indicates whether or not to rebase the collected results onto their
+ declaring source directory path. Note that this requires the data key(s) to
+ contain only lists of strings, which will be interpreted as file names.
+)";
+
+const char kQuiet[] = "q";
+const char kQuiet_HelpShort[] =
+ "-q: Quiet mode. Don't print output on success.";
+const char kQuiet_Help[] =
+ R"(-q: Quiet mode. Don't print output on success.
+
+ This is useful when running as a part of another script.
+)";
+
+const char kRoot[] = "root";
+const char kRoot_HelpShort[] = "--root: Explicitly specify source root.";
+const char kRoot_Help[] =
+ R"(--root: Explicitly specify source root.
+
+ Normally GN will look up in the directory tree from the current directory to
+ find a ".gn" file. The source root directory specifies the meaning of "//"
+ beginning with paths, and the BUILD.gn file in that directory will be the
+ first thing loaded.
+
+ Specifying --root allows GN to do builds in a specific directory regardless
+ of the current directory.
+
+Examples
+
+ gn gen //out/Default --root=/home/baracko/src
+
+ gn desc //out/Default --root="C:\Users\BObama\My Documents\foo"
+)";
+
+const char kRootTarget[] = "root-target";
+const char kRootTarget_HelpShort[] =
+ "--root-target: Override the root target.";
+const char kRootTarget_Help[] =
+ R"(--root-target: Override the root target.
+
+ The root target is the target initially loaded to begin population of the
+ build graph. It defaults to "//:" which normally causes the "//BUILD.gn" file
+ to be loaded. It can be specified in the .gn file via the "root" variable (see
+ "gn help dotfile").
+
+ If specified, the value of this switch will be take precedence over the value
+ in ".gn". The target name (after the colon) is ignored, only the directory
+ name is required. Relative paths will be resolved relative to the current "//"
+ directory.
+
+ Specifying a different initial BUILD.gn file does not change the meaning of
+ the source root (the "//" directory) which can be independently set via the
+ --root switch. It also does not prevent the build file located at "//BUILD.gn"
+ from being loaded if a target in the build references that directory.
+
+ One use-case of this feature is to load a different set of initial targets
+ from project that uses GN without modifying any files.
+
+Examples
+
+ gn gen //out/Default --root-target="//third_party/icu"
+
+ gn gen //out/Default --root-target="//third_party/grpc"
+)";
+
+const char kRuntimeDepsListFile[] = "runtime-deps-list-file";
+const char kRuntimeDepsListFile_HelpShort[] =
+ "--runtime-deps-list-file: Save runtime dependencies for targets in file.";
+const char kRuntimeDepsListFile_Help[] =
+ R"(--runtime-deps-list-file: Save runtime dependencies for targets in file.
+
+ --runtime-deps-list-file=<filename>
+
+ Where <filename> is a text file consisting of the labels, one per line, of
+ the targets for which runtime dependencies are desired.
+
+ See "gn help runtime_deps" for a description of how runtime dependencies are
+ computed.
+
+Runtime deps output file
+
+ For each target requested, GN will write a separate runtime dependency file.
+ The runtime dependency file will be in the output directory alongside the
+ output file of the target, with a ".runtime_deps" extension. For example, if
+ the target "//foo:bar" is listed in the input file, and that target produces
+ an output file "bar.so", GN will create a file "bar.so.runtime_deps" in the
+ build directory.
+
+ If a source set, action, copy, or group is listed, the runtime deps file will
+ correspond to the .stamp file corresponding to that target. This is probably
+ not useful; the use-case for this feature is generally executable targets.
+
+ The runtime dependency file will list one file per line, with no escaping.
+ The files will be relative to the root_build_dir. The first line of the file
+ will be the main output file of the target itself (in the above example,
+ "bar.so").
+)";
+
+const char kThreads[] = "threads";
+const char kThreads_HelpShort[] =
+ "--threads: Specify number of worker threads.";
+const char kThreads_Help[] =
+ R"(--threads: Specify number of worker threads.
+
+ GN runs many threads to load and run build files. This can make debugging
+ challenging. Or you may want to experiment with different values to see how
+ it affects performance.
+
+ The parameter is the number of worker threads. This does not count the main
+ thread (so there are always at least two).
+
+Examples
+
+ gen gen out/Default --threads=1
+)";
+
+const char kTime[] = "time";
+const char kTime_HelpShort[] =
+ "--time: Outputs a summary of how long everything took.";
+const char kTime_Help[] =
+ R"(--time: Outputs a summary of how long everything took.
+
+ Hopefully self-explanatory.
+
+Examples
+
+ gn gen out/Default --time
+)";
+
+const char kTracelog[] = "tracelog";
+const char kTracelog_HelpShort[] =
+ "--tracelog: Writes a Chrome-compatible trace log to the given file.";
+const char kTracelog_Help[] =
+ R"(--tracelog: Writes a Chrome-compatible trace log to the given file.
+
+ The trace log will show file loads, executions, scripts, and writes. This
+ allows performance analysis of the generation step.
+
+ To view the trace, open Chrome and navigate to "chrome://tracing/", then
+ press "Load" and specify the file you passed to this parameter.
+
+Examples
+
+ gn gen out/Default --tracelog=mytrace.trace
+)";
+
+const char kVerbose[] = "v";
+const char kVerbose_HelpShort[] = "-v: Verbose logging.";
+const char kVerbose_Help[] =
+ R"(-v: Verbose logging.
+
+ This will spew logging events to the console for debugging issues.
+
+ Good luck!
+)";
+
+const char kVersion[] = "version";
+const char kVersion_HelpShort[] =
+ "--version: Prints the GN version number and exits.";
+// It's impossible to see this since gn_main prints the version and exits
+// immediately if this switch is used.
+const char kVersion_Help[] = "";
+
+const char kDefaultToolchain[] = "default-toolchain";
+
+const char kRegeneration[] = "regeneration";
+// -----------------------------------------------------------------------------
+
+SwitchInfo::SwitchInfo() : short_help(""), long_help("") {}
+
+SwitchInfo::SwitchInfo(const char* short_help, const char* long_help)
+ : short_help(short_help), long_help(long_help) {}
+
+#define INSERT_VARIABLE(var) \
+ info_map[k##var] = SwitchInfo(k##var##_HelpShort, k##var##_Help);
+
+const SwitchInfoMap& GetSwitches() {
+ static SwitchInfoMap info_map;
+ if (info_map.empty()) {
+ INSERT_VARIABLE(Args)
+ INSERT_VARIABLE(Color)
+ INSERT_VARIABLE(Dotfile)
+ INSERT_VARIABLE(FailOnUnusedArgs)
+ INSERT_VARIABLE(Markdown)
+ INSERT_VARIABLE(NinjaExecutable)
+ INSERT_VARIABLE(NoColor)
+ INSERT_VARIABLE(Root)
+ INSERT_VARIABLE(RootTarget)
+ INSERT_VARIABLE(Quiet)
+ INSERT_VARIABLE(RuntimeDepsListFile)
+ INSERT_VARIABLE(ScriptExecutable)
+ INSERT_VARIABLE(Threads)
+ INSERT_VARIABLE(Time)
+ INSERT_VARIABLE(Tracelog)
+ INSERT_VARIABLE(Verbose)
+ INSERT_VARIABLE(Version)
+ }
+ return info_map;
+}
+
+#undef INSERT_VARIABLE
+
+} // namespace switches
diff --git a/gn/src/gn/switches.h b/gn/src/gn/switches.h
new file mode 100644
index 00000000000..2382bb7aede
--- /dev/null
+++ b/gn/src/gn/switches.h
@@ -0,0 +1,131 @@
+// 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 TOOLS_GN_SWITCHES_H_
+#define TOOLS_GN_SWITCHES_H_
+
+#include <map>
+#include <string_view>
+
+namespace switches {
+
+struct SwitchInfo {
+ SwitchInfo();
+ SwitchInfo(const char* short_help, const char* long_help);
+
+ const char* short_help;
+ const char* long_help;
+};
+
+using SwitchInfoMap = std::map<std::string_view, SwitchInfo>;
+
+// Returns the mapping of all global switches.
+const SwitchInfoMap& GetSwitches();
+
+// This file contains global switches. If a command takes specific ones only
+// to that command, just put them in that command's .cc file.
+
+extern const char kArgs[];
+extern const char kArgs_HelpShort[];
+extern const char kArgs_Help[];
+
+extern const char kColor[];
+extern const char kColor_HelpShort[];
+extern const char kColor_Help[];
+
+extern const char kDotfile[];
+extern const char kDotfile_HelpShort[];
+extern const char kDotfile_Help[];
+
+extern const char kFailOnUnusedArgs[];
+extern const char kFailOnUnusedArgs_HelpShort[];
+extern const char kFailOnUnusedArgs_Help[];
+
+extern const char kMarkdown[];
+extern const char kMarkdown_HelpShort[];
+extern const char kMarkdown_Help[];
+
+extern const char kMetaDataKeys[];
+extern const char kMetaDataKeys_HelpShort[];
+extern const char kMetaDataKeys_Help[];
+
+extern const char kMetaWalkKeys[];
+extern const char kMetaWalkKeys_HelpShort[];
+extern const char kMetaWalkKeys_Help[];
+
+extern const char kMetaRebaseFiles[];
+extern const char kMetaRebaseFiles_HelpShort[];
+extern const char kMetaRebaseFiles_Help[];
+
+extern const char kNinjaExecutable[];
+extern const char kNinjaExecutable_HelpShort[];
+extern const char kNinjaExecutable_Help[];
+
+extern const char kNoColor[];
+extern const char kNoColor_HelpShort[];
+extern const char kNoColor_Help[];
+
+extern const char kScriptExecutable[];
+extern const char kScriptExecutable_HelpShort[];
+extern const char kScriptExecutable_Help[];
+
+extern const char kQuiet[];
+extern const char kQuiet_HelpShort[];
+extern const char kQuiet_Help[];
+
+extern const char kRoot[];
+extern const char kRoot_HelpShort[];
+extern const char kRoot_Help[];
+
+extern const char kRootTarget[];
+extern const char kRootTarget_HelpShort[];
+extern const char kRootTarget_Help[];
+
+extern const char kRuntimeDepsListFile[];
+extern const char kRuntimeDepsListFile_HelpShort[];
+extern const char kRuntimeDepsListFile_Help[];
+
+extern const char kThreads[];
+extern const char kThreads_HelpShort[];
+extern const char kThreads_Help[];
+
+extern const char kTime[];
+extern const char kTime_HelpShort[];
+extern const char kTime_Help[];
+
+extern const char kTracelog[];
+extern const char kTracelog_HelpShort[];
+extern const char kTracelog_Help[];
+
+extern const char kVerbose[];
+extern const char kVerbose_HelpShort[];
+extern const char kVerbose_Help[];
+
+extern const char kVersion[];
+extern const char kVersion_HelpShort[];
+extern const char kVersion_Help[];
+
+// This switch is used by several commands. It is here so it can be shared,
+// but it's documented in the individual commands it applies to rather than
+// globally.
+extern const char kDefaultToolchain[];
+#define DEFAULT_TOOLCHAIN_SWITCH_HELP \
+ " --default-toolchain\n" \
+ " Normally wildcard targets are matched in all toolchains. This\n" \
+ " switch makes wildcard labels with no explicit toolchain reference\n" \
+ " only match targets in the default toolchain.\n" \
+ "\n" \
+ " Non-wildcard inputs with no explicit toolchain specification will\n" \
+ " always match only a target in the default toolchain if one exists.\n"
+
+// This switch is used to signal to the gen command that it is being invoked on
+// a regeneration step. Ie, ninja has realized that build.ninja needs to be
+// generated again and has invoked gn gen. There is no help associated with it
+// because users should not be setting this switch. It is located in this file
+// so it can be shared between command_gen and ninja_build_writer.
+extern const char kRegeneration[];
+
+} // namespace switches
+
+#endif // TOOLS_GN_SWITCHES_H_
diff --git a/gn/src/gn/target.cc b/gn/src/gn/target.cc
new file mode 100644
index 00000000000..dd71abbcc06
--- /dev/null
+++ b/gn/src/gn/target.cc
@@ -0,0 +1,1170 @@
+// 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 "gn/target.h"
+
+#include <stddef.h>
+
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "gn/c_tool.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/scheduler.h"
+#include "gn/substitution_writer.h"
+#include "gn/tool.h"
+#include "gn/toolchain.h"
+#include "gn/trace.h"
+
+namespace {
+
+using ConfigSet = std::set<const Config*>;
+
+// Merges the public configs from the given target to the given config list.
+void MergePublicConfigsFrom(const Target* from_target,
+ UniqueVector<LabelConfigPair>* dest) {
+ const UniqueVector<LabelConfigPair>& pub = from_target->public_configs();
+ dest->Append(pub.begin(), pub.end());
+}
+
+// Like MergePublicConfigsFrom above except does the "all dependent" ones. This
+// additionally adds all configs to the all_dependent_configs_ of the dest
+// target given in *all_dest.
+void MergeAllDependentConfigsFrom(const Target* from_target,
+ UniqueVector<LabelConfigPair>* dest,
+ UniqueVector<LabelConfigPair>* all_dest) {
+ for (const auto& pair : from_target->all_dependent_configs()) {
+ all_dest->push_back(pair);
+ dest->push_back(pair);
+ }
+}
+
+Err MakeTestOnlyError(const Target* from, const Target* to) {
+ return Err(
+ from->defined_from(), "Test-only dependency not allowed.",
+ from->label().GetUserVisibleName(false) +
+ "\n"
+ "which is NOT marked testonly can't depend on\n" +
+ to->label().GetUserVisibleName(false) +
+ "\n"
+ "which is marked testonly. Only targets with \"testonly = true\"\n"
+ "can depend on other test-only targets.\n"
+ "\n"
+ "Either mark it test-only or don't do this dependency.");
+}
+
+// Set check_private_deps to true for the first invocation since a target
+// can see all of its dependencies. For recursive invocations this will be set
+// to false to follow only public dependency paths.
+//
+// Pass a pointer to an empty set for the first invocation. This will be used
+// to avoid duplicate checking.
+//
+// Checking of object files is optional because it is much slower. This allows
+// us to check targets for normal outputs, and then as a second pass check
+// object files (since we know it will be an error otherwise). This allows
+// us to avoid computing all object file names in the common case.
+bool EnsureFileIsGeneratedByDependency(const Target* target,
+ const OutputFile& file,
+ bool check_private_deps,
+ bool consider_object_files,
+ bool check_data_deps,
+ std::set<const Target*>* seen_targets) {
+ if (seen_targets->find(target) != seen_targets->end())
+ return false; // Already checked this one and it's not found.
+ seen_targets->insert(target);
+
+ // Assume that we have relatively few generated inputs so brute-force
+ // searching here is OK. If this becomes a bottleneck, consider storing
+ // computed_outputs as a hash set.
+ for (const OutputFile& cur : target->computed_outputs()) {
+ if (file == cur)
+ return true;
+ }
+
+ if (file == target->write_runtime_deps_output())
+ return true;
+
+ // Check binary target intermediate files if requested.
+ if (consider_object_files && target->IsBinary()) {
+ std::vector<OutputFile> source_outputs;
+ for (const SourceFile& source : target->sources()) {
+ const char* tool_name;
+ if (!target->GetOutputFilesForSource(source, &tool_name, &source_outputs))
+ continue;
+ if (base::ContainsValue(source_outputs, file))
+ return true;
+ }
+ }
+
+ if (check_data_deps) {
+ check_data_deps = false; // Consider only direct data_deps.
+ for (const auto& pair : target->data_deps()) {
+ if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
+ consider_object_files,
+ check_data_deps, seen_targets))
+ return true; // Found a path.
+ }
+ }
+
+ // Check all public dependencies (don't do data ones since those are
+ // runtime-only).
+ for (const auto& pair : target->public_deps()) {
+ if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
+ consider_object_files,
+ check_data_deps, seen_targets))
+ return true; // Found a path.
+ }
+
+ // Only check private deps if requested.
+ if (check_private_deps) {
+ for (const auto& pair : target->private_deps()) {
+ if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
+ consider_object_files,
+ check_data_deps, seen_targets))
+ return true; // Found a path.
+ }
+ if (target->output_type() == Target::CREATE_BUNDLE) {
+ for (auto* dep : target->bundle_data().bundle_deps()) {
+ if (EnsureFileIsGeneratedByDependency(dep, file, false,
+ consider_object_files,
+ check_data_deps, seen_targets))
+ return true; // Found a path.
+ }
+ }
+ }
+ return false;
+}
+
+// check_this indicates if the given target should be matched against the
+// patterns. It should be set to false for the first call since assert_no_deps
+// shouldn't match the target itself.
+//
+// visited should point to an empty set, this will be used to prevent
+// multiple visits.
+//
+// *failure_path_str will be filled with a string describing the path of the
+// dependency failure, and failure_pattern will indicate the pattern in
+// assert_no that matched the target.
+//
+// Returns true if everything is OK. failure_path_str and failure_pattern_index
+// will be unchanged in this case.
+bool RecursiveCheckAssertNoDeps(const Target* target,
+ bool check_this,
+ const std::vector<LabelPattern>& assert_no,
+ std::set<const Target*>* visited,
+ std::string* failure_path_str,
+ const LabelPattern** failure_pattern) {
+ static const char kIndentPath[] = " ";
+
+ if (visited->find(target) != visited->end())
+ return true; // Already checked this target.
+ visited->insert(target);
+
+ if (check_this) {
+ // Check this target against the given list of patterns.
+ for (const LabelPattern& pattern : assert_no) {
+ if (pattern.Matches(target->label())) {
+ // Found a match.
+ *failure_pattern = &pattern;
+ *failure_path_str =
+ kIndentPath + target->label().GetUserVisibleName(false);
+ return false;
+ }
+ }
+ }
+
+ // Recursively check dependencies.
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
+ if (pair.ptr->output_type() == Target::EXECUTABLE)
+ continue;
+ if (!RecursiveCheckAssertNoDeps(pair.ptr, true, assert_no, visited,
+ failure_path_str, failure_pattern)) {
+ // To reconstruct the path, prepend the current target to the error.
+ std::string prepend_path =
+ kIndentPath + target->label().GetUserVisibleName(false) + " ->\n";
+ failure_path_str->insert(0, prepend_path);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+const char kExecution_Help[] =
+ R"(Build graph and execution overview
+
+Overall build flow
+
+ 1. Look for ".gn" file (see "gn help dotfile") in the current directory and
+ walk up the directory tree until one is found. Set this directory to be
+ the "source root" and interpret this file to find the name of the build
+ config file.
+
+ 2. Execute the build config file identified by .gn to set up the global
+ variables and default toolchain name. Any arguments, variables, defaults,
+ etc. set up in this file will be visible to all files in the build.
+
+ 3. Load the //BUILD.gn (in the source root directory).
+
+ 4. Recursively evaluate rules and load BUILD.gn in other directories as
+ necessary to resolve dependencies. If a BUILD file isn't found in the
+ specified location, GN will look in the corresponding location inside
+ the secondary_source defined in the dotfile (see "gn help dotfile").
+
+ 5. When a target's dependencies are resolved, write out the `.ninja`
+ file to disk.
+
+ 6. When all targets are resolved, write out the root build.ninja file.
+
+ Note that the BUILD.gn file name may be modulated by .gn arguments such as
+ build_file_extension.
+
+Executing target definitions and templates
+
+ Build files are loaded in parallel. This means it is impossible to
+ interrogate a target from GN code for any information not derivable from its
+ label (see "gn help label"). The exception is the get_target_outputs()
+ function which requires the target being interrogated to have been defined
+ previously in the same file.
+
+ Targets are declared by their type and given a name:
+
+ static_library("my_static_library") {
+ ... target parameter definitions ...
+ }
+
+ There is also a generic "target" function for programmatically defined types
+ (see "gn help target"). You can define new types using templates (see "gn
+ help template"). A template defines some custom code that expands to one or
+ more other targets.
+
+ Before executing the code inside the target's { }, the target defaults are
+ applied (see "gn help set_defaults"). It will inject implicit variable
+ definitions that can be overridden by the target code as necessary. Typically
+ this mechanism is used to inject a default set of configs that define the
+ global compiler and linker flags.
+
+Which targets are built
+
+ All targets encountered in the default toolchain (see "gn help toolchain")
+ will have build rules generated for them, even if no other targets reference
+ them. Their dependencies must resolve and they will be added to the implicit
+ "all" rule (see "gn help ninja_rules").
+
+ Targets in non-default toolchains will only be generated when they are
+ required (directly or transitively) to build a target in the default
+ toolchain.
+
+ See also "gn help ninja_rules".
+
+Dependencies
+
+ The only difference between "public_deps" and "deps" except for pushing
+ configs around the build tree and allowing includes for the purposes of "gn
+ check".
+
+ A target's "data_deps" are guaranteed to be built whenever the target is
+ built, but the ordering is not defined. The meaning of this is dependencies
+ required at runtime. Currently data deps will be complete before the target
+ is linked, but this is not semantically guaranteed and this is undesirable
+ from a build performance perspective. Since we hope to change this in the
+ future, do not rely on this behavior.
+)";
+
+Target::Target(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files)
+ : Item(settings, label, build_dependency_files) {}
+
+Target::~Target() = default;
+
+// static
+const char* Target::GetStringForOutputType(OutputType type) {
+ switch (type) {
+ case UNKNOWN:
+ return "unknown";
+ case GROUP:
+ return functions::kGroup;
+ case EXECUTABLE:
+ return functions::kExecutable;
+ case LOADABLE_MODULE:
+ return functions::kLoadableModule;
+ case SHARED_LIBRARY:
+ return functions::kSharedLibrary;
+ case STATIC_LIBRARY:
+ return functions::kStaticLibrary;
+ case SOURCE_SET:
+ return functions::kSourceSet;
+ case COPY_FILES:
+ return functions::kCopy;
+ case ACTION:
+ return functions::kAction;
+ case ACTION_FOREACH:
+ return functions::kActionForEach;
+ case BUNDLE_DATA:
+ return functions::kBundleData;
+ case CREATE_BUNDLE:
+ return functions::kCreateBundle;
+ case GENERATED_FILE:
+ return functions::kGeneratedFile;
+ case RUST_LIBRARY:
+ return functions::kRustLibrary;
+ case RUST_PROC_MACRO:
+ return functions::kRustProcMacro;
+ default:
+ return "";
+ }
+}
+
+Target* Target::AsTarget() {
+ return this;
+}
+
+const Target* Target::AsTarget() const {
+ return this;
+}
+
+bool Target::OnResolved(Err* err) {
+ DCHECK(output_type_ != UNKNOWN);
+ DCHECK(toolchain_) << "Toolchain should have been set before resolving.";
+
+ ScopedTrace trace(TraceItem::TRACE_ON_RESOLVED, label());
+ trace.SetToolchain(settings()->toolchain_label());
+
+ // Check visibility for just this target's own configs, before dependents are
+ // added.
+ if (!CheckConfigVisibility(err))
+ return false;
+
+ // Copy this target's own dependent and public configs to the list of configs
+ // applying to it.
+ configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
+ MergePublicConfigsFrom(this, &configs_);
+
+ // Copy public configs from all dependencies into the list of configs
+ // applying to this target (configs_).
+ PullDependentTargetConfigs();
+
+ // Copies public dependencies' public configs to this target's public
+ // configs. These configs have already been applied to this target by
+ // PullDependentTargetConfigs above, along with the public configs from
+ // private deps. This step re-exports them as public configs for targets that
+ // depend on this one.
+ for (const auto& dep : public_deps_) {
+ if (dep.ptr->toolchain() == toolchain() ||
+ dep.ptr->toolchain()->propagates_configs())
+ public_configs_.Append(dep.ptr->public_configs().begin(),
+ dep.ptr->public_configs().end());
+ }
+
+ // Copy our own libs and lib_dirs to the final set. This will be from our
+ // target and all of our configs. We do this specially since these must be
+ // inherited through the dependency tree (other flags don't work this way).
+ //
+ // This needs to happen after we pull dependent target configs for the
+ // public config's libs to be included here. And it needs to happen
+ // before pulling the dependent target libs so the libs are in the correct
+ // order (local ones first, then the dependency's).
+ for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
+ const ConfigValues& cur = iter.cur();
+ all_lib_dirs_.append(cur.lib_dirs().begin(), cur.lib_dirs().end());
+ all_libs_.append(cur.libs().begin(), cur.libs().end());
+
+ all_framework_dirs_.append(cur.framework_dirs().begin(),
+ cur.framework_dirs().end());
+ all_frameworks_.append(cur.frameworks().begin(), cur.frameworks().end());
+ all_weak_frameworks_.append(cur.weak_frameworks().begin(),
+ cur.weak_frameworks().end());
+ }
+
+ PullRecursiveBundleData();
+ PullDependentTargetLibs();
+ PullRecursiveHardDeps();
+ if (!ResolvePrecompiledHeaders(err))
+ return false;
+
+ if (!FillOutputFiles(err))
+ return false;
+
+ if (!swift_values_.OnTargetResolved(this, err))
+ return false;
+
+ if (!CheckSourceSetLanguages(err))
+ return false;
+ if (!CheckVisibility(err))
+ return false;
+ if (!CheckTestonly(err))
+ return false;
+ if (!CheckAssertNoDeps(err))
+ return false;
+ CheckSourcesGenerated();
+
+ if (!write_runtime_deps_output_.value().empty())
+ g_scheduler->AddWriteRuntimeDepsTarget(this);
+
+ if (output_type_ == GENERATED_FILE) {
+ DCHECK(!computed_outputs_.empty());
+ g_scheduler->AddGeneratedFile(
+ computed_outputs_[0].AsSourceFile(settings()->build_settings()));
+ }
+
+ return true;
+}
+
+bool Target::IsBinary() const {
+ return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
+ output_type_ == LOADABLE_MODULE || output_type_ == STATIC_LIBRARY ||
+ output_type_ == SOURCE_SET || output_type_ == RUST_LIBRARY ||
+ output_type_ == RUST_PROC_MACRO;
+}
+
+bool Target::IsLinkable() const {
+ return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY ||
+ output_type_ == RUST_LIBRARY || output_type_ == RUST_PROC_MACRO;
+}
+
+bool Target::IsFinal() const {
+ return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
+ output_type_ == LOADABLE_MODULE || output_type_ == ACTION ||
+ output_type_ == ACTION_FOREACH || output_type_ == COPY_FILES ||
+ output_type_ == CREATE_BUNDLE || output_type_ == RUST_PROC_MACRO ||
+ (output_type_ == STATIC_LIBRARY && complete_static_lib_);
+}
+
+bool Target::IsDataOnly() const {
+ // BUNDLE_DATA exists only to declare inputs to subsequent CREATE_BUNDLE
+ // targets. Changing only contents of the bundle data target should not cause
+ // a binary to be re-linked. It should affect only the CREATE_BUNDLE steps
+ // instead. As a result, normal targets should treat this as a data
+ // dependency.
+ return output_type_ == BUNDLE_DATA;
+}
+
+DepsIteratorRange Target::GetDeps(DepsIterationType type) const {
+ if (type == DEPS_LINKED) {
+ return DepsIteratorRange(
+ DepsIterator(&public_deps_, &private_deps_, nullptr));
+ }
+ // All deps.
+ return DepsIteratorRange(
+ DepsIterator(&public_deps_, &private_deps_, &data_deps_));
+}
+
+std::string Target::GetComputedOutputName() const {
+ DCHECK(toolchain_)
+ << "Toolchain must be specified before getting the computed output name.";
+
+ const std::string& name =
+ output_name_.empty() ? label().name() : output_name_;
+
+ std::string result;
+ const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
+ if (tool) {
+ // Only add the prefix if the name doesn't already have it and it's not
+ // being overridden.
+ if (!output_prefix_override_ &&
+ !base::StartsWith(name, tool->output_prefix(),
+ base::CompareCase::SENSITIVE))
+ result = tool->output_prefix();
+ }
+ result.append(name);
+ return result;
+}
+
+bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
+ DCHECK(!toolchain_);
+ DCHECK_NE(UNKNOWN, output_type_);
+ toolchain_ = toolchain;
+
+ const Tool* tool = toolchain->GetToolForTargetFinalOutput(this);
+ if (tool)
+ return true;
+
+ // Tool not specified for this target type.
+ if (err) {
+ *err =
+ Err(defined_from(), "This target uses an undefined tool.",
+ base::StringPrintf(
+ "The target %s\n"
+ "of type \"%s\"\n"
+ "uses toolchain %s\n"
+ "which doesn't have the tool \"%s\" defined.\n\n"
+ "Alas, I can not continue.",
+ label().GetUserVisibleName(false).c_str(),
+ GetStringForOutputType(output_type_),
+ label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
+ Tool::GetToolTypeForTargetFinalOutput(this)));
+ }
+ return false;
+}
+
+bool Target::GetOutputsAsSourceFiles(const LocationRange& loc_for_error,
+ bool build_complete,
+ std::vector<SourceFile>* outputs,
+ Err* err) const {
+ const static char kBuildIncompleteMsg[] =
+ "This target is a binary target which can't be queried for its "
+ "outputs\nduring the build. It will work for action, action_foreach, "
+ "generated_file,\nand copy targets.";
+
+ outputs->clear();
+
+ std::vector<SourceFile> files;
+ if (output_type() == Target::ACTION || output_type() == Target::COPY_FILES ||
+ output_type() == Target::ACTION_FOREACH ||
+ output_type() == Target::GENERATED_FILE) {
+ action_values().GetOutputsAsSourceFiles(this, outputs);
+ } else if (output_type() == Target::CREATE_BUNDLE) {
+ if (!bundle_data().GetOutputsAsSourceFiles(settings(), this, outputs, err))
+ return false;
+ } else if (IsBinary() && output_type() != Target::SOURCE_SET) {
+ // Binary target with normal outputs (source sets have stamp outputs like
+ // groups).
+ DCHECK(IsBinary()) << static_cast<int>(output_type());
+ if (!build_complete) {
+ // Can't access the toolchain for a target before the build is complete.
+ // Otherwise it will race with loading and setting the toolchain
+ // definition.
+ *err = Err(loc_for_error, kBuildIncompleteMsg);
+ return false;
+ }
+
+ const Tool* tool = toolchain()->GetToolForTargetFinalOutput(this);
+
+ std::vector<OutputFile> output_files;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ this, tool, tool->outputs(), &output_files);
+ for (const OutputFile& output_file : output_files) {
+ outputs->push_back(
+ output_file.AsSourceFile(settings()->build_settings()));
+ }
+ } else {
+ // Everything else (like a group or bundle_data) has a stamp output. The
+ // dependency output file should have computed what this is. This won't be
+ // valid unless the build is complete.
+ if (!build_complete) {
+ *err = Err(loc_for_error, kBuildIncompleteMsg);
+ return false;
+ }
+ outputs->push_back(
+ dependency_output_file().AsSourceFile(settings()->build_settings()));
+ }
+ return true;
+}
+
+bool Target::GetOutputFilesForSource(const SourceFile& source,
+ const char** computed_tool_type,
+ std::vector<OutputFile>* outputs) const {
+ DCHECK(toolchain()); // Should be resolved before calling.
+
+ outputs->clear();
+ *computed_tool_type = Tool::kToolNone;
+
+ if (output_type() == Target::COPY_FILES ||
+ output_type() == Target::ACTION_FOREACH) {
+ // These target types apply the output pattern to the input.
+ std::vector<SourceFile> output_files;
+ SubstitutionWriter::ApplyListToSourceAsOutputFile(
+ this, settings(), action_values().outputs(), source, outputs);
+ } else if (!IsBinary()) {
+ // All other non-binary target types just return the target outputs. We
+ // don't know if the build is complete and it doesn't matter for non-binary
+ // targets, so just assume it's not and pass "false".
+ std::vector<SourceFile> outputs_as_source_files;
+ Err err; // We can ignore the error and return empty for failure.
+ GetOutputsAsSourceFiles(LocationRange(), false, &outputs_as_source_files,
+ &err);
+
+ // Convert to output files.
+ for (const auto& cur : outputs_as_source_files)
+ outputs->emplace_back(OutputFile(settings()->build_settings(), cur));
+ } else {
+ // All binary targets do a tool lookup.
+ DCHECK(IsBinary());
+
+ SourceFile::Type file_type = source.type();
+ if (file_type == SourceFile::SOURCE_UNKNOWN)
+ return false;
+ if (file_type == SourceFile::SOURCE_O) {
+ // Object files just get passed to the output and not compiled.
+ outputs->emplace_back(OutputFile(settings()->build_settings(), source));
+ return true;
+ }
+
+ // Rust generates on a module level, not source.
+ if (file_type == SourceFile::SOURCE_RS)
+ return false;
+
+ *computed_tool_type = Tool::GetToolTypeForSourceType(file_type);
+ if (*computed_tool_type == Tool::kToolNone)
+ return false; // No tool for this file (it's a header file or something).
+ const Tool* tool = toolchain_->GetTool(*computed_tool_type);
+ if (!tool)
+ return false; // Tool does not apply for this toolchain.file.
+
+ // Swift may generate on a module or source level.
+ if (file_type == SourceFile::SOURCE_SWIFT) {
+ if (tool->partial_outputs().list().empty())
+ return false;
+ }
+
+ const SubstitutionList& substitution_list =
+ file_type == SourceFile::SOURCE_SWIFT ? tool->partial_outputs()
+ : tool->outputs();
+
+ // Figure out what output(s) this compiler produces.
+ SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+ this, source, substitution_list, outputs);
+ }
+ return !outputs->empty();
+}
+
+void Target::PullDependentTargetConfigs() {
+ for (const auto& pair : GetDeps(DEPS_LINKED)) {
+ if (pair.ptr->toolchain() == toolchain() ||
+ pair.ptr->toolchain()->propagates_configs())
+ MergeAllDependentConfigsFrom(pair.ptr, &configs_,
+ &all_dependent_configs_);
+ }
+ for (const auto& pair : GetDeps(DEPS_LINKED)) {
+ if (pair.ptr->toolchain() == toolchain() ||
+ pair.ptr->toolchain()->propagates_configs())
+ MergePublicConfigsFrom(pair.ptr, &configs_);
+ }
+}
+
+void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) {
+ // Direct dependent libraries.
+ if (dep->output_type() == STATIC_LIBRARY ||
+ dep->output_type() == SHARED_LIBRARY) {
+ inherited_libraries_.Append(dep, is_public);
+ rust_values().transitive_libs().Append(dep, is_public);
+ }
+
+ if (dep->output_type() == RUST_LIBRARY ||
+ dep->output_type() == RUST_PROC_MACRO ||
+ dep->output_type() == SOURCE_SET ||
+ (dep->output_type() == CREATE_BUNDLE &&
+ dep->bundle_data().is_framework())) {
+ inherited_libraries_.Append(dep, is_public);
+ }
+
+ if (dep->output_type() == RUST_LIBRARY) {
+ rust_values().transitive_libs().Append(dep, is_public);
+ rust_values().transitive_libs().AppendInherited(
+ dep->rust_values().transitive_libs(), is_public);
+
+ // If there is a transitive dependency that is not a rust library, place it
+ // in the normal location
+ for (const auto& inherited :
+ rust_values().transitive_libs().GetOrderedAndPublicFlag()) {
+ if (!(inherited.first->output_type() == RUST_LIBRARY ||
+ inherited.first->output_type() == RUST_PROC_MACRO)) {
+ inherited_libraries_.Append(inherited.first,
+ is_public && inherited.second);
+ }
+ }
+ } else if (dep->output_type() == RUST_PROC_MACRO) {
+ // We will need to specify the path to find a procedural macro,
+ // but have no need to specify the paths to find its dependencies
+ // as the procedural macro is now a complete .so.
+ rust_values().transitive_libs().Append(dep, is_public);
+ } else if (dep->output_type() == SHARED_LIBRARY) {
+ // Shared library dependendencies are inherited across public shared
+ // library boundaries.
+ //
+ // In this case:
+ // EXE -> INTERMEDIATE_SHLIB --[public]--> FINAL_SHLIB
+ // The EXE will also link to to FINAL_SHLIB. The public dependeny means
+ // that the EXE can use the headers in FINAL_SHLIB so the FINAL_SHLIB
+ // will need to appear on EXE's link line.
+ //
+ // However, if the dependency is private:
+ // EXE -> INTERMEDIATE_SHLIB --[private]--> FINAL_SHLIB
+ // the dependency will not be propagated because INTERMEDIATE_SHLIB is
+ // not granting permission to call functiosn from FINAL_SHLIB. If EXE
+ // wants to use functions (and link to) FINAL_SHLIB, it will need to do
+ // so explicitly.
+ //
+ // Static libraries and source sets aren't inherited across shared
+ // library boundaries because they will be linked into the shared
+ // library.
+ inherited_libraries_.AppendPublicSharedLibraries(dep->inherited_libraries(),
+ is_public);
+ } else if (!dep->IsFinal()) {
+ // The current target isn't linked, so propogate linked deps and
+ // libraries up the dependency tree.
+ inherited_libraries_.AppendInherited(dep->inherited_libraries(), is_public);
+ rust_values().transitive_libs().AppendInherited(
+ dep->rust_values().transitive_libs(), is_public);
+ } else if (dep->complete_static_lib()) {
+ // Inherit only final targets through _complete_ static libraries.
+ //
+ // Inherited final libraries aren't linked into complete static libraries.
+ // They are forwarded here so that targets that depend on complete
+ // static libraries can link them in. Conversely, since complete static
+ // libraries link in non-final targets they shouldn't be inherited.
+ for (const auto& inherited :
+ dep->inherited_libraries().GetOrderedAndPublicFlag()) {
+ if (inherited.first->IsFinal()) {
+ inherited_libraries_.Append(inherited.first,
+ is_public && inherited.second);
+ }
+ }
+ }
+
+ // Library settings are always inherited across static library boundaries.
+ if (!dep->IsFinal() || dep->output_type() == STATIC_LIBRARY) {
+ all_lib_dirs_.append(dep->all_lib_dirs());
+ all_libs_.append(dep->all_libs());
+
+ all_framework_dirs_.append(dep->all_framework_dirs());
+ all_frameworks_.append(dep->all_frameworks());
+ all_weak_frameworks_.append(dep->all_weak_frameworks());
+ }
+}
+
+void Target::PullDependentTargetLibs() {
+ for (const auto& dep : public_deps_)
+ PullDependentTargetLibsFrom(dep.ptr, true);
+ for (const auto& dep : private_deps_)
+ PullDependentTargetLibsFrom(dep.ptr, false);
+}
+
+void Target::PullRecursiveHardDeps() {
+ for (const auto& pair : GetDeps(DEPS_LINKED)) {
+ // Direct hard dependencies.
+ if (hard_dep() || pair.ptr->hard_dep()) {
+ recursive_hard_deps_.insert(pair.ptr);
+ continue;
+ }
+
+ // If |pair.ptr| is binary target and |pair.ptr| has no public header,
+ // |this| target does not need to have |pair.ptr|'s hard_deps as its
+ // hard_deps to start compiles earlier. Unless the target compiles a
+ // Swift module (since they also generate a header that can be used
+ // by the current target).
+ if (pair.ptr->IsBinary() && !pair.ptr->all_headers_public() &&
+ pair.ptr->public_headers().empty() &&
+ !pair.ptr->swift_values().builds_module()) {
+ continue;
+ }
+
+ // Recursive hard dependencies of all dependencies.
+ recursive_hard_deps_.insert(pair.ptr->recursive_hard_deps().begin(),
+ pair.ptr->recursive_hard_deps().end());
+ }
+}
+
+void Target::PullRecursiveBundleData() {
+ for (const auto& pair : GetDeps(DEPS_LINKED)) {
+ // Don't propagate bundle_data once they are added to a bundle.
+ if (pair.ptr->output_type() == CREATE_BUNDLE)
+ continue;
+
+ // Don't propagate across toolchain.
+ if (pair.ptr->toolchain() != toolchain())
+ continue;
+
+ // Direct dependency on a bundle_data target.
+ if (pair.ptr->output_type() == BUNDLE_DATA)
+ bundle_data_.AddBundleData(pair.ptr);
+
+ // Recursive bundle_data informations from all dependencies.
+ for (auto* target : pair.ptr->bundle_data().bundle_deps())
+ bundle_data_.AddBundleData(target);
+ }
+
+ bundle_data_.OnTargetResolved(this);
+}
+
+bool Target::FillOutputFiles(Err* err) {
+ const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
+ bool check_tool_outputs = false;
+ switch (output_type_) {
+ case GROUP:
+ case BUNDLE_DATA:
+ case CREATE_BUNDLE:
+ case SOURCE_SET:
+ case COPY_FILES:
+ case ACTION:
+ case ACTION_FOREACH:
+ case GENERATED_FILE: {
+ // These don't get linked to and use stamps which should be the first
+ // entry in the outputs. These stamps are named
+ // "<target_out_dir>/<targetname>.stamp".
+ dependency_output_file_ =
+ GetBuildDirForTargetAsOutputFile(this, BuildDirType::OBJ);
+ dependency_output_file_.value().append(GetComputedOutputName());
+ dependency_output_file_.value().append(".stamp");
+ break;
+ }
+ case EXECUTABLE:
+ case LOADABLE_MODULE:
+ // Executables and loadable modules don't get linked to, but the first
+ // output is used for dependency management.
+ CHECK_GE(tool->outputs().list().size(), 1u);
+ check_tool_outputs = true;
+ dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->outputs().list()[0]);
+
+ if (tool->runtime_outputs().list().empty()) {
+ // Default to the first output for the runtime output.
+ runtime_outputs_.push_back(dependency_output_file_);
+ } else {
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ this, tool, tool->runtime_outputs(), &runtime_outputs_);
+ }
+ break;
+ case RUST_LIBRARY:
+ case STATIC_LIBRARY:
+ // Static libraries both have dependencies and linking going off of the
+ // first output.
+ CHECK(tool->outputs().list().size() >= 1);
+ check_tool_outputs = true;
+ link_output_file_ = dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->outputs().list()[0]);
+ break;
+ case RUST_PROC_MACRO:
+ case SHARED_LIBRARY:
+ CHECK(tool->outputs().list().size() >= 1);
+ check_tool_outputs = true;
+ if (const CTool* ctool = tool->AsC()) {
+ if (ctool->link_output().empty() && ctool->depend_output().empty()) {
+ // Default behavior, use the first output file for both.
+ link_output_file_ = dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->outputs().list()[0]);
+ } else {
+ // Use the tool-specified ones.
+ if (!ctool->link_output().empty()) {
+ link_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, ctool->link_output());
+ }
+ if (!ctool->depend_output().empty()) {
+ dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, ctool->depend_output());
+ }
+ }
+ if (tool->runtime_outputs().list().empty()) {
+ // Default to the link output for the runtime output.
+ runtime_outputs_.push_back(link_output_file_);
+ } else {
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ this, tool, tool->runtime_outputs(), &runtime_outputs_);
+ }
+ } else if (const RustTool* rstool = tool->AsRust()) {
+ // Default behavior, use the first output file for both.
+ link_output_file_ = dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->outputs().list()[0]);
+ }
+ break;
+ case UNKNOWN:
+ default:
+ NOTREACHED();
+ }
+
+ // Count anything generated from bundle_data dependencies.
+ if (output_type_ == CREATE_BUNDLE) {
+ if (!bundle_data_.GetOutputFiles(settings(), this, &computed_outputs_, err))
+ return false;
+ }
+
+ // Count all outputs from this tool as something generated by this target.
+ if (check_tool_outputs) {
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ this, tool, tool->outputs(), &computed_outputs_);
+
+ // Output names aren't canonicalized in the same way that source files
+ // are. For example, the tool outputs often use
+ // {{some_var}}/{{output_name}} which expands to "./foo", but this won't
+ // match "foo" which is what we'll compute when converting a SourceFile to
+ // an OutputFile.
+ for (auto& out : computed_outputs_)
+ NormalizePath(&out.value());
+ }
+
+ // Also count anything the target has declared to be an output.
+ std::vector<SourceFile> outputs_as_sources;
+ action_values_.GetOutputsAsSourceFiles(this, &outputs_as_sources);
+ for (const SourceFile& out : outputs_as_sources)
+ computed_outputs_.push_back(OutputFile(settings()->build_settings(), out));
+
+ return true;
+}
+
+bool Target::ResolvePrecompiledHeaders(Err* err) {
+ // Precompiled headers are stored on a ConfigValues struct. This way, the
+ // build can set all the precompiled header settings in a config and apply
+ // it to many targets. Likewise, the precompiled header values may be
+ // specified directly on a target.
+ //
+ // Unlike other values on configs which are lists that just get concatenated,
+ // the precompiled header settings are unique values. We allow them to be
+ // specified anywhere, but if they are specified in more than one place all
+ // places must match.
+
+ // Track where the current settings came from for issuing errors.
+ const Label* pch_header_settings_from = NULL;
+ if (config_values_.has_precompiled_headers())
+ pch_header_settings_from = &label();
+
+ for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
+ if (!iter.GetCurrentConfig())
+ continue; // Skip the one on the target itself.
+
+ const Config* config = iter.GetCurrentConfig();
+ const ConfigValues& cur = config->resolved_values();
+ if (!cur.has_precompiled_headers())
+ continue; // This one has no precompiled header info, skip.
+
+ if (config_values_.has_precompiled_headers()) {
+ // Already have a precompiled header values, the settings must match.
+ if (config_values_.precompiled_header() != cur.precompiled_header() ||
+ config_values_.precompiled_source() != cur.precompiled_source()) {
+ *err = Err(
+ defined_from(), "Precompiled header setting conflict.",
+ "The target " + label().GetUserVisibleName(false) +
+ "\n"
+ "has conflicting precompiled header settings.\n"
+ "\n"
+ "From " +
+ pch_header_settings_from->GetUserVisibleName(false) +
+ "\n header: " + config_values_.precompiled_header() +
+ "\n source: " + config_values_.precompiled_source().value() +
+ "\n\n"
+ "From " +
+ config->label().GetUserVisibleName(false) +
+ "\n header: " + cur.precompiled_header() +
+ "\n source: " + cur.precompiled_source().value());
+ return false;
+ }
+ } else {
+ // Have settings from a config, apply them to ourselves.
+ pch_header_settings_from = &config->label();
+ config_values_.set_precompiled_header(cur.precompiled_header());
+ config_values_.set_precompiled_source(cur.precompiled_source());
+ }
+ }
+
+ return true;
+}
+
+bool Target::CheckVisibility(Err* err) const {
+ for (const auto& pair : GetDeps(DEPS_ALL)) {
+ if (!Visibility::CheckItemVisibility(this, pair.ptr, err))
+ return false;
+ }
+ return true;
+}
+
+bool Target::CheckConfigVisibility(Err* err) const {
+ for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
+ if (const Config* config = iter.GetCurrentConfig())
+ if (!Visibility::CheckItemVisibility(this, config, err))
+ return false;
+ }
+ return true;
+}
+
+bool Target::CheckSourceSetLanguages(Err* err) const {
+ if (output_type() == Target::SOURCE_SET &&
+ source_types_used().RustSourceUsed()) {
+ *err = Err(defined_from(), "source_set contained Rust code.",
+ label().GetUserVisibleName(false) +
+ " has Rust code. Only C/C++ source_sets are supported.");
+ return false;
+ }
+ return true;
+}
+
+bool Target::CheckTestonly(Err* err) const {
+ // If the current target is marked testonly, it can include both testonly
+ // and non-testonly targets, so there's nothing to check.
+ if (testonly())
+ return true;
+
+ // Verify no deps have "testonly" set.
+ for (const auto& pair : GetDeps(DEPS_ALL)) {
+ if (pair.ptr->testonly()) {
+ *err = MakeTestOnlyError(this, pair.ptr);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Target::CheckAssertNoDeps(Err* err) const {
+ if (assert_no_deps_.empty())
+ return true;
+
+ std::set<const Target*> visited;
+ std::string failure_path_str;
+ const LabelPattern* failure_pattern = nullptr;
+
+ if (!RecursiveCheckAssertNoDeps(this, false, assert_no_deps_, &visited,
+ &failure_path_str, &failure_pattern)) {
+ *err = Err(
+ defined_from(), "assert_no_deps failed.",
+ label().GetUserVisibleName(false) +
+ " has an assert_no_deps entry:\n " + failure_pattern->Describe() +
+ "\nwhich fails for the dependency path:\n" + failure_path_str);
+ return false;
+ }
+ return true;
+}
+
+void Target::CheckSourcesGenerated() const {
+ // Checks that any inputs or sources to this target that are in the build
+ // directory are generated by a target that this one transitively depends on
+ // in some way. We already guarantee that all generated files are written
+ // to the build dir.
+ //
+ // See Scheduler::AddUnknownGeneratedInput's declaration for more.
+ for (const SourceFile& file : sources_)
+ CheckSourceGenerated(file);
+ for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
+ for (const SourceFile& file : iter.cur().inputs())
+ CheckSourceGenerated(file);
+ }
+ // TODO(agrieve): Check all_libs_ here as well (those that are source files).
+ // http://crbug.com/571731
+}
+
+void Target::CheckSourceGenerated(const SourceFile& source) const {
+ if (!IsStringInOutputDir(settings()->build_settings()->build_dir(),
+ source.value()))
+ return; // Not in output dir, this is OK.
+
+ // Tell the scheduler about unknown files. This will be noted for later so
+ // the list of files written by the GN build itself (often response files)
+ // can be filtered out of this list.
+ OutputFile out_file(settings()->build_settings(), source);
+ std::set<const Target*> seen_targets;
+ bool check_data_deps = false;
+ bool consider_object_files = false;
+ if (!EnsureFileIsGeneratedByDependency(this, out_file, true,
+ consider_object_files, check_data_deps,
+ &seen_targets)) {
+ seen_targets.clear();
+ // Allow dependency to be through data_deps for files generated by gn.
+ check_data_deps =
+ g_scheduler->IsFileGeneratedByWriteRuntimeDeps(out_file) ||
+ g_scheduler->IsFileGeneratedByTarget(source);
+ // Check object files (much slower and very rare) only if the "normal"
+ // output check failed.
+ consider_object_files = !check_data_deps;
+ if (!EnsureFileIsGeneratedByDependency(this, out_file, true,
+ consider_object_files,
+ check_data_deps, &seen_targets))
+ g_scheduler->AddUnknownGeneratedInput(this, source);
+ }
+}
+
+bool Target::GetMetadata(const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ bool deps_only,
+ std::vector<Value>* result,
+ std::set<const Target*>* targets_walked,
+ Err* err) const {
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> current_result;
+ // If deps_only, this is the top-level target and thus we don't want to
+ // collect its metadata, only that of its deps and data_deps.
+ if (deps_only) {
+ // Empty string will be converted below to mean all deps and data_deps.
+ // Origin is null because this isn't declared anywhere, and should never
+ // trigger any errors.
+ next_walk_keys.push_back(Value(nullptr, ""));
+ } else {
+ // Otherwise, we walk this target and collect the appropriate data.
+ if (!metadata_.WalkStep(settings()->build_settings(), keys_to_extract,
+ keys_to_walk, rebase_dir, &next_walk_keys,
+ &current_result, err))
+ return false;
+ }
+
+ // Gather walk keys and find the appropriate target. Targets identified in
+ // the walk key set must be deps or data_deps of the declaring target.
+ const DepsIteratorRange& all_deps = GetDeps(Target::DEPS_ALL);
+ const SourceDir& current_dir = label().dir();
+ for (const auto& next : next_walk_keys) {
+ DCHECK(next.type() == Value::STRING);
+
+ // If we hit an empty string in this list, add all deps and data_deps. The
+ // ordering in the resulting list of values as a result will be the data
+ // from each explicitly listed dep prior to this, followed by all data in
+ // walk order of the remaining deps.
+ if (next.string_value().empty()) {
+ for (const auto& dep : all_deps) {
+ // If we haven't walked this dep yet, go down into it.
+ auto pair = targets_walked->insert(dep.ptr);
+ if (pair.second) {
+ if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
+ false, result, targets_walked, err))
+ return false;
+ }
+ }
+
+ // Any other walk keys are superfluous, as they can only be a subset of
+ // all deps.
+ break;
+ }
+
+ // Otherwise, look through the target's deps for the specified one.
+ // Canonicalize the label if possible.
+ Label next_label = Label::Resolve(
+ current_dir, settings()->build_settings()->root_path_utf8(),
+ settings()->toolchain_label(), next, err);
+ if (next_label.is_null()) {
+ *err = Err(next.origin(), std::string("Failed to canonicalize ") +
+ next.string_value() + std::string("."));
+ }
+ std::string canonicalize_next_label = next_label.GetUserVisibleName(true);
+
+ bool found_next = false;
+ for (const auto& dep : all_deps) {
+ // Match against the label with the toolchain.
+ if (dep.label.GetUserVisibleName(true) == canonicalize_next_label) {
+ // If we haven't walked this dep yet, go down into it.
+ auto pair = targets_walked->insert(dep.ptr);
+ if (pair.second) {
+ if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
+ false, result, targets_walked, err))
+ return false;
+ }
+ // We found it, so we can exit this search now.
+ found_next = true;
+ break;
+ }
+ }
+ // If we didn't find the specified dep in the target, that's an error.
+ // Propagate it back to the user.
+ if (!found_next) {
+ *err = Err(next.origin(),
+ std::string("I was expecting ") + canonicalize_next_label +
+ std::string(" to be a dependency of ") +
+ label().GetUserVisibleName(true) +
+ ". Make sure it's included in the deps or data_deps, and "
+ "that you've specified the appropriate toolchain.");
+ return false;
+ }
+ }
+ result->insert(result->end(), std::make_move_iterator(current_result.begin()),
+ std::make_move_iterator(current_result.end()));
+ return true;
+}
diff --git a/gn/src/gn/target.h b/gn/src/gn/target.h
new file mode 100644
index 00000000000..03f456cdb33
--- /dev/null
+++ b/gn/src/gn/target.h
@@ -0,0 +1,518 @@
+// 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 TOOLS_GN_TARGET_H_
+#define TOOLS_GN_TARGET_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/action_values.h"
+#include "gn/bundle_data.h"
+#include "gn/config_values.h"
+#include "gn/inherited_libraries.h"
+#include "gn/item.h"
+#include "gn/label_pattern.h"
+#include "gn/label_ptr.h"
+#include "gn/lib_file.h"
+#include "gn/metadata.h"
+#include "gn/ordered_set.h"
+#include "gn/output_file.h"
+#include "gn/rust_values.h"
+#include "gn/source_file.h"
+#include "gn/swift_values.h"
+#include "gn/toolchain.h"
+#include "gn/unique_vector.h"
+
+class DepsIteratorRange;
+class Settings;
+class Toolchain;
+
+class Target : public Item {
+ public:
+ enum OutputType {
+ UNKNOWN,
+ GROUP,
+ EXECUTABLE,
+ SHARED_LIBRARY,
+ LOADABLE_MODULE,
+ STATIC_LIBRARY,
+ SOURCE_SET,
+ COPY_FILES,
+ ACTION,
+ ACTION_FOREACH,
+ BUNDLE_DATA,
+ CREATE_BUNDLE,
+ GENERATED_FILE,
+ RUST_LIBRARY,
+ RUST_PROC_MACRO,
+ };
+
+ enum DepsIterationType {
+ DEPS_ALL, // Iterates through all public, private, and data deps.
+ DEPS_LINKED, // Iterates through all non-data dependencies.
+ };
+
+ using FileList = std::vector<SourceFile>;
+ using StringVector = std::vector<std::string>;
+
+ // We track the set of build files that may affect this target, please refer
+ // to Scope for how this is determined.
+ Target(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files = {});
+ ~Target() override;
+
+ // Returns a string naming the output type.
+ static const char* GetStringForOutputType(OutputType type);
+
+ // Item overrides.
+ Target* AsTarget() override;
+ const Target* AsTarget() const override;
+ bool OnResolved(Err* err) override;
+
+ OutputType output_type() const { return output_type_; }
+ void set_output_type(OutputType t) { output_type_ = t; }
+
+ // True for targets that compile source code (all types of libaries and
+ // executables).
+ bool IsBinary() const;
+
+ // Can be linked into other targets.
+ bool IsLinkable() const;
+
+ // True if the target links dependencies rather than propogated up the graph.
+ // This is also true of action and copy steps even though they don't link
+ // dependencies, because they also don't propogate libraries up.
+ bool IsFinal() const;
+
+ // Set when the target should normally be treated as a data dependency. These
+ // do not need to be treated as inputs or hard dependencies for normal build
+ // steps, but have to be kept in the dependency tree to be properly
+ // propagated. Treating these as data only decreases superfluous rebuilds and
+ // increases parallelism.
+ bool IsDataOnly() const;
+
+ // Will be the empty string to use the target label as the output name.
+ // See GetComputedOutputName().
+ const std::string& output_name() const { return output_name_; }
+ void set_output_name(const std::string& name) { output_name_ = name; }
+
+ // Returns the output name for this target, which is the output_name if
+ // specified, or the target label if not.
+ //
+ // Because this depends on the tool for this target, the toolchain must
+ // have been set before calling.
+ std::string GetComputedOutputName() const;
+
+ bool output_prefix_override() const { return output_prefix_override_; }
+ void set_output_prefix_override(bool prefix_override) {
+ output_prefix_override_ = prefix_override;
+ }
+
+ // Desired output directory for the final output. This will be used for
+ // the {{output_dir}} substitution in the tool if it is specified. If
+ // is_null, the tool default will be used.
+ const SourceDir& output_dir() const { return output_dir_; }
+ void set_output_dir(const SourceDir& dir) { output_dir_ = dir; }
+
+ // The output extension is really a tri-state: unset (output_extension_set
+ // is false and the string is empty, meaning the default extension should be
+ // used), the output extension is set but empty (output should have no
+ // extension) and the output extension is set but nonempty (use the given
+ // extension).
+ const std::string& output_extension() const { return output_extension_; }
+ void set_output_extension(const std::string& extension) {
+ output_extension_ = extension;
+ output_extension_set_ = true;
+ }
+ bool output_extension_set() const { return output_extension_set_; }
+
+ const FileList& sources() const { return sources_; }
+ FileList& sources() { return sources_; }
+
+ const SourceFileTypeSet& source_types_used() const {
+ return source_types_used_;
+ }
+ SourceFileTypeSet& source_types_used() { return source_types_used_; }
+
+ // Set to true when all sources are public. This is the default. In this case
+ // the public headers list should be empty.
+ bool all_headers_public() const { return all_headers_public_; }
+ void set_all_headers_public(bool p) { all_headers_public_ = p; }
+
+ // When all_headers_public is false, this is the list of public headers. It
+ // could be empty which would mean no headers are public.
+ const FileList& public_headers() const { return public_headers_; }
+ FileList& public_headers() { return public_headers_; }
+
+ // Whether this target's includes should be checked by "gn check".
+ bool check_includes() const { return check_includes_; }
+ void set_check_includes(bool ci) { check_includes_ = ci; }
+
+ // Whether this static_library target should have code linked in.
+ bool complete_static_lib() const { return complete_static_lib_; }
+ void set_complete_static_lib(bool complete) {
+ DCHECK_EQ(STATIC_LIBRARY, output_type_);
+ complete_static_lib_ = complete;
+ }
+
+ // Metadata. Target takes ownership of the resulting scope.
+ const Metadata& metadata() const { return metadata_; }
+ Metadata& metadata() { return metadata_; }
+
+ // Get metadata from this target and its dependencies. This is intended to
+ // be called after the target is resolved.
+ bool GetMetadata(const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ bool deps_only,
+ std::vector<Value>* result,
+ std::set<const Target*>* targets_walked,
+ Err* err) const;
+
+ // GeneratedFile-related methods.
+ bool GenerateFile(Err* err) const;
+
+ const Value& contents() const { return contents_; }
+ void set_contents(const Value& value) { contents_ = value; }
+ const Value& output_conversion() const { return output_conversion_; }
+ void set_output_conversion(const Value& value) { output_conversion_ = value; }
+
+ // Metadata collection methods for GeneratedFile targets.
+ const SourceDir& rebase() const { return rebase_; }
+ void set_rebase(const SourceDir& value) { rebase_ = value; }
+ const std::vector<std::string>& data_keys() const { return data_keys_; }
+ std::vector<std::string>& data_keys() { return data_keys_; }
+ const std::vector<std::string>& walk_keys() const { return walk_keys_; }
+ std::vector<std::string>& walk_keys() { return walk_keys_; }
+
+ bool testonly() const { return testonly_; }
+ void set_testonly(bool value) { testonly_ = value; }
+
+ OutputFile write_runtime_deps_output() const {
+ return write_runtime_deps_output_;
+ }
+ void set_write_runtime_deps_output(const OutputFile& value) {
+ write_runtime_deps_output_ = value;
+ }
+
+ // Runtime dependencies. These are "file-like things" that can either be
+ // directories or files. They do not need to exist, these are just passed as
+ // runtime dependencies to external test systems as necessary.
+ const std::vector<std::string>& data() const { return data_; }
+ std::vector<std::string>& data() { return data_; }
+
+ // Information about the bundle. Only valid for CREATE_BUNDLE target after
+ // they have been resolved.
+ const BundleData& bundle_data() const { return bundle_data_; }
+ BundleData& bundle_data() { return bundle_data_; }
+
+ // Returns true if targets depending on this one should have an order
+ // dependency.
+ bool hard_dep() const {
+ return output_type_ == ACTION || output_type_ == ACTION_FOREACH ||
+ output_type_ == COPY_FILES || output_type_ == CREATE_BUNDLE ||
+ output_type_ == BUNDLE_DATA || output_type_ == GENERATED_FILE ||
+ (IsBinary() && swift_values().builds_module());
+ }
+
+ // Returns the iterator range which can be used in range-based for loops
+ // to iterate over multiple types of deps in one loop:
+ // for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) ...
+ DepsIteratorRange GetDeps(DepsIterationType type) const;
+
+ // Linked private dependencies.
+ const LabelTargetVector& private_deps() const { return private_deps_; }
+ LabelTargetVector& private_deps() { return private_deps_; }
+
+ // Linked public dependencies.
+ const LabelTargetVector& public_deps() const { return public_deps_; }
+ LabelTargetVector& public_deps() { return public_deps_; }
+
+ // Non-linked dependencies.
+ const LabelTargetVector& data_deps() const { return data_deps_; }
+ LabelTargetVector& data_deps() { return data_deps_; }
+
+ // List of configs that this class inherits settings from. Once a target is
+ // resolved, this will also list all-dependent and public configs.
+ const UniqueVector<LabelConfigPair>& configs() const { return configs_; }
+ UniqueVector<LabelConfigPair>& configs() { return configs_; }
+
+ // List of configs that all dependencies (direct and indirect) of this
+ // target get. These configs are not added to this target. Note that due
+ // to the way this is computed, there may be duplicates in this list.
+ const UniqueVector<LabelConfigPair>& all_dependent_configs() const {
+ return all_dependent_configs_;
+ }
+ UniqueVector<LabelConfigPair>& all_dependent_configs() {
+ return all_dependent_configs_;
+ }
+
+ // List of configs that targets depending directly on this one get. These
+ // configs are also added to this target.
+ const UniqueVector<LabelConfigPair>& public_configs() const {
+ return public_configs_;
+ }
+ UniqueVector<LabelConfigPair>& public_configs() { return public_configs_; }
+
+ // Dependencies that can include files from this target.
+ const std::set<Label>& allow_circular_includes_from() const {
+ return allow_circular_includes_from_;
+ }
+ std::set<Label>& allow_circular_includes_from() {
+ return allow_circular_includes_from_;
+ }
+
+ const InheritedLibraries& inherited_libraries() const {
+ return inherited_libraries_;
+ }
+
+ // This config represents the configuration set directly on this target.
+ ConfigValues& config_values() { return config_values_; }
+ const ConfigValues& config_values() const { return config_values_; }
+
+ ActionValues& action_values() { return action_values_; }
+ const ActionValues& action_values() const { return action_values_; }
+
+ SwiftValues& swift_values() { return swift_values_; }
+ const SwiftValues& swift_values() const { return swift_values_; }
+
+ RustValues& rust_values() { return rust_values_; }
+ const RustValues& rust_values() const { return rust_values_; }
+
+ const OrderedSet<SourceDir>& all_lib_dirs() const { return all_lib_dirs_; }
+ const OrderedSet<LibFile>& all_libs() const { return all_libs_; }
+
+ const OrderedSet<SourceDir>& all_framework_dirs() const {
+ return all_framework_dirs_;
+ }
+ const OrderedSet<std::string>& all_frameworks() const {
+ return all_frameworks_;
+ }
+ const OrderedSet<std::string>& all_weak_frameworks() const {
+ return all_weak_frameworks_;
+ }
+
+ const std::set<const Target*>& recursive_hard_deps() const {
+ return recursive_hard_deps_;
+ }
+
+ std::vector<LabelPattern>& friends() { return friends_; }
+ const std::vector<LabelPattern>& friends() const { return friends_; }
+
+ std::vector<LabelPattern>& assert_no_deps() { return assert_no_deps_; }
+ const std::vector<LabelPattern>& assert_no_deps() const {
+ return assert_no_deps_;
+ }
+
+ // The toolchain is only known once this target is resolved (all if its
+ // dependencies are known). They will be null until then. Generally, this can
+ // only be used during target writing.
+ const Toolchain* toolchain() const { return toolchain_; }
+
+ // Sets the toolchain. The toolchain must include a tool for this target
+ // or the error will be set and the function will return false. Unusually,
+ // this function's "err" output is optional since this is commonly used
+ // frequently by unit tests which become needlessly verbose.
+ bool SetToolchain(const Toolchain* toolchain, Err* err = nullptr);
+
+ // Once this target has been resolved, all outputs from the target will be
+ // listed here. This will include things listed in the "outputs" for an
+ // action or a copy step, and the output library or executable file(s) from
+ // binary targets.
+ //
+ // It will NOT include stamp files and object files.
+ const std::vector<OutputFile>& computed_outputs() const {
+ return computed_outputs_;
+ }
+
+ // Returns outputs from this target. The link output file is the one that
+ // other targets link to when they depend on this target. This will only be
+ // valid for libraries and will be empty for all other target types.
+ //
+ // The dependency output file is the file that should be used to express
+ // a dependency on this one. It could be the same as the link output file
+ // (this will be the case for static libraries). For shared libraries it
+ // could be the same or different than the link output file, depending on the
+ // system. For actions this will be the stamp file.
+ //
+ // These are only known once the target is resolved and will be empty before
+ // that. This is a cache of the files to prevent every target that depends on
+ // a given library from recomputing the same pattern.
+ const OutputFile& link_output_file() const { return link_output_file_; }
+ const OutputFile& dependency_output_file() const {
+ return dependency_output_file_;
+ }
+
+ // The subset of computed_outputs that are considered runtime outputs.
+ const std::vector<OutputFile>& runtime_outputs() const {
+ return runtime_outputs_;
+ }
+
+ // Computes and returns the outputs of this target expressed as SourceFiles.
+ //
+ // For binary target this depends on the tool for this target so the toolchain
+ // must have been loaded beforehand. This will happen asynchronously so
+ // calling this on a binary target before the build is complete will produce a
+ // race condition.
+ //
+ // To resolve this, the caller passes in whether the entire build is complete
+ // (this is used for the introspection commands which run after everything
+ // else).
+ //
+ // If the build is complete, the toolchain will be used for binary targets to
+ // compute the outputs. If the build is not complete, calling this function
+ // for binary targets will produce an error.
+ //
+ // The |loc_for_error| is used to blame a location for any errors produced. It
+ // can be empty if there is no range (like this is being called based on the
+ // command-line.
+ bool GetOutputsAsSourceFiles(const LocationRange& loc_for_error,
+ bool build_complete,
+ std::vector<SourceFile>* outputs,
+ Err* err) const;
+
+ // Computes the set of output files resulting from compiling the given source
+ // file.
+ //
+ // For binary targets, if the file can be compiled and the tool exists, fills
+ // the outputs in and writes the tool type to computed_tool_type. If the file
+ // is not compilable, returns false.
+ //
+ // For action_foreach and copy targets, applies the output pattern to the
+ // given file name to compute the outputs.
+ //
+ // For all other target types, just returns the target outputs because such
+ // targets conceptually process all of their inputs as one step.
+ //
+ // The function can succeed with a "NONE" tool type for object files which
+ // are just passed to the output. The output will always be overwritten, not
+ // appended to.
+ bool GetOutputFilesForSource(const SourceFile& source,
+ const char** computed_tool_type,
+ std::vector<OutputFile>* outputs) const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(TargetTest, ResolvePrecompiledHeaders);
+
+ // Pulls necessary information from dependencies to this one when all
+ // dependencies have been resolved.
+ void PullDependentTargetConfigs();
+ void PullDependentTargetLibsFrom(const Target* dep, bool is_public);
+ void PullDependentTargetLibs();
+ void PullRecursiveHardDeps();
+ void PullRecursiveBundleData();
+
+ // Fills the link and dependency output files when a target is resolved.
+ bool FillOutputFiles(Err* err);
+
+ // Checks precompiled headers from configs and makes sure the resulting
+ // values are in config_values_.
+ bool ResolvePrecompiledHeaders(Err* err);
+
+ // Validates the given thing when a target is resolved.
+ bool CheckVisibility(Err* err) const;
+ bool CheckConfigVisibility(Err* err) const;
+ bool CheckTestonly(Err* err) const;
+ bool CheckAssertNoDeps(Err* err) const;
+ void CheckSourcesGenerated() const;
+ void CheckSourceGenerated(const SourceFile& source) const;
+ bool CheckSourceSetLanguages(Err* err) const;
+
+ OutputType output_type_ = UNKNOWN;
+ std::string output_name_;
+ bool output_prefix_override_ = false;
+ SourceDir output_dir_;
+ std::string output_extension_;
+ bool output_extension_set_ = false;
+
+ FileList sources_;
+ SourceFileTypeSet source_types_used_;
+ bool all_headers_public_ = true;
+ FileList public_headers_;
+ bool check_includes_ = true;
+ bool complete_static_lib_ = false;
+ bool testonly_ = false;
+ std::vector<std::string> data_;
+ BundleData bundle_data_;
+ OutputFile write_runtime_deps_output_;
+
+ LabelTargetVector private_deps_;
+ LabelTargetVector public_deps_;
+ LabelTargetVector data_deps_;
+
+ // See getters for more info.
+ UniqueVector<LabelConfigPair> configs_;
+ UniqueVector<LabelConfigPair> all_dependent_configs_;
+ UniqueVector<LabelConfigPair> public_configs_;
+
+ std::set<Label> allow_circular_includes_from_;
+
+ // Static libraries, shared libraries, and source sets from transitive deps
+ // that need to be linked.
+ InheritedLibraries inherited_libraries_;
+
+ // These libs and dirs are inherited from statically linked deps and all
+ // configs applying to this target.
+ OrderedSet<SourceDir> all_lib_dirs_;
+ OrderedSet<LibFile> all_libs_;
+
+ // These frameworks and dirs are inherited from statically linked deps and
+ // all configs applying to this target.
+ OrderedSet<SourceDir> all_framework_dirs_;
+ OrderedSet<std::string> all_frameworks_;
+ OrderedSet<std::string> all_weak_frameworks_;
+
+ // All hard deps from this target and all dependencies. Filled in when this
+ // target is marked resolved. This will not include the current target.
+ std::set<const Target*> recursive_hard_deps_;
+
+ std::vector<LabelPattern> friends_;
+ std::vector<LabelPattern> assert_no_deps_;
+
+ // Used for all binary targets, and for inputs in regular targets. The
+ // precompiled header values in this struct will be resolved to the ones to
+ // use for this target, if precompiled headers are used.
+ ConfigValues config_values_;
+
+ // Used for action[_foreach] targets.
+ ActionValues action_values_;
+
+ // Used for Rust targets.
+ RustValues rust_values_;
+
+ // User for Swift targets.
+ SwiftValues swift_values_;
+
+ // Toolchain used by this target. Null until target is resolved.
+ const Toolchain* toolchain_ = nullptr;
+
+ // Output files. Empty until the target is resolved.
+ std::vector<OutputFile> computed_outputs_;
+ OutputFile link_output_file_;
+ OutputFile dependency_output_file_;
+ std::vector<OutputFile> runtime_outputs_;
+
+ Metadata metadata_;
+
+ // GeneratedFile values.
+ Value output_conversion_;
+ Value contents_; // Value::NONE if metadata collection should occur.
+
+ // GeneratedFile as metadata collection values.
+ SourceDir rebase_;
+ std::vector<std::string> data_keys_;
+ std::vector<std::string> walk_keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(Target);
+};
+
+extern const char kExecution_Help[];
+
+#endif // TOOLS_GN_TARGET_H_
diff --git a/gn/src/gn/target_generator.cc b/gn/src/gn/target_generator.cc
new file mode 100644
index 00000000000..0ae9dafda81
--- /dev/null
+++ b/gn/src/gn/target_generator.cc
@@ -0,0 +1,451 @@
+// 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 "gn/target_generator.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "gn/action_target_generator.h"
+#include "gn/binary_target_generator.h"
+#include "gn/build_settings.h"
+#include "gn/bundle_data_target_generator.h"
+#include "gn/config.h"
+#include "gn/copy_target_generator.h"
+#include "gn/create_bundle_target_generator.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/generated_file_target_generator.h"
+#include "gn/group_target_generator.h"
+#include "gn/metadata.h"
+#include "gn/parse_tree.h"
+#include "gn/scheduler.h"
+#include "gn/scope.h"
+#include "gn/token.h"
+#include "gn/value.h"
+#include "gn/value_extractors.h"
+#include "gn/variables.h"
+
+TargetGenerator::TargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : target_(target),
+ scope_(scope),
+ function_call_(function_call),
+ err_(err) {}
+
+TargetGenerator::~TargetGenerator() = default;
+
+void TargetGenerator::Run() {
+ // All target types use these.
+ if (!FillDependentConfigs())
+ return;
+
+ if (!FillData())
+ return;
+
+ if (!FillDependencies())
+ return;
+
+ if (!FillMetadata())
+ return;
+
+ if (!FillTestonly())
+ return;
+
+ if (!FillAssertNoDeps())
+ return;
+
+ if (!Visibility::FillItemVisibility(target_, scope_, err_))
+ return;
+
+ if (!FillWriteRuntimeDeps())
+ return;
+
+ // Do type-specific generation.
+ DoRun();
+}
+
+// static
+void TargetGenerator::GenerateTarget(Scope* scope,
+ const FunctionCallNode* function_call,
+ const std::vector<Value>& args,
+ const std::string& output_type,
+ Err* err) {
+ // Name is the argument to the function.
+ if (args.size() != 1u || args[0].type() != Value::STRING) {
+ *err = Err(function_call, "Target generator requires one string argument.",
+ "Otherwise I'm not sure what to call this target.");
+ return;
+ }
+
+ // The location of the target is the directory name with no slash at the end.
+ // FIXME(brettw) validate name.
+ const Label& toolchain_label = ToolchainLabelForScope(scope);
+ Label label(scope->GetSourceDir(), args[0].string_value(),
+ toolchain_label.dir(), toolchain_label.name());
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Defining target", label.GetUserVisibleName(true));
+
+ std::unique_ptr<Target> target = std::make_unique<Target>(
+ scope->settings(), label, scope->build_dependency_files());
+ target->set_defined_from(function_call);
+
+ // Create and call out to the proper generator.
+ if (output_type == functions::kBundleData) {
+ BundleDataTargetGenerator generator(target.get(), scope, function_call,
+ err);
+ generator.Run();
+ } else if (output_type == functions::kCreateBundle) {
+ CreateBundleTargetGenerator generator(target.get(), scope, function_call,
+ err);
+ generator.Run();
+ } else if (output_type == functions::kCopy) {
+ CopyTargetGenerator generator(target.get(), scope, function_call, err);
+ generator.Run();
+ } else if (output_type == functions::kAction) {
+ ActionTargetGenerator generator(target.get(), scope, function_call,
+ Target::ACTION, err);
+ generator.Run();
+ } else if (output_type == functions::kActionForEach) {
+ ActionTargetGenerator generator(target.get(), scope, function_call,
+ Target::ACTION_FOREACH, err);
+ generator.Run();
+ } else if (output_type == functions::kExecutable) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::EXECUTABLE, err);
+ generator.Run();
+ } else if (output_type == functions::kGroup) {
+ GroupTargetGenerator generator(target.get(), scope, function_call, err);
+ generator.Run();
+ } else if (output_type == functions::kLoadableModule) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::LOADABLE_MODULE, err);
+ generator.Run();
+ } else if (output_type == functions::kSharedLibrary) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::SHARED_LIBRARY, err);
+ generator.Run();
+ } else if (output_type == functions::kSourceSet) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::SOURCE_SET, err);
+ generator.Run();
+ } else if (output_type == functions::kStaticLibrary) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::STATIC_LIBRARY, err);
+ generator.Run();
+ } else if (output_type == functions::kGeneratedFile) {
+ GeneratedFileTargetGenerator generator(target.get(), scope, function_call,
+ Target::GENERATED_FILE, err);
+ generator.Run();
+ } else if (output_type == functions::kRustLibrary) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::RUST_LIBRARY, err);
+ generator.Run();
+ } else if (output_type == functions::kRustProcMacro) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::RUST_PROC_MACRO, err);
+ generator.Run();
+ } else {
+ *err = Err(function_call, "Not a known target type",
+ "I am very confused by the target type \"" + output_type + "\"");
+ }
+
+ if (err->has_error())
+ return;
+
+ // Save this target for the file.
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function_call, "Can't define a target in this context.");
+ return;
+ }
+ collector->push_back(std::move(target));
+}
+
+const BuildSettings* TargetGenerator::GetBuildSettings() const {
+ return scope_->settings()->build_settings();
+}
+
+bool TargetGenerator::FillSources() {
+ const Value* value = scope_->GetValue(variables::kSources, true);
+ if (!value)
+ return true;
+
+ Target::FileList dest_sources;
+ if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &dest_sources, err_))
+ return false;
+ target_->sources() = std::move(dest_sources);
+ return true;
+}
+
+bool TargetGenerator::FillPublic() {
+ const Value* value = scope_->GetValue(variables::kPublic, true);
+ if (!value)
+ return true;
+
+ // If the public headers are defined, don't default to public.
+ target_->set_all_headers_public(false);
+
+ Target::FileList dest_public;
+ if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &dest_public, err_))
+ return false;
+ target_->public_headers() = std::move(dest_public);
+ return true;
+}
+
+bool TargetGenerator::FillConfigs() {
+ return FillGenericConfigs(variables::kConfigs, &target_->configs());
+}
+
+bool TargetGenerator::FillDependentConfigs() {
+ if (!FillGenericConfigs(variables::kAllDependentConfigs,
+ &target_->all_dependent_configs()))
+ return false;
+
+ if (!FillGenericConfigs(variables::kPublicConfigs,
+ &target_->public_configs()))
+ return false;
+
+ return true;
+}
+
+bool TargetGenerator::FillData() {
+ const Value* value = scope_->GetValue(variables::kData, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ const std::vector<Value>& input_list = value->list_value();
+ std::vector<std::string>& output_list = target_->data();
+ output_list.reserve(input_list.size());
+
+ const SourceDir& dir = scope_->GetSourceDir();
+ const std::string& root_path =
+ scope_->settings()->build_settings()->root_path_utf8();
+
+ for (size_t i = 0; i < input_list.size(); i++) {
+ const Value& input = input_list[i];
+ if (!input.VerifyTypeIs(Value::STRING, err_))
+ return false;
+ const std::string input_str = input.string_value();
+
+ // Treat each input as either a file or a directory, depending on the
+ // last character.
+ bool as_dir = !input_str.empty() && input_str[input_str.size() - 1] == '/';
+
+ std::string resolved =
+ dir.ResolveRelativeAs(!as_dir, input, err_, root_path, &input_str);
+ if (err_->has_error())
+ return false;
+
+ output_list.push_back(resolved);
+ }
+ return true;
+}
+
+bool TargetGenerator::FillDependencies() {
+ if (!FillGenericDeps(variables::kDeps, &target_->private_deps()))
+ return false;
+ if (!FillGenericDeps(variables::kPublicDeps, &target_->public_deps()))
+ return false;
+ if (!FillGenericDeps(variables::kDataDeps, &target_->data_deps()))
+ return false;
+
+ // "data_deps" was previously named "datadeps". For backwards-compat, read
+ // the old one if no "data_deps" were specified.
+ if (!scope_->GetValue(variables::kDataDeps, false)) {
+ if (!FillGenericDeps("datadeps", &target_->data_deps()))
+ return false;
+ }
+
+ return true;
+}
+
+bool TargetGenerator::FillMetadata() {
+ // Need to get a mutable value to mark all values in the scope as used. This
+ // cannot be done on a const Scope.
+ Value* value = scope_->GetMutableValue(variables::kMetadata,
+ Scope::SEARCH_CURRENT, true);
+
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::SCOPE, err_))
+ return false;
+
+ Scope* scope_value = value->scope_value();
+
+ scope_value->GetCurrentScopeValues(&target_->metadata().contents());
+ scope_value->MarkAllUsed();
+
+ // Metadata values should always hold lists of Values, such that they can be
+ // collected and concatenated. Any additional specific type verification is
+ // done at walk time.
+ for (const auto& iter : target_->metadata().contents()) {
+ if (!iter.second.VerifyTypeIs(Value::LIST, err_))
+ return false;
+ }
+
+ target_->metadata().set_source_dir(scope_->GetSourceDir());
+ target_->metadata().set_origin(value->origin());
+ return true;
+}
+
+bool TargetGenerator::FillTestonly() {
+ const Value* value = scope_->GetValue(variables::kTestonly, true);
+ if (value) {
+ if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
+ return false;
+ target_->set_testonly(value->boolean_value());
+ }
+ return true;
+}
+
+bool TargetGenerator::FillAssertNoDeps() {
+ const Value* value = scope_->GetValue(variables::kAssertNoDeps, true);
+ if (value) {
+ return ExtractListOfLabelPatterns(scope_->settings()->build_settings(),
+ *value, scope_->GetSourceDir(),
+ &target_->assert_no_deps(), err_);
+ }
+ return true;
+}
+
+bool TargetGenerator::FillOutputs(bool allow_substitutions) {
+ const Value* value = scope_->GetValue(variables::kOutputs, true);
+ if (!value)
+ return true;
+
+ SubstitutionList& outputs = target_->action_values().outputs();
+ if (!outputs.Parse(*value, err_))
+ return false;
+
+ if (!allow_substitutions) {
+ // Verify no substitutions were actually used.
+ if (!outputs.required_types().empty()) {
+ *err_ =
+ Err(*value, "Source expansions not allowed here.",
+ "The outputs of this target used source {{expansions}} but this "
+ "target type\ndoesn't support them. Just express the outputs "
+ "literally.");
+ return false;
+ }
+ }
+
+ // Check the substitutions used are valid for this purpose.
+ if (!EnsureValidSubstitutions(outputs.required_types(),
+ &IsValidSourceSubstitution, value->origin(),
+ err_))
+ return false;
+
+ // Validate that outputs are in the output dir.
+ CHECK(outputs.list().size() == value->list_value().size());
+ for (size_t i = 0; i < outputs.list().size(); i++) {
+ if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
+ value->list_value()[i]))
+ return false;
+ }
+ return true;
+}
+
+bool TargetGenerator::FillCheckIncludes() {
+ const Value* value = scope_->GetValue(variables::kCheckIncludes, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
+ return false;
+ target_->set_check_includes(value->boolean_value());
+ return true;
+}
+
+bool TargetGenerator::FillOutputExtension() {
+ const Value* value = scope_->GetValue(variables::kOutputExtension, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->set_output_extension(value->string_value());
+ return true;
+}
+
+bool TargetGenerator::EnsureSubstitutionIsInOutputDir(
+ const SubstitutionPattern& pattern,
+ const Value& original_value) {
+ if (pattern.ranges().empty()) {
+ // Pattern is empty, error out (this prevents weirdness below).
+ *err_ = Err(original_value, "This has an empty value in it.");
+ return false;
+ }
+
+ if (pattern.ranges()[0].type == &SubstitutionLiteral) {
+ // If the first thing is a literal, it must start with the output dir.
+ if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(),
+ pattern.ranges()[0].literal,
+ original_value.origin(), err_))
+ return false;
+ } else {
+ // Otherwise, the first subrange must be a pattern that expands to
+ // something in the output directory.
+ if (!SubstitutionIsInOutputDir(pattern.ranges()[0].type)) {
+ *err_ =
+ Err(original_value, "File is not inside output directory.",
+ "The given file should be in the output directory. Normally you\n"
+ "would specify\n\"$target_out_dir/foo\" or "
+ "\"{{source_gen_dir}}/foo\".");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TargetGenerator::FillGenericConfigs(const char* var_name,
+ UniqueVector<LabelConfigPair>* dest) {
+ const Value* value = scope_->GetValue(var_name, true);
+ if (value) {
+ ExtractListOfUniqueLabels(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(),
+ ToolchainLabelForScope(scope_), dest, err_);
+ }
+ return !err_->has_error();
+}
+
+bool TargetGenerator::FillGenericDeps(const char* var_name,
+ LabelTargetVector* dest) {
+ const Value* value = scope_->GetValue(var_name, true);
+ if (value) {
+ ExtractListOfLabels(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), ToolchainLabelForScope(scope_),
+ dest, err_);
+ }
+ return !err_->has_error();
+}
+
+bool TargetGenerator::FillWriteRuntimeDeps() {
+ const Value* value = scope_->GetValue(variables::kWriteRuntimeDeps, true);
+ if (!value)
+ return true;
+
+ // Compute the file name and make sure it's in the output dir.
+ SourceFile source_file = scope_->GetSourceDir().ResolveRelativeFile(
+ *value, err_, GetBuildSettings()->root_path_utf8());
+ if (err_->has_error())
+ return false;
+ if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(),
+ source_file.value(), value->origin(), err_))
+ return false;
+ OutputFile output_file(GetBuildSettings(), source_file);
+ target_->set_write_runtime_deps_output(output_file);
+
+ return true;
+}
diff --git a/gn/src/gn/target_generator.h b/gn/src/gn/target_generator.h
new file mode 100644
index 00000000000..20531969afe
--- /dev/null
+++ b/gn/src/gn/target_generator.h
@@ -0,0 +1,86 @@
+// 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 TOOLS_GN_TARGET_GENERATOR_H_
+#define TOOLS_GN_TARGET_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/label_ptr.h"
+#include "gn/unique_vector.h"
+
+class BuildSettings;
+class Err;
+class FunctionCallNode;
+class Scope;
+class SubstitutionPattern;
+class Value;
+
+// Fills the variables in a Target object from a Scope (the result of a script
+// execution). Target-type-specific derivations of this class will be used
+// for each different type of function call. This class implements the common
+// behavior.
+class TargetGenerator {
+ public:
+ TargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ virtual ~TargetGenerator();
+
+ void Run();
+
+ // The function call is the parse tree node that invoked the target.
+ // err() will be set on failure.
+ static void GenerateTarget(Scope* scope,
+ const FunctionCallNode* function_call,
+ const std::vector<Value>& args,
+ const std::string& output_type,
+ Err* err);
+
+ protected:
+ // Derived classes implement this to do type-specific generation.
+ virtual void DoRun() = 0;
+
+ const BuildSettings* GetBuildSettings() const;
+
+ virtual bool FillSources();
+ bool FillPublic();
+ bool FillConfigs();
+ bool FillOutputs(bool allow_substitutions);
+ bool FillCheckIncludes();
+ bool FillOutputExtension();
+
+ // Rrturns true if the given pattern will expand to a file in the output
+ // directory. If not, returns false and sets the error, blaming the given
+ // Value.
+ bool EnsureSubstitutionIsInOutputDir(const SubstitutionPattern& pattern,
+ const Value& original_value);
+
+ Target* target_;
+ Scope* scope_;
+ const FunctionCallNode* function_call_;
+ Err* err_;
+
+ private:
+ bool FillDependentConfigs(); // Includes all types of dependent configs.
+ bool FillData();
+ bool FillDependencies(); // Includes data dependencies.
+ bool FillMetadata();
+ bool FillTestonly();
+ bool FillAssertNoDeps();
+ bool FillWriteRuntimeDeps();
+
+ // Reads configs/deps from the given var name, and uses the given setting on
+ // the target to save them.
+ bool FillGenericConfigs(const char* var_name,
+ UniqueVector<LabelConfigPair>* dest);
+ bool FillGenericDeps(const char* var_name, LabelTargetVector* dest);
+
+ DISALLOW_COPY_AND_ASSIGN(TargetGenerator);
+};
+
+#endif // TOOLS_GN_TARGET_GENERATOR_H_
diff --git a/gn/src/gn/target_unittest.cc b/gn/src/gn/target_unittest.cc
new file mode 100644
index 00000000000..792c5304ea5
--- /dev/null
+++ b/gn/src/gn/target_unittest.cc
@@ -0,0 +1,1539 @@
+// 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 "gn/target.h"
+
+#include <memory>
+#include <utility>
+
+#include "gn/build_settings.h"
+#include "gn/config.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/test_with_scheduler.h"
+#include "gn/test_with_scope.h"
+#include "gn/toolchain.h"
+#include "util/test/test.h"
+
+namespace {
+
+// Asserts that the current global scheduler has a single unknown generated
+// file with the given name from the given target.
+void AssertSchedulerHasOneUnknownFileMatching(const Target* target,
+ const SourceFile& file) {
+ auto unknown = g_scheduler->GetUnknownGeneratedInputs();
+ ASSERT_EQ(1u, unknown.size()); // Should be one unknown file.
+ auto found = unknown.find(file);
+ ASSERT_TRUE(found != unknown.end()) << file.value();
+ EXPECT_TRUE(target == found->second)
+ << "Target doesn't match. Expected\n "
+ << target->label().GetUserVisibleName(false) << "\nBut got\n "
+ << found->second->label().GetUserVisibleName(false);
+}
+
+} // namespace
+
+using TargetTest = TestWithScheduler;
+
+// Tests that lib[_dir]s are inherited across deps boundaries for static
+// libraries but not executables.
+TEST_F(TargetTest, LibInheritance) {
+ TestWithScope setup;
+ Err err;
+
+ const LibFile lib("foo");
+ const SourceDir libdir("/foo_dir/");
+
+ // Leaf target with ldflags set.
+ TestTarget z(setup, "//foo:z", Target::STATIC_LIBRARY);
+ z.config_values().libs().push_back(lib);
+ z.config_values().lib_dirs().push_back(libdir);
+ ASSERT_TRUE(z.OnResolved(&err));
+
+ // All lib[_dir]s should be set when target is resolved.
+ ASSERT_EQ(1u, z.all_libs().size());
+ EXPECT_EQ(lib, z.all_libs()[0]);
+ ASSERT_EQ(1u, z.all_lib_dirs().size());
+ EXPECT_EQ(libdir, z.all_lib_dirs()[0]);
+
+ // Shared library target should inherit the libs from the static library
+ // and its own. Its own flag should be before the inherited one.
+ const LibFile second_lib("bar");
+ const SourceDir second_libdir("/bar_dir/");
+ TestTarget shared(setup, "//foo:shared", Target::SHARED_LIBRARY);
+ shared.config_values().libs().push_back(second_lib);
+ shared.config_values().lib_dirs().push_back(second_libdir);
+ shared.private_deps().push_back(LabelTargetPair(&z));
+ ASSERT_TRUE(shared.OnResolved(&err));
+
+ ASSERT_EQ(2u, shared.all_libs().size());
+ EXPECT_EQ(second_lib, shared.all_libs()[0]);
+ EXPECT_EQ(lib, shared.all_libs()[1]);
+ ASSERT_EQ(2u, shared.all_lib_dirs().size());
+ EXPECT_EQ(second_libdir, shared.all_lib_dirs()[0]);
+ EXPECT_EQ(libdir, shared.all_lib_dirs()[1]);
+
+ // Executable target shouldn't get either by depending on shared.
+ TestTarget exec(setup, "//foo:exec", Target::EXECUTABLE);
+ exec.private_deps().push_back(LabelTargetPair(&shared));
+ ASSERT_TRUE(exec.OnResolved(&err));
+ EXPECT_EQ(0u, exec.all_libs().size());
+ EXPECT_EQ(0u, exec.all_lib_dirs().size());
+}
+
+// Tests that framework[_dir]s are inherited across deps boundaries for static
+// libraries but not executables.
+TEST_F(TargetTest, FrameworkInheritance) {
+ TestWithScope setup;
+ Err err;
+
+ const std::string framework("Foo.framework");
+ const SourceDir frameworkdir("//out/foo/");
+
+ // Leaf target with ldflags set.
+ TestTarget z(setup, "//foo:z", Target::STATIC_LIBRARY);
+ z.config_values().frameworks().push_back(framework);
+ z.config_values().framework_dirs().push_back(frameworkdir);
+ ASSERT_TRUE(z.OnResolved(&err));
+
+ // All framework[_dir]s should be set when target is resolved.
+ ASSERT_EQ(1u, z.all_frameworks().size());
+ EXPECT_EQ(framework, z.all_frameworks()[0]);
+ ASSERT_EQ(1u, z.all_framework_dirs().size());
+ EXPECT_EQ(frameworkdir, z.all_framework_dirs()[0]);
+
+ // Shared library target should inherit the libs from the static library
+ // and its own. Its own flag should be before the inherited one.
+ const std::string second_framework("Bar.framework");
+ const SourceDir second_frameworkdir("//out/bar/");
+ TestTarget shared(setup, "//foo:shared", Target::SHARED_LIBRARY);
+ shared.config_values().frameworks().push_back(second_framework);
+ shared.config_values().framework_dirs().push_back(second_frameworkdir);
+ shared.private_deps().push_back(LabelTargetPair(&z));
+ ASSERT_TRUE(shared.OnResolved(&err));
+
+ ASSERT_EQ(2u, shared.all_frameworks().size());
+ EXPECT_EQ(second_framework, shared.all_frameworks()[0]);
+ EXPECT_EQ(framework, shared.all_frameworks()[1]);
+ ASSERT_EQ(2u, shared.all_framework_dirs().size());
+ EXPECT_EQ(second_frameworkdir, shared.all_framework_dirs()[0]);
+ EXPECT_EQ(frameworkdir, shared.all_framework_dirs()[1]);
+
+ // Executable target shouldn't get either by depending on shared.
+ TestTarget exec(setup, "//foo:exec", Target::EXECUTABLE);
+ exec.private_deps().push_back(LabelTargetPair(&shared));
+ ASSERT_TRUE(exec.OnResolved(&err));
+ EXPECT_EQ(0u, exec.all_frameworks().size());
+ EXPECT_EQ(0u, exec.all_framework_dirs().size());
+}
+
+// Test all_dependent_configs and public_config inheritance.
+TEST_F(TargetTest, DependentConfigs) {
+ TestWithScope setup;
+ Err err;
+
+ // Set up a dependency chain of a -> b -> c
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
+ TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
+ a.private_deps().push_back(LabelTargetPair(&b));
+ b.private_deps().push_back(LabelTargetPair(&c));
+
+ // Normal non-inherited config.
+ Config config(setup.settings(), Label(SourceDir("//foo/"), "config"));
+ config.visibility().SetPublic();
+ ASSERT_TRUE(config.OnResolved(&err));
+ c.configs().push_back(LabelConfigPair(&config));
+
+ // All dependent config.
+ Config all(setup.settings(), Label(SourceDir("//foo/"), "all"));
+ all.visibility().SetPublic();
+ ASSERT_TRUE(all.OnResolved(&err));
+ c.all_dependent_configs().push_back(LabelConfigPair(&all));
+
+ // Direct dependent config.
+ Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct"));
+ direct.visibility().SetPublic();
+ ASSERT_TRUE(direct.OnResolved(&err));
+ c.public_configs().push_back(LabelConfigPair(&direct));
+
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // B should have gotten both dependent configs from C.
+ ASSERT_EQ(2u, b.configs().size());
+ EXPECT_EQ(&all, b.configs()[0].ptr);
+ EXPECT_EQ(&direct, b.configs()[1].ptr);
+ ASSERT_EQ(1u, b.all_dependent_configs().size());
+ EXPECT_EQ(&all, b.all_dependent_configs()[0].ptr);
+
+ // A should have just gotten the "all" dependent config from C.
+ ASSERT_EQ(1u, a.configs().size());
+ EXPECT_EQ(&all, a.configs()[0].ptr);
+ EXPECT_EQ(&all, a.all_dependent_configs()[0].ptr);
+
+ // Making an an alternate A and B with B forwarding the direct dependents.
+ TestTarget a_fwd(setup, "//foo:a_fwd", Target::EXECUTABLE);
+ TestTarget b_fwd(setup, "//foo:b_fwd", Target::STATIC_LIBRARY);
+ a_fwd.private_deps().push_back(LabelTargetPair(&b_fwd));
+ b_fwd.private_deps().push_back(LabelTargetPair(&c));
+
+ ASSERT_TRUE(b_fwd.OnResolved(&err));
+ ASSERT_TRUE(a_fwd.OnResolved(&err));
+
+ // A_fwd should now have both configs.
+ ASSERT_EQ(1u, a_fwd.configs().size());
+ EXPECT_EQ(&all, a_fwd.configs()[0].ptr);
+ ASSERT_EQ(1u, a_fwd.all_dependent_configs().size());
+ EXPECT_EQ(&all, a_fwd.all_dependent_configs()[0].ptr);
+}
+
+// Tests that dependent configs don't propagate between toolchains.
+TEST_F(TargetTest, NoDependentConfigsBetweenToolchains) {
+ TestWithScope setup;
+ Err err;
+
+ // Create another toolchain.
+ Toolchain other_toolchain(setup.settings(),
+ Label(SourceDir("//other/"), "toolchain"));
+ TestWithScope::SetupToolchain(&other_toolchain);
+
+ // Set up a dependency chain of |a| -> |b| -> |c| where |a| has a different
+ // toolchain.
+ Target a(setup.settings(),
+ Label(SourceDir("//foo/"), "a", other_toolchain.label().dir(),
+ other_toolchain.label().name()));
+ a.set_output_type(Target::EXECUTABLE);
+ EXPECT_TRUE(a.SetToolchain(&other_toolchain, &err));
+ TestTarget b(setup, "//foo:b", Target::EXECUTABLE);
+ TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
+ a.private_deps().push_back(LabelTargetPair(&b));
+ b.private_deps().push_back(LabelTargetPair(&c));
+
+ // All dependent config.
+ Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all"));
+ all_dependent.visibility().SetPublic();
+ ASSERT_TRUE(all_dependent.OnResolved(&err));
+ c.all_dependent_configs().push_back(LabelConfigPair(&all_dependent));
+
+ // Public config.
+ Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public"));
+ public_config.visibility().SetPublic();
+ ASSERT_TRUE(public_config.OnResolved(&err));
+ c.public_configs().push_back(LabelConfigPair(&public_config));
+
+ // Another public config.
+ Config public_config2(setup.settings(),
+ Label(SourceDir("//foo/"), "public2"));
+ public_config2.visibility().SetPublic();
+ ASSERT_TRUE(public_config2.OnResolved(&err));
+ b.public_configs().push_back(LabelConfigPair(&public_config2));
+
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // B should have gotten the configs from C.
+ ASSERT_EQ(3u, b.configs().size());
+ EXPECT_EQ(&public_config2, b.configs()[0].ptr);
+ EXPECT_EQ(&all_dependent, b.configs()[1].ptr);
+ EXPECT_EQ(&public_config, b.configs()[2].ptr);
+ ASSERT_EQ(1u, b.all_dependent_configs().size());
+ EXPECT_EQ(&all_dependent, b.all_dependent_configs()[0].ptr);
+
+ // A should not have gotten any configs from B or C.
+ ASSERT_EQ(0u, a.configs().size());
+ ASSERT_EQ(0u, a.all_dependent_configs().size());
+}
+
+// Tests that dependent configs propagate between toolchains if
+// propagates_configs is set.
+TEST_F(TargetTest, DependentConfigsBetweenToolchainsWhenSet) {
+ TestWithScope setup;
+ Err err;
+
+ // Create another toolchain.
+ Toolchain other_toolchain(setup.settings(),
+ Label(SourceDir("//other/"), "toolchain"));
+ TestWithScope::SetupToolchain(&other_toolchain);
+ other_toolchain.set_propagates_configs(true);
+
+ // Set up a dependency chain of |a| -> |b| where |b| has a different
+ // toolchain (with propagate_configs set).
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ Target b(setup.settings(),
+ Label(SourceDir("//foo/"), "b", other_toolchain.label().dir(),
+ other_toolchain.label().name()));
+ b.visibility().SetPublic();
+ b.set_output_type(Target::SHARED_LIBRARY);
+ EXPECT_TRUE(b.SetToolchain(&other_toolchain, &err));
+ a.private_deps().push_back(LabelTargetPair(&b));
+
+ // All dependent config.
+ Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all"));
+ all_dependent.visibility().SetPublic();
+ ASSERT_TRUE(all_dependent.OnResolved(&err));
+ b.all_dependent_configs().push_back(LabelConfigPair(&all_dependent));
+
+ // Public config.
+ Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public"));
+ public_config.visibility().SetPublic();
+ ASSERT_TRUE(public_config.OnResolved(&err));
+ b.public_configs().push_back(LabelConfigPair(&public_config));
+
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // A should have gotten the configs from B.
+ ASSERT_EQ(2u, a.configs().size());
+ EXPECT_EQ(&all_dependent, a.configs()[0].ptr);
+ EXPECT_EQ(&public_config, a.configs()[1].ptr);
+ ASSERT_EQ(1u, a.all_dependent_configs().size());
+ EXPECT_EQ(&all_dependent, a.all_dependent_configs()[0].ptr);
+}
+
+TEST_F(TargetTest, InheritLibs) {
+ TestWithScope setup;
+ Err err;
+
+ // Create a dependency chain:
+ // A (executable) -> B (shared lib) -> C (static lib) -> D (source set)
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ TestTarget b(setup, "//foo:b", Target::SHARED_LIBRARY);
+ TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
+ TestTarget d(setup, "//foo:d", Target::SOURCE_SET);
+ a.private_deps().push_back(LabelTargetPair(&b));
+ b.private_deps().push_back(LabelTargetPair(&c));
+ c.private_deps().push_back(LabelTargetPair(&d));
+
+ ASSERT_TRUE(d.OnResolved(&err));
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // C should have D in its inherited libs.
+ std::vector<const Target*> c_inherited = c.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, c_inherited.size());
+ EXPECT_EQ(&d, c_inherited[0]);
+
+ // B should have C and D in its inherited libs.
+ std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
+ ASSERT_EQ(2u, b_inherited.size());
+ EXPECT_EQ(&c, b_inherited[0]);
+ EXPECT_EQ(&d, b_inherited[1]);
+
+ // A should have B in its inherited libs, but not any others (the shared
+ // library will include the static library and source set).
+ std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, a_inherited.size());
+ EXPECT_EQ(&b, a_inherited[0]);
+}
+
+TEST_F(TargetTest, InheritCompleteStaticLib) {
+ TestWithScope setup;
+ Err err;
+
+ // Create a dependency chain:
+ // A (executable) -> B (complete static lib) -> C (source set)
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
+ b.set_complete_static_lib(true);
+
+ const LibFile lib("foo");
+ const SourceDir lib_dir("/foo_dir/");
+ TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
+ c.config_values().libs().push_back(lib);
+ c.config_values().lib_dirs().push_back(lib_dir);
+
+ a.public_deps().push_back(LabelTargetPair(&b));
+ b.public_deps().push_back(LabelTargetPair(&c));
+
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // B should have C in its inherited libs.
+ std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, b_inherited.size());
+ EXPECT_EQ(&c, b_inherited[0]);
+
+ // A should have B in its inherited libs, but not any others (the complete
+ // static library will include the source set).
+ std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, a_inherited.size());
+ EXPECT_EQ(&b, a_inherited[0]);
+
+ // A should inherit the libs and lib_dirs from the C.
+ ASSERT_EQ(1u, a.all_libs().size());
+ EXPECT_EQ(lib, a.all_libs()[0]);
+ ASSERT_EQ(1u, a.all_lib_dirs().size());
+ EXPECT_EQ(lib_dir, a.all_lib_dirs()[0]);
+}
+
+TEST_F(TargetTest, InheritCompleteStaticLibStaticLibDeps) {
+ TestWithScope setup;
+ Err err;
+
+ // Create a dependency chain:
+ // A (executable) -> B (complete static lib) -> C (static lib)
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
+ b.set_complete_static_lib(true);
+ TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
+ a.public_deps().push_back(LabelTargetPair(&b));
+ b.public_deps().push_back(LabelTargetPair(&c));
+
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // B should have C in its inherited libs.
+ std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, b_inherited.size());
+ EXPECT_EQ(&c, b_inherited[0]);
+
+ // A should have B in its inherited libs, but not any others (the complete
+ // static library will include the static library).
+ std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, a_inherited.size());
+ EXPECT_EQ(&b, a_inherited[0]);
+}
+
+TEST_F(TargetTest, InheritCompleteStaticLibInheritedCompleteStaticLibDeps) {
+ TestWithScope setup;
+ Err err;
+
+ // Create a dependency chain:
+ // A (executable) -> B (complete static lib) -> C (complete static lib)
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
+ b.set_complete_static_lib(true);
+ TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
+ c.set_complete_static_lib(true);
+
+ a.private_deps().push_back(LabelTargetPair(&b));
+ b.private_deps().push_back(LabelTargetPair(&c));
+
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // B should have C in its inherited libs.
+ std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
+ ASSERT_EQ(1u, b_inherited.size());
+ EXPECT_EQ(&c, b_inherited[0]);
+
+ // A should have B and C in its inherited libs.
+ std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
+ ASSERT_EQ(2u, a_inherited.size());
+ EXPECT_EQ(&b, a_inherited[0]);
+ EXPECT_EQ(&c, a_inherited[1]);
+}
+
+TEST_F(TargetTest, NoActionDepPropgation) {
+ TestWithScope setup;
+ Err err;
+
+ // Create a dependency chain:
+ // A (exe) -> B (action) -> C (source_set)
+ {
+ TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
+ TestTarget b(setup, "//foo:b", Target::ACTION);
+ TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
+
+ a.private_deps().push_back(LabelTargetPair(&b));
+ b.private_deps().push_back(LabelTargetPair(&c));
+
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(b.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // The executable should not have inherited the source set across the
+ // action.
+ std::vector<const Target*> libs = a.inherited_libraries().GetOrdered();
+ ASSERT_TRUE(libs.empty());
+ }
+}
+
+TEST_F(TargetTest, GetComputedOutputName) {
+ TestWithScope setup;
+ Err err;
+
+ // Basic target with no prefix (executable type tool in the TestWithScope has
+ // no prefix) or output name.
+ TestTarget basic(setup, "//foo:bar", Target::EXECUTABLE);
+ ASSERT_TRUE(basic.OnResolved(&err));
+ EXPECT_EQ("bar", basic.GetComputedOutputName());
+
+ // Target with no prefix but an output name.
+ TestTarget with_name(setup, "//foo:bar", Target::EXECUTABLE);
+ with_name.set_output_name("myoutput");
+ ASSERT_TRUE(with_name.OnResolved(&err));
+ EXPECT_EQ("myoutput", with_name.GetComputedOutputName());
+
+ // Target with a "lib" prefix (the static library tool in the TestWithScope
+ // should specify a "lib" output prefix).
+ TestTarget with_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ ASSERT_TRUE(with_prefix.OnResolved(&err));
+ EXPECT_EQ("libbar", with_prefix.GetComputedOutputName());
+
+ // Target with a "lib" prefix that already has it applied. The prefix should
+ // not duplicate something already in the target name.
+ TestTarget dup_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ dup_prefix.set_output_name("libbar");
+ ASSERT_TRUE(dup_prefix.OnResolved(&err));
+ EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName());
+
+ // Target with an output prefix override should not have a prefix.
+ TestTarget override_prefix(setup, "//foo:bar", Target::SHARED_LIBRARY);
+ override_prefix.set_output_prefix_override(true);
+ ASSERT_TRUE(dup_prefix.OnResolved(&err));
+ EXPECT_EQ("bar", override_prefix.GetComputedOutputName());
+}
+
+// Test visibility failure case.
+TEST_F(TargetTest, VisibilityFails) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY);
+ b.visibility().SetPrivate(b.label().dir());
+ ASSERT_TRUE(b.OnResolved(&err));
+
+ // Make a target depending on "b". The dependency must have an origin to mark
+ // it as user-set so we check visibility. This check should fail.
+ TestTarget a(setup, "//app:a", Target::EXECUTABLE);
+ a.private_deps().push_back(LabelTargetPair(&b));
+ IdentifierNode origin; // Dummy origin.
+ a.private_deps()[0].origin = &origin;
+ ASSERT_FALSE(a.OnResolved(&err));
+}
+
+// Test visibility with a single data_dep.
+TEST_F(TargetTest, VisibilityDatadeps) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget b(setup, "//public:b", Target::STATIC_LIBRARY);
+ ASSERT_TRUE(b.OnResolved(&err));
+
+ // Make a target depending on "b". The dependency must have an origin to mark
+ // it as user-set so we check visibility. This check should fail.
+ TestTarget a(setup, "//app:a", Target::EXECUTABLE);
+ a.data_deps().push_back(LabelTargetPair(&b));
+ IdentifierNode origin; // Dummy origin.
+ a.data_deps()[0].origin = &origin;
+ ASSERT_TRUE(a.OnResolved(&err)) << err.help_text();
+}
+
+// Tests that A -> Group -> B where the group is visible from A but B isn't,
+// passes visibility even though the group's deps get expanded into A.
+TEST_F(TargetTest, VisibilityGroup) {
+ TestWithScope setup;
+ Err err;
+
+ IdentifierNode origin; // Dummy origin.
+
+ // B has private visibility. This lets the group see it since the group is in
+ // the same directory.
+ TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY);
+ b.visibility().SetPrivate(b.label().dir());
+ ASSERT_TRUE(b.OnResolved(&err));
+
+ // The group has public visibility and depends on b.
+ TestTarget g(setup, "//public:g", Target::GROUP);
+ g.private_deps().push_back(LabelTargetPair(&b));
+ g.private_deps()[0].origin = &origin;
+ ASSERT_TRUE(b.OnResolved(&err));
+
+ // Make a target depending on "g". This should succeed.
+ TestTarget a(setup, "//app:a", Target::EXECUTABLE);
+ a.private_deps().push_back(LabelTargetPair(&g));
+ a.private_deps()[0].origin = &origin;
+ ASSERT_TRUE(a.OnResolved(&err));
+}
+
+// Verifies that only testonly targets can depend on other testonly targets.
+// Many of the above dependency checking cases covered the non-testonly
+// case.
+TEST_F(TargetTest, Testonly) {
+ TestWithScope setup;
+ Err err;
+
+ // "testlib" is a test-only library.
+ TestTarget testlib(setup, "//test:testlib", Target::STATIC_LIBRARY);
+ testlib.set_testonly(true);
+ ASSERT_TRUE(testlib.OnResolved(&err));
+
+ // "test" is a test-only executable depending on testlib, this is OK.
+ TestTarget test(setup, "//test:test", Target::EXECUTABLE);
+ test.set_testonly(true);
+ test.private_deps().push_back(LabelTargetPair(&testlib));
+ ASSERT_TRUE(test.OnResolved(&err));
+
+ // "product" is a non-test depending on testlib. This should fail.
+ TestTarget product(setup, "//app:product", Target::EXECUTABLE);
+ product.set_testonly(false);
+ product.private_deps().push_back(LabelTargetPair(&testlib));
+ ASSERT_FALSE(product.OnResolved(&err));
+}
+
+TEST_F(TargetTest, PublicConfigs) {
+ TestWithScope setup;
+ Err err;
+
+ Label pub_config_label(SourceDir("//a/"), "pubconfig");
+ Config pub_config(setup.settings(), pub_config_label);
+ pub_config.visibility().SetPublic();
+ LibFile lib_name("testlib");
+ pub_config.own_values().libs().push_back(lib_name);
+ ASSERT_TRUE(pub_config.OnResolved(&err));
+
+ // This is the destination target that has a public config.
+ TestTarget dest(setup, "//a:a", Target::SOURCE_SET);
+ dest.public_configs().push_back(LabelConfigPair(&pub_config));
+ ASSERT_TRUE(dest.OnResolved(&err));
+
+ // This target has a public dependency on dest.
+ TestTarget pub(setup, "//a:pub", Target::SOURCE_SET);
+ pub.public_deps().push_back(LabelTargetPair(&dest));
+ ASSERT_TRUE(pub.OnResolved(&err));
+
+ // Depending on the target with the public dependency should forward dest's
+ // to the current target.
+ TestTarget dep_on_pub(setup, "//a:dop", Target::SOURCE_SET);
+ dep_on_pub.private_deps().push_back(LabelTargetPair(&pub));
+ ASSERT_TRUE(dep_on_pub.OnResolved(&err));
+ ASSERT_EQ(1u, dep_on_pub.configs().size());
+ EXPECT_EQ(&pub_config, dep_on_pub.configs()[0].ptr);
+
+ // Libs have special handling, check that they were forwarded from the
+ // public config to all_libs.
+ ASSERT_EQ(1u, dep_on_pub.all_libs().size());
+ ASSERT_EQ(lib_name, dep_on_pub.all_libs()[0]);
+
+ // This target has a private dependency on dest for forwards configs.
+ TestTarget forward(setup, "//a:f", Target::SOURCE_SET);
+ forward.private_deps().push_back(LabelTargetPair(&dest));
+ ASSERT_TRUE(forward.OnResolved(&err));
+}
+
+// Tests that configs are ordered properly between local and pulled ones.
+TEST_F(TargetTest, ConfigOrdering) {
+ TestWithScope setup;
+ Err err;
+
+ // Make Dep1. It has all_dependent_configs and public_configs.
+ TestTarget dep1(setup, "//:dep1", Target::SOURCE_SET);
+ Label dep1_all_config_label(SourceDir("//"), "dep1_all_config");
+ Config dep1_all_config(setup.settings(), dep1_all_config_label);
+ dep1_all_config.visibility().SetPublic();
+ ASSERT_TRUE(dep1_all_config.OnResolved(&err));
+ dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all_config));
+
+ Label dep1_public_config_label(SourceDir("//"), "dep1_public_config");
+ Config dep1_public_config(setup.settings(), dep1_public_config_label);
+ dep1_public_config.visibility().SetPublic();
+ ASSERT_TRUE(dep1_public_config.OnResolved(&err));
+ dep1.public_configs().push_back(LabelConfigPair(&dep1_public_config));
+ ASSERT_TRUE(dep1.OnResolved(&err));
+
+ // Make Dep2 with the same structure.
+ TestTarget dep2(setup, "//:dep2", Target::SOURCE_SET);
+ Label dep2_all_config_label(SourceDir("//"), "dep2_all_config");
+ Config dep2_all_config(setup.settings(), dep2_all_config_label);
+ dep2_all_config.visibility().SetPublic();
+ ASSERT_TRUE(dep2_all_config.OnResolved(&err));
+ dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all_config));
+
+ Label dep2_public_config_label(SourceDir("//"), "dep2_public_config");
+ Config dep2_public_config(setup.settings(), dep2_public_config_label);
+ dep2_public_config.visibility().SetPublic();
+ ASSERT_TRUE(dep2_public_config.OnResolved(&err));
+ dep2.public_configs().push_back(LabelConfigPair(&dep2_public_config));
+ ASSERT_TRUE(dep2.OnResolved(&err));
+
+ // This target depends on both previous targets.
+ TestTarget target(setup, "//:foo", Target::SOURCE_SET);
+ target.private_deps().push_back(LabelTargetPair(&dep1));
+ target.private_deps().push_back(LabelTargetPair(&dep2));
+
+ // It also has a private and public config.
+ Label public_config_label(SourceDir("//"), "public");
+ Config public_config(setup.settings(), public_config_label);
+ public_config.visibility().SetPublic();
+ ASSERT_TRUE(public_config.OnResolved(&err));
+ target.public_configs().push_back(LabelConfigPair(&public_config));
+
+ Label private_config_label(SourceDir("//"), "private");
+ Config private_config(setup.settings(), private_config_label);
+ private_config.visibility().SetPublic();
+ ASSERT_TRUE(private_config.OnResolved(&err));
+ target.configs().push_back(LabelConfigPair(&private_config));
+
+ // Resolve to get the computed list of configs applying.
+ ASSERT_TRUE(target.OnResolved(&err));
+ const auto& computed = target.configs();
+
+ // Order should be:
+ // 1. local private
+ // 2. local public
+ // 3. inherited all dependent
+ // 4. inherited public
+ ASSERT_EQ(6u, computed.size());
+ EXPECT_EQ(private_config_label, computed[0].label);
+ EXPECT_EQ(public_config_label, computed[1].label);
+ EXPECT_EQ(dep1_all_config_label, computed[2].label);
+ EXPECT_EQ(dep2_all_config_label, computed[3].label);
+ EXPECT_EQ(dep1_public_config_label, computed[4].label);
+ EXPECT_EQ(dep2_public_config_label, computed[5].label);
+}
+
+// Tests that different link/depend outputs work for solink tools.
+TEST_F(TargetTest, LinkAndDepOutputs) {
+ TestWithScope setup;
+ Err err;
+
+ Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
+
+ std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+ CTool* solink_tool = solink->AsC();
+ solink_tool->set_output_prefix("lib");
+ solink_tool->set_default_output_extension(".so");
+
+ const char kLinkPattern[] =
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
+ SubstitutionPattern link_output =
+ SubstitutionPattern::MakeForTest(kLinkPattern);
+ solink_tool->set_link_output(link_output);
+
+ const char kDependPattern[] =
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC";
+ SubstitutionPattern depend_output =
+ SubstitutionPattern::MakeForTest(kDependPattern);
+ solink_tool->set_depend_output(depend_output);
+
+ solink_tool->set_outputs(
+ SubstitutionList::MakeForTest(kLinkPattern, kDependPattern));
+
+ toolchain.SetTool(std::move(solink));
+
+ Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.SetToolchain(&toolchain);
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ EXPECT_EQ("./liba.so", target.link_output_file().value());
+ EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value());
+
+ ASSERT_EQ(1u, target.runtime_outputs().size());
+ EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value());
+}
+
+// Tests that runtime_outputs works without an explicit link_output for
+// solink tools.
+//
+// Also tests GetOutputsAsSourceFiles() for binaries (the setup is the same).
+TEST_F(TargetTest, RuntimeOuputs) {
+ TestWithScope setup;
+ Err err;
+
+ Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
+
+ std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+ CTool* solink_tool = solink->AsC();
+ solink_tool->set_output_prefix("");
+ solink_tool->set_default_output_extension(".dll");
+
+ // Say the linker makes a DLL< an import library, and a symbol file we want
+ // to treat as a runtime output.
+ const char kLibPattern[] =
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib";
+ const char kDllPattern[] =
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
+ const char kPdbPattern[] = "{{root_out_dir}}/{{target_output_name}}.pdb";
+ SubstitutionPattern pdb_pattern =
+ SubstitutionPattern::MakeForTest(kPdbPattern);
+
+ solink_tool->set_outputs(
+ SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern));
+
+ // Say we only want the DLL and symbol file treaded as runtime outputs.
+ solink_tool->set_runtime_outputs(
+ SubstitutionList::MakeForTest(kDllPattern, kPdbPattern));
+
+ toolchain.SetTool(std::move(solink));
+
+ Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.SetToolchain(&toolchain);
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ EXPECT_EQ("./a.dll.lib", target.link_output_file().value());
+ EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value());
+
+ ASSERT_EQ(2u, target.runtime_outputs().size());
+ EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value());
+ EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value());
+
+ // Test GetOutputsAsSourceFiles().
+ std::vector<SourceFile> computed_outputs;
+ EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
+ &computed_outputs, &err));
+ ASSERT_EQ(3u, computed_outputs.size());
+ EXPECT_EQ("//out/Debug/a.dll.lib", computed_outputs[0].value());
+ EXPECT_EQ("//out/Debug/a.dll", computed_outputs[1].value());
+ EXPECT_EQ("//out/Debug/a.pdb", computed_outputs[2].value());
+}
+
+// Tests Target::GetOutputFilesForSource for binary targets (these require a
+// tool definition). Also tests GetOutputsAsSourceFiles() for source sets.
+TEST_F(TargetTest, GetOutputFilesForSource_Binary) {
+ TestWithScope setup;
+
+ Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
+
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxx);
+ CTool* cxx = tool->AsC();
+ cxx->set_outputs(SubstitutionList::MakeForTest("{{source_file_part}}.o"));
+ toolchain.SetTool(std::move(tool));
+
+ Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.SetToolchain(&toolchain);
+ Err err;
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ const char* computed_tool_type = nullptr;
+ std::vector<OutputFile> output;
+ bool result = target.GetOutputFilesForSource(SourceFile("//source/input.cc"),
+ &computed_tool_type, &output);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(std::string("cxx"), std::string(computed_tool_type));
+
+ // Outputs are relative to the build directory "//out/Debug/".
+ ASSERT_EQ(1u, output.size());
+ EXPECT_EQ("input.cc.o", output[0].value()) << output[0].value();
+
+ // Test GetOutputsAsSourceFiles(). Since this is a source set it should give a
+ // stamp file.
+ std::vector<SourceFile> computed_outputs;
+ EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
+ &computed_outputs, &err));
+ ASSERT_EQ(1u, computed_outputs.size());
+ EXPECT_EQ("//out/Debug/obj/a/a.stamp", computed_outputs[0].value());
+}
+
+// Tests Target::GetOutputFilesForSource for action_foreach targets (these, like
+// copy targets, apply a pattern to the source file). Also tests
+// GetOutputsAsSourceFiles() for action_foreach().
+TEST_F(TargetTest, GetOutputFilesForSource_ActionForEach) {
+ TestWithScope setup;
+
+ TestTarget target(setup, "//a:a", Target::ACTION_FOREACH);
+ target.sources().push_back(SourceFile("//a/source_file1.txt"));
+ target.sources().push_back(SourceFile("//a/source_file2.txt"));
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one",
+ "//out/Debug/{{source_file_part}}.two");
+ Err err;
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ const char* computed_tool_type = nullptr;
+ std::vector<OutputFile> output;
+ bool result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"),
+ &computed_tool_type, &output);
+ ASSERT_TRUE(result);
+
+ // Outputs are relative to the build directory "//out/Debug/".
+ ASSERT_EQ(2u, output.size());
+ EXPECT_EQ("input.txt.one", output[0].value());
+ EXPECT_EQ("input.txt.two", output[1].value());
+
+ // Test GetOutputsAsSourceFiles(). It should give both outputs for each of the
+ // two inputs.
+ std::vector<SourceFile> computed_outputs;
+ EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
+ &computed_outputs, &err));
+ ASSERT_EQ(4u, computed_outputs.size());
+ EXPECT_EQ("//out/Debug/source_file1.txt.one", computed_outputs[0].value());
+ EXPECT_EQ("//out/Debug/source_file1.txt.two", computed_outputs[1].value());
+ EXPECT_EQ("//out/Debug/source_file2.txt.one", computed_outputs[2].value());
+ EXPECT_EQ("//out/Debug/source_file2.txt.two", computed_outputs[3].value());
+}
+
+// Tests Target::GetOutputFilesForSource for action targets (these just list the
+// output of the action as the result of all possible inputs). This should also
+// cover everything other than binary, action_foreach, and copy targets.
+TEST_F(TargetTest, GetOutputFilesForSource_Action) {
+ TestWithScope setup;
+
+ TestTarget target(setup, "//a:a", Target::ACTION);
+ target.sources().push_back(SourceFile("//a/source_file1.txt"));
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/one", "//out/Debug/two");
+ Err err;
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ const char* computed_tool_type = nullptr;
+ std::vector<OutputFile> output;
+ bool result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"),
+ &computed_tool_type, &output);
+ ASSERT_TRUE(result);
+
+ // Outputs are relative to the build directory "//out/Debug/".
+ ASSERT_EQ(2u, output.size());
+ EXPECT_EQ("one", output[0].value());
+ EXPECT_EQ("two", output[1].value());
+
+ // Test GetOutputsAsSourceFiles(). It should give the listed outputs.
+ std::vector<SourceFile> computed_outputs;
+ EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
+ &computed_outputs, &err));
+ ASSERT_EQ(2u, computed_outputs.size());
+ EXPECT_EQ("//out/Debug/one", computed_outputs[0].value());
+ EXPECT_EQ("//out/Debug/two", computed_outputs[1].value());
+
+ // Test that the copy target type behaves the same. This target requires only
+ // one output.
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/one");
+ target.set_output_type(Target::COPY_FILES);
+
+ // Outputs are relative to the build directory "//out/Debug/".
+ result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"),
+ &computed_tool_type, &output);
+ ASSERT_TRUE(result);
+ ASSERT_EQ(1u, output.size());
+ EXPECT_EQ("one", output[0].value());
+
+ // Test GetOutputsAsSourceFiles() for the copy case.
+ EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
+ &computed_outputs, &err));
+ ASSERT_EQ(1u, computed_outputs.size()) << computed_outputs.size();
+ EXPECT_EQ("//out/Debug/one", computed_outputs[0].value());
+}
+
+// Shared libraries should be inherited across public shared library
+// boundaries.
+TEST_F(TargetTest, SharedInheritance) {
+ TestWithScope setup;
+ Err err;
+
+ // Create two leaf shared libraries.
+ TestTarget pub(setup, "//foo:pub", Target::SHARED_LIBRARY);
+ ASSERT_TRUE(pub.OnResolved(&err));
+
+ TestTarget priv(setup, "//foo:priv", Target::SHARED_LIBRARY);
+ ASSERT_TRUE(priv.OnResolved(&err));
+
+ // Intermediate shared library with the leaf shared libraries as
+ // dependencies, one public, one private.
+ TestTarget inter(setup, "//foo:inter", Target::SHARED_LIBRARY);
+ inter.public_deps().push_back(LabelTargetPair(&pub));
+ inter.private_deps().push_back(LabelTargetPair(&priv));
+ ASSERT_TRUE(inter.OnResolved(&err));
+
+ // The intermediate shared library should have both "pub" and "priv" in its
+ // inherited libraries.
+ std::vector<const Target*> inter_inherited =
+ inter.inherited_libraries().GetOrdered();
+ ASSERT_EQ(2u, inter_inherited.size());
+ EXPECT_EQ(&pub, inter_inherited[0]);
+ EXPECT_EQ(&priv, inter_inherited[1]);
+
+ // Make a toplevel executable target depending on the intermediate one.
+ TestTarget exe(setup, "//foo:exe", Target::SHARED_LIBRARY);
+ exe.private_deps().push_back(LabelTargetPair(&inter));
+ ASSERT_TRUE(exe.OnResolved(&err));
+
+ // The exe's inherited libraries should be "inter" (because it depended
+ // directly on it) and "pub" (because inter depended publicly on it).
+ std::vector<const Target*> exe_inherited =
+ exe.inherited_libraries().GetOrdered();
+ ASSERT_EQ(2u, exe_inherited.size());
+ EXPECT_EQ(&inter, exe_inherited[0]);
+ EXPECT_EQ(&pub, exe_inherited[1]);
+}
+
+TEST_F(TargetTest, GeneratedInputs) {
+ TestWithScope setup;
+ Err err;
+
+ SourceFile generated_file("//out/Debug/generated.cc");
+
+ // This target has a generated input and no dependency makes it.
+ TestTarget non_existent_generator(setup, "//foo:non_existent_generator",
+ Target::EXECUTABLE);
+ non_existent_generator.sources().push_back(generated_file);
+ EXPECT_TRUE(non_existent_generator.OnResolved(&err)) << err.message();
+ AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator,
+ generated_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // Make a target that generates the file.
+ TestTarget generator(setup, "//foo:generator", Target::ACTION);
+ generator.action_values().outputs() =
+ SubstitutionList::MakeForTest(generated_file.value().c_str());
+ err = Err();
+ EXPECT_TRUE(generator.OnResolved(&err)) << err.message();
+
+ // A target that depends on the generator that uses the file as a source
+ // should be OK. This uses a private dep (will be used later).
+ TestTarget existent_generator(setup, "//foo:existent_generator",
+ Target::SHARED_LIBRARY);
+ existent_generator.sources().push_back(generated_file);
+ existent_generator.private_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(existent_generator.OnResolved(&err)) << err.message();
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+
+ // A target that depends on the previous one should *not* be allowed to
+ // use the generated file, because existent_generator used private deps.
+ // This is:
+ // indirect_private --> existent_generator --[private]--> generator
+ TestTarget indirect_private(setup, "//foo:indirect_private",
+ Target::EXECUTABLE);
+ indirect_private.sources().push_back(generated_file);
+ indirect_private.public_deps().push_back(
+ LabelTargetPair(&existent_generator));
+ EXPECT_TRUE(indirect_private.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&indirect_private, generated_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // Now make a chain like the above but with all public deps, it should be OK.
+ TestTarget existent_public(setup, "//foo:existent_public",
+ Target::SHARED_LIBRARY);
+ existent_public.public_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(existent_public.OnResolved(&err)) << err.message();
+ TestTarget indirect_public(setup, "//foo:indirect_public",
+ Target::EXECUTABLE);
+ indirect_public.sources().push_back(generated_file);
+ indirect_public.public_deps().push_back(LabelTargetPair(&existent_public));
+ EXPECT_TRUE(indirect_public.OnResolved(&err)) << err.message();
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+}
+
+// This is sort of a Scheduler test, but is related to the above test more.
+TEST_F(TargetTest, WriteFileGeneratedInputs) {
+ TestWithScope setup;
+ Err err;
+
+ SourceFile generated_file("//out/Debug/generated.data");
+
+ // This target has a generated input and no dependency makes it.
+ TestTarget non_existent_generator(setup, "//foo:non_existent_generator",
+ Target::EXECUTABLE);
+ non_existent_generator.sources().push_back(generated_file);
+ EXPECT_TRUE(non_existent_generator.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator,
+ generated_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've decared we write it.
+ TestTarget existent_generator(setup, "//foo:existent_generator",
+ Target::EXECUTABLE);
+ existent_generator.sources().push_back(generated_file);
+ EXPECT_TRUE(existent_generator.OnResolved(&err));
+ scheduler().AddWrittenFile(generated_file);
+
+ // Should be OK.
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+}
+
+TEST_F(TargetTest, WriteRuntimeDepsGeneratedInputs) {
+ TestWithScope setup;
+ Err err;
+
+ SourceFile source_file("//out/Debug/generated.runtime_deps");
+ OutputFile output_file(setup.build_settings(), source_file);
+
+ TestTarget generator(setup, "//foo:generator", Target::EXECUTABLE);
+ generator.set_write_runtime_deps_output(output_file);
+ g_scheduler->AddWriteRuntimeDepsTarget(&generator);
+
+ TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
+ middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));
+
+ // This target has a generated input and no dependency makes it.
+ TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
+ dep_missing.sources().push_back(source_file);
+ EXPECT_TRUE(dep_missing.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've directly dependended on it.
+ TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
+ dep_present.sources().push_back(source_file);
+ dep_present.private_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(dep_present.OnResolved(&err));
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+
+ // This target has a generated file and we've indirectly dependended on it
+ // via data_deps.
+ TestTarget dep_indirect(setup, "//foo:with_dep", Target::EXECUTABLE);
+ dep_indirect.sources().push_back(source_file);
+ dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
+ EXPECT_TRUE(dep_indirect.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've directly dependended on it
+ // via data_deps.
+ TestTarget data_dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
+ data_dep_present.sources().push_back(source_file);
+ data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(data_dep_present.OnResolved(&err));
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+}
+
+// Tests that intermediate object files generated by binary targets are also
+// considered generated for the purposes of input checking. Above, we tested
+// the failure cases for generated inputs, so here only test .o files that are
+// present.
+TEST_F(TargetTest, ObjectGeneratedInputs) {
+ TestWithScope setup;
+ Err err;
+
+ // This target compiles the source.
+ SourceFile source_file("//source.cc");
+ TestTarget source_generator(setup, "//:source_target", Target::SOURCE_SET);
+ source_generator.sources().push_back(source_file);
+ EXPECT_TRUE(source_generator.OnResolved(&err));
+
+ // This is the object file that the test toolchain generates for the source.
+ SourceFile object_file("//out/Debug/obj/source_target.source.o");
+
+ TestTarget final_target(setup, "//:final", Target::ACTION);
+ final_target.config_values().inputs().push_back(object_file);
+ EXPECT_TRUE(final_target.OnResolved(&err));
+
+ AssertSchedulerHasOneUnknownFileMatching(&final_target, object_file);
+}
+
+TEST_F(TargetTest, ResolvePrecompiledHeaders) {
+ TestWithScope setup;
+ Err err;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+
+ // Target with no settings, no configs, should be a no-op.
+ EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
+
+ // Config with PCH values.
+ Config config_1(setup.settings(), Label(SourceDir("//foo/"), "c1"));
+ std::string pch_1("pch.h");
+ SourceFile pcs_1("//pcs.cc");
+ config_1.own_values().set_precompiled_header(pch_1);
+ config_1.own_values().set_precompiled_source(pcs_1);
+ ASSERT_TRUE(config_1.OnResolved(&err));
+ target.configs().push_back(LabelConfigPair(&config_1));
+
+ // No PCH info specified on TargetTest, but the config specifies one, the
+ // values should get copied to the target.
+ EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
+ EXPECT_EQ(pch_1, target.config_values().precompiled_header());
+ EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);
+
+ // Now both target and config have matching PCH values. Resolving again
+ // should be a no-op since they all match.
+ EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
+ EXPECT_TRUE(target.config_values().precompiled_header() == pch_1);
+ EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);
+
+ // Second config with different PCH values.
+ Config config_2(setup.settings(), Label(SourceDir("//foo/"), "c2"));
+ std::string pch_2("pch2.h");
+ SourceFile pcs_2("//pcs2.cc");
+ config_2.own_values().set_precompiled_header(pch_2);
+ config_2.own_values().set_precompiled_source(pcs_2);
+ ASSERT_TRUE(config_2.OnResolved(&err));
+ target.configs().push_back(LabelConfigPair(&config_2));
+
+ // This should be an error since they don't match.
+ EXPECT_FALSE(target.ResolvePrecompiledHeaders(&err));
+
+ // Make sure the proper labels are blamed.
+ EXPECT_EQ(
+ "The target //foo:bar\n"
+ "has conflicting precompiled header settings.\n"
+ "\n"
+ "From //foo:bar\n"
+ " header: pch.h\n"
+ " source: //pcs.cc\n"
+ "\n"
+ "From //foo:c2\n"
+ " header: pch2.h\n"
+ " source: //pcs2.cc",
+ err.help_text());
+}
+
+TEST_F(TargetTest, AssertNoDeps) {
+ TestWithScope setup;
+ Err err;
+
+ // A target.
+ TestTarget a(setup, "//a", Target::SHARED_LIBRARY);
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // B depends on A and has an assert_no_deps for a random dir.
+ TestTarget b(setup, "//b", Target::SHARED_LIBRARY);
+ b.private_deps().push_back(LabelTargetPair(&a));
+ b.assert_no_deps().push_back(LabelPattern(LabelPattern::RECURSIVE_DIRECTORY,
+ SourceDir("//disallowed/"),
+ std::string(), Label()));
+ ASSERT_TRUE(b.OnResolved(&err));
+
+ LabelPattern disallow_a(LabelPattern::RECURSIVE_DIRECTORY, SourceDir("//a/"),
+ std::string(), Label());
+
+ // C depends on B and disallows depending on A. This should fail.
+ TestTarget c(setup, "//c", Target::EXECUTABLE);
+ c.private_deps().push_back(LabelTargetPair(&b));
+ c.assert_no_deps().push_back(disallow_a);
+ ASSERT_FALSE(c.OnResolved(&err));
+
+ // Validate the error message has the proper path.
+ EXPECT_EQ(
+ "//c:c has an assert_no_deps entry:\n"
+ " //a/*\n"
+ "which fails for the dependency path:\n"
+ " //c:c ->\n"
+ " //b:b ->\n"
+ " //a:a",
+ err.help_text());
+ err = Err();
+
+ // Add an intermediate executable with: exe -> b -> a
+ TestTarget exe(setup, "//exe", Target::EXECUTABLE);
+ exe.private_deps().push_back(LabelTargetPair(&b));
+ ASSERT_TRUE(exe.OnResolved(&err));
+
+ // D depends on the executable and disallows depending on A. Since there is
+ // an intermediate executable, this should be OK.
+ TestTarget d(setup, "//d", Target::EXECUTABLE);
+ d.private_deps().push_back(LabelTargetPair(&exe));
+ d.assert_no_deps().push_back(disallow_a);
+ ASSERT_TRUE(d.OnResolved(&err));
+
+ // A2 disallows depending on anything in its own directory, but the
+ // assertions should not match the target itself so this should be OK.
+ TestTarget a2(setup, "//a:a2", Target::EXECUTABLE);
+ a2.assert_no_deps().push_back(disallow_a);
+ ASSERT_TRUE(a2.OnResolved(&err));
+}
+
+TEST_F(TargetTest, PullRecursiveBundleData) {
+ TestWithScope setup;
+ Err err;
+
+ // We have the following dependency graph:
+ // A (create_bundle) -> B (bundle_data)
+ // \-> C (create_bundle) -> D (bundle_data)
+ // \-> E (group) -> F (bundle_data)
+ // \-> B (bundle_data)
+ TestTarget a(setup, "//foo:a", Target::CREATE_BUNDLE);
+ TestTarget b(setup, "//foo:b", Target::BUNDLE_DATA);
+ TestTarget c(setup, "//foo:c", Target::CREATE_BUNDLE);
+ TestTarget d(setup, "//foo:d", Target::BUNDLE_DATA);
+ TestTarget e(setup, "//foo:e", Target::GROUP);
+ TestTarget f(setup, "//foo:f", Target::BUNDLE_DATA);
+ a.public_deps().push_back(LabelTargetPair(&b));
+ a.public_deps().push_back(LabelTargetPair(&c));
+ a.public_deps().push_back(LabelTargetPair(&e));
+ c.public_deps().push_back(LabelTargetPair(&d));
+ e.public_deps().push_back(LabelTargetPair(&f));
+ e.public_deps().push_back(LabelTargetPair(&b));
+
+ a.bundle_data().root_dir() = SourceDir("//out/foo_a.bundle");
+ a.bundle_data().resources_dir() = SourceDir("//out/foo_a.bundle/Resources");
+
+ b.sources().push_back(SourceFile("//foo/b1.txt"));
+ b.sources().push_back(SourceFile("//foo/b2.txt"));
+ b.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ ASSERT_TRUE(b.OnResolved(&err));
+
+ c.bundle_data().root_dir() = SourceDir("//out/foo_c.bundle");
+ c.bundle_data().resources_dir() = SourceDir("//out/foo_c.bundle/Resources");
+
+ d.sources().push_back(SourceFile("//foo/d.txt"));
+ d.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ ASSERT_TRUE(d.OnResolved(&err));
+
+ f.sources().push_back(SourceFile("//foo/f1.txt"));
+ f.sources().push_back(SourceFile("//foo/f2.txt"));
+ f.sources().push_back(SourceFile("//foo/f3.txt"));
+ f.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
+ f.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29.png"));
+ f.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@2x.png"));
+ f.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@3x.png"));
+ f.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ ASSERT_TRUE(f.OnResolved(&err));
+
+ ASSERT_TRUE(e.OnResolved(&err));
+ ASSERT_TRUE(c.OnResolved(&err));
+ ASSERT_TRUE(a.OnResolved(&err));
+
+ // A gets its data from B and F.
+ ASSERT_EQ(a.bundle_data().file_rules().size(), 2u);
+ ASSERT_EQ(a.bundle_data().file_rules()[0].sources().size(), 2u);
+ ASSERT_EQ(a.bundle_data().file_rules()[1].sources().size(), 3u);
+ ASSERT_EQ(a.bundle_data().assets_catalog_sources().size(), 1u);
+ ASSERT_EQ(a.bundle_data().bundle_deps().size(), 2u);
+
+ // C gets its data from D.
+ ASSERT_EQ(c.bundle_data().file_rules().size(), 1u);
+ ASSERT_EQ(c.bundle_data().file_rules()[0].sources().size(), 1u);
+ ASSERT_EQ(c.bundle_data().bundle_deps().size(), 1u);
+
+ // E does not have any bundle_data information but gets a list of
+ // bundle_deps to propagate them during target resolution.
+ ASSERT_TRUE(e.bundle_data().file_rules().empty());
+ ASSERT_TRUE(e.bundle_data().assets_catalog_sources().empty());
+ ASSERT_EQ(e.bundle_data().bundle_deps().size(), 2u);
+}
+
+TEST(TargetTest, CollectMetadataNoRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("b", b_expected));
+
+ one.metadata().set_source_dir(SourceDir("/usr/home/files/"));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ EXPECT_EQ(result, expected);
+}
+
+TEST(TargetTest, CollectMetadataWithRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("b", b_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_2_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ EXPECT_EQ(result, expected);
+}
+
+TEST(TargetTest, CollectMetadataWithBarrier) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(Value(nullptr, "two"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("walk", walk_expected));
+
+ TestTarget two(setup, "//foo/two:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_2_expected));
+
+ TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
+ Value a_3_expected(nullptr, Value::LIST);
+ a_3_expected.list_value().push_back(Value(nullptr, "baz"));
+ three.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_3_expected));
+
+ one.private_deps().push_back(LabelTargetPair(&two));
+ one.public_deps().push_back(LabelTargetPair(&three));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_FALSE(err.has_error()) << err.message();
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ EXPECT_EQ(result, expected) << result.size();
+}
+
+TEST(TargetTest, CollectMetadataWithError) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
+ one.metadata().contents().insert(
+ std::pair<std::string_view, Value>("walk", walk_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.message(),
+ "I was expecting //foo:missing(//toolchain:default) to be a "
+ "dependency of //foo:one(//toolchain:default). "
+ "Make sure it's included in the deps or data_deps, and that you've "
+ "specified the appropriate toolchain.")
+ << err.message();
+}
+
+TEST_F(TargetTest, WriteMetadataCollection) {
+ TestWithScope setup;
+ Err err;
+
+ SourceFile source_file("//out/Debug/metadata.json");
+ OutputFile output_file(setup.build_settings(), source_file);
+
+ TestTarget generator(setup, "//foo:write", Target::GENERATED_FILE);
+ generator.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/metadata.json");
+ EXPECT_TRUE(generator.OnResolved(&err));
+
+ TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
+ middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(middle_data_dep.OnResolved(&err));
+
+ // This target has a generated metadata input and no dependency makes it.
+ TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
+ dep_missing.sources().push_back(source_file);
+ EXPECT_TRUE(dep_missing.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've directly dependended on it.
+ TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
+ dep_present.sources().push_back(source_file);
+ dep_present.private_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(dep_present.OnResolved(&err));
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+
+ // This target has a generated file and we've indirectly dependended on it
+ // via data_deps.
+ TestTarget dep_indirect(setup, "//foo:indirect_dep", Target::EXECUTABLE);
+ dep_indirect.sources().push_back(source_file);
+ dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
+ EXPECT_TRUE(dep_indirect.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've directly dependended on it
+ // via data_deps.
+ TestTarget data_dep_present(setup, "//foo:with_data_dep", Target::EXECUTABLE);
+ data_dep_present.sources().push_back(source_file);
+ data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(data_dep_present.OnResolved(&err));
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+}
+
+// Tests that modulemap files use the cxx_module tool.
+TEST_F(TargetTest, ModuleMap) {
+ TestWithScope setup;
+
+ Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
+
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxxModule);
+ CTool* cxx_module = tool->AsC();
+ cxx_module->set_outputs(
+ SubstitutionList::MakeForTest("{{source_file_part}}.pcm"));
+ toolchain.SetTool(std::move(tool));
+
+ Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.SetToolchain(&toolchain);
+ Err err;
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ const char* computed_tool_type = nullptr;
+ std::vector<OutputFile> output;
+ bool result = target.GetOutputFilesForSource(
+ SourceFile("//source/input.modulemap"), &computed_tool_type, &output);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(std::string("cxx_module"), std::string(computed_tool_type));
+
+ // Outputs are relative to the build directory "//out/Debug/".
+ ASSERT_EQ(1u, output.size());
+ EXPECT_EQ("input.modulemap.pcm", output[0].value()) << output[0].value();
+}
diff --git a/gn/src/gn/template.cc b/gn/src/gn/template.cc
new file mode 100644
index 00000000000..d5b2ecd4c88
--- /dev/null
+++ b/gn/src/gn/template.cc
@@ -0,0 +1,128 @@
+// 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 "gn/template.h"
+
+#include <memory>
+#include <utility>
+
+#include "gn/err.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/scope_per_file_provider.h"
+#include "gn/value.h"
+#include "gn/variables.h"
+
+Template::Template(const Scope* scope, const FunctionCallNode* def)
+ : closure_(scope->MakeClosure()), definition_(def) {}
+
+Template::Template(std::unique_ptr<Scope> scope, const FunctionCallNode* def)
+ : closure_(std::move(scope)), definition_(def) {}
+
+Template::~Template() = default;
+
+Value Template::Invoke(Scope* scope,
+ const FunctionCallNode* invocation,
+ const std::string& template_name,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) const {
+ // Don't allow templates to be executed from imported files. Imports are for
+ // simple values only.
+ if (!EnsureNotProcessingImport(invocation, scope, err))
+ return Value();
+
+ // First run the invocation's block. Need to allocate the scope on the heap
+ // so we can pass ownership to the template.
+ std::unique_ptr<Scope> invocation_scope = std::make_unique<Scope>(scope);
+ if (!FillTargetBlockScope(scope, invocation, template_name, block, args,
+ invocation_scope.get(), err))
+ return Value();
+
+ {
+ // Don't allow the block of the template invocation to include other
+ // targets configs, or template invocations. This must only be applied
+ // to the invoker's block rather than the whole function because the
+ // template execution itself must be able to define targets, etc.
+ NonNestableBlock non_nestable(scope, invocation, "template invocation");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ block->Execute(invocation_scope.get(), err);
+ if (err->has_error())
+ return Value();
+ }
+
+ // Set up the scope to run the template and set the current directory for the
+ // template (which ScopePerFileProvider uses to base the target-related
+ // variables target_gen_dir and target_out_dir on) to be that of the invoker.
+ // This way, files don't have to be rebased and target_*_dir works the way
+ // people expect (otherwise its to easy to be putting generated files in the
+ // gen dir corresponding to an imported file).
+ Scope template_scope(closure_.get());
+ template_scope.set_source_dir(scope->GetSourceDir());
+
+ // Propogate build dependency files from invoker scope (template scope already
+ // propagated via parent scope).
+ template_scope.AddBuildDependencyFiles(
+ invocation_scope->build_dependency_files());
+
+ ScopePerFileProvider per_file_provider(&template_scope, true);
+
+ // Targets defined in the template go in the collector for the invoking file.
+ template_scope.set_item_collector(scope->GetItemCollector());
+
+ // We jump through some hoops to avoid copying the invocation scope when
+ // setting it in the template scope (since the invocation scope may have
+ // large lists of source files in it and could be expensive to copy).
+ //
+ // Scope.SetValue will copy the value which will in turn copy the scope, but
+ // if we instead create a value and then set the scope on it, the copy can
+ // be avoided.
+ template_scope.SetValue(variables::kInvoker,
+ Value(nullptr, std::unique_ptr<Scope>()), invocation);
+ Value* invoker_value = template_scope.GetMutableValue(
+ variables::kInvoker, Scope::SEARCH_NESTED, false);
+ invoker_value->SetScopeValue(std::move(invocation_scope));
+ template_scope.set_source_dir(scope->GetSourceDir());
+
+ const std::string_view target_name(variables::kTargetName);
+ template_scope.SetValue(
+ target_name, Value(invocation, args[0].string_value()), invocation);
+
+ // Actually run the template code.
+ Value result = definition_->block()->Execute(&template_scope, err);
+ if (err->has_error()) {
+ // If there was an error, append the caller location so the error message
+ // displays a stack trace of how it got here.
+ err->AppendSubErr(Err(invocation, "whence it was called."));
+ return Value();
+ }
+
+ // Check for unused variables in the invocation scope. This will find typos
+ // of things the caller meant to pass to the template but the template didn't
+ // read out.
+ //
+ // This is a bit tricky because it's theoretically possible for the template
+ // to overwrite the value of "invoker" and free the Scope owned by the
+ // value. So we need to look it up again and don't do anything if it doesn't
+ // exist.
+ invoker_value = template_scope.GetMutableValue(variables::kInvoker,
+ Scope::SEARCH_NESTED, false);
+ if (invoker_value && invoker_value->type() == Value::SCOPE) {
+ if (!invoker_value->scope_value()->CheckForUnusedVars(err))
+ return Value();
+ }
+
+ // Check for unused variables in the template itself.
+ if (!template_scope.CheckForUnusedVars(err))
+ return Value();
+
+ return result;
+}
+
+LocationRange Template::GetDefinitionRange() const {
+ return definition_->GetRange();
+}
diff --git a/gn/tools/gn/template.h b/gn/src/gn/template.h
index 6386ba9f2ba..6386ba9f2ba 100644
--- a/gn/tools/gn/template.h
+++ b/gn/src/gn/template.h
diff --git a/gn/src/gn/template_unittest.cc b/gn/src/gn/template_unittest.cc
new file mode 100644
index 00000000000..a8c88dc845f
--- /dev/null
+++ b/gn/src/gn/template_unittest.cc
@@ -0,0 +1,93 @@
+// 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 "base/strings/string_number_conversions.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(Template, Basic) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " print(invoker.bar)\n"
+ "}\n"
+ "foo(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("lala\n42\n", setup.print_output());
+}
+
+TEST(Template, UnusedTargetNameShouldThrowError) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(invoker.bar)\n"
+ "}\n"
+ "foo(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(Template, UnusedInvokerShouldThrowError) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ "}\n"
+ "foo(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(Template, UnusedVarInInvokerShouldThrowError) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " print(invoker.bar)\n"
+ "}\n"
+ "foo(\"lala\") {\n"
+ " bar = 42\n"
+ " baz = [ \"foo\" ]\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+// Previous versions of the template implementation would copy templates by
+// value when it makes a closure. Doing a sequence of them means that every new
+// one copies all previous ones, which gives a significant blow-up in memory.
+// If this test doesn't crash with out-of-memory, it passed.
+TEST(Template, MemoryBlowUp) {
+ TestWithScope setup;
+ std::string code;
+ for (int i = 0; i < 100; i++)
+ code += "template(\"test" + base::IntToString(i) + "\") {}\n";
+
+ TestParseInput input(code);
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(input.has_error());
+}
diff --git a/gn/src/gn/test_with_scheduler.cc b/gn/src/gn/test_with_scheduler.cc
new file mode 100644
index 00000000000..8022c5a3a32
--- /dev/null
+++ b/gn/src/gn/test_with_scheduler.cc
@@ -0,0 +1,8 @@
+// Copyright 2018 The Chromium 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 "gn/test_with_scheduler.h"
+
+TestWithScheduler::TestWithScheduler() = default;
+TestWithScheduler::~TestWithScheduler() = default;
diff --git a/gn/src/gn/test_with_scheduler.h b/gn/src/gn/test_with_scheduler.h
new file mode 100644
index 00000000000..4ae3697439b
--- /dev/null
+++ b/gn/src/gn/test_with_scheduler.h
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium 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 TOOLS_GN_TEST_WITH_SCHEDULER_H_
+#define TOOLS_GN_TEST_WITH_SCHEDULER_H_
+
+#include "base/macros.h"
+#include "gn/scheduler.h"
+#include "util/msg_loop.h"
+#include "util/test/test.h"
+
+class TestWithScheduler : public testing::Test {
+ protected:
+ TestWithScheduler();
+ ~TestWithScheduler() override;
+
+ Scheduler& scheduler() { return scheduler_; }
+
+ private:
+ MsgLoop run_loop_;
+ Scheduler scheduler_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWithScheduler);
+};
+
+#endif // TOOLS_GN_TEST_WITH_SCHEDULER_H_
diff --git a/gn/src/gn/test_with_scope.cc b/gn/src/gn/test_with_scope.cc
new file mode 100644
index 00000000000..583eebb525c
--- /dev/null
+++ b/gn/src/gn/test_with_scope.cc
@@ -0,0 +1,349 @@
+// 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 "gn/test_with_scope.h"
+
+#include <memory>
+#include <utility>
+
+#include "gn/parser.h"
+#include "gn/tokenizer.h"
+
+namespace {
+
+BuildSettings CreateBuildSettingsForTest() {
+ BuildSettings build_settings;
+ build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+ return build_settings;
+}
+
+} // namespace
+
+TestWithScope::TestWithScope()
+ : build_settings_(CreateBuildSettingsForTest()),
+ settings_(&build_settings_, std::string()),
+ toolchain_(&settings_, Label(SourceDir("//toolchain/"), "default")),
+ scope_(&settings_),
+ scope_progammatic_provider_(&scope_, true) {
+ build_settings_.set_print_callback(
+ [this](const std::string& str) { AppendPrintOutput(str); });
+
+ settings_.set_toolchain_label(toolchain_.label());
+ settings_.set_default_toolchain_label(toolchain_.label());
+
+ SetupToolchain(&toolchain_);
+ scope_.set_item_collector(&items_);
+}
+
+TestWithScope::~TestWithScope() = default;
+
+Label TestWithScope::ParseLabel(const std::string& str) const {
+ Err err;
+ Label result = Label::Resolve(SourceDir("//"), std::string_view(),
+ toolchain_.label(), Value(nullptr, str), &err);
+ CHECK(!err.has_error());
+ return result;
+}
+
+bool TestWithScope::ExecuteSnippet(const std::string& str, Err* err) {
+ TestParseInput input(str);
+ if (input.has_error()) {
+ *err = input.parse_err();
+ return false;
+ }
+
+ size_t first_item = items_.size();
+ input.parsed()->Execute(&scope_, err);
+ if (err->has_error())
+ return false;
+
+ for (size_t i = first_item; i < items_.size(); ++i) {
+ CHECK(items_[i]->AsTarget() != nullptr)
+ << "Only targets are supported in ExecuteSnippet()";
+ items_[i]->AsTarget()->SetToolchain(&toolchain_);
+ if (!items_[i]->OnResolved(err))
+ return false;
+ }
+ return true;
+}
+
+Value TestWithScope::ExecuteExpression(const std::string& expr, Err* err) {
+ InputFile input_file(SourceFile("//test"));
+ input_file.SetContents(expr);
+
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
+ if (err->has_error()) {
+ return Value();
+ }
+ std::unique_ptr<ParseNode> node = Parser::ParseExpression(tokens, err);
+ if (err->has_error()) {
+ return Value();
+ }
+
+ return node->Execute(&scope_, err);
+}
+
+// static
+void TestWithScope::SetupToolchain(Toolchain* toolchain, bool use_toc) {
+ Err err;
+
+ // CC
+ std::unique_ptr<Tool> cc_tool = Tool::CreateTool(CTool::kCToolCc);
+ SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool.get());
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ toolchain->SetTool(std::move(cc_tool));
+
+ // CXX
+ std::unique_ptr<Tool> cxx_tool = Tool::CreateTool(CTool::kCToolCxx);
+ SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool.get());
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_command_launcher("launcher");
+ toolchain->SetTool(std::move(cxx_tool));
+
+ // OBJC
+ std::unique_ptr<Tool> objc_tool = Tool::CreateTool(CTool::kCToolObjC);
+ SetCommandForTool(
+ "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} "
+ "{{include_dirs}} -o {{output}}",
+ objc_tool.get());
+ objc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ toolchain->SetTool(std::move(objc_tool));
+
+ // OBJC
+ std::unique_ptr<Tool> objcxx_tool = Tool::CreateTool(CTool::kCToolObjCxx);
+ SetCommandForTool(
+ "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} "
+ "{{include_dirs}} -o {{output}}",
+ objcxx_tool.get());
+ objcxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ toolchain->SetTool(std::move(objcxx_tool));
+
+ // Don't use RC and ASM tools in unit tests yet. Add here if needed.
+
+ // ALINK
+ std::unique_ptr<Tool> alink = Tool::CreateTool(CTool::kCToolAlink);
+ CTool* alink_tool = alink->AsC();
+ SetCommandForTool("ar {{output}} {{source}}", alink_tool);
+ alink_tool->set_lib_switch("-l");
+ alink_tool->set_lib_dir_switch("-L");
+ alink_tool->set_output_prefix("lib");
+ alink_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{target_output_name}}.a"));
+ toolchain->SetTool(std::move(alink));
+
+ // SOLINK
+ std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+ CTool* solink_tool = solink->AsC();
+ SetCommandForTool(
+ "ld -shared -o {{target_output_name}}.so {{inputs}} "
+ "{{ldflags}} {{libs}}",
+ solink_tool);
+ solink_tool->set_lib_switch("-l");
+ solink_tool->set_lib_dir_switch("-L");
+ solink_tool->set_output_prefix("lib");
+ solink_tool->set_default_output_extension(".so");
+ if (use_toc) {
+ solink_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC",
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ solink_tool->set_link_output(SubstitutionPattern::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ solink_tool->set_depend_output(SubstitutionPattern::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC"));
+ } else {
+ solink_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ }
+ toolchain->SetTool(std::move(solink));
+
+ // SOLINK_MODULE
+ std::unique_ptr<Tool> solink_module =
+ Tool::CreateTool(CTool::kCToolSolinkModule);
+ CTool* solink_module_tool = solink_module->AsC();
+ SetCommandForTool(
+ "ld -bundle -o {{target_output_name}}.so {{inputs}} "
+ "{{ldflags}} {{libs}}",
+ solink_module_tool);
+ solink_module_tool->set_lib_switch("-l");
+ solink_module_tool->set_lib_dir_switch("-L");
+ solink_module_tool->set_output_prefix("lib");
+ solink_module_tool->set_default_output_extension(".so");
+ solink_module_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(solink_module));
+
+ // LINK
+ std::unique_ptr<Tool> link = Tool::CreateTool(CTool::kCToolLink);
+ CTool* link_tool = link->AsC();
+ SetCommandForTool(
+ "ld -o {{target_output_name}} {{source}} "
+ "{{ldflags}} {{libs}}",
+ link_tool);
+ link_tool->set_lib_switch("-l");
+ link_tool->set_lib_dir_switch("-L");
+ link_tool->set_outputs(
+ SubstitutionList::MakeForTest("{{root_out_dir}}/{{target_output_name}}"));
+ toolchain->SetTool(std::move(link));
+
+ // STAMP
+ std::unique_ptr<Tool> stamp_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolStamp);
+ SetCommandForTool("touch {{output}}", stamp_tool.get());
+ toolchain->SetTool(std::move(stamp_tool));
+
+ // COPY
+ std::unique_ptr<Tool> copy_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolCopy);
+ SetCommandForTool("cp {{source}} {{output}}", copy_tool.get());
+ toolchain->SetTool(std::move(copy_tool));
+
+ // COPY_BUNDLE_DATA
+ std::unique_ptr<Tool> copy_bundle_data_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolCopyBundleData);
+ SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get());
+ toolchain->SetTool(std::move(copy_bundle_data_tool));
+
+ // COMPILE_XCASSETS
+ std::unique_ptr<Tool> compile_xcassets_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolCompileXCAssets);
+ SetCommandForTool("touch {{output}}", compile_xcassets_tool.get());
+ toolchain->SetTool(std::move(compile_xcassets_tool));
+
+ // RUST
+ std::unique_ptr<Tool> rustc_tool = Tool::CreateTool(RustTool::kRsToolBin);
+ SetCommandForTool(
+ "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
+ "{{rustdeps}} {{externs}}",
+ rustc_tool.get());
+ rustc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_out_dir}}/{{crate_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(rustc_tool));
+
+ // SWIFT
+ std::unique_ptr<Tool> swift_tool = Tool::CreateTool(CTool::kCToolSwift);
+ SetCommandForTool(
+ "swiftc --module-name {{module_name}} {{module_dirs}} {{inputs}}",
+ swift_tool.get());
+ swift_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{module_name}}.swiftmodule"));
+ swift_tool->set_partial_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{source_name_part}}.o"));
+ toolchain->SetTool(std::move(swift_tool));
+
+ // CDYLIB
+ std::unique_ptr<Tool> cdylib_tool = Tool::CreateTool(RustTool::kRsToolCDylib);
+ SetCommandForTool(
+ "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
+ "{{rustdeps}} {{externs}}",
+ cdylib_tool.get());
+ cdylib_tool->set_output_prefix("lib");
+ cdylib_tool->set_default_output_extension(".so");
+ cdylib_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_output_dir}}/{{target_output_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(cdylib_tool));
+
+ // DYLIB
+ std::unique_ptr<Tool> dylib_tool = Tool::CreateTool(RustTool::kRsToolDylib);
+ SetCommandForTool(
+ "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
+ "{{rustdeps}} {{externs}}",
+ dylib_tool.get());
+ dylib_tool->set_output_prefix("lib");
+ dylib_tool->set_default_output_extension(".so");
+ dylib_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_output_dir}}/{{target_output_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(dylib_tool));
+
+ // RUST_PROC_MACRO
+ std::unique_ptr<Tool> rust_proc_macro_tool =
+ Tool::CreateTool(RustTool::kRsToolMacro);
+ SetCommandForTool(
+ "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
+ "{{rustdeps}} {{externs}}",
+ rust_proc_macro_tool.get());
+ rust_proc_macro_tool->set_output_prefix("lib");
+ rust_proc_macro_tool->set_default_output_extension(".so");
+ rust_proc_macro_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(rust_proc_macro_tool));
+
+ // RLIB
+ std::unique_ptr<Tool> rlib_tool = Tool::CreateTool(RustTool::kRsToolRlib);
+ SetCommandForTool(
+ "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
+ "{{rustdeps}} {{externs}}",
+ rlib_tool.get());
+ rlib_tool->set_output_prefix("lib");
+ rlib_tool->set_default_output_extension(".rlib");
+ rlib_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(rlib_tool));
+
+ // STATICLIB
+ std::unique_ptr<Tool> staticlib_tool =
+ Tool::CreateTool(RustTool::kRsToolStaticlib);
+ SetCommandForTool(
+ "{{rustenv}} rustc --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o {{output}} "
+ "{{rustdeps}} {{externs}}",
+ staticlib_tool.get());
+ staticlib_tool->set_output_prefix("lib");
+ staticlib_tool->set_default_output_extension(".a");
+ staticlib_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}"));
+ toolchain->SetTool(std::move(staticlib_tool));
+
+ toolchain->ToolchainSetupComplete();
+}
+
+// static
+void TestWithScope::SetCommandForTool(const std::string& cmd, Tool* tool) {
+ Err err;
+ SubstitutionPattern command;
+ command.Parse(cmd, nullptr, &err);
+ CHECK(!err.has_error()) << "Couldn't parse \"" << cmd << "\", "
+ << "got " << err.message();
+ tool->set_command(command);
+}
+
+void TestWithScope::AppendPrintOutput(const std::string& str) {
+ print_output_.append(str);
+}
+
+TestParseInput::TestParseInput(const std::string& input)
+ : input_file_(SourceFile("//test")) {
+ input_file_.SetContents(input);
+
+ tokens_ = Tokenizer::Tokenize(&input_file_, &parse_err_);
+ if (!parse_err_.has_error())
+ parsed_ = Parser::Parse(tokens_, &parse_err_);
+}
+
+TestParseInput::~TestParseInput() = default;
+
+TestTarget::TestTarget(const TestWithScope& setup,
+ const std::string& label_string,
+ Target::OutputType type)
+ : Target(setup.settings(), setup.ParseLabel(label_string)) {
+ visibility().SetPublic();
+ set_output_type(type);
+ SetToolchain(setup.toolchain());
+}
+
+TestTarget::~TestTarget() = default;
diff --git a/gn/src/gn/test_with_scope.h b/gn/src/gn/test_with_scope.h
new file mode 100644
index 00000000000..19854dbea65
--- /dev/null
+++ b/gn/src/gn/test_with_scope.h
@@ -0,0 +1,128 @@
+// 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 TOOLS_GN_TEST_WITH_SCOPE_H_
+#define TOOLS_GN_TEST_WITH_SCOPE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/build_settings.h"
+#include "gn/c_tool.h"
+#include "gn/err.h"
+#include "gn/general_tool.h"
+#include "gn/input_file.h"
+#include "gn/parse_tree.h"
+#include "gn/rust_tool.h"
+#include "gn/scope.h"
+#include "gn/scope_per_file_provider.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+#include "gn/token.h"
+#include "gn/toolchain.h"
+#include "gn/value.h"
+
+// A helper class for setting up a Scope that a test can use. It makes a
+// toolchain and sets up all the build state.
+class TestWithScope {
+ public:
+ TestWithScope();
+ ~TestWithScope();
+
+ BuildSettings* build_settings() { return &build_settings_; }
+ Settings* settings() { return &settings_; }
+ const Settings* settings() const { return &settings_; }
+ Toolchain* toolchain() { return &toolchain_; }
+ const Toolchain* toolchain() const { return &toolchain_; }
+ Scope* scope() { return &scope_; }
+ const Scope::ItemVector& items() { return items_; }
+
+ // This buffer accumulates output from any print() commands executed in the
+ // context of this test. Note that the implementation of this is not
+ // threadsafe so don't write tests that call print from multiple threads.
+ std::string& print_output() { return print_output_; }
+
+ // Parse the given string into a label in the default toolchain. This will
+ // assert if the label isn't valid (this is intended for hardcoded labels).
+ Label ParseLabel(const std::string& str) const;
+
+ // Parses, evaluates, and resolves targets from the given snippet of code.
+ // All targets must be defined in dependency order (does not use a Builder,
+ // just blindly resolves all targets in order).
+ bool ExecuteSnippet(const std::string& str, Err* err);
+
+ Value ExecuteExpression(const std::string& expr, Err* err);
+
+ // Fills in the tools for the given toolchain with reasonable default values.
+ // The toolchain in this object will be automatically set up with this
+ // function, it is exposed to allow tests to get the same functionality for
+ // other toolchains they make.
+ // Two slightly different toolchains can be generated by this function,
+ // based on the use_toc argument. This is only currently required by
+ // one test.
+ static void SetupToolchain(Toolchain* toolchain, bool use_toc = false);
+
+ // Sets the given text command on the given tool, parsing it as a
+ // substitution pattern. This will assert if the input is malformed. This is
+ // designed to help setting up Tools for tests.
+ static void SetCommandForTool(const std::string& cmd, Tool* tool);
+
+ private:
+ void AppendPrintOutput(const std::string& str);
+
+ BuildSettings build_settings_;
+ Settings settings_;
+ Toolchain toolchain_;
+ Scope scope_;
+ Scope::ItemVector items_;
+
+ // Supplies the scope with built-in variables like root_out_dir.
+ ScopePerFileProvider scope_progammatic_provider_;
+
+ std::string print_output_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWithScope);
+};
+
+// Helper class to treat some string input as a file.
+//
+// Instantiate it with the contents you want, be sure to check for error, and
+// then you can execute the ParseNode or whatever.
+class TestParseInput {
+ public:
+ explicit TestParseInput(const std::string& input);
+ ~TestParseInput();
+
+ // Indicates whether and what error occurred during tokenizing and parsing.
+ bool has_error() const { return parse_err_.has_error(); }
+ const Err& parse_err() const { return parse_err_; }
+
+ const InputFile& input_file() const { return input_file_; }
+ const std::vector<Token>& tokens() const { return tokens_; }
+ const ParseNode* parsed() const { return parsed_.get(); }
+
+ private:
+ InputFile input_file_;
+
+ std::vector<Token> tokens_;
+ std::unique_ptr<ParseNode> parsed_;
+
+ Err parse_err_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestParseInput);
+};
+
+// Shortcut for creating targets for tests that take the test setup, a pretty-
+// style label, and a target type and sets everything up. The target will
+// default to public visibility.
+class TestTarget : public Target {
+ public:
+ TestTarget(const TestWithScope& setup,
+ const std::string& label_string,
+ Target::OutputType type);
+ ~TestTarget() override;
+};
+
+#endif // TOOLS_GN_TEST_WITH_SCOPE_H_
diff --git a/gn/src/gn/token.cc b/gn/src/gn/token.cc
new file mode 100644
index 00000000000..3af9c48f36f
--- /dev/null
+++ b/gn/src/gn/token.cc
@@ -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.
+
+#include "gn/token.h"
+
+#include "base/logging.h"
+#include "gn/tokenizer.h"
+
+Token::Token() : type_(INVALID), value_() {}
+
+Token::Token(const Location& location, Type t, const std::string_view& v)
+ : type_(t), value_(v), location_(location) {}
+
+// static
+Token Token::ClassifyAndMake(const Location& location,
+ const std::string_view& v) {
+ char first = v.size() > 0 ? v[0] : '\0';
+ char second = v.size() > 1 ? v[1] : '\0';
+ return Token(location, Tokenizer::ClassifyToken(first, second), v);
+}
+
+bool Token::IsIdentifierEqualTo(const char* v) const {
+ return type_ == IDENTIFIER && value_ == v;
+}
+
+bool Token::IsStringEqualTo(const char* v) const {
+ return type_ == STRING && value_ == v;
+}
diff --git a/gn/src/gn/token.h b/gn/src/gn/token.h
new file mode 100644
index 00000000000..a11807a47d8
--- /dev/null
+++ b/gn/src/gn/token.h
@@ -0,0 +1,88 @@
+// 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 TOOLS_GN_TOKEN_H_
+#define TOOLS_GN_TOKEN_H_
+
+#include <string_view>
+
+#include "gn/location.h"
+
+class Token {
+ public:
+ enum Type {
+ INVALID,
+ INTEGER, // 123
+ STRING, // "blah"
+ TRUE_TOKEN, // Not "TRUE" to avoid collisions with #define in windows.h.
+ FALSE_TOKEN,
+
+ // Various operators.
+ EQUAL,
+ PLUS,
+ MINUS,
+ PLUS_EQUALS,
+ MINUS_EQUALS,
+ EQUAL_EQUAL,
+ NOT_EQUAL,
+ LESS_EQUAL,
+ GREATER_EQUAL,
+ LESS_THAN,
+ GREATER_THAN,
+ BOOLEAN_AND,
+ BOOLEAN_OR,
+ BANG,
+ DOT,
+
+ LEFT_PAREN,
+ RIGHT_PAREN,
+ LEFT_BRACKET,
+ RIGHT_BRACKET,
+ LEFT_BRACE,
+ RIGHT_BRACE,
+
+ IF,
+ ELSE,
+ IDENTIFIER, // foo
+ COMMA, // ,
+ UNCLASSIFIED_COMMENT, // #...\n, of unknown style (will be converted
+ // to one below)
+ LINE_COMMENT, // #...\n on a line alone.
+ SUFFIX_COMMENT, // #...\n on a line following other code.
+ BLOCK_COMMENT, // #...\n line comment, but free-standing.
+
+ UNCLASSIFIED_OPERATOR,
+
+ NUM_TYPES
+ };
+
+ Token();
+ Token(const Location& location, Type t, const std::string_view& v);
+
+ static Token ClassifyAndMake(const Location& location,
+ const std::string_view& v);
+
+ Type type() const { return type_; }
+ const std::string_view& value() const { return value_; }
+ const Location& location() const { return location_; }
+ void set_location(Location location) { location_ = location; }
+ LocationRange range() const {
+ return LocationRange(
+ location_,
+ Location(location_.file(), location_.line_number(),
+ location_.column_number() + static_cast<int>(value_.size()),
+ location_.byte() + static_cast<int>(value_.size())));
+ }
+
+ // Helper functions for comparing this token to something.
+ bool IsIdentifierEqualTo(const char* v) const;
+ bool IsStringEqualTo(const char* v) const;
+
+ private:
+ Type type_;
+ std::string_view value_;
+ Location location_;
+};
+
+#endif // TOOLS_GN_TOKEN_H_
diff --git a/gn/src/gn/tokenizer.cc b/gn/src/gn/tokenizer.cc
new file mode 100644
index 00000000000..93a64259de6
--- /dev/null
+++ b/gn/src/gn/tokenizer.cc
@@ -0,0 +1,417 @@
+// 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 "gn/tokenizer.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "gn/input_file.h"
+
+namespace {
+
+bool CouldBeTwoCharOperatorBegin(char c) {
+ return c == '<' || c == '>' || c == '!' || c == '=' || c == '-' || c == '+' ||
+ c == '|' || c == '&';
+}
+
+bool CouldBeTwoCharOperatorEnd(char c) {
+ return c == '=' || c == '|' || c == '&';
+}
+
+bool CouldBeOneCharOperator(char c) {
+ return c == '=' || c == '<' || c == '>' || c == '+' || c == '!' || c == ':' ||
+ c == '|' || c == '&' || c == '-';
+}
+
+bool CouldBeOperator(char c) {
+ return CouldBeOneCharOperator(c) || CouldBeTwoCharOperatorBegin(c);
+}
+
+bool IsScoperChar(char c) {
+ return c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}';
+}
+
+Token::Type GetSpecificOperatorType(std::string_view value) {
+ if (value == "=")
+ return Token::EQUAL;
+ if (value == "+")
+ return Token::PLUS;
+ if (value == "-")
+ return Token::MINUS;
+ if (value == "+=")
+ return Token::PLUS_EQUALS;
+ if (value == "-=")
+ return Token::MINUS_EQUALS;
+ if (value == "==")
+ return Token::EQUAL_EQUAL;
+ if (value == "!=")
+ return Token::NOT_EQUAL;
+ if (value == "<=")
+ return Token::LESS_EQUAL;
+ if (value == ">=")
+ return Token::GREATER_EQUAL;
+ if (value == "<")
+ return Token::LESS_THAN;
+ if (value == ">")
+ return Token::GREATER_THAN;
+ if (value == "&&")
+ return Token::BOOLEAN_AND;
+ if (value == "||")
+ return Token::BOOLEAN_OR;
+ if (value == "!")
+ return Token::BANG;
+ if (value == ".")
+ return Token::DOT;
+ return Token::INVALID;
+}
+
+} // namespace
+
+Tokenizer::Tokenizer(const InputFile* input_file,
+ Err* err,
+ WhitespaceTransform whitespace_transform)
+ : input_file_(input_file),
+ input_(input_file->contents()),
+ err_(err),
+ whitespace_transform_(whitespace_transform) {}
+
+Tokenizer::~Tokenizer() = default;
+
+// static
+std::vector<Token> Tokenizer::Tokenize(
+ const InputFile* input_file,
+ Err* err,
+ WhitespaceTransform whitespace_transform) {
+ Tokenizer t(input_file, err, whitespace_transform);
+ return t.Run();
+}
+
+std::vector<Token> Tokenizer::Run() {
+ DCHECK(tokens_.empty());
+ while (!done()) {
+ AdvanceToNextToken();
+ if (done())
+ break;
+ Location location = GetCurrentLocation();
+
+ Token::Type type = ClassifyCurrent();
+ if (type == Token::INVALID) {
+ *err_ = GetErrorForInvalidToken(location);
+ break;
+ }
+ size_t token_begin = cur_;
+ AdvanceToEndOfToken(location, type);
+ if (has_error())
+ break;
+ size_t token_end = cur_;
+
+ std::string_view token_value(&input_.data()[token_begin],
+ token_end - token_begin);
+
+ if (type == Token::UNCLASSIFIED_OPERATOR) {
+ type = GetSpecificOperatorType(token_value);
+ } else if (type == Token::IDENTIFIER) {
+ if (token_value == "if")
+ type = Token::IF;
+ else if (token_value == "else")
+ type = Token::ELSE;
+ else if (token_value == "true")
+ type = Token::TRUE_TOKEN;
+ else if (token_value == "false")
+ type = Token::FALSE_TOKEN;
+ } else if (type == Token::UNCLASSIFIED_COMMENT) {
+ if (AtStartOfLine(token_begin) &&
+ // If it's a standalone comment, but is a continuation of a comment on
+ // a previous line, then instead make it a continued suffix comment.
+ (tokens_.empty() || tokens_.back().type() != Token::SUFFIX_COMMENT ||
+ tokens_.back().location().line_number() + 1 !=
+ location.line_number() ||
+ tokens_.back().location().column_number() !=
+ location.column_number())) {
+ type = Token::LINE_COMMENT;
+ if (!at_end()) // Could be EOF.
+ Advance(); // The current \n.
+ // If this comment is separated from the next syntax element, then we
+ // want to tag it as a block comment. This will become a standalone
+ // statement at the parser level to keep this comment separate, rather
+ // than attached to the subsequent statement.
+ while (!at_end() && IsCurrentWhitespace()) {
+ if (IsCurrentNewline()) {
+ type = Token::BLOCK_COMMENT;
+ break;
+ }
+ Advance();
+ }
+ } else {
+ type = Token::SUFFIX_COMMENT;
+ }
+ }
+
+ tokens_.push_back(Token(location, type, token_value));
+ }
+ if (err_->has_error())
+ tokens_.clear();
+ return tokens_;
+}
+
+// static
+size_t Tokenizer::ByteOffsetOfNthLine(const std::string_view& buf, int n) {
+ DCHECK_GT(n, 0);
+
+ if (n == 1)
+ return 0;
+
+ int cur_line = 1;
+ size_t cur_byte = 0;
+ while (cur_byte < buf.size()) {
+ if (IsNewline(buf, cur_byte)) {
+ cur_line++;
+ if (cur_line == n)
+ return cur_byte + 1;
+ }
+ cur_byte++;
+ }
+ return static_cast<size_t>(-1);
+}
+
+// static
+bool Tokenizer::IsNewline(const std::string_view& buffer, size_t offset) {
+ DCHECK(offset < buffer.size());
+ // We may need more logic here to handle different line ending styles.
+ return buffer[offset] == '\n';
+}
+
+// static
+bool Tokenizer::IsIdentifierFirstChar(char c) {
+ return base::IsAsciiAlpha(c) || c == '_';
+}
+
+// static
+bool Tokenizer::IsIdentifierContinuingChar(char c) {
+ // Also allow digits after the first char.
+ return IsIdentifierFirstChar(c) || base::IsAsciiDigit(c);
+}
+
+void Tokenizer::AdvanceToNextToken() {
+ while (!at_end() && IsCurrentWhitespace())
+ Advance();
+}
+
+// static
+Token::Type Tokenizer::ClassifyToken(char next_char, char following_char) {
+ if (base::IsAsciiDigit(next_char))
+ return Token::INTEGER;
+ if (next_char == '"')
+ return Token::STRING;
+
+ // Note: '-' handled specially below.
+ if (next_char != '-' && CouldBeOperator(next_char))
+ return Token::UNCLASSIFIED_OPERATOR;
+
+ if (IsIdentifierFirstChar(next_char))
+ return Token::IDENTIFIER;
+
+ if (next_char == '[')
+ return Token::LEFT_BRACKET;
+ if (next_char == ']')
+ return Token::RIGHT_BRACKET;
+ if (next_char == '(')
+ return Token::LEFT_PAREN;
+ if (next_char == ')')
+ return Token::RIGHT_PAREN;
+ if (next_char == '{')
+ return Token::LEFT_BRACE;
+ if (next_char == '}')
+ return Token::RIGHT_BRACE;
+
+ if (next_char == '.')
+ return Token::DOT;
+ if (next_char == ',')
+ return Token::COMMA;
+
+ if (next_char == '#')
+ return Token::UNCLASSIFIED_COMMENT;
+
+ // For the case of '-' differentiate between a negative number and anything
+ // else.
+ if (next_char == '-') {
+ if (following_char == '\0')
+ return Token::UNCLASSIFIED_OPERATOR; // Just the minus before end of
+ // file.
+ if (base::IsAsciiDigit(following_char))
+ return Token::INTEGER;
+ return Token::UNCLASSIFIED_OPERATOR;
+ }
+
+ return Token::INVALID;
+}
+
+Token::Type Tokenizer::ClassifyCurrent() const {
+ DCHECK(!at_end());
+ char next_char = cur_char();
+ char following_char = CanIncrement() ? input_[cur_ + 1] : '\0';
+ return ClassifyToken(next_char, following_char);
+}
+
+void Tokenizer::AdvanceToEndOfToken(const Location& location,
+ Token::Type type) {
+ switch (type) {
+ case Token::INTEGER:
+ do {
+ Advance();
+ } while (!at_end() && base::IsAsciiDigit(cur_char()));
+ if (!at_end()) {
+ // Require the char after a number to be some kind of space, scope,
+ // or operator.
+ char c = cur_char();
+ if (!IsCurrentWhitespace() && !CouldBeOperator(c) && !IsScoperChar(c) &&
+ c != ',') {
+ *err_ = Err(GetCurrentLocation(), "This is not a valid number.");
+ // Highlight the number.
+ err_->AppendRange(LocationRange(location, GetCurrentLocation()));
+ }
+ }
+ break;
+
+ case Token::STRING: {
+ char initial = cur_char();
+ Advance(); // Advance past initial "
+ for (;;) {
+ if (at_end()) {
+ *err_ = Err(LocationRange(location, GetCurrentLocation()),
+ "Unterminated string literal.",
+ "Don't leave me hanging like this!");
+ break;
+ }
+ if (IsCurrentStringTerminator(initial)) {
+ Advance(); // Skip past last "
+ break;
+ } else if (IsCurrentNewline()) {
+ *err_ = Err(LocationRange(location, GetCurrentLocation()),
+ "Newline in string constant.");
+ }
+ Advance();
+ }
+ break;
+ }
+
+ case Token::UNCLASSIFIED_OPERATOR:
+ // Some operators are two characters, some are one.
+ if (CouldBeTwoCharOperatorBegin(cur_char())) {
+ if (CanIncrement() && CouldBeTwoCharOperatorEnd(input_[cur_ + 1]))
+ Advance();
+ }
+ Advance();
+ break;
+
+ case Token::IDENTIFIER:
+ while (!at_end() && IsIdentifierContinuingChar(cur_char()))
+ Advance();
+ break;
+
+ case Token::LEFT_BRACKET:
+ case Token::RIGHT_BRACKET:
+ case Token::LEFT_BRACE:
+ case Token::RIGHT_BRACE:
+ case Token::LEFT_PAREN:
+ case Token::RIGHT_PAREN:
+ case Token::DOT:
+ case Token::COMMA:
+ Advance(); // All are one char.
+ break;
+
+ case Token::UNCLASSIFIED_COMMENT:
+ // Eat to EOL.
+ while (!at_end() && !IsCurrentNewline())
+ Advance();
+ break;
+
+ case Token::INVALID:
+ default:
+ *err_ = Err(location, "Everything is all messed up",
+ "Please insert system disk in drive A: and press any key.");
+ NOTREACHED();
+ return;
+ }
+}
+
+bool Tokenizer::AtStartOfLine(size_t location) const {
+ while (location > 0) {
+ --location;
+ char c = input_[location];
+ if (c == '\n')
+ return true;
+ if (c != ' ')
+ return false;
+ }
+ return true;
+}
+
+bool Tokenizer::IsCurrentWhitespace() const {
+ DCHECK(!at_end());
+ char c = input_[cur_];
+ // Note that tab (0x09), vertical tab (0x0B), and formfeed (0x0C) are illegal.
+ return c == 0x0A || c == 0x0D || c == 0x20 ||
+ (whitespace_transform_ == WhitespaceTransform::kInvalidToSpace &&
+ (c == 0x09 || c == 0x0B || c == 0x0C));
+}
+
+bool Tokenizer::IsCurrentStringTerminator(char quote_char) const {
+ DCHECK(!at_end());
+ if (cur_char() != quote_char)
+ return false;
+
+ // Check for escaping. \" is not a string terminator, but \\" is. Count
+ // the number of preceding backslashes.
+ int num_backslashes = 0;
+ for (int i = static_cast<int>(cur_) - 1; i >= 0 && input_[i] == '\\'; i--)
+ num_backslashes++;
+
+ // Even backslashes mean that they were escaping each other and don't count
+ // as escaping this quote.
+ return (num_backslashes % 2) == 0;
+}
+
+bool Tokenizer::IsCurrentNewline() const {
+ return IsNewline(input_, cur_);
+}
+
+void Tokenizer::Advance() {
+ DCHECK(cur_ < input_.size());
+ if (IsCurrentNewline()) {
+ line_number_++;
+ column_number_ = 1;
+ } else {
+ column_number_++;
+ }
+ cur_++;
+}
+
+Location Tokenizer::GetCurrentLocation() const {
+ return Location(input_file_, line_number_, column_number_,
+ static_cast<int>(cur_));
+}
+
+Err Tokenizer::GetErrorForInvalidToken(const Location& location) const {
+ std::string help;
+ if (cur_char() == ';') {
+ // Semicolon.
+ help = "Semicolons are not needed, delete this one.";
+ } else if (cur_char() == '\t') {
+ // Tab.
+ help =
+ "You got a tab character in here. Tabs are evil. "
+ "Convert to spaces.";
+ } else if (cur_char() == '/' && cur_ + 1 < input_.size() &&
+ (input_[cur_ + 1] == '/' || input_[cur_ + 1] == '*')) {
+ // Different types of comments.
+ help = "Comments should start with # instead";
+ } else if (cur_char() == '\'') {
+ help = "Strings are delimited by \" characters, not apostrophes.";
+ } else {
+ help = "I have no idea what this is.";
+ }
+
+ return Err(location, "Invalid token.", help);
+}
diff --git a/gn/src/gn/tokenizer.h b/gn/src/gn/tokenizer.h
new file mode 100644
index 00000000000..d4b347bdbda
--- /dev/null
+++ b/gn/src/gn/tokenizer.h
@@ -0,0 +1,108 @@
+// 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 TOOLS_GN_TOKENIZER_H_
+#define TOOLS_GN_TOKENIZER_H_
+
+#include <stddef.h>
+
+#include <string_view>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/err.h"
+#include "gn/token.h"
+
+class InputFile;
+
+// Tab (0x09), vertical tab (0x0B), and formfeed (0x0C) are illegal in GN files.
+// Almost always these are errors. However, in the case of running the formatter
+// it's nice to convert these to spaces when encountered so that the input can
+// still be parsed and rewritten correctly by the formatter.
+enum class WhitespaceTransform {
+ kMaintainOriginalInput,
+ kInvalidToSpace,
+};
+
+class Tokenizer {
+ public:
+ static std::vector<Token> Tokenize(
+ const InputFile* input_file,
+ Err* err,
+ WhitespaceTransform whitespace_transform =
+ WhitespaceTransform::kMaintainOriginalInput);
+
+ // Counts lines in the given buffer (the first line is "1") and returns
+ // the byte offset of the beginning of that line, or (size_t)-1 if there
+ // aren't that many lines in the file. Note that this will return the byte
+ // one past the end of the input if the last character is a newline.
+ //
+ // This is a helper function for error output so that the tokenizer's
+ // notion of lines can be used elsewhere.
+ static size_t ByteOffsetOfNthLine(const std::string_view& buf, int n);
+
+ // Returns true if the given offset of the string piece counts as a newline.
+ // The offset must be in the buffer.
+ static bool IsNewline(const std::string_view& buffer, size_t offset);
+
+ static bool IsIdentifierFirstChar(char c);
+
+ static bool IsIdentifierContinuingChar(char c);
+
+ static Token::Type ClassifyToken(char next_char, char following_char);
+
+ private:
+ // InputFile must outlive the tokenizer and all generated tokens.
+ Tokenizer(const InputFile* input_file,
+ Err* err,
+ WhitespaceTransform whitespace_transform);
+ ~Tokenizer();
+
+ std::vector<Token> Run();
+
+ void AdvanceToNextToken();
+ Token::Type ClassifyCurrent() const;
+ void AdvanceToEndOfToken(const Location& location, Token::Type type);
+
+ // Whether from this location back to the beginning of the line is only
+ // whitespace. |location| should be the first character of the token to be
+ // checked.
+ bool AtStartOfLine(size_t location) const;
+
+ bool IsCurrentWhitespace() const;
+ bool IsCurrentNewline() const;
+ bool IsCurrentStringTerminator(char quote_char) const;
+
+ bool CanIncrement() const { return cur_ < input_.size() - 1; }
+
+ // Increments the current location by one.
+ void Advance();
+
+ // Returns the current character in the file as a location.
+ Location GetCurrentLocation() const;
+
+ Err GetErrorForInvalidToken(const Location& location) const;
+
+ bool done() const { return at_end() || has_error(); }
+
+ bool at_end() const { return cur_ == input_.size(); }
+ char cur_char() const { return input_[cur_]; }
+
+ bool has_error() const { return err_->has_error(); }
+
+ std::vector<Token> tokens_;
+
+ const InputFile* input_file_;
+ const std::string_view input_;
+ Err* err_;
+ WhitespaceTransform whitespace_transform_;
+ size_t cur_ = 0; // Byte offset into input buffer.
+
+ int line_number_ = 1;
+ int column_number_ = 1;
+
+ DISALLOW_COPY_AND_ASSIGN(Tokenizer);
+};
+
+#endif // TOOLS_GN_TOKENIZER_H_
diff --git a/gn/src/gn/tokenizer_unittest.cc b/gn/src/gn/tokenizer_unittest.cc
new file mode 100644
index 00000000000..d740d866986
--- /dev/null
+++ b/gn/src/gn/tokenizer_unittest.cc
@@ -0,0 +1,237 @@
+// 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 <stddef.h>
+
+#include "gn/input_file.h"
+#include "gn/token.h"
+#include "gn/tokenizer.h"
+#include "util/test/test.h"
+
+namespace {
+
+struct TokenExpectation {
+ Token::Type type;
+ const char* value;
+};
+
+template <size_t len>
+bool CheckTokenizer(const char* input, const TokenExpectation (&expect)[len]) {
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&input_file, &err);
+
+ if (results.size() != len)
+ return false;
+ for (size_t i = 0; i < len; i++) {
+ if (expect[i].type != results[i].type())
+ return false;
+ if (expect[i].value != results[i].value())
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST(Tokenizer, Empty) {
+ InputFile empty_string_input(SourceFile("/test"));
+ empty_string_input.SetContents("");
+
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&empty_string_input, &err);
+ EXPECT_TRUE(results.empty());
+
+ InputFile whitespace_input(SourceFile("/test"));
+ whitespace_input.SetContents(" \r \n \r\n");
+
+ results = Tokenizer::Tokenize(&whitespace_input, &err);
+ EXPECT_TRUE(results.empty());
+}
+
+TEST(Tokenizer, Identifier) {
+ TokenExpectation one_ident[] = {{Token::IDENTIFIER, "foo"}};
+ EXPECT_TRUE(CheckTokenizer(" foo ", one_ident));
+}
+
+TEST(Tokenizer, Integer) {
+ TokenExpectation integers[] = {{Token::INTEGER, "123"},
+ {Token::INTEGER, "-123"}};
+ EXPECT_TRUE(CheckTokenizer(" 123 -123 ", integers));
+}
+
+TEST(Tokenizer, IntegerNoSpace) {
+ TokenExpectation integers[] = {{Token::INTEGER, "123"},
+ {Token::INTEGER, "-123"}};
+ EXPECT_TRUE(CheckTokenizer(" 123-123 ", integers));
+}
+
+TEST(Tokenizer, String) {
+ TokenExpectation strings[] = {{Token::STRING, "\"foo\""},
+ {Token::STRING, "\"bar\\\"baz\""},
+ {Token::STRING, "\"asdf\\\\\""}};
+ EXPECT_TRUE(
+ CheckTokenizer(" \"foo\" \"bar\\\"baz\" \"asdf\\\\\" ", strings));
+}
+
+TEST(Tokenizer, Operator) {
+ TokenExpectation operators[] = {
+ {Token::MINUS, "-"},
+ {Token::PLUS, "+"},
+ {Token::EQUAL, "="},
+ {Token::PLUS_EQUALS, "+="},
+ {Token::MINUS_EQUALS, "-="},
+ {Token::NOT_EQUAL, "!="},
+ {Token::EQUAL_EQUAL, "=="},
+ {Token::LESS_THAN, "<"},
+ {Token::GREATER_THAN, ">"},
+ {Token::LESS_EQUAL, "<="},
+ {Token::GREATER_EQUAL, ">="},
+ {Token::BANG, "!"},
+ {Token::BOOLEAN_OR, "||"},
+ {Token::BOOLEAN_AND, "&&"},
+ {Token::DOT, "."},
+ {Token::COMMA, ","},
+ };
+ EXPECT_TRUE(
+ CheckTokenizer("- + = += -= != == < > <= >= ! || && . ,", operators));
+}
+
+TEST(Tokenizer, Scoper) {
+ TokenExpectation scopers[] = {
+ {Token::LEFT_BRACE, "{"}, {Token::LEFT_BRACKET, "["},
+ {Token::RIGHT_BRACKET, "]"}, {Token::RIGHT_BRACE, "}"},
+ {Token::LEFT_PAREN, "("}, {Token::RIGHT_PAREN, ")"},
+ };
+ EXPECT_TRUE(CheckTokenizer("{[ ]} ()", scopers));
+}
+
+TEST(Tokenizer, FunctionCall) {
+ TokenExpectation fn[] = {
+ {Token::IDENTIFIER, "fun"}, {Token::LEFT_PAREN, "("},
+ {Token::STRING, "\"foo\""}, {Token::RIGHT_PAREN, ")"},
+ {Token::LEFT_BRACE, "{"}, {Token::IDENTIFIER, "foo"},
+ {Token::EQUAL, "="}, {Token::INTEGER, "12"},
+ {Token::RIGHT_BRACE, "}"},
+ };
+ EXPECT_TRUE(CheckTokenizer("fun(\"foo\") {\nfoo = 12}", fn));
+}
+
+TEST(Tokenizer, Locations) {
+ InputFile input(SourceFile("/test"));
+ input.SetContents("1 2 \"three\"\n 4");
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(&input, &err);
+
+ ASSERT_EQ(4u, results.size());
+ ASSERT_TRUE(results[0].location() == Location(&input, 1, 1, 1));
+ ASSERT_TRUE(results[1].location() == Location(&input, 1, 3, 3));
+ ASSERT_TRUE(results[2].location() == Location(&input, 1, 5, 5));
+ ASSERT_TRUE(results[3].location() == Location(&input, 2, 3, 8));
+}
+
+TEST(Tokenizer, ByteOffsetOfNthLine) {
+ EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine("foo", 1));
+
+ // Windows and Posix have different line endings, so check the byte at the
+ // location rather than the offset.
+ char input1[] = "aaa\nxaa\n\nya";
+ EXPECT_EQ('x', input1[Tokenizer::ByteOffsetOfNthLine(input1, 2)]);
+ EXPECT_EQ('y', input1[Tokenizer::ByteOffsetOfNthLine(input1, 4)]);
+
+ char input2[3];
+ input2[0] = 'a';
+ input2[1] = '\n'; // Manually set to avoid Windows double-byte endings.
+ input2[2] = 0;
+ EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine(input2, 1));
+ EXPECT_EQ(2u, Tokenizer::ByteOffsetOfNthLine(input2, 2));
+}
+
+TEST(Tokenizer, Comments) {
+ TokenExpectation fn[] = {
+ {Token::LINE_COMMENT, "# Stuff"},
+ {Token::IDENTIFIER, "fun"},
+ {Token::LEFT_PAREN, "("},
+ {Token::STRING, "\"foo\""},
+ {Token::RIGHT_PAREN, ")"},
+ {Token::LEFT_BRACE, "{"},
+ {Token::SUFFIX_COMMENT, "# Things"},
+ {Token::LINE_COMMENT, "#Wee"},
+ {Token::IDENTIFIER, "foo"},
+ {Token::EQUAL, "="},
+ {Token::INTEGER, "12"},
+ {Token::SUFFIX_COMMENT, "#Zip"},
+ {Token::RIGHT_BRACE, "}"},
+ };
+ EXPECT_TRUE(
+ CheckTokenizer("# Stuff\n"
+ "fun(\"foo\") { # Things\n"
+ "#Wee\n"
+ "foo = 12 #Zip\n"
+ "}",
+ fn));
+}
+
+TEST(Tokenizer, CommentsContinued) {
+ // In the first test, the comments aren't horizontally aligned, so they're
+ // considered separate. In the second test, they are, so "B" is a
+ // continuation of "A" (another SUFFIX comment).
+ TokenExpectation fn1[] = {
+ {Token::IDENTIFIER, "fun"}, {Token::LEFT_PAREN, "("},
+ {Token::STRING, "\"foo\""}, {Token::RIGHT_PAREN, ")"},
+ {Token::LEFT_BRACE, "{"}, {Token::SUFFIX_COMMENT, "# A"},
+ {Token::LINE_COMMENT, "# B"}, {Token::RIGHT_BRACE, "}"},
+ };
+ EXPECT_TRUE(
+ CheckTokenizer("fun(\"foo\") { # A\n"
+ " # B\n"
+ "}",
+ fn1));
+
+ TokenExpectation fn2[] = {
+ {Token::IDENTIFIER, "fun"}, {Token::LEFT_PAREN, "("},
+ {Token::STRING, "\"foo\""}, {Token::RIGHT_PAREN, ")"},
+ {Token::LEFT_BRACE, "{"}, {Token::SUFFIX_COMMENT, "# A"},
+ {Token::SUFFIX_COMMENT, "# B"}, {Token::RIGHT_BRACE, "}"},
+ };
+ EXPECT_TRUE(CheckTokenizer(
+ "fun(\"foo\") { # A\n"
+ " # B\n" // Note that these are aligned, the \"s move A out.
+ "}",
+ fn2));
+}
+
+TEST(Tokenizer, WhitespaceTransformMaintain) {
+ InputFile input(SourceFile("/test"));
+ input.SetContents("a\t2\v\"st\tuff\"\f{");
+
+ Err err;
+ std::vector<Token> results = Tokenizer::Tokenize(
+ &input, &err, WhitespaceTransform::kMaintainOriginalInput);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.location().column_number(), 2);
+}
+
+TEST(Tokenizer, WhitespaceTransformToSpace) {
+ InputFile input(SourceFile("/test"));
+ input.SetContents("a\t2\v\"st\tuff\"\f{");
+
+ Err err;
+ std::vector<Token> results =
+ Tokenizer::Tokenize(&input, &err, WhitespaceTransform::kInvalidToSpace);
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(results.size(), 4u);
+ EXPECT_EQ(results[0].type(), Token::IDENTIFIER);
+ EXPECT_EQ(results[0].value(), "a");
+ EXPECT_EQ(results[1].type(), Token::INTEGER);
+ EXPECT_EQ(results[1].value(), "2");
+ EXPECT_EQ(results[2].type(), Token::STRING);
+ EXPECT_EQ(results[2].value(),
+ "\"st\tuff\""); // Note, embedded \t not transformed.
+ EXPECT_EQ(results[3].type(), Token::LEFT_BRACE);
+ EXPECT_EQ(results[3].value(), "{");
+}
+
diff --git a/gn/src/gn/tool.cc b/gn/src/gn/tool.cc
new file mode 100644
index 00000000000..5e4186dbe18
--- /dev/null
+++ b/gn/src/gn/tool.cc
@@ -0,0 +1,407 @@
+// 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 "gn/tool.h"
+
+#include "gn/c_tool.h"
+#include "gn/general_tool.h"
+#include "gn/rust_tool.h"
+#include "gn/settings.h"
+#include "gn/target.h"
+
+const char* Tool::kToolNone = "";
+
+Tool::Tool(const char* n) : name_(n) {}
+
+Tool::~Tool() = default;
+
+void Tool::SetToolComplete() {
+ DCHECK(!complete_);
+ complete_ = true;
+
+ command_.FillRequiredTypes(&substitution_bits_);
+ depfile_.FillRequiredTypes(&substitution_bits_);
+ description_.FillRequiredTypes(&substitution_bits_);
+ outputs_.FillRequiredTypes(&substitution_bits_);
+ rspfile_.FillRequiredTypes(&substitution_bits_);
+ rspfile_content_.FillRequiredTypes(&substitution_bits_);
+ partial_outputs_.FillRequiredTypes(&substitution_bits_);
+}
+
+GeneralTool* Tool::AsGeneral() {
+ return nullptr;
+}
+
+const GeneralTool* Tool::AsGeneral() const {
+ return nullptr;
+}
+
+CTool* Tool::AsC() {
+ return nullptr;
+}
+
+const CTool* Tool::AsC() const {
+ return nullptr;
+}
+
+RustTool* Tool::AsRust() {
+ return nullptr;
+}
+const RustTool* Tool::AsRust() const {
+ return nullptr;
+}
+
+bool Tool::IsPatternInOutputList(const SubstitutionList& output_list,
+ const SubstitutionPattern& pattern) const {
+ for (const auto& cur : output_list.list()) {
+ if (pattern.ranges().size() == cur.ranges().size() &&
+ std::equal(pattern.ranges().begin(), pattern.ranges().end(),
+ cur.ranges().begin()))
+ return true;
+ }
+ return false;
+}
+
+bool Tool::ValidateSubstitutionList(
+ const std::vector<const Substitution*>& list,
+ const Value* origin,
+ Err* err) const {
+ for (const auto& cur_type : list) {
+ if (!ValidateSubstitution(cur_type)) {
+ *err = Err(*origin, "Pattern not valid here.",
+ "You used the pattern " + std::string(cur_type->name) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Tool::ReadBool(Scope* scope, const char* var, bool* field, Err* err) {
+ DCHECK(!complete_);
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+ if (!v->VerifyTypeIs(Value::BOOLEAN, err))
+ return false;
+ *field = v->boolean_value();
+ return true;
+}
+
+bool Tool::ReadString(Scope* scope,
+ const char* var,
+ std::string* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+ if (!v->VerifyTypeIs(Value::STRING, err))
+ return false;
+ *field = v->string_value();
+ return true;
+}
+
+bool Tool::ReadPattern(Scope* scope,
+ const char* var,
+ SubstitutionPattern* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ SubstitutionPattern pattern;
+ if (!pattern.Parse(*value, err))
+ return false;
+ if (!ValidateSubstitutionList(pattern.required_types(), value, err))
+ return false;
+
+ *field = std::move(pattern);
+ return true;
+}
+
+bool Tool::ReadPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ SubstitutionList list;
+ if (!list.Parse(*value, err))
+ return false;
+
+ // Validate the right kinds of patterns are used.
+ if (!ValidateSubstitutionList(list.required_types(), value, err))
+ return false;
+
+ *field = std::move(list);
+ return true;
+}
+
+bool Tool::ReadLabel(Scope* scope,
+ const char* var,
+ const Label& current_toolchain,
+ LabelPtrPair<Pool>* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+
+ Label label =
+ Label::Resolve(scope->GetSourceDir(),
+ scope->settings()->build_settings()->root_path_utf8(),
+ current_toolchain, *v, err);
+ if (err->has_error())
+ return false;
+
+ LabelPtrPair<Pool> pair(label);
+ pair.origin = defined_from();
+
+ *field = std::move(pair);
+ return true;
+}
+
+bool Tool::ReadOutputExtension(Scope* scope, Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue("default_output_extension", true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Accept empty string.
+
+ if (value->string_value()[0] != '.') {
+ *err = Err(*value, "default_output_extension must begin with a '.'");
+ return false;
+ }
+
+ set_default_output_extension(value->string_value());
+ return true;
+}
+
+bool Tool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ if (!ReadPattern(scope, "command", &command_, err) ||
+ !ReadString(scope, "command_launcher", &command_launcher_, err) ||
+ !ReadOutputExtension(scope, err) ||
+ !ReadPattern(scope, "depfile", &depfile_, err) ||
+ !ReadPattern(scope, "description", &description_, err) ||
+ !ReadPatternList(scope, "runtime_outputs", &runtime_outputs_, err) ||
+ !ReadString(scope, "output_prefix", &output_prefix_, err) ||
+ !ReadPattern(scope, "default_output_dir", &default_output_dir_, err) ||
+ !ReadBool(scope, "restat", &restat_, err) ||
+ !ReadPattern(scope, "rspfile", &rspfile_, err) ||
+ !ReadPattern(scope, "rspfile_content", &rspfile_content_, err) ||
+ !ReadLabel(scope, "pool", toolchain->label(), &pool_, err)) {
+ return false;
+ }
+ const bool command_is_required = name_ != GeneralTool::kGeneralToolAction;
+ if (command_.empty() == command_is_required) {
+ *err = Err(defined_from(), "This tool's command is bad.",
+ command_is_required
+ ? "This tool requires \"command\" to be defined."
+ : "This tool doesn't support \"command\".");
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<Tool> Tool::CreateTool(const ParseNode* function,
+ const std::string& name,
+ Scope* scope,
+ Toolchain* toolchain,
+ Err* err) {
+ std::unique_ptr<Tool> tool = CreateTool(name);
+ if (!tool) {
+ *err = Err(function, "Unknown tool type.");
+ return nullptr;
+ }
+ if (CTool* c_tool = tool->AsC()) {
+ if (c_tool->InitTool(scope, toolchain, err))
+ return tool;
+ return nullptr;
+ }
+ if (GeneralTool* general_tool = tool->AsGeneral()) {
+ if (general_tool->InitTool(scope, toolchain, err))
+ return tool;
+ return nullptr;
+ }
+ if (RustTool* rust_tool = tool->AsRust()) {
+ if (rust_tool->InitTool(scope, toolchain, err))
+ return tool;
+ return nullptr;
+ }
+ NOTREACHED();
+ *err = Err(function, "Unknown tool type.");
+ return nullptr;
+}
+
+// static
+std::unique_ptr<Tool> Tool::CreateTool(const std::string& name) {
+ // C tools
+ if (name == CTool::kCToolCc)
+ return std::make_unique<CTool>(CTool::kCToolCc);
+ else if (name == CTool::kCToolCxx)
+ return std::make_unique<CTool>(CTool::kCToolCxx);
+ else if (name == CTool::kCToolCxxModule)
+ return std::make_unique<CTool>(CTool::kCToolCxxModule);
+ else if (name == CTool::kCToolObjC)
+ return std::make_unique<CTool>(CTool::kCToolObjC);
+ else if (name == CTool::kCToolObjCxx)
+ return std::make_unique<CTool>(CTool::kCToolObjCxx);
+ else if (name == CTool::kCToolRc)
+ return std::make_unique<CTool>(CTool::kCToolRc);
+ else if (name == CTool::kCToolAsm)
+ return std::make_unique<CTool>(CTool::kCToolAsm);
+ else if (name == CTool::kCToolSwift)
+ return std::make_unique<CTool>(CTool::kCToolSwift);
+ else if (name == CTool::kCToolAlink)
+ return std::make_unique<CTool>(CTool::kCToolAlink);
+ else if (name == CTool::kCToolSolink)
+ return std::make_unique<CTool>(CTool::kCToolSolink);
+ else if (name == CTool::kCToolSolinkModule)
+ return std::make_unique<CTool>(CTool::kCToolSolinkModule);
+ else if (name == CTool::kCToolLink)
+ return std::make_unique<CTool>(CTool::kCToolLink);
+
+ // General tools
+ else if (name == GeneralTool::kGeneralToolAction)
+ return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolAction);
+ else if (name == GeneralTool::kGeneralToolStamp)
+ return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolStamp);
+ else if (name == GeneralTool::kGeneralToolCopy)
+ return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolCopy);
+ else if (name == GeneralTool::kGeneralToolCopyBundleData)
+ return std::make_unique<GeneralTool>(
+ GeneralTool::kGeneralToolCopyBundleData);
+ else if (name == GeneralTool::kGeneralToolCompileXCAssets)
+ return std::make_unique<GeneralTool>(
+ GeneralTool::kGeneralToolCompileXCAssets);
+
+ // Rust tool
+ else if (name == RustTool::kRsToolBin)
+ return std::make_unique<RustTool>(RustTool::kRsToolBin);
+ else if (name == RustTool::kRsToolCDylib)
+ return std::make_unique<RustTool>(RustTool::kRsToolCDylib);
+ else if (name == RustTool::kRsToolDylib)
+ return std::make_unique<RustTool>(RustTool::kRsToolDylib);
+ else if (name == RustTool::kRsToolMacro)
+ return std::make_unique<RustTool>(RustTool::kRsToolMacro);
+ else if (name == RustTool::kRsToolRlib)
+ return std::make_unique<RustTool>(RustTool::kRsToolRlib);
+ else if (name == RustTool::kRsToolStaticlib)
+ return std::make_unique<RustTool>(RustTool::kRsToolStaticlib);
+
+ return nullptr;
+}
+
+// static
+const char* Tool::GetToolTypeForSourceType(SourceFile::Type type) {
+ switch (type) {
+ case SourceFile::SOURCE_C:
+ return CTool::kCToolCc;
+ case SourceFile::SOURCE_CPP:
+ return CTool::kCToolCxx;
+ case SourceFile::SOURCE_MODULEMAP:
+ return CTool::kCToolCxxModule;
+ case SourceFile::SOURCE_M:
+ return CTool::kCToolObjC;
+ case SourceFile::SOURCE_MM:
+ return CTool::kCToolObjCxx;
+ case SourceFile::SOURCE_ASM:
+ case SourceFile::SOURCE_S:
+ return CTool::kCToolAsm;
+ case SourceFile::SOURCE_RC:
+ return CTool::kCToolRc;
+ case SourceFile::SOURCE_RS:
+ return RustTool::kRsToolBin;
+ case SourceFile::SOURCE_SWIFT:
+ return CTool::kCToolSwift;
+ case SourceFile::SOURCE_UNKNOWN:
+ case SourceFile::SOURCE_H:
+ case SourceFile::SOURCE_O:
+ case SourceFile::SOURCE_DEF:
+ case SourceFile::SOURCE_GO:
+ return kToolNone;
+ default:
+ NOTREACHED();
+ return kToolNone;
+ }
+}
+
+// static
+const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
+ // The contents of this list might be suprising (i.e. stamp tool for copy
+ // rules). See the header for why.
+ // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
+ if (target->source_types_used().RustSourceUsed()) {
+ switch (target->rust_values().crate_type()) {
+ case RustValues::CRATE_AUTO: {
+ switch (target->output_type()) {
+ case Target::EXECUTABLE:
+ return RustTool::kRsToolBin;
+ case Target::SHARED_LIBRARY:
+ return RustTool::kRsToolDylib;
+ case Target::STATIC_LIBRARY:
+ return RustTool::kRsToolStaticlib;
+ case Target::RUST_LIBRARY:
+ return RustTool::kRsToolRlib;
+ case Target::RUST_PROC_MACRO:
+ return RustTool::kRsToolMacro;
+ default:
+ break;
+ }
+ break;
+ }
+ case RustValues::CRATE_BIN:
+ return RustTool::kRsToolBin;
+ case RustValues::CRATE_CDYLIB:
+ return RustTool::kRsToolCDylib;
+ case RustValues::CRATE_DYLIB:
+ return RustTool::kRsToolDylib;
+ case RustValues::CRATE_PROC_MACRO:
+ return RustTool::kRsToolMacro;
+ case RustValues::CRATE_RLIB:
+ return RustTool::kRsToolRlib;
+ case RustValues::CRATE_STATICLIB:
+ return RustTool::kRsToolStaticlib;
+ default:
+ NOTREACHED();
+ }
+ }
+ switch (target->output_type()) {
+ case Target::GROUP:
+ return GeneralTool::kGeneralToolStamp;
+ case Target::EXECUTABLE:
+ return CTool::kCToolLink;
+ case Target::SHARED_LIBRARY:
+ return CTool::kCToolSolink;
+ case Target::LOADABLE_MODULE:
+ return CTool::kCToolSolinkModule;
+ case Target::STATIC_LIBRARY:
+ return CTool::kCToolAlink;
+ case Target::SOURCE_SET:
+ return GeneralTool::kGeneralToolStamp;
+ case Target::ACTION:
+ case Target::ACTION_FOREACH:
+ case Target::BUNDLE_DATA:
+ case Target::CREATE_BUNDLE:
+ case Target::COPY_FILES:
+ case Target::GENERATED_FILE:
+ return GeneralTool::kGeneralToolStamp;
+ default:
+ NOTREACHED();
+ return kToolNone;
+ }
+}
diff --git a/gn/src/gn/tool.h b/gn/src/gn/tool.h
new file mode 100644
index 00000000000..e64935f548b
--- /dev/null
+++ b/gn/src/gn/tool.h
@@ -0,0 +1,304 @@
+// 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 TOOLS_GN_TOOL_H_
+#define TOOLS_GN_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/label.h"
+#include "gn/label_ptr.h"
+#include "gn/scope.h"
+#include "gn/source_file.h"
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+
+class ParseNode;
+class Pool;
+class Target;
+class Toolchain;
+
+class CTool;
+class GeneralTool;
+class RustTool;
+
+// To add a new Tool category, create a subclass implementing SetComplete()
+// Add a new category to ToolCategories
+// Add a GetAs* function
+class Tool {
+ public:
+ static const char* kToolNone;
+
+ virtual ~Tool();
+
+ // Manual RTTI and required functions ---------------------------------------
+ //
+ // To implement a new tool category to compile binaries in a different way,
+ // inherit this class, implement the following functions, and add the
+ // appropriate ToolTypes and RTTI getters. New tools will also need to
+ // implement a corresponding class inheriting NinjaBinaryTargetWriter that
+ // does the actual rule writing.
+
+ // Initialize tool from a scope. Child classes should override this function
+ // and call the parent.
+ bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+
+ // Validate the char* passed to the creation.
+ virtual bool ValidateName(const char* name) const = 0;
+
+ // Called when the toolchain is saving this tool, after everything is filled
+ // in.
+ virtual void SetComplete() = 0;
+
+ // Validate substitutions in this tool.
+ virtual bool ValidateSubstitution(const Substitution* sub_type) const = 0;
+
+ // Manual RTTI
+ virtual CTool* AsC();
+ virtual const CTool* AsC() const;
+ virtual GeneralTool* AsGeneral();
+ virtual const GeneralTool* AsGeneral() const;
+ virtual RustTool* AsRust();
+ virtual const RustTool* AsRust() const;
+
+ // Basic information ---------------------------------------------------------
+
+ const ParseNode* defined_from() const { return defined_from_; }
+ void set_defined_from(const ParseNode* df) { defined_from_ = df; }
+
+ const char* name() const { return name_; }
+
+ // Getters/setters ----------------------------------------------------------
+ //
+ // After the tool has had its attributes set, the caller must call
+ // SetComplete(), at which point no other changes can be made.
+
+ // Command to run.
+ const SubstitutionPattern& command() const { return command_; }
+ void set_command(SubstitutionPattern cmd) {
+ DCHECK(!complete_);
+ command_ = std::move(cmd);
+ }
+
+ // Launcher for the command (e.g. goma)
+ const std::string& command_launcher() const {
+ return command_launcher_;
+ }
+ void set_command_launcher(std::string l) {
+ DCHECK(!complete_);
+ command_launcher_ = std::move(l);
+ }
+
+ // Should include a leading "." if nonempty.
+ const std::string& default_output_extension() const {
+ return default_output_extension_;
+ }
+ void set_default_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ default_output_extension_ = std::move(ext);
+ }
+
+ const SubstitutionPattern& default_output_dir() const {
+ return default_output_dir_;
+ }
+ void set_default_output_dir(SubstitutionPattern dir) {
+ DCHECK(!complete_);
+ default_output_dir_ = std::move(dir);
+ }
+
+ // Dependency file (if supported).
+ const SubstitutionPattern& depfile() const { return depfile_; }
+ void set_depfile(SubstitutionPattern df) {
+ DCHECK(!complete_);
+ depfile_ = std::move(df);
+ }
+
+ const SubstitutionPattern& description() const { return description_; }
+ void set_description(SubstitutionPattern desc) {
+ DCHECK(!complete_);
+ description_ = std::move(desc);
+ }
+
+ const std::string& framework_switch() const { return framework_switch_; }
+ void set_framework_switch(std::string s) {
+ DCHECK(!complete_);
+ framework_switch_ = std::move(s);
+ }
+
+ const std::string& weak_framework_switch() const {
+ return weak_framework_switch_;
+ }
+ void set_weak_framework_switch(std::string s) {
+ DCHECK(!complete_);
+ weak_framework_switch_ = std::move(s);
+ }
+
+ const std::string& framework_dir_switch() const {
+ return framework_dir_switch_;
+ }
+ void set_framework_dir_switch(std::string s) {
+ DCHECK(!complete_);
+ framework_dir_switch_ = std::move(s);
+ }
+
+ const std::string& lib_switch() const { return lib_switch_; }
+ void set_lib_switch(std::string s) {
+ DCHECK(!complete_);
+ lib_switch_ = std::move(s);
+ }
+
+ const std::string& lib_dir_switch() const { return lib_dir_switch_; }
+ void set_lib_dir_switch(std::string s) {
+ DCHECK(!complete_);
+ lib_dir_switch_ = std::move(s);
+ }
+
+ const std::string& swiftmodule_switch() const { return swiftmodule_switch_; }
+ void set_swiftmodule_switch(std::string s) {
+ DCHECK(!complete_);
+ swiftmodule_switch_ = std::move(s);
+ }
+
+ const std::string& linker_arg() const { return linker_arg_; }
+ void set_linker_arg(std::string s) {
+ DCHECK(!complete_);
+ linker_arg_ = std::move(s);
+ }
+
+ const SubstitutionList& outputs() const { return outputs_; }
+ void set_outputs(SubstitutionList out) {
+ DCHECK(!complete_);
+ outputs_ = std::move(out);
+ }
+
+ const SubstitutionList& partial_outputs() const { return partial_outputs_; }
+ void set_partial_outputs(SubstitutionList partial_out) {
+ DCHECK(!complete_);
+ partial_outputs_ = std::move(partial_out);
+ }
+
+ const SubstitutionList& runtime_outputs() const { return runtime_outputs_; }
+ void set_runtime_outputs(SubstitutionList run_out) {
+ DCHECK(!complete_);
+ runtime_outputs_ = std::move(run_out);
+ }
+
+ const std::string& output_prefix() const { return output_prefix_; }
+ void set_output_prefix(std::string s) {
+ DCHECK(!complete_);
+ output_prefix_ = std::move(s);
+ }
+
+ bool restat() const { return restat_; }
+ void set_restat(bool r) {
+ DCHECK(!complete_);
+ restat_ = r;
+ }
+
+ const SubstitutionPattern& rspfile() const { return rspfile_; }
+ void set_rspfile(SubstitutionPattern rsp) {
+ DCHECK(!complete_);
+ rspfile_ = std::move(rsp);
+ }
+
+ const SubstitutionPattern& rspfile_content() const {
+ return rspfile_content_;
+ }
+ void set_rspfile_content(SubstitutionPattern content) {
+ DCHECK(!complete_);
+ rspfile_content_ = std::move(content);
+ }
+
+ const LabelPtrPair<Pool>& pool() const { return pool_; }
+ void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
+
+ // Other functions ----------------------------------------------------------
+
+ // Function for the above override to call to complete the tool.
+ void SetToolComplete();
+
+ // Substitutions required by this tool.
+ const SubstitutionBits& substitution_bits() const {
+ DCHECK(complete_);
+ return substitution_bits_;
+ }
+
+ // Create a tool based on given features.
+ static std::unique_ptr<Tool> CreateTool(const std::string& name);
+ static std::unique_ptr<Tool> CreateTool(const ParseNode* function,
+ const std::string& name,
+ Scope* scope,
+ Toolchain* toolchain,
+ Err* err);
+
+ static const char* GetToolTypeForSourceType(SourceFile::Type type);
+ static const char* GetToolTypeForTargetFinalOutput(const Target* target);
+
+ protected:
+ explicit Tool(const char* t);
+
+ // Initialization functions -------------------------------------------------
+ //
+ // Initialization methods used by InitTool(). If successful, will set the
+ // field and return true, otherwise will return false. Must be called before
+ // SetComplete().
+ bool IsPatternInOutputList(const SubstitutionList& output_list,
+ const SubstitutionPattern& pattern) const;
+ bool ValidateSubstitutionList(const std::vector<const Substitution*>& list,
+ const Value* origin,
+ Err* err) const;
+ bool ValidateOutputs(Err* err) const;
+ bool ReadBool(Scope* scope, const char* var, bool* field, Err* err);
+ bool ReadString(Scope* scope, const char* var, std::string* field, Err* err);
+ bool ReadPattern(Scope* scope,
+ const char* var,
+ SubstitutionPattern* field,
+ Err* err);
+ bool ReadPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err);
+ bool ReadLabel(Scope* scope,
+ const char* var,
+ const Label& current_toolchain,
+ LabelPtrPair<Pool>* field,
+ Err* err);
+ bool ReadOutputExtension(Scope* scope, Err* err);
+
+ const ParseNode* defined_from_ = nullptr;
+ const char* name_ = nullptr;
+
+ SubstitutionPattern command_;
+ std::string command_launcher_;
+ std::string default_output_extension_;
+ SubstitutionPattern default_output_dir_;
+ SubstitutionPattern depfile_;
+ SubstitutionPattern description_;
+ std::string framework_switch_;
+ std::string weak_framework_switch_;
+ std::string framework_dir_switch_;
+ std::string lib_switch_;
+ std::string lib_dir_switch_;
+ std::string swiftmodule_switch_;
+ std::string linker_arg_;
+ SubstitutionList outputs_;
+ SubstitutionList partial_outputs_;
+ SubstitutionList runtime_outputs_;
+ std::string output_prefix_;
+ bool restat_ = false;
+ SubstitutionPattern rspfile_;
+ SubstitutionPattern rspfile_content_;
+ LabelPtrPair<Pool> pool_;
+
+ bool complete_ = false;
+
+ SubstitutionBits substitution_bits_;
+
+ DISALLOW_COPY_AND_ASSIGN(Tool);
+};
+
+#endif // TOOLS_GN_TOOL_H_
diff --git a/gn/src/gn/toolchain.cc b/gn/src/gn/toolchain.cc
new file mode 100644
index 00000000000..bfad81db635
--- /dev/null
+++ b/gn/src/gn/toolchain.cc
@@ -0,0 +1,140 @@
+// 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 "gn/toolchain.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <utility>
+
+#include "base/logging.h"
+#include "gn/target.h"
+#include "gn/value.h"
+
+Toolchain::Toolchain(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files)
+ : Item(settings, label, build_dependency_files) {}
+
+Toolchain::~Toolchain() = default;
+
+Toolchain* Toolchain::AsToolchain() {
+ return this;
+}
+
+const Toolchain* Toolchain::AsToolchain() const {
+ return this;
+}
+
+Tool* Toolchain::GetTool(const char* name) {
+ DCHECK(name != Tool::kToolNone);
+ auto pair = tools_.find(name);
+ if (pair != tools_.end()) {
+ return pair->second.get();
+ }
+ return nullptr;
+}
+
+const Tool* Toolchain::GetTool(const char* name) const {
+ DCHECK(name != Tool::kToolNone);
+ auto pair = tools_.find(name);
+ if (pair != tools_.end()) {
+ return pair->second.get();
+ }
+ return nullptr;
+}
+
+GeneralTool* Toolchain::GetToolAsGeneral(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsGeneral();
+ }
+ return nullptr;
+}
+
+const GeneralTool* Toolchain::GetToolAsGeneral(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsGeneral();
+ }
+ return nullptr;
+}
+
+CTool* Toolchain::GetToolAsC(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsC();
+ }
+ return nullptr;
+}
+
+const CTool* Toolchain::GetToolAsC(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsC();
+ }
+ return nullptr;
+}
+
+RustTool* Toolchain::GetToolAsRust(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsRust();
+ }
+ return nullptr;
+}
+
+const RustTool* Toolchain::GetToolAsRust(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsRust();
+ }
+ return nullptr;
+}
+
+void Toolchain::SetTool(std::unique_ptr<Tool> t) {
+ DCHECK(t->name() != Tool::kToolNone);
+ DCHECK(tools_.find(t->name()) == tools_.end());
+ t->SetComplete();
+ tools_[t->name()] = std::move(t);
+}
+
+void Toolchain::ToolchainSetupComplete() {
+ // Collect required bits from all tools.
+ for (const auto& tool : tools_) {
+ substitution_bits_.MergeFrom(tool.second->substitution_bits());
+ }
+ setup_complete_ = true;
+}
+
+const Tool* Toolchain::GetToolForSourceType(SourceFile::Type type) const {
+ return GetTool(Tool::GetToolTypeForSourceType(type));
+}
+
+const CTool* Toolchain::GetToolForSourceTypeAsC(SourceFile::Type type) const {
+ return GetToolAsC(Tool::GetToolTypeForSourceType(type));
+}
+
+const GeneralTool* Toolchain::GetToolForSourceTypeAsGeneral(
+ SourceFile::Type type) const {
+ return GetToolAsGeneral(Tool::GetToolTypeForSourceType(type));
+}
+
+const RustTool* Toolchain::GetToolForSourceTypeAsRust(
+ SourceFile::Type type) const {
+ return GetToolAsRust(Tool::GetToolTypeForSourceType(type));
+}
+
+const Tool* Toolchain::GetToolForTargetFinalOutput(const Target* target) const {
+ return GetTool(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const CTool* Toolchain::GetToolForTargetFinalOutputAsC(
+ const Target* target) const {
+ return GetToolAsC(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const GeneralTool* Toolchain::GetToolForTargetFinalOutputAsGeneral(
+ const Target* target) const {
+ return GetToolAsGeneral(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const RustTool* Toolchain::GetToolForTargetFinalOutputAsRust(
+ const Target* target) const {
+ return GetToolAsRust(Tool::GetToolTypeForTargetFinalOutput(target));
+}
diff --git a/gn/src/gn/toolchain.h b/gn/src/gn/toolchain.h
new file mode 100644
index 00000000000..eb5a60c526e
--- /dev/null
+++ b/gn/src/gn/toolchain.h
@@ -0,0 +1,129 @@
+// 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 TOOLS_GN_TOOLCHAIN_H_
+#define TOOLS_GN_TOOLCHAIN_H_
+
+#include <memory>
+#include <string_view>
+
+#include "base/logging.h"
+#include "gn/item.h"
+#include "gn/label_ptr.h"
+#include "gn/scope.h"
+#include "gn/substitution_type.h"
+#include "gn/tool.h"
+#include "gn/value.h"
+
+// Holds information on a specific toolchain. This data is filled in when we
+// encounter a toolchain definition.
+//
+// This class is an Item so it can participate in dependency management. In
+// particular, when a target uses a toolchain, it should have a dependency on
+// that toolchain's object so that we can be sure we loaded the toolchain
+// before generating the build for that target.
+//
+// Note on threadsafety: The label of the toolchain never changes so can
+// safely be accessed from any thread at any time (we do this when asking for
+// the toolchain name). But the values in the toolchain do, so these can't
+// be accessed until this Item is resolved.
+class Toolchain : public Item {
+ public:
+ // The Settings of an Item is always the context in which the Item was
+ // defined. For a toolchain this is confusing because this is NOT the
+ // settings object that applies to the things in the toolchain.
+ //
+ // To get the Settings object corresponding to objects loaded in the context
+ // of this toolchain (probably what you want instead), see
+ // Loader::GetToolchainSettings(). Many toolchain objects may be created in a
+ // given build, but only a few might be used, and the Loader is in charge of
+ // this process.
+ //
+ // We also track the set of build files that may affect this target, please
+ // refer to Scope for how this is determined.
+ Toolchain(const Settings* settings,
+ const Label& label,
+ const SourceFileSet& build_dependency_files = {});
+ ~Toolchain() override;
+
+ // Item overrides.
+ Toolchain* AsToolchain() override;
+ const Toolchain* AsToolchain() const override;
+
+ // Returns null if the tool hasn't been defined.
+ Tool* GetTool(const char* name);
+ const Tool* GetTool(const char* name) const;
+
+ // Returns null if the tool hasn't been defined or is not the correct type.
+ GeneralTool* GetToolAsGeneral(const char* name);
+ const GeneralTool* GetToolAsGeneral(const char* name) const;
+ CTool* GetToolAsC(const char* name);
+ const CTool* GetToolAsC(const char* name) const;
+ RustTool* GetToolAsRust(const char* name);
+ const RustTool* GetToolAsRust(const char* name) const;
+
+ // Set a tool. When all tools are configured, you should call
+ // ToolchainSetupComplete().
+ void SetTool(std::unique_ptr<Tool> t);
+
+ // Does final setup on the toolchain once all tools are known.
+ void ToolchainSetupComplete();
+
+ // Targets that must be resolved before compiling any targets.
+ const LabelTargetVector& deps() const { return deps_; }
+ LabelTargetVector& deps() { return deps_; }
+
+ // Specifies build argument overrides that will be set on the base scope. It
+ // will be as if these arguments were passed in on the command line. This
+ // allows a toolchain to override the OS type of the default toolchain or
+ // pass in other settings.
+ Scope::KeyValueMap& args() { return args_; }
+ const Scope::KeyValueMap& args() const { return args_; }
+
+ // Specifies whether public_configs and all_dependent_configs in this
+ // toolchain propagate to targets in other toolchains.
+ bool propagates_configs() const { return propagates_configs_; }
+ void set_propagates_configs(bool propagates_configs) {
+ propagates_configs_ = propagates_configs;
+ }
+
+ // Returns the tool for compiling the given source file type.
+ const Tool* GetToolForSourceType(SourceFile::Type type) const;
+ const CTool* GetToolForSourceTypeAsC(SourceFile::Type type) const;
+ const GeneralTool* GetToolForSourceTypeAsGeneral(SourceFile::Type type) const;
+ const RustTool* GetToolForSourceTypeAsRust(SourceFile::Type type) const;
+
+ // Returns the tool that produces the final output for the given target type.
+ // This isn't necessarily the tool you would expect. For copy target, this
+ // will return the stamp tool instead since the final output of a copy
+ // target is to stamp the set of copies done so there is one output.
+ const Tool* GetToolForTargetFinalOutput(const Target* target) const;
+ const CTool* GetToolForTargetFinalOutputAsC(const Target* target) const;
+ const GeneralTool* GetToolForTargetFinalOutputAsGeneral(
+ const Target* target) const;
+ const RustTool* GetToolForTargetFinalOutputAsRust(const Target* target) const;
+
+ const SubstitutionBits& substitution_bits() const {
+ DCHECK(setup_complete_);
+ return substitution_bits_;
+ }
+
+ const std::map<const char*, std::unique_ptr<Tool>>& tools() const {
+ return tools_;
+ }
+
+ private:
+ std::map<const char*, std::unique_ptr<Tool>> tools_;
+
+ bool setup_complete_ = false;
+
+ // Substitutions used by the tools in this toolchain.
+ SubstitutionBits substitution_bits_;
+
+ LabelTargetVector deps_;
+ Scope::KeyValueMap args_;
+ bool propagates_configs_ = false;
+};
+
+#endif // TOOLS_GN_TOOLCHAIN_H_
diff --git a/gn/src/gn/trace.cc b/gn/src/gn/trace.cc
new file mode 100644
index 00000000000..87c3cb61755
--- /dev/null
+++ b/gn/src/gn/trace.cc
@@ -0,0 +1,334 @@
+// 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 "gn/trace.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <map>
+#include <mutex>
+#include <sstream>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "gn/filesystem_utils.h"
+#include "gn/label.h"
+
+namespace {
+
+constexpr uint64_t kNanosecondsToMicroseconds = 1'000;
+
+class TraceLog {
+ public:
+ TraceLog() { events_.reserve(16384); }
+ // Trace items leaked intentionally.
+
+ void Add(TraceItem* item) {
+ std::lock_guard<std::mutex> lock(lock_);
+ events_.push_back(item);
+ }
+
+ // Returns a copy for threadsafety.
+ std::vector<TraceItem*> events() const { return events_; }
+
+ private:
+ std::mutex lock_;
+
+ std::vector<TraceItem*> events_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceLog);
+};
+
+TraceLog* trace_log = nullptr;
+
+struct Coalesced {
+ Coalesced() : name_ptr(nullptr), total_duration(0.0), count(0) {}
+
+ const std::string* name_ptr; // Pointer to a string with the name in it.
+ double total_duration;
+ int count;
+};
+
+bool DurationGreater(const TraceItem* a, const TraceItem* b) {
+ return a->delta().raw() > b->delta().raw();
+}
+
+bool CoalescedDurationGreater(const Coalesced& a, const Coalesced& b) {
+ return a.total_duration > b.total_duration;
+}
+
+void SummarizeParses(std::vector<const TraceItem*>& loads, std::ostream& out) {
+ out << "File parse times: (time in ms, name)\n";
+
+ std::sort(loads.begin(), loads.end(), &DurationGreater);
+ for (auto* load : loads) {
+ out << base::StringPrintf(" %8.2f ", load->delta().InMillisecondsF());
+ out << load->name() << std::endl;
+ }
+}
+
+void SummarizeCoalesced(std::vector<const TraceItem*>& items,
+ std::ostream& out) {
+ // Group by file name.
+ std::map<std::string, Coalesced> coalesced;
+ for (auto* item : items) {
+ Coalesced& c = coalesced[item->name()];
+ c.name_ptr = &item->name();
+ c.total_duration += item->delta().InMillisecondsF();
+ c.count++;
+ }
+
+ // Sort by duration.
+ std::vector<Coalesced> sorted;
+ for (const auto& pair : coalesced)
+ sorted.push_back(pair.second);
+ std::sort(sorted.begin(), sorted.end(), &CoalescedDurationGreater);
+
+ for (const auto& cur : sorted) {
+ out << base::StringPrintf(" %8.2f %d ", cur.total_duration, cur.count);
+ out << *cur.name_ptr << std::endl;
+ }
+}
+
+void SummarizeFileExecs(std::vector<const TraceItem*>& execs,
+ std::ostream& out) {
+ out << "File execute times: (total time in ms, # executions, name)\n";
+ SummarizeCoalesced(execs, out);
+}
+
+void SummarizeScriptExecs(std::vector<const TraceItem*>& execs,
+ std::ostream& out) {
+ out << "Script execute times: (total time in ms, # executions, name)\n";
+ SummarizeCoalesced(execs, out);
+}
+
+} // namespace
+
+TraceItem::TraceItem(Type type,
+ const std::string& name,
+ std::thread::id thread_id)
+ : type_(type), name_(name), thread_id_(thread_id) {}
+
+TraceItem::~TraceItem() = default;
+
+ScopedTrace::ScopedTrace(TraceItem::Type t, const std::string& name)
+ : item_(nullptr), done_(false) {
+ if (trace_log) {
+ item_ = new TraceItem(t, name, std::this_thread::get_id());
+ item_->set_begin(TicksNow());
+ }
+}
+
+ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label)
+ : item_(nullptr), done_(false) {
+ if (trace_log) {
+ item_ = new TraceItem(t, label.GetUserVisibleName(false),
+ std::this_thread::get_id());
+ item_->set_begin(TicksNow());
+ }
+}
+
+ScopedTrace::~ScopedTrace() {
+ Done();
+}
+
+void ScopedTrace::SetToolchain(const Label& label) {
+ if (item_)
+ item_->set_toolchain(label.GetUserVisibleName(false));
+}
+
+void ScopedTrace::SetCommandLine(const base::CommandLine& cmdline) {
+ if (item_)
+ item_->set_cmdline(FilePathToUTF8(cmdline.GetArgumentsString()));
+}
+
+void ScopedTrace::Done() {
+ if (!done_) {
+ done_ = true;
+ if (trace_log) {
+ item_->set_end(TicksNow());
+ AddTrace(item_);
+ }
+ }
+}
+
+void EnableTracing() {
+ if (!trace_log)
+ trace_log = new TraceLog;
+}
+
+bool TracingEnabled() {
+ return !!trace_log;
+}
+
+void AddTrace(TraceItem* item) {
+ trace_log->Add(item);
+}
+
+std::string SummarizeTraces() {
+ if (!trace_log)
+ return std::string();
+
+ std::vector<TraceItem*> events = trace_log->events();
+
+ // Classify all events.
+ std::vector<const TraceItem*> parses;
+ std::vector<const TraceItem*> file_execs;
+ std::vector<const TraceItem*> script_execs;
+ std::vector<const TraceItem*> check_headers;
+ int headers_checked = 0;
+ for (auto* event : events) {
+ switch (event->type()) {
+ case TraceItem::TRACE_FILE_PARSE:
+ parses.push_back(event);
+ break;
+ case TraceItem::TRACE_FILE_EXECUTE:
+ file_execs.push_back(event);
+ break;
+ case TraceItem::TRACE_SCRIPT_EXECUTE:
+ script_execs.push_back(event);
+ break;
+ case TraceItem::TRACE_CHECK_HEADERS:
+ check_headers.push_back(event);
+ break;
+ case TraceItem::TRACE_CHECK_HEADER:
+ headers_checked++;
+ break;
+ case TraceItem::TRACE_IMPORT_LOAD:
+ case TraceItem::TRACE_IMPORT_BLOCK:
+ case TraceItem::TRACE_SETUP:
+ case TraceItem::TRACE_FILE_LOAD:
+ case TraceItem::TRACE_FILE_WRITE:
+ case TraceItem::TRACE_DEFINE_TARGET:
+ case TraceItem::TRACE_ON_RESOLVED:
+ break; // Ignore these for the summary.
+ }
+ }
+
+ std::ostringstream out;
+ SummarizeParses(parses, out);
+ out << std::endl;
+ SummarizeFileExecs(file_execs, out);
+ out << std::endl;
+ SummarizeScriptExecs(script_execs, out);
+ out << std::endl;
+
+ // Generally there will only be one header check, but it's theoretically
+ // possible for more than one to run if more than one build is going in
+ // parallel. Just report the total of all of them.
+ if (!check_headers.empty()) {
+ double check_headers_time = 0;
+ for (auto* cur : check_headers)
+ check_headers_time += cur->delta().InMillisecondsF();
+
+ out << "Header check time: (total time in ms, files checked)\n";
+ out << base::StringPrintf(" %8.2f %d\n", check_headers_time,
+ headers_checked);
+ }
+
+ return out.str();
+}
+
+void SaveTraces(const base::FilePath& file_name) {
+ std::ostringstream out;
+
+ out << "{\"traceEvents\":[";
+
+ std::string quote_buffer; // Allocate outside loop to prevent reallocationg.
+
+ // Write main thread metadata (assume this is being written on the main
+ // thread).
+ out << "{\"pid\":0,\"tid\":\"" << std::this_thread::get_id() << "\"";
+ out << ",\"ts\":0,\"ph\":\"M\",";
+ out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},";
+
+ std::vector<TraceItem*> events = trace_log->events();
+ for (size_t i = 0; i < events.size(); i++) {
+ const TraceItem& item = *events[i];
+
+ if (i != 0)
+ out << ",";
+ out << "{\"pid\":0,\"tid\":\"" << item.thread_id() << "\"";
+ out << ",\"ts\":" << item.begin() / kNanosecondsToMicroseconds;
+ out << ",\"ph\":\"X\""; // "X" = complete event with begin & duration.
+ out << ",\"dur\":" << item.delta().InMicroseconds();
+
+ quote_buffer.resize(0);
+ base::EscapeJSONString(item.name(), true, &quote_buffer);
+ out << ",\"name\":" << quote_buffer;
+
+ out << ",\"cat\":";
+ switch (item.type()) {
+ case TraceItem::TRACE_SETUP:
+ out << "\"setup\"";
+ break;
+ case TraceItem::TRACE_FILE_LOAD:
+ out << "\"load\"";
+ break;
+ case TraceItem::TRACE_FILE_PARSE:
+ out << "\"parse\"";
+ break;
+ case TraceItem::TRACE_FILE_EXECUTE:
+ out << "\"file_exec\"";
+ break;
+ case TraceItem::TRACE_FILE_WRITE:
+ out << "\"file_write\"";
+ break;
+ case TraceItem::TRACE_IMPORT_LOAD:
+ out << "\"import_load\"";
+ break;
+ case TraceItem::TRACE_IMPORT_BLOCK:
+ out << "\"import_block\"";
+ break;
+ case TraceItem::TRACE_SCRIPT_EXECUTE:
+ out << "\"script_exec\"";
+ break;
+ case TraceItem::TRACE_DEFINE_TARGET:
+ out << "\"define\"";
+ break;
+ case TraceItem::TRACE_ON_RESOLVED:
+ out << "\"onresolved\"";
+ break;
+ case TraceItem::TRACE_CHECK_HEADER:
+ out << "\"hdr\"";
+ break;
+ case TraceItem::TRACE_CHECK_HEADERS:
+ out << "\"header_check\"";
+ break;
+ }
+
+ if (!item.toolchain().empty() || !item.cmdline().empty()) {
+ out << ",\"args\":{";
+ bool needs_comma = false;
+ if (!item.toolchain().empty()) {
+ quote_buffer.resize(0);
+ base::EscapeJSONString(item.toolchain(), true, &quote_buffer);
+ out << "\"toolchain\":" << quote_buffer;
+ needs_comma = true;
+ }
+ if (!item.cmdline().empty()) {
+ quote_buffer.resize(0);
+ base::EscapeJSONString(item.cmdline(), true, &quote_buffer);
+ if (needs_comma)
+ out << ",";
+ out << "\"cmdline\":" << quote_buffer;
+ needs_comma = true;
+ }
+ out << "}";
+ }
+ out << "}";
+ }
+
+ out << "]}";
+
+ std::string out_str = out.str();
+ base::WriteFile(file_name, out_str.data(), static_cast<int>(out_str.size()));
+}
diff --git a/gn/tools/gn/trace.h b/gn/src/gn/trace.h
index f4f06dfa279..f4f06dfa279 100644
--- a/gn/tools/gn/trace.h
+++ b/gn/src/gn/trace.h
diff --git a/gn/src/gn/unique_vector.h b/gn/src/gn/unique_vector.h
new file mode 100644
index 00000000000..ce5380b275e
--- /dev/null
+++ b/gn/src/gn/unique_vector.h
@@ -0,0 +1,143 @@
+// 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 TOOLS_GN_UNIQUE_VECTOR_H_
+#define TOOLS_GN_UNIQUE_VECTOR_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
+
+#include "hash_table_base.h"
+
+// A HashTableBase node type used by all UniqueVector<> instantiations.
+// The node stores the item's hash value and its index plus 1, in order
+// to follow the HashTableBase requirements (i.e. zero-initializable null
+// value).
+struct UniqueVectorNode {
+ size_t hash;
+ size_t index_plus1;
+
+ size_t hash_value() const { return hash; }
+
+ bool is_valid() const { return !is_null(); }
+
+ bool is_null() const { return index_plus1 == 0; }
+
+ // Do not support deletion, making lookup faster.
+ static constexpr bool is_tombstone() { return false; }
+
+ // Return vector index.
+ size_t index() const { return index_plus1 - 1u; }
+
+ // Create new instance from hash value and vector index.
+ static UniqueVectorNode Make(size_t hash, size_t index) {
+ return {hash, index + 1u};
+ }
+};
+
+using UniqueVectorHashTableBase = HashTableBase<UniqueVectorNode>;
+
+// A common HashSet implementation used by all UniqueVector instantiations.
+class UniqueVectorHashSet : public UniqueVectorHashTableBase {
+ public:
+ using BaseType = UniqueVectorHashTableBase;
+ using Node = BaseType::Node;
+
+ // Specialized Lookup() template function.
+ // |hash| is the hash value for |item|.
+ // |item| is the item search key being looked up.
+ // |vector| is containing vector for existing items.
+ //
+ // Returns a non-null mutable Node pointer.
+ template <typename T>
+ Node* Lookup(size_t hash, const T& item, const std::vector<T>& vector) const {
+ return BaseType::NodeLookup(hash, [&](const Node* node) {
+ return node->hash == hash && vector[node->index()] == item;
+ });
+ }
+
+ // Specialized Insert() function that converts |index| into the proper
+ // UniqueVectorKey type.
+ void Insert(Node* node, size_t hash, size_t index) {
+ *node = Node::Make(hash, index);
+ BaseType::UpdateAfterInsert();
+ }
+
+ void Clear() { NodeClear(); }
+};
+
+// An ordered set optimized for GN's usage. Such sets are used to store lists
+// of configs and libraries, and are appended to but not randomly inserted
+// into.
+template <typename T>
+class UniqueVector {
+ public:
+ using Vector = std::vector<T>;
+ using iterator = typename Vector::iterator;
+ using const_iterator = typename Vector::const_iterator;
+
+ const Vector& vector() const { return vector_; }
+ size_t size() const { return vector_.size(); }
+ bool empty() const { return vector_.empty(); }
+ void clear() {
+ vector_.clear();
+ set_.Clear();
+ }
+ void reserve(size_t s) { vector_.reserve(s); }
+
+ const T& operator[](size_t index) const { return vector_[index]; }
+
+ const_iterator begin() const { return vector_.begin(); }
+ const_iterator end() const { return vector_.end(); }
+
+ // Returns true if the item was appended, false if it already existed (and
+ // thus the vector was not modified).
+ bool push_back(const T& t) {
+ size_t hash = std::hash<T>()(t);
+ auto* node = set_.Lookup(hash, t, vector_);
+ if (node->is_valid()) {
+ return false; // Already have this one.
+ }
+
+ vector_.push_back(t);
+ set_.Insert(node, hash, vector_.size() - 1);
+ return true;
+ }
+
+ bool push_back(T&& t) {
+ size_t hash = std::hash<T>()(t);
+ auto* node = set_.Lookup(hash, t, vector_);
+ if (node->is_valid()) {
+ return false; // Already have this one.
+ }
+
+ vector_.push_back(std::move(t));
+ set_.Insert(node, hash, vector_.size() - 1);
+ return true;
+ }
+
+ // Appends a range of items from an iterator.
+ template <typename iter>
+ void Append(const iter& begin, const iter& end) {
+ for (iter i = begin; i != end; ++i)
+ push_back(*i);
+ }
+
+ // Returns the index of the item matching the given value in the list, or
+ // (size_t)(-1) if it's not found.
+ size_t IndexOf(const T& t) const {
+ size_t hash = std::hash<T>()(t);
+ auto* node = set_.Lookup(hash, t, vector_);
+ return node->index();
+ }
+
+ private:
+ Vector vector_;
+ UniqueVectorHashSet set_;
+};
+
+#endif // TOOLS_GN_UNIQUE_VECTOR_H_
diff --git a/gn/src/gn/unique_vector_unittest.cc b/gn/src/gn/unique_vector_unittest.cc
new file mode 100644
index 00000000000..96303b460d5
--- /dev/null
+++ b/gn/src/gn/unique_vector_unittest.cc
@@ -0,0 +1,45 @@
+// 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 "gn/unique_vector.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "util/test/test.h"
+
+TEST(UniqueVector, PushBack) {
+ UniqueVector<int> foo;
+ EXPECT_TRUE(foo.push_back(1));
+ EXPECT_FALSE(foo.push_back(1));
+ EXPECT_TRUE(foo.push_back(2));
+ EXPECT_TRUE(foo.push_back(0));
+ EXPECT_FALSE(foo.push_back(2));
+ EXPECT_FALSE(foo.push_back(1));
+
+ EXPECT_EQ(3u, foo.size());
+ EXPECT_EQ(1, foo[0]);
+ EXPECT_EQ(2, foo[1]);
+ EXPECT_EQ(0, foo[2]);
+
+ // Verify those results with IndexOf as well.
+ EXPECT_EQ(0u, foo.IndexOf(1));
+ EXPECT_EQ(1u, foo.IndexOf(2));
+ EXPECT_EQ(2u, foo.IndexOf(0));
+ EXPECT_EQ(static_cast<size_t>(-1), foo.IndexOf(99));
+}
+
+TEST(UniqueVector, PushBackMove) {
+ UniqueVector<std::string> vect;
+ std::string a("a");
+ EXPECT_TRUE(vect.push_back(std::move(a)));
+ EXPECT_EQ("", a);
+
+ a = "a";
+ EXPECT_FALSE(vect.push_back(std::move(a)));
+ EXPECT_EQ("a", a);
+
+ EXPECT_EQ(0u, vect.IndexOf("a"));
+}
diff --git a/gn/src/gn/value.cc b/gn/src/gn/value.cc
new file mode 100644
index 00000000000..c3bb9b512c7
--- /dev/null
+++ b/gn/src/gn/value.cc
@@ -0,0 +1,262 @@
+// 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 "gn/value.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "gn/scope.h"
+
+// NOTE: Cannot use = default here due to the use of a union member.
+Value::Value() {}
+
+Value::Value(const ParseNode* origin, Type t) : type_(t), origin_(origin) {
+ switch (type_) {
+ case NONE:
+ break;
+ case BOOLEAN:
+ boolean_value_ = false;
+ break;
+ case INTEGER:
+ int_value_ = 0;
+ break;
+ case STRING:
+ new (&string_value_) std::string();
+ break;
+ case LIST:
+ new (&list_value_) std::vector<Value>();
+ break;
+ case SCOPE:
+ new (&scope_value_) std::unique_ptr<Scope>();
+ break;
+ }
+}
+
+Value::Value(const ParseNode* origin, bool bool_val)
+ : type_(BOOLEAN), origin_(origin), boolean_value_(bool_val) {}
+
+Value::Value(const ParseNode* origin, int64_t int_val)
+ : type_(INTEGER), origin_(origin), int_value_(int_val) {}
+
+Value::Value(const ParseNode* origin, std::string str_val)
+ : type_(STRING), origin_(origin), string_value_(std::move(str_val)) {}
+
+Value::Value(const ParseNode* origin, const char* str_val)
+ : type_(STRING), origin_(origin), string_value_(str_val) {}
+
+Value::Value(const ParseNode* origin, std::unique_ptr<Scope> scope)
+ : type_(SCOPE), origin_(origin), scope_value_(std::move(scope)) {}
+
+Value::Value(const Value& other) : type_(other.type_), origin_(other.origin_) {
+ switch (type_) {
+ case NONE:
+ break;
+ case BOOLEAN:
+ boolean_value_ = other.boolean_value_;
+ break;
+ case INTEGER:
+ int_value_ = other.int_value_;
+ break;
+ case STRING:
+ new (&string_value_) std::string(other.string_value_);
+ break;
+ case LIST:
+ new (&list_value_) std::vector<Value>(other.list_value_);
+ break;
+ case SCOPE:
+ new (&scope_value_) std::unique_ptr<Scope>(
+ other.scope_value_.get() ? other.scope_value_->MakeClosure()
+ : nullptr);
+ break;
+ }
+}
+
+Value::Value(Value&& other) noexcept
+ : type_(other.type_), origin_(other.origin_) {
+ switch (type_) {
+ case NONE:
+ break;
+ case BOOLEAN:
+ boolean_value_ = other.boolean_value_;
+ break;
+ case INTEGER:
+ int_value_ = other.int_value_;
+ break;
+ case STRING:
+ new (&string_value_) std::string(std::move(other.string_value_));
+ break;
+ case LIST:
+ new (&list_value_) std::vector<Value>(std::move(other.list_value_));
+ break;
+ case SCOPE:
+ new (&scope_value_) std::unique_ptr<Scope>(std::move(other.scope_value_));
+ break;
+ }
+}
+
+Value& Value::operator=(const Value& other) {
+ if (this != &other) {
+ this->~Value();
+ new (this) Value(other);
+ }
+ return *this;
+}
+
+Value& Value::operator=(Value&& other) noexcept {
+ if (this != &other) {
+ this->~Value();
+ new (this) Value(std::move(other));
+ }
+ return *this;
+}
+
+Value::~Value() {
+ using namespace std;
+ switch (type_) {
+ case STRING:
+ string_value_.~string();
+ break;
+ case LIST:
+ list_value_.~vector<Value>();
+ break;
+ case SCOPE:
+ scope_value_.~unique_ptr<Scope>();
+ break;
+ default:;
+ }
+}
+
+// static
+const char* Value::DescribeType(Type t) {
+ switch (t) {
+ case NONE:
+ return "none";
+ case BOOLEAN:
+ return "boolean";
+ case INTEGER:
+ return "integer";
+ case STRING:
+ return "string";
+ case LIST:
+ return "list";
+ case SCOPE:
+ return "scope";
+ default:
+ NOTREACHED();
+ return "UNKNOWN";
+ }
+}
+
+void Value::SetScopeValue(std::unique_ptr<Scope> scope) {
+ DCHECK(type_ == SCOPE);
+ scope_value_ = std::move(scope);
+}
+
+std::string Value::ToString(bool quote_string) const {
+ switch (type_) {
+ case NONE:
+ return "<void>";
+ case BOOLEAN:
+ return boolean_value_ ? "true" : "false";
+ case INTEGER:
+ return base::Int64ToString(int_value_);
+ case STRING:
+ if (quote_string) {
+ std::string result = "\"";
+ bool hanging_backslash = false;
+ for (char ch : string_value_) {
+ // If the last character was a literal backslash and the next
+ // character could form a valid escape sequence, we need to insert
+ // an extra backslash to prevent that.
+ if (hanging_backslash && (ch == '$' || ch == '"' || ch == '\\'))
+ result += '\\';
+ // If the next character is a dollar sign or double quote, it needs
+ // to be escaped; otherwise it can be printed as is.
+ if (ch == '$' || ch == '"')
+ result += '\\';
+ result += ch;
+ hanging_backslash = (ch == '\\');
+ }
+ // Again, we need to prevent the closing double quotes from becoming
+ // an escape sequence.
+ if (hanging_backslash)
+ result += '\\';
+ result += '"';
+ return result;
+ }
+ return string_value_;
+ case LIST: {
+ std::string result = "[";
+ for (size_t i = 0; i < list_value_.size(); i++) {
+ if (i > 0)
+ result += ", ";
+ result += list_value_[i].ToString(true);
+ }
+ result.push_back(']');
+ return result;
+ }
+ case SCOPE: {
+ Scope::KeyValueMap scope_values;
+ scope_value_->GetCurrentScopeValues(&scope_values);
+ if (scope_values.empty())
+ return std::string("{ }");
+
+ std::string result = "{\n";
+ for (const auto& pair : scope_values) {
+ result += " " + std::string(pair.first) + " = " +
+ pair.second.ToString(true) + "\n";
+ }
+ result += "}";
+
+ return result;
+ }
+ }
+ return std::string();
+}
+
+bool Value::VerifyTypeIs(Type t, Err* err) const {
+ if (type_ == t)
+ return true;
+
+ *err = Err(origin(), std::string("This is not a ") + DescribeType(t) + ".",
+ std::string("Instead I see a ") + DescribeType(type_) + " = " +
+ ToString(true));
+ return false;
+}
+
+bool Value::operator==(const Value& other) const {
+ if (type_ != other.type_)
+ return false;
+
+ switch (type_) {
+ case Value::BOOLEAN:
+ return boolean_value() == other.boolean_value();
+ case Value::INTEGER:
+ return int_value() == other.int_value();
+ case Value::STRING:
+ return string_value() == other.string_value();
+ case Value::LIST:
+ if (list_value().size() != other.list_value().size())
+ return false;
+ for (size_t i = 0; i < list_value().size(); i++) {
+ if (list_value()[i] != other.list_value()[i])
+ return false;
+ }
+ return true;
+ case Value::SCOPE:
+ return scope_value()->CheckCurrentScopeValuesEqual(other.scope_value());
+ case Value::NONE:
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool Value::operator!=(const Value& other) const {
+ return !operator==(other);
+}
diff --git a/gn/src/gn/value.h b/gn/src/gn/value.h
new file mode 100644
index 00000000000..bf0107342bb
--- /dev/null
+++ b/gn/src/gn/value.h
@@ -0,0 +1,137 @@
+// 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 TOOLS_GN_VALUE_H_
+#define TOOLS_GN_VALUE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "gn/err.h"
+
+class ParseNode;
+class Scope;
+
+// Represents a variable value in the interpreter.
+class Value {
+ public:
+ enum Type {
+ NONE = 0,
+ BOOLEAN,
+ INTEGER,
+ STRING,
+ LIST,
+ SCOPE,
+ };
+
+ Value();
+ Value(const ParseNode* origin, Type t);
+ Value(const ParseNode* origin, bool bool_val);
+ Value(const ParseNode* origin, int64_t int_val);
+ Value(const ParseNode* origin, std::string str_val);
+ Value(const ParseNode* origin, const char* str_val);
+ // Values "shouldn't" have null scopes when type == Scope, so be sure to
+ // always set one. However, this is not asserted since there are some
+ // use-cases for creating values and immediately setting the scope on it. So
+ // you can pass a null scope here if you promise to set it before any other
+ // code gets it (code will generally assume the scope is not null).
+ Value(const ParseNode* origin, std::unique_ptr<Scope> scope);
+
+ Value(const Value& other);
+ Value(Value&& other) noexcept;
+ ~Value();
+
+ Value& operator=(const Value& other);
+ Value& operator=(Value&& other) noexcept;
+
+ Type type() const { return type_; }
+
+ // Returns a string describing the given type.
+ static const char* DescribeType(Type t);
+
+ // Returns the node that made this. May be NULL.
+ const ParseNode* origin() const { return origin_; }
+ void set_origin(const ParseNode* o) { origin_ = o; }
+
+ bool& boolean_value() {
+ DCHECK(type_ == BOOLEAN);
+ return boolean_value_;
+ }
+ const bool& boolean_value() const {
+ DCHECK(type_ == BOOLEAN);
+ return boolean_value_;
+ }
+
+ int64_t& int_value() {
+ DCHECK(type_ == INTEGER);
+ return int_value_;
+ }
+ const int64_t& int_value() const {
+ DCHECK(type_ == INTEGER);
+ return int_value_;
+ }
+
+ std::string& string_value() {
+ DCHECK(type_ == STRING);
+ return string_value_;
+ }
+ const std::string& string_value() const {
+ DCHECK(type_ == STRING);
+ return string_value_;
+ }
+
+ std::vector<Value>& list_value() {
+ DCHECK(type_ == LIST);
+ return list_value_;
+ }
+ const std::vector<Value>& list_value() const {
+ DCHECK(type_ == LIST);
+ return list_value_;
+ }
+
+ Scope* scope_value() {
+ DCHECK(type_ == SCOPE);
+ return scope_value_.get();
+ }
+ const Scope* scope_value() const {
+ DCHECK(type_ == SCOPE);
+ return scope_value_.get();
+ }
+ void SetScopeValue(std::unique_ptr<Scope> scope);
+
+ // Converts the given value to a string. Returns true if strings should be
+ // quoted or the ToString of a string should be the string itself. If the
+ // string is quoted, it will also enable escaping.
+ std::string ToString(bool quote_strings) const;
+
+ // Verifies that the value is of the given type. If it isn't, returns
+ // false and sets the error.
+ bool VerifyTypeIs(Type t, Err* err) const;
+
+ // Compares values. Only the "value" is compared, not the origin. Scope
+ // values check only the contents of the current scope, and do not go to
+ // parent scopes.
+ bool operator==(const Value& other) const;
+ bool operator!=(const Value& other) const;
+
+ private:
+ void Deallocate();
+
+ Type type_ = NONE;
+ const ParseNode* origin_ = nullptr;
+
+ union {
+ bool boolean_value_;
+ int64_t int_value_;
+ std::string string_value_;
+ std::vector<Value> list_value_;
+ std::unique_ptr<Scope> scope_value_;
+ };
+};
+
+#endif // TOOLS_GN_VALUE_H_
diff --git a/gn/src/gn/value_extractors.cc b/gn/src/gn/value_extractors.cc
new file mode 100644
index 00000000000..9eb759318d1
--- /dev/null
+++ b/gn/src/gn/value_extractors.cc
@@ -0,0 +1,323 @@
+// 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 "gn/value_extractors.h"
+
+#include <stddef.h>
+
+#include "gn/build_settings.h"
+#include "gn/err.h"
+#include "gn/frameworks_utils.h"
+#include "gn/label.h"
+#include "gn/source_dir.h"
+#include "gn/source_file.h"
+#include "gn/target.h"
+#include "gn/value.h"
+
+namespace {
+
+// Sets the error and returns false on failure.
+template <typename T, class Converter>
+bool ListValueExtractor(const Value& value,
+ std::vector<T>* dest,
+ Err* err,
+ const Converter& converter) {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return false;
+ const std::vector<Value>& input_list = value.list_value();
+ dest->resize(input_list.size());
+ for (size_t i = 0; i < input_list.size(); i++) {
+ if (!converter(input_list[i], &(*dest)[i], err))
+ return false;
+ }
+ return true;
+}
+
+// Like the above version but extracts to a UniqueVector and sets the error if
+// there are duplicates.
+template <typename T, class Converter>
+bool ListValueUniqueExtractor(const Value& value,
+ UniqueVector<T>* dest,
+ Err* err,
+ const Converter& converter) {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return false;
+ const std::vector<Value>& input_list = value.list_value();
+
+ for (const auto& item : input_list) {
+ T new_one;
+ if (!converter(item, &new_one, err))
+ return false;
+ if (!dest->push_back(new_one)) {
+ // Already in the list, throw error.
+ *err = Err(item, "Duplicate item in list");
+ size_t previous_index = dest->IndexOf(new_one);
+ err->AppendSubErr(
+ Err(input_list[previous_index], "This was the previous definition."));
+ return false;
+ }
+ }
+ return true;
+}
+
+struct RelativeFileConverter {
+ RelativeFileConverter(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in)
+ : build_settings(build_settings_in), current_dir(current_dir_in) {}
+ bool operator()(const Value& v, SourceFile* out, Err* err) const {
+ *out = current_dir.ResolveRelativeFile(v, err,
+ build_settings->root_path_utf8());
+ return !err->has_error();
+ }
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+};
+
+struct LibFileConverter {
+ LibFileConverter(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in)
+ : build_settings(build_settings_in), current_dir(current_dir_in) {}
+ bool operator()(const Value& v, LibFile* out, Err* err) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return false;
+ if (!GetFrameworkName(v.string_value()).empty()) {
+ *err = Err(v, "Unsupported value in libs.",
+ "Use frameworks to list framework dependencies.");
+ return false;
+ }
+ if (v.string_value().find('/') == std::string::npos) {
+ *out = LibFile(v.string_value());
+ } else {
+ *out = LibFile(current_dir.ResolveRelativeFile(
+ v, err, build_settings->root_path_utf8()));
+ }
+ return !err->has_error();
+ }
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+};
+
+struct RelativeDirConverter {
+ RelativeDirConverter(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in)
+ : build_settings(build_settings_in), current_dir(current_dir_in) {}
+ bool operator()(const Value& v, SourceDir* out, Err* err) const {
+ *out = current_dir.ResolveRelativeDir(v, err,
+ build_settings->root_path_utf8());
+ return true;
+ }
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+};
+
+struct ExternConverter {
+ ExternConverter(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in)
+ : build_settings(build_settings_in), current_dir(current_dir_in) {}
+ bool operator()(const Value& v,
+ std::pair<std::string, LibFile>* out,
+ Err* err) const {
+ if (!v.VerifyTypeIs(Value::SCOPE, err))
+ return false;
+ Scope::KeyValueMap scope;
+ v.scope_value()->GetCurrentScopeValues(&scope);
+ std::string cratename;
+ if (auto it = scope.find("crate_name"); it != scope.end()) {
+ if (!it->second.VerifyTypeIs(Value::STRING, err))
+ return false;
+ cratename = it->second.string_value();
+ } else {
+ return false;
+ }
+ LibFile path;
+ if (auto it = scope.find("path"); it != scope.end()) {
+ if (!it->second.VerifyTypeIs(Value::STRING, err))
+ return false;
+ if (it->second.string_value().find('/') == std::string::npos) {
+ path = LibFile(it->second.string_value());
+ } else {
+ path = LibFile(current_dir.ResolveRelativeFile(
+ it->second, err, build_settings->root_path_utf8()));
+ }
+ } else {
+ return false;
+ }
+ *out = std::pair(cratename, path);
+ return !err->has_error();
+ }
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+};
+
+// Fills in a label.
+template <typename T>
+struct LabelResolver {
+ LabelResolver(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in,
+ const Label& current_toolchain_in)
+ : build_settings(build_settings_in),
+ current_dir(current_dir_in),
+ current_toolchain(current_toolchain_in) {}
+ bool operator()(const Value& v, Label* out, Err* err) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return false;
+ *out = Label::Resolve(current_dir, build_settings->root_path_utf8(),
+ current_toolchain, v, err);
+ return !err->has_error();
+ }
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+ const Label& current_toolchain;
+};
+
+// Fills the label part of a LabelPtrPair, leaving the pointer null.
+template <typename T>
+struct LabelPtrResolver {
+ LabelPtrResolver(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in,
+ const Label& current_toolchain_in)
+ : build_settings(build_settings_in),
+ current_dir(current_dir_in),
+ current_toolchain(current_toolchain_in) {}
+ bool operator()(const Value& v, LabelPtrPair<T>* out, Err* err) const {
+ if (!v.VerifyTypeIs(Value::STRING, err))
+ return false;
+ out->label = Label::Resolve(current_dir, build_settings->root_path_utf8(),
+ current_toolchain, v, err);
+ out->origin = v.origin();
+ return !err->has_error();
+ }
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+ const Label& current_toolchain;
+};
+
+struct LabelPatternResolver {
+ LabelPatternResolver(const BuildSettings* build_settings_in,
+ const SourceDir& current_dir_in)
+ : build_settings(build_settings_in), current_dir(current_dir_in) {}
+ bool operator()(const Value& v, LabelPattern* out, Err* err) const {
+ *out = LabelPattern::GetPattern(current_dir,
+ build_settings->root_path_utf8(), v, err);
+ return !err->has_error();
+ }
+
+ const BuildSettings* build_settings;
+ const SourceDir& current_dir;
+};
+
+} // namespace
+
+bool ExtractListOfStringValues(const Value& value,
+ std::vector<std::string>* dest,
+ Err* err) {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return false;
+ const std::vector<Value>& input_list = value.list_value();
+ dest->reserve(input_list.size());
+ for (const auto& item : input_list) {
+ if (!item.VerifyTypeIs(Value::STRING, err))
+ return false;
+ dest->push_back(item.string_value());
+ }
+ return true;
+}
+
+bool ExtractListOfRelativeFiles(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceFile>* files,
+ Err* err) {
+ return ListValueExtractor(value, files, err,
+ RelativeFileConverter(build_settings, current_dir));
+}
+
+bool ExtractListOfLibs(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<LibFile>* libs,
+ Err* err) {
+ return ListValueExtractor(value, libs, err,
+ LibFileConverter(build_settings, current_dir));
+}
+
+bool ExtractListOfRelativeDirs(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceDir>* dest,
+ Err* err) {
+ return ListValueExtractor(value, dest, err,
+ RelativeDirConverter(build_settings, current_dir));
+}
+
+bool ExtractListOfLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ LabelTargetVector* dest,
+ Err* err) {
+ return ListValueExtractor(
+ value, dest, err,
+ LabelPtrResolver<Target>(build_settings, current_dir, current_toolchain));
+}
+
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ UniqueVector<Label>* dest,
+ Err* err) {
+ return ListValueUniqueExtractor(
+ value, dest, err,
+ LabelResolver<Config>(build_settings, current_dir, current_toolchain));
+}
+
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ UniqueVector<LabelConfigPair>* dest,
+ Err* err) {
+ return ListValueUniqueExtractor(
+ value, dest, err,
+ LabelPtrResolver<Config>(build_settings, current_dir, current_toolchain));
+}
+
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ UniqueVector<LabelTargetPair>* dest,
+ Err* err) {
+ return ListValueUniqueExtractor(
+ value, dest, err,
+ LabelPtrResolver<Target>(build_settings, current_dir, current_toolchain));
+}
+
+bool ExtractRelativeFile(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ SourceFile* file,
+ Err* err) {
+ RelativeFileConverter converter(build_settings, current_dir);
+ return converter(value, file, err);
+}
+
+bool ExtractListOfLabelPatterns(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<LabelPattern>* patterns,
+ Err* err) {
+ return ListValueExtractor(value, patterns, err,
+ LabelPatternResolver(build_settings, current_dir));
+}
+
+bool ExtractListOfExterns(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<std::pair<std::string, LibFile>>* externs,
+ Err* err) {
+ return ListValueExtractor(value, externs, err,
+ ExternConverter(build_settings, current_dir));
+}
diff --git a/gn/src/gn/value_extractors.h b/gn/src/gn/value_extractors.h
new file mode 100644
index 00000000000..9cc9e0086d3
--- /dev/null
+++ b/gn/src/gn/value_extractors.h
@@ -0,0 +1,100 @@
+// 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 TOOLS_GN_VALUE_EXTRACTORS_H_
+#define TOOLS_GN_VALUE_EXTRACTORS_H_
+
+#include <string>
+#include <vector>
+
+#include "gn/label_ptr.h"
+#include "gn/lib_file.h"
+#include "gn/unique_vector.h"
+
+class BuildSettings;
+class Err;
+class Label;
+class LabelPattern;
+class SourceDir;
+class SourceFile;
+class Value;
+
+// On failure, returns false and sets the error.
+bool ExtractListOfStringValues(const Value& value,
+ std::vector<std::string>* dest,
+ Err* err);
+
+// Looks for a list of source files relative to a given current dir.
+bool ExtractListOfRelativeFiles(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceFile>* files,
+ Err* err);
+
+// Extracts a list of libraries. When they contain a "/" they are treated as
+// source paths and are otherwise treated as plain strings.
+bool ExtractListOfLibs(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<LibFile>* libs,
+ Err* err);
+
+// Looks for a list of source directories relative to a given current dir.
+bool ExtractListOfRelativeDirs(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<SourceDir>* dest,
+ Err* err);
+
+// Extracts the list of labels and their origins to the given vector. Only the
+// labels are filled in, the ptr for each pair in the vector will be null.
+bool ExtractListOfLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ LabelTargetVector* dest,
+ Err* err);
+
+// Extracts the list of labels and their origins to the given vector. For the
+// version taking Label*Pair, only the labels are filled in, the ptr for each
+// pair in the vector will be null. Sets an error and returns false if a label
+// is maformed or there are duplicates.
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ UniqueVector<Label>* dest,
+ Err* err);
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ UniqueVector<LabelConfigPair>* dest,
+ Err* err);
+bool ExtractListOfUniqueLabels(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ const Label& current_toolchain,
+ UniqueVector<LabelTargetPair>* dest,
+ Err* err);
+
+bool ExtractRelativeFile(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ SourceFile* file,
+ Err* err);
+
+bool ExtractListOfLabelPatterns(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<LabelPattern>* patterns,
+ Err* err);
+
+bool ExtractListOfExterns(const BuildSettings* build_settings,
+ const Value& value,
+ const SourceDir& current_dir,
+ std::vector<std::pair<std::string, LibFile>>* externs,
+ Err* err);
+
+#endif // TOOLS_GN_VALUE_EXTRACTORS_H_
diff --git a/gn/src/gn/value_unittest.cc b/gn/src/gn/value_unittest.cc
new file mode 100644
index 00000000000..555f10112f8
--- /dev/null
+++ b/gn/src/gn/value_unittest.cc
@@ -0,0 +1,60 @@
+// 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 <stdint.h>
+
+#include "gn/test_with_scope.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+TEST(Value, ToString) {
+ Value strval(nullptr, "hi\" $me\\you\\$\\\"");
+ EXPECT_EQ("hi\" $me\\you\\$\\\"", strval.ToString(false));
+ EXPECT_EQ("\"hi\\\" \\$me\\you\\\\\\$\\\\\\\"\"", strval.ToString(true));
+
+ // crbug.com/470217
+ Value strval2(nullptr, "\\foo\\\\bar\\");
+ EXPECT_EQ("\"\\foo\\\\\\bar\\\\\"", strval2.ToString(true));
+
+ // Void type.
+ EXPECT_EQ("<void>", Value().ToString(false));
+
+ // Test lists, bools, and ints.
+ Value listval(nullptr, Value::LIST);
+ listval.list_value().push_back(Value(nullptr, "hi\"me"));
+ listval.list_value().push_back(Value(nullptr, true));
+ listval.list_value().push_back(Value(nullptr, false));
+ listval.list_value().push_back(Value(nullptr, static_cast<int64_t>(42)));
+ // Printing lists always causes embedded strings to be quoted (ignoring the
+ // quote flag), or else they wouldn't make much sense.
+ EXPECT_EQ("[\"hi\\\"me\", true, false, 42]", listval.ToString(false));
+ EXPECT_EQ("[\"hi\\\"me\", true, false, 42]", listval.ToString(true));
+
+ // Scopes.
+ TestWithScope setup;
+ Scope* scope = new Scope(setup.settings());
+ Value scopeval(nullptr, std::unique_ptr<Scope>(scope));
+ EXPECT_EQ("{ }", scopeval.ToString(false));
+
+ // Test that an empty scope equals an empty scope.
+ EXPECT_TRUE(scopeval == scopeval);
+
+ scope->SetValue("a", Value(nullptr, static_cast<int64_t>(42)), nullptr);
+ scope->SetValue("b", Value(nullptr, "hello, world"), nullptr);
+ EXPECT_EQ("{\n a = 42\n b = \"hello, world\"\n}", scopeval.ToString(false));
+ EXPECT_TRUE(scopeval == scopeval);
+
+ Scope* inner_scope = new Scope(setup.settings());
+ Value inner_scopeval(nullptr, std::unique_ptr<Scope>(inner_scope));
+ inner_scope->SetValue("d", Value(nullptr, static_cast<int64_t>(42)), nullptr);
+ scope->SetValue("c", inner_scopeval, nullptr);
+
+ // Test inner scope equality.
+ EXPECT_TRUE(scopeval == scopeval);
+
+ // Nested scopes should not be equal.
+ Scope* nested_scope = new Scope(scope);
+ Value nested_scopeval(nullptr, std::unique_ptr<Scope>(nested_scope));
+ EXPECT_FALSE(nested_scopeval == nested_scopeval);
+}
diff --git a/gn/src/gn/variables.cc b/gn/src/gn/variables.cc
new file mode 100644
index 00000000000..d5c5e45859b
--- /dev/null
+++ b/gn/src/gn/variables.cc
@@ -0,0 +1,2342 @@
+// 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 "gn/variables.h"
+
+#include "gn/rust_variables.h"
+#include "gn/swift_variables.h"
+
+namespace variables {
+
+// Built-in variables ----------------------------------------------------------
+
+const char kGnVersion[] = "gn_version";
+const char kGnVersion_HelpShort[] =
+ "gn_version: [number] The version of gn.";
+const char kGnVersion_Help[] =
+ R"(gn_version: [number] The version of gn.
+
+ Corresponds to the number printed by `gn --version`.
+
+Example
+
+ assert(gn_version >= 1700, "need GN version 1700 for the frobulate feature")
+)";
+
+const char kHostCpu[] = "host_cpu";
+const char kHostCpu_HelpShort[] =
+ "host_cpu: [string] The processor architecture that GN is running on.";
+const char kHostCpu_Help[] =
+ R"(host_cpu: The processor architecture that GN is running on.
+
+ This is value is exposed so that cross-compile toolchains can access the host
+ architecture when needed.
+
+ The value should generally be considered read-only, but it can be overridden
+ in order to handle unusual cases where there might be multiple plausible
+ values for the host architecture (e.g., if you can do either 32-bit or 64-bit
+ builds). The value is not used internally by GN for any purpose.
+
+Some possible values
+
+ - "x64"
+ - "x86"
+)";
+
+const char kHostOs[] = "host_os";
+const char kHostOs_HelpShort[] =
+ "host_os: [string] The operating system that GN is running on.";
+const char kHostOs_Help[] =
+ R"(host_os: [string] The operating system that GN is running on.
+
+ This value is exposed so that cross-compiles can access the host build
+ system's settings.
+
+ This value should generally be treated as read-only. It, however, is not used
+ internally by GN for any purpose.
+
+Some possible values
+
+ - "linux"
+ - "mac"
+ - "win"
+)";
+
+const char kInvoker[] = "invoker";
+const char kInvoker_HelpShort[] =
+ "invoker: [string] The invoking scope inside a template.";
+const char kInvoker_Help[] =
+ R"(invoker: [string] The invoking scope inside a template.
+
+ Inside a template invocation, this variable refers to the scope of the
+ invoker of the template. Outside of template invocations, this variable is
+ undefined.
+
+ All of the variables defined inside the template invocation are accessible as
+ members of the "invoker" scope. This is the way that templates read values
+ set by the callers.
+
+ This is often used with "defined" to see if a value is set on the invoking
+ scope.
+
+ See "gn help template" for more examples.
+
+Example
+
+ template("my_template") {
+ print(invoker.sources) # Prints [ "a.cc", "b.cc" ]
+ print(defined(invoker.foo)) # Prints false.
+ print(defined(invoker.bar)) # Prints true.
+ }
+
+ my_template("doom_melon") {
+ sources = [ "a.cc", "b.cc" ]
+ bar = 123
+ }
+)";
+
+const char kTargetCpu[] = "target_cpu";
+const char kTargetCpu_HelpShort[] =
+ "target_cpu: [string] The desired cpu architecture for the build.";
+const char kTargetCpu_Help[] =
+ R"(target_cpu: The desired cpu architecture for the build.
+
+ This value should be used to indicate the desired architecture for the
+ primary objects of the build. It will match the cpu architecture of the
+ default toolchain, but not necessarily the current toolchain.
+
+ In many cases, this is the same as "host_cpu", but in the case of
+ cross-compiles, this can be set to something different. This value is
+ different from "current_cpu" in that it does not change based on the current
+ toolchain. When writing rules, "current_cpu" should be used rather than
+ "target_cpu" most of the time.
+
+ This value is not used internally by GN for any purpose, so it may be set to
+ whatever value is needed for the build. GN defaults this value to the empty
+ string ("") and the configuration files should set it to an appropriate value
+ (e.g., setting it to the value of "host_cpu") if it is not overridden on the
+ command line or in the args.gn file.
+
+Possible values
+
+ - "x86"
+ - "x64"
+ - "arm"
+ - "arm64"
+ - "mipsel"
+)";
+
+const char kTargetName[] = "target_name";
+const char kTargetName_HelpShort[] =
+ "target_name: [string] The name of the current target.";
+const char kTargetName_Help[] =
+ R"(target_name: [string] The name of the current target.
+
+ Inside a target or template invocation, this variable refers to the name
+ given to the target or template invocation. Outside of these, this variable
+ is undefined.
+
+ This is most often used in template definitions to name targets defined in
+ the template based on the name of the invocation. This is necessary both to
+ ensure generated targets have unique names and to generate a target with the
+ exact name of the invocation that other targets can depend on.
+
+ Be aware that this value will always reflect the innermost scope. So when
+ defining a target inside a template, target_name will refer to the target
+ rather than the template invocation. To get the name of the template
+ invocation in this case, you should save target_name to a temporary variable
+ outside of any target definitions.
+
+ See "gn help template" for more examples.
+
+Example
+
+ executable("doom_melon") {
+ print(target_name) # Prints "doom_melon".
+ }
+
+ template("my_template") {
+ print(target_name) # Prints "space_ray" when invoked below.
+
+ executable(target_name + "_impl") {
+ print(target_name) # Prints "space_ray_impl".
+ }
+ }
+
+ my_template("space_ray") {
+ }
+)";
+
+const char kTargetOs[] = "target_os";
+const char kTargetOs_HelpShort[] =
+ "target_os: [string] The desired operating system for the build.";
+const char kTargetOs_Help[] =
+ R"(target_os: The desired operating system for the build.
+
+ This value should be used to indicate the desired operating system for the
+ primary object(s) of the build. It will match the OS of the default
+ toolchain.
+
+ In many cases, this is the same as "host_os", but in the case of
+ cross-compiles, it may be different. This variable differs from "current_os"
+ in that it can be referenced from inside any toolchain and will always return
+ the initial value.
+
+ This should be set to the most specific value possible. So, "android" or
+ "chromeos" should be used instead of "linux" where applicable, even though
+ Android and ChromeOS are both Linux variants. This can mean that one needs to
+ write
+
+ if (target_os == "android" || target_os == "linux") {
+ # ...
+ }
+
+ and so forth.
+
+ This value is not used internally by GN for any purpose, so it may be set to
+ whatever value is needed for the build. GN defaults this value to the empty
+ string ("") and the configuration files should set it to an appropriate value
+ (e.g., setting it to the value of "host_os") if it is not set via the command
+ line or in the args.gn file.
+
+Possible values
+
+ - "android"
+ - "chromeos"
+ - "ios"
+ - "linux"
+ - "nacl"
+ - "mac"
+ - "win"
+)";
+
+const char kCurrentCpu[] = "current_cpu";
+const char kCurrentCpu_HelpShort[] =
+ "current_cpu: [string] The processor architecture of the current "
+ "toolchain.";
+const char kCurrentCpu_Help[] =
+ R"(current_cpu: The processor architecture of the current toolchain.
+
+ The build configuration usually sets this value based on the value of
+ "host_cpu" (see "gn help host_cpu") and then threads this through the
+ toolchain definitions to ensure that it always reflects the appropriate
+ value.
+
+ This value is not used internally by GN for any purpose. It is set to the
+ empty string ("") by default but is declared so that it can be overridden on
+ the command line if so desired.
+
+ See "gn help target_cpu" for a list of common values returned.)";
+
+const char kCurrentOs[] = "current_os";
+const char kCurrentOs_HelpShort[] =
+ "current_os: [string] The operating system of the current toolchain.";
+const char kCurrentOs_Help[] =
+ R"(current_os: The operating system of the current toolchain.
+
+ The build configuration usually sets this value based on the value of
+ "target_os" (see "gn help target_os"), and then threads this through the
+ toolchain definitions to ensure that it always reflects the appropriate
+ value.
+
+ This value is not used internally by GN for any purpose. It is set to the
+ empty string ("") by default but is declared so that it can be overridden on
+ the command line if so desired.
+
+ See "gn help target_os" for a list of common values returned.
+)";
+
+const char kCurrentToolchain[] = "current_toolchain";
+const char kCurrentToolchain_HelpShort[] =
+ "current_toolchain: [string] Label of the current toolchain.";
+const char kCurrentToolchain_Help[] =
+ R"(current_toolchain: Label of the current toolchain.
+
+ A fully-qualified label representing the current toolchain. You can use this
+ to make toolchain-related decisions in the build. See also
+ "default_toolchain".
+
+Example
+
+ if (current_toolchain == "//build:64_bit_toolchain") {
+ executable("output_thats_64_bit_only") {
+ ...
+)";
+
+const char kDefaultToolchain[] = "default_toolchain";
+const char kDefaultToolchain_HelpShort[] =
+ "default_toolchain: [string] Label of the default toolchain.";
+const char kDefaultToolchain_Help[] =
+ R"(default_toolchain: [string] Label of the default toolchain.
+
+ A fully-qualified label representing the default toolchain, which may not
+ necessarily be the current one (see "current_toolchain").
+)";
+
+const char kPythonPath[] = "python_path";
+const char kPythonPath_HelpShort[] =
+ "python_path: [string] Absolute path of Python.";
+const char kPythonPath_Help[] =
+ R"(python_path: Absolute path of Python.
+
+ Normally used in toolchain definitions if running some command requires
+ Python. You will normally not need this when invoking scripts since GN
+ automatically finds it for you.
+)";
+
+const char kRootBuildDir[] = "root_build_dir";
+const char kRootBuildDir_HelpShort[] =
+ "root_build_dir: [string] Directory where build commands are run.";
+const char kRootBuildDir_Help[] =
+ R"(root_build_dir: [string] Directory where build commands are run.
+
+ This is the root build output directory which will be the current directory
+ when executing all compilers and scripts.
+
+ Most often this is used with rebase_path (see "gn help rebase_path") to
+ convert arguments to be relative to a script's current directory.
+)";
+
+const char kRootGenDir[] = "root_gen_dir";
+const char kRootGenDir_HelpShort[] =
+ "root_gen_dir: [string] Directory for the toolchain's generated files.";
+const char kRootGenDir_Help[] =
+ R"(root_gen_dir: Directory for the toolchain's generated files.
+
+ Absolute path to the root of the generated output directory tree for the
+ current toolchain. An example would be "//out/Debug/gen" for the default
+ toolchain, or "//out/Debug/arm/gen" for the "arm" toolchain.
+
+ This is primarily useful for setting up include paths for generated files. If
+ you are passing this to a script, you will want to pass it through
+ rebase_path() (see "gn help rebase_path") to convert it to be relative to the
+ build directory.
+
+ See also "target_gen_dir" which is usually a better location for generated
+ files. It will be inside the root generated dir.
+)";
+
+const char kRootOutDir[] = "root_out_dir";
+const char kRootOutDir_HelpShort[] =
+ "root_out_dir: [string] Root directory for toolchain output files.";
+const char kRootOutDir_Help[] =
+ R"(root_out_dir: [string] Root directory for toolchain output files.
+
+ Absolute path to the root of the output directory tree for the current
+ toolchain. It will not have a trailing slash.
+
+ For the default toolchain this will be the same as the root_build_dir. An
+ example would be "//out/Debug" for the default toolchain, or
+ "//out/Debug/arm" for the "arm" toolchain.
+
+ This is primarily useful for setting up script calls. If you are passing this
+ to a script, you will want to pass it through rebase_path() (see "gn help
+ rebase_path") to convert it to be relative to the build directory.
+
+ See also "target_out_dir" which is usually a better location for output
+ files. It will be inside the root output dir.
+
+Example
+
+ action("myscript") {
+ # Pass the output dir to the script.
+ args = [ "-o", rebase_path(root_out_dir, root_build_dir) ]
+ }
+)";
+
+const char kTargetGenDir[] = "target_gen_dir";
+const char kTargetGenDir_HelpShort[] =
+ "target_gen_dir: [string] Directory for a target's generated files.";
+const char kTargetGenDir_Help[] =
+ R"(target_gen_dir: Directory for a target's generated files.
+
+ Absolute path to the target's generated file directory. This will be the
+ "root_gen_dir" followed by the relative path to the current build file. If
+ your file is in "//tools/doom_melon" then target_gen_dir would be
+ "//out/Debug/gen/tools/doom_melon". It will not have a trailing slash.
+
+ This is primarily useful for setting up include paths for generated files. If
+ you are passing this to a script, you will want to pass it through
+ rebase_path() (see "gn help rebase_path") to convert it to be relative to the
+ build directory.
+
+ See also "gn help root_gen_dir".
+
+Example
+
+ action("myscript") {
+ # Pass the generated output dir to the script.
+ args = [ "-o", rebase_path(target_gen_dir, root_build_dir) ]
+ }
+)";
+
+const char kTargetOutDir[] = "target_out_dir";
+const char kTargetOutDir_HelpShort[] =
+ "target_out_dir: [string] Directory for target output files.";
+const char kTargetOutDir_Help[] =
+ R"(target_out_dir: [string] Directory for target output files.
+
+ Absolute path to the target's generated file directory. If your current
+ target is in "//tools/doom_melon" then this value might be
+ "//out/Debug/obj/tools/doom_melon". It will not have a trailing slash.
+
+ This is primarily useful for setting up arguments for calling scripts. If you
+ are passing this to a script, you will want to pass it through rebase_path()
+ (see "gn help rebase_path") to convert it to be relative to the build
+ directory.
+
+ See also "gn help root_out_dir".
+
+Example
+
+ action("myscript") {
+ # Pass the output dir to the script.
+ args = [ "-o", rebase_path(target_out_dir, root_build_dir) ]
+ }
+)";
+
+// Target variables ------------------------------------------------------------
+
+#define COMMON_ORDERING_HELP \
+ "\n" \
+ "Ordering of flags and values\n" \
+ "\n" \
+ " 1. Those set on the current target (not in a config).\n" \
+ " 2. Those set on the \"configs\" on the target in order that the\n" \
+ " configs appear in the list.\n" \
+ " 3. Those set on the \"all_dependent_configs\" on the target in order\n" \
+ " that the configs appear in the list.\n" \
+ " 4. Those set on the \"public_configs\" on the target in order that\n" \
+ " those configs appear in the list.\n" \
+ " 5. all_dependent_configs pulled from dependencies, in the order of\n" \
+ " the \"deps\" list. This is done recursively. If a config appears\n" \
+ " more than once, only the first occurrence will be used.\n" \
+ " 6. public_configs pulled from dependencies, in the order of the\n" \
+ " \"deps\" list. If a dependency is public, they will be applied\n" \
+ " recursively.\n"
+
+const char kAllDependentConfigs[] = "all_dependent_configs";
+const char kAllDependentConfigs_HelpShort[] =
+ "all_dependent_configs: [label list] Configs to be forced on dependents.";
+const char kAllDependentConfigs_Help[] =
+ R"(all_dependent_configs: Configs to be forced on dependents.
+
+ A list of config labels.
+
+ All targets depending on this one, and recursively, all targets depending on
+ those, will have the configs listed in this variable added to them. These
+ configs will also apply to the current target.
+
+ This addition happens in a second phase once a target and all of its
+ dependencies have been resolved. Therefore, a target will not see these
+ force-added configs in their "configs" variable while the script is running,
+ and they can not be removed. As a result, this capability should generally
+ only be used to add defines and include directories necessary to compile a
+ target's headers.
+
+ See also "public_configs".
+)" COMMON_ORDERING_HELP;
+
+const char kAllowCircularIncludesFrom[] = "allow_circular_includes_from";
+const char kAllowCircularIncludesFrom_HelpShort[] =
+ "allow_circular_includes_from: [label list] Permit includes from deps.";
+const char kAllowCircularIncludesFrom_Help[] =
+ R"(allow_circular_includes_from: Permit includes from deps.
+
+ A list of target labels. Must be a subset of the target's "deps". These
+ targets will be permitted to include headers from the current target despite
+ the dependency going in the opposite direction.
+
+ When you use this, both targets must be included in a final binary for it to
+ link. To keep linker errors from happening, it is good practice to have all
+ external dependencies depend only on one of the two targets, and to set the
+ visibility on the other to enforce this. Thus the targets will always be
+ linked together in any output.
+
+Details
+
+ Normally, for a file in target A to include a file from target B, A must list
+ B as a dependency. This invariant is enforced by the "gn check" command (and
+ the --check flag to "gn gen" -- see "gn help check").
+
+ Sometimes, two targets might be the same unit for linking purposes (two
+ source sets or static libraries that would always be linked together in a
+ final executable or shared library) and they each include headers from the
+ other: you want A to be able to include B's headers, and B to include A's
+ headers. This is not an ideal situation but is sometimes unavoidable.
+
+ This list, if specified, lists which of the dependencies of the current
+ target can include header files from the current target. That is, if A
+ depends on B, B can only include headers from A if it is in A's
+ allow_circular_includes_from list. Normally includes must follow the
+ direction of dependencies, this flag allows them to go in the opposite
+ direction.
+
+Danger
+
+ In the above example, A's headers are likely to include headers from A's
+ dependencies. Those dependencies may have public_configs that apply flags,
+ defines, and include paths that make those headers work properly.
+
+ With allow_circular_includes_from, B can include A's headers, and
+ transitively from A's dependencies, without having the dependencies that
+ would bring in the public_configs those headers need. The result may be
+ errors or inconsistent builds.
+
+ So when you use allow_circular_includes_from, make sure that any compiler
+ settings, flags, and include directories are the same between both targets
+ (consider putting such things in a shared config they can both reference).
+ Make sure the dependencies are also the same (you might consider a group to
+ collect such dependencies they both depend on).
+
+Example
+
+ source_set("a") {
+ deps = [ ":b", ":a_b_shared_deps" ]
+ allow_circular_includes_from = [ ":b" ]
+ ...
+ }
+
+ source_set("b") {
+ deps = [ ":a_b_shared_deps" ]
+ # Sources here can include headers from a despite lack of deps.
+ ...
+ }
+
+ group("a_b_shared_deps") {
+ public_deps = [ ":c" ]
+ }
+)";
+
+const char kArflags[] = "arflags";
+const char kArflags_HelpShort[] =
+ "arflags: [string list] Arguments passed to static_library archiver.";
+const char kArflags_Help[] =
+ R"(arflags: Arguments passed to static_library archiver.
+
+ A list of flags passed to the archive/lib command that creates static
+ libraries.
+
+ arflags are NOT pushed to dependents, so applying arflags to source sets or
+ any other target type will be a no-op. As with ldflags, you could put the
+ arflags in a config and set that as a public or "all dependent" config, but
+ that will likely not be what you want. If you have a chain of static
+ libraries dependent on each other, this can cause the flags to propagate up
+ to other static libraries. Due to the nature of how arflags are typically
+ used, you will normally want to apply them directly on static_library targets
+ themselves.
+)" COMMON_ORDERING_HELP;
+
+const char kArgs[] = "args";
+const char kArgs_HelpShort[] =
+ "args: [string list] Arguments passed to an action.";
+const char kArgs_Help[] =
+ R"(args: (target variable) Arguments passed to an action.
+
+ For action and action_foreach targets, args is the list of arguments to pass
+ to the script. Typically you would use source expansion (see "gn help
+ source_expansion") to insert the source file names.
+
+ See also "gn help action" and "gn help action_foreach".
+)";
+
+const char kAssertNoDeps[] = "assert_no_deps";
+const char kAssertNoDeps_HelpShort[] =
+ "assert_no_deps: [label pattern list] Ensure no deps on these targets.";
+const char kAssertNoDeps_Help[] =
+ R"(assert_no_deps: Ensure no deps on these targets.
+
+ A list of label patterns.
+
+ This list is a list of patterns that must not match any of the transitive
+ dependencies of the target. These include all public, private, and data
+ dependencies, and cross shared library boundaries. This allows you to express
+ that undesirable code isn't accidentally added to downstream dependencies in
+ a way that might otherwise be difficult to notice.
+
+ Checking does not cross executable boundaries. If a target depends on an
+ executable, it's assumed that the executable is a tool that is producing part
+ of the build rather than something that is linked and distributed. This
+ allows assert_no_deps to express what is distributed in the final target
+ rather than depend on the internal build steps (which may include
+ non-distributable code).
+
+ See "gn help label_pattern" for the format of the entries in the list. These
+ patterns allow blacklisting individual targets or whole directory
+ hierarchies.
+
+ Sometimes it is desirable to enforce that many targets have no dependencies
+ on a target or set of targets. One efficient way to express this is to create
+ a group with the assert_no_deps rule on it, and make that group depend on all
+ targets you want to apply that assertion to.
+
+Example
+
+ executable("doom_melon") {
+ deps = [ "//foo:bar" ]
+ ...
+ assert_no_deps = [
+ "//evil/*", # Don't link any code from the evil directory.
+ "//foo:test_support", # This target is also disallowed.
+ ]
+ }
+)";
+
+const char kBundleRootDir[] = "bundle_root_dir";
+const char kBundleRootDir_HelpShort[] =
+ "bundle_root_dir: Expansion of {{bundle_root_dir}} in create_bundle.";
+const char kBundleRootDir_Help[] =
+ R"(bundle_root_dir: Expansion of {{bundle_root_dir}} in create_bundle.
+
+ A string corresponding to a path in root_build_dir.
+
+ This string is used by the "create_bundle" target to expand the
+ {{bundle_root_dir}} of the "bundle_data" target it depends on. This must
+ correspond to a path under root_build_dir.
+
+Example
+
+ bundle_data("info_plist") {
+ sources = [ "Info.plist" ]
+ outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
+ }
+
+ create_bundle("doom_melon.app") {
+ deps = [ ":info_plist" ]
+ bundle_root_dir = "${root_build_dir}/doom_melon.app"
+ bundle_contents_dir = "${bundle_root_dir}/Contents"
+ bundle_resources_dir = "${bundle_contents_dir}/Resources"
+ bundle_executable_dir = "${bundle_contents_dir}/MacOS"
+ }
+)";
+
+const char kBundleContentsDir[] = "bundle_contents_dir";
+const char kBundleContentsDir_HelpShort[] =
+ "bundle_contents_dir: "
+ "Expansion of {{bundle_contents_dir}} in create_bundle.";
+const char kBundleContentsDir_Help[] =
+ R"(bundle_contents_dir: Expansion of {{bundle_contents_dir}} in
+ create_bundle.
+
+ A string corresponding to a path in $root_build_dir.
+
+ This string is used by the "create_bundle" target to expand the
+ {{bundle_contents_dir}} of the "bundle_data" target it depends on. This must
+ correspond to a path under "bundle_root_dir".
+
+ See "gn help bundle_root_dir" for examples.
+)";
+
+const char kBundleResourcesDir[] = "bundle_resources_dir";
+const char kBundleResourcesDir_HelpShort[] =
+ "bundle_resources_dir: "
+ "Expansion of {{bundle_resources_dir}} in create_bundle.";
+const char kBundleResourcesDir_Help[] =
+ R"(bundle_resources_dir
+
+ bundle_resources_dir: Expansion of {{bundle_resources_dir}} in
+ create_bundle.
+
+ A string corresponding to a path in $root_build_dir.
+
+ This string is used by the "create_bundle" target to expand the
+ {{bundle_resources_dir}} of the "bundle_data" target it depends on. This must
+ correspond to a path under "bundle_root_dir".
+
+ See "gn help bundle_root_dir" for examples.
+)";
+
+const char kBundleDepsFilter[] = "bundle_deps_filter";
+const char kBundleDepsFilter_HelpShort[] =
+ "bundle_deps_filter: [label list] A list of labels that are filtered out.";
+const char kBundleDepsFilter_Help[] =
+ R"(bundle_deps_filter: [label list] A list of labels that are filtered out.
+
+ A list of target labels.
+
+ This list contains target label patterns that should be filtered out when
+ creating the bundle. Any target matching one of those label will be removed
+ from the dependencies of the create_bundle target.
+
+ This is mostly useful when creating application extension bundle as the
+ application extension has access to runtime resources from the application
+ bundle and thus do not require a second copy.
+
+ See "gn help create_bundle" for more information.
+
+Example
+
+ create_bundle("today_extension") {
+ deps = [
+ "//base"
+ ]
+ bundle_root_dir = "$root_out_dir/today_extension.appex"
+ bundle_deps_filter = [
+ # The extension uses //base but does not use any function calling into
+ # third_party/icu and thus does not need the icudtl.dat file.
+ "//third_party/icu:icudata",
+ ]
+ }
+)";
+
+const char kBundleExecutableDir[] = "bundle_executable_dir";
+const char kBundleExecutableDir_HelpShort[] =
+ "bundle_executable_dir: "
+ "Expansion of {{bundle_executable_dir}} in create_bundle";
+const char kBundleExecutableDir_Help[] =
+ R"(bundle_executable_dir
+
+ bundle_executable_dir: Expansion of {{bundle_executable_dir}} in
+ create_bundle.
+
+ A string corresponding to a path in $root_build_dir.
+
+ This string is used by the "create_bundle" target to expand the
+ {{bundle_executable_dir}} of the "bundle_data" target it depends on. This
+ must correspond to a path under "bundle_root_dir".
+
+ See "gn help bundle_root_dir" for examples.
+)";
+
+const char kXcassetCompilerFlags[] = "xcasset_compiler_flags";
+const char kXcassetCompilerFlags_HelpShort[] =
+ "xcasset_compiler_flags: [string list] Flags passed to xcassets compiler";
+const char kXcassetCompilerFlags_Help[] =
+ R"(xcasset_compiler_flags: Flags passed to xcassets compiler.
+
+ A list of strings.
+
+ Valid for create_bundle target. Those flags are directly passed to
+ xcassets compiler, corresponding to {{xcasset_compiler_flags}} substitution
+ in compile_xcassets tool.
+)";
+
+const char kCflags[] = "cflags";
+const char kCflags_HelpShort[] =
+ "cflags: [string list] Flags passed to all C compiler variants.";
+const char kCommonCflagsHelp[] =
+ R"(cflags*: Flags passed to the C compiler.
+
+ A list of strings.
+
+ "cflags" are passed to all invocations of the C, C++, Objective C, and
+ Objective C++ compilers.
+
+ To target one of these variants individually, use "cflags_c", "cflags_cc",
+ "cflags_objc", and "cflags_objcc", respectively. These variant-specific
+ versions of cflags* will be appended on the compiler command line after
+ "cflags".
+
+ See also "asmflags" for flags for assembly-language files and "swiftflags"
+ for swift files.
+)" COMMON_ORDERING_HELP;
+const char* kCflags_Help = kCommonCflagsHelp;
+
+const char kAsmflags[] = "asmflags";
+const char kAsmflags_HelpShort[] =
+ "asmflags: [string list] Flags passed to the assembler.";
+const char* kAsmflags_Help =
+ R"(asmflags: Flags passed to the assembler.
+
+ A list of strings.
+
+ "asmflags" are passed to any invocation of a tool that takes an .asm or .S
+ file as input.
+)" COMMON_ORDERING_HELP;
+
+const char kCflagsC[] = "cflags_c";
+const char kCflagsC_HelpShort[] =
+ "cflags_c: [string list] Flags passed to the C compiler.";
+const char* kCflagsC_Help = kCommonCflagsHelp;
+
+const char kCflagsCC[] = "cflags_cc";
+const char kCflagsCC_HelpShort[] =
+ "cflags_cc: [string list] Flags passed to the C++ compiler.";
+const char* kCflagsCC_Help = kCommonCflagsHelp;
+
+const char kCflagsObjC[] = "cflags_objc";
+const char kCflagsObjC_HelpShort[] =
+ "cflags_objc: [string list] Flags passed to the Objective C compiler.";
+const char* kCflagsObjC_Help = kCommonCflagsHelp;
+
+const char kCflagsObjCC[] = "cflags_objcc";
+const char kCflagsObjCC_HelpShort[] =
+ "cflags_objcc: [string list] Flags passed to the Objective C++ compiler.";
+const char* kCflagsObjCC_Help = kCommonCflagsHelp;
+
+const char kCheckIncludes[] = "check_includes";
+const char kCheckIncludes_HelpShort[] =
+ "check_includes: [boolean] Controls whether a target's files are checked.";
+const char kCheckIncludes_Help[] =
+ R"(check_includes: [boolean] Controls whether a target's files are checked.
+
+ When true (the default), the "gn check" command (as well as "gn gen" with the
+ --check flag) will check this target's sources and headers for proper
+ dependencies.
+
+ When false, the files in this target will be skipped by default. This does
+ not affect other targets that depend on the current target, it just skips
+ checking the includes of the current target's files.
+
+ If there are a few conditionally included headers that trip up checking, you
+ can exclude headers individually by annotating them with "nogncheck" (see "gn
+ help nogncheck").
+
+ The topic "gn help check" has general information on how checking works and
+ advice on how to pass a check in problematic cases.
+
+Example
+
+ source_set("busted_includes") {
+ # This target's includes are messed up, exclude it from checking.
+ check_includes = false
+ ...
+ }
+)";
+
+const char kCodeSigningArgs[] = "code_signing_args";
+const char kCodeSigningArgs_HelpShort[] =
+ "code_signing_args: [string list] Arguments passed to code signing script.";
+const char kCodeSigningArgs_Help[] =
+ R"(code_signing_args: [string list] Arguments passed to code signing script.
+
+ For create_bundle targets, code_signing_args is the list of arguments to pass
+ to the code signing script. Typically you would use source expansion (see "gn
+ help source_expansion") to insert the source file names.
+
+ See also "gn help create_bundle".
+)";
+
+const char kCodeSigningScript[] = "code_signing_script";
+const char kCodeSigningScript_HelpShort[] =
+ "code_signing_script: [file name] Script for code signing.";
+const char kCodeSigningScript_Help[] =
+ R"(code_signing_script: [file name] Script for code signing."
+
+ An absolute or buildfile-relative file name of a Python script to run for a
+ create_bundle target to perform code signing step.
+
+ See also "gn help create_bundle".
+)";
+
+const char kCodeSigningSources[] = "code_signing_sources";
+const char kCodeSigningSources_HelpShort[] =
+ "code_signing_sources: [file list] Sources for code signing step.";
+const char kCodeSigningSources_Help[] =
+ R"(code_signing_sources: [file list] Sources for code signing step.
+
+ A list of files used as input for code signing script step of a create_bundle
+ target. Non-absolute paths will be resolved relative to the current build
+ file.
+
+ See also "gn help create_bundle".
+)";
+
+const char kCodeSigningOutputs[] = "code_signing_outputs";
+const char kCodeSigningOutputs_HelpShort[] =
+ "code_signing_outputs: [file list] Output files for code signing step.";
+const char kCodeSigningOutputs_Help[] =
+ R"(code_signing_outputs: [file list] Output files for code signing step.
+
+ Outputs from the code signing step of a create_bundle target. Must refer to
+ files in the build directory.
+
+ See also "gn help create_bundle".
+)";
+
+const char kCompleteStaticLib[] = "complete_static_lib";
+const char kCompleteStaticLib_HelpShort[] =
+ "complete_static_lib: [boolean] Links all deps into a static library.";
+const char kCompleteStaticLib_Help[] =
+ R"(complete_static_lib: [boolean] Links all deps into a static library.
+
+ A static library normally doesn't include code from dependencies, but instead
+ forwards the static libraries and source sets in its deps up the dependency
+ chain until a linkable target (an executable or shared library) is reached.
+ The final linkable target only links each static library once, even if it
+ appears more than once in its dependency graph.
+
+ In some cases the static library might be the final desired output. For
+ example, you may be producing a static library for distribution to third
+ parties. In this case, the static library should include code for all
+ dependencies in one complete package. However, complete static libraries
+ themselves are never linked into other complete static libraries. All
+ complete static libraries are for distribution and linking them in would
+ cause code duplication in this case. If the static library is not for
+ distribution, it should not be complete.
+
+ GN treats non-complete static libraries as source sets when they are linked
+ into complete static libraries. This is done because some tools like AR do
+ not handle dependent static libraries properly. This makes it easier to write
+ "alink" rules.
+
+ In rare cases it makes sense to list a header in more than one target if it
+ could be considered conceptually a member of both. libraries.
+
+Example
+
+ static_library("foo") {
+ complete_static_lib = true
+ deps = [ "bar" ]
+ }
+)";
+
+const char kConfigs[] = "configs";
+const char kConfigs_HelpShort[] =
+ "configs: [label list] Configs applying to this target or config.";
+const char kConfigs_Help[] =
+ R"(configs: Configs applying to this target or config.
+
+ A list of config labels.
+
+Configs on a target
+
+ When used on a target, the include_dirs, defines, etc. in each config are
+ appended in the order they appear to the compile command for each file in the
+ target. They will appear after the include_dirs, defines, etc. that the
+ target sets directly.
+
+ Since configs apply after the values set on a target, directly setting a
+ compiler flag will prepend it to the command line. If you want to append a
+ flag instead, you can put that flag in a one-off config and append that
+ config to the target's configs list.
+
+ The build configuration script will generally set up the default configs
+ applying to a given target type (see "set_defaults"). When a target is being
+ defined, it can add to or remove from this list.
+
+Configs on a config
+
+ It is possible to create composite configs by specifying configs on a config.
+ One might do this to forward values, or to factor out blocks of settings from
+ very large configs into more manageable named chunks.
+
+ In this case, the composite config is expanded to be the concatenation of its
+ own values, and in order, the values from its sub-configs *before* anything
+ else happens. This has some ramifications:
+
+ - A target has no visibility into a config's sub-configs. Target code only
+ sees the name of the composite config. It can't remove sub-configs or opt
+ in to only parts of it. The composite config may not even be defined
+ before the target is.
+
+ - You can get duplication of values if a config is listed twice, say, on a
+ target and in a sub-config that also applies. In other cases, the configs
+ applying to a target are de-duped. It's expected that if a config is
+ listed as a sub-config that it is only used in that context. (Note that
+ it's possible to fix this and de-dupe, but it's not normally relevant and
+ complicates the implementation.)
+)" COMMON_ORDERING_HELP
+ R"(
+Example
+
+ # Configs on a target.
+ source_set("foo") {
+ # Don't use the default RTTI config that BUILDCONFIG applied to us.
+ configs -= [ "//build:no_rtti" ]
+
+ # Add some of our own settings.
+ configs += [ ":mysettings" ]
+ }
+
+ # Create a default_optimization config that forwards to one of a set of more
+ # specialized configs depending on build flags. This pattern is useful
+ # because it allows a target to opt in to either a default set, or a more
+ # specific set, while avoid duplicating the settings in two places.
+ config("super_optimization") {
+ cflags = [ ... ]
+ }
+ config("default_optimization") {
+ if (optimize_everything) {
+ configs = [ ":super_optimization" ]
+ } else {
+ configs = [ ":no_optimization" ]
+ }
+ }
+)";
+
+const char kData[] = "data";
+const char kData_HelpShort[] =
+ "data: [file list] Runtime data file dependencies.";
+const char kData_Help[] =
+ R"(data: Runtime data file dependencies.
+
+ Lists files or directories required to run the given target. These are
+ typically data files or directories of data files. The paths are interpreted
+ as being relative to the current build file. Since these are runtime
+ dependencies, they do not affect which targets are built or when. To declare
+ input files to a script, use "inputs".
+
+ Appearing in the "data" section does not imply any special handling such as
+ copying them to the output directory. This is just used for declaring runtime
+ dependencies. Runtime dependencies can be queried using the "runtime_deps"
+ category of "gn desc" or written during build generation via
+ "--runtime-deps-list-file".
+
+ GN doesn't require data files to exist at build-time. So actions that produce
+ files that are in turn runtime dependencies can list those generated files
+ both in the "outputs" list as well as the "data" list.
+
+ By convention, directories are listed with a trailing slash:
+ data = [ "test/data/" ]
+ However, no verification is done on these so GN doesn't enforce this. The
+ paths are just rebased and passed along when requested.
+
+ Note: On iOS and macOS, create_bundle targets will not be recursed into when
+ gathering data. See "gn help create_bundle" for details.
+
+ See "gn help runtime_deps" for how these are used.
+)";
+
+const char kDataDeps[] = "data_deps";
+const char kDataDeps_HelpShort[] =
+ "data_deps: [label list] Non-linked dependencies.";
+const char kDataDeps_Help[] =
+ R"(data_deps: Non-linked dependencies.
+
+ A list of target labels.
+
+ Specifies dependencies of a target that are not actually linked into the
+ current target. Such dependencies will be built and will be available at
+ runtime.
+
+ This is normally used for things like plugins or helper programs that a
+ target needs at runtime.
+
+ Note: On iOS and macOS, create_bundle targets will not be recursed into when
+ gathering data_deps. See "gn help create_bundle" for details.
+
+ See also "gn help deps" and "gn help data".
+
+Example
+
+ executable("foo") {
+ deps = [ "//base" ]
+ data_deps = [ "//plugins:my_runtime_plugin" ]
+ }
+)";
+
+const char kDataKeys[] = "data_keys";
+const char kDataKeys_HelpShort[] =
+ "data_keys: [string list] Keys from which to collect metadata.";
+const char kDataKeys_Help[] =
+ R"(data_keys: Keys from which to collect metadata.
+
+ These keys are used to identify metadata to collect. If a walked target
+ defines this key in its metadata, its value will be appended to the resulting
+ collection.
+
+ See "gn help generated_file".
+)";
+
+const char kDefines[] = "defines";
+const char kDefines_HelpShort[] =
+ "defines: [string list] C preprocessor defines.";
+const char kDefines_Help[] =
+ R"(defines: C preprocessor defines.
+
+ A list of strings
+
+ These strings will be passed to the C/C++ compiler as #defines. The strings
+ may or may not include an "=" to assign a value.
+)" COMMON_ORDERING_HELP
+ R"(
+Example
+
+ defines = [ "AWESOME_FEATURE", "LOG_LEVEL=3" ]
+)";
+
+const char kDepfile[] = "depfile";
+const char kDepfile_HelpShort[] =
+ "depfile: [string] File name for input dependencies for actions.";
+const char kDepfile_Help[] =
+ R"(depfile: [string] File name for input dependencies for actions.
+
+ If nonempty, this string specifies that the current action or action_foreach
+ target will generate the given ".d" file containing the dependencies of the
+ input. Empty or unset means that the script doesn't generate the files.
+
+ A depfile should be used only when a target depends on files that are not
+ already specified by a target's inputs and sources. Likewise, depfiles should
+ specify only those dependencies not already included in sources or inputs.
+
+ The .d file should go in the target output directory. If you have more than
+ one source file that the script is being run over, you can use the output
+ file expansions described in "gn help action_foreach" to name the .d file
+ according to the input.
+
+ The format is that of a Makefile and all paths must be relative to the root
+ build directory. Only one output may be listed and it must match the first
+ output of the action.
+
+ Although depfiles are created by an action, they should not be listed in the
+ action's "outputs" unless another target will use the file as an input.
+
+Example
+
+ action_foreach("myscript_target") {
+ script = "myscript.py"
+ sources = [ ... ]
+
+ # Locate the depfile in the output directory named like the
+ # inputs but with a ".d" appended.
+ depfile = "$relative_target_output_dir/{{source_name}}.d"
+
+ # Say our script uses "-o <d file>" to indicate the depfile.
+ args = [ "{{source}}", "-o", depfile ]
+ }
+)";
+
+const char kDeps[] = "deps";
+const char kDeps_HelpShort[] =
+ "deps: [label list] Private linked dependencies.";
+const char kDeps_Help[] =
+ R"(deps: Private linked dependencies.
+
+ A list of target labels.
+
+ Specifies private dependencies of a target. Private dependencies are
+ propagated up the dependency tree and linked to dependent targets, but do not
+ grant the ability to include headers from the dependency. Public configs are
+ not forwarded.
+
+Details of dependency propagation
+
+ Source sets, shared libraries, and non-complete static libraries will be
+ propagated up the dependency tree across groups, non-complete static
+ libraries and source sets.
+
+ Executables, shared libraries, and complete static libraries will link all
+ propagated targets and stop propagation. Actions and copy steps also stop
+ propagation, allowing them to take a library as an input but not force
+ dependents to link to it.
+
+ Propagation of all_dependent_configs and public_configs happens independently
+ of target type. all_dependent_configs are always propagated across all types
+ of targets, and public_configs are always propagated across public deps of
+ all types of targets.
+
+ Data dependencies are propagated differently. See "gn help data_deps" and
+ "gn help runtime_deps".
+
+ See also "public_deps".
+)";
+
+const char kExterns[] = "externs";
+const char kExterns_HelpShort[] =
+ "externs: [scope] Set of Rust crate-dependency pairs.";
+const char kExterns_Help[] =
+ R"(externs: [scope] Set of Rust crate-dependency pairs.
+
+ A list, each value being a scope indicating a pair of crate name and the path
+ to the Rust library.
+
+ These libraries will be passed as `--extern crate_name=path` to compiler
+ invocation containing the current target.
+
+Examples
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ externs = [{
+ crate_name = "bar",
+ path = "path/to/bar.rlib"
+ }]
+ }
+
+ This target would compile the `foo` crate with the following `extern` flag:
+ `--extern bar=path/to/bar.rlib`.
+)";
+
+const char kFriend[] = "friend";
+const char kFriend_HelpShort[] =
+ "friend: [label pattern list] Allow targets to include private headers.";
+const char kFriend_Help[] =
+ R"(friend: Allow targets to include private headers.
+
+ A list of label patterns (see "gn help label_pattern") that allow dependent
+ targets to include private headers. Applies to all binary targets.
+
+ Normally if a target lists headers in the "public" list (see "gn help
+ public"), other headers are implicitly marked as private. Private headers
+ can not be included by other targets, even with a public dependency path.
+ The "gn check" function performs this validation.
+
+ A friend declaration allows one or more targets to include private headers.
+ This is useful for things like unit tests that are closely associated with a
+ target and require internal knowledge without opening up all headers to be
+ included by all dependents.
+
+ A friend target does not allow that target to include headers when no
+ dependency exists. A public dependency path must still exist between two
+ targets to include any headers from a destination target. The friend
+ annotation merely allows the use of headers that would otherwise be
+ prohibited because they are private.
+
+ The friend annotation is matched only against the target containing the file
+ with the include directive. Friend annotations are not propagated across
+ public or private dependencies. Friend annotations do not affect visibility.
+
+Example
+
+ static_library("lib") {
+ # This target can include our private headers.
+ friend = [ ":unit_tests" ]
+
+ public = [
+ "public_api.h", # Normal public API for dependent targets.
+ ]
+
+ # Private API and sources.
+ sources = [
+ "a_source_file.cc",
+
+ # Normal targets that depend on this one won't be able to include this
+ # because this target defines a list of "public" headers. Without the
+ # "public" list, all headers are implicitly public.
+ "private_api.h",
+ ]
+ }
+
+ executable("unit_tests") {
+ sources = [
+ # This can include "private_api.h" from the :lib target because it
+ # depends on that target and because of the friend annotation.
+ "my_test.cc",
+ ]
+
+ deps = [
+ ":lib", # Required for the include to be allowed.
+ ]
+ }
+)";
+
+const char kFrameworkDirs[] = "framework_dirs";
+const char kFrameworkDirs_HelpShort[] =
+ "framework_dirs: [directory list] Additional framework search directories.";
+const char kFrameworkDirs_Help[] =
+ R"(framework_dirs: [directory list] Additional framework search directories.
+
+ A list of source directories.
+
+ The directories in this list will be added to the framework search path for
+ the files in the affected target.
+)" COMMON_ORDERING_HELP
+ R"(
+Example
+
+ framework_dirs = [ "src/include", "//third_party/foo" ]
+)";
+
+const char kFrameworks[] = "frameworks";
+const char kFrameworks_HelpShort[] =
+ "frameworks: [name list] Name of frameworks that must be linked.";
+const char kFrameworks_Help[] =
+ R"(frameworks: [name list] Name of frameworks that must be linked.
+
+ A list of framework names.
+
+ The frameworks named in that list will be linked with any dynamic link
+ type target.
+)" COMMON_ORDERING_HELP
+ R"(
+Example
+
+ frameworks = [ "Foundation.framework", "Foo.framework" ]
+)";
+
+const char kIncludeDirs[] = "include_dirs";
+const char kIncludeDirs_HelpShort[] =
+ "include_dirs: [directory list] Additional include directories.";
+const char kIncludeDirs_Help[] =
+ R"(include_dirs: Additional include directories.
+
+ A list of source directories.
+
+ The directories in this list will be added to the include path for the files
+ in the affected target.
+)" COMMON_ORDERING_HELP
+ R"(
+Example
+
+ include_dirs = [ "src/include", "//third_party/foo" ]
+)";
+
+const char kInputs[] = "inputs";
+const char kInputs_HelpShort[] =
+ "inputs: [file list] Additional compile-time dependencies.";
+const char kInputs_Help[] =
+ R"(inputs: Additional compile-time dependencies.
+
+ Inputs are compile-time dependencies of the current target. This means that
+ all inputs must be available before compiling any of the sources or executing
+ any actions.
+
+ Inputs are typically only used for action and action_foreach targets.
+
+Inputs for actions
+
+ For action and action_foreach targets, inputs should be the inputs to script
+ that don't vary. These should be all .py files that the script uses via
+ imports (the main script itself will be an implicit dependency of the action
+ so need not be listed).
+
+ For action targets, inputs and sources are treated the same, but from a style
+ perspective, it's recommended to follow the same rule as action_foreach and
+ put helper files in the inputs, and the data used by the script (if any) in
+ sources.
+
+ Note that another way to declare input dependencies from an action is to have
+ the action write a depfile (see "gn help depfile"). This allows the script to
+ dynamically write input dependencies, that might not be known until actually
+ executing the script. This is more efficient than doing processing while
+ running GN to determine the inputs, and is easier to keep in-sync than
+ hardcoding the list.
+
+Script input gotchas
+
+ It may be tempting to write a script that enumerates all files in a directory
+ as inputs. Don't do this! Even if you specify all the files in the inputs or
+ sources in the GN target (or worse, enumerate the files in an exec_script
+ call when running GN, which will be slow), the dependencies will be broken.
+
+ The problem happens if a file is ever removed because the inputs are not
+ listed on the command line to the script. Because the script hasn't changed
+ and all inputs are up to date, the script will not re-run and you will get a
+ stale build. Instead, either list all inputs on the command line to the
+ script, or if there are many, create a separate list file that the script
+ reads. As long as this file is listed in the inputs, the build will detect
+ when it has changed in any way and the action will re-run.
+
+Inputs for binary targets
+
+ Any input dependencies will be resolved before compiling any sources or
+ linking the target. Normally, all actions that a target depends on will be run
+ before any files in a target are compiled. So if you depend on generated
+ headers, you do not typically need to list them in the inputs section.
+
+ Inputs for binary targets will be treated as implicit dependencies, meaning
+ that changes in any of the inputs will force all sources in the target to be
+ recompiled. If an input only applies to a subset of source files, you may
+ want to split those into a separate target to avoid unnecessary recompiles.
+
+Example
+
+ action("myscript") {
+ script = "domything.py"
+ inputs = [ "input.data" ]
+ }
+)";
+
+const char kLdflags[] = "ldflags";
+const char kLdflags_HelpShort[] =
+ "ldflags: [string list] Flags passed to the linker.";
+const char kLdflags_Help[] =
+ R"(ldflags: Flags passed to the linker.
+
+ A list of strings.
+
+ These flags are passed on the command-line to the linker and generally
+ specify various linking options. Most targets will not need these and will
+ use "libs" and "lib_dirs" instead.
+
+ ldflags are NOT pushed to dependents, so applying ldflags to source sets or
+ static libraries will be a no-op. If you want to apply ldflags to dependent
+ targets, put them in a config and set it in the all_dependent_configs or
+ public_configs.
+)" COMMON_ORDERING_HELP;
+
+#define COMMON_LIB_INHERITANCE_HELP \
+ "\n" \
+ " libs and lib_dirs work differently than other flags in two respects.\n" \
+ " First, they are inherited across static library boundaries until a\n" \
+ " shared library or executable target is reached. Second, they are\n" \
+ " uniquified so each one is only passed once (the first instance of it\n" \
+ " will be the one used).\n"
+
+#define LIBS_AND_LIB_DIRS_ORDERING_HELP \
+ "\n" \
+ " For \"libs\" and \"lib_dirs\" only, the values propagated from\n" \
+ " dependencies (as described above) are applied last assuming they\n" \
+ " are not already in the list.\n"
+
+const char kLibDirs[] = "lib_dirs";
+const char kLibDirs_HelpShort[] =
+ "lib_dirs: [directory list] Additional library directories.";
+const char kLibDirs_Help[] =
+ R"(lib_dirs: Additional library directories.
+
+ A list of directories.
+
+ Specifies additional directories passed to the linker for searching for the
+ required libraries. If an item is not an absolute path, it will be treated as
+ being relative to the current build file.
+)" COMMON_LIB_INHERITANCE_HELP COMMON_ORDERING_HELP
+ LIBS_AND_LIB_DIRS_ORDERING_HELP
+ R"(
+Example
+
+ lib_dirs = [ "/usr/lib/foo", "lib/doom_melon" ]
+)";
+
+const char kLibs[] = "libs";
+const char kLibs_HelpShort[] =
+ "libs: [string list] Additional libraries to link.";
+const char kLibs_Help[] =
+ R"(libs: Additional libraries to link.
+
+ A list of library names or library paths.
+
+ These libraries will be linked into the final binary (executable or shared
+ library) containing the current target.
+)" COMMON_LIB_INHERITANCE_HELP
+ R"(
+Types of libs
+
+ There are several different things that can be expressed in libs:
+
+ File paths
+ Values containing '/' will be treated as references to files in the
+ checkout. They will be rebased to be relative to the build directory and
+ specified in the "libs" for linker tools. This facility should be used
+ for libraries that are checked in to the version control. For libraries
+ that are generated by the build, use normal GN deps to link them.
+
+ System libraries
+ Values not containing '/' will be treated as system library names. These
+ will be passed unmodified to the linker and prefixed with the
+ "lib_switch" attribute of the linker tool. Generally you would set the
+ "lib_dirs" so the given library is found. Your BUILD.gn file should not
+ specify the switch (like "-l"): this will be encoded in the "lib_switch"
+ of the tool.
+)" COMMON_ORDERING_HELP LIBS_AND_LIB_DIRS_ORDERING_HELP
+ R"(
+Examples
+
+ On Windows:
+ libs = [ "ctl3d.lib" ]
+
+ On Linux:
+ libs = [ "ld" ]
+)";
+
+const char kMetadata[] = "metadata";
+const char kMetadata_HelpShort[] = "metadata: [scope] Metadata of this target.";
+const char kMetadata_Help[] =
+ R"(metadata: Metadata of this target.
+
+ Metadata is a collection of keys and values relating to a particular target.
+ Values must be lists, allowing for sane and predictable collection behavior.
+ Generally, these keys will include three types of lists: lists of ordinary
+ strings, lists of filenames intended to be rebased according to their
+ particular source directory, and lists of target labels intended to be used
+ as barriers to the walk. Verification of these categories occurs at walk time,
+ not creation time (since it is not clear until the walk which values are
+ intended for which purpose).
+
+Example
+
+ group("doom_melon") {
+ metadata = {
+ # These keys are not built in to GN but are interpreted when consuming
+ # metadata.
+ my_barrier = []
+ my_files = [ "a.txt", "b.txt" ]
+ }
+ }
+)";
+
+const char kOutputExtension[] = "output_extension";
+const char kOutputExtension_HelpShort[] =
+ "output_extension: [string] Value to use for the output's file extension.";
+const char kOutputExtension_Help[] =
+ R"(output_extension: Value to use for the output's file extension.
+
+ Normally the file extension for a target is based on the target type and the
+ operating system, but in rare cases you will need to override the name (for
+ example to use "libfreetype.so.6" instead of libfreetype.so on Linux).
+
+ This value should not include a leading dot. If undefined, the default
+ specified on the tool will be used. If set to the empty string, no output
+ extension will be used.
+
+ The output_extension will be used to set the "{{output_extension}}" expansion
+ which the linker tool will generally use to specify the output file name. See
+ "gn help tool".
+
+Example
+
+ shared_library("freetype") {
+ if (is_linux) {
+ # Call the output "libfreetype.so.6"
+ output_extension = "so.6"
+ }
+ ...
+ }
+
+ # On Windows, generate a "mysettings.cpl" control panel applet. Control panel
+ # applets are actually special shared libraries.
+ if (is_win) {
+ shared_library("mysettings") {
+ output_extension = "cpl"
+ ...
+ }
+ }
+)";
+
+const char kOutputDir[] = "output_dir";
+const char kOutputDir_HelpShort[] =
+ "output_dir: [directory] Directory to put output file in.";
+const char kOutputDir_Help[] =
+ R"(output_dir: [directory] Directory to put output file in.
+
+ For library and executable targets, overrides the directory for the final
+ output. This must be in the root_build_dir or a child thereof.
+
+ This should generally be in the root_out_dir or a subdirectory thereof (the
+ root_out_dir will be the same as the root_build_dir for the default
+ toolchain, and will be a subdirectory for other toolchains). Not putting the
+ output in a subdirectory of root_out_dir can result in collisions between
+ different toolchains, so you will need to take steps to ensure that your
+ target is only present in one toolchain.
+
+ Normally the toolchain specifies the output directory for libraries and
+ executables (see "gn help tool"). You will have to consult that for the
+ default location. The default location will be used if output_dir is
+ undefined or empty.
+
+Example
+
+ shared_library("doom_melon") {
+ output_dir = "$root_out_dir/plugin_libs"
+ ...
+ }
+)";
+
+const char kOutputName[] = "output_name";
+const char kOutputName_HelpShort[] =
+ "output_name: [string] Name for the output file other than the default.";
+const char kOutputName_Help[] =
+ R"(output_name: Define a name for the output file other than the default.
+
+ Normally the output name of a target will be based on the target name, so the
+ target "//foo/bar:bar_unittests" will generate an output file such as
+ "bar_unittests.exe" (using Windows as an example).
+
+ Sometimes you will want an alternate name to avoid collisions or if the
+ internal name isn't appropriate for public distribution.
+
+ The output name should have no extension or prefixes, these will be added
+ using the default system rules. For example, on Linux an output name of "foo"
+ will produce a shared library "libfoo.so". There is no way to override the
+ output prefix of a linker tool on a per- target basis. If you need more
+ flexibility, create a copy target to produce the file you want.
+
+ This variable is valid for all binary output target types.
+
+Example
+
+ static_library("doom_melon") {
+ output_name = "fluffy_bunny"
+ }
+)";
+
+const char kOutputPrefixOverride[] = "output_prefix_override";
+const char kOutputPrefixOverride_HelpShort[] =
+ "output_prefix_override: [boolean] Don't use prefix for output name.";
+const char kOutputPrefixOverride_Help[] =
+ R"(output_prefix_override: Don't use prefix for output name.
+
+ A boolean that overrides the output prefix for a target. Defaults to false.
+
+ Some systems use prefixes for the names of the final target output file. The
+ normal example is "libfoo.so" on Linux for a target named "foo".
+
+ The output prefix for a given target type is specified on the linker tool
+ (see "gn help tool"). Sometimes this prefix is undesired.
+
+ See also "gn help output_extension".
+
+Example
+
+ shared_library("doom_melon") {
+ # Normally this will produce "libdoom_melon.so" on Linux. Setting this flag
+ # will produce "doom_melon.so".
+ output_prefix_override = true
+ ...
+ }
+)";
+
+const char kPartialInfoPlist[] = "partial_info_plist";
+const char kPartialInfoPlist_HelpShort[] =
+ "partial_info_plist: [filename] Path plist from asset catalog compiler.";
+const char kPartialInfoPlist_Help[] =
+ R"(partial_info_plist: [filename] Path plist from asset catalog compiler.
+
+ Valid for create_bundle target, corresponds to the path for the partial
+ Info.plist created by the asset catalog compiler that needs to be merged
+ with the application Info.plist (usually done by the code signing script).
+
+ The file will be generated regardless of whether the asset compiler has
+ been invoked or not. See "gn help create_bundle".
+)";
+
+const char kOutputs[] = "outputs";
+const char kOutputs_HelpShort[] =
+ "outputs: [file list] Output files for actions and copy targets.";
+const char kOutputs_Help[] =
+ R"(outputs: Output files for actions and copy targets.
+
+ Outputs is valid for "copy", "action", and "action_foreach" target types and
+ indicates the resulting files. Outputs must always refer to files in the
+ build directory.
+
+ copy
+ Copy targets should have exactly one entry in the outputs list. If there is
+ exactly one source, this can be a literal file name or a source expansion.
+ If there is more than one source, this must contain a source expansion to
+ map a single input name to a single output name. See "gn help copy".
+
+ action_foreach
+ Action_foreach targets must always use source expansions to map input files
+ to output files. There can be more than one output, which means that each
+ invocation of the script will produce a set of files (presumably based on
+ the name of the input file). See "gn help action_foreach".
+
+ action
+ Action targets (excluding action_foreach) must list literal output file(s)
+ with no source expansions. See "gn help action".
+)";
+
+const char kPool[] = "pool";
+const char kPool_HelpShort[] =
+ "pool: [string] Label of the pool used by the action.";
+const char kPool_Help[] =
+ R"(pool: Label of the pool used by the action.
+
+ A fully-qualified label representing the pool that will be used for the
+ action. Pools are defined using the pool() {...} declaration.
+
+Example
+
+ action("action") {
+ pool = "//build:custom_pool"
+ ...
+ }
+)";
+
+const char kPrecompiledHeader[] = "precompiled_header";
+const char kPrecompiledHeader_HelpShort[] =
+ "precompiled_header: [string] Header file to precompile.";
+const char kPrecompiledHeader_Help[] =
+ R"(precompiled_header: [string] Header file to precompile.
+
+ Precompiled headers will be used when a target specifies this value, or a
+ config applying to this target specifies this value. In addition, the tool
+ corresponding to the source files must also specify precompiled headers (see
+ "gn help tool"). The tool will also specify what type of precompiled headers
+ to use, by setting precompiled_header_type to either "gcc" or "msvc".
+
+ The precompiled header/source variables can be specified on a target or a
+ config, but must be the same for all configs applying to a given target since
+ a target can only have one precompiled header.
+
+ If you use both C and C++ sources, the precompiled header and source file
+ will be compiled once per language. You will want to make sure to wrap C++
+ includes in __cplusplus #ifdefs so the file will compile in C mode.
+
+GCC precompiled headers
+
+ When using GCC-style precompiled headers, "precompiled_source" contains the
+ path of a .h file that is precompiled and then included by all source files
+ in targets that set "precompiled_source".
+
+ The value of "precompiled_header" is not used with GCC-style precompiled
+ headers.
+
+MSVC precompiled headers
+
+ When using MSVC-style precompiled headers, the "precompiled_header" value is
+ a string corresponding to the header. This is NOT a path to a file that GN
+ recognises, but rather the exact string that appears in quotes after
+ an #include line in source code. The compiler will match this string against
+ includes or forced includes (/FI).
+
+ MSVC also requires a source file to compile the header with. This must be
+ specified by the "precompiled_source" value. In contrast to the header value,
+ this IS a GN-style file name, and tells GN which source file to compile to
+ make the .pch file used for subsequent compiles.
+
+ For example, if the toolchain specifies MSVC headers:
+
+ toolchain("vc_x64") {
+ ...
+ tool("cxx") {
+ precompiled_header_type = "msvc"
+ ...
+
+ You might make a config like this:
+
+ config("use_precompiled_headers") {
+ precompiled_header = "build/precompile.h"
+ precompiled_source = "//build/precompile.cc"
+
+ # Either your source files should #include "build/precompile.h"
+ # first, or you can do this to force-include the header.
+ cflags = [ "/FI$precompiled_header" ]
+ }
+
+ And then define a target that uses the config:
+
+ executable("doom_melon") {
+ configs += [ ":use_precompiled_headers" ]
+ ...
+)";
+
+const char kPrecompiledHeaderType[] = "precompiled_header_type";
+const char kPrecompiledHeaderType_HelpShort[] =
+ "precompiled_header_type: [string] \"gcc\" or \"msvc\".";
+const char kPrecompiledHeaderType_Help[] =
+ R"(precompiled_header_type: [string] "gcc" or "msvc".
+
+ See "gn help precompiled_header".
+)";
+
+const char kPrecompiledSource[] = "precompiled_source";
+const char kPrecompiledSource_HelpShort[] =
+ "precompiled_source: [file name] Source file to precompile.";
+const char kPrecompiledSource_Help[] =
+ R"(precompiled_source: [file name] Source file to precompile.
+
+ The source file that goes along with the precompiled_header when using
+ "msvc"-style precompiled headers. It will be implicitly added to the sources
+ of the target. See "gn help precompiled_header".
+)";
+
+const char kProductType[] = "product_type";
+const char kProductType_HelpShort[] =
+ "product_type: [string] Product type for Xcode projects.";
+const char kProductType_Help[] =
+ R"(product_type: Product type for Xcode projects.
+
+ Correspond to the type of the product of a create_bundle target. Only
+ meaningful to Xcode (used as part of the Xcode project generation).
+
+ When generating Xcode project files, only create_bundle target with a
+ non-empty product_type will have a corresponding target in Xcode project.
+)";
+
+const char kPublic[] = "public";
+const char kPublic_HelpShort[] =
+ "public: [file list] Declare public header files for a target.";
+const char kPublic_Help[] =
+ R"(public: Declare public header files for a target.
+
+ A list of files that other targets can include. These permissions are checked
+ via the "check" command (see "gn help check").
+
+ If no public files are declared, other targets (assuming they have visibility
+ to depend on this target) can include any file in the sources list. If this
+ variable is defined on a target, dependent targets may only include files on
+ this whitelist unless that target is marked as a friend (see "gn help
+ friend").
+
+ Header file permissions are also subject to visibility. A target must be
+ visible to another target to include any files from it at all and the public
+ headers indicate which subset of those files are permitted. See "gn help
+ visibility" for more.
+
+ Public files are inherited through the dependency tree. So if there is a
+ dependency A -> B -> C, then A can include C's public headers. However, the
+ same is NOT true of visibility, so unless A is in C's visibility list, the
+ include will be rejected.
+
+ GN only knows about files declared in the "sources" and "public" sections of
+ targets. If a file is included that is not known to the build, it will be
+ allowed.
+
+ It is common for test targets to need to include private headers for their
+ associated code. In this case, list the test target in the "friend" list of
+ the target that owns the private header to allow the inclusion. See
+ "gn help friend" for more.
+
+ When a binary target has no explicit or implicit public headers (a "public"
+ list is defined but is empty), GN assumes that the target can not propagate
+ any compile-time dependencies up the dependency tree. In this case, the build
+ can be parallelized more efficiently.
+ Say there are dependencies:
+ A (shared library) -> B (shared library) -> C (action).
+ Normally C must complete before any source files in A can compile (because
+ there might be generated includes). But when B explicitly declares no public
+ headers, C can execute in parallel with A's compile steps. C must still be
+ complete before any dependents link.
+
+Examples
+
+ These exact files are public:
+ public = [ "foo.h", "bar.h" ]
+
+ No files are public (no targets may include headers from this one):
+ # This allows starting compilation in dependent targets earlier.
+ public = []
+)";
+
+const char kPublicConfigs[] = "public_configs";
+const char kPublicConfigs_HelpShort[] =
+ "public_configs: [label list] Configs applied to dependents.";
+const char kPublicConfigs_Help[] =
+ R"(public_configs: Configs to be applied on dependents.
+
+ A list of config labels.
+
+ Targets directly depending on this one will have the configs listed in this
+ variable added to them. These configs will also apply to the current target.
+ Generally, public configs are used to apply defines and include directories
+ necessary to compile this target's header files.
+
+ See also "gn help all_dependent_configs".
+
+Propagation of public configs
+
+ Public configs are applied to all targets that depend directly on this one.
+ These dependent targets can further push this target's public configs
+ higher in the dependency tree by depending on it via public_deps (see "gn
+ help public_deps").
+
+ static_library("toplevel") {
+ # This target will get "my_config" applied to it. However, since this
+ # target uses "deps" and not "public_deps", targets that depend on this
+ # one won't get it.
+ deps = [ ":intermediate" ]
+ }
+
+ static_library("intermediate") {
+ # Depending on "lower" in any way will apply "my_config" to this target.
+ # Additionall, since this target depends on "lower" via public_deps,
+ # targets that depend on this one will also get "my_config".
+ public_deps = [ ":lower" ]
+ }
+
+ static_library("lower") {
+ # This will get applied to all targets that depend on this one.
+ public_configs = [ ":my_config" ]
+ }
+
+ Public config propagation happens in a second phase once a target and all of
+ its dependencies have been resolved. Therefore, a target will not see these
+ force-added configs in their "configs" variable while the script is running,
+ and they can not be removed. As a result, this capability should generally
+ only be used to add defines and include directories rather than setting
+ complicated flags that some targets may not want.
+
+ Public configs may or may not be propagated across toolchain boundaries
+ depending on the value of the propagates_configs flag (see "gn help
+ toolchain") on the toolchain of the target declaring the public_config.
+
+Avoiding applying public configs to this target
+
+ If you want the config to apply to targets that depend on this one, but NOT
+ this one, define an extra layer of indirection using a group:
+
+ # External targets depend on this group.
+ group("my_target") {
+ # Config to apply to all targets that depend on this one.
+ public_configs = [ ":external_settings" ]
+ deps = [ ":internal_target" ]
+ }
+
+ # Internal target to actually compile the sources.
+ static_library("internal_target") {
+ # Force all external targets to depend on the group instead of directly
+ # on this so the "external_settings" config will get applied.
+ visibility = [ ":my_target" ]
+ ...
+ }
+
+)" COMMON_ORDERING_HELP;
+
+const char kPublicDeps[] = "public_deps";
+const char kPublicDeps_HelpShort[] =
+ "public_deps: [label list] Declare public dependencies.";
+const char kPublicDeps_Help[] =
+ R"(public_deps: Declare public dependencies.
+
+ Public dependencies are like private dependencies (see "gn help deps") but
+ additionally express that the current target exposes the listed deps as part
+ of its public API.
+
+ This has several ramifications:
+
+ - public_configs that are part of the dependency are forwarded to direct
+ dependents.
+
+ - Public headers in the dependency are usable by dependents (includes do
+ not require a direct dependency or visibility).
+
+ - If the current target is a shared library, other shared libraries that it
+ publicly depends on (directly or indirectly) are propagated up the
+ dependency tree to dependents for linking.
+
+ See also "gn help public_configs".
+
+Discussion
+
+ Say you have three targets: A -> B -> C. C's visibility may allow B to depend
+ on it but not A. Normally, this would prevent A from including any headers
+ from C, and C's public_configs would apply only to B.
+
+ If B lists C in its public_deps instead of regular deps, A will now inherit
+ C's public_configs and the ability to include C's public headers.
+
+ Generally if you are writing a target B and you include C's headers as part
+ of B's public headers, or targets depending on B should consider B and C to
+ be part of a unit, you should use public_deps instead of deps.
+
+Example
+
+ # This target can include files from "c" but not from
+ # "super_secret_implementation_details".
+ executable("a") {
+ deps = [ ":b" ]
+ }
+
+ shared_library("b") {
+ deps = [ ":super_secret_implementation_details" ]
+ public_deps = [ ":c" ]
+ }
+)";
+
+const char kRebase[] = "rebase";
+const char kRebase_HelpShort[] =
+ "rebase: [boolean] Rebase collected metadata as files.";
+const char kRebase_Help[] =
+ R"(rebase: Rebase collected metadata as files.
+
+ A boolean that triggers a rebase of collected metadata strings based on their
+ declared file. Defaults to false.
+
+ Metadata generally declares files as strings relative to the local build file.
+ However, this data is often used in other contexts, and so setting this flag
+ will force the metadata collection to be rebased according to the local build
+ file's location and thus allow the filename to be used anywhere.
+
+ Setting this flag will raise an error if any target's specified metadata is
+ not a string value.
+
+ See also "gn help generated_file".
+)";
+
+const char kResponseFileContents[] = "response_file_contents";
+const char kResponseFileContents_HelpShort[] =
+ "response_file_contents: [string list] Contents of .rsp file for actions.";
+const char kResponseFileContents_Help[] =
+ R"*(response_file_contents: Contents of a response file for actions.
+
+ Sometimes the arguments passed to a script can be too long for the system's
+ command-line capabilities. This is especially the case on Windows where the
+ maximum command-line length is less than 8K. A response file allows you to
+ pass an unlimited amount of data to a script in a temporary file for an
+ action or action_foreach target.
+
+ If the response_file_contents variable is defined and non-empty, the list
+ will be treated as script args (including possibly substitution patterns)
+ that will be written to a temporary file at build time. The name of the
+ temporary file will be substituted for "{{response_file_name}}" in the script
+ args.
+
+ The response file contents will always be quoted and escaped according to
+ Unix shell rules. To parse the response file, the Python script should use
+ "shlex.split(file_contents)".
+
+Example
+
+ action("process_lots_of_files") {
+ script = "process.py",
+ inputs = [ ... huge list of files ... ]
+
+ # Write all the inputs to a response file for the script. Also,
+ # make the paths relative to the script working directory.
+ response_file_contents = rebase_path(inputs, root_build_dir)
+
+ # The script expects the name of the response file in --file-list.
+ args = [
+ "--enable-foo",
+ "--file-list={{response_file_name}}",
+ ]
+ }
+)*";
+
+const char kScript[] = "script";
+const char kScript_HelpShort[] = "script: [file name] Script file for actions.";
+const char kScript_Help[] =
+ R"(script: Script file for actions.
+
+ An absolute or buildfile-relative file name of a Python script to run for a
+ action and action_foreach targets (see "gn help action" and "gn help
+ action_foreach").
+)";
+
+const char kSources[] = "sources";
+const char kSources_HelpShort[] =
+ "sources: [file list] Source files for a target.";
+const char kSources_Help[] =
+ R"(sources: Source files for a target
+
+ A list of files. Non-absolute paths will be resolved relative to the current
+ build file.
+
+Sources for binary targets
+
+ For binary targets (source sets, executables, and libraries), the known file
+ types will be compiled with the associated tools. Unknown file types and
+ headers will be skipped. However, you should still list all C/C+ header files
+ so GN knows about the existence of those files for the purposes of include
+ checking.
+
+ As a special case, a file ending in ".def" will be treated as a Windows
+ module definition file. It will be appended to the link line with a
+ preceding "/DEF:" string. There must be at most one .def file in a target
+ and they do not cross dependency boundaries (so specifying a .def file in a
+ static library or source set will have no effect on the executable or shared
+ library they're linked into).
+
+ For Rust targets that do not specify a crate_root, then the crate_root will
+ look for a lib.rs file (or main.rs for executable) or a single file in
+ sources, if sources contains only one file.
+
+Sources for non-binary targets
+
+ action_foreach
+ The sources are the set of files that the script will be executed over. The
+ script will run once per file.
+
+ action
+ The sources will be treated the same as inputs. See "gn help inputs" for
+ more information and usage advice.
+
+ copy
+ The source are the source files to copy.
+)";
+
+const char kSwiftflags[] = "swiftflags";
+const char kSwiftflags_HelpShort[] =
+ "swiftflags: [string list] Flags passed to the swift compiler.";
+const char* kSwiftflags_Help =
+ R"(swiftflags: Flags passed to the swift compiler.
+
+ A list of strings.
+
+ "swiftflags" are passed to any invocation of a tool that takes an .swift
+ file as input.
+)" COMMON_ORDERING_HELP;
+
+const char kXcodeTestApplicationName[] = "xcode_test_application_name";
+const char kXcodeTestApplicationName_HelpShort[] =
+ "xcode_test_application_name: [string] Name for Xcode test target.";
+const char kXcodeTestApplicationName_Help[] =
+ R"(xcode_test_application_name: Name for Xcode test target.
+
+ Each unit and ui test target must have a test application target, and this
+ value is used to specify the relationship. Only meaningful to Xcode (used as
+ part of the Xcode project generation).
+
+ See "gn help create_bundle" for more information.
+
+Example
+
+ create_bundle("chrome_xctest") {
+ test_application_name = "chrome"
+ ...
+ }
+)";
+
+const char kTestonly[] = "testonly";
+const char kTestonly_HelpShort[] =
+ "testonly: [boolean] Declares a target must only be used for testing.";
+const char kTestonly_Help[] =
+ R"(testonly: Declares a target must only be used for testing.
+
+ Boolean. Defaults to false.
+
+ When a target is marked "testonly = true", it must only be depended on by
+ other test-only targets. Otherwise, GN will issue an error that the
+ depenedency is not allowed.
+
+ This feature is intended to prevent accidentally shipping test code in a
+ final product.
+
+Example
+
+ source_set("test_support") {
+ testonly = true
+ ...
+ }
+)";
+
+const char kVisibility[] = "visibility";
+const char kVisibility_HelpShort[] =
+ "visibility: [label list] A list of labels that can depend on a target.";
+const char kVisibility_Help[] =
+ R"(visibility: A list of labels that can depend on a target.
+
+ A list of labels and label patterns that define which targets can depend on
+ the current one. These permissions are checked via the "check" command (see
+ "gn help check").
+
+ If visibility is not defined, it defaults to public ("*").
+
+ If visibility is defined, only the targets with labels that match it can
+ depend on the current target. The empty list means no targets can depend on
+ the current target.
+
+ Tip: Often you will want the same visibility for all targets in a BUILD file.
+ In this case you can just put the definition at the top, outside of any
+ target, and the targets will inherit that scope and see the definition.
+
+Patterns
+
+ See "gn help label_pattern" for more details on what types of patterns are
+ supported. If a toolchain is specified, only targets in that toolchain will
+ be matched. If a toolchain is not specified on a pattern, targets in all
+ toolchains will be matched.
+
+Examples
+
+ Only targets in the current buildfile ("private"):
+ visibility = [ ":*" ]
+
+ No targets (used for targets that should be leaf nodes):
+ visibility = []
+
+ Any target ("public", the default):
+ visibility = [ "*" ]
+
+ All targets in the current directory and any subdirectory:
+ visibility = [ "./*" ]
+
+ Any target in "//bar/BUILD.gn":
+ visibility = [ "//bar:*" ]
+
+ Any target in "//bar/" or any subdirectory thereof:
+ visibility = [ "//bar/*" ]
+
+ Just these specific targets:
+ visibility = [ ":mything", "//foo:something_else" ]
+
+ Any target in the current directory and any subdirectory thereof, plus
+ any targets in "//bar/" and any subdirectory thereof.
+ visibility = [ "./*", "//bar/*" ]
+)";
+
+const char kWalkKeys[] = "walk_keys";
+const char kWalkKeys_HelpShort[] =
+ "walk_keys: [string list] Key(s) for managing the metadata collection "
+ "walk.";
+const char kWalkKeys_Help[] =
+ R"(walk_keys: Key(s) for managing the metadata collection walk.
+
+ Defaults to [""].
+
+ These keys are used to control the next step in a collection walk, acting as
+ barriers. If a specified key is defined in a target's metadata, the walk will
+ use the targets listed in that value to determine which targets are walked.
+
+ If no walk_keys are specified for a generated_file target (i.e. "[""]"), the
+ walk will touch all deps and data_deps of the specified target recursively.
+
+ See "gn help generated_file".
+)";
+
+const char kWeakFrameworks[] = "weak_frameworks";
+const char kWeakFrameworks_HelpShort[] =
+ "weak_frameworks: [name list] Name of frameworks that must be weak linked.";
+const char kWeakFrameworks_Help[] =
+ R"(weak_frameworks: [name list] Name of frameworks that must be weak linked.
+
+ A list of framework names.
+
+ The frameworks named in that list will be weak linked with any dynamic link
+ type target. Weak linking instructs the dynamic loader to attempt to load
+ the framework, but if it is not able to do so, it leaves any imported symbols
+ unresolved. This is typically used when a framework is present in a new
+ version of an SDK but not on older versions of the OS that the software runs
+ on.
+)" COMMON_ORDERING_HELP
+ R"(
+Example
+
+ weak_frameworks = [ "OnlyOnNewerOSes.framework" ]
+)";
+
+const char kWriteValueContents[] = "contents";
+const char kWriteValueContents_HelpShort[] =
+ "contents: Contents to write to file.";
+const char kWriteValueContents_Help[] =
+ R"(contents: Contents to write to file.
+
+ The contents of the file for a generated_file target.
+ See "gn help generated_file".
+)";
+
+const char kWriteOutputConversion[] = "output_conversion";
+const char kWriteOutputConversion_HelpShort[] =
+ "output_conversion: Data format for generated_file targets.";
+const char kWriteOutputConversion_Help[] =
+ R"(output_conversion: Data format for generated_file targets.
+
+ Controls how the "contents" of a generated_file target is formatted.
+ See "gn help io_conversion".
+)";
+
+const char kWriteRuntimeDeps[] = "write_runtime_deps";
+const char kWriteRuntimeDeps_HelpShort[] =
+ "write_runtime_deps: Writes the target's runtime_deps to the given path.";
+const char kWriteRuntimeDeps_Help[] =
+ R"(write_runtime_deps: Writes the target's runtime_deps to the given path.
+
+ Does not synchronously write the file, but rather schedules it to be written
+ at the end of generation.
+
+ If the file exists and the contents are identical to that being written, the
+ file will not be updated. This will prevent unnecessary rebuilds of targets
+ that depend on this file.
+
+ Path must be within the output directory.
+
+ See "gn help runtime_deps" for how the runtime dependencies are computed.
+
+ The format of this file will list one file per line with no escaping. The
+ files will be relative to the root_build_dir. The first line of the file will
+ be the main output file of the target itself. The file contents will be the
+ same as requesting the runtime deps be written on the command line (see "gn
+ help --runtime-deps-list-file").
+)";
+
+const char kXcodeExtraAttributes[] = "xcode_extra_attributes";
+const char kXcodeExtraAttributes_HelpShort[] =
+ "xcode_extra_attributes: [scope] Extra attributes for Xcode projects.";
+const char kXcodeExtraAttributes_Help[] =
+ R"(xcode_extra_attributes: [scope] Extra attributes for Xcode projects.
+
+ The value defined in this scope will be copied to the EXTRA_ATTRIBUTES
+ property of the generated Xcode project. They are only meaningful when
+ generating with --ide=xcode.
+
+ See "gn help create_bundle" for more information.
+)";
+
+// -----------------------------------------------------------------------------
+
+VariableInfo::VariableInfo() : help_short(""), help("") {}
+
+VariableInfo::VariableInfo(const char* in_help_short, const char* in_help)
+ : help_short(in_help_short), help(in_help) {}
+
+#define INSERT_VARIABLE(var) \
+ info_map[k##var] = VariableInfo(k##var##_HelpShort, k##var##_Help);
+
+const VariableInfoMap& GetBuiltinVariables() {
+ static VariableInfoMap info_map;
+ if (info_map.empty()) {
+ INSERT_VARIABLE(CurrentCpu)
+ INSERT_VARIABLE(CurrentOs)
+ INSERT_VARIABLE(CurrentToolchain)
+ INSERT_VARIABLE(DefaultToolchain)
+ INSERT_VARIABLE(GnVersion)
+ INSERT_VARIABLE(HostCpu)
+ INSERT_VARIABLE(HostOs)
+ INSERT_VARIABLE(Invoker)
+ INSERT_VARIABLE(PythonPath)
+ INSERT_VARIABLE(RootBuildDir)
+ INSERT_VARIABLE(RootGenDir)
+ INSERT_VARIABLE(RootOutDir)
+ INSERT_VARIABLE(TargetCpu)
+ INSERT_VARIABLE(TargetGenDir)
+ INSERT_VARIABLE(TargetName)
+ INSERT_VARIABLE(TargetOs)
+ INSERT_VARIABLE(TargetOutDir)
+ }
+ return info_map;
+}
+
+const VariableInfoMap& GetTargetVariables() {
+ static VariableInfoMap info_map;
+ if (info_map.empty()) {
+ INSERT_VARIABLE(AllDependentConfigs)
+ INSERT_VARIABLE(AllowCircularIncludesFrom)
+ INSERT_VARIABLE(Arflags)
+ INSERT_VARIABLE(Args)
+ INSERT_VARIABLE(Asmflags)
+ INSERT_VARIABLE(AssertNoDeps)
+ INSERT_VARIABLE(BundleRootDir)
+ INSERT_VARIABLE(BundleContentsDir)
+ INSERT_VARIABLE(BundleResourcesDir)
+ INSERT_VARIABLE(BundleDepsFilter)
+ INSERT_VARIABLE(BundleExecutableDir)
+ INSERT_VARIABLE(XcassetCompilerFlags)
+ INSERT_VARIABLE(Cflags)
+ INSERT_VARIABLE(CflagsC)
+ INSERT_VARIABLE(CflagsCC)
+ INSERT_VARIABLE(CflagsObjC)
+ INSERT_VARIABLE(CflagsObjCC)
+ INSERT_VARIABLE(CheckIncludes)
+ INSERT_VARIABLE(CodeSigningArgs)
+ INSERT_VARIABLE(CodeSigningScript)
+ INSERT_VARIABLE(CodeSigningSources)
+ INSERT_VARIABLE(CodeSigningOutputs)
+ INSERT_VARIABLE(CompleteStaticLib)
+ INSERT_VARIABLE(Configs)
+ INSERT_VARIABLE(Data)
+ INSERT_VARIABLE(DataDeps)
+ INSERT_VARIABLE(DataKeys)
+ INSERT_VARIABLE(Defines)
+ INSERT_VARIABLE(Depfile)
+ INSERT_VARIABLE(Deps)
+ INSERT_VARIABLE(Externs)
+ INSERT_VARIABLE(Friend)
+ INSERT_VARIABLE(FrameworkDirs)
+ INSERT_VARIABLE(Frameworks)
+ INSERT_VARIABLE(IncludeDirs)
+ INSERT_VARIABLE(Inputs)
+ INSERT_VARIABLE(Ldflags)
+ INSERT_VARIABLE(Libs)
+ INSERT_VARIABLE(LibDirs)
+ INSERT_VARIABLE(Metadata)
+ INSERT_VARIABLE(OutputDir)
+ INSERT_VARIABLE(OutputExtension)
+ INSERT_VARIABLE(OutputName)
+ INSERT_VARIABLE(OutputPrefixOverride)
+ INSERT_VARIABLE(Outputs)
+ INSERT_VARIABLE(PartialInfoPlist)
+ INSERT_VARIABLE(Pool)
+ INSERT_VARIABLE(PrecompiledHeader)
+ INSERT_VARIABLE(PrecompiledHeaderType)
+ INSERT_VARIABLE(PrecompiledSource)
+ INSERT_VARIABLE(ProductType)
+ INSERT_VARIABLE(Public)
+ INSERT_VARIABLE(PublicConfigs)
+ INSERT_VARIABLE(PublicDeps)
+ INSERT_VARIABLE(Rebase)
+ INSERT_VARIABLE(ResponseFileContents)
+ INSERT_VARIABLE(Script)
+ INSERT_VARIABLE(Sources)
+ INSERT_VARIABLE(Swiftflags)
+ INSERT_VARIABLE(XcodeTestApplicationName)
+ INSERT_VARIABLE(Testonly)
+ INSERT_VARIABLE(Visibility)
+ INSERT_VARIABLE(WalkKeys)
+ INSERT_VARIABLE(WeakFrameworks)
+ INSERT_VARIABLE(WriteOutputConversion)
+ INSERT_VARIABLE(WriteValueContents)
+ INSERT_VARIABLE(WriteRuntimeDeps)
+ INSERT_VARIABLE(XcodeExtraAttributes)
+ InsertRustVariables(&info_map);
+ InsertSwiftVariables(&info_map);
+ }
+ return info_map;
+}
+
+#undef INSERT_VARIABLE
+
+} // namespace variables
diff --git a/gn/src/gn/variables.h b/gn/src/gn/variables.h
new file mode 100644
index 00000000000..f5794d1d691
--- /dev/null
+++ b/gn/src/gn/variables.h
@@ -0,0 +1,379 @@
+// 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 TOOLS_GN_VARIABLES_H_
+#define TOOLS_GN_VARIABLES_H_
+
+#include <map>
+#include <string_view>
+
+namespace variables {
+
+// Builtin vars ----------------------------------------------------------------
+
+extern const char kHostCpu[];
+extern const char kHostCpu_HelpShort[];
+extern const char kHostCpu_Help[];
+
+extern const char kHostOs[];
+extern const char kHostOs_HelpShort[];
+extern const char kHostOs_Help[];
+
+extern const char kCurrentCpu[];
+extern const char kCurrentCpu_HelpShort[];
+extern const char kCurrentCpu_Help[];
+
+extern const char kCurrentOs[];
+extern const char kCurrentOs_HelpShort[];
+extern const char kCurrentOs_Help[];
+
+extern const char kCurrentToolchain[];
+extern const char kCurrentToolchain_HelpShort[];
+extern const char kCurrentToolchain_Help[];
+
+extern const char kDefaultToolchain[];
+extern const char kDefaultToolchain_HelpShort[];
+extern const char kDefaultToolchain_Help[];
+
+extern const char kGnVersion[];
+extern const char kGnVersion_HelpShort[];
+extern const char kGnVersion_Help[];
+
+extern const char kInvoker[];
+extern const char kInvoker_HelpShort[];
+extern const char kInvoker_Help[];
+
+extern const char kPythonPath[];
+extern const char kPythonPath_HelpShort[];
+extern const char kPythonPath_Help[];
+
+extern const char kRootBuildDir[];
+extern const char kRootBuildDir_HelpShort[];
+extern const char kRootBuildDir_Help[];
+
+extern const char kRootGenDir[];
+extern const char kRootGenDir_HelpShort[];
+extern const char kRootGenDir_Help[];
+
+extern const char kRootOutDir[];
+extern const char kRootOutDir_HelpShort[];
+extern const char kRootOutDir_Help[];
+
+extern const char kTargetCpu[];
+extern const char kTargetCpu_HelpShort[];
+extern const char kTargetCpu_Help[];
+
+extern const char kTargetName[];
+extern const char kTargetName_HelpShort[];
+extern const char kTargetName_Help[];
+
+extern const char kTargetOs[];
+extern const char kTargetOs_HelpShort[];
+extern const char kTargetOs_Help[];
+
+extern const char kTargetGenDir[];
+extern const char kTargetGenDir_HelpShort[];
+extern const char kTargetGenDir_Help[];
+
+extern const char kTargetOutDir[];
+extern const char kTargetOutDir_HelpShort[];
+extern const char kTargetOutDir_Help[];
+
+// Target vars -----------------------------------------------------------------
+
+extern const char kAllDependentConfigs[];
+extern const char kAllDependentConfigs_HelpShort[];
+extern const char kAllDependentConfigs_Help[];
+
+extern const char kAllowCircularIncludesFrom[];
+extern const char kAllowCircularIncludesFrom_HelpShort[];
+extern const char kAllowCircularIncludesFrom_Help[];
+
+extern const char kArflags[];
+extern const char kArflags_HelpShort[];
+extern const char kArflags_Help[];
+
+extern const char kArgs[];
+extern const char kArgs_HelpShort[];
+extern const char kArgs_Help[];
+
+extern const char kAsmflags[];
+extern const char kAsmflags_HelpShort[];
+extern const char* kAsmflags_Help;
+
+extern const char kAssertNoDeps[];
+extern const char kAssertNoDeps_HelpShort[];
+extern const char kAssertNoDeps_Help[];
+
+extern const char kBundleRootDir[];
+extern const char kBundleRootDir_HelpShort[];
+extern const char kBundleRootDir_Help[];
+
+extern const char kBundleContentsDir[];
+extern const char kBundleContentsDir_HelpShort[];
+extern const char kBundleContentsDir_Help[];
+
+extern const char kBundleResourcesDir[];
+extern const char kBundleResourcesDir_HelpShort[];
+extern const char kBundleResourcesDir_Help[];
+
+extern const char kBundleDepsFilter[];
+extern const char kBundleDepsFilter_HelpShort[];
+extern const char kBundleDepsFilter_Help[];
+
+extern const char kBundleExecutableDir[];
+extern const char kBundleExecutableDir_HelpShort[];
+extern const char kBundleExecutableDir_Help[];
+
+extern const char kXcassetCompilerFlags[];
+extern const char kXcassetCompilerFlags_HelpShort[];
+extern const char kXcassetCompilerFlags_Help[];
+
+extern const char kCflags[];
+extern const char kCflags_HelpShort[];
+extern const char* kCflags_Help;
+
+extern const char kCflagsC[];
+extern const char kCflagsC_HelpShort[];
+extern const char* kCflagsC_Help;
+
+extern const char kCflagsCC[];
+extern const char kCflagsCC_HelpShort[];
+extern const char* kCflagsCC_Help;
+
+extern const char kCflagsObjC[];
+extern const char kCflagsObjC_HelpShort[];
+extern const char* kCflagsObjC_Help;
+
+extern const char kCflagsObjCC[];
+extern const char kCflagsObjCC_HelpShort[];
+extern const char* kCflagsObjCC_Help;
+
+extern const char kCheckIncludes[];
+extern const char kCheckIncludes_HelpShort[];
+extern const char kCheckIncludes_Help[];
+
+extern const char kCodeSigningArgs[];
+extern const char kCodeSigningArgs_HelpShort[];
+extern const char kCodeSigningArgs_Help[];
+
+extern const char kCodeSigningScript[];
+extern const char kCodeSigningScript_HelpShort[];
+extern const char kCodeSigningScript_Help[];
+
+extern const char kCodeSigningSources[];
+extern const char kCodeSigningSources_HelpShort[];
+extern const char kCodeSigningSources_Help[];
+
+extern const char kCodeSigningOutputs[];
+extern const char kCodeSigningOutputs_HelpShort[];
+extern const char kCodeSigningOutputs_Help[];
+
+extern const char kCompleteStaticLib[];
+extern const char kCompleteStaticLib_HelpShort[];
+extern const char kCompleteStaticLib_Help[];
+
+extern const char kConfigs[];
+extern const char kConfigs_HelpShort[];
+extern const char kConfigs_Help[];
+
+extern const char kData[];
+extern const char kData_HelpShort[];
+extern const char kData_Help[];
+
+extern const char kDataDeps[];
+extern const char kDataDeps_HelpShort[];
+extern const char kDataDeps_Help[];
+
+extern const char kDataKeys[];
+extern const char kDataKeys_HelpShort[];
+extern const char kDataKeys_Help[];
+
+extern const char kDefines[];
+extern const char kDefines_HelpShort[];
+extern const char kDefines_Help[];
+
+extern const char kDepfile[];
+extern const char kDepfile_HelpShort[];
+extern const char kDepfile_Help[];
+
+extern const char kDeps[];
+extern const char kDeps_HelpShort[];
+extern const char kDeps_Help[];
+
+extern const char kExterns[];
+extern const char kExterns_HelpShort[];
+extern const char kExterns_Help[];
+
+extern const char kFriend[];
+extern const char kFriend_HelpShort[];
+extern const char kFriend_Help[];
+
+extern const char kFrameworkDirs[];
+extern const char kFrameworkDirs_HelpShort[];
+extern const char kFrameworkDirs_Help[];
+
+extern const char kFrameworks[];
+extern const char kFrameworks_HelpShort[];
+extern const char kFrameworks_Help[];
+
+extern const char kIncludeDirs[];
+extern const char kIncludeDirs_HelpShort[];
+extern const char kIncludeDirs_Help[];
+
+extern const char kInputs[];
+extern const char kInputs_HelpShort[];
+extern const char kInputs_Help[];
+
+extern const char kLdflags[];
+extern const char kLdflags_HelpShort[];
+extern const char kLdflags_Help[];
+
+extern const char kLibDirs[];
+extern const char kLibDirs_HelpShort[];
+extern const char kLibDirs_Help[];
+
+extern const char kLibs[];
+extern const char kLibs_HelpShort[];
+extern const char kLibs_Help[];
+
+extern const char kMetadata[];
+extern const char kMetadata_HelpShort[];
+extern const char kMetadata_Help[];
+
+extern const char kOutputDir[];
+extern const char kOutputDir_HelpShort[];
+extern const char kOutputDir_Help[];
+
+extern const char kOutputExtension[];
+extern const char kOutputExtension_HelpShort[];
+extern const char kOutputExtension_Help[];
+
+extern const char kOutputName[];
+extern const char kOutputName_HelpShort[];
+extern const char kOutputName_Help[];
+
+extern const char kOutputPrefixOverride[];
+extern const char kOutputPrefixOverride_HelpShort[];
+extern const char kOutputPrefixOverride_Help[];
+
+extern const char kOutputs[];
+extern const char kOutputs_HelpShort[];
+extern const char kOutputs_Help[];
+
+extern const char kPartialInfoPlist[];
+extern const char kPartialInfoPlist_HelpShort[];
+extern const char kPartialInfoPlist_Help[];
+
+extern const char kPool[];
+extern const char kPool_HelpShort[];
+extern const char kPool_Help[];
+
+extern const char kPrecompiledHeader[];
+extern const char kPrecompiledHeader_HelpShort[];
+extern const char kPrecompiledHeader_Help[];
+
+extern const char kPrecompiledHeaderType[];
+extern const char kPrecompiledHeaderType_HelpShort[];
+extern const char kPrecompiledHeaderType_Help[];
+
+extern const char kPrecompiledSource[];
+extern const char kPrecompiledSource_HelpShort[];
+extern const char kPrecompiledSource_Help[];
+
+extern const char kProductType[];
+extern const char kProductType_HelpShort[];
+extern const char kProductType_Help[];
+
+extern const char kPublic[];
+extern const char kPublic_HelpShort[];
+extern const char kPublic_Help[];
+
+extern const char kPublicConfigs[];
+extern const char kPublicConfigs_HelpShort[];
+extern const char kPublicConfigs_Help[];
+
+extern const char kPublicDeps[];
+extern const char kPublicDeps_HelpShort[];
+extern const char kPublicDeps_Help[];
+
+extern const char kRebase[];
+extern const char kRebase_HelpShort[];
+extern const char kRebase_Help[];
+
+extern const char kResponseFileContents[];
+extern const char kResponseFileContents_HelpShort[];
+extern const char kResponseFileContents_Help[];
+
+extern const char kScript[];
+extern const char kScript_HelpShort[];
+extern const char kScript_Help[];
+
+extern const char kSources[];
+extern const char kSources_HelpShort[];
+extern const char kSources_Help[];
+
+extern const char kSwiftflags[];
+extern const char kSwiftflags_HelpShort[];
+extern const char* kSwiftflags_Help;
+
+extern const char kXcodeTestApplicationName[];
+extern const char kXcodeTestApplicationName_HelpShort[];
+extern const char kXcodeTestApplicationName_Help[];
+
+extern const char kTestonly[];
+extern const char kTestonly_HelpShort[];
+extern const char kTestonly_Help[];
+
+extern const char kVisibility[];
+extern const char kVisibility_HelpShort[];
+extern const char kVisibility_Help[];
+
+extern const char kWalkKeys[];
+extern const char kWalkKeys_HelpShort[];
+extern const char kWalkKeys_Help[];
+
+extern const char kWeakFrameworks[];
+extern const char kWeakFrameworks_HelpShort[];
+extern const char kWeakFrameworks_Help[];
+
+extern const char kWriteValueContents[];
+extern const char kWriteValueContents_HelpShort[];
+extern const char kWriteValueContents_Help[];
+
+extern const char kWriteOutputConversion[];
+extern const char kWriteOutputConversion_HelpShort[];
+extern const char kWriteOutputConversion_Help[];
+
+extern const char kWriteRuntimeDeps[];
+extern const char kWriteRuntimeDeps_HelpShort[];
+extern const char kWriteRuntimeDeps_Help[];
+
+extern const char kXcodeExtraAttributes[];
+extern const char kXcodeExtraAttributes_HelpShort[];
+extern const char kXcodeExtraAttributes_Help[];
+
+// -----------------------------------------------------------------------------
+
+struct VariableInfo {
+ VariableInfo();
+ VariableInfo(const char* in_help_short, const char* in_help);
+
+ const char* help_short;
+ const char* help;
+};
+
+using VariableInfoMap = std::map<std::string_view, VariableInfo>;
+
+// Returns the built-in readonly variables.
+// Note: this is used only for help so this getter is not threadsafe.
+const VariableInfoMap& GetBuiltinVariables();
+
+// Returns the variables used by target generators.
+// Note: this is used only for help so this getter is not threadsafe.
+const VariableInfoMap& GetTargetVariables();
+
+} // namespace variables
+
+#endif // TOOLS_GN_VARIABLES_H_
diff --git a/gn/src/gn/vector_utils.h b/gn/src/gn/vector_utils.h
new file mode 100644
index 00000000000..8366dda173e
--- /dev/null
+++ b/gn/src/gn/vector_utils.h
@@ -0,0 +1,103 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_VECTOR_UTILS_H_
+#define TOOLS_GN_VECTOR_UTILS_H_
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+// A VectorSetSorter is a convenience class used to efficiently sort and
+// de-duplicate one or more sets of items of type T, then iterate over the
+// result, or get it as a simple vector. Usage is the following:
+//
+// For performance reasons, this implementation only stores pointers to the
+// input items in order to minimize memory usage. Callers should ensure the
+// items added to this sorter do not change until the instance is destroyed.
+//
+// 1) Create instance, passing an optional initial capacity.
+//
+// 2) Add items using one of the Add() methods, as many times as
+// necessary. Note that this records only pointers to said items
+// so their content should not change until the instance is destroyed.
+//
+// 3) Call IteratorOver() to iterate over all sorted and de-duplicated
+// items.
+//
+// 4) Alternatively, call AsVector() to return a new vector that contains
+// copies of the original sorted / deduplicated items.
+//
+template <typename T>
+class VectorSetSorter {
+ public:
+ // Constructor. |initial_capacity| might be provided to minimize the number
+ // of allocations performed by this instance, if the maximum number of
+ // input items is known in advance.
+ VectorSetSorter(size_t initial_capacity = 0) {
+ ptrs_.reserve(initial_capacity);
+ }
+
+ // Add one single item to the sorter.
+ void Add(const T& item) {
+ ptrs_.push_back(&item);
+ sorted_ = false;
+ }
+
+ // Add one range of items to the sorter.
+ void Add(typename std::vector<T>::const_iterator begin,
+ typename std::vector<T>::const_iterator end) {
+ for (; begin != end; ++begin)
+ ptrs_.push_back(&(*begin));
+ sorted_ = false;
+ }
+
+ // Add one range of items to the sorter.
+ void Add(const T* start, const T* end) {
+ for (; start != end; ++start)
+ ptrs_.push_back(start);
+ sorted_ = false;
+ }
+
+ // Iterate over all sorted items, removing duplicates from the loop.
+ // |item_callback| is a callable that will be invoked for each item in the
+ // result.
+ template <typename ITEM_CALLBACK>
+ void IterateOver(ITEM_CALLBACK item_callback) {
+ if (!sorted_) {
+ Sort();
+ }
+ const T* prev_item = nullptr;
+ for (const T* item : ptrs_) {
+ if (!prev_item || *prev_item != *item) {
+ item_callback(*item);
+ prev_item = item;
+ }
+ }
+ }
+
+ // Return the sorted and de-duplicated resulting set as a vector of items.
+ // Note that this copies the input items.
+ std::vector<T> AsVector() {
+ std::vector<T> result;
+ result.reserve(ptrs_.size());
+ IterateOver([&result](const T& item) { result.push_back(item); });
+ return result;
+ }
+
+ private:
+ // Sort all items previously added to this instance.
+ // Must be called after adding all desired items, and before
+ // calling IterateOver() or AsVector().
+ void Sort() {
+ std::sort(ptrs_.begin(), ptrs_.end(),
+ [](const T* a, const T* b) { return *a < *b; });
+ sorted_ = true;
+ }
+
+ std::vector<const T*> ptrs_;
+ bool sorted_ = false;
+};
+
+#endif // TOOLS_GN_VECTOR_UTILS_H_
diff --git a/gn/src/gn/vector_utils_unittest.cc b/gn/src/gn/vector_utils_unittest.cc
new file mode 100644
index 00000000000..c7cee012eff
--- /dev/null
+++ b/gn/src/gn/vector_utils_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium 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 "gn/vector_utils.h"
+
+#include "util/test/test.h"
+
+#include <string>
+
+TEST(VectorSetSorter, AsVectorWithStrings) {
+ VectorSetSorter<std::string> sorter;
+
+ std::vector<std::string> input = {
+ "World!", "Hello", "bonjour", "Hello", "monde!", "World!",
+ };
+
+ sorter.Add(input.begin(), input.end());
+ auto result = sorter.AsVector();
+
+ ASSERT_EQ(result.size(), 4u) << result.size();
+ EXPECT_STREQ(result[0].c_str(), "Hello");
+ EXPECT_STREQ(result[1].c_str(), "World!");
+ EXPECT_STREQ(result[2].c_str(), "bonjour");
+ EXPECT_STREQ(result[3].c_str(), "monde!");
+}
+
+TEST(VectorSetSorter, IterateOverWithStrings) {
+ VectorSetSorter<std::string> sorter;
+
+ std::vector<std::string> input = {
+ "World!", "Hello", "bonjour", "Hello", "monde!", "World!",
+ };
+
+ sorter.Add(input.begin(), input.end());
+
+ std::vector<std::string> result;
+
+ sorter.IterateOver(
+ [&result](const std::string& str) { result.push_back(str); });
+
+ ASSERT_EQ(result.size(), 4u) << result.size();
+ EXPECT_STREQ(result[0].c_str(), "Hello");
+ EXPECT_STREQ(result[1].c_str(), "World!");
+ EXPECT_STREQ(result[2].c_str(), "bonjour");
+ EXPECT_STREQ(result[3].c_str(), "monde!");
+}
diff --git a/gn/src/gn/version.cc b/gn/src/gn/version.cc
new file mode 100644
index 00000000000..0ed25f2feea
--- /dev/null
+++ b/gn/src/gn/version.cc
@@ -0,0 +1,80 @@
+// Copyright 2020 The Chromium 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 "gn/version.h"
+#include <iostream>
+#include <string_view>
+#include <tuple>
+
+#include "base/strings/string_number_conversions.h"
+
+using namespace std::literals;
+
+constexpr std::string_view kDot = "."sv;
+
+Version::Version(int major, int minor, int patch)
+ : major_(major), minor_(minor), patch_(patch) {}
+
+// static
+std::optional<Version> Version::FromString(std::string s) {
+ int major = 0, minor = 0, patch = 0;
+ // First, parse the major version.
+ size_t major_begin = 0;
+ if (size_t major_end = s.find(kDot, major_begin);
+ major_end != std::string::npos) {
+ if (!base::StringToInt(s.substr(major_begin, major_end - major_begin),
+ &major))
+ return {};
+ // Then, parse the minor version.
+ size_t minor_begin = major_end + kDot.size();
+ if (size_t minor_end = s.find(kDot, minor_begin);
+ minor_end != std::string::npos) {
+ if (!base::StringToInt(s.substr(minor_begin, minor_end - minor_begin),
+ &minor))
+ return {};
+ // Finally, parse the patch version.
+ size_t patch_begin = minor_end + kDot.size();
+ if (!base::StringToInt(s.substr(patch_begin, std::string::npos), &patch))
+ return {};
+ return Version(major, minor, patch);
+ }
+ }
+ return {};
+}
+
+bool Version::operator==(const Version& other) const {
+ return other.major_ == major_ && other.minor_ == minor_ &&
+ other.patch_ == patch_;
+}
+
+bool Version::operator<(const Version& other) const {
+ return std::tie(major_, minor_, patch_) <
+ std::tie(other.major_, other.minor_, other.patch_);
+}
+
+bool Version::operator!=(const Version& other) const {
+ return !(*this == other);
+}
+
+bool Version::operator>=(const Version& other) const {
+ return !(*this < other);
+}
+
+bool Version::operator>(const Version& other) const {
+ return other < *this;
+}
+
+bool Version::operator<=(const Version& other) const {
+ return !(*this > other);
+}
+
+std::string Version::Describe() const {
+ std::string ret;
+ ret += base::IntToString(major_);
+ ret += kDot;
+ ret += base::IntToString(minor_);
+ ret += kDot;
+ ret += base::IntToString(patch_);
+ return ret;
+}
diff --git a/gn/src/gn/version.h b/gn/src/gn/version.h
new file mode 100644
index 00000000000..7fcf81d1170
--- /dev/null
+++ b/gn/src/gn/version.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium 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 TOOLS_GN_VERSION_H_
+#define TOOLS_GN_VERSION_H_
+
+#include <optional>
+#include <string>
+
+// Represents a semantic version.
+class Version {
+ public:
+ Version(int major, int minor, int patch);
+
+ static std::optional<Version> FromString(std::string s);
+
+ int major() const { return major_; }
+ int minor() const { return minor_; }
+ int patch() const { return patch_; }
+
+ bool operator==(const Version& other) const;
+ bool operator<(const Version& other) const;
+ bool operator!=(const Version& other) const;
+ bool operator>=(const Version& other) const;
+ bool operator>(const Version& other) const;
+ bool operator<=(const Version& other) const;
+
+ std::string Describe() const;
+
+ private:
+ int major_;
+ int minor_;
+ int patch_;
+};
+
+#endif // TOOLS_GN_VERSION_H_
diff --git a/gn/src/gn/version_unittest.cc b/gn/src/gn/version_unittest.cc
new file mode 100644
index 00000000000..d1530f3a3ad
--- /dev/null
+++ b/gn/src/gn/version_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium 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 "gn/version.h"
+
+#include "util/test/test.h"
+
+TEST(VersionTest, FromString) {
+ Version v0_0_1{0, 0, 1};
+ ASSERT_EQ(Version::FromString("0.0.1"), v0_0_1);
+ Version v0_1_0{0, 1, 0};
+ ASSERT_EQ(Version::FromString("0.1.0"), v0_1_0);
+ Version v1_0_0{1, 0, 0};
+ ASSERT_EQ(Version::FromString("1.0.0"), v1_0_0);
+}
+
+TEST(VersionTest, Comparison) {
+ Version v0_0_1{0, 0, 1};
+ Version v0_1_0{0, 1, 0};
+ ASSERT_TRUE(v0_0_1 == v0_0_1);
+ ASSERT_TRUE(v0_0_1 != v0_1_0);
+ ASSERT_TRUE(v0_0_1 <= v0_0_1);
+ ASSERT_TRUE(v0_0_1 <= v0_1_0);
+ ASSERT_TRUE(v0_0_1 < v0_1_0);
+ ASSERT_TRUE(v0_0_1 >= v0_0_1);
+ ASSERT_TRUE(v0_1_0 > v0_0_1);
+ ASSERT_TRUE(v0_1_0 >= v0_0_1);
+}
+
+TEST(VersionTest, Describe) {
+ ASSERT_EQ(Version::FromString("0.0.1")->Describe(), "0.0.1");
+}
diff --git a/gn/src/gn/visibility.cc b/gn/src/gn/visibility.cc
new file mode 100644
index 00000000000..eb99a2719fa
--- /dev/null
+++ b/gn/src/gn/visibility.cc
@@ -0,0 +1,120 @@
+// 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 "gn/visibility.h"
+
+#include <memory>
+#include <string_view>
+
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "gn/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/item.h"
+#include "gn/label.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+#include "gn/variables.h"
+
+Visibility::Visibility() = default;
+
+Visibility::~Visibility() = default;
+
+bool Visibility::Set(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Value& value,
+ Err* err) {
+ patterns_.clear();
+
+ if (!value.VerifyTypeIs(Value::LIST, err)) {
+ CHECK(err->has_error());
+ return false;
+ }
+
+ for (const auto& item : value.list_value()) {
+ patterns_.push_back(
+ LabelPattern::GetPattern(current_dir, source_root, item, err));
+ if (err->has_error())
+ return false;
+ }
+ return true;
+}
+
+void Visibility::SetPublic() {
+ patterns_.clear();
+ patterns_.push_back(LabelPattern(LabelPattern::RECURSIVE_DIRECTORY,
+ SourceDir(), std::string(), Label()));
+}
+
+void Visibility::SetPrivate(const SourceDir& current_dir) {
+ patterns_.clear();
+ patterns_.push_back(LabelPattern(LabelPattern::DIRECTORY, current_dir,
+ std::string(), Label()));
+}
+
+bool Visibility::CanSeeMe(const Label& label) const {
+ return LabelPattern::VectorMatches(patterns_, label);
+}
+
+std::string Visibility::Describe(int indent, bool include_brackets) const {
+ std::string outer_indent_string(indent, ' ');
+
+ if (patterns_.empty())
+ return outer_indent_string + "[] (no visibility)\n";
+
+ std::string result;
+
+ std::string inner_indent_string = outer_indent_string;
+ if (include_brackets) {
+ result += outer_indent_string + "[\n";
+ // Indent the insides more if brackets are requested.
+ inner_indent_string += " ";
+ }
+
+ for (const auto& pattern : patterns_)
+ result += inner_indent_string + pattern.Describe() + "\n";
+
+ if (include_brackets)
+ result += outer_indent_string + "]\n";
+ return result;
+}
+
+std::unique_ptr<base::Value> Visibility::AsValue() const {
+ auto res = std::make_unique<base::ListValue>();
+ for (const auto& pattern : patterns_)
+ res->AppendString(pattern.Describe());
+ return std::move(res);
+}
+
+// static
+bool Visibility::CheckItemVisibility(const Item* from,
+ const Item* to,
+ Err* err) {
+ if (!to->visibility().CanSeeMe(from->label())) {
+ std::string to_label = to->label().GetUserVisibleName(false);
+ *err = Err(from->defined_from(), "Dependency not allowed.",
+ "The item " + from->label().GetUserVisibleName(false) +
+ "\n"
+ "can not depend on " +
+ to_label +
+ "\n"
+ "because it is not in " +
+ to_label +
+ "'s visibility list: " + to->visibility().Describe(0, true));
+ return false;
+ }
+ return true;
+}
+
+// static
+bool Visibility::FillItemVisibility(Item* item, Scope* scope, Err* err) {
+ const Value* vis_value = scope->GetValue(variables::kVisibility, true);
+ if (vis_value)
+ item->visibility().Set(
+ scope->GetSourceDir(),
+ scope->settings()->build_settings()->root_path_utf8(), *vis_value, err);
+ else // Default to public.
+ item->visibility().SetPublic();
+ return !err->has_error();
+}
diff --git a/gn/src/gn/visibility.h b/gn/src/gn/visibility.h
new file mode 100644
index 00000000000..fc1673138aa
--- /dev/null
+++ b/gn/src/gn/visibility.h
@@ -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.
+
+#ifndef TOOLS_GN_VISIBILITY_H_
+#define TOOLS_GN_VISIBILITY_H_
+
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "base/macros.h"
+#include "gn/label_pattern.h"
+#include "gn/source_dir.h"
+
+namespace base {
+class Value;
+}
+
+class Err;
+class Item;
+class Label;
+class Scope;
+class Value;
+
+class Visibility {
+ public:
+ // Defaults to private visibility (only the current file).
+ Visibility();
+ ~Visibility();
+
+ // Set the visibility to the thing specified by the given value. On failure,
+ // returns false and sets the error.
+ bool Set(const SourceDir& current_dir,
+ const std::string_view& source_root,
+ const Value& value,
+ Err* err);
+
+ // Sets the visibility to be public.
+ void SetPublic();
+
+ // Sets the visibility to be private to the given directory.
+ void SetPrivate(const SourceDir& current_dir);
+
+ // Returns true if the target with the given label can depend on one with the
+ // current visibility.
+ bool CanSeeMe(const Label& label) const;
+
+ // Returns a string listing the visibility. |indent| number of spaces will
+ // be added on the left side of the output. If |include_brackets| is set, the
+ // result will be wrapped in "[ ]" and the contents further indented. The
+ // result will end in a newline.
+ std::string Describe(int indent, bool include_brackets) const;
+
+ // Returns value representation of this visibility
+ std::unique_ptr<base::Value> AsValue() const;
+
+ // Helper function to check visibility between the given two items. If
+ // to is invisible to from, returns false and sets the error.
+ static bool CheckItemVisibility(const Item* from, const Item* to, Err* err);
+
+ // Helper function to fill an item's visibility from the "visibility" value
+ // in the current scope.
+ static bool FillItemVisibility(Item* item, Scope* scope, Err* err);
+
+ private:
+ std::vector<LabelPattern> patterns_;
+
+ DISALLOW_COPY_AND_ASSIGN(Visibility);
+};
+
+#endif // TOOLS_GN_VISIBILITY_H_
diff --git a/gn/src/gn/visibility_unittest.cc b/gn/src/gn/visibility_unittest.cc
new file mode 100644
index 00000000000..0122119e557
--- /dev/null
+++ b/gn/src/gn/visibility_unittest.cc
@@ -0,0 +1,71 @@
+// 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 "gn/visibility.h"
+#include "gn/err.h"
+#include "gn/label.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+#include "util/test/test.h"
+
+TEST(Visibility, CanSeeMe) {
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(Value(nullptr, "//rec/*")); // Recursive.
+ list.list_value().push_back(Value(nullptr, "//dir:*")); // One dir.
+ list.list_value().push_back(Value(nullptr, "//my:name")); // Exact match.
+
+ Err err;
+ Visibility vis;
+ ASSERT_TRUE(vis.Set(SourceDir("//"), std::string_view(), list, &err));
+
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//my/"), "notname")));
+
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//my/"), "name")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//rec/"), "anything")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//rec/a/"), "anything")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//rec/b/"), "anything")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//dir/"), "anything")));
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//dir/a/"), "anything")));
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//directory/"), "anything")));
+}
+
+TEST(Visibility, Public) {
+ Err err;
+ Visibility vis;
+
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(Value(nullptr, "*"));
+ ASSERT_TRUE(vis.Set(SourceDir("//"), std::string_view(), list, &err));
+
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//"), "")));
+}
+
+TEST(Visibility, Private) {
+ Err err;
+ Visibility vis;
+ ASSERT_TRUE(vis.Set(SourceDir("//"), std::string_view(),
+ Value(nullptr, Value::LIST), &err));
+
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//"), "")));
+}
+
+TEST(Visibility, AboveSourceDir) {
+ std::string source_root = "/foo/bar/baz/";
+ SourceDir cur_dir("//");
+
+ Err err;
+ Visibility vis;
+
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(Value(nullptr, "../../*"));
+ ASSERT_TRUE(vis.Set(cur_dir, source_root, list, &err));
+
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("/foo/"), "foo")));
+ EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("/foo/bar/"), "bar")));
+ EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("/nowhere/"), "foo")));
+}
diff --git a/gn/src/gn/visual_studio_utils.cc b/gn/src/gn/visual_studio_utils.cc
new file mode 100644
index 00000000000..5282d0eb303
--- /dev/null
+++ b/gn/src/gn/visual_studio_utils.cc
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium 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 "gn/visual_studio_utils.h"
+
+#include <vector>
+
+#include "base/md5.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+
+CompilerOptions::CompilerOptions() = default;
+
+CompilerOptions::~CompilerOptions() = default;
+
+LinkerOptions::LinkerOptions() = default;
+
+LinkerOptions::~LinkerOptions() = default;
+
+std::string MakeGuid(const std::string& entry_path, const std::string& seed) {
+ std::string str = base::ToUpperASCII(base::MD5String(seed + entry_path));
+ return '{' + str.substr(0, 8) + '-' + str.substr(8, 4) + '-' +
+ str.substr(12, 4) + '-' + str.substr(16, 4) + '-' +
+ str.substr(20, 12) + '}';
+}
+
+#define SetOption(condition, member, value) \
+ if (condition) { \
+ options->member = value; \
+ return; \
+ }
+
+#define AppendOption(condition, member, value, separator) \
+ if (condition) { \
+ options->member += value + separator; \
+ return; \
+ }
+
+void ParseCompilerOption(const std::string& cflag, CompilerOptions* options) {
+ if (cflag.size() > 2 && cflag[0] == '/') {
+ switch (cflag[1]) {
+ case 'F':
+ AppendOption(cflag.size() > 3 && cflag[2] == 'I', forced_include_files,
+ cflag.substr(3), ';') break;
+
+ case 'G':
+ if (cflag[2] == 'S') {
+ SetOption(cflag.size() == 3, buffer_security_check, "true")
+ SetOption(cflag.size() == 4 && cflag[3] == '-',
+ buffer_security_check, "false")
+ }
+ break;
+
+ case 'M':
+ switch (cflag[2]) {
+ case 'D':
+ SetOption(cflag.size() == 3, runtime_library, "MultiThreadedDLL")
+ SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library,
+ "MultiThreadedDebugDLL") break;
+
+ case 'T':
+ SetOption(cflag.size() == 3, runtime_library, "MultiThreaded")
+ SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library,
+ "MultiThreadedDebug") break;
+ }
+ break;
+
+ case 'O':
+ switch (cflag[2]) {
+ case '1':
+ SetOption(cflag.size() == 3, optimization, "MinSpace") break;
+
+ case '2':
+ SetOption(cflag.size() == 3, optimization, "MaxSpeed") break;
+
+ case 'd':
+ SetOption(cflag.size() == 3, optimization, "Disabled") break;
+
+ case 'x':
+ SetOption(cflag.size() == 3, optimization, "Full") break;
+ }
+ break;
+
+ case 'T':
+ // Skip flags that cause treating all source files as C and C++ files.
+ if (cflag.size() == 3 && (cflag[2] == 'C' || cflag[2] == 'P'))
+ return;
+ break;
+
+ case 'W':
+ switch (cflag[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ SetOption(cflag.size() == 3, warning_level,
+ std::string("Level") + cflag[2]) break;
+
+ case 'X':
+ SetOption(cflag.size() == 3, treat_warning_as_error, "true") break;
+ }
+ break;
+
+ case 'w':
+ AppendOption(cflag.size() > 3 && cflag[2] == 'd',
+ disable_specific_warnings, cflag.substr(3), ';') break;
+ }
+ }
+
+ // Put everything else into additional_options.
+ options->additional_options += cflag + ' ';
+}
+
+// Parses |ldflags| value and stores it in |options|.
+void ParseLinkerOption(const std::string& ldflag, LinkerOptions* options) {
+ const char kSubsytemPrefix[] = "/SUBSYSTEM:";
+ if (base::StartsWith(ldflag, kSubsytemPrefix, base::CompareCase::SENSITIVE)) {
+ const std::string subsystem(
+ ldflag.begin() + std::string(kSubsytemPrefix).length(), ldflag.end());
+ const std::vector<std::string> tokens = base::SplitString(
+ subsystem, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (!tokens.empty())
+ options->subsystem = tokens[0];
+ }
+}
diff --git a/gn/tools/gn/visual_studio_utils.h b/gn/src/gn/visual_studio_utils.h
index 319428fbc13..319428fbc13 100644
--- a/gn/tools/gn/visual_studio_utils.h
+++ b/gn/src/gn/visual_studio_utils.h
diff --git a/gn/src/gn/visual_studio_utils_unittest.cc b/gn/src/gn/visual_studio_utils_unittest.cc
new file mode 100644
index 00000000000..bf84a9e9810
--- /dev/null
+++ b/gn/src/gn/visual_studio_utils_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright 2016 The Chromium 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 "gn/visual_studio_utils.h"
+
+#include "base/strings/string_util.h"
+#include "util/test/test.h"
+
+TEST(VisualStudioUtils, MakeGuid) {
+ std::string pattern = "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}";
+ std::string guid = MakeGuid(__FILE__, "foo");
+ ASSERT_EQ(pattern.size(), guid.size());
+ for (size_t i = 0; i < pattern.size(); ++i) {
+ if (pattern[i] == 'x')
+ ASSERT_TRUE(base::IsAsciiAlpha(guid[i]) || base::IsAsciiDigit(guid[i]));
+ else
+ ASSERT_EQ(pattern[i], guid[i]);
+ }
+
+ // Calling function again should produce the same GUID.
+ ASSERT_EQ(guid, MakeGuid(__FILE__, "foo"));
+
+ // GUIDs should be different if path or seed is different.
+ ASSERT_NE(guid, MakeGuid(std::string(__FILE__) + ".txt", "foo"));
+ ASSERT_NE(guid, MakeGuid(__FILE__, "bar"));
+}
+
+TEST(VisualStudioUtils, ParseCompilerOption) {
+ CompilerOptions options;
+ ParseCompilerOption("/FIinclude.h", &options);
+ ParseCompilerOption("/FIC:/path/file.h", &options);
+ ASSERT_EQ("include.h;C:/path/file.h;", options.forced_include_files);
+
+ CHECK(options.buffer_security_check.empty());
+ ParseCompilerOption("/GS", &options);
+ ASSERT_EQ("true", options.buffer_security_check);
+ ParseCompilerOption("/GS-", &options);
+ ASSERT_EQ("false", options.buffer_security_check);
+
+ CHECK(options.runtime_library.empty());
+ ParseCompilerOption("/MD", &options);
+ ASSERT_EQ("MultiThreadedDLL", options.runtime_library);
+ ParseCompilerOption("/MDd", &options);
+ ASSERT_EQ("MultiThreadedDebugDLL", options.runtime_library);
+ ParseCompilerOption("/MT", &options);
+ ASSERT_EQ("MultiThreaded", options.runtime_library);
+ ParseCompilerOption("/MTd", &options);
+ ASSERT_EQ("MultiThreadedDebug", options.runtime_library);
+
+ CHECK(options.optimization.empty());
+ ParseCompilerOption("/O1", &options);
+ ASSERT_EQ("MinSpace", options.optimization);
+ ParseCompilerOption("/O2", &options);
+ ASSERT_EQ("MaxSpeed", options.optimization);
+ ParseCompilerOption("/Od", &options);
+ ASSERT_EQ("Disabled", options.optimization);
+ ParseCompilerOption("/Ox", &options);
+ ASSERT_EQ("Full", options.optimization);
+
+ CHECK(options.additional_options.empty());
+ ParseCompilerOption("/TC", &options);
+ ASSERT_TRUE(options.additional_options.empty());
+ ParseCompilerOption("/TP", &options);
+ ASSERT_TRUE(options.additional_options.empty());
+
+ CHECK(options.warning_level.empty());
+ ParseCompilerOption("/W0", &options);
+ ASSERT_EQ("Level0", options.warning_level);
+ ParseCompilerOption("/W1", &options);
+ ASSERT_EQ("Level1", options.warning_level);
+ ParseCompilerOption("/W2", &options);
+ ASSERT_EQ("Level2", options.warning_level);
+ ParseCompilerOption("/W3", &options);
+ ASSERT_EQ("Level3", options.warning_level);
+ ParseCompilerOption("/W4", &options);
+ ASSERT_EQ("Level4", options.warning_level);
+
+ CHECK(options.treat_warning_as_error.empty());
+ ParseCompilerOption("/WX", &options);
+ ASSERT_EQ("true", options.treat_warning_as_error);
+
+ CHECK(options.disable_specific_warnings.empty());
+ ParseCompilerOption("/wd1234", &options);
+ ParseCompilerOption("/wd56", &options);
+ ASSERT_EQ("1234;56;", options.disable_specific_warnings);
+
+ CHECK(options.additional_options.empty());
+ ParseCompilerOption("/MP", &options);
+ ParseCompilerOption("/bigobj", &options);
+ ParseCompilerOption("/Zc:sizedDealloc", &options);
+ ASSERT_EQ("/MP /bigobj /Zc:sizedDealloc ", options.additional_options);
+}
+
+TEST(VisualStudioUtils, ParseLinkerOption) {
+ LinkerOptions options;
+ ParseLinkerOption("/SUBSYSTEM:CONSOLE,5.02h", &options);
+ ASSERT_EQ("CONSOLE", options.subsystem);
+
+ ParseLinkerOption("/SUBSYSTEM:WINDOWS", &options);
+ ASSERT_EQ("WINDOWS", options.subsystem);
+}
diff --git a/gn/src/gn/visual_studio_writer.cc b/gn/src/gn/visual_studio_writer.cc
new file mode 100644
index 00000000000..6ee56e86d0a
--- /dev/null
+++ b/gn/src/gn/visual_studio_writer.cc
@@ -0,0 +1,916 @@
+// Copyright 2016 The Chromium 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 "gn/visual_studio_writer.h"
+
+#include <algorithm>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/containers/queue.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gn/builder.h"
+#include "gn/commands.h"
+#include "gn/config.h"
+#include "gn/config_values_extractors.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/label_pattern.h"
+#include "gn/parse_tree.h"
+#include "gn/path_output.h"
+#include "gn/standard_out.h"
+#include "gn/target.h"
+#include "gn/variables.h"
+#include "gn/visual_studio_utils.h"
+#include "gn/xml_element_writer.h"
+
+#if defined(OS_WIN)
+#include "base/win/registry.h"
+#endif
+
+namespace {
+
+struct SemicolonSeparatedWriter {
+ void operator()(const std::string& value, std::ostream& out) const {
+ out << XmlEscape(value) + ';';
+ }
+};
+
+struct IncludeDirWriter {
+ explicit IncludeDirWriter(PathOutput& path_output)
+ : path_output_(path_output) {}
+ ~IncludeDirWriter() = default;
+
+ void operator()(const SourceDir& dir, std::ostream& out) const {
+ path_output_.WriteDir(out, dir, PathOutput::DIR_NO_LAST_SLASH);
+ out << ";";
+ }
+
+ PathOutput& path_output_;
+};
+
+struct SourceFileWriter {
+ SourceFileWriter(PathOutput& path_output, const SourceFile& source_file)
+ : path_output_(path_output), source_file_(source_file) {}
+ ~SourceFileWriter() = default;
+
+ void operator()(std::ostream& out) const {
+ path_output_.WriteFile(out, source_file_);
+ }
+
+ PathOutput& path_output_;
+ const SourceFile& source_file_;
+};
+
+const char kToolsetVersionVs2013[] = "v120"; // Visual Studio 2013
+const char kToolsetVersionVs2015[] = "v140"; // Visual Studio 2015
+const char kToolsetVersionVs2017[] = "v141"; // Visual Studio 2017
+const char kToolsetVersionVs2019[] = "v142"; // Visual Studio 2019
+const char kProjectVersionVs2013[] = "12.0"; // Visual Studio 2013
+const char kProjectVersionVs2015[] = "14.0"; // Visual Studio 2015
+const char kProjectVersionVs2017[] = "15.0"; // Visual Studio 2017
+const char kProjectVersionVs2019[] = "16.0"; // Visual Studio 2019
+const char kVersionStringVs2013[] = "Visual Studio 2013"; // Visual Studio 2013
+const char kVersionStringVs2015[] = "Visual Studio 2015"; // Visual Studio 2015
+const char kVersionStringVs2017[] = "Visual Studio 2017"; // Visual Studio 2017
+const char kVersionStringVs2019[] = "Visual Studio 2019"; // Visual Studio 2019
+const char kWindowsKitsVersion[] = "10"; // Windows 10 SDK
+const char kWindowsKitsDefaultVersion[] = "10"; // Windows 10 SDK
+
+const char kGuidTypeProject[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
+const char kGuidTypeFolder[] = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
+const char kGuidSeedProject[] = "project";
+const char kGuidSeedFolder[] = "folder";
+const char kGuidSeedFilter[] = "filter";
+
+const char kConfigurationName[] = "GN";
+
+const char kCharSetUnicode[] = "_UNICODE";
+const char kCharSetMultiByte[] = "_MBCS";
+
+std::string GetWindowsKitsIncludeDirs(const std::string& win_kit) {
+ std::string kits_path;
+
+#if defined(OS_WIN)
+ const char16_t* const subkeys[] = {
+ u"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
+ u"SOFTWARE\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots"};
+
+ std::u16string value_name =
+ base::ASCIIToUTF16("KitsRoot") + base::ASCIIToUTF16(kWindowsKitsVersion);
+
+ for (const char16_t* subkey : subkeys) {
+ base::win::RegKey key(HKEY_LOCAL_MACHINE, subkey, KEY_READ);
+ std::u16string value;
+ if (key.ReadValue(value_name.c_str(), &value) == ERROR_SUCCESS) {
+ kits_path = base::UTF16ToUTF8(value);
+ break;
+ }
+ }
+#endif // OS_WIN
+
+ if (kits_path.empty()) {
+ kits_path = std::string("C:\\Program Files (x86)\\Windows Kits\\") +
+ kWindowsKitsVersion + "\\";
+ }
+
+ const std::string kit_prefix = kits_path + "Include\\" + win_kit + "\\";
+ return kit_prefix + "shared;" + kit_prefix + "um;" + kit_prefix + "winrt;";
+}
+
+std::string GetConfigurationType(const Target* target, Err* err) {
+ switch (target->output_type()) {
+ case Target::EXECUTABLE:
+ return "Application";
+ case Target::SHARED_LIBRARY:
+ case Target::LOADABLE_MODULE:
+ return "DynamicLibrary";
+ case Target::STATIC_LIBRARY:
+ case Target::SOURCE_SET:
+ return "StaticLibrary";
+ case Target::GROUP:
+ return "Utility";
+
+ default:
+ *err = Err(Location(),
+ "Visual Studio doesn't support '" + target->label().name() +
+ "' target output type: " +
+ Target::GetStringForOutputType(target->output_type()));
+ return std::string();
+ }
+}
+
+void ParseCompilerOptions(const std::vector<std::string>& cflags,
+ CompilerOptions* options) {
+ for (const std::string& flag : cflags)
+ ParseCompilerOption(flag, options);
+}
+
+void ParseCompilerOptions(const Target* target, CompilerOptions* options) {
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ ParseCompilerOptions(iter.cur().cflags(), options);
+ ParseCompilerOptions(iter.cur().cflags_c(), options);
+ ParseCompilerOptions(iter.cur().cflags_cc(), options);
+ }
+}
+
+void ParseLinkerOptions(const std::vector<std::string>& ldflags,
+ LinkerOptions* options) {
+ for (const std::string& flag : ldflags)
+ ParseLinkerOption(flag, options);
+}
+
+void ParseLinkerOptions(const Target* target, LinkerOptions* options) {
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ ParseLinkerOptions(iter.cur().ldflags(), options);
+ }
+}
+
+// Returns a string piece pointing into the input string identifying the parent
+// directory path, excluding the last slash. Note that the input pointer must
+// outlive the output.
+std::string_view FindParentDir(const std::string* path) {
+ DCHECK(path && !path->empty());
+ for (int i = static_cast<int>(path->size()) - 2; i >= 0; --i) {
+ if (IsSlash((*path)[i]))
+ return std::string_view(path->data(), i);
+ }
+ return std::string_view();
+}
+
+bool FilterTargets(const BuildSettings* build_settings,
+ const Builder& builder,
+ const std::string& filters,
+ bool no_deps,
+ std::vector<const Target*>* targets,
+ Err* err) {
+ if (filters.empty()) {
+ *targets = builder.GetAllResolvedTargets();
+ return true;
+ }
+
+ std::vector<LabelPattern> patterns;
+ if (!commands::FilterPatternsFromString(build_settings, filters, &patterns,
+ err))
+ return false;
+
+ commands::FilterTargetsByPatterns(builder.GetAllResolvedTargets(), patterns,
+ targets);
+
+ if (no_deps)
+ return true;
+
+ std::set<Label> labels;
+ base::queue<const Target*> to_process;
+ for (const Target* target : *targets) {
+ labels.insert(target->label());
+ to_process.push(target);
+ }
+
+ while (!to_process.empty()) {
+ const Target* target = to_process.front();
+ to_process.pop();
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
+ if (labels.find(pair.label) == labels.end()) {
+ targets->push_back(pair.ptr);
+ to_process.push(pair.ptr);
+ labels.insert(pair.label);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool UnicodeTarget(const Target* target) {
+ for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
+ for (const std::string& define : it.cur().defines()) {
+ if (define == kCharSetUnicode)
+ return true;
+ if (define == kCharSetMultiByte)
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name,
+ const std::string& _path,
+ const std::string& _guid)
+ : name(_name), path(_path), guid(_guid), parent_folder(nullptr) {}
+
+VisualStudioWriter::SolutionEntry::~SolutionEntry() = default;
+
+VisualStudioWriter::SolutionProject::SolutionProject(
+ const std::string& _name,
+ const std::string& _path,
+ const std::string& _guid,
+ const std::string& _label_dir_path,
+ const std::string& _config_platform)
+ : SolutionEntry(_name, _path, _guid),
+ label_dir_path(_label_dir_path),
+ config_platform(_config_platform) {
+ // Make sure all paths use the same drive letter case. This is especially
+ // important when searching for the common path prefix.
+ label_dir_path[0] = base::ToUpperASCII(label_dir_path[0]);
+}
+
+VisualStudioWriter::SolutionProject::~SolutionProject() = default;
+
+VisualStudioWriter::SourceFileCompileTypePair::SourceFileCompileTypePair(
+ const SourceFile* _file,
+ const char* _compile_type)
+ : file(_file), compile_type(_compile_type) {}
+
+VisualStudioWriter::SourceFileCompileTypePair::~SourceFileCompileTypePair() =
+ default;
+
+VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings,
+ const char* config_platform,
+ Version version,
+ const std::string& win_kit)
+ : build_settings_(build_settings),
+ config_platform_(config_platform),
+ ninja_path_output_(build_settings->build_dir(),
+ build_settings->root_path_utf8(),
+ EscapingMode::ESCAPE_NINJA_COMMAND),
+ windows_sdk_version_(win_kit) {
+ DCHECK(!win_kit.empty());
+
+ switch (version) {
+ case Version::Vs2013:
+ project_version_ = kProjectVersionVs2013;
+ toolset_version_ = kToolsetVersionVs2013;
+ version_string_ = kVersionStringVs2013;
+ break;
+ case Version::Vs2015:
+ project_version_ = kProjectVersionVs2015;
+ toolset_version_ = kToolsetVersionVs2015;
+ version_string_ = kVersionStringVs2015;
+ break;
+ case Version::Vs2017:
+ project_version_ = kProjectVersionVs2017;
+ toolset_version_ = kToolsetVersionVs2017;
+ version_string_ = kVersionStringVs2017;
+ break;
+ case Version::Vs2019:
+ project_version_ = kProjectVersionVs2019;
+ toolset_version_ = kToolsetVersionVs2019;
+ version_string_ = kVersionStringVs2019;
+ break;
+ default:
+ NOTREACHED() << "Not a valid Visual Studio Version: " << version;
+ }
+
+ windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs(win_kit);
+}
+
+VisualStudioWriter::~VisualStudioWriter() = default;
+
+// static
+bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ Version version,
+ const std::string& sln_name,
+ const std::string& filters,
+ const std::string& win_sdk,
+ const std::string& ninja_extra_args,
+ bool no_deps,
+ Err* err) {
+ std::vector<const Target*> targets;
+ if (!FilterTargets(build_settings, builder, filters, no_deps, &targets, err))
+ return false;
+
+ std::string win_kit = kWindowsKitsDefaultVersion;
+ if (!win_sdk.empty())
+ win_kit = win_sdk;
+
+ const char* config_platform = "Win32";
+
+ // Assume the "target_cpu" variable does not change between different
+ // toolchains.
+ if (!targets.empty()) {
+ const Scope* scope = targets.front()->settings()->base_config();
+ const Value* target_cpu_value = scope->GetValue(variables::kTargetCpu);
+ if (target_cpu_value != nullptr &&
+ target_cpu_value->string_value() == "x64")
+ config_platform = "x64";
+ }
+
+ VisualStudioWriter writer(build_settings, config_platform, version, win_kit);
+ writer.projects_.reserve(targets.size());
+ writer.folders_.reserve(targets.size());
+
+ for (const Target* target : targets) {
+ // Skip actions and bundle targets.
+ if (target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH ||
+ target->output_type() == Target::BUNDLE_DATA ||
+ target->output_type() == Target::COPY_FILES ||
+ target->output_type() == Target::CREATE_BUNDLE ||
+ target->output_type() == Target::GENERATED_FILE) {
+ continue;
+ }
+
+ if (!writer.WriteProjectFiles(target, ninja_extra_args, err))
+ return false;
+ }
+
+ if (writer.projects_.empty()) {
+ *err = Err(Location(), "No Visual Studio projects generated.");
+ return false;
+ }
+
+ // Sort projects so they appear always in the same order in solution file.
+ // Otherwise solution file is rewritten and reloaded by Visual Studio.
+ std::sort(writer.projects_.begin(), writer.projects_.end(),
+ [](const std::unique_ptr<SolutionProject>& a,
+ const std::unique_ptr<SolutionProject>& b) {
+ return a->path < b->path;
+ });
+
+ writer.ResolveSolutionFolders();
+ return writer.WriteSolutionFile(sln_name, err);
+}
+
+bool VisualStudioWriter::WriteProjectFiles(const Target* target,
+ const std::string& ninja_extra_args,
+ Err* err) {
+ std::string project_name = target->label().name();
+ const char* project_config_platform = config_platform_;
+ if (!target->settings()->is_default()) {
+ project_name += "_" + target->toolchain()->label().name();
+ const Value* value =
+ target->settings()->base_config()->GetValue(variables::kCurrentCpu);
+ if (value != nullptr && value->string_value() == "x64")
+ project_config_platform = "x64";
+ else
+ project_config_platform = "Win32";
+ }
+
+ SourceFile target_file =
+ GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ)
+ .ResolveRelativeFile(Value(nullptr, project_name + ".vcxproj"), err);
+ if (target_file.is_null())
+ return false;
+
+ base::FilePath vcxproj_path = build_settings_->GetFullPath(target_file);
+ std::string vcxproj_path_str = FilePathToUTF8(vcxproj_path);
+
+ projects_.push_back(std::make_unique<SolutionProject>(
+ project_name, vcxproj_path_str,
+ MakeGuid(vcxproj_path_str, kGuidSeedProject),
+ FilePathToUTF8(build_settings_->GetFullPath(target->label().dir())),
+ project_config_platform));
+
+ std::stringstream vcxproj_string_out;
+ SourceFileCompileTypePairs source_types;
+ if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target,
+ ninja_extra_args, &source_types, err)) {
+ projects_.pop_back();
+ return false;
+ }
+
+ // Only write the content to the file if it's different. That is
+ // both a performance optimization and more importantly, prevents
+ // Visual Studio from reloading the projects.
+ if (!WriteFileIfChanged(vcxproj_path, vcxproj_string_out.str(), err))
+ return false;
+
+ base::FilePath filters_path = UTF8ToFilePath(vcxproj_path_str + ".filters");
+ std::stringstream filters_string_out;
+ WriteFiltersFileContents(filters_string_out, target, source_types);
+ return WriteFileIfChanged(filters_path, filters_string_out.str(), err);
+}
+
+bool VisualStudioWriter::WriteProjectFileContents(
+ std::ostream& out,
+ const SolutionProject& solution_project,
+ const Target* target,
+ const std::string& ninja_extra_args,
+ SourceFileCompileTypePairs* source_types,
+ Err* err) {
+ PathOutput path_output(
+ GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ),
+ build_settings_->root_path_utf8(), EscapingMode::ESCAPE_NONE);
+
+ out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
+ XmlElementWriter project(
+ out, "Project",
+ XmlAttributes("DefaultTargets", "Build")
+ .add("ToolsVersion", project_version_)
+ .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
+
+ {
+ std::unique_ptr<XmlElementWriter> configurations = project.SubElement(
+ "ItemGroup", XmlAttributes("Label", "ProjectConfigurations"));
+ std::unique_ptr<XmlElementWriter> project_config =
+ configurations->SubElement(
+ "ProjectConfiguration",
+ XmlAttributes("Include", std::string(kConfigurationName) + '|' +
+ solution_project.config_platform));
+ project_config->SubElement("Configuration")->Text(kConfigurationName);
+ project_config->SubElement("Platform")
+ ->Text(solution_project.config_platform);
+ }
+
+ {
+ std::unique_ptr<XmlElementWriter> globals =
+ project.SubElement("PropertyGroup", XmlAttributes("Label", "Globals"));
+ globals->SubElement("ProjectGuid")->Text(solution_project.guid);
+ globals->SubElement("Keyword")->Text("Win32Proj");
+ globals->SubElement("RootNamespace")->Text(target->label().name());
+ globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true");
+ globals->SubElement("PreferredToolArchitecture")->Text("x64");
+ globals->SubElement("WindowsTargetPlatformVersion")
+ ->Text(windows_sdk_version_);
+ }
+
+ project.SubElement(
+ "Import", XmlAttributes("Project",
+ "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"));
+
+ {
+ std::unique_ptr<XmlElementWriter> configuration = project.SubElement(
+ "PropertyGroup", XmlAttributes("Label", "Configuration"));
+ bool unicode_target = UnicodeTarget(target);
+ configuration->SubElement("CharacterSet")
+ ->Text(unicode_target ? "Unicode" : "MultiByte");
+ std::string configuration_type = GetConfigurationType(target, err);
+ if (configuration_type.empty())
+ return false;
+ configuration->SubElement("ConfigurationType")->Text(configuration_type);
+ }
+
+ {
+ std::unique_ptr<XmlElementWriter> locals =
+ project.SubElement("PropertyGroup", XmlAttributes("Label", "Locals"));
+ locals->SubElement("PlatformToolset")->Text(toolset_version_);
+ }
+
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"));
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project",
+ "$(VCTargetsPath)\\BuildCustomizations\\masm.props"));
+ project.SubElement("ImportGroup",
+ XmlAttributes("Label", "ExtensionSettings"));
+
+ {
+ std::unique_ptr<XmlElementWriter> property_sheets = project.SubElement(
+ "ImportGroup", XmlAttributes("Label", "PropertySheets"));
+ property_sheets->SubElement(
+ "Import",
+ XmlAttributes(
+ "Condition",
+ "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')")
+ .add("Label", "LocalAppDataPlatform")
+ .add("Project",
+ "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"));
+ }
+
+ project.SubElement("PropertyGroup", XmlAttributes("Label", "UserMacros"));
+
+ std::string ninja_target = GetNinjaTarget(target);
+
+ {
+ std::unique_ptr<XmlElementWriter> properties =
+ project.SubElement("PropertyGroup");
+ properties->SubElement("OutDir")->Text("$(SolutionDir)");
+ properties->SubElement("TargetName")->Text("$(ProjectName)");
+ if (target->output_type() != Target::GROUP) {
+ properties->SubElement("TargetPath")->Text("$(OutDir)\\" + ninja_target);
+ }
+ }
+
+ {
+ std::unique_ptr<XmlElementWriter> item_definitions =
+ project.SubElement("ItemDefinitionGroup");
+ {
+ std::unique_ptr<XmlElementWriter> cl_compile =
+ item_definitions->SubElement("ClCompile");
+ {
+ std::unique_ptr<XmlElementWriter> include_dirs =
+ cl_compile->SubElement("AdditionalIncludeDirectories");
+ RecursiveTargetConfigToStream<SourceDir>(
+ target, &ConfigValues::include_dirs, IncludeDirWriter(path_output),
+ include_dirs->StartContent(false));
+ include_dirs->Text(windows_kits_include_dirs_ +
+ "$(VSInstallDir)\\VC\\atlmfc\\include;" +
+ "%(AdditionalIncludeDirectories)");
+ }
+ CompilerOptions options;
+ ParseCompilerOptions(target, &options);
+ if (!options.additional_options.empty()) {
+ cl_compile->SubElement("AdditionalOptions")
+ ->Text(options.additional_options + "%(AdditionalOptions)");
+ }
+ if (!options.buffer_security_check.empty()) {
+ cl_compile->SubElement("BufferSecurityCheck")
+ ->Text(options.buffer_security_check);
+ }
+ cl_compile->SubElement("CompileAsWinRT")->Text("false");
+ cl_compile->SubElement("DebugInformationFormat")->Text("ProgramDatabase");
+ if (!options.disable_specific_warnings.empty()) {
+ cl_compile->SubElement("DisableSpecificWarnings")
+ ->Text(options.disable_specific_warnings +
+ "%(DisableSpecificWarnings)");
+ }
+ cl_compile->SubElement("ExceptionHandling")->Text("false");
+ if (!options.forced_include_files.empty()) {
+ cl_compile->SubElement("ForcedIncludeFiles")
+ ->Text(options.forced_include_files);
+ }
+ cl_compile->SubElement("MinimalRebuild")->Text("false");
+ if (!options.optimization.empty())
+ cl_compile->SubElement("Optimization")->Text(options.optimization);
+ cl_compile->SubElement("PrecompiledHeader")->Text("NotUsing");
+ {
+ std::unique_ptr<XmlElementWriter> preprocessor_definitions =
+ cl_compile->SubElement("PreprocessorDefinitions");
+ RecursiveTargetConfigToStream<std::string>(
+ target, &ConfigValues::defines, SemicolonSeparatedWriter(),
+ preprocessor_definitions->StartContent(false));
+ preprocessor_definitions->Text("%(PreprocessorDefinitions)");
+ }
+ if (!options.runtime_library.empty())
+ cl_compile->SubElement("RuntimeLibrary")->Text(options.runtime_library);
+ if (!options.treat_warning_as_error.empty()) {
+ cl_compile->SubElement("TreatWarningAsError")
+ ->Text(options.treat_warning_as_error);
+ }
+ if (!options.warning_level.empty())
+ cl_compile->SubElement("WarningLevel")->Text(options.warning_level);
+ }
+
+ std::unique_ptr<XmlElementWriter> link =
+ item_definitions->SubElement("Link");
+ {
+ LinkerOptions options;
+ ParseLinkerOptions(target, &options);
+ if (!options.subsystem.empty())
+ link->SubElement("SubSystem")->Text(options.subsystem);
+ }
+
+ // We don't include resource compilation and other link options as ninja
+ // files are used to generate real build.
+ }
+
+ {
+ std::unique_ptr<XmlElementWriter> group = project.SubElement("ItemGroup");
+ std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
+
+ for (const SourceFile& file : target->sources()) {
+ const char* compile_type;
+ const char* tool_name = Tool::kToolNone;
+ if (target->GetOutputFilesForSource(file, &tool_name, &tool_outputs)) {
+ compile_type = "CustomBuild";
+ std::unique_ptr<XmlElementWriter> build = group->SubElement(
+ compile_type, "Include", SourceFileWriter(path_output, file));
+ build->SubElement("Command")->Text("call ninja.exe -C $(OutDir) " +
+ ninja_extra_args + " " +
+ tool_outputs[0].value());
+ build->SubElement("Outputs")->Text("$(OutDir)" +
+ tool_outputs[0].value());
+ } else {
+ compile_type = "None";
+ group->SubElement(compile_type, "Include",
+ SourceFileWriter(path_output, file));
+ }
+ source_types->push_back(SourceFileCompileTypePair(&file, compile_type));
+ }
+ }
+
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"));
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project",
+ "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"));
+ project.SubElement("ImportGroup", XmlAttributes("Label", "ExtensionTargets"));
+
+ {
+ std::unique_ptr<XmlElementWriter> build =
+ project.SubElement("Target", XmlAttributes("Name", "Build"));
+ build->SubElement(
+ "Exec",
+ XmlAttributes("Command", "call ninja.exe -C $(OutDir) " +
+ ninja_extra_args + " " + ninja_target));
+ }
+
+ {
+ std::unique_ptr<XmlElementWriter> clean =
+ project.SubElement("Target", XmlAttributes("Name", "Clean"));
+ clean->SubElement(
+ "Exec",
+ XmlAttributes("Command",
+ "call ninja.exe -C $(OutDir) -tclean " + ninja_target));
+ }
+
+ return true;
+}
+
+void VisualStudioWriter::WriteFiltersFileContents(
+ std::ostream& out,
+ const Target* target,
+ const SourceFileCompileTypePairs& source_types) {
+ out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
+ XmlElementWriter project(
+ out, "Project",
+ XmlAttributes("ToolsVersion", "4.0")
+ .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
+
+ std::ostringstream files_out;
+
+ {
+ std::unique_ptr<XmlElementWriter> filters_group =
+ project.SubElement("ItemGroup");
+ XmlElementWriter files_group(files_out, "ItemGroup", XmlAttributes(), 2);
+
+ // File paths are relative to vcxproj files which are generated to out dirs.
+ // Filters tree structure need to reflect source directories and be relative
+ // to target file. We need two path outputs then.
+ PathOutput file_path_output(
+ GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ),
+ build_settings_->root_path_utf8(), EscapingMode::ESCAPE_NONE);
+ PathOutput filter_path_output(target->label().dir(),
+ build_settings_->root_path_utf8(),
+ EscapingMode::ESCAPE_NONE);
+
+ std::set<std::string> processed_filters;
+
+ for (const auto& file_and_type : source_types) {
+ std::unique_ptr<XmlElementWriter> cl_item = files_group.SubElement(
+ file_and_type.compile_type, "Include",
+ SourceFileWriter(file_path_output, *file_and_type.file));
+
+ std::ostringstream target_relative_out;
+ filter_path_output.WriteFile(target_relative_out, *file_and_type.file);
+ std::string target_relative_path = target_relative_out.str();
+ ConvertPathToSystem(&target_relative_path);
+ std::string_view filter_path = FindParentDir(&target_relative_path);
+
+ if (!filter_path.empty()) {
+ std::string filter_path_str(filter_path);
+ while (processed_filters.find(filter_path_str) ==
+ processed_filters.end()) {
+ auto it = processed_filters.insert(filter_path_str).first;
+ filters_group
+ ->SubElement("Filter", XmlAttributes("Include", filter_path_str))
+ ->SubElement("UniqueIdentifier")
+ ->Text(MakeGuid(filter_path_str, kGuidSeedFilter));
+ filter_path_str = std::string(FindParentDir(&(*it)));
+ if (filter_path_str.empty())
+ break;
+ }
+ cl_item->SubElement("Filter")->Text(filter_path);
+ }
+ }
+ }
+
+ project.Text(files_out.str());
+}
+
+bool VisualStudioWriter::WriteSolutionFile(const std::string& sln_name,
+ Err* err) {
+ std::string name = sln_name.empty() ? "all" : sln_name;
+ SourceFile sln_file = build_settings_->build_dir().ResolveRelativeFile(
+ Value(nullptr, name + ".sln"), err);
+ if (sln_file.is_null())
+ return false;
+
+ base::FilePath sln_path = build_settings_->GetFullPath(sln_file);
+
+ std::stringstream string_out;
+ WriteSolutionFileContents(string_out, sln_path.DirName());
+
+ // Only write the content to the file if it's different. That is
+ // both a performance optimization and more importantly, prevents
+ // Visual Studio from reloading the projects.
+ return WriteFileIfChanged(sln_path, string_out.str(), err);
+}
+
+void VisualStudioWriter::WriteSolutionFileContents(
+ std::ostream& out,
+ const base::FilePath& solution_dir_path) {
+ out << "Microsoft Visual Studio Solution File, Format Version 12.00"
+ << std::endl;
+ out << "# " << version_string_ << std::endl;
+
+ SourceDir solution_dir(FilePathToUTF8(solution_dir_path));
+ for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
+ out << "Project(\"" << kGuidTypeFolder << "\") = \"(" << folder->name
+ << ")\", \"" << RebasePath(folder->path, solution_dir) << "\", \""
+ << folder->guid << "\"" << std::endl;
+ out << "EndProject" << std::endl;
+ }
+
+ for (const std::unique_ptr<SolutionProject>& project : projects_) {
+ out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name
+ << "\", \"" << RebasePath(project->path, solution_dir) << "\", \""
+ << project->guid << "\"" << std::endl;
+ out << "EndProject" << std::endl;
+ }
+
+ out << "Global" << std::endl;
+
+ out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"
+ << std::endl;
+ const std::string config_mode_prefix = std::string(kConfigurationName) + '|';
+ const std::string config_mode = config_mode_prefix + config_platform_;
+ out << "\t\t" << config_mode << " = " << config_mode << std::endl;
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"
+ << std::endl;
+ for (const std::unique_ptr<SolutionProject>& project : projects_) {
+ const std::string project_config_mode =
+ config_mode_prefix + project->config_platform;
+ out << "\t\t" << project->guid << '.' << config_mode
+ << ".ActiveCfg = " << project_config_mode << std::endl;
+ out << "\t\t" << project->guid << '.' << config_mode
+ << ".Build.0 = " << project_config_mode << std::endl;
+ }
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl;
+ out << "\t\tHideSolutionNode = FALSE" << std::endl;
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl;
+ for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
+ if (folder->parent_folder) {
+ out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid
+ << std::endl;
+ }
+ }
+ for (const std::unique_ptr<SolutionProject>& project : projects_) {
+ out << "\t\t" << project->guid << " = " << project->parent_folder->guid
+ << std::endl;
+ }
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "EndGlobal" << std::endl;
+}
+
+void VisualStudioWriter::ResolveSolutionFolders() {
+ root_folder_path_.clear();
+
+ // Get all project directories. Create solution folder for each directory.
+ std::map<std::string_view, SolutionEntry*> processed_paths;
+ for (const std::unique_ptr<SolutionProject>& project : projects_) {
+ std::string_view folder_path = project->label_dir_path;
+ if (IsSlash(folder_path[folder_path.size() - 1]))
+ folder_path = folder_path.substr(0, folder_path.size() - 1);
+ auto it = processed_paths.find(folder_path);
+ if (it != processed_paths.end()) {
+ project->parent_folder = it->second;
+ } else {
+ std::string folder_path_str(folder_path);
+ std::unique_ptr<SolutionEntry> folder = std::make_unique<SolutionEntry>(
+ std::string(
+ FindLastDirComponent(SourceDir(std::string(folder_path)))),
+ folder_path_str, MakeGuid(folder_path_str, kGuidSeedFolder));
+ project->parent_folder = folder.get();
+ processed_paths[folder_path] = folder.get();
+ folders_.push_back(std::move(folder));
+
+ if (root_folder_path_.empty()) {
+ root_folder_path_ = folder_path_str;
+ } else {
+ size_t common_prefix_len = 0;
+ size_t max_common_length =
+ std::min(root_folder_path_.size(), folder_path.size());
+ size_t i;
+ for (i = common_prefix_len; i < max_common_length; ++i) {
+ if (IsSlash(root_folder_path_[i]) && IsSlash(folder_path[i]))
+ common_prefix_len = i + 1;
+ else if (root_folder_path_[i] != folder_path[i])
+ break;
+ }
+ if (i == max_common_length &&
+ (i == folder_path.size() || IsSlash(folder_path[i])))
+ common_prefix_len = max_common_length;
+ if (common_prefix_len < root_folder_path_.size()) {
+ if (IsSlash(root_folder_path_[common_prefix_len - 1]))
+ --common_prefix_len;
+ root_folder_path_ = root_folder_path_.substr(0, common_prefix_len);
+ }
+ }
+ }
+ }
+
+ // Create also all parent folders up to |root_folder_path_|.
+ SolutionFolders additional_folders;
+ for (const std::unique_ptr<SolutionEntry>& solution_folder : folders_) {
+ if (solution_folder->path == root_folder_path_)
+ continue;
+
+ SolutionEntry* folder = solution_folder.get();
+ std::string_view parent_path;
+ while ((parent_path = FindParentDir(&folder->path)) != root_folder_path_) {
+ auto it = processed_paths.find(parent_path);
+ if (it != processed_paths.end()) {
+ folder = it->second;
+ } else {
+ std::unique_ptr<SolutionEntry> new_folder =
+ std::make_unique<SolutionEntry>(
+ std::string(
+ FindLastDirComponent(SourceDir(std::string(parent_path)))),
+ std::string(parent_path),
+ MakeGuid(std::string(parent_path), kGuidSeedFolder));
+ processed_paths[parent_path] = new_folder.get();
+ folder = new_folder.get();
+ additional_folders.push_back(std::move(new_folder));
+ }
+ }
+ }
+ folders_.insert(folders_.end(),
+ std::make_move_iterator(additional_folders.begin()),
+ std::make_move_iterator(additional_folders.end()));
+
+ // Sort folders by path.
+ std::sort(folders_.begin(), folders_.end(),
+ [](const std::unique_ptr<SolutionEntry>& a,
+ const std::unique_ptr<SolutionEntry>& b) {
+ return a->path < b->path;
+ });
+
+ // Match subfolders with their parents. Since |folders_| are sorted by path we
+ // know that parent folder always precedes its children in vector.
+ std::vector<SolutionEntry*> parents;
+ for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
+ while (!parents.empty()) {
+ if (base::StartsWith(folder->path, parents.back()->path,
+ base::CompareCase::SENSITIVE)) {
+ folder->parent_folder = parents.back();
+ break;
+ } else {
+ parents.pop_back();
+ }
+ }
+ parents.push_back(folder.get());
+ }
+}
+
+std::string VisualStudioWriter::GetNinjaTarget(const Target* target) {
+ std::ostringstream ninja_target_out;
+ DCHECK(!target->dependency_output_file().value().empty());
+ ninja_path_output_.WriteFile(ninja_target_out,
+ target->dependency_output_file());
+ std::string s = ninja_target_out.str();
+ if (s.compare(0, 2, "./") == 0)
+ s = s.substr(2);
+ return s;
+}
diff --git a/gn/src/gn/visual_studio_writer.h b/gn/src/gn/visual_studio_writer.h
new file mode 100644
index 00000000000..e4957a1ff47
--- /dev/null
+++ b/gn/src/gn/visual_studio_writer.h
@@ -0,0 +1,168 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_VISUAL_STUDIO_WRITER_H_
+#define TOOLS_GN_VISUAL_STUDIO_WRITER_H_
+
+#include <iosfwd>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "gn/path_output.h"
+
+namespace base {
+class FilePath;
+}
+
+class Builder;
+class BuildSettings;
+class Err;
+class SourceFile;
+class Target;
+
+class VisualStudioWriter {
+ public:
+ enum Version {
+ Vs2013 = 1, // Visual Studio 2013
+ Vs2015, // Visual Studio 2015
+ Vs2017, // Visual Studio 2017
+ Vs2019, // Visual Studio 2019
+ };
+
+ // Writes Visual Studio project and solution files. |sln_name| is the optional
+ // solution file name ("all" is used if not specified). |filters| is optional
+ // semicolon-separated list of label patterns used to limit the set of
+ // generated projects. Only matching targets and their dependencies (unless
+ // |no_deps| is true) will be included to the solution. On failure will
+ // populate |err| and will return false. |win_sdk| is the Windows SDK version
+ // which will be used by Visual Studio IntelliSense.
+ static bool RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ Version version,
+ const std::string& sln_name,
+ const std::string& filters,
+ const std::string& win_sdk,
+ const std::string& ninja_extra_args,
+ bool no_deps,
+ Err* err);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(VisualStudioWriterTest, ResolveSolutionFolders);
+ FRIEND_TEST_ALL_PREFIXES(VisualStudioWriterTest,
+ ResolveSolutionFolders_AbsPath);
+ FRIEND_TEST_ALL_PREFIXES(VisualStudioWriterTest, NoDotSlash);
+
+ // Solution project or folder.
+ struct SolutionEntry {
+ SolutionEntry(const std::string& name,
+ const std::string& path,
+ const std::string& guid);
+ virtual ~SolutionEntry();
+
+ // Entry name. For projects must be unique in the solution.
+ std::string name;
+ // Absolute project file or folder directory path.
+ std::string path;
+ // GUID-like string.
+ std::string guid;
+ // Pointer to parent folder. nullptr if entry has no parent.
+ SolutionEntry* parent_folder;
+ };
+
+ struct SolutionProject : public SolutionEntry {
+ SolutionProject(const std::string& name,
+ const std::string& path,
+ const std::string& guid,
+ const std::string& label_dir_path,
+ const std::string& config_platform);
+ ~SolutionProject() override;
+
+ // Absolute label dir path.
+ std::string label_dir_path;
+ // Configuration platform. May be different than solution config platform.
+ std::string config_platform;
+ };
+
+ struct SourceFileCompileTypePair {
+ SourceFileCompileTypePair(const SourceFile* file, const char* compile_type);
+ ~SourceFileCompileTypePair();
+
+ // Source file.
+ const SourceFile* file;
+ // Compile type string.
+ const char* compile_type;
+ };
+
+ using SolutionProjects = std::vector<std::unique_ptr<SolutionProject>>;
+ using SolutionFolders = std::vector<std::unique_ptr<SolutionEntry>>;
+ using SourceFileCompileTypePairs = std::vector<SourceFileCompileTypePair>;
+
+ VisualStudioWriter(const BuildSettings* build_settings,
+ const char* config_platform,
+ Version version,
+ const std::string& win_kit);
+ ~VisualStudioWriter();
+
+ bool WriteProjectFiles(const Target* target,
+ const std::string& ninja_extra_args,
+ Err* err);
+ bool WriteProjectFileContents(std::ostream& out,
+ const SolutionProject& solution_project,
+ const Target* target,
+ const std::string& ninja_extra_args,
+ SourceFileCompileTypePairs* source_types,
+ Err* err);
+ void WriteFiltersFileContents(std::ostream& out,
+ const Target* target,
+ const SourceFileCompileTypePairs& source_types);
+ bool WriteSolutionFile(const std::string& sln_name, Err* err);
+ void WriteSolutionFileContents(std::ostream& out,
+ const base::FilePath& solution_dir_path);
+
+ // Resolves all solution folders (parent folders for projects) into |folders_|
+ // and updates |root_folder_dir_|. Also sets |parent_folder| for |projects_|.
+ void ResolveSolutionFolders();
+
+ std::string GetNinjaTarget(const Target* target);
+
+ const BuildSettings* build_settings_;
+
+ // Toolset version.
+ const char* toolset_version_;
+
+ // Project version.
+ const char* project_version_;
+
+ // Visual Studio version string.
+ const char* version_string_;
+
+ // Platform for solution configuration (Win32, x64). Some projects may be
+ // configured for different platform.
+ const char* config_platform_;
+
+ // All projects contained by solution.
+ SolutionProjects projects_;
+
+ // Absolute root solution folder path.
+ std::string root_folder_path_;
+
+ // Folders for all solution projects.
+ SolutionFolders folders_;
+
+ // Semicolon-separated Windows SDK include directories.
+ std::string windows_kits_include_dirs_;
+
+ // Path formatter for ninja targets.
+ PathOutput ninja_path_output_;
+
+ // Windows 10 SDK version string (e.g. 10.0.14393.0)
+ std::string windows_sdk_version_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisualStudioWriter);
+};
+
+#endif // TOOLS_GN_VISUAL_STUDIO_WRITER_H_
diff --git a/gn/src/gn/visual_studio_writer_unittest.cc b/gn/src/gn/visual_studio_writer_unittest.cc
new file mode 100644
index 00000000000..5e89fe11829
--- /dev/null
+++ b/gn/src/gn/visual_studio_writer_unittest.cc
@@ -0,0 +1,200 @@
+// Copyright 2016 The Chromium 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 "gn/visual_studio_writer.h"
+
+#include <memory>
+
+#include "base/strings/string_util.h"
+#include "gn/test_with_scope.h"
+#include "gn/visual_studio_utils.h"
+#include "util/test/test.h"
+
+namespace {
+
+class VisualStudioWriterTest : public testing::Test {
+ protected:
+ TestWithScope setup_;
+};
+
+std::string MakeTestPath(const std::string& path) {
+#if defined(OS_WIN)
+ return "C:" + path;
+#else
+ return path;
+#endif
+}
+
+} // namespace
+
+TEST_F(VisualStudioWriterTest, ResolveSolutionFolders) {
+ VisualStudioWriter writer(setup_.build_settings(), "Win32",
+ VisualStudioWriter::Version::Vs2015,
+ "10.0.17134.0");
+
+ std::string path =
+ MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "base", path, MakeGuid(path, "project"),
+ MakeTestPath("/foo/chromium/src/base"), "Win32"));
+
+ path = MakeTestPath("/foo/chromium/src/out/Debug/obj/tools/gn/gn.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "gn", path, MakeGuid(path, "project"),
+ MakeTestPath("/foo/chromium/src/tools/gn"), "Win32"));
+
+ path = MakeTestPath("/foo/chromium/src/out/Debug/obj/chrome/chrome.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "chrome", path, MakeGuid(path, "project"),
+ MakeTestPath("/foo/chromium/src/chrome"), "Win32"));
+
+ path = MakeTestPath("/foo/chromium/src/out/Debug/obj/base/bar.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "bar", path, MakeGuid(path, "project"),
+ MakeTestPath("/foo/chromium/src/base"), "Win32"));
+
+ writer.ResolveSolutionFolders();
+
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src"), writer.root_folder_path_);
+
+ ASSERT_EQ(4u, writer.folders_.size());
+
+ ASSERT_EQ("base", writer.folders_[0]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/base"), writer.folders_[0]->path);
+ ASSERT_EQ(nullptr, writer.folders_[0]->parent_folder);
+
+ ASSERT_EQ("chrome", writer.folders_[1]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/chrome"), writer.folders_[1]->path);
+ ASSERT_EQ(nullptr, writer.folders_[1]->parent_folder);
+
+ ASSERT_EQ("tools", writer.folders_[2]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools"), writer.folders_[2]->path);
+ ASSERT_EQ(nullptr, writer.folders_[2]->parent_folder);
+
+ ASSERT_EQ("gn", writer.folders_[3]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools/gn"),
+ writer.folders_[3]->path);
+ ASSERT_EQ(writer.folders_[2].get(), writer.folders_[3]->parent_folder);
+
+ ASSERT_EQ(writer.folders_[0].get(), writer.projects_[0]->parent_folder);
+ ASSERT_EQ(writer.folders_[3].get(), writer.projects_[1]->parent_folder);
+ ASSERT_EQ(writer.folders_[1].get(), writer.projects_[2]->parent_folder);
+ ASSERT_EQ(writer.folders_[0].get(), writer.projects_[3]->parent_folder);
+}
+
+TEST_F(VisualStudioWriterTest, ResolveSolutionFolders_AbsPath) {
+ VisualStudioWriter writer(setup_.build_settings(), "Win32",
+ VisualStudioWriter::Version::Vs2015,
+ "10.0.17134.0");
+
+ std::string path =
+ MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "base", path, MakeGuid(path, "project"),
+ MakeTestPath("/foo/chromium/src/base"), "Win32"));
+
+ path = MakeTestPath("/foo/chromium/src/out/Debug/obj/tools/gn/gn.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "gn", path, MakeGuid(path, "project"),
+ MakeTestPath("/foo/chromium/src/tools/gn"), "Win32"));
+
+ path = MakeTestPath(
+ "/foo/chromium/src/out/Debug/obj/ABS_PATH/C/foo/bar/bar.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "bar", path, MakeGuid(path, "project"), MakeTestPath("/foo/bar"),
+ "Win32"));
+
+ std::string baz_label_dir_path = MakeTestPath("/foo/bar/baz");
+#if defined(OS_WIN)
+ // Make sure mixed lower and upper-case drive letters are handled properly.
+ baz_label_dir_path[0] = base::ToLowerASCII(baz_label_dir_path[0]);
+#endif
+ path = MakeTestPath(
+ "/foo/chromium/src/out/Debug/obj/ABS_PATH/C/foo/bar/baz/baz.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "baz", path, MakeGuid(path, "project"), baz_label_dir_path, "Win32"));
+
+ writer.ResolveSolutionFolders();
+
+ ASSERT_EQ(MakeTestPath("/foo"), writer.root_folder_path_);
+
+ ASSERT_EQ(7u, writer.folders_.size());
+
+ ASSERT_EQ("bar", writer.folders_[0]->name);
+ ASSERT_EQ(MakeTestPath("/foo/bar"), writer.folders_[0]->path);
+ ASSERT_EQ(nullptr, writer.folders_[0]->parent_folder);
+
+ ASSERT_EQ("baz", writer.folders_[1]->name);
+ ASSERT_EQ(MakeTestPath("/foo/bar/baz"), writer.folders_[1]->path);
+ ASSERT_EQ(writer.folders_[0].get(), writer.folders_[1]->parent_folder);
+
+ ASSERT_EQ("chromium", writer.folders_[2]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium"), writer.folders_[2]->path);
+ ASSERT_EQ(nullptr, writer.folders_[2]->parent_folder);
+
+ ASSERT_EQ("src", writer.folders_[3]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src"), writer.folders_[3]->path);
+ ASSERT_EQ(writer.folders_[2].get(), writer.folders_[3]->parent_folder);
+
+ ASSERT_EQ("base", writer.folders_[4]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/base"), writer.folders_[4]->path);
+ ASSERT_EQ(writer.folders_[3].get(), writer.folders_[4]->parent_folder);
+
+ ASSERT_EQ("tools", writer.folders_[5]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools"), writer.folders_[5]->path);
+ ASSERT_EQ(writer.folders_[3].get(), writer.folders_[5]->parent_folder);
+
+ ASSERT_EQ("gn", writer.folders_[6]->name);
+ ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools/gn"),
+ writer.folders_[6]->path);
+ ASSERT_EQ(writer.folders_[5].get(), writer.folders_[6]->parent_folder);
+
+ ASSERT_EQ(writer.folders_[4].get(), writer.projects_[0]->parent_folder);
+ ASSERT_EQ(writer.folders_[6].get(), writer.projects_[1]->parent_folder);
+ ASSERT_EQ(writer.folders_[0].get(), writer.projects_[2]->parent_folder);
+ ASSERT_EQ(writer.folders_[1].get(), writer.projects_[3]->parent_folder);
+}
+
+TEST_F(VisualStudioWriterTest, NoDotSlash) {
+ VisualStudioWriter writer(setup_.build_settings(), "Win32",
+ VisualStudioWriter::Version::Vs2015,
+ "10.0.17134.0");
+
+ std::string path = MakeTestPath("blah.vcxproj");
+ writer.projects_.push_back(
+ std::make_unique<VisualStudioWriter::SolutionProject>(
+ "base", path, MakeGuid(path, "project"), MakeTestPath("/foo"),
+ "Win32"));
+
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolAlink);
+ tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}", ""));
+
+ Toolchain toolchain(setup_.settings(), Label(SourceDir("//tc/"), "tc"));
+ toolchain.SetTool(std::move(tool));
+
+ Target target(setup_.settings(), Label(SourceDir("//baz/"), "baz"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.SetToolchain(&toolchain);
+
+ Err err;
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ VisualStudioWriter::SourceFileCompileTypePairs source_types;
+
+ std::stringstream file_contents;
+ writer.WriteProjectFileContents(file_contents, *writer.projects_.back(),
+ &target, "", &source_types, &err);
+
+ // Should find args of a ninja clean command, with no ./ before the file name.
+ ASSERT_NE(file_contents.str().find("-tclean baz"), std::string::npos);
+}
diff --git a/gn/src/gn/xcode_object.cc b/gn/src/gn/xcode_object.cc
new file mode 100644
index 00000000000..0761b4f346c
--- /dev/null
+++ b/gn/src/gn/xcode_object.cc
@@ -0,0 +1,1118 @@
+// Copyright 2016 The Chromium 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 "gn/xcode_object.h"
+
+#include <iomanip>
+#include <iterator>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "gn/filesystem_utils.h"
+
+// Helper methods -------------------------------------------------------------
+
+namespace {
+struct IndentRules {
+ bool one_line;
+ unsigned level;
+};
+
+std::vector<std::unique_ptr<PBXObject>> EmptyPBXObjectVector() {
+ return std::vector<std::unique_ptr<PBXObject>>();
+}
+
+bool CharNeedEscaping(char c) {
+ if (base::IsAsciiAlpha(c) || base::IsAsciiDigit(c))
+ return false;
+ if (c == '$' || c == '.' || c == '/' || c == '_')
+ return false;
+ return true;
+}
+
+bool StringNeedEscaping(const std::string& string) {
+ if (string.empty())
+ return true;
+ if (string.find("___") != std::string::npos)
+ return true;
+
+ for (char c : string) {
+ if (CharNeedEscaping(c))
+ return true;
+ }
+ return false;
+}
+
+std::string EncodeString(const std::string& string) {
+ if (!StringNeedEscaping(string))
+ return string;
+
+ std::stringstream buffer;
+ buffer << '"';
+ for (char c : string) {
+ if (c <= 31) {
+ switch (c) {
+ case '\a':
+ buffer << "\\a";
+ break;
+ case '\b':
+ buffer << "\\b";
+ break;
+ case '\t':
+ buffer << "\\t";
+ break;
+ case '\n':
+ case '\r':
+ buffer << "\\n";
+ break;
+ case '\v':
+ buffer << "\\v";
+ break;
+ case '\f':
+ buffer << "\\f";
+ break;
+ default:
+ buffer << std::hex << std::setw(4) << std::left << "\\U"
+ << static_cast<unsigned>(c);
+ break;
+ }
+ } else {
+ if (c == '"' || c == '\\')
+ buffer << '\\';
+ buffer << c;
+ }
+ }
+ buffer << '"';
+ return buffer.str();
+}
+
+struct SourceTypeForExt {
+ const char* ext;
+ const char* source_type;
+};
+
+const SourceTypeForExt kSourceTypeForExt[] = {
+ {"a", "archive.ar"},
+ {"app", "wrapper.application"},
+ {"appex", "wrapper.app-extension"},
+ {"bdic", "file"},
+ {"bundle", "wrapper.cfbundle"},
+ {"c", "sourcecode.c.c"},
+ {"cc", "sourcecode.cpp.cpp"},
+ {"cpp", "sourcecode.cpp.cpp"},
+ {"css", "text.css"},
+ {"cxx", "sourcecode.cpp.cpp"},
+ {"dart", "sourcecode"},
+ {"dylib", "compiled.mach-o.dylib"},
+ {"framework", "wrapper.framework"},
+ {"h", "sourcecode.c.h"},
+ {"hxx", "sourcecode.cpp.h"},
+ {"icns", "image.icns"},
+ {"java", "sourcecode.java"},
+ {"js", "sourcecode.javascript"},
+ {"kext", "wrapper.kext"},
+ {"m", "sourcecode.c.objc"},
+ {"mm", "sourcecode.cpp.objcpp"},
+ {"nib", "wrapper.nib"},
+ {"o", "compiled.mach-o.objfile"},
+ {"pdf", "image.pdf"},
+ {"pl", "text.script.perl"},
+ {"plist", "text.plist.xml"},
+ {"pm", "text.script.perl"},
+ {"png", "image.png"},
+ {"py", "text.script.python"},
+ {"r", "sourcecode.rez"},
+ {"rez", "sourcecode.rez"},
+ {"s", "sourcecode.asm"},
+ {"storyboard", "file.storyboard"},
+ {"strings", "text.plist.strings"},
+ {"swift", "sourcecode.swift"},
+ {"ttf", "file"},
+ {"xcassets", "folder.assetcatalog"},
+ {"xcconfig", "text.xcconfig"},
+ {"xcdatamodel", "wrapper.xcdatamodel"},
+ {"xcdatamodeld", "wrapper.xcdatamodeld"},
+ {"xctest", "wrapper.cfbundle"},
+ {"xpc", "wrapper.xpc-service"},
+ {"xib", "file.xib"},
+ {"y", "sourcecode.yacc"},
+};
+
+const char* GetSourceType(const std::string_view& ext) {
+ for (size_t i = 0; i < std::size(kSourceTypeForExt); ++i) {
+ if (kSourceTypeForExt[i].ext == ext)
+ return kSourceTypeForExt[i].source_type;
+ }
+
+ return "text";
+}
+
+bool HasExplicitFileType(const std::string_view& ext) {
+ return ext == "dart";
+}
+
+bool IsSourceFileForIndexing(const std::string_view& ext) {
+ return ext == "c" || ext == "cc" || ext == "cpp" || ext == "cxx" ||
+ ext == "m" || ext == "mm";
+}
+
+// Wrapper around a const PBXObject* allowing to print just the object
+// identifier instead of a reference (i.e. identitifer and name). This
+// is used in a few place where Xcode uses the short identifier only.
+struct NoReference {
+ const PBXObject* value;
+
+ explicit NoReference(const PBXObject* value) : value(value) {}
+};
+
+void PrintValue(std::ostream& out, IndentRules rules, unsigned value) {
+ out << value;
+}
+
+void PrintValue(std::ostream& out, IndentRules rules, const char* value) {
+ out << EncodeString(value);
+}
+
+void PrintValue(std::ostream& out,
+ IndentRules rules,
+ const std::string& value) {
+ out << EncodeString(value);
+}
+
+void PrintValue(std::ostream& out, IndentRules rules, const NoReference& obj) {
+ out << obj.value->id();
+}
+
+void PrintValue(std::ostream& out, IndentRules rules, const PBXObject* value) {
+ out << value->Reference();
+}
+
+template <typename ObjectClass>
+void PrintValue(std::ostream& out,
+ IndentRules rules,
+ const std::unique_ptr<ObjectClass>& value) {
+ PrintValue(out, rules, value.get());
+}
+
+template <typename ValueType>
+void PrintValue(std::ostream& out,
+ IndentRules rules,
+ const std::vector<ValueType>& values) {
+ IndentRules sub_rule{rules.one_line, rules.level + 1};
+ out << "(" << (rules.one_line ? " " : "\n");
+ for (const auto& value : values) {
+ if (!sub_rule.one_line)
+ out << std::string(sub_rule.level, '\t');
+
+ PrintValue(out, sub_rule, value);
+ out << "," << (rules.one_line ? " " : "\n");
+ }
+
+ if (!rules.one_line && rules.level)
+ out << std::string(rules.level, '\t');
+ out << ")";
+}
+
+template <typename ValueType>
+void PrintValue(std::ostream& out,
+ IndentRules rules,
+ const std::map<std::string, ValueType>& values) {
+ IndentRules sub_rule{rules.one_line, rules.level + 1};
+ out << "{" << (rules.one_line ? " " : "\n");
+ for (const auto& pair : values) {
+ if (!sub_rule.one_line)
+ out << std::string(sub_rule.level, '\t');
+
+ out << pair.first << " = ";
+ PrintValue(out, sub_rule, pair.second);
+ out << ";" << (rules.one_line ? " " : "\n");
+ }
+
+ if (!rules.one_line && rules.level)
+ out << std::string(rules.level, '\t');
+ out << "}";
+}
+
+template <typename ValueType>
+void PrintProperty(std::ostream& out,
+ IndentRules rules,
+ const char* name,
+ ValueType&& value) {
+ if (!rules.one_line && rules.level)
+ out << std::string(rules.level, '\t');
+
+ out << name << " = ";
+ PrintValue(out, rules, std::forward<ValueType>(value));
+ out << ";" << (rules.one_line ? " " : "\n");
+}
+
+struct PBXGroupComparator {
+ using PBXObjectPtr = std::unique_ptr<PBXObject>;
+ bool operator()(const PBXObjectPtr& lhs, const PBXObjectPtr& rhs) {
+ if (lhs->Class() != rhs->Class())
+ return rhs->Class() < lhs->Class();
+
+ if (lhs->Class() == PBXGroupClass) {
+ PBXGroup* lhs_group = static_cast<PBXGroup*>(lhs.get());
+ PBXGroup* rhs_group = static_cast<PBXGroup*>(rhs.get());
+ return lhs_group->name() < rhs_group->name();
+ }
+
+ DCHECK_EQ(lhs->Class(), PBXFileReferenceClass);
+ PBXFileReference* lhs_file = static_cast<PBXFileReference*>(lhs.get());
+ PBXFileReference* rhs_file = static_cast<PBXFileReference*>(rhs.get());
+ return lhs_file->Name() < rhs_file->Name();
+ }
+};
+} // namespace
+
+// PBXObjectClass -------------------------------------------------------------
+
+const char* ToString(PBXObjectClass cls) {
+ switch (cls) {
+ case PBXAggregateTargetClass:
+ return "PBXAggregateTarget";
+ case PBXBuildFileClass:
+ return "PBXBuildFile";
+ case PBXContainerItemProxyClass:
+ return "PBXContainerItemProxy";
+ case PBXFileReferenceClass:
+ return "PBXFileReference";
+ case PBXFrameworksBuildPhaseClass:
+ return "PBXFrameworksBuildPhase";
+ case PBXGroupClass:
+ return "PBXGroup";
+ case PBXNativeTargetClass:
+ return "PBXNativeTarget";
+ case PBXProjectClass:
+ return "PBXProject";
+ case PBXResourcesBuildPhaseClass:
+ return "PBXResourcesBuildPhase";
+ case PBXShellScriptBuildPhaseClass:
+ return "PBXShellScriptBuildPhase";
+ case PBXSourcesBuildPhaseClass:
+ return "PBXSourcesBuildPhase";
+ case PBXTargetDependencyClass:
+ return "PBXTargetDependency";
+ case XCBuildConfigurationClass:
+ return "XCBuildConfiguration";
+ case XCConfigurationListClass:
+ return "XCConfigurationList";
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+// PBXObjectVisitor -----------------------------------------------------------
+
+PBXObjectVisitor::PBXObjectVisitor() = default;
+
+PBXObjectVisitor::~PBXObjectVisitor() = default;
+
+// PBXObjectVisitorConst ------------------------------------------------------
+
+PBXObjectVisitorConst::PBXObjectVisitorConst() = default;
+
+PBXObjectVisitorConst::~PBXObjectVisitorConst() = default;
+
+// PBXObject ------------------------------------------------------------------
+
+PBXObject::PBXObject() = default;
+
+PBXObject::~PBXObject() = default;
+
+void PBXObject::SetId(const std::string& id) {
+ DCHECK(id_.empty());
+ DCHECK(!id.empty());
+ id_.assign(id);
+}
+
+std::string PBXObject::Reference() const {
+ std::string comment = Comment();
+ if (comment.empty())
+ return id_;
+
+ return id_ + " /* " + comment + " */";
+}
+
+std::string PBXObject::Comment() const {
+ return Name();
+}
+
+void PBXObject::Visit(PBXObjectVisitor& visitor) {
+ visitor.Visit(this);
+}
+
+void PBXObject::Visit(PBXObjectVisitorConst& visitor) const {
+ visitor.Visit(this);
+}
+
+// PBXBuildPhase --------------------------------------------------------------
+
+PBXBuildPhase::PBXBuildPhase() = default;
+
+PBXBuildPhase::~PBXBuildPhase() = default;
+
+void PBXBuildPhase::AddBuildFile(std::unique_ptr<PBXBuildFile> build_file) {
+ DCHECK(build_file);
+ files_.push_back(std::move(build_file));
+}
+
+void PBXBuildPhase::Visit(PBXObjectVisitor& visitor) {
+ PBXObject::Visit(visitor);
+ for (const auto& file : files_) {
+ file->Visit(visitor);
+ }
+}
+
+void PBXBuildPhase::Visit(PBXObjectVisitorConst& visitor) const {
+ PBXObject::Visit(visitor);
+ for (const auto& file : files_) {
+ file->Visit(visitor);
+ }
+}
+
+// PBXTarget ------------------------------------------------------------------
+
+PBXTarget::PBXTarget(const std::string& name,
+ const std::string& shell_script,
+ const std::string& config_name,
+ const PBXAttributes& attributes)
+ : configurations_(
+ std::make_unique<XCConfigurationList>(config_name, attributes, this)),
+ name_(name) {
+ if (!shell_script.empty()) {
+ build_phases_.push_back(
+ std::make_unique<PBXShellScriptBuildPhase>(name, shell_script));
+ }
+}
+
+PBXTarget::~PBXTarget() = default;
+
+void PBXTarget::AddDependency(std::unique_ptr<PBXTargetDependency> dependency) {
+ DCHECK(dependency);
+ dependencies_.push_back(std::move(dependency));
+}
+
+std::string PBXTarget::Name() const {
+ return name_;
+}
+
+void PBXTarget::Visit(PBXObjectVisitor& visitor) {
+ PBXObject::Visit(visitor);
+ configurations_->Visit(visitor);
+ for (const auto& dependency : dependencies_)
+ dependency->Visit(visitor);
+ for (const auto& build_phase : build_phases_)
+ build_phase->Visit(visitor);
+}
+
+void PBXTarget::Visit(PBXObjectVisitorConst& visitor) const {
+ PBXObject::Visit(visitor);
+ configurations_->Visit(visitor);
+ for (const auto& dependency : dependencies_)
+ dependency->Visit(visitor);
+ for (const auto& build_phase : build_phases_)
+ build_phase->Visit(visitor);
+}
+
+// PBXAggregateTarget ---------------------------------------------------------
+
+PBXAggregateTarget::PBXAggregateTarget(const std::string& name,
+ const std::string& shell_script,
+ const std::string& config_name,
+ const PBXAttributes& attributes)
+ : PBXTarget(name, shell_script, config_name, attributes) {}
+
+PBXAggregateTarget::~PBXAggregateTarget() = default;
+
+PBXObjectClass PBXAggregateTarget::Class() const {
+ return PBXAggregateTargetClass;
+}
+
+void PBXAggregateTarget::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildConfigurationList", configurations_);
+ PrintProperty(out, rules, "buildPhases", build_phases_);
+ PrintProperty(out, rules, "dependencies", EmptyPBXObjectVector());
+ PrintProperty(out, rules, "name", name_);
+ PrintProperty(out, rules, "productName", name_);
+ out << indent_str << "};\n";
+}
+
+// PBXBuildFile ---------------------------------------------------------------
+
+PBXBuildFile::PBXBuildFile(const PBXFileReference* file_reference,
+ const PBXBuildPhase* build_phase)
+ : file_reference_(file_reference), build_phase_(build_phase) {
+ DCHECK(file_reference_);
+ DCHECK(build_phase_);
+}
+
+PBXBuildFile::~PBXBuildFile() = default;
+
+PBXObjectClass PBXBuildFile::Class() const {
+ return PBXBuildFileClass;
+}
+
+std::string PBXBuildFile::Name() const {
+ return file_reference_->Name() + " in " + build_phase_->Name();
+}
+
+void PBXBuildFile::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {true, 0};
+ out << indent_str << Reference() << " = {";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "fileRef", file_reference_);
+ out << "};\n";
+}
+
+// PBXContainerItemProxy ------------------------------------------------------
+PBXContainerItemProxy::PBXContainerItemProxy(const PBXProject* project,
+ const PBXTarget* target)
+ : project_(project), target_(target) {}
+
+PBXContainerItemProxy::~PBXContainerItemProxy() = default;
+
+PBXObjectClass PBXContainerItemProxy::Class() const {
+ return PBXContainerItemProxyClass;
+}
+
+std::string PBXContainerItemProxy::Name() const {
+ return "PBXContainerItemProxy";
+}
+
+void PBXContainerItemProxy::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "containerPortal", project_);
+ PrintProperty(out, rules, "proxyType", 1u);
+ PrintProperty(out, rules, "remoteGlobalIDString", NoReference(target_));
+ PrintProperty(out, rules, "remoteInfo", target_->Name());
+ out << indent_str << "};\n";
+}
+
+// PBXFileReference -----------------------------------------------------------
+
+PBXFileReference::PBXFileReference(const std::string& name,
+ const std::string& path,
+ const std::string& type)
+ : name_(name), path_(path), type_(type) {}
+
+PBXFileReference::~PBXFileReference() = default;
+
+PBXObjectClass PBXFileReference::Class() const {
+ return PBXFileReferenceClass;
+}
+
+std::string PBXFileReference::Name() const {
+ return name_;
+}
+
+std::string PBXFileReference::Comment() const {
+ return !name_.empty() ? name_ : path_;
+}
+
+void PBXFileReference::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {true, 0};
+ out << indent_str << Reference() << " = {";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+
+ if (!type_.empty()) {
+ PrintProperty(out, rules, "explicitFileType", type_);
+ PrintProperty(out, rules, "includeInIndex", 0u);
+ } else {
+ std::string_view ext = FindExtension(&name_);
+ if (HasExplicitFileType(ext))
+ PrintProperty(out, rules, "explicitFileType", GetSourceType(ext));
+ else
+ PrintProperty(out, rules, "lastKnownFileType", GetSourceType(ext));
+ }
+
+ if (!name_.empty() && name_ != path_)
+ PrintProperty(out, rules, "name", name_);
+
+ DCHECK(!path_.empty());
+ PrintProperty(out, rules, "path", path_);
+ PrintProperty(out, rules, "sourceTree",
+ type_.empty() ? "<group>" : "BUILT_PRODUCTS_DIR");
+ out << "};\n";
+}
+
+// PBXFrameworksBuildPhase ----------------------------------------------------
+
+PBXFrameworksBuildPhase::PBXFrameworksBuildPhase() = default;
+
+PBXFrameworksBuildPhase::~PBXFrameworksBuildPhase() = default;
+
+PBXObjectClass PBXFrameworksBuildPhase::Class() const {
+ return PBXFrameworksBuildPhaseClass;
+}
+
+std::string PBXFrameworksBuildPhase::Name() const {
+ return "Frameworks";
+}
+
+void PBXFrameworksBuildPhase::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
+ PrintProperty(out, rules, "files", files_);
+ PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
+ out << indent_str << "};\n";
+}
+
+// PBXGroup -------------------------------------------------------------------
+
+PBXGroup::PBXGroup(const std::string& path, const std::string& name)
+ : name_(name), path_(path) {}
+
+PBXGroup::~PBXGroup() = default;
+
+PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path,
+ const std::string& source_path) {
+ DCHECK(!navigator_path.empty());
+ DCHECK(!source_path.empty());
+ std::string::size_type sep = navigator_path.find("/");
+ if (sep == std::string::npos) {
+ // Prevent same file reference being created and added multiple times.
+ for (const auto& child : children_) {
+ if (child->Class() != PBXFileReferenceClass)
+ continue;
+
+ PBXFileReference* child_as_file_reference =
+ static_cast<PBXFileReference*>(child.get());
+ if (child_as_file_reference->Name() == navigator_path &&
+ child_as_file_reference->path() == source_path) {
+ return child_as_file_reference;
+ }
+ }
+
+ return CreateChild<PBXFileReference>(navigator_path, source_path,
+ std::string());
+ }
+
+ PBXGroup* group = nullptr;
+ std::string_view component(navigator_path.data(), sep);
+ for (const auto& child : children_) {
+ if (child->Class() != PBXGroupClass)
+ continue;
+
+ PBXGroup* child_as_group = static_cast<PBXGroup*>(child.get());
+ if (child_as_group->name_ == component) {
+ group = child_as_group;
+ break;
+ }
+ }
+
+ if (!group) {
+ group =
+ CreateChild<PBXGroup>(std::string(component), std::string(component));
+ }
+
+ DCHECK(group);
+ DCHECK(group->name_ == component);
+ return group->AddSourceFile(navigator_path.substr(sep + 1), source_path);
+}
+
+PBXObjectClass PBXGroup::Class() const {
+ return PBXGroupClass;
+}
+
+std::string PBXGroup::Name() const {
+ if (!name_.empty())
+ return name_;
+ if (!path_.empty())
+ return path_;
+ return std::string();
+}
+
+void PBXGroup::Visit(PBXObjectVisitor& visitor) {
+ PBXObject::Visit(visitor);
+ for (const auto& child : children_) {
+ child->Visit(visitor);
+ }
+}
+
+void PBXGroup::Visit(PBXObjectVisitorConst& visitor) const {
+ PBXObject::Visit(visitor);
+ for (const auto& child : children_) {
+ child->Visit(visitor);
+ }
+}
+
+void PBXGroup::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "children", children_);
+ if (!name_.empty())
+ PrintProperty(out, rules, "name", name_);
+ if (is_source_ && !path_.empty())
+ PrintProperty(out, rules, "path", path_);
+ PrintProperty(out, rules, "sourceTree", "<group>");
+ out << indent_str << "};\n";
+}
+
+PBXObject* PBXGroup::AddChildImpl(std::unique_ptr<PBXObject> child) {
+ DCHECK(child);
+ DCHECK(child->Class() == PBXGroupClass ||
+ child->Class() == PBXFileReferenceClass);
+
+ PBXObject* child_ptr = child.get();
+ if (autosorted()) {
+ auto iter = std::lower_bound(children_.begin(), children_.end(), child,
+ PBXGroupComparator());
+ children_.insert(iter, std::move(child));
+ } else {
+ children_.push_back(std::move(child));
+ }
+ return child_ptr;
+}
+
+// PBXNativeTarget ------------------------------------------------------------
+
+PBXNativeTarget::PBXNativeTarget(const std::string& name,
+ const std::string& shell_script,
+ const std::string& config_name,
+ const PBXAttributes& attributes,
+ const std::string& product_type,
+ const std::string& product_name,
+ const PBXFileReference* product_reference)
+ : PBXTarget(name, shell_script, config_name, attributes),
+ product_reference_(product_reference),
+ product_type_(product_type),
+ product_name_(product_name) {
+ DCHECK(product_reference_);
+ build_phases_.push_back(std::make_unique<PBXSourcesBuildPhase>());
+ source_build_phase_ =
+ static_cast<PBXSourcesBuildPhase*>(build_phases_.back().get());
+
+ build_phases_.push_back(std::make_unique<PBXFrameworksBuildPhase>());
+ build_phases_.push_back(std::make_unique<PBXResourcesBuildPhase>());
+ resource_build_phase_ =
+ static_cast<PBXResourcesBuildPhase*>(build_phases_.back().get());
+}
+
+PBXNativeTarget::~PBXNativeTarget() = default;
+
+void PBXNativeTarget::AddResourceFile(const PBXFileReference* file_reference) {
+ DCHECK(file_reference);
+ resource_build_phase_->AddBuildFile(
+ std::make_unique<PBXBuildFile>(file_reference, resource_build_phase_));
+}
+
+void PBXNativeTarget::AddFileForIndexing(
+ const PBXFileReference* file_reference) {
+ DCHECK(file_reference);
+ source_build_phase_->AddBuildFile(
+ std::make_unique<PBXBuildFile>(file_reference, source_build_phase_));
+}
+
+PBXObjectClass PBXNativeTarget::Class() const {
+ return PBXNativeTargetClass;
+}
+
+void PBXNativeTarget::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildConfigurationList", configurations_);
+ PrintProperty(out, rules, "buildPhases", build_phases_);
+ PrintProperty(out, rules, "buildRules", EmptyPBXObjectVector());
+ PrintProperty(out, rules, "dependencies", dependencies_);
+ PrintProperty(out, rules, "name", name_);
+ PrintProperty(out, rules, "productName", product_name_);
+ PrintProperty(out, rules, "productReference", product_reference_);
+ PrintProperty(out, rules, "productType", product_type_);
+ out << indent_str << "};\n";
+}
+
+// PBXProject -----------------------------------------------------------------
+
+PBXProject::PBXProject(const std::string& name,
+ const std::string& config_name,
+ const std::string& source_path,
+ const PBXAttributes& attributes)
+ : name_(name), config_name_(config_name), target_for_indexing_(nullptr) {
+ main_group_ = std::make_unique<PBXGroup>();
+ main_group_->set_autosorted(false);
+
+ sources_ = main_group_->CreateChild<PBXGroup>(source_path, "Source");
+ sources_->set_is_source(true);
+
+ products_ = main_group_->CreateChild<PBXGroup>(std::string(), "Products");
+
+ configurations_ =
+ std::make_unique<XCConfigurationList>(config_name, attributes, this);
+}
+
+PBXProject::~PBXProject() = default;
+
+void PBXProject::AddSourceFileToIndexingTarget(
+ const std::string& navigator_path,
+ const std::string& source_path) {
+ if (!target_for_indexing_) {
+ AddIndexingTarget();
+ }
+ AddSourceFile(navigator_path, source_path, target_for_indexing_);
+}
+
+void PBXProject::AddSourceFile(const std::string& navigator_path,
+ const std::string& source_path,
+ PBXNativeTarget* target) {
+ PBXFileReference* file_reference =
+ sources_->AddSourceFile(navigator_path, source_path);
+ std::string_view ext = FindExtension(&source_path);
+ if (!IsSourceFileForIndexing(ext))
+ return;
+
+ DCHECK(target);
+ target->AddFileForIndexing(file_reference);
+}
+
+void PBXProject::AddAggregateTarget(const std::string& name,
+ const std::string& shell_script) {
+ PBXAttributes attributes;
+ attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
+ attributes["CODE_SIGNING_REQUIRED"] = "NO";
+ attributes["CONFIGURATION_BUILD_DIR"] = ".";
+ attributes["PRODUCT_NAME"] = name;
+
+ targets_.push_back(std::make_unique<PBXAggregateTarget>(
+ name, shell_script, config_name_, attributes));
+}
+
+void PBXProject::AddIndexingTarget() {
+ DCHECK(!target_for_indexing_);
+ PBXAttributes attributes;
+ attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
+ attributes["CODE_SIGNING_REQUIRED"] = "NO";
+ attributes["EXECUTABLE_PREFIX"] = "";
+ attributes["HEADER_SEARCH_PATHS"] = sources_->path();
+ attributes["PRODUCT_NAME"] = "sources";
+
+ PBXFileReference* product_reference =
+ products_->CreateChild<PBXFileReference>(std::string(), "sources",
+ "compiled.mach-o.executable");
+
+ const char product_type[] = "com.apple.product-type.tool";
+ targets_.push_back(std::make_unique<PBXNativeTarget>(
+ "sources", std::string(), config_name_, attributes, product_type,
+ "sources", product_reference));
+ target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get());
+}
+
+PBXNativeTarget* PBXProject::AddNativeTarget(
+ const std::string& name,
+ const std::string& type,
+ const std::string& output_name,
+ const std::string& output_type,
+ const std::string& output_dir,
+ const std::string& shell_script,
+ const PBXAttributes& extra_attributes) {
+ std::string_view ext = FindExtension(&output_name);
+ PBXFileReference* product = products_->CreateChild<PBXFileReference>(
+ std::string(), output_name, type.empty() ? GetSourceType(ext) : type);
+
+ // Per Xcode build settings documentation: Product Name (PRODUCT_NAME) should
+ // the basename of the product generated by the target.
+ // Therefore, take the basename of output name without file extension as the
+ // "PRODUCT_NAME".
+ size_t basename_offset = FindFilenameOffset(output_name);
+ std::string output_basename = basename_offset != std::string::npos
+ ? output_name.substr(basename_offset)
+ : output_name;
+ size_t ext_offset = FindExtensionOffset(output_basename);
+ std::string product_name = ext_offset != std::string::npos
+ ? output_basename.substr(0, ext_offset - 1)
+ : output_basename;
+
+ PBXAttributes attributes = extra_attributes;
+ attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
+ attributes["CODE_SIGNING_REQUIRED"] = "NO";
+ attributes["CONFIGURATION_BUILD_DIR"] = output_dir;
+ attributes["PRODUCT_NAME"] = product_name;
+ attributes["EXCLUDED_SOURCE_FILE_NAMES"] = "*.*";
+
+ targets_.push_back(std::make_unique<PBXNativeTarget>(
+ name, shell_script, config_name_, attributes, output_type, product_name,
+ product));
+ return static_cast<PBXNativeTarget*>(targets_.back().get());
+}
+
+void PBXProject::SetProjectDirPath(const std::string& project_dir_path) {
+ DCHECK(!project_dir_path.empty());
+ project_dir_path_.assign(project_dir_path);
+}
+
+void PBXProject::SetProjectRoot(const std::string& project_root) {
+ DCHECK(!project_root.empty());
+ project_root_.assign(project_root);
+}
+
+void PBXProject::AddTarget(std::unique_ptr<PBXTarget> target) {
+ DCHECK(target);
+ targets_.push_back(std::move(target));
+}
+
+PBXObjectClass PBXProject::Class() const {
+ return PBXProjectClass;
+}
+
+std::string PBXProject::Name() const {
+ return name_;
+}
+
+std::string PBXProject::Comment() const {
+ return "Project object";
+}
+
+void PBXProject::Visit(PBXObjectVisitor& visitor) {
+ PBXObject::Visit(visitor);
+ configurations_->Visit(visitor);
+ main_group_->Visit(visitor);
+ for (const auto& target : targets_) {
+ target->Visit(visitor);
+ }
+}
+
+void PBXProject::Visit(PBXObjectVisitorConst& visitor) const {
+ PBXObject::Visit(visitor);
+ configurations_->Visit(visitor);
+ main_group_->Visit(visitor);
+ for (const auto& target : targets_) {
+ target->Visit(visitor);
+ }
+}
+void PBXProject::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "attributes", attributes_);
+ PrintProperty(out, rules, "buildConfigurationList", configurations_);
+ PrintProperty(out, rules, "compatibilityVersion", "Xcode 3.2");
+ PrintProperty(out, rules, "developmentRegion", "en");
+ PrintProperty(out, rules, "hasScannedForEncodings", 1u);
+ PrintProperty(out, rules, "knownRegions",
+ std::vector<std::string>({"en", "Base"}));
+ PrintProperty(out, rules, "mainGroup", main_group_);
+ PrintProperty(out, rules, "projectDirPath", project_dir_path_);
+ PrintProperty(out, rules, "projectRoot", project_root_);
+ PrintProperty(out, rules, "targets", targets_);
+ out << indent_str << "};\n";
+}
+
+// PBXResourcesBuildPhase -----------------------------------------------------
+
+PBXResourcesBuildPhase::PBXResourcesBuildPhase() = default;
+
+PBXResourcesBuildPhase::~PBXResourcesBuildPhase() = default;
+
+PBXObjectClass PBXResourcesBuildPhase::Class() const {
+ return PBXResourcesBuildPhaseClass;
+}
+
+std::string PBXResourcesBuildPhase::Name() const {
+ return "Resources";
+}
+
+void PBXResourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
+ PrintProperty(out, rules, "files", files_);
+ PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
+ out << indent_str << "};\n";
+}
+
+// PBXShellScriptBuildPhase ---------------------------------------------------
+
+PBXShellScriptBuildPhase::PBXShellScriptBuildPhase(
+ const std::string& name,
+ const std::string& shell_script)
+ : name_("Action \"Compile and copy " + name + " via ninja\""),
+ shell_script_(shell_script) {}
+
+PBXShellScriptBuildPhase::~PBXShellScriptBuildPhase() = default;
+
+PBXObjectClass PBXShellScriptBuildPhase::Class() const {
+ return PBXShellScriptBuildPhaseClass;
+}
+
+std::string PBXShellScriptBuildPhase::Name() const {
+ return name_;
+}
+
+void PBXShellScriptBuildPhase::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
+ PrintProperty(out, rules, "files", files_);
+ PrintProperty(out, rules, "inputPaths", EmptyPBXObjectVector());
+ PrintProperty(out, rules, "name", name_);
+ PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector());
+ PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
+ PrintProperty(out, rules, "shellPath", "/usr/bin/python3");
+ PrintProperty(out, rules, "shellScript", shell_script_);
+ PrintProperty(out, rules, "showEnvVarsInLog", 0u);
+ out << indent_str << "};\n";
+}
+
+// PBXSourcesBuildPhase -------------------------------------------------------
+
+PBXSourcesBuildPhase::PBXSourcesBuildPhase() = default;
+
+PBXSourcesBuildPhase::~PBXSourcesBuildPhase() = default;
+
+PBXObjectClass PBXSourcesBuildPhase::Class() const {
+ return PBXSourcesBuildPhaseClass;
+}
+
+std::string PBXSourcesBuildPhase::Name() const {
+ return "Sources";
+}
+
+void PBXSourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
+ PrintProperty(out, rules, "files", files_);
+ PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
+ out << indent_str << "};\n";
+}
+
+PBXTargetDependency::PBXTargetDependency(
+ const PBXTarget* target,
+ std::unique_ptr<PBXContainerItemProxy> container_item_proxy)
+ : target_(target), container_item_proxy_(std::move(container_item_proxy)) {}
+
+PBXTargetDependency::~PBXTargetDependency() = default;
+
+PBXObjectClass PBXTargetDependency::Class() const {
+ return PBXTargetDependencyClass;
+}
+
+std::string PBXTargetDependency::Name() const {
+ return "PBXTargetDependency";
+}
+
+void PBXTargetDependency::Visit(PBXObjectVisitor& visitor) {
+ PBXObject::Visit(visitor);
+ container_item_proxy_->Visit(visitor);
+}
+
+void PBXTargetDependency::Visit(PBXObjectVisitorConst& visitor) const {
+ PBXObject::Visit(visitor);
+ container_item_proxy_->Visit(visitor);
+}
+
+void PBXTargetDependency::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "target", target_);
+ PrintProperty(out, rules, "targetProxy", container_item_proxy_);
+ out << indent_str << "};\n";
+}
+
+// XCBuildConfiguration -------------------------------------------------------
+
+XCBuildConfiguration::XCBuildConfiguration(const std::string& name,
+ const PBXAttributes& attributes)
+ : attributes_(attributes), name_(name) {}
+
+XCBuildConfiguration::~XCBuildConfiguration() = default;
+
+PBXObjectClass XCBuildConfiguration::Class() const {
+ return XCBuildConfigurationClass;
+}
+
+std::string XCBuildConfiguration::Name() const {
+ return name_;
+}
+
+void XCBuildConfiguration::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildSettings", attributes_);
+ PrintProperty(out, rules, "name", name_);
+ out << indent_str << "};\n";
+}
+
+// XCConfigurationList --------------------------------------------------------
+
+XCConfigurationList::XCConfigurationList(const std::string& name,
+ const PBXAttributes& attributes,
+ const PBXObject* owner_reference)
+ : owner_reference_(owner_reference) {
+ DCHECK(owner_reference_);
+ configurations_.push_back(
+ std::make_unique<XCBuildConfiguration>(name, attributes));
+}
+
+XCConfigurationList::~XCConfigurationList() = default;
+
+PBXObjectClass XCConfigurationList::Class() const {
+ return XCConfigurationListClass;
+}
+
+std::string XCConfigurationList::Name() const {
+ std::stringstream buffer;
+ buffer << "Build configuration list for "
+ << ToString(owner_reference_->Class()) << " \""
+ << owner_reference_->Name() << "\"";
+ return buffer.str();
+}
+
+void XCConfigurationList::Visit(PBXObjectVisitor& visitor) {
+ PBXObject::Visit(visitor);
+ for (const auto& configuration : configurations_) {
+ configuration->Visit(visitor);
+ }
+}
+
+void XCConfigurationList::Visit(PBXObjectVisitorConst& visitor) const {
+ PBXObject::Visit(visitor);
+ for (const auto& configuration : configurations_) {
+ configuration->Visit(visitor);
+ }
+}
+
+void XCConfigurationList::Print(std::ostream& out, unsigned indent) const {
+ const std::string indent_str(indent, '\t');
+ const IndentRules rules = {false, indent + 1};
+ out << indent_str << Reference() << " = {\n";
+ PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "buildConfigurations", configurations_);
+ PrintProperty(out, rules, "defaultConfigurationIsVisible", 1u);
+ PrintProperty(out, rules, "defaultConfigurationName",
+ configurations_[0]->Name());
+ out << indent_str << "};\n";
+}
diff --git a/gn/src/gn/xcode_object.h b/gn/src/gn/xcode_object.h
new file mode 100644
index 00000000000..82dec5e60de
--- /dev/null
+++ b/gn/src/gn/xcode_object.h
@@ -0,0 +1,512 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_XCODE_OBJECT_H_
+#define TOOLS_GN_XCODE_OBJECT_H_
+
+#include <iosfwd>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+// Helper classes to generate Xcode project files.
+//
+// This code is based on gyp xcodeproj_file.py generator. It does not support
+// all features of Xcode project but instead just enough to implement a hybrid
+// mode where Xcode uses external scripts to perform the compilation steps.
+//
+// See
+// https://chromium.googlesource.com/external/gyp/+/master/pylib/gyp/xcodeproj_file.py
+// for more information on Xcode project file format.
+
+// PBXObjectClass -------------------------------------------------------------
+
+enum PBXObjectClass {
+ // Those values needs to stay sorted in alphabetic order.
+ PBXAggregateTargetClass,
+ PBXBuildFileClass,
+ PBXContainerItemProxyClass,
+ PBXFileReferenceClass,
+ PBXFrameworksBuildPhaseClass,
+ PBXGroupClass,
+ PBXNativeTargetClass,
+ PBXProjectClass,
+ PBXResourcesBuildPhaseClass,
+ PBXShellScriptBuildPhaseClass,
+ PBXSourcesBuildPhaseClass,
+ PBXTargetDependencyClass,
+ XCBuildConfigurationClass,
+ XCConfigurationListClass,
+};
+
+const char* ToString(PBXObjectClass cls);
+
+// Forward-declarations -------------------------------------------------------
+
+class PBXAggregateTarget;
+class PBXBuildFile;
+class PBXBuildPhase;
+class PBXContainerItemProxy;
+class PBXFileReference;
+class PBXFrameworksBuildPhase;
+class PBXGroup;
+class PBXNativeTarget;
+class PBXObject;
+class PBXProject;
+class PBXResourcesBuildPhase;
+class PBXShellScriptBuildPhase;
+class PBXSourcesBuildPhase;
+class PBXTarget;
+class PBXTargetDependency;
+class XCBuildConfiguration;
+class XCConfigurationList;
+
+using PBXAttributes = std::map<std::string, std::string>;
+
+// PBXObjectVisitor -----------------------------------------------------------
+
+class PBXObjectVisitor {
+ public:
+ PBXObjectVisitor();
+ virtual ~PBXObjectVisitor();
+ virtual void Visit(PBXObject* object) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXObjectVisitor);
+};
+
+// PBXObjectVisitorConst ------------------------------------------------------
+
+class PBXObjectVisitorConst {
+ public:
+ PBXObjectVisitorConst();
+ virtual ~PBXObjectVisitorConst();
+ virtual void Visit(const PBXObject* object) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXObjectVisitorConst);
+};
+
+// PBXObject ------------------------------------------------------------------
+
+class PBXObject {
+ public:
+ PBXObject();
+ virtual ~PBXObject();
+
+ const std::string id() const { return id_; }
+ void SetId(const std::string& id);
+
+ std::string Reference() const;
+
+ virtual PBXObjectClass Class() const = 0;
+ virtual std::string Name() const = 0;
+ virtual std::string Comment() const;
+ virtual void Visit(PBXObjectVisitor& visitor);
+ virtual void Visit(PBXObjectVisitorConst& visitor) const;
+ virtual void Print(std::ostream& out, unsigned indent) const = 0;
+
+ private:
+ std::string id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXObject);
+};
+
+// PBXBuildPhase --------------------------------------------------------------
+
+class PBXBuildPhase : public PBXObject {
+ public:
+ PBXBuildPhase();
+ ~PBXBuildPhase() override;
+
+ void AddBuildFile(std::unique_ptr<PBXBuildFile> build_file);
+
+ // PBXObject implementation.
+ void Visit(PBXObjectVisitor& visitor) override;
+ void Visit(PBXObjectVisitorConst& visitor) const override;
+
+ protected:
+ std::vector<std::unique_ptr<PBXBuildFile>> files_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXBuildPhase);
+};
+
+// PBXTarget ------------------------------------------------------------------
+
+class PBXTarget : public PBXObject {
+ public:
+ PBXTarget(const std::string& name,
+ const std::string& shell_script,
+ const std::string& config_name,
+ const PBXAttributes& attributes);
+ ~PBXTarget() override;
+
+ void AddDependency(std::unique_ptr<PBXTargetDependency> dependency);
+
+ // PBXObject implementation.
+ std::string Name() const override;
+ void Visit(PBXObjectVisitor& visitor) override;
+ void Visit(PBXObjectVisitorConst& visitor) const override;
+
+ protected:
+ std::unique_ptr<XCConfigurationList> configurations_;
+ std::vector<std::unique_ptr<PBXBuildPhase>> build_phases_;
+ std::vector<std::unique_ptr<PBXTargetDependency>> dependencies_;
+ PBXSourcesBuildPhase* source_build_phase_ = nullptr;
+ PBXResourcesBuildPhase* resource_build_phase_ = nullptr;
+ std::string name_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXTarget);
+};
+
+// PBXAggregateTarget ---------------------------------------------------------
+
+class PBXAggregateTarget : public PBXTarget {
+ public:
+ PBXAggregateTarget(const std::string& name,
+ const std::string& shell_script,
+ const std::string& config_name,
+ const PBXAttributes& attributes);
+ ~PBXAggregateTarget() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXAggregateTarget);
+};
+
+// PBXBuildFile ---------------------------------------------------------------
+
+class PBXBuildFile : public PBXObject {
+ public:
+ PBXBuildFile(const PBXFileReference* file_reference,
+ const PBXBuildPhase* build_phase);
+ ~PBXBuildFile() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ const PBXFileReference* file_reference_ = nullptr;
+ const PBXBuildPhase* build_phase_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXBuildFile);
+};
+
+// PBXContainerItemProxy ------------------------------------------------------
+class PBXContainerItemProxy : public PBXObject {
+ public:
+ PBXContainerItemProxy(const PBXProject* project, const PBXTarget* target);
+ ~PBXContainerItemProxy() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ const PBXProject* project_ = nullptr;
+ const PBXTarget* target_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXContainerItemProxy);
+};
+
+// PBXFileReference -----------------------------------------------------------
+
+class PBXFileReference : public PBXObject {
+ public:
+ PBXFileReference(const std::string& name,
+ const std::string& path,
+ const std::string& type);
+ ~PBXFileReference() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ std::string Comment() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ const std::string& path() const { return path_; }
+
+ private:
+ std::string name_;
+ std::string path_;
+ std::string type_;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXFileReference);
+};
+
+// PBXFrameworksBuildPhase ----------------------------------------------------
+
+class PBXFrameworksBuildPhase : public PBXBuildPhase {
+ public:
+ PBXFrameworksBuildPhase();
+ ~PBXFrameworksBuildPhase() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXFrameworksBuildPhase);
+};
+
+// PBXGroup -------------------------------------------------------------------
+
+class PBXGroup : public PBXObject {
+ public:
+ explicit PBXGroup(const std::string& path = std::string(),
+ const std::string& name = std::string());
+ ~PBXGroup() override;
+
+ const std::string& path() const { return path_; }
+ const std::string& name() const { return name_; }
+
+ PBXFileReference* AddSourceFile(const std::string& navigator_path,
+ const std::string& source_path);
+
+ bool is_source() const { return is_source_; }
+ void set_is_source(bool is_source) { is_source_ = is_source; }
+
+ bool autosorted() const { return autosorted_; }
+ void set_autosorted(bool autosorted) { autosorted_ = autosorted; }
+
+ template <typename T, typename... Args>
+ T* CreateChild(Args&&... args) {
+ return static_cast<T*>(
+ AddChildImpl(std::make_unique<T>(std::forward<Args>(args)...)));
+ }
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Visit(PBXObjectVisitor& visitor) override;
+ void Visit(PBXObjectVisitorConst& visitor) const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ PBXObject* AddChildImpl(std::unique_ptr<PBXObject> child);
+
+ std::vector<std::unique_ptr<PBXObject>> children_;
+ std::string name_;
+ std::string path_;
+ bool is_source_ = false;
+ bool autosorted_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXGroup);
+};
+
+// PBXNativeTarget ------------------------------------------------------------
+
+class PBXNativeTarget : public PBXTarget {
+ public:
+ PBXNativeTarget(const std::string& name,
+ const std::string& shell_script,
+ const std::string& config_name,
+ const PBXAttributes& attributes,
+ const std::string& product_type,
+ const std::string& product_name,
+ const PBXFileReference* product_reference);
+ ~PBXNativeTarget() override;
+
+ void AddResourceFile(const PBXFileReference* file_reference);
+
+ void AddFileForIndexing(const PBXFileReference* file_reference);
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ const PBXFileReference* product_reference_ = nullptr;
+ std::string product_type_;
+ std::string product_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXNativeTarget);
+};
+
+// PBXProject -----------------------------------------------------------------
+
+class PBXProject : public PBXObject {
+ public:
+ PBXProject(const std::string& name,
+ const std::string& config_name,
+ const std::string& source_path,
+ const PBXAttributes& attributes);
+ ~PBXProject() override;
+
+ void AddSourceFileToIndexingTarget(const std::string& navigator_path,
+ const std::string& source_path);
+ void AddSourceFile(const std::string& navigator_path,
+ const std::string& source_path,
+ PBXNativeTarget* target);
+ void AddAggregateTarget(const std::string& name,
+ const std::string& shell_script);
+ void AddIndexingTarget();
+ PBXNativeTarget* AddNativeTarget(
+ const std::string& name,
+ const std::string& type,
+ const std::string& output_dir,
+ const std::string& output_name,
+ const std::string& output_type,
+ const std::string& shell_script,
+ const PBXAttributes& extra_attributes = PBXAttributes());
+
+ void SetProjectDirPath(const std::string& project_dir_path);
+ void SetProjectRoot(const std::string& project_root);
+ void AddTarget(std::unique_ptr<PBXTarget> target);
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ std::string Comment() const override;
+ void Visit(PBXObjectVisitor& visitor) override;
+ void Visit(PBXObjectVisitorConst& visitor) const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ PBXAttributes attributes_;
+ std::unique_ptr<XCConfigurationList> configurations_;
+ std::unique_ptr<PBXGroup> main_group_;
+ std::string project_dir_path_;
+ std::string project_root_;
+ std::vector<std::unique_ptr<PBXTarget>> targets_;
+ std::string name_;
+ std::string config_name_;
+
+ PBXGroup* sources_ = nullptr;
+ PBXGroup* products_ = nullptr;
+ PBXNativeTarget* target_for_indexing_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXProject);
+};
+
+// PBXResourcesBuildPhase -----------------------------------------------------
+
+class PBXResourcesBuildPhase : public PBXBuildPhase {
+ public:
+ PBXResourcesBuildPhase();
+ ~PBXResourcesBuildPhase() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXResourcesBuildPhase);
+};
+
+// PBXShellScriptBuildPhase ---------------------------------------------------
+
+class PBXShellScriptBuildPhase : public PBXBuildPhase {
+ public:
+ PBXShellScriptBuildPhase(const std::string& name,
+ const std::string& shell_script);
+ ~PBXShellScriptBuildPhase() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ std::string name_;
+ std::string shell_script_;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXShellScriptBuildPhase);
+};
+
+// PBXSourcesBuildPhase -------------------------------------------------------
+
+class PBXSourcesBuildPhase : public PBXBuildPhase {
+ public:
+ PBXSourcesBuildPhase();
+ ~PBXSourcesBuildPhase() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PBXSourcesBuildPhase);
+};
+
+// PBXTargetDependency -----------------------------------------------------
+class PBXTargetDependency : public PBXObject {
+ public:
+ PBXTargetDependency(
+ const PBXTarget* target,
+ std::unique_ptr<PBXContainerItemProxy> container_item_proxy);
+ ~PBXTargetDependency() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Visit(PBXObjectVisitor& visitor) override;
+ void Visit(PBXObjectVisitorConst& visitor) const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ const PBXTarget* target_ = nullptr;
+ std::unique_ptr<PBXContainerItemProxy> container_item_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(PBXTargetDependency);
+};
+
+// XCBuildConfiguration -------------------------------------------------------
+
+class XCBuildConfiguration : public PBXObject {
+ public:
+ XCBuildConfiguration(const std::string& name,
+ const PBXAttributes& attributes);
+ ~XCBuildConfiguration() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ PBXAttributes attributes_;
+ std::string name_;
+
+ DISALLOW_COPY_AND_ASSIGN(XCBuildConfiguration);
+};
+
+// XCConfigurationList --------------------------------------------------------
+
+class XCConfigurationList : public PBXObject {
+ public:
+ XCConfigurationList(const std::string& name,
+ const PBXAttributes& attributes,
+ const PBXObject* owner_reference);
+ ~XCConfigurationList() override;
+
+ // PBXObject implementation.
+ PBXObjectClass Class() const override;
+ std::string Name() const override;
+ void Visit(PBXObjectVisitor& visitor) override;
+ void Visit(PBXObjectVisitorConst& visitor) const override;
+ void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+ std::vector<std::unique_ptr<XCBuildConfiguration>> configurations_;
+ const PBXObject* owner_reference_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(XCConfigurationList);
+};
+
+#endif // TOOLS_GN_XCODE_OBJECT_H_
diff --git a/gn/src/gn/xcode_object_unittest.cc b/gn/src/gn/xcode_object_unittest.cc
new file mode 100644
index 00000000000..0498c3f1f36
--- /dev/null
+++ b/gn/src/gn/xcode_object_unittest.cc
@@ -0,0 +1,435 @@
+// Copyright 2017 The Chromium 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 "gn/xcode_object.h"
+
+#include "util/test/test.h"
+
+namespace {
+
+// Instantiate a PBXSourcesBuildPhase object.
+std::unique_ptr<PBXSourcesBuildPhase> GetPBXSourcesBuildPhaseObject() {
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase(
+ new PBXSourcesBuildPhase());
+ return pbx_sources_build_phase;
+}
+
+// Instantiate a PBXFrameworksBuildPhase object.
+std::unique_ptr<PBXFrameworksBuildPhase> GetPBXFrameworksBuildPhaseObject() {
+ std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase(
+ new PBXFrameworksBuildPhase());
+ return pbx_frameworks_build_phase;
+}
+
+// Instantiate a PBXShellScriptBuildPhase object with arbitrary names.
+std::unique_ptr<PBXShellScriptBuildPhase> GetPBXShellScriptBuildPhaseObject() {
+ std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase(
+ new PBXShellScriptBuildPhase("name", "shell_script"));
+ return pbx_shell_script_build_phase;
+}
+
+// Instantiate a PBXGroup object with arbitrary names.
+std::unique_ptr<PBXGroup> GetPBXGroupObject() {
+ std::unique_ptr<PBXGroup> pbx_group(new PBXGroup("/dir1/dir2", "group"));
+ return pbx_group;
+}
+
+// Instantiate a PBXProject object with arbitrary names.
+std::unique_ptr<PBXProject> GetPBXProjectObject() {
+ std::unique_ptr<PBXProject> pbx_project(
+ new PBXProject("project", "config", "out/build", PBXAttributes()));
+ return pbx_project;
+}
+
+// Instantiate a PBXFileReference object with arbitrary names.
+std::unique_ptr<PBXFileReference> GetPBXFileReferenceObject() {
+ std::unique_ptr<PBXFileReference> pbx_file_reference(new PBXFileReference(
+ "product.app", "product.app", "wrapper.application"));
+ return pbx_file_reference;
+}
+
+// Instantiate a PBXBuildFile object.
+std::unique_ptr<PBXBuildFile> GetPBXBuildFileObject(
+ const PBXFileReference* file_reference,
+ const PBXSourcesBuildPhase* build_phase) {
+ std::unique_ptr<PBXBuildFile> pbx_build_file(
+ new PBXBuildFile(file_reference, build_phase));
+ return pbx_build_file;
+}
+
+// Instantiate a PBXAggregateTarget object with arbitrary names.
+std::unique_ptr<PBXAggregateTarget> GetPBXAggregateTargetObject() {
+ std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target(
+ new PBXAggregateTarget("target_name", "shell_script", "config_name",
+ PBXAttributes()));
+ return pbx_aggregate_target;
+}
+
+// Instantiate a PBXNativeTarget object with arbitrary names.
+std::unique_ptr<PBXNativeTarget> GetPBXNativeTargetObject(
+ const PBXFileReference* product_reference) {
+ std::unique_ptr<PBXNativeTarget> pbx_native_target(new PBXNativeTarget(
+ "target_name", "ninja gn_unittests", "config_name", PBXAttributes(),
+ "com.apple.product-type.application", "product_name", product_reference));
+ return pbx_native_target;
+}
+
+// Instantiate a PBXContainerItemProxy object.
+std::unique_ptr<PBXContainerItemProxy> GetPBXContainerItemProxyObject(
+ const PBXProject* project,
+ const PBXTarget* target) {
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy(
+ new PBXContainerItemProxy(project, target));
+ return pbx_container_item_proxy;
+}
+
+// Instantiate a PBXTargetDependency object.
+std::unique_ptr<PBXTargetDependency> GetPBXTargetDependencyObject(
+ const PBXTarget* target,
+ std::unique_ptr<PBXContainerItemProxy> container_item_proxy) {
+ std::unique_ptr<PBXTargetDependency> pbx_target_dependency(
+ new PBXTargetDependency(target, std::move(container_item_proxy)));
+ return pbx_target_dependency;
+}
+
+// Instantiate a XCBuildConfiguration object with arbitrary names.
+std::unique_ptr<XCBuildConfiguration> GetXCBuildConfigurationObject() {
+ std::unique_ptr<XCBuildConfiguration> xc_build_configuration(
+ new XCBuildConfiguration("config_name", PBXAttributes()));
+ return xc_build_configuration;
+}
+
+// Instantiate a XCConfigurationList object with arbitrary names.
+std::unique_ptr<XCConfigurationList> GetXCConfigurationListObject(
+ const PBXObject* owner_reference) {
+ std::unique_ptr<XCConfigurationList> xc_configuration_list(
+ new XCConfigurationList("config_list_name", PBXAttributes(),
+ owner_reference));
+ return xc_configuration_list;
+}
+
+} // namespace
+
+// Tests that instantiating Xcode objects doesn't crash.
+TEST(XcodeObject, InstantiatePBXSourcesBuildPhase) {
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
+ GetPBXSourcesBuildPhaseObject();
+}
+
+TEST(XcodeObject, InstantiatePBXFrameworksBuildPhase) {
+ std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase =
+ GetPBXFrameworksBuildPhaseObject();
+}
+
+TEST(XcodeObject, InstantiatePBXShellScriptBuildPhase) {
+ std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase =
+ GetPBXShellScriptBuildPhaseObject();
+}
+
+TEST(XcodeObject, InstantiatePBXGroup) {
+ std::unique_ptr<PBXGroup> pbx_group = GetPBXGroupObject();
+}
+
+TEST(XcodeObject, InstantiatePBXProject) {
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+}
+
+TEST(XcodeObject, InstantiatePBXFileReference) {
+ std::unique_ptr<PBXFileReference> pbx_file_reference =
+ GetPBXFileReferenceObject();
+}
+
+TEST(XcodeObject, InstantiatePBXBuildFile) {
+ std::unique_ptr<PBXFileReference> pbx_file_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
+ GetPBXSourcesBuildPhaseObject();
+ std::unique_ptr<PBXBuildFile> pbx_build_file = GetPBXBuildFileObject(
+ pbx_file_reference.get(), pbx_sources_build_phase.get());
+}
+
+TEST(XcodeObject, InstantiatePBXAggregateTarget) {
+ std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target =
+ GetPBXAggregateTargetObject();
+}
+
+TEST(XcodeObject, InstantiatePBXNativeTarget) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+}
+
+TEST(XcodeObject, InstantiatePBXContainerItemProxy) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
+ GetPBXContainerItemProxyObject(pbx_project.get(),
+ pbx_native_target.get());
+}
+
+TEST(XcodeObject, InstantiatePBXTargetDependency) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
+ GetPBXContainerItemProxyObject(pbx_project.get(),
+ pbx_native_target.get());
+ std::unique_ptr<PBXTargetDependency> pbx_target_dependency =
+ GetPBXTargetDependencyObject(pbx_native_target.get(),
+ std::move(pbx_container_item_proxy));
+}
+
+TEST(XcodeObject, InstantiateXCBuildConfiguration) {
+ std::unique_ptr<XCBuildConfiguration> xc_build_configuration =
+ GetXCBuildConfigurationObject();
+}
+
+TEST(XcodeObject, InstantiateXCConfigurationList) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<XCConfigurationList> xc_configuration_list =
+ GetXCConfigurationListObject(pbx_native_target.get());
+}
+
+// Tests that the mapping between PBXObject and PBXObjectClass.
+TEST(XcodeObject, PBXSourcesBuildPhaseObjectToClass) {
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
+ GetPBXSourcesBuildPhaseObject();
+ EXPECT_EQ(PBXSourcesBuildPhaseClass, pbx_sources_build_phase->Class());
+}
+
+TEST(XcodeObject, PBXFrameworksBuildPhaseObjectToClass) {
+ std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase =
+ GetPBXFrameworksBuildPhaseObject();
+ EXPECT_EQ(PBXFrameworksBuildPhaseClass, pbx_frameworks_build_phase->Class());
+}
+
+TEST(XcodeObject, PBXShellScriptBuildPhaseObjectToClass) {
+ std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase =
+ GetPBXShellScriptBuildPhaseObject();
+ EXPECT_EQ(PBXShellScriptBuildPhaseClass,
+ pbx_shell_script_build_phase->Class());
+}
+
+TEST(XcodeObject, PBXGroupObjectToClass) {
+ std::unique_ptr<PBXGroup> pbx_group = GetPBXGroupObject();
+ EXPECT_EQ(PBXGroupClass, pbx_group->Class());
+}
+
+TEST(XcodeObject, PBXProjectObjectToClass) {
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ EXPECT_EQ(PBXProjectClass, pbx_project->Class());
+}
+
+TEST(XcodeObject, PBXFileReferenceObjectToClass) {
+ std::unique_ptr<PBXFileReference> pbx_file_reference =
+ GetPBXFileReferenceObject();
+ EXPECT_EQ(PBXFileReferenceClass, pbx_file_reference->Class());
+}
+
+TEST(XcodeObject, PBXBuildFileObjectToClass) {
+ std::unique_ptr<PBXFileReference> pbx_file_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
+ GetPBXSourcesBuildPhaseObject();
+ std::unique_ptr<PBXBuildFile> pbx_build_file = GetPBXBuildFileObject(
+ pbx_file_reference.get(), pbx_sources_build_phase.get());
+ EXPECT_EQ(PBXBuildFileClass, pbx_build_file->Class());
+}
+
+TEST(XcodeObject, PBXAggregateTargetObjectToClass) {
+ std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target =
+ GetPBXAggregateTargetObject();
+ EXPECT_EQ(PBXAggregateTargetClass, pbx_aggregate_target->Class());
+}
+
+TEST(XcodeObject, PBXNativeTargetObjectToClass) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ EXPECT_EQ(PBXNativeTargetClass, pbx_native_target->Class());
+}
+
+TEST(XcodeObject, PBXContainerItemProxyObjectToClass) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
+ GetPBXContainerItemProxyObject(pbx_project.get(),
+ pbx_native_target.get());
+ EXPECT_EQ(PBXContainerItemProxyClass, pbx_container_item_proxy->Class());
+}
+
+TEST(XcodeObject, PBXTargetDependencyObjectToClass) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
+ GetPBXContainerItemProxyObject(pbx_project.get(),
+ pbx_native_target.get());
+ std::unique_ptr<PBXTargetDependency> pbx_target_dependency =
+ GetPBXTargetDependencyObject(pbx_native_target.get(),
+ std::move(pbx_container_item_proxy));
+ EXPECT_EQ(PBXTargetDependencyClass, pbx_target_dependency->Class());
+}
+
+TEST(XcodeObject, XCBuildConfigurationObjectToClass) {
+ std::unique_ptr<XCBuildConfiguration> xc_build_configuration =
+ GetXCBuildConfigurationObject();
+ EXPECT_EQ(XCBuildConfigurationClass, xc_build_configuration->Class());
+}
+
+TEST(XcodeObject, XCConfigurationListObjectToClass) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<XCConfigurationList> xc_configuration_list =
+ GetXCConfigurationListObject(pbx_native_target.get());
+ EXPECT_EQ(XCConfigurationListClass, xc_configuration_list->Class());
+}
+
+// Tests the mapping between PBXObjectClass and it's name as a string.
+TEST(XcodeObject, ClassToString) {
+ EXPECT_STREQ("PBXAggregateTarget", ToString(PBXAggregateTargetClass));
+ EXPECT_STREQ("PBXBuildFile", ToString(PBXBuildFileClass));
+ EXPECT_STREQ("PBXAggregateTarget", ToString(PBXAggregateTargetClass));
+ EXPECT_STREQ("PBXBuildFile", ToString(PBXBuildFileClass));
+ EXPECT_STREQ("PBXContainerItemProxy", ToString(PBXContainerItemProxyClass));
+ EXPECT_STREQ("PBXFileReference", ToString(PBXFileReferenceClass));
+ EXPECT_STREQ("PBXFrameworksBuildPhase",
+ ToString(PBXFrameworksBuildPhaseClass));
+ EXPECT_STREQ("PBXGroup", ToString(PBXGroupClass));
+ EXPECT_STREQ("PBXNativeTarget", ToString(PBXNativeTargetClass));
+ EXPECT_STREQ("PBXProject", ToString(PBXProjectClass));
+ EXPECT_STREQ("PBXSourcesBuildPhase", ToString(PBXSourcesBuildPhaseClass));
+ EXPECT_STREQ("PBXTargetDependency", ToString(PBXTargetDependencyClass));
+ EXPECT_STREQ("XCBuildConfiguration", ToString(XCBuildConfigurationClass));
+ EXPECT_STREQ("XCConfigurationList", ToString(XCConfigurationListClass));
+ EXPECT_STREQ("PBXShellScriptBuildPhase",
+ ToString(PBXShellScriptBuildPhaseClass));
+}
+
+// Tests the mapping between PBXObject and it's name as a string.
+TEST(XcodeObject, PBXSourcesBuildPhaseName) {
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
+ GetPBXSourcesBuildPhaseObject();
+ EXPECT_EQ("Sources", pbx_sources_build_phase->Name());
+}
+
+TEST(XcodeObject, PBXFrameworksBuildPhaseName) {
+ std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase =
+ GetPBXFrameworksBuildPhaseObject();
+ EXPECT_EQ("Frameworks", pbx_frameworks_build_phase->Name());
+}
+
+TEST(XcodeObject, PBXShellScriptBuildPhaseName) {
+ std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase =
+ GetPBXShellScriptBuildPhaseObject();
+ EXPECT_EQ("Action \"Compile and copy name via ninja\"",
+ pbx_shell_script_build_phase->Name());
+}
+
+TEST(XcodeObject, PBXGroupName) {
+ PBXGroup pbx_group_with_name(std::string(), "name");
+ EXPECT_EQ("name", pbx_group_with_name.Name());
+
+ PBXGroup pbx_group_with_path("path", std::string());
+ EXPECT_EQ("path", pbx_group_with_path.Name());
+
+ PBXGroup pbx_group_empty{std::string(), std::string()};
+ EXPECT_EQ(std::string(), pbx_group_empty.Name());
+}
+
+TEST(XcodeObject, PBXProjectName) {
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ EXPECT_EQ("project", pbx_project->Name());
+}
+
+TEST(XcodeObject, PBXFileReferenceName) {
+ std::unique_ptr<PBXFileReference> pbx_file_reference =
+ GetPBXFileReferenceObject();
+ EXPECT_EQ("product.app", pbx_file_reference->Name());
+}
+
+TEST(XcodeObject, PBXBuildFileName) {
+ std::unique_ptr<PBXFileReference> pbx_file_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
+ GetPBXSourcesBuildPhaseObject();
+ std::unique_ptr<PBXBuildFile> pbx_build_file = GetPBXBuildFileObject(
+ pbx_file_reference.get(), pbx_sources_build_phase.get());
+ EXPECT_EQ("product.app in Sources", pbx_build_file->Name());
+}
+
+TEST(XcodeObject, PBXAggregateTargetName) {
+ std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target =
+ GetPBXAggregateTargetObject();
+ EXPECT_EQ("target_name", pbx_aggregate_target->Name());
+}
+
+TEST(XcodeObject, PBXNativeTargetName) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ EXPECT_EQ("target_name", pbx_native_target->Name());
+}
+
+TEST(XcodeObject, PBXContainerItemProxyName) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
+ GetPBXContainerItemProxyObject(pbx_project.get(),
+ pbx_native_target.get());
+ EXPECT_EQ("PBXContainerItemProxy", pbx_container_item_proxy->Name());
+}
+
+TEST(XcodeObject, PBXTargetDependencyName) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
+ GetPBXContainerItemProxyObject(pbx_project.get(),
+ pbx_native_target.get());
+ std::unique_ptr<PBXTargetDependency> pbx_target_dependency =
+ GetPBXTargetDependencyObject(pbx_native_target.get(),
+ std::move(pbx_container_item_proxy));
+ EXPECT_EQ("PBXTargetDependency", pbx_target_dependency->Name());
+}
+
+TEST(XcodeObject, XCBuildConfigurationName) {
+ std::unique_ptr<XCBuildConfiguration> xc_build_configuration =
+ GetXCBuildConfigurationObject();
+ EXPECT_EQ("config_name", xc_build_configuration->Name());
+}
+
+TEST(XcodeObject, XCConfigurationListName) {
+ std::unique_ptr<PBXFileReference> product_reference =
+ GetPBXFileReferenceObject();
+ std::unique_ptr<PBXNativeTarget> pbx_native_target =
+ GetPBXNativeTargetObject(product_reference.get());
+ std::unique_ptr<XCConfigurationList> xc_configuration_list =
+ GetXCConfigurationListObject(pbx_native_target.get());
+ EXPECT_EQ("Build configuration list for PBXNativeTarget \"target_name\"",
+ xc_configuration_list->Name());
+}
diff --git a/gn/src/gn/xcode_writer.cc b/gn/src/gn/xcode_writer.cc
new file mode 100644
index 00000000000..a5650fa351d
--- /dev/null
+++ b/gn/src/gn/xcode_writer.cc
@@ -0,0 +1,1029 @@
+// Copyright 2016 The Chromium 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 "gn/xcode_writer.h"
+
+#include <iomanip>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "gn/args.h"
+#include "gn/build_settings.h"
+#include "gn/builder.h"
+#include "gn/commands.h"
+#include "gn/deps_iterator.h"
+#include "gn/filesystem_utils.h"
+#include "gn/item.h"
+#include "gn/loader.h"
+#include "gn/scheduler.h"
+#include "gn/settings.h"
+#include "gn/source_file.h"
+#include "gn/substitution_writer.h"
+#include "gn/target.h"
+#include "gn/value.h"
+#include "gn/variables.h"
+#include "gn/xcode_object.h"
+
+namespace {
+
+// This is the template of the script used to build the target. It invokes
+// ninja (supporting --ninja-executable parameter), parsing ninja's output
+// using a regular expression looking for relative path to the source root
+// from root_build_dir that are at the start of a path and converting them
+// to absolute paths (use str.replace(rel_root_src, abs_root_src) would be
+// simpler but would fail if rel_root_src is present multiple time in the
+// path).
+const char kBuildScriptTemplate[] = R"(
+import re
+import os
+import subprocess
+import sys
+
+rel_root_src = '%s'
+abs_root_src = os.path.abspath(rel_root_src) + '/'
+
+build_target = '%s'
+ninja_binary = '%s'
+ninja_params = [ '-C', '.' ]
+
+%s
+
+if build_target:
+ ninja_params.append(build_target)
+ print('Compile "' + build_target + '" via ninja')
+else:
+ print('Compile "all" via ninja')
+
+process = subprocess.Popen(
+ [ ninja_binary ] + ninja_params,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True,
+ encoding='utf-8',
+ env=environ)
+
+pattern = re.compile('(?<!/)' + re.escape(rel_root_src))
+
+for line in iter(process.stdout.readline, ''):
+ while True:
+ match = pattern.search(line)
+ if not match:
+ break
+ span = match.span()
+ print(line[:span[0]], end='')
+ print(abs_root_src, end='')
+ line = line[span[1]:]
+ print(line, flush=True, end='')
+
+process.wait()
+
+sys.exit(process.returncode)
+)";
+
+enum TargetOsType {
+ WRITER_TARGET_OS_IOS,
+ WRITER_TARGET_OS_MACOS,
+};
+
+const char* kXCTestFileSuffixes[] = {
+ "egtest.m", "egtest.mm", "xctest.m", "xctest.mm", "UITests.m", "UITests.mm",
+};
+
+const char kXCTestModuleTargetNamePostfix[] = "_module";
+const char kXCUITestRunnerTargetNamePostfix[] = "_runner";
+
+struct SafeEnvironmentVariableInfo {
+ const char* name;
+ bool capture_at_generation;
+};
+
+SafeEnvironmentVariableInfo kSafeEnvironmentVariables[] = {
+ {"HOME", true},
+ {"LANG", true},
+ {"PATH", true},
+ {"USER", true},
+ {"TMPDIR", false},
+ {"ICECC_VERSION", true},
+ {"ICECC_CLANG_REMOTE_CPP", true}};
+
+TargetOsType GetTargetOs(const Args& args) {
+ const Value* target_os_value = args.GetArgOverride(variables::kTargetOs);
+ if (target_os_value) {
+ if (target_os_value->type() == Value::STRING) {
+ if (target_os_value->string_value() == "ios")
+ return WRITER_TARGET_OS_IOS;
+ }
+ }
+ return WRITER_TARGET_OS_MACOS;
+}
+
+std::string GetNinjaExecutable(const std::string& ninja_executable) {
+ return ninja_executable.empty() ? "ninja" : ninja_executable;
+}
+
+std::string ComputeScriptEnviron(base::Environment* environment) {
+ std::stringstream buffer;
+ buffer << "environ = {}";
+ for (const auto& variable : kSafeEnvironmentVariables) {
+ buffer << "\nenviron['" << variable.name << "'] = ";
+ if (variable.capture_at_generation) {
+ std::string value;
+ environment->GetVar(variable.name, &value);
+ buffer << "'" << value << "'";
+ } else {
+ buffer << "os.environ.get('" << variable.name << "', '')";
+ }
+ }
+ return buffer.str();
+}
+
+std::string GetBuildScript(const std::string& target_name,
+ const std::string& ninja_executable,
+ const std::string& root_src_dir,
+ base::Environment* environment) {
+ std::string environ_script = ComputeScriptEnviron(environment);
+ std::string ninja = GetNinjaExecutable(ninja_executable);
+ return base::StringPrintf(kBuildScriptTemplate, root_src_dir.c_str(),
+ target_name.c_str(), ninja.c_str(),
+ environ_script.c_str());
+}
+
+bool IsApplicationTarget(const Target* target) {
+ return target->output_type() == Target::CREATE_BUNDLE &&
+ target->bundle_data().product_type() ==
+ "com.apple.product-type.application";
+}
+
+bool IsXCUITestRunnerTarget(const Target* target) {
+ return IsApplicationTarget(target) &&
+ base::EndsWith(target->label().name(),
+ kXCUITestRunnerTargetNamePostfix,
+ base::CompareCase::SENSITIVE);
+}
+
+bool IsXCTestModuleTarget(const Target* target) {
+ return target->output_type() == Target::CREATE_BUNDLE &&
+ target->bundle_data().product_type() ==
+ "com.apple.product-type.bundle.unit-test" &&
+ base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix,
+ base::CompareCase::SENSITIVE);
+}
+
+bool IsXCUITestModuleTarget(const Target* target) {
+ return target->output_type() == Target::CREATE_BUNDLE &&
+ target->bundle_data().product_type() ==
+ "com.apple.product-type.bundle.ui-testing" &&
+ base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix,
+ base::CompareCase::SENSITIVE);
+}
+
+bool IsXCTestFile(const SourceFile& file) {
+ std::string file_name = file.GetName();
+ for (size_t i = 0; i < std::size(kXCTestFileSuffixes); ++i) {
+ if (base::EndsWith(file_name, kXCTestFileSuffixes[i],
+ base::CompareCase::SENSITIVE)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Finds the application target from its target name.
+std::optional<std::pair<const Target*, PBXNativeTarget*>>
+FindApplicationTargetByName(
+ const ParseNode* node,
+ const std::string& target_name,
+ const std::map<const Target*, PBXNativeTarget*>& targets,
+ Err* err) {
+ for (auto& pair : targets) {
+ const Target* target = pair.first;
+ if (target->label().name() == target_name) {
+ if (!IsApplicationTarget(target)) {
+ *err = Err(node, "host application target \"" + target_name +
+ "\" not an application bundle");
+ return std::nullopt;
+ }
+ DCHECK(pair.first);
+ DCHECK(pair.second);
+ return pair;
+ }
+ }
+ *err =
+ Err(node, "cannot find host application bundle \"" + target_name + "\"");
+ return std::nullopt;
+}
+
+// Adds |base_pbxtarget| as a dependency of |dependent_pbxtarget| in the
+// generated Xcode project.
+void AddPBXTargetDependency(const PBXTarget* base_pbxtarget,
+ PBXTarget* dependent_pbxtarget,
+ const PBXProject* project) {
+ auto container_item_proxy =
+ std::make_unique<PBXContainerItemProxy>(project, base_pbxtarget);
+ auto dependency = std::make_unique<PBXTargetDependency>(
+ base_pbxtarget, std::move(container_item_proxy));
+
+ dependent_pbxtarget->AddDependency(std::move(dependency));
+}
+
+// Helper class to resolve list of XCTest files per target.
+//
+// Uses a cache of file found per intermediate targets to reduce the need
+// to shared targets multiple times. It is recommended to reuse the same
+// object to resolve all the targets for a project.
+class XCTestFilesResolver {
+ public:
+ XCTestFilesResolver();
+ ~XCTestFilesResolver();
+
+ // Returns a set of all XCTest files for |target|. The returned reference
+ // may be invalidated the next time this method is called.
+ const SourceFileSet& SearchFilesForTarget(const Target* target);
+
+ private:
+ std::map<const Target*, SourceFileSet> cache_;
+};
+
+XCTestFilesResolver::XCTestFilesResolver() = default;
+
+XCTestFilesResolver::~XCTestFilesResolver() = default;
+
+const SourceFileSet& XCTestFilesResolver::SearchFilesForTarget(
+ const Target* target) {
+ // Early return if already visited and processed.
+ auto iter = cache_.find(target);
+ if (iter != cache_.end())
+ return iter->second;
+
+ SourceFileSet xctest_files;
+ for (const SourceFile& file : target->sources()) {
+ if (IsXCTestFile(file)) {
+ xctest_files.insert(file);
+ }
+ }
+
+ // Call recursively on public and private deps.
+ for (const auto& t : target->public_deps()) {
+ const SourceFileSet& deps_xctest_files = SearchFilesForTarget(t.ptr);
+ xctest_files.insert(deps_xctest_files.begin(), deps_xctest_files.end());
+ }
+
+ for (const auto& t : target->private_deps()) {
+ const SourceFileSet& deps_xctest_files = SearchFilesForTarget(t.ptr);
+ xctest_files.insert(deps_xctest_files.begin(), deps_xctest_files.end());
+ }
+
+ auto insert = cache_.insert(std::make_pair(target, xctest_files));
+ DCHECK(insert.second);
+ return insert.first->second;
+}
+
+// Add xctest files to the "Compiler Sources" of corresponding test module
+// native targets.
+void AddXCTestFilesToTestModuleTarget(const std::vector<SourceFile>& sources,
+ PBXNativeTarget* native_target,
+ PBXProject* project,
+ SourceDir source_dir,
+ const BuildSettings* build_settings) {
+ for (const SourceFile& source : sources) {
+ const std::string source_path = RebasePath(
+ source.value(), source_dir, build_settings->root_path_utf8());
+ project->AddSourceFile(source_path, source_path, native_target);
+ }
+}
+
+// Helper class to collect all PBXObject per class.
+class CollectPBXObjectsPerClassHelper : public PBXObjectVisitorConst {
+ public:
+ CollectPBXObjectsPerClassHelper() = default;
+
+ void Visit(const PBXObject* object) override {
+ DCHECK(object);
+ objects_per_class_[object->Class()].push_back(object);
+ }
+
+ const std::map<PBXObjectClass, std::vector<const PBXObject*>>&
+ objects_per_class() const {
+ return objects_per_class_;
+ }
+
+ private:
+ std::map<PBXObjectClass, std::vector<const PBXObject*>> objects_per_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(CollectPBXObjectsPerClassHelper);
+};
+
+std::map<PBXObjectClass, std::vector<const PBXObject*>>
+CollectPBXObjectsPerClass(const PBXProject* project) {
+ CollectPBXObjectsPerClassHelper visitor;
+ project->Visit(visitor);
+ return visitor.objects_per_class();
+}
+
+// Helper class to assign unique ids to all PBXObject.
+class RecursivelyAssignIdsHelper : public PBXObjectVisitor {
+ public:
+ RecursivelyAssignIdsHelper(const std::string& seed)
+ : seed_(seed), counter_(0) {}
+
+ void Visit(PBXObject* object) override {
+ std::stringstream buffer;
+ buffer << seed_ << " " << object->Name() << " " << counter_;
+ std::string hash = base::SHA1HashString(buffer.str());
+ DCHECK_EQ(hash.size() % 4, 0u);
+
+ uint32_t id[3] = {0, 0, 0};
+ const uint32_t* ptr = reinterpret_cast<const uint32_t*>(hash.data());
+ for (size_t i = 0; i < hash.size() / 4; i++)
+ id[i % 3] ^= ptr[i];
+
+ object->SetId(base::HexEncode(id, sizeof(id)));
+ ++counter_;
+ }
+
+ private:
+ std::string seed_;
+ int64_t counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecursivelyAssignIdsHelper);
+};
+
+void RecursivelyAssignIds(PBXProject* project) {
+ RecursivelyAssignIdsHelper visitor(project->Name());
+ project->Visit(visitor);
+}
+
+// Returns a configuration name derived from the build directory. This gives
+// standard names if using the Xcode convention of naming the build directory
+// out/$configuration-$platform (e.g. out/Debug-iphonesimulator).
+std::string ConfigNameFromBuildSettings(const BuildSettings* build_settings) {
+ std::string config_name = FilePathToUTF8(build_settings->build_dir()
+ .Resolve(base::FilePath())
+ .StripTrailingSeparators()
+ .BaseName());
+
+ std::string::size_type separator = config_name.find('-');
+ if (separator != std::string::npos)
+ config_name = config_name.substr(0, separator);
+
+ DCHECK(!config_name.empty());
+ return config_name;
+}
+
+// Returns the path to root_src_dir from settings.
+std::string SourcePathFromBuildSettings(const BuildSettings* build_settings) {
+ return RebasePath("//", build_settings->build_dir());
+}
+
+// Returns the default attributes for the project from settings.
+PBXAttributes ProjectAttributesFromBuildSettings(
+ const BuildSettings* build_settings) {
+ const TargetOsType target_os = GetTargetOs(build_settings->build_args());
+
+ PBXAttributes attributes;
+ switch (target_os) {
+ case WRITER_TARGET_OS_IOS:
+ attributes["SDKROOT"] = "iphoneos";
+ attributes["TARGETED_DEVICE_FAMILY"] = "1,2";
+ break;
+ case WRITER_TARGET_OS_MACOS:
+ attributes["SDKROOT"] = "macosx";
+ break;
+ }
+
+ // Xcode complains that the project needs to be upgraded if those keys are
+ // not set. Since the generated Xcode project is only used for debugging
+ // and the source of truth for build settings is the .gn files themselves,
+ // we can safely set them in the project as they won't be used by "ninja".
+ attributes["ALWAYS_SEARCH_USER_PATHS"] = "NO";
+ attributes["CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED"] = "YES";
+ attributes["CLANG_WARN__DUPLICATE_METHOD_MATCH"] = "YES";
+ attributes["CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING"] = "YES";
+ attributes["CLANG_WARN_BOOL_CONVERSION"] = "YES";
+ attributes["CLANG_WARN_COMMA"] = "YES";
+ attributes["CLANG_WARN_CONSTANT_CONVERSION"] = "YES";
+ attributes["CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS"] = "YES";
+ attributes["CLANG_WARN_EMPTY_BODY"] = "YES";
+ attributes["CLANG_WARN_ENUM_CONVERSION"] = "YES";
+ attributes["CLANG_WARN_INFINITE_RECURSION"] = "YES";
+ attributes["CLANG_WARN_INT_CONVERSION"] = "YES";
+ attributes["CLANG_WARN_NON_LITERAL_NULL_CONVERSION"] = "YES";
+ attributes["CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF"] = "YES";
+ attributes["CLANG_WARN_OBJC_LITERAL_CONVERSION"] = "YES";
+ attributes["CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER"] = "YES";
+ attributes["CLANG_WARN_RANGE_LOOP_ANALYSIS"] = "YES";
+ attributes["CLANG_WARN_STRICT_PROTOTYPES"] = "YES";
+ attributes["CLANG_WARN_SUSPICIOUS_MOVE"] = "YES";
+ attributes["CLANG_WARN_UNREACHABLE_CODE"] = "YES";
+ attributes["ENABLE_STRICT_OBJC_MSGSEND"] = "YES";
+ attributes["ENABLE_TESTABILITY"] = "YES";
+ attributes["GCC_NO_COMMON_BLOCKS"] = "YES";
+ attributes["GCC_WARN_64_TO_32_BIT_CONVERSION"] = "YES";
+ attributes["GCC_WARN_ABOUT_RETURN_TYPE"] = "YES";
+ attributes["GCC_WARN_UNDECLARED_SELECTOR"] = "YES";
+ attributes["GCC_WARN_UNINITIALIZED_AUTOS"] = "YES";
+ attributes["GCC_WARN_UNUSED_FUNCTION"] = "YES";
+ attributes["GCC_WARN_UNUSED_VARIABLE"] = "YES";
+ attributes["ONLY_ACTIVE_ARCH"] = "YES";
+
+ return attributes;
+}
+
+} // namespace
+
+// Class representing the workspace embedded in an xcodeproj file used to
+// configure the build settings shared by all targets in the project (used
+// to configure the build system).
+class XcodeWorkspace {
+ public:
+ XcodeWorkspace(const BuildSettings* build_settings,
+ XcodeWriter::Options options);
+ ~XcodeWorkspace();
+
+ XcodeWorkspace(const XcodeWorkspace&) = delete;
+ XcodeWorkspace& operator=(const XcodeWorkspace&) = delete;
+
+ // Generates the .xcworkspace files to disk.
+ bool WriteWorkspace(const std::string& name, Err* err) const;
+
+ private:
+ // Writes the workspace data file.
+ bool WriteWorkspaceDataFile(const std::string& name, Err* err) const;
+
+ // Writes the settings file.
+ bool WriteSettingsFile(const std::string& name, Err* err) const;
+
+ const BuildSettings* build_settings_ = nullptr;
+ XcodeWriter::Options options_;
+};
+
+XcodeWorkspace::XcodeWorkspace(const BuildSettings* build_settings,
+ XcodeWriter::Options options)
+ : build_settings_(build_settings), options_(options) {}
+
+XcodeWorkspace::~XcodeWorkspace() = default;
+
+bool XcodeWorkspace::WriteWorkspace(const std::string& name, Err* err) const {
+ return WriteWorkspaceDataFile(name, err) && WriteSettingsFile(name, err);
+}
+
+bool XcodeWorkspace::WriteWorkspaceDataFile(const std::string& name,
+ Err* err) const {
+ const SourceFile source_file =
+ build_settings_->build_dir().ResolveRelativeFile(
+ Value(nullptr, name + "/contents.xcworkspacedata"), err);
+ if (source_file.is_null())
+ return false;
+
+ std::stringstream out;
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ << "<Workspace\n"
+ << " version = \"1.0\">\n"
+ << " <FileRef\n"
+ << " location = \"self:\">\n"
+ << " </FileRef>\n"
+ << "</Workspace>\n";
+
+ return WriteFileIfChanged(build_settings_->GetFullPath(source_file),
+ out.str(), err);
+}
+
+bool XcodeWorkspace::WriteSettingsFile(const std::string& name,
+ Err* err) const {
+ const SourceFile source_file =
+ build_settings_->build_dir().ResolveRelativeFile(
+ Value(nullptr, name + "/xcshareddata/WorkspaceSettings.xcsettings"),
+ err);
+ if (source_file.is_null())
+ return false;
+
+ std::stringstream out;
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
+ << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ << "<plist version=\"1.0\">\n"
+ << "<dict>\n";
+
+ switch (options_.build_system) {
+ case XcodeBuildSystem::kLegacy:
+ out << "\t<key>BuildSystemType</key>\n"
+ << "\t<string>Original</string>\n";
+ break;
+ case XcodeBuildSystem::kNew:
+ break;
+ }
+
+ out << "</dict>\n"
+ << "</plist>\n";
+
+ return WriteFileIfChanged(build_settings_->GetFullPath(source_file),
+ out.str(), err);
+}
+
+// Class responsible for constructing and writing the .xcodeproj from the
+// targets known to gn. It currently requires using the "Legacy build system"
+// so it will embed an .xcworkspace file to force the setting.
+class XcodeProject {
+ public:
+ XcodeProject(const BuildSettings* build_settings,
+ XcodeWriter::Options options);
+ ~XcodeProject();
+
+ // Recursively finds "source" files from |builder| and adds them to the
+ // project (this includes more than just text source files, e.g. images
+ // in resources, ...).
+ bool AddSourcesFromBuilder(const Builder& builder, Err* err);
+
+ // Recursively finds targets from |builder| and adds them to the project.
+ // Only targets of type CREATE_BUNDLE or EXECUTABLE are kept since they
+ // are the only one that can be run and thus debugged from Xcode.
+ bool AddTargetsFromBuilder(const Builder& builder, Err* err);
+
+ // Assigns ids to all PBXObject that were added to the project. Must be
+ // called before calling WriteFile().
+ bool AssignIds(Err* err);
+
+ // Generates the project file and the .xcodeproj file to disk if updated
+ // (i.e. if the generated project is identical to the currently existing
+ // one, it is not overwritten).
+ bool WriteFile(Err* err) const;
+
+ private:
+ // Finds all targets that needs to be generated for the project (applies
+ // the filter passed via |options|).
+ std::optional<std::vector<const Target*>> GetTargetsFromBuilder(
+ const Builder& builder,
+ Err* err) const;
+
+ // Adds a target of type EXECUTABLE to the project.
+ PBXNativeTarget* AddBinaryTarget(const Target* target,
+ base::Environment* env,
+ Err* err);
+
+ // Adds a target of type CREATE_BUNDLE to the project.
+ PBXNativeTarget* AddBundleTarget(const Target* target,
+ base::Environment* env,
+ Err* err);
+
+ // Adds the XCTest source files for all test xctest or xcuitest module target
+ // to allow Xcode to index the list of tests (thus allowing to run individual
+ // tests from Xcode UI).
+ bool AddCXTestSourceFilesForTestModuleTargets(
+ const std::map<const Target*, PBXNativeTarget*>& bundle_targets,
+ Err* err);
+
+ // Adds the corresponding test application target as dependency of xctest or
+ // xcuitest module target in the generated Xcode project.
+ bool AddDependencyTargetsForTestModuleTargets(
+ const std::map<const Target*, PBXNativeTarget*>& bundle_targets,
+ Err* err);
+
+ // Generates the content of the .xcodeproj file into |out|.
+ void WriteFileContent(std::ostream& out) const;
+
+ // Returns whether the file should be added to the project.
+ bool ShouldIncludeFileInProject(const SourceFile& source) const;
+
+ const BuildSettings* build_settings_;
+ XcodeWriter::Options options_;
+ PBXProject project_;
+};
+
+XcodeProject::XcodeProject(const BuildSettings* build_settings,
+ XcodeWriter::Options options)
+ : build_settings_(build_settings),
+ options_(options),
+ project_(options.project_name,
+ ConfigNameFromBuildSettings(build_settings),
+ SourcePathFromBuildSettings(build_settings),
+ ProjectAttributesFromBuildSettings(build_settings)) {}
+
+XcodeProject::~XcodeProject() = default;
+
+bool XcodeProject::ShouldIncludeFileInProject(const SourceFile& source) const {
+ if (IsStringInOutputDir(build_settings_->build_dir(), source.value()))
+ return false;
+
+ if (IsPathAbsolute(source.value()))
+ return false;
+
+ return true;
+}
+
+bool XcodeProject::AddSourcesFromBuilder(const Builder& builder, Err* err) {
+ SourceFileSet sources;
+
+ // Add sources from all targets.
+ for (const Target* target : builder.GetAllResolvedTargets()) {
+ for (const SourceFile& source : target->sources()) {
+ if (ShouldIncludeFileInProject(source))
+ sources.insert(source);
+ }
+
+ for (const SourceFile& source : target->config_values().inputs()) {
+ if (ShouldIncludeFileInProject(source))
+ sources.insert(source);
+ }
+
+ for (const SourceFile& source : target->public_headers()) {
+ if (ShouldIncludeFileInProject(source))
+ sources.insert(source);
+ }
+
+ if (target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH) {
+ if (ShouldIncludeFileInProject(target->action_values().script()))
+ sources.insert(target->action_values().script());
+ }
+ }
+
+ // Add BUILD.gn and *.gni for targets, configs and toolchains.
+ for (const Item* item : builder.GetAllResolvedItems()) {
+ if (!item->AsConfig() && !item->AsTarget() && !item->AsToolchain())
+ continue;
+
+ const SourceFile build = builder.loader()->BuildFileForLabel(item->label());
+ if (ShouldIncludeFileInProject(build))
+ sources.insert(build);
+
+ for (const SourceFile& source :
+ item->settings()->import_manager().GetImportedFiles()) {
+ if (ShouldIncludeFileInProject(source))
+ sources.insert(source);
+ }
+ }
+
+ // Add other files read by gn (the main dotfile, exec_script scripts, ...).
+ for (const auto& path : g_scheduler->GetGenDependencies()) {
+ if (!build_settings_->root_path().IsParent(path))
+ continue;
+
+ const std::string as8bit = path.As8Bit();
+ const SourceFile source(
+ "//" + as8bit.substr(build_settings_->root_path().value().size() + 1));
+
+ if (ShouldIncludeFileInProject(source))
+ sources.insert(source);
+ }
+
+ // Sort files to ensure deterministic generation of the project file (and
+ // nicely sorted file list in Xcode).
+ std::vector<SourceFile> sorted_sources(sources.begin(), sources.end());
+ std::sort(sorted_sources.begin(), sorted_sources.end());
+
+ const SourceDir source_dir("//");
+ for (const SourceFile& source : sorted_sources) {
+ const std::string source_file = RebasePath(
+ source.value(), source_dir, build_settings_->root_path_utf8());
+ project_.AddSourceFileToIndexingTarget(source_file, source_file);
+ }
+
+ return true;
+}
+
+bool XcodeProject::AddTargetsFromBuilder(const Builder& builder, Err* err) {
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+
+ const std::string root_src_dir =
+ RebasePath("//", build_settings_->build_dir());
+ project_.AddAggregateTarget("All", GetBuildScript(options_.root_target_name,
+ options_.ninja_executable,
+ root_src_dir, env.get()));
+
+ const std::optional<std::vector<const Target*>> targets =
+ GetTargetsFromBuilder(builder, err);
+ if (!targets)
+ return false;
+
+ std::map<const Target*, PBXNativeTarget*> bundle_targets;
+
+ const TargetOsType target_os = GetTargetOs(build_settings_->build_args());
+
+ for (const Target* target : *targets) {
+ PBXNativeTarget* native_target = nullptr;
+ switch (target->output_type()) {
+ case Target::EXECUTABLE:
+ if (target_os == WRITER_TARGET_OS_IOS)
+ continue;
+
+ native_target = AddBinaryTarget(target, env.get(), err);
+ if (!native_target)
+ return false;
+
+ break;
+
+ case Target::CREATE_BUNDLE: {
+ if (target->bundle_data().product_type().empty())
+ continue;
+
+ // For XCUITest, two CREATE_BUNDLE targets are generated:
+ // ${target_name}_runner and ${target_name}_module, however, Xcode
+ // requires only one target named ${target_name} to run tests.
+ if (IsXCUITestRunnerTarget(target))
+ continue;
+
+ native_target = AddBundleTarget(target, env.get(), err);
+ if (!native_target)
+ return false;
+
+ bundle_targets.insert(std::make_pair(target, native_target));
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (!AddCXTestSourceFilesForTestModuleTargets(bundle_targets, err))
+ return false;
+
+ // Adding the corresponding test application target as a dependency of xctest
+ // or xcuitest module target in the generated Xcode project so that the
+ // application target is re-compiled when compiling the test module target.
+ if (!AddDependencyTargetsForTestModuleTargets(bundle_targets, err))
+ return false;
+
+ return true;
+}
+
+bool XcodeProject::AddCXTestSourceFilesForTestModuleTargets(
+ const std::map<const Target*, PBXNativeTarget*>& bundle_targets,
+ Err* err) {
+ const SourceDir source_dir("//");
+
+ // Needs to search for xctest files under the application targets, and this
+ // variable is used to store the results of visited targets, thus making the
+ // search more efficient.
+ XCTestFilesResolver resolver;
+
+ for (const auto& pair : bundle_targets) {
+ const Target* target = pair.first;
+ if (!IsXCTestModuleTarget(target) && !IsXCUITestModuleTarget(target))
+ continue;
+
+ // For XCTest, test files are compiled into the application bundle.
+ // For XCUITest, test files are compiled into the test module bundle.
+ const Target* target_with_xctest_files = nullptr;
+ if (IsXCTestModuleTarget(target)) {
+ auto app_pair = FindApplicationTargetByName(
+ target->defined_from(),
+ target->bundle_data().xcode_test_application_name(), bundle_targets,
+ err);
+ if (!app_pair)
+ return false;
+ target_with_xctest_files = app_pair.value().first;
+ } else {
+ DCHECK(IsXCUITestModuleTarget(target));
+ target_with_xctest_files = target;
+ }
+
+ const SourceFileSet& sources =
+ resolver.SearchFilesForTarget(target_with_xctest_files);
+
+ // Sort files to ensure deterministic generation of the project file (and
+ // nicely sorted file list in Xcode).
+ std::vector<SourceFile> sorted_sources(sources.begin(), sources.end());
+ std::sort(sorted_sources.begin(), sorted_sources.end());
+
+ // Add xctest files to the "Compiler Sources" of corresponding xctest
+ // and xcuitest native targets for proper indexing and for discovery of
+ // tests function.
+ AddXCTestFilesToTestModuleTarget(sorted_sources, pair.second, &project_,
+ source_dir, build_settings_);
+ }
+
+ return true;
+}
+
+bool XcodeProject::AddDependencyTargetsForTestModuleTargets(
+ const std::map<const Target*, PBXNativeTarget*>& bundle_targets,
+ Err* err) {
+ for (const auto& pair : bundle_targets) {
+ const Target* target = pair.first;
+ if (!IsXCTestModuleTarget(target) && !IsXCUITestModuleTarget(target))
+ continue;
+
+ auto app_pair = FindApplicationTargetByName(
+ target->defined_from(),
+ target->bundle_data().xcode_test_application_name(), bundle_targets,
+ err);
+ if (!app_pair)
+ return false;
+
+ AddPBXTargetDependency(app_pair.value().second, pair.second, &project_);
+ }
+
+ return true;
+}
+
+bool XcodeProject::AssignIds(Err* err) {
+ RecursivelyAssignIds(&project_);
+ return true;
+}
+
+bool XcodeProject::WriteFile(Err* err) const {
+ DCHECK(!project_.id().empty());
+
+ SourceFile pbxproj_file = build_settings_->build_dir().ResolveRelativeFile(
+ Value(nullptr, project_.Name() + ".xcodeproj/project.pbxproj"), err);
+ if (pbxproj_file.is_null())
+ return false;
+
+ std::stringstream pbxproj_string_out;
+ WriteFileContent(pbxproj_string_out);
+
+ if (!WriteFileIfChanged(build_settings_->GetFullPath(pbxproj_file),
+ pbxproj_string_out.str(), err)) {
+ return false;
+ }
+
+ XcodeWorkspace workspace(build_settings_, options_);
+ return workspace.WriteWorkspace(
+ project_.Name() + ".xcodeproj/project.xcworkspace", err);
+}
+
+std::optional<std::vector<const Target*>> XcodeProject::GetTargetsFromBuilder(
+ const Builder& builder,
+ Err* err) const {
+ std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
+
+ // Filter targets according to the dir_filters_string if defined.
+ if (!options_.dir_filters_string.empty()) {
+ std::vector<LabelPattern> filters;
+ if (!commands::FilterPatternsFromString(
+ build_settings_, options_.dir_filters_string, &filters, err)) {
+ return std::nullopt;
+ }
+
+ std::vector<const Target*> unfiltered_targets;
+ std::swap(unfiltered_targets, all_targets);
+
+ commands::FilterTargetsByPatterns(unfiltered_targets, filters,
+ &all_targets);
+ }
+
+ // Filter out all target of type EXECUTABLE that are direct dependency of
+ // a BUNDLE_DATA target (under the assumption that they will be part of a
+ // CREATE_BUNDLE target generating an application bundle).
+ std::set<const Target*> targets(all_targets.begin(), all_targets.end());
+ for (const Target* target : all_targets) {
+ if (!target->settings()->is_default())
+ continue;
+
+ if (target->output_type() != Target::BUNDLE_DATA)
+ continue;
+
+ for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
+ if (pair.ptr->output_type() != Target::EXECUTABLE)
+ continue;
+
+ auto iter = targets.find(pair.ptr);
+ if (iter != targets.end())
+ targets.erase(iter);
+ }
+ }
+
+ // Sort the list of targets per-label to get a consistent ordering of them
+ // in the generated Xcode project (and thus stability of the file generated).
+ std::vector<const Target*> sorted_targets(targets.begin(), targets.end());
+ std::sort(sorted_targets.begin(), sorted_targets.end(),
+ [](const Target* lhs, const Target* rhs) {
+ return lhs->label() < rhs->label();
+ });
+
+ return sorted_targets;
+}
+
+PBXNativeTarget* XcodeProject::AddBinaryTarget(const Target* target,
+ base::Environment* env,
+ Err* err) {
+ DCHECK_EQ(target->output_type(), Target::EXECUTABLE);
+
+ std::string output_dir = target->output_dir().value();
+ if (output_dir.empty()) {
+ const Tool* tool = target->toolchain()->GetToolForTargetFinalOutput(target);
+ if (!tool) {
+ std::string tool_name = Tool::GetToolTypeForTargetFinalOutput(target);
+ *err = Err(nullptr, tool_name + " tool not defined",
+ "The toolchain " +
+ target->toolchain()->label().GetUserVisibleName(false) +
+ " used by target " +
+ target->label().GetUserVisibleName(false) +
+ " doesn't define a \"" + tool_name + "\" tool.");
+ return nullptr;
+ }
+ output_dir = SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ target, tool, tool->default_output_dir())
+ .value();
+ } else {
+ output_dir = RebasePath(output_dir, build_settings_->build_dir());
+ }
+
+ const std::string root_src_dir =
+ RebasePath("//", build_settings_->build_dir());
+ return project_.AddNativeTarget(
+ target->label().name(), "compiled.mach-o.executable",
+ target->output_name().empty() ? target->label().name()
+ : target->output_name(),
+ "com.apple.product-type.tool", output_dir,
+ GetBuildScript(target->label().name(), options_.ninja_executable,
+ root_src_dir, env));
+}
+
+PBXNativeTarget* XcodeProject::AddBundleTarget(const Target* target,
+ base::Environment* env,
+ Err* err) {
+ DCHECK_EQ(target->output_type(), Target::CREATE_BUNDLE);
+
+ std::string pbxtarget_name = target->label().name();
+ if (IsXCUITestModuleTarget(target)) {
+ std::string target_name = target->label().name();
+ pbxtarget_name = target_name.substr(
+ 0, target_name.rfind(kXCTestModuleTargetNamePostfix));
+ }
+
+ PBXAttributes xcode_extra_attributes =
+ target->bundle_data().xcode_extra_attributes();
+ if (options_.build_system == XcodeBuildSystem::kLegacy) {
+ xcode_extra_attributes["CODE_SIGN_IDENTITY"] = "";
+ }
+
+ const std::string& target_output_name = RebasePath(
+ target->bundle_data().GetBundleRootDirOutput(target->settings()).value(),
+ build_settings_->build_dir());
+ const std::string output_dir = RebasePath(target->bundle_data()
+ .GetBundleDir(target->settings())
+ .value(),
+ build_settings_->build_dir());
+ const std::string root_src_dir =
+ RebasePath("//", build_settings_->build_dir());
+ return project_.AddNativeTarget(
+ pbxtarget_name, std::string(), target_output_name,
+ target->bundle_data().product_type(), output_dir,
+ GetBuildScript(pbxtarget_name, options_.ninja_executable, root_src_dir,
+ env),
+ xcode_extra_attributes);
+}
+
+void XcodeProject::WriteFileContent(std::ostream& out) const {
+ out << "// !$*UTF8*$!\n"
+ << "{\n"
+ << "\tarchiveVersion = 1;\n"
+ << "\tclasses = {\n"
+ << "\t};\n"
+ << "\tobjectVersion = 46;\n"
+ << "\tobjects = {\n";
+
+ for (auto& pair : CollectPBXObjectsPerClass(&project_)) {
+ out << "\n"
+ << "/* Begin " << ToString(pair.first) << " section */\n";
+ std::sort(pair.second.begin(), pair.second.end(),
+ [](const PBXObject* a, const PBXObject* b) {
+ return a->id() < b->id();
+ });
+ for (auto* object : pair.second) {
+ object->Print(out, 2);
+ }
+ out << "/* End " << ToString(pair.first) << " section */\n";
+ }
+
+ out << "\t};\n"
+ << "\trootObject = " << project_.Reference() << ";\n"
+ << "}\n";
+}
+
+// static
+bool XcodeWriter::RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ Options options,
+ Err* err) {
+ XcodeProject project(build_settings, options);
+ if (!project.AddSourcesFromBuilder(builder, err))
+ return false;
+
+ if (!project.AddTargetsFromBuilder(builder, err))
+ return false;
+
+ if (!project.AssignIds(err))
+ return false;
+
+ if (!project.WriteFile(err))
+ return false;
+
+ return true;
+}
diff --git a/gn/src/gn/xcode_writer.h b/gn/src/gn/xcode_writer.h
new file mode 100644
index 00000000000..f9342754b50
--- /dev/null
+++ b/gn/src/gn/xcode_writer.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_XCODE_WRITER_H_
+#define TOOLS_GN_XCODE_WRITER_H_
+
+#include <iosfwd>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+class Builder;
+class BuildSettings;
+class Err;
+
+enum class XcodeBuildSystem {
+ kLegacy,
+ kNew,
+};
+
+// Writes an Xcode workspace to build and debug code.
+class XcodeWriter {
+ public:
+ // Controls some parameters and behaviour of the RunAndWriteFiles().
+ struct Options {
+ // Name of the generated project file. Defaults to "all" is empty.
+ std::string project_name;
+
+ // Name of the ninja target to use for the "All" target in the generated
+ // project. If empty, no target will be passed to ninja which will thus
+ // try to build all defined targets.
+ std::string root_target_name;
+
+ // Name of the ninja executable. Defaults to "ninja" if empty.
+ std::string ninja_executable;
+
+ // If specified, should be a semicolon-separated list of label patterns.
+ // It will be used to filter the list of targets generated in the project
+ // (in the same way that the other filtering is done, source and header
+ // files for those target will still be listed in the generated project).
+ std::string dir_filters_string;
+
+ // Control which version of the build system should be used for the
+ // generated Xcode project.
+ XcodeBuildSystem build_system = XcodeBuildSystem::kLegacy;
+ };
+
+ // Writes an Xcode workspace with a single project file.
+ //
+ // The project will lists all files referenced for the build (including the
+ // sources, headers and some supporting files). The project can be used to
+ // build, develop and debug from Xcode (though adding files, changing build
+ // settings, etc. still needs to be done via BUILD.gn files).
+ //
+ // The list of targets is filtered to only include relevant targets for
+ // debugging (mostly binaries and bundles) so it is not possible to build
+ // individuals targets (i.e. source_set) via Xcode. This filtering is done
+ // to improve the performances when loading the solution in Xcode (project
+ // like Chromium cannot be opened if all targets are generated).
+ //
+ // The source and header files are still listed in the generated generated
+ // Xcode project, even if the target they are defined in are filtered (not
+ // doing so would make it less pleasant to use Xcode to debug without any
+ // significant performance improvement).
+ //
+ // Extra behaviour is controlled by the |options| parameter. See comments
+ // of the Options type for more informations.
+ //
+ // Returns true on success, fails on failure. |err| is set in that case.
+ static bool RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ Options options,
+ Err* err);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(XcodeWriter);
+};
+
+#endif // TOOLS_GN_XCODE_WRITER_H_
diff --git a/gn/src/gn/xml_element_writer.cc b/gn/src/gn/xml_element_writer.cc
new file mode 100644
index 00000000000..8525057b5e0
--- /dev/null
+++ b/gn/src/gn/xml_element_writer.cc
@@ -0,0 +1,114 @@
+// Copyright 2016 The Chromium 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 "gn/xml_element_writer.h"
+
+#include <memory>
+
+XmlAttributes::XmlAttributes() = default;
+
+XmlAttributes::XmlAttributes(const std::string_view& attr_key,
+ const std::string_view& attr_value) {
+ add(attr_key, attr_value);
+}
+
+XmlAttributes& XmlAttributes::add(const std::string_view& attr_key,
+ const std::string_view& attr_value) {
+ push_back(std::make_pair(attr_key, attr_value));
+ return *this;
+}
+
+XmlElementWriter::XmlElementWriter(std::ostream& out,
+ const std::string& tag,
+ const XmlAttributes& attributes)
+ : XmlElementWriter(out, tag, attributes, 0) {}
+
+XmlElementWriter::XmlElementWriter(std::ostream& out,
+ const std::string& tag,
+ const XmlAttributes& attributes,
+ int indent)
+ : out_(out),
+ tag_(tag),
+ indent_(indent),
+ opening_tag_finished_(false),
+ one_line_(true) {
+ out << std::string(indent, ' ') << '<' << tag;
+ for (auto attribute : attributes)
+ out << ' ' << attribute.first << "=\"" << attribute.second << '"';
+}
+
+XmlElementWriter::~XmlElementWriter() {
+ if (!opening_tag_finished_) {
+ // The XML spec does not require a space before the closing slash. However,
+ // Eclipse is unable to parse XML settings files if there is no space.
+ out_ << " />" << std::endl;
+ } else {
+ if (!one_line_)
+ out_ << std::string(indent_, ' ');
+ out_ << "</" << tag_ << '>' << std::endl;
+ }
+}
+
+void XmlElementWriter::Text(const std::string_view& content) {
+ StartContent(false);
+ out_ << content;
+}
+
+std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement(
+ const std::string& tag) {
+ return SubElement(tag, XmlAttributes());
+}
+
+std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement(
+ const std::string& tag,
+ const XmlAttributes& attributes) {
+ StartContent(true);
+ return std::make_unique<XmlElementWriter>(out_, tag, attributes, indent_ + 2);
+}
+
+std::ostream& XmlElementWriter::StartContent(bool start_new_line) {
+ if (!opening_tag_finished_) {
+ out_ << '>';
+ opening_tag_finished_ = true;
+
+ if (start_new_line && one_line_) {
+ out_ << std::endl;
+ one_line_ = false;
+ }
+ }
+
+ return out_;
+}
+
+std::string XmlEscape(const std::string& value) {
+ std::string result;
+ for (char c : value) {
+ switch (c) {
+ case '\n':
+ result += "&#10;";
+ break;
+ case '\r':
+ result += "&#13;";
+ break;
+ case '\t':
+ result += "&#9;";
+ break;
+ case '"':
+ result += "&quot;";
+ break;
+ case '<':
+ result += "&lt;";
+ break;
+ case '>':
+ result += "&gt;";
+ break;
+ case '&':
+ result += "&amp;";
+ break;
+ default:
+ result += c;
+ }
+ }
+ return result;
+}
diff --git a/gn/src/gn/xml_element_writer.h b/gn/src/gn/xml_element_writer.h
new file mode 100644
index 00000000000..b2760529686
--- /dev/null
+++ b/gn/src/gn/xml_element_writer.h
@@ -0,0 +1,124 @@
+// Copyright 2016 The Chromium 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 TOOLS_GN_XML_ELEMENT_WRITER_H_
+#define TOOLS_GN_XML_ELEMENT_WRITER_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+
+// Vector of XML attribute key-value pairs.
+class XmlAttributes
+ : public std::vector<std::pair<std::string_view, std::string_view>> {
+ public:
+ XmlAttributes();
+ XmlAttributes(const std::string_view& attr_key,
+ const std::string_view& attr_value);
+
+ XmlAttributes& add(const std::string_view& attr_key,
+ const std::string_view& attr_value);
+};
+
+// Helper class for writing XML elements. New XML element is started in
+// XmlElementWriter constructor and ended in its destructor. XmlElementWriter
+// handles XML file formatting in order to produce human-readable document.
+class XmlElementWriter {
+ public:
+ // Starts new XML element. This constructor adds no indentation and is
+ // designed for XML root element.
+ XmlElementWriter(std::ostream& out,
+ const std::string& tag,
+ const XmlAttributes& attributes);
+ // Starts new XML element with specified indentation.
+ XmlElementWriter(std::ostream& out,
+ const std::string& tag,
+ const XmlAttributes& attributes,
+ int indent);
+ // Starts new XML element with specified indentation. Specialized constructor
+ // that allows writting XML element with single attribute without copying
+ // attribute value.
+ template <class Writer>
+ XmlElementWriter(std::ostream& out,
+ const std::string& tag,
+ const std::string& attribute_name,
+ const Writer& attribute_value_writer,
+ int indent);
+ // Ends XML element. All sub-elements should be ended at this point.
+ ~XmlElementWriter();
+
+ // Writes arbitrary XML element text.
+ void Text(const std::string_view& content);
+
+ // Starts new XML sub-element. Caller must ensure that parent element outlives
+ // its children.
+ std::unique_ptr<XmlElementWriter> SubElement(const std::string& tag);
+ std::unique_ptr<XmlElementWriter> SubElement(const std::string& tag,
+ const XmlAttributes& attributes);
+ template <class Writer>
+ std::unique_ptr<XmlElementWriter> SubElement(
+ const std::string& tag,
+ const std::string& attribute_name,
+ const Writer& attribute_value_writer);
+
+ // Finishes opening tag if it isn't finished yet and optionally starts new
+ // document line. Returns the stream where XML element content can be written.
+ // This is an alternative to Text() and SubElement() methods.
+ std::ostream& StartContent(bool start_new_line);
+
+ private:
+ // Output stream. XmlElementWriter objects for XML element and its
+ // sub-elements share the same output stream.
+ std::ostream& out_;
+
+ // XML element tag name.
+ std::string tag_;
+
+ // XML element indentation in the document.
+ int indent_;
+
+ // Flag indicating if opening tag is finished with '>' character already.
+ bool opening_tag_finished_;
+
+ // Flag indicating if XML element should be written in one document line.
+ bool one_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlElementWriter);
+};
+
+template <class Writer>
+XmlElementWriter::XmlElementWriter(std::ostream& out,
+ const std::string& tag,
+ const std::string& attribute_name,
+ const Writer& attribute_value_writer,
+ int indent)
+ : out_(out),
+ tag_(tag),
+ indent_(indent),
+ opening_tag_finished_(false),
+ one_line_(true) {
+ out << std::string(indent, ' ') << '<' << tag;
+ out << ' ' << attribute_name << "=\"";
+ attribute_value_writer(out);
+ out << '\"';
+}
+
+template <class Writer>
+std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement(
+ const std::string& tag,
+ const std::string& attribute_name,
+ const Writer& attribute_value_writer) {
+ StartContent(true);
+ return std::make_unique<XmlElementWriter>(
+ out_, tag, attribute_name, attribute_value_writer, indent_ + 2);
+}
+
+std::string XmlEscape(const std::string& value);
+
+#endif // TOOLS_GN_XML_ELEMENT_WRITER_H_
diff --git a/gn/src/gn/xml_element_writer_unittest.cc b/gn/src/gn/xml_element_writer_unittest.cc
new file mode 100644
index 00000000000..6a4cfedc6bd
--- /dev/null
+++ b/gn/src/gn/xml_element_writer_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium 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 "gn/xml_element_writer.h"
+
+#include <sstream>
+
+#include "util/test/test.h"
+
+namespace {
+
+class MockValueWriter {
+ public:
+ explicit MockValueWriter(const std::string& value) : value_(value) {}
+ void operator()(std::ostream& out) const { out << value_; }
+
+ private:
+ std::string value_;
+};
+
+} // namespace
+
+TEST(XmlElementWriter, EmptyElement) {
+ std::ostringstream out;
+ { XmlElementWriter writer(out, "foo", XmlAttributes()); }
+ EXPECT_EQ("<foo />\n", out.str());
+
+ std::ostringstream out_attr;
+ {
+ XmlElementWriter writer(out_attr, "foo",
+ XmlAttributes("bar", "abc").add("baz", "123"));
+ }
+ EXPECT_EQ("<foo bar=\"abc\" baz=\"123\" />\n", out_attr.str());
+
+ std::ostringstream out_indent;
+ {
+ XmlElementWriter writer(out_indent, "foo", XmlAttributes("bar", "baz"), 2);
+ }
+ EXPECT_EQ(" <foo bar=\"baz\" />\n", out_indent.str());
+
+ std::ostringstream out_writer;
+ {
+ XmlElementWriter writer(out_writer, "foo", "bar", MockValueWriter("baz"),
+ 2);
+ }
+ EXPECT_EQ(" <foo bar=\"baz\" />\n", out_writer.str());
+}
+
+TEST(XmlElementWriter, ElementWithText) {
+ std::ostringstream out;
+ {
+ XmlElementWriter writer(out, "foo", XmlAttributes("bar", "baz"));
+ writer.Text("Hello world!");
+ }
+ EXPECT_EQ("<foo bar=\"baz\">Hello world!</foo>\n", out.str());
+}
+
+TEST(XmlElementWriter, SubElements) {
+ std::ostringstream out;
+ {
+ XmlElementWriter writer(out, "root", XmlAttributes("aaa", "000"));
+ writer.SubElement("foo", XmlAttributes());
+ writer.SubElement("bar", XmlAttributes("bbb", "111"))->Text("hello");
+ writer.SubElement("baz", "ccc", MockValueWriter("222"))
+ ->SubElement("grandchild");
+ }
+ std::string expected =
+ "<root aaa=\"000\">\n"
+ " <foo />\n"
+ " <bar bbb=\"111\">hello</bar>\n"
+ " <baz ccc=\"222\">\n"
+ " <grandchild />\n"
+ " </baz>\n"
+ "</root>\n";
+ EXPECT_EQ(expected, out.str());
+}
+
+TEST(XmlElementWriter, StartContent) {
+ std::ostringstream out;
+ {
+ XmlElementWriter writer(out, "foo", XmlAttributes("bar", "baz"));
+ writer.StartContent(false) << "Hello world!";
+ }
+ EXPECT_EQ("<foo bar=\"baz\">Hello world!</foo>\n", out.str());
+}
+
+TEST(XmlElementWriter, TestXmlEscape) {
+ std::string input = "\r \n \t & < > \"";
+ std::string output = XmlEscape(input);
+ std::string expected = "&#13; &#10; &#9; &amp; &lt; &gt; &quot;";
+ EXPECT_EQ(expected, output);
+}
diff --git a/gn/util/auto_reset_event.h b/gn/src/util/auto_reset_event.h
index 5e040a8984f..5e040a8984f 100644
--- a/gn/util/auto_reset_event.h
+++ b/gn/src/util/auto_reset_event.h
diff --git a/gn/src/util/build_config.h b/gn/src/util/build_config.h
new file mode 100644
index 00000000000..667f009ddb3
--- /dev/null
+++ b/gn/src/util/build_config.h
@@ -0,0 +1,177 @@
+// 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 adds defines about the platform we're currently building on.
+// Operating System:
+// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) /
+// OS_CHROMEOS is set by the build system
+// Compiler:
+// COMPILER_MSVC / COMPILER_GCC
+// Processor:
+// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
+// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+
+#ifndef BUILD_BUILD_CONFIG_H_
+#define BUILD_BUILD_CONFIG_H_
+
+#if defined(ANDROID)
+#define OS_ANDROID 1
+#elif defined(__APPLE__)
+// only include TargetConditions after testing ANDROID as some android builds
+// on mac don't have this header available and it's not needed unless the target
+// is really mac/ios.
+#include <TargetConditionals.h>
+#define OS_MACOSX 1
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OS_IOS 1
+#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#elif defined(__linux__)
+#define OS_LINUX 1
+// include a system header to pull in features.h for glibc/uclibc macros.
+#include <unistd.h>
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+// we really are using glibc, not uClibc pretending to be glibc
+#define LIBC_GLIBC 1
+#endif
+#elif defined(__MSYS__)
+#define OS_MSYS 1
+#elif defined(_WIN32)
+#define OS_WIN 1
+#elif defined(__Fuchsia__)
+#define OS_FUCHSIA 1
+#elif defined(__FreeBSD__)
+#define OS_FREEBSD 1
+#elif defined(__NetBSD__)
+#define OS_NETBSD 1
+#elif defined(__OpenBSD__)
+#define OS_OPENBSD 1
+#elif defined(__sun)
+#define OS_SOLARIS 1
+#elif defined(__QNXNTO__)
+#define OS_QNX 1
+#elif defined(_AIX)
+#define OS_AIX 1
+#elif defined(__asmjs__)
+#define OS_ASMJS 1
+#elif defined(__HAIKU__)
+#define OS_HAIKU 1
+#else
+#error Please add support for your platform in build_config.h
+#endif
+// NOTE: Adding a new port? Please follow
+// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
+
+// For access to standard BSD features, use OS_BSD instead of a
+// more specific macro.
+#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)
+#define OS_BSD 1
+#endif
+
+// For access to standard POSIXish features, use OS_POSIX instead of a
+// more specific macro.
+#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \
+ defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \
+ defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || \
+ defined(OS_QNX) || defined(OS_SOLARIS) || defined(OS_HAIKU) || \
+ defined(OS_MSYS)
+#define OS_POSIX 1
+#endif
+
+// Use tcmalloc
+#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \
+ !defined(NO_TCMALLOC)
+#define USE_TCMALLOC 1
+#endif
+
+// Compiler detection.
+#if defined(__GNUC__)
+#define COMPILER_GCC 1
+#elif defined(_MSC_VER)
+#define COMPILER_MSVC 1
+#else
+#error Please add support for your compiler in build_config.h
+#endif
+
+// Processor architecture detection. For more info on what's defined, see:
+// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+// http://www.agner.org/optimize/calling_conventions.pdf
+// or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86_64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__s390x__)
+#define ARCH_CPU_S390_FAMILY 1
+#define ARCH_CPU_S390X 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif defined(__s390__)
+#define ARCH_CPU_S390_FAMILY 1
+#define ARCH_CPU_S390 1
+#define ARCH_CPU_31_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__)
+#define ARCH_CPU_PPC64_FAMILY 1
+#define ARCH_CPU_PPC64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif defined(__PPC64__)
+#define ARCH_CPU_PPC64_FAMILY 1
+#define ARCH_CPU_PPC64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__ARMEL__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARMEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__aarch64__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__pnacl__) || defined(__asmjs__)
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__MIPSEL__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS64EL 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPSEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#endif
+#elif defined(__MIPSEB__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#endif
+#elif defined(__e2k__)
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__riscv) && (__riscv_xlen == 64)
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#error Please add support for your architecture in build_config.h
+#endif
+
+#endif // BUILD_BUILD_CONFIG_H_
diff --git a/gn/src/util/exe_path.cc b/gn/src/util/exe_path.cc
new file mode 100644
index 00000000000..b67318c4b2b
--- /dev/null
+++ b/gn/src/util/exe_path.cc
@@ -0,0 +1,119 @@
+// Copyright 2018 The Chromium 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 "util/exe_path.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "util/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <mach-o/dyld.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/win_util.h"
+#elif defined(OS_FREEBSD) || defined(OS_NETBSD)
+#include <limits.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#elif defined(OS_HAIKU)
+#include <OS.h>
+#include <image.h>
+#elif defined(OS_SOLARIS)
+#include <stdlib.h>
+#endif
+
+#if defined(OS_MACOSX)
+
+base::FilePath GetExePath() {
+ // Executable path can have relative references ("..") depending on
+ // how the app was launched.
+ uint32_t executable_length = 0;
+ _NSGetExecutablePath(NULL, &executable_length);
+ DCHECK_GT(executable_length, 1u);
+ std::string executable_path;
+ int rv = _NSGetExecutablePath(
+ base::WriteInto(&executable_path, executable_length), &executable_length);
+ DCHECK_EQ(rv, 0);
+
+ // _NSGetExecutablePath may return paths containing ./ or ../ which makes
+ // FilePath::DirName() work incorrectly, convert it to absolute path so that
+ // paths such as DIR_SOURCE_ROOT can work, since we expect absolute paths to
+ // be returned here.
+ return base::MakeAbsoluteFilePath(base::FilePath(executable_path));
+}
+
+#elif defined(OS_WIN)
+
+base::FilePath GetExePath() {
+ char16_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+ if (GetModuleFileName(NULL, base::ToWCharT(system_buffer), MAX_PATH) == 0) {
+ return base::FilePath();
+ }
+ return base::FilePath(system_buffer);
+}
+
+#elif defined(OS_FREEBSD)
+
+base::FilePath GetExePath() {
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ char buf[PATH_MAX];
+ size_t buf_size = PATH_MAX;
+ if (sysctl(mib, 4, buf, &buf_size, nullptr, 0) == -1) {
+ return base::FilePath();
+ }
+ return base::FilePath(buf);
+}
+
+#elif defined(OS_NETBSD)
+
+base::FilePath GetExePath() {
+ int mib[] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_PATHNAME};
+ char buf[PATH_MAX];
+ size_t buf_size = PATH_MAX;
+ if (sysctl(mib, 4, buf, &buf_size, nullptr, 0) == -1) {
+ return base::FilePath();
+ }
+ return base::FilePath(buf);
+}
+
+#elif defined(OS_HAIKU)
+
+base::FilePath GetExePath() {
+ image_info i_info;
+ int32 image_cookie = 0;
+ while (get_next_image_info(B_CURRENT_TEAM, &image_cookie, &i_info) == B_OK) {
+ if (i_info.type == B_APP_IMAGE) {
+ break;
+ }
+ }
+ return base::FilePath(std::string(i_info.name));
+}
+
+#elif defined(OS_SOLARIS)
+
+base::FilePath GetExePath() {
+ const char *raw = getexecname();
+ if (raw == NULL) {
+ return base::FilePath();
+ }
+ return base::FilePath(raw);
+}
+
+#else
+
+base::FilePath GetExePath() {
+ base::FilePath result;
+ const char kProcSelfExe[] = "/proc/self/exe";
+ if (!ReadSymbolicLink(base::FilePath(kProcSelfExe), &result)) {
+ NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
+ return base::FilePath();
+ }
+ return result;
+}
+
+#endif
diff --git a/gn/util/exe_path.h b/gn/src/util/exe_path.h
index 0e1b8cbed38..0e1b8cbed38 100644
--- a/gn/util/exe_path.h
+++ b/gn/src/util/exe_path.h
diff --git a/gn/src/util/msg_loop.cc b/gn/src/util/msg_loop.cc
new file mode 100644
index 00000000000..15665321fbb
--- /dev/null
+++ b/gn/src/util/msg_loop.cc
@@ -0,0 +1,75 @@
+// Copyright 2018 The Chromium 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 "util/msg_loop.h"
+
+#include "base/logging.h"
+
+namespace {
+
+thread_local MsgLoop* g_current;
+}
+
+MsgLoop::MsgLoop() {
+ DCHECK(g_current == nullptr);
+ g_current = this;
+}
+
+MsgLoop::~MsgLoop() {
+ DCHECK(g_current == this);
+ g_current = nullptr;
+}
+
+void MsgLoop::Run() {
+ while (!should_quit_) {
+ std::function<void()> task;
+ {
+ std::unique_lock<std::mutex> queue_lock(queue_mutex_);
+ notifier_.wait(queue_lock, [this]() {
+ return (!task_queue_.empty()) || should_quit_;
+ });
+
+ if (should_quit_)
+ return;
+
+ task = std::move(task_queue_.front());
+ task_queue_.pop();
+ }
+
+ task();
+ }
+}
+
+void MsgLoop::PostQuit() {
+ PostTask([this]() { should_quit_ = true; });
+}
+
+void MsgLoop::PostTask(std::function<void()> work) {
+ {
+ std::unique_lock<std::mutex> queue_lock(queue_mutex_);
+ task_queue_.emplace(std::move(work));
+ }
+
+ notifier_.notify_one();
+}
+
+void MsgLoop::RunUntilIdleForTesting() {
+ for (bool done = false; !done;) {
+ std::function<void()> task;
+ {
+ std::unique_lock<std::mutex> queue_lock(queue_mutex_);
+ task = std::move(task_queue_.front());
+ task_queue_.pop();
+
+ if (task_queue_.empty())
+ done = true;
+ }
+
+ task();
+ }
+}
+
+MsgLoop* MsgLoop::Current() {
+ return g_current;
+}
diff --git a/gn/src/util/msg_loop.h b/gn/src/util/msg_loop.h
new file mode 100644
index 00000000000..b6e1ec7dbc6
--- /dev/null
+++ b/gn/src/util/msg_loop.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium 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 UTIL_RUN_LOOP_H_
+#define UTIL_RUN_LOOP_H_
+
+#include "base/macros.h"
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <queue>
+
+class MsgLoop {
+ public:
+ MsgLoop();
+ ~MsgLoop();
+
+ // Blocks until PostQuit() is called, processing work items posted via
+ void Run();
+
+ // Schedules Run() to exit, but will not happen until other outstanding tasks
+ // complete. Can be called from any thread.
+ void PostQuit();
+
+ // Posts a work item to this queue. All items will be run on the thread from
+ // which Run() was called. Can be called from any thread.
+ void PostTask(std::function<void()> task);
+
+ // Run()s until the queue is empty. Should only be used (carefully) in tests.
+ void RunUntilIdleForTesting();
+
+ // Gets the MsgLoop for the thread from which it's called, or nullptr if
+ // there's no MsgLoop for the current thread.
+ static MsgLoop* Current();
+
+ private:
+ std::mutex queue_mutex_;
+ std::queue<std::function<void()>> task_queue_;
+ std::condition_variable notifier_;
+ bool should_quit_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(MsgLoop);
+};
+
+#endif // UTIL_RUN_LOOP_H_
diff --git a/gn/src/util/semaphore.cc b/gn/src/util/semaphore.cc
new file mode 100644
index 00000000000..afed45e188b
--- /dev/null
+++ b/gn/src/util/semaphore.cc
@@ -0,0 +1,91 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Based on
+// https://cs.chromium.org/chromium/src/v8/src/base/platform/semaphore.cc
+
+#include "util/semaphore.h"
+
+#include "base/logging.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_MACOSX)
+
+Semaphore::Semaphore(int count) {
+ native_handle_ = dispatch_semaphore_create(count);
+ DCHECK(native_handle_);
+}
+
+Semaphore::~Semaphore() {
+ dispatch_release(native_handle_);
+}
+
+void Semaphore::Signal() {
+ dispatch_semaphore_signal(native_handle_);
+}
+
+void Semaphore::Wait() {
+ dispatch_semaphore_wait(native_handle_, DISPATCH_TIME_FOREVER);
+}
+
+#elif defined(OS_POSIX)
+
+Semaphore::Semaphore(int count) {
+ DCHECK_GE(count, 0);
+ int result = sem_init(&native_handle_, 0, count);
+ DCHECK_EQ(0, result);
+}
+
+Semaphore::~Semaphore() {
+ int result = sem_destroy(&native_handle_);
+ DCHECK_EQ(0, result);
+}
+
+void Semaphore::Signal() {
+ int result = sem_post(&native_handle_);
+ // This check may fail with <libc-2.21, which we use on the try bots, if the
+ // semaphore is destroyed while sem_post is still executed. A work around is
+ // to extend the lifetime of the semaphore.
+ CHECK_EQ(0, result);
+}
+
+void Semaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&native_handle_);
+ if (result == 0)
+ return; // Semaphore was signalled.
+ // Signal caused spurious wakeup.
+ DCHECK_EQ(-1, result);
+ DCHECK_EQ(EINTR, errno);
+ }
+}
+
+#elif defined(OS_WIN)
+
+Semaphore::Semaphore(int count) {
+ DCHECK_GE(count, 0);
+ native_handle_ = ::CreateSemaphoreA(nullptr, count, 0x7FFFFFFF, nullptr);
+ DCHECK(native_handle_);
+}
+
+Semaphore::~Semaphore() {
+ BOOL result = CloseHandle(native_handle_);
+ DCHECK(result);
+}
+
+void Semaphore::Signal() {
+ LONG dummy;
+ BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy);
+ DCHECK(result);
+}
+
+void Semaphore::Wait() {
+ DWORD result = WaitForSingleObject(native_handle_, INFINITE);
+ DCHECK(result == WAIT_OBJECT_0);
+}
+
+#endif
diff --git a/gn/src/util/semaphore.h b/gn/src/util/semaphore.h
new file mode 100644
index 00000000000..2952caec0f3
--- /dev/null
+++ b/gn/src/util/semaphore.h
@@ -0,0 +1,53 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Based on
+// https://cs.chromium.org/chromium/src/v8/src/base/platform/semaphore.h
+
+#ifndef UTIL_SEMAPHORE_H_
+#define UTIL_SEMAPHORE_H_
+
+#include "base/macros.h"
+#include "util/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <dispatch/dispatch.h>
+#elif defined(OS_POSIX)
+#include <semaphore.h>
+#else
+#error Port.
+#endif
+
+class Semaphore {
+ public:
+ explicit Semaphore(int count);
+ ~Semaphore();
+
+ // Increments the semaphore counter.
+ void Signal();
+
+ // Decrements the semaphore counter if it is positive, or blocks until it
+ // becomes positive and then decrements the counter.
+ void Wait();
+
+#if defined(OS_MACOSX)
+ using NativeHandle = dispatch_semaphore_t;
+#elif defined(OS_POSIX)
+ using NativeHandle = sem_t;
+#elif defined(OS_WIN)
+ using NativeHandle = HANDLE;
+#endif
+
+ NativeHandle& native_handle() { return native_handle_; }
+ const NativeHandle& native_handle() const { return native_handle_; }
+
+ private:
+ NativeHandle native_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(Semaphore);
+};
+
+#endif // UTIL_SEMAPHORE_H_
diff --git a/gn/src/util/sys_info.cc b/gn/src/util/sys_info.cc
new file mode 100644
index 00000000000..8124e0730d0
--- /dev/null
+++ b/gn/src/util/sys_info.cc
@@ -0,0 +1,85 @@
+// Copyright 2018 The Chromium 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 "util/sys_info.h"
+
+#include "base/logging.h"
+#include "util/build_config.h"
+
+#if defined(OS_POSIX)
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+std::string OperatingSystemArchitecture() {
+#if defined(OS_POSIX)
+ struct utsname info;
+ if (uname(&info) < 0) {
+ NOTREACHED();
+ return std::string();
+ }
+ std::string arch(info.machine);
+ std::string os(info.sysname);
+ if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") {
+ arch = "x86";
+ } else if (arch == "i86pc") {
+ // Solaris and illumos systems report 'i86pc' (an Intel x86 PC) as their
+ // machine for both 32-bit and 64-bit x86 systems. Considering the rarity
+ // of 32-bit systems at this point, it is safe to assume 64-bit.
+ arch = "x86_64";
+ } else if (arch == "amd64") {
+ arch = "x86_64";
+ } else if (os == "AIX" || os == "OS400") {
+ arch = "ppc64";
+ }
+ return arch;
+#elif defined(OS_WIN)
+ SYSTEM_INFO system_info = {};
+ ::GetNativeSystemInfo(&system_info);
+ switch (system_info.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ return "x86";
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ return "x86_64";
+ case PROCESSOR_ARCHITECTURE_IA64:
+ return "ia64";
+ }
+ return std::string();
+#else
+#error
+#endif
+}
+
+int NumberOfProcessors() {
+#if defined(OS_POSIX)
+ // sysconf returns the number of "logical" (not "physical") processors on both
+ // Mac and Linux. So we get the number of max available "logical" processors.
+ //
+ // Note that the number of "currently online" processors may be fewer than the
+ // returned value of NumberOfProcessors(). On some platforms, the kernel may
+ // make some processors offline intermittently, to save power when system
+ // loading is low.
+ //
+ // One common use case that needs to know the processor count is to create
+ // optimal number of threads for optimization. It should make plan according
+ // to the number of "max available" processors instead of "currently online"
+ // ones. The kernel should be smart enough to make all processors online when
+ // it has sufficient number of threads waiting to run.
+ long res = sysconf(_SC_NPROCESSORS_CONF);
+ if (res == -1) {
+ NOTREACHED();
+ return 1;
+ }
+
+ return static_cast<int>(res);
+#elif defined(OS_WIN)
+ return ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
+#else
+#error
+#endif
+}
diff --git a/gn/util/sys_info.h b/gn/src/util/sys_info.h
index 68133e1088f..68133e1088f 100644
--- a/gn/util/sys_info.h
+++ b/gn/src/util/sys_info.h
diff --git a/gn/src/util/test/gn_test.cc b/gn/src/util/test/gn_test.cc
new file mode 100644
index 00000000000..6e9b96429d7
--- /dev/null
+++ b/gn/src/util/test/gn_test.cc
@@ -0,0 +1,182 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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.
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/command_line.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+namespace testing {
+Test* g_current_test;
+} // namespace testing
+
+struct RegisteredTest {
+ testing::Test* (*factory)();
+ const char* name;
+ bool should_run;
+};
+
+// This can't be a vector because tests call RegisterTest from static
+// initializers and the order static initializers run it isn't specified. So
+// the vector constructor isn't guaranteed to run before all of the
+// RegisterTest() calls.
+static RegisteredTest tests[10000];
+static int ntests;
+
+void RegisterTest(testing::Test* (*factory)(), const char* name) {
+ tests[ntests].factory = factory;
+ tests[ntests++].name = name;
+}
+
+namespace {
+
+bool PatternMatchesString(const char* pattern, const char* str) {
+ switch (*pattern) {
+ case '\0':
+ case '-':
+ case ':':
+ return *str == '\0';
+ case '*':
+ return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
+ PatternMatchesString(pattern + 1, str);
+ default:
+ return *pattern == *str && PatternMatchesString(pattern + 1, str + 1);
+ }
+}
+
+bool PatternListMatchString(const char* pattern, const char* str) {
+ const char* const colon = strchr(pattern, ':');
+ if (PatternMatchesString(pattern, str))
+ return true;
+
+ if (!colon)
+ return false;
+
+ return PatternListMatchString(colon + 1, str);
+}
+
+bool TestMatchesFilter(const char* test, const char* filter) {
+ // Split --gtest_filter at '-' into positive and negative filters.
+ const char* const dash = strchr(filter, '-');
+ const char* pos =
+ dash == filter ? "*" : filter; // Treat '-test1' as '*-test1'
+ const char* neg = dash ? dash + 1 : "";
+ return PatternListMatchString(pos, test) &&
+ !PatternListMatchString(neg, test);
+}
+
+#if defined(OS_WIN)
+struct ScopedEnableVTEscapeProcessing {
+ ScopedEnableVTEscapeProcessing() {
+ console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if (GetConsoleScreenBufferInfo(console_, &csbi) &&
+ GetConsoleMode(console_, &original_mode_)) {
+ SetConsoleMode(console_, original_mode_ |
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING |
+ DISABLE_NEWLINE_AUTO_RETURN);
+ } else {
+ console_ = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ ~ScopedEnableVTEscapeProcessing() {
+ if (is_valid())
+ SetConsoleMode(console_, original_mode_);
+ }
+
+ bool is_valid() const { return console_ != INVALID_HANDLE_VALUE; }
+
+ HANDLE console_;
+ DWORD original_mode_;
+};
+#endif
+
+} // namespace
+
+int main(int argc, char** argv) {
+ base::CommandLine::Init(argc, argv);
+
+#if defined(OS_WIN)
+ ScopedEnableVTEscapeProcessing enable_vt_processing;
+#endif
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+
+ int tests_started = 0;
+
+ const char* test_filter = "*";
+ for (int i = 1; i < argc; ++i) {
+ const char kTestFilterPrefix[] = "--gtest_filter=";
+ if (strncmp(argv[i], kTestFilterPrefix, strlen(kTestFilterPrefix)) == 0) {
+ test_filter = &argv[i][strlen(kTestFilterPrefix)];
+ }
+ }
+
+ int num_active_tests = 0;
+ for (int i = 0; i < ntests; i++) {
+ tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter);
+ if (tests[i].should_run) {
+ ++num_active_tests;
+ }
+ }
+
+ const char* prefix = "";
+ const char* suffix = "\n";
+#if defined(OS_WIN)
+ if (enable_vt_processing.is_valid())
+#else
+ // When run from Xcode, the console returns "true" to isatty(1) but it
+ // does not interprets ANSI escape sequence resulting in difficult to
+ // read output. There is no portable way to detect if the console is
+ // Xcode's console (term is set to xterm or xterm-256colors) but Xcode
+ // sets the __XCODE_BUILT_PRODUCTS_DIR_PATHS environment variable. Use
+ // this as a proxy to detect that the console does not interpret the
+ // ANSI sequences correctly.
+ if (isatty(1) && getenv("__XCODE_BUILT_PRODUCTS_DIR_PATHS") == NULL)
+#endif
+ {
+ prefix = "\r";
+ suffix = "\x1B[K";
+ }
+ bool passed = true;
+ for (int i = 0; i < ntests; i++) {
+ if (!tests[i].should_run)
+ continue;
+
+ ++tests_started;
+ testing::Test* test = tests[i].factory();
+ printf("%s[%d/%d] %s%s", prefix, tests_started, num_active_tests,
+ tests[i].name, suffix);
+ test->SetUp();
+ test->Run();
+ test->TearDown();
+ if (test->Failed())
+ passed = false;
+ delete test;
+ }
+
+ printf("\n%s\n", passed ? "PASSED" : "FAILED");
+ fflush(stdout);
+ return passed ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/gn/src/util/test/test.h b/gn/src/util/test/test.h
new file mode 100644
index 00000000000..d3fc056fdc2
--- /dev/null
+++ b/gn/src/util/test/test.h
@@ -0,0 +1,194 @@
+// Copyright 2018 The Chromium 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 UTIL_TEST_TEST_H_
+#define UTIL_TEST_TEST_H_
+
+#include <string.h>
+
+#include <sstream>
+#include <string>
+
+// This is a minimal googletest-like testing framework. It's originally derived
+// from Ninja's src/test.h. You might prefer that one if you have different
+// tradeoffs (in particular, if you don't need to stream message to assertion
+// failures, Ninja's is a bit simpler.)
+namespace testing {
+
+class Test {
+ public:
+ Test() : failed_(false) {}
+ virtual ~Test() {}
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+ virtual void Run() = 0;
+
+ bool Failed() const { return failed_; }
+
+ private:
+ friend class TestResult;
+
+ bool failed_;
+};
+
+extern testing::Test* g_current_test;
+
+class TestResult {
+ public:
+ TestResult(bool condition, const char* error)
+ : condition_(condition), error_(error) {
+ if (!condition)
+ g_current_test->failed_ = true;
+ }
+
+ operator bool() const { return condition_; }
+ const char* error() const { return error_; }
+
+ private:
+ bool condition_;
+ const char* error_;
+};
+
+class Message {
+ public:
+ Message() {}
+ ~Message() { printf("%s\n\n", ss_.str().c_str()); }
+
+ template <typename T>
+ inline Message& operator<<(const T& val) {
+ ss_ << val;
+ return *this;
+ }
+
+ private:
+ std::stringstream ss_;
+};
+
+class AssertHelper {
+ public:
+ AssertHelper(const char* file, int line, const TestResult& test_result)
+ : file_(file), line_(line), error_(test_result.error()) {}
+
+ void operator=(const Message& message) const {
+ printf("\n*** FAILURE %s:%d: %s\n", file_, line_, error_);
+ }
+
+ private:
+ const char* file_;
+ int line_;
+ const char* error_;
+};
+
+} // namespace testing
+
+void RegisterTest(testing::Test* (*)(), const char*);
+
+#define TEST_F_(x, y, name) \
+ struct y : public x { \
+ static testing::Test* Create() { return testing::g_current_test = new y; } \
+ virtual void Run(); \
+ }; \
+ struct Register##y { \
+ Register##y() { RegisterTest(y::Create, name); } \
+ }; \
+ Register##y g_register_##y; \
+ void y::Run()
+
+#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y)
+#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y)
+
+#define FRIEND_TEST(x, y) friend class x##y
+
+// Some compilers emit a warning if nested "if" statements are followed by an
+// "else" statement and braces are not used to explicitly disambiguate the
+// "else" binding. This leads to problems with code like:
+//
+// if (something)
+// ASSERT_TRUE(condition) << "Some message";
+#define TEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ switch (0) \
+ case 0: \
+ default:
+
+#define TEST_ASSERT_(expression, on_failure) \
+ TEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const ::testing::TestResult test_result = (expression)) \
+ ; \
+ else \
+ on_failure(test_result)
+
+#define TEST_NONFATAL_FAILURE_(message) \
+ ::testing::AssertHelper(__FILE__, __LINE__, message) = ::testing::Message()
+
+#define TEST_FATAL_FAILURE_(message) \
+ return ::testing::AssertHelper(__FILE__, __LINE__, message) = \
+ ::testing::Message()
+
+#define EXPECT_EQ(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a == b, #a " == " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_NE(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_LT(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_GT(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_LE(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_GE(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_TRUE(a) \
+ TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_FALSE(a) \
+ TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \
+ TEST_NONFATAL_FAILURE_)
+
+#define EXPECT_STREQ(a, b) \
+ TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \
+ TEST_NONFATAL_FAILURE_)
+
+#define ASSERT_EQ(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a == b, #a " == " #b), TEST_FATAL_FAILURE_)
+
+#define ASSERT_NE(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), TEST_FATAL_FAILURE_)
+
+#define ASSERT_LT(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), TEST_FATAL_FAILURE_)
+
+#define ASSERT_GT(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), TEST_FATAL_FAILURE_)
+
+#define ASSERT_LE(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), TEST_FATAL_FAILURE_)
+
+#define ASSERT_GE(a, b) \
+ TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), TEST_FATAL_FAILURE_)
+
+#define ASSERT_TRUE(a) \
+ TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \
+ TEST_FATAL_FAILURE_)
+
+#define ASSERT_FALSE(a) \
+ TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \
+ TEST_FATAL_FAILURE_)
+
+#define ASSERT_STREQ(a, b) \
+ TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \
+ TEST_FATAL_FAILURE_)
+
+#endif // UTIL_TEST_TEST_H_
diff --git a/gn/src/util/ticks.cc b/gn/src/util/ticks.cc
new file mode 100644
index 00000000000..121c7199120
--- /dev/null
+++ b/gn/src/util/ticks.cc
@@ -0,0 +1,95 @@
+// Copyright 2018 The Chromium 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 "ticks.h"
+
+#include "base/logging.h"
+#include "build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <mach/mach_time.h>
+#elif defined(OS_POSIX)
+#include <time.h>
+#else
+#error Port.
+#endif
+
+namespace {
+
+bool g_initialized;
+
+#if defined(OS_WIN)
+LARGE_INTEGER g_frequency;
+LARGE_INTEGER g_start;
+#elif defined(OS_MACOSX)
+mach_timebase_info_data_t g_timebase;
+uint64_t g_start;
+#elif defined(OS_POSIX)
+uint64_t g_start;
+#else
+#error Port.
+#endif
+
+#if !defined(OS_MACOSX)
+constexpr uint64_t kNano = 1'000'000'000;
+#endif
+
+void Init() {
+ DCHECK(!g_initialized);
+
+#if defined(OS_WIN)
+ QueryPerformanceFrequency(&g_frequency);
+ QueryPerformanceCounter(&g_start);
+#elif defined(OS_MACOSX)
+ mach_timebase_info(&g_timebase);
+ g_start = (mach_absolute_time() * g_timebase.numer) / g_timebase.denom;
+#elif defined(OS_POSIX)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ g_start = static_cast<uint64_t>(ts.tv_sec) * kNano +
+ static_cast<uint64_t>(ts.tv_nsec);
+#else
+#error Port.
+#endif
+
+ g_initialized = true;
+}
+
+} // namespace
+
+Ticks TicksNow() {
+ static bool initialized = []() {
+ Init();
+ return true;
+ }();
+ DCHECK(initialized);
+
+ Ticks now;
+
+#if defined(OS_WIN)
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ now = ((t.QuadPart - g_start.QuadPart) * kNano) / g_frequency.QuadPart;
+#elif defined(OS_MACOSX)
+ now =
+ ((mach_absolute_time() * g_timebase.numer) / g_timebase.denom) - g_start;
+#elif defined(OS_POSIX)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ now = (static_cast<uint64_t>(ts.tv_sec) * kNano +
+ static_cast<uint64_t>(ts.tv_nsec)) -
+ g_start;
+#else
+#error Port.
+#endif
+
+ return now;
+}
+
+TickDelta TicksDelta(Ticks new_ticks, Ticks old_ticks) {
+ DCHECK(new_ticks >= old_ticks);
+ return TickDelta(new_ticks - old_ticks);
+}
diff --git a/gn/util/ticks.h b/gn/src/util/ticks.h
index fcbde0509e8..fcbde0509e8 100644
--- a/gn/util/ticks.h
+++ b/gn/src/util/ticks.h
diff --git a/gn/src/util/worker_pool.cc b/gn/src/util/worker_pool.cc
new file mode 100644
index 00000000000..9f6a47b38af
--- /dev/null
+++ b/gn/src/util/worker_pool.cc
@@ -0,0 +1,150 @@
+// Copyright 2018 The Chromium 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 "util/worker_pool.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "gn/switches.h"
+#include "util/build_config.h"
+#include "util/sys_info.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+class ProcessorGroupSetter {
+ public:
+ void SetProcessorGroup(std::thread* thread);
+
+ private:
+ int group_ = 0;
+ GROUP_AFFINITY group_affinity_;
+ int num_available_cores_in_group_ = ::GetActiveProcessorCount(group_) / 2;
+ const int num_groups_ = ::GetActiveProcessorGroupCount();
+};
+
+void ProcessorGroupSetter::SetProcessorGroup(std::thread* thread) {
+ if (num_groups_ <= 1)
+ return;
+
+ const HANDLE thread_handle = HANDLE(thread->native_handle());
+ ::GetThreadGroupAffinity(thread_handle, &group_affinity_);
+ group_affinity_.Group = group_;
+ const bool success =
+ ::SetThreadGroupAffinity(thread_handle, &group_affinity_, nullptr);
+ DCHECK(success);
+
+ // Move to next group once one thread has been assigned per core in |group_|.
+ num_available_cores_in_group_--;
+ if (num_available_cores_in_group_ <= 0) {
+ group_++;
+ if (group_ >= num_groups_) {
+ group_ = 0;
+ }
+ num_available_cores_in_group_ = ::GetActiveProcessorCount(group_) / 2;
+ }
+}
+#endif
+
+int GetThreadCount() {
+ std::string thread_count =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kThreads);
+
+ // See if an override was specified on the command line.
+ int result;
+ if (!thread_count.empty() && base::StringToInt(thread_count, &result) &&
+ result >= 1) {
+ return result;
+ }
+
+ // Base the default number of worker threads on number of cores in the
+ // system. When building large projects, the speed can be limited by how fast
+ // the main thread can dispatch work and connect the dependency graph. If
+ // there are too many worker threads, the main thread can be starved and it
+ // will run slower overall.
+ //
+ // One less worker thread than the number of physical CPUs seems to be a
+ // good value, both theoretically and experimentally. But always use at
+ // least some workers to prevent us from being too sensitive to I/O latency
+ // on low-end systems.
+ //
+ // The minimum thread count is based on measuring the optimal threads for the
+ // Chrome build on a several-year-old 4-core MacBook.
+ // Almost all CPUs now are hyperthreaded.
+ int num_cores = NumberOfProcessors() / 2;
+ return std::max(num_cores - 1, 8);
+}
+
+} // namespace
+
+WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {}
+
+WorkerPool::WorkerPool(size_t thread_count) : should_stop_processing_(false) {
+#if defined(OS_WIN)
+ ProcessorGroupSetter processor_group_setter;
+#endif
+
+ threads_.reserve(thread_count);
+ for (size_t i = 0; i < thread_count; ++i) {
+ threads_.emplace_back([this]() { Worker(); });
+
+#if defined(OS_WIN)
+ // Set thread processor group. This is needed for systems with more than 64
+ // logical processors, wherein available processors are divided into groups,
+ // and applications that need to use more than one group's processors must
+ // manually assign their threads to groups.
+ processor_group_setter.SetProcessorGroup(&threads_.back());
+#endif
+ }
+}
+
+WorkerPool::~WorkerPool() {
+ {
+ std::unique_lock<std::mutex> queue_lock(queue_mutex_);
+ should_stop_processing_ = true;
+ }
+
+ pool_notifier_.notify_all();
+
+ for (auto& task_thread : threads_) {
+ task_thread.join();
+ }
+}
+
+void WorkerPool::PostTask(std::function<void()> work) {
+ {
+ std::unique_lock<std::mutex> queue_lock(queue_mutex_);
+ CHECK(!should_stop_processing_);
+ task_queue_.emplace(std::move(work));
+ }
+
+ pool_notifier_.notify_one();
+}
+
+void WorkerPool::Worker() {
+ for (;;) {
+ std::function<void()> task;
+
+ {
+ std::unique_lock<std::mutex> queue_lock(queue_mutex_);
+
+ pool_notifier_.wait(queue_lock, [this]() {
+ return (!task_queue_.empty()) || should_stop_processing_;
+ });
+
+ if (should_stop_processing_ && task_queue_.empty())
+ return;
+
+ task = std::move(task_queue_.front());
+ task_queue_.pop();
+ }
+
+ task();
+ }
+}
diff --git a/gn/src/util/worker_pool.h b/gn/src/util/worker_pool.h
new file mode 100644
index 00000000000..7284ebe72c9
--- /dev/null
+++ b/gn/src/util/worker_pool.h
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium 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 UTIL_WORKER_POOL_H_
+#define UTIL_WORKER_POOL_H_
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+class WorkerPool {
+ public:
+ WorkerPool();
+ WorkerPool(size_t thread_count);
+ ~WorkerPool();
+
+ void PostTask(std::function<void()> work);
+
+ private:
+ void Worker();
+
+ std::vector<std::thread> threads_;
+ std::queue<std::function<void()>> task_queue_;
+ std::mutex queue_mutex_;
+ std::condition_variable_any pool_notifier_;
+ bool should_stop_processing_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerPool);
+};
+
+#endif // UTIL_WORKER_POOL_H_
diff --git a/gn/tools/find_unreachable.py b/gn/tools/find_unreachable.py
new file mode 100755
index 00000000000..406a312b537
--- /dev/null
+++ b/gn/tools/find_unreachable.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# Copyright 2020 The Chromium 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 unreachable gn targets by analysing --ide=json output
+from gn gen.
+
+Usage:
+# Generate json file with targets info, will be located at out/project.json:
+gn gen out --ide=json
+# Lists all targets that are not reachable from //:all or //ci:test_all:
+find_unreachable.py --from //:all --from //ci:test_all --json-file out/project.json
+# Lists targets unreachable from //:all that aren't referenced by any other target:
+find_unreachable.py --from //:all --json-file out/project.json --no-refs
+"""
+
+import argparse
+import json
+import sys
+
+
+def find_reachable_targets(known, graph):
+ reachable = set()
+ to_visit = known
+ while to_visit:
+ next = to_visit.pop()
+ if next in reachable:
+ continue
+ reachable.add(next)
+ to_visit += graph[next]['deps']
+ return reachable
+
+
+def find_source_targets_from(targets, graph):
+ source_targets = set(targets)
+ for target in targets:
+ source_targets -= set(graph[target]['deps'])
+ return source_targets
+
+
+def main():
+ parser = argparse.ArgumentParser(description='''
+ Tool to find unreachable targets.
+ This can be useful to inspect forgotten targets,
+ for example tests or intermediate targets in templates
+ that are no longer needed.
+ ''')
+ parser.add_argument(
+ '--json-file', required=True,
+ help='JSON file from gn gen with --ide=json option')
+ parser.add_argument(
+ '--from', action='append', dest='roots',
+ help='Known "root" targets. Can be multiple. Those targets \
+ and all their recursive dependencies are considered reachable.\
+ Examples: //:all, //ci:test_all')
+ parser.add_argument(
+ '--no-refs', action='store_true',
+ help='Show only targets that aren\'t referenced by any other target')
+ cmd_args = parser.parse_args()
+
+ with open(cmd_args.json_file) as json_file:
+ targets_graph = json.load(json_file)['targets']
+
+ reachable = find_reachable_targets(cmd_args.roots, targets_graph)
+ all = set(targets_graph.keys())
+ unreachable = all - reachable
+
+ result = find_source_targets_from(unreachable, targets_graph) \
+ if cmd_args.no_refs else unreachable
+
+ print '\n'.join(sorted(result))
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/gn/tools/gn/action_target_generator.cc b/gn/tools/gn/action_target_generator.cc
deleted file mode 100644
index 61e9d091bc5..00000000000
--- a/gn/tools/gn/action_target_generator.cc
+++ /dev/null
@@ -1,221 +0,0 @@
-// 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 "tools/gn/action_target_generator.h"
-
-#include "base/stl_util.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/value.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-ActionTargetGenerator::ActionTargetGenerator(
- Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Target::OutputType type,
- Err* err)
- : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
-
-ActionTargetGenerator::~ActionTargetGenerator() = default;
-
-void ActionTargetGenerator::DoRun() {
- target_->set_output_type(output_type_);
-
- if (!FillSources())
- return;
- if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) {
- // Foreach rules must always have some sources to have an effect.
- *err_ =
- Err(function_call_, "action_foreach target has no sources.",
- "If you don't specify any sources, there is nothing to run your\n"
- "script over.");
- return;
- }
-
- if (!FillInputs())
- return;
-
- if (!FillScript())
- return;
-
- if (!FillScriptArgs())
- return;
-
- if (!FillResponseFileContents())
- return;
-
- if (!FillOutputs(output_type_ == Target::ACTION_FOREACH))
- return;
-
- if (!FillDepfile())
- return;
-
- if (!FillPool())
- return;
-
- if (!FillCheckIncludes())
- return;
-
- if (!CheckOutputs())
- return;
-
- // Action outputs don't depend on the current toolchain so we can skip adding
- // that dependency.
-
- // response_file_contents and {{response_file_name}} in the args must go
- // together.
- const auto& required_args_substitutions =
- target_->action_values().args().required_types();
- bool has_rsp_file_name = base::ContainsValue(required_args_substitutions,
- &SubstitutionRspFileName);
- if (target_->action_values().uses_rsp_file() && !has_rsp_file_name) {
- *err_ = Err(
- function_call_, "Missing {{response_file_name}} in args.",
- "This target defines response_file_contents but doesn't use\n"
- "{{response_file_name}} in the args, which means the response file\n"
- "will be unused.");
- return;
- }
- if (!target_->action_values().uses_rsp_file() && has_rsp_file_name) {
- *err_ = Err(
- function_call_, "Missing response_file_contents definition.",
- "This target uses {{response_file_name}} in the args, but does not\n"
- "define response_file_contents which means the response file\n"
- "will be empty.");
- return;
- }
-}
-
-bool ActionTargetGenerator::FillScript() {
- // If this gets called, the target type requires a script, so error out
- // if it doesn't have one.
- const Value* value = scope_->GetValue(variables::kScript, true);
- if (!value) {
- *err_ = Err(function_call_, "This target type requires a \"script\".");
- return false;
- }
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
- *value, err_, scope_->settings()->build_settings()->root_path_utf8());
- if (err_->has_error())
- return false;
- target_->action_values().set_script(script_file);
- return true;
-}
-
-bool ActionTargetGenerator::FillScriptArgs() {
- const Value* value = scope_->GetValue(variables::kArgs, true);
- if (!value)
- return true; // Nothing to do.
-
- if (!target_->action_values().args().Parse(*value, err_))
- return false;
- if (!EnsureValidSubstitutions(
- target_->action_values().args().required_types(),
- &IsValidScriptArgsSubstitution, value->origin(), err_))
- return false;
-
- return true;
-}
-
-bool ActionTargetGenerator::FillResponseFileContents() {
- const Value* value = scope_->GetValue(variables::kResponseFileContents, true);
- if (!value)
- return true; // Nothing to do.
-
- if (!target_->action_values().rsp_file_contents().Parse(*value, err_))
- return false;
- if (!EnsureValidSubstitutions(
- target_->action_values().rsp_file_contents().required_types(),
- &IsValidSourceSubstitution, value->origin(), err_))
- return false;
-
- return true;
-}
-
-bool ActionTargetGenerator::FillDepfile() {
- const Value* value = scope_->GetValue(variables::kDepfile, true);
- if (!value)
- return true;
-
- SubstitutionPattern depfile;
- if (!depfile.Parse(*value, err_))
- return false;
- if (!EnsureSubstitutionIsInOutputDir(depfile, *value))
- return false;
-
- target_->action_values().set_depfile(depfile);
- return true;
-}
-
-bool ActionTargetGenerator::FillPool() {
- const Value* value = scope_->GetValue(variables::kPool, true);
- if (!value)
- return true;
-
- Label label = Label::Resolve(scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), *value, err_);
- if (err_->has_error())
- return false;
-
- LabelPtrPair<Pool> pair(label);
- pair.origin = target_->defined_from();
-
- target_->action_values().set_pool(std::move(pair));
- return true;
-}
-
-bool ActionTargetGenerator::CheckOutputs() {
- const SubstitutionList& outputs = target_->action_values().outputs();
- if (outputs.list().empty()) {
- *err_ =
- Err(function_call_, "Action has no outputs.",
- "If you have no outputs, the build system can not tell when your\n"
- "script needs to be run.");
- return false;
- }
-
- if (output_type_ == Target::ACTION) {
- if (!outputs.required_types().empty()) {
- *err_ = Err(
- function_call_, "Action has patterns in the output.",
- "An action target should have the outputs completely specified. If\n"
- "you want to provide a mapping from source to output, use an\n"
- "\"action_foreach\" target.");
- return false;
- }
- } else if (output_type_ == Target::ACTION_FOREACH) {
- // A foreach target should always have a pattern in the outputs.
- if (outputs.required_types().empty()) {
- *err_ = Err(
- function_call_, "action_foreach should have a pattern in the output.",
- "An action_foreach target should have a source expansion pattern in\n"
- "it to map source file to unique output file name. Otherwise, the\n"
- "build system can't determine when your script needs to be run.");
- return false;
- }
- }
- return true;
-}
-
-bool ActionTargetGenerator::FillInputs() {
- const Value* value = scope_->GetValue(variables::kInputs, true);
- if (!value)
- return true;
-
- Target::FileList dest_inputs;
- if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
- scope_->GetSourceDir(), &dest_inputs, err_))
- return false;
- target_->config_values().inputs().swap(dest_inputs);
- return true;
-}
diff --git a/gn/tools/gn/action_target_generator.h b/gn/tools/gn/action_target_generator.h
deleted file mode 100644
index 0ea3cbbab87..00000000000
--- a/gn/tools/gn/action_target_generator.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 TOOLS_GN_ACTION_TARGET_GENERATOR_H_
-#define TOOLS_GN_ACTION_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target.h"
-#include "tools/gn/target_generator.h"
-
-// Populates a Target with the values from an action[_foreach] rule.
-class ActionTargetGenerator : public TargetGenerator {
- public:
- ActionTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Target::OutputType type,
- Err* err);
- ~ActionTargetGenerator() override;
-
- protected:
- void DoRun() override;
-
- private:
- bool FillScript();
- bool FillScriptArgs();
- bool FillResponseFileContents();
- bool FillDepfile();
- bool FillPool();
- bool FillInputs();
-
- // Checks for errors in the outputs variable.
- bool CheckOutputs();
-
- Target::OutputType output_type_;
-
- DISALLOW_COPY_AND_ASSIGN(ActionTargetGenerator);
-};
-
-#endif // TOOLS_GN_ACTION_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/action_target_generator_unittest.cc b/gn/tools/gn/action_target_generator_unittest.cc
deleted file mode 100644
index b4c2b99c0e6..00000000000
--- a/gn/tools/gn/action_target_generator_unittest.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using ActionTargetGenerator = TestWithScheduler;
-
-// Tests that actions can't have output substitutions.
-TEST_F(ActionTargetGenerator, ActionOutputSubstitutions) {
- TestWithScope setup;
- Scope::ItemVector items_;
- setup.scope()->set_item_collector(&items_);
-
- // First test one with no substitutions, this should be valid.
- TestParseInput input_good(
- R"(action("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/one.txt" ]
- })");
- ASSERT_FALSE(input_good.has_error());
-
- // This should run fine.
- Err err;
- input_good.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // Same thing with a pattern in the output should fail.
- TestParseInput input_bad(
- R"(action("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/{{source_name_part}}.txt" ]
- })");
- ASSERT_FALSE(input_bad.has_error());
-
- // This should run fine.
- input_bad.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
-}
-
-// Tests that arg and response file substitutions are validated for
-// action_foreach targets.
-TEST_F(ActionTargetGenerator, ActionForeachSubstitutions) {
- TestWithScope setup;
- Scope::ItemVector items_;
- setup.scope()->set_item_collector(&items_);
-
- // Args listing a response file but missing a response file definition should
- // fail.
- TestParseInput input_missing_resp_file(
- R"(action_foreach("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/{{source_name_part}}" ]
- args = [ "{{response_file_name}}" ]
- })");
- ASSERT_FALSE(input_missing_resp_file.has_error());
- Err err;
- input_missing_resp_file.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
-
- // Adding a response file definition should pass.
- err = Err();
- TestParseInput input_resp_file(
- R"(action_foreach("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/{{source_name_part}}" ]
- args = [ "{{response_file_name}}" ]
- response_file_contents = [ "{{source_name_part}}" ]
- })");
- ASSERT_FALSE(input_resp_file.has_error());
- input_resp_file.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // Defining a response file but not referencing it should fail.
- err = Err();
- TestParseInput input_missing_rsp_args(
- R"(action_foreach("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/{{source_name_part}}" ]
- args = [ "{{source_name_part}}" ]
- response_file_contents = [ "{{source_name_part}}" ]
- })");
- ASSERT_FALSE(input_missing_rsp_args.has_error());
- input_missing_rsp_args.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()) << err.message();
-
- // Bad substitutions in args.
- err = Err();
- TestParseInput input_bad_args(
- R"(action_foreach("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/{{source_name_part}}" ]
- args = [ "{{response_file_name}} {{ldflags}}" ]
- response_file_contents = [ "{{source_name_part}}" ]
- })");
- ASSERT_FALSE(input_bad_args.has_error());
- input_bad_args.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()) << err.message();
-
- // Bad substitutions in response file contents.
- err = Err();
- TestParseInput input_bad_rsp(
- R"(action_foreach("foo") {
- script = "//foo.py"
- sources = [ "//bar.txt" ]
- outputs = [ "//out/Debug/{{source_name_part}}" ]
- args = [ "{{response_file_name}}" ]
- response_file_contents = [ "{{source_name_part}} {{ldflags}}" ]
- })");
- ASSERT_FALSE(input_bad_rsp.has_error());
- input_bad_rsp.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()) << err.message();
-}
diff --git a/gn/tools/gn/action_values.cc b/gn/tools/gn/action_values.cc
deleted file mode 100644
index f73d869cafb..00000000000
--- a/gn/tools/gn/action_values.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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 "tools/gn/action_values.h"
-
-#include "tools/gn/settings.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-
-ActionValues::ActionValues() = default;
-
-ActionValues::~ActionValues() = default;
-
-void ActionValues::GetOutputsAsSourceFiles(
- const Target* target,
- std::vector<SourceFile>* result) const {
- if (target->output_type() == Target::BUNDLE_DATA) {
- // The bundle_data target has no output, the real output will be generated
- // by the create_bundle target.
- } else if (target->output_type() == Target::COPY_FILES ||
- target->output_type() == Target::ACTION_FOREACH) {
- // Copy and foreach applies the outputs to the sources.
- SubstitutionWriter::ApplyListToSources(target, target->settings(), outputs_,
- target->sources(), result);
- } else {
- // Actions (and anything else that happens to specify an output) just use
- // the output list with no substitution.
- SubstitutionWriter::GetListAsSourceFiles(outputs_, result);
- }
-}
diff --git a/gn/tools/gn/action_values.h b/gn/tools/gn/action_values.h
deleted file mode 100644
index 806a39f8c64..00000000000
--- a/gn/tools/gn/action_values.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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 TOOLS_GN_ACTION_VALUES_H_
-#define TOOLS_GN_ACTION_VALUES_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/substitution_list.h"
-
-class Pool;
-class Target;
-
-// Holds the values (outputs, args, script name, etc.) for either an action or
-// an action_foreach target.
-class ActionValues {
- public:
- ActionValues();
- ~ActionValues();
-
- // Filename of the script to execute.
- const SourceFile& script() const { return script_; }
- void set_script(const SourceFile& s) { script_ = s; }
-
- // Arguments to the script.
- SubstitutionList& args() { return args_; }
- const SubstitutionList& args() const { return args_; }
-
- // Files created by the script. These are strings rather than SourceFiles
- // since they will often contain {{source expansions}}.
- SubstitutionList& outputs() { return outputs_; }
- const SubstitutionList& outputs() const { return outputs_; }
-
- // Expands the outputs() above to the final SourceFile list.
- void GetOutputsAsSourceFiles(const Target* target,
- std::vector<SourceFile>* result) const;
-
- // Depfile generated by the script.
- const SubstitutionPattern& depfile() const { return depfile_; }
- bool has_depfile() const { return !depfile_.ranges().empty(); }
- void set_depfile(const SubstitutionPattern& depfile) { depfile_ = depfile; }
-
- // Response file contents. Empty means no response file.
- SubstitutionList& rsp_file_contents() { return rsp_file_contents_; }
- const SubstitutionList& rsp_file_contents() const {
- return rsp_file_contents_;
- }
- bool uses_rsp_file() const { return !rsp_file_contents_.list().empty(); }
-
- // Pool option
- const LabelPtrPair<Pool>& pool() const { return pool_; }
- void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
-
- private:
- SourceFile script_;
- SubstitutionList args_;
- SubstitutionList outputs_;
- SubstitutionPattern depfile_;
- SubstitutionList rsp_file_contents_;
- LabelPtrPair<Pool> pool_;
-
- DISALLOW_COPY_AND_ASSIGN(ActionValues);
-};
-
-#endif // TOOLS_GN_ACTION_VALUES_H_
diff --git a/gn/tools/gn/analyzer.cc b/gn/tools/gn/analyzer.cc
deleted file mode 100644
index c58cf8cf6a4..00000000000
--- a/gn/tools/gn/analyzer.cc
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/analyzer.h"
-
-#include <algorithm>
-#include <iterator>
-#include <memory>
-#include <set>
-#include <vector>
-
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/config.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/location.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/target.h"
-
-namespace {
-
-struct Inputs {
- std::vector<SourceFile> source_vec;
- std::vector<Label> compile_vec;
- std::vector<Label> test_vec;
- bool compile_included_all = false;
- std::set<const SourceFile*> source_files;
- std::set<Label> compile_labels;
- std::set<Label> test_labels;
-};
-
-struct Outputs {
- std::string status;
- std::string error;
- bool compile_includes_all = false;
- std::set<Label> compile_labels;
- std::set<Label> test_labels;
- std::set<Label> invalid_labels;
-};
-
-std::set<Label> LabelsFor(const std::set<const Target*>& targets) {
- std::set<Label> labels;
- for (auto* target : targets)
- labels.insert(target->label());
- return labels;
-}
-
-std::set<const Target*> Intersect(const std::set<const Target*>& l,
- const std::set<const Target*>& r) {
- std::set<const Target*> result;
- std::set_intersection(l.begin(), l.end(), r.begin(), r.end(),
- std::inserter(result, result.begin()));
- return result;
-}
-
-std::vector<std::string> GetStringVector(const base::DictionaryValue& dict,
- const std::string& key,
- Err* err) {
- std::vector<std::string> strings;
- const base::ListValue* lst;
- bool ret = dict.GetList(key, &lst);
- if (!ret) {
- *err = Err(Location(), "Input does not have a key named \"" + key +
- "\" with a list value.");
- return strings;
- }
-
- for (size_t i = 0; i < lst->GetSize(); i++) {
- std::string s;
- ret = lst->GetString(i, &s);
- if (!ret) {
- *err = Err(Location(), "Item " + std::to_string(i) + " of \"" + key +
- "\" is not a string.");
- strings.clear();
- return strings;
- }
- strings.push_back(std::move(s));
- }
- *err = Err();
- return strings;
-}
-
-void WriteString(base::DictionaryValue& dict,
- const std::string& key,
- const std::string& value) {
- dict.SetKey(key, base::Value(value));
-};
-
-void WriteLabels(const Label& default_toolchain,
- base::DictionaryValue& dict,
- const std::string& key,
- const std::set<Label>& labels) {
- std::vector<std::string> strings;
- auto value = std::make_unique<base::ListValue>();
- for (const auto l : labels)
- strings.push_back(l.GetUserVisibleName(default_toolchain));
- std::sort(strings.begin(), strings.end());
- value->AppendStrings(strings);
- dict.SetWithoutPathExpansion(key, std::move(value));
-}
-
-Label AbsoluteOrSourceAbsoluteStringToLabel(const Label& default_toolchain,
- const std::string& s,
- Err* err) {
- if (!IsPathSourceAbsolute(s) && !IsPathAbsolute(s)) {
- *err = Err(Location(),
- "\"" + s + "\" is not a source-absolute or absolute path.");
- return Label();
- }
- return Label::Resolve(SourceDir("//"), default_toolchain, Value(nullptr, s),
- err);
-}
-
-Err JSONToInputs(const Label& default_toolchain,
- const std::string input,
- Inputs* inputs) {
- int error_code_out;
- std::string error_msg_out;
- int error_line_out;
- int error_column_out;
- std::unique_ptr<base::Value> value = base::JSONReader().ReadAndReturnError(
- input, base::JSONParserOptions::JSON_PARSE_RFC, &error_code_out,
- &error_msg_out, &error_line_out, &error_column_out);
- if (!value)
- return Err(Location(), "Input is not valid JSON:" + error_msg_out);
-
- const base::DictionaryValue* dict;
- if (!value->GetAsDictionary(&dict))
- return Err(Location(), "Input is not a dictionary.");
-
- Err err;
- std::vector<std::string> strings;
- strings = GetStringVector(*dict, "files", &err);
- if (err.has_error())
- return err;
- for (auto& s : strings) {
- if (!IsPathSourceAbsolute(s) && !IsPathAbsolute(s)) {
- return Err(Location(),
- "\"" + s + "\" is not a source-absolute or absolute path.");
- }
- inputs->source_vec.emplace_back(std::move(s));
- }
-
- strings = GetStringVector(*dict, "additional_compile_targets", &err);
- if (err.has_error())
- return err;
-
- inputs->compile_included_all = false;
- for (auto& s : strings) {
- if (s == "all") {
- inputs->compile_included_all = true;
- } else {
- inputs->compile_vec.push_back(
- AbsoluteOrSourceAbsoluteStringToLabel(default_toolchain, s, &err));
- if (err.has_error())
- return err;
- }
- }
-
- strings = GetStringVector(*dict, "test_targets", &err);
- if (err.has_error())
- return err;
- for (auto& s : strings) {
- inputs->test_vec.push_back(
- AbsoluteOrSourceAbsoluteStringToLabel(default_toolchain, s, &err));
- if (err.has_error())
- return err;
- }
-
- for (auto& s : inputs->source_vec)
- inputs->source_files.insert(&s);
- for (auto& l : inputs->compile_vec)
- inputs->compile_labels.insert(l);
- for (auto& l : inputs->test_vec)
- inputs->test_labels.insert(l);
- return Err();
-}
-
-std::string OutputsToJSON(const Outputs& outputs,
- const Label& default_toolchain,
- Err* err) {
- std::string output;
- auto value = std::make_unique<base::DictionaryValue>();
-
- if (outputs.error.size()) {
- WriteString(*value, "error", outputs.error);
- WriteLabels(default_toolchain, *value, "invalid_targets",
- outputs.invalid_labels);
- } else {
- WriteString(*value, "status", outputs.status);
- if (outputs.compile_includes_all) {
- auto compile_targets = std::make_unique<base::ListValue>();
- compile_targets->AppendString("all");
- value->SetWithoutPathExpansion("compile_targets",
- std::move(compile_targets));
- } else {
- WriteLabels(default_toolchain, *value, "compile_targets",
- outputs.compile_labels);
- }
- WriteLabels(default_toolchain, *value, "test_targets", outputs.test_labels);
- }
-
- if (!base::JSONWriter::Write(*value.get(), &output))
- *err = Err(Location(), "Failed to marshal JSON value for output");
- return output;
-}
-
-} // namespace
-
-Analyzer::Analyzer(const Builder& builder,
- const SourceFile& build_config_file,
- const SourceFile& dot_file,
- const std::set<SourceFile>& build_args_dependency_files)
- : all_items_(builder.GetAllResolvedItems()),
- default_toolchain_(builder.loader()->GetDefaultToolchain()),
- build_config_file_(build_config_file),
- dot_file_(dot_file),
- build_args_dependency_files_(build_args_dependency_files) {
- for (const auto* item : all_items_) {
- labels_to_items_[item->label()] = item;
-
- // Fill dep_map_.
- if (item->AsTarget()) {
- for (const auto& dep_target_pair :
- item->AsTarget()->GetDeps(Target::DEPS_ALL))
- dep_map_.insert(std::make_pair(dep_target_pair.ptr, item));
-
- for (const auto& dep_config_pair : item->AsTarget()->configs())
- dep_map_.insert(std::make_pair(dep_config_pair.ptr, item));
-
- dep_map_.insert(std::make_pair(item->AsTarget()->toolchain(), item));
-
- if (item->AsTarget()->output_type() == Target::ACTION ||
- item->AsTarget()->output_type() == Target::ACTION_FOREACH) {
- const LabelPtrPair<Pool>& pool =
- item->AsTarget()->action_values().pool();
- if (pool.ptr)
- dep_map_.insert(std::make_pair(pool.ptr, item));
- }
- } else if (item->AsConfig()) {
- for (const auto& dep_config_pair : item->AsConfig()->configs())
- dep_map_.insert(std::make_pair(dep_config_pair.ptr, item));
- } else if (item->AsToolchain()) {
- for (const auto& dep_pair : item->AsToolchain()->deps())
- dep_map_.insert(std::make_pair(dep_pair.ptr, item));
- } else {
- DCHECK(item->AsPool());
- }
- }
-}
-
-Analyzer::~Analyzer() = default;
-
-std::string Analyzer::Analyze(const std::string& input, Err* err) const {
- Inputs inputs;
- Outputs outputs;
-
- Err local_err = JSONToInputs(default_toolchain_, input, &inputs);
- if (local_err.has_error()) {
- outputs.error = local_err.message();
- return OutputsToJSON(outputs, default_toolchain_, err);
- }
-
- std::set<Label> invalid_labels;
- for (const auto& label : InvalidLabels(inputs.compile_labels))
- invalid_labels.insert(label);
- for (const auto& label : InvalidLabels(inputs.test_labels))
- invalid_labels.insert(label);
- if (!invalid_labels.empty()) {
- outputs.error = "Invalid targets";
- outputs.invalid_labels = invalid_labels;
- return OutputsToJSON(outputs, default_toolchain_, err);
- }
-
- if (WereMainGNFilesModified(inputs.source_files)) {
- outputs.status = "Found dependency (all)";
- if (inputs.compile_included_all) {
- outputs.compile_includes_all = true;
- } else {
- outputs.compile_labels.insert(inputs.compile_labels.begin(),
- inputs.compile_labels.end());
- outputs.compile_labels.insert(inputs.test_labels.begin(),
- inputs.test_labels.end());
- }
- outputs.test_labels = inputs.test_labels;
- return OutputsToJSON(outputs, default_toolchain_, err);
- }
-
- std::set<const Item*> affected_items =
- GetAllAffectedItems(inputs.source_files);
- std::set<const Target*> affected_targets;
- for (const Item* affected_item : affected_items) {
- // Only handles targets in the default toolchain.
- // TODO(crbug.com/667989): Expand analyzer to non-default toolchains when
- // the bug is fixed.
- if (affected_item->AsTarget() &&
- affected_item->label().GetToolchainLabel() == default_toolchain_)
- affected_targets.insert(affected_item->AsTarget());
- }
-
- if (affected_targets.empty()) {
- outputs.status = "No dependency";
- return OutputsToJSON(outputs, default_toolchain_, err);
- }
-
- std::set<const Target*> root_targets;
- for (const auto* item : all_items_) {
- if (item->AsTarget() && dep_map_.find(item) == dep_map_.end())
- root_targets.insert(item->AsTarget());
- }
-
- std::set<const Target*> compile_targets = TargetsFor(inputs.compile_labels);
- if (inputs.compile_included_all) {
- for (auto* root_target : root_targets)
- compile_targets.insert(root_target);
- }
- std::set<const Target*> filtered_targets = Filter(compile_targets);
- outputs.compile_labels =
- LabelsFor(Intersect(filtered_targets, affected_targets));
-
- // If every target is affected, simply compile All instead of listing all
- // the targets to make the output easier to read.
- if (inputs.compile_included_all &&
- outputs.compile_labels.size() == filtered_targets.size())
- outputs.compile_includes_all = true;
-
- std::set<const Target*> test_targets = TargetsFor(inputs.test_labels);
- outputs.test_labels = LabelsFor(Intersect(test_targets, affected_targets));
-
- if (outputs.compile_labels.empty() && outputs.test_labels.empty())
- outputs.status = "No dependency";
- else
- outputs.status = "Found dependency";
- return OutputsToJSON(outputs, default_toolchain_, err);
-}
-
-std::set<const Item*> Analyzer::GetAllAffectedItems(
- const std::set<const SourceFile*>& source_files) const {
- std::set<const Item*> directly_affected_items;
- for (auto* source_file : source_files)
- AddItemsDirectlyReferringToFile(source_file, &directly_affected_items);
-
- std::set<const Item*> all_affected_items;
- for (auto* affected_item : directly_affected_items)
- AddAllItemsReferringToItem(affected_item, &all_affected_items);
-
- return all_affected_items;
-}
-
-std::set<Label> Analyzer::InvalidLabels(const std::set<Label>& labels) const {
- std::set<Label> invalid_labels;
- for (const Label& label : labels) {
- if (labels_to_items_.find(label) == labels_to_items_.end())
- invalid_labels.insert(label);
- }
- return invalid_labels;
-}
-
-std::set<const Target*> Analyzer::TargetsFor(
- const std::set<Label>& labels) const {
- std::set<const Target*> targets;
- for (const auto& label : labels) {
- auto it = labels_to_items_.find(label);
- if (it != labels_to_items_.end()) {
- DCHECK(it->second->AsTarget());
- targets.insert(it->second->AsTarget());
- }
- }
- return targets;
-}
-
-std::set<const Target*> Analyzer::Filter(
- const std::set<const Target*>& targets) const {
- std::set<const Target*> seen;
- std::set<const Target*> filtered;
- for (const auto* target : targets)
- FilterTarget(target, &seen, &filtered);
- return filtered;
-}
-
-void Analyzer::FilterTarget(const Target* target,
- std::set<const Target*>* seen,
- std::set<const Target*>* filtered) const {
- if (seen->find(target) == seen->end()) {
- seen->insert(target);
- if (target->output_type() != Target::GROUP) {
- filtered->insert(target);
- } else {
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
- FilterTarget(pair.ptr, seen, filtered);
- }
- }
-}
-
-bool Analyzer::ItemRefersToFile(const Item* item,
- const SourceFile* file) const {
- for (const auto& cur_file : item->build_dependency_files()) {
- if (cur_file == *file)
- return true;
- }
-
- if (!item->AsTarget())
- return false;
-
- const Target* target = item->AsTarget();
- for (const auto& cur_file : target->sources()) {
- if (cur_file == *file)
- return true;
- }
- for (const auto& cur_file : target->public_headers()) {
- if (cur_file == *file)
- return true;
- }
- for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
- for (const auto& cur_file : iter.cur().inputs()) {
- if (cur_file == *file)
- return true;
- }
- }
- for (const auto& cur_file : target->data()) {
- if (cur_file == file->value())
- return true;
- if (cur_file.back() == '/' &&
- base::StartsWith(file->value(), cur_file, base::CompareCase::SENSITIVE))
- return true;
- }
-
- if (target->action_values().script().value() == file->value())
- return true;
-
- std::vector<SourceFile> outputs;
- target->action_values().GetOutputsAsSourceFiles(target, &outputs);
- for (const auto& cur_file : outputs) {
- if (cur_file == *file)
- return true;
- }
- return false;
-}
-
-void Analyzer::AddItemsDirectlyReferringToFile(
- const SourceFile* file,
- std::set<const Item*>* directly_affected_items) const {
- for (const auto* item : all_items_) {
- if (ItemRefersToFile(item, file))
- directly_affected_items->insert(item);
- }
-}
-
-void Analyzer::AddAllItemsReferringToItem(
- const Item* item,
- std::set<const Item*>* all_affected_items) const {
- if (all_affected_items->find(item) != all_affected_items->end())
- return; // Already found this item.
-
- all_affected_items->insert(item);
-
- auto dep_begin = dep_map_.lower_bound(item);
- auto dep_end = dep_map_.upper_bound(item);
- for (auto cur_dep = dep_begin; cur_dep != dep_end; ++cur_dep)
- AddAllItemsReferringToItem(cur_dep->second, all_affected_items);
-}
-
-bool Analyzer::WereMainGNFilesModified(
- const std::set<const SourceFile*>& modified_files) const {
- for (const auto* file : modified_files) {
- if (*file == dot_file_)
- return true;
-
- if (*file == build_config_file_)
- return true;
-
- for (const auto& build_args_dependency_file :
- build_args_dependency_files_) {
- if (*file == build_args_dependency_file)
- return true;
- }
- }
-
- return false;
-}
diff --git a/gn/tools/gn/analyzer.h b/gn/tools/gn/analyzer.h
deleted file mode 100644
index 01be0e45552..00000000000
--- a/gn/tools/gn/analyzer.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_ANALYZER_H_
-#define TOOLS_GN_ANALYZER_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "tools/gn/builder.h"
-#include "tools/gn/item.h"
-#include "tools/gn/label.h"
-#include "tools/gn/source_file.h"
-
-// An Analyzer can answer questions about a build graph. It is used
-// to answer queries for the `refs` and `analyze` commands, where we
-// need to look at the graph in ways that can't easily be determined
-// from just a single Target.
-class Analyzer {
- public:
- Analyzer(const Builder& builder,
- const SourceFile& build_config_file,
- const SourceFile& dot_file,
- const std::set<SourceFile>& build_args_dependency_files);
- ~Analyzer();
-
- // Figures out from a Buider and a JSON-formatted string containing lists
- // of files and targets, which targets would be affected by modifications
- // to the files . See the help text for the analyze command (kAnalyze_Help)
- // for the specification of the input and output string formats and the
- // expected behavior of the method.
- std::string Analyze(const std::string& input, Err* err) const;
-
- private:
- // Returns the set of all items that might be affected, directly or
- // indirectly, by modifications to the given source files.
- std::set<const Item*> GetAllAffectedItems(
- const std::set<const SourceFile*>& source_files) const;
-
- // Returns the set of labels that do not refer to objects in the graph.
- std::set<Label> InvalidLabels(const std::set<Label>& labels) const;
-
- // Returns the set of all targets that have a label in the given set.
- // Invalid (or missing) labels will be ignored.
- std::set<const Target*> TargetsFor(const std::set<Label>& labels) const;
-
- // Returns a filtered set of the given targets, meaning that for each of the
- // given targets,
- // - if the target is not a group, add it to the set
- // - if the target is a group, recursively filter each dependency and add
- // its filtered results to the set.
- //
- // For example, if we had:
- //
- // group("foobar") { deps = [ ":foo", ":bar" ] }
- // group("bar") { deps = [ ":baz", ":quux" ] }
- // executable("foo") { ... }
- // executable("baz") { ... }
- // executable("quux") { ... }
- //
- // Then the filtered version of {"foobar"} would be {":foo", ":baz",
- // ":quux"}. This is used by the analyze command in order to only build
- // the affected dependencies of a group (and not also build the unaffected
- // ones).
- //
- // This filtering behavior is also known as "pruning" the list of targets.
- std::set<const Target*> Filter(const std::set<const Target*>& targets) const;
-
- // Filter an individual target and adds the results to filtered
- // (see Filter(), above).
- void FilterTarget(const Target*,
- std::set<const Target*>* seen,
- std::set<const Target*>* filtered) const;
-
- bool ItemRefersToFile(const Item* item, const SourceFile* file) const;
-
- void AddItemsDirectlyReferringToFile(
- const SourceFile* file,
- std::set<const Item*>* affected_items) const;
-
- void AddAllItemsReferringToItem(const Item* item,
- std::set<const Item*>* affected_items) const;
-
- // Main GN files stand for files whose context are used globally to execute
- // every other build files, this list includes dot file, build config file,
- // build args files etc.
- bool WereMainGNFilesModified(
- const std::set<const SourceFile*>& modified_files) const;
-
- std::vector<const Item*> all_items_;
- std::map<Label, const Item*> labels_to_items_;
- Label default_toolchain_;
-
- // Maps items to the list of items that depend on them.
- std::multimap<const Item*, const Item*> dep_map_;
-
- const SourceFile build_config_file_;
- const SourceFile dot_file_;
- const std::set<SourceFile> build_args_dependency_files_;
-};
-
-#endif // TOOLS_GN_ANALYZER_H_
diff --git a/gn/tools/gn/analyzer_unittest.cc b/gn/tools/gn/analyzer_unittest.cc
deleted file mode 100644
index d796300b00f..00000000000
--- a/gn/tools/gn/analyzer_unittest.cc
+++ /dev/null
@@ -1,597 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/analyzer.h"
-
-#include <tools/gn/c_tool.h>
-#include "tools/gn/build_settings.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/config.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/target.h"
-#include <tools/gn/tool.h>
-#include "tools/gn/toolchain.h"
-#include "util/test/test.h"
-
-namespace gn_analyzer_unittest {
-
-class MockLoader : public Loader {
- public:
- MockLoader() = default;
-
- void Load(const SourceFile& file,
- const LocationRange& origin,
- const Label& toolchain_name) override {}
- void ToolchainLoaded(const Toolchain* toolchain) override {}
- Label GetDefaultToolchain() const override {
- return Label(SourceDir("//tc/"), "default");
- }
- const Settings* GetToolchainSettings(const Label& label) const override {
- return nullptr;
- }
-
- private:
- ~MockLoader() override = default;
-};
-
-class AnalyzerTest : public testing::Test {
- public:
- AnalyzerTest()
- : loader_(new MockLoader),
- builder_(loader_.get()),
- settings_(&build_settings_, std::string()) {
- build_settings_.SetBuildDir(SourceDir("//out/"));
- settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
- settings_.set_default_toolchain_label(settings_.toolchain_label());
- tc_dir_ = settings_.toolchain_label().dir();
- tc_name_ = settings_.toolchain_label().name();
- }
-
- // Ownership of the target will be transfered to the builder, so no leaks.
- Target* MakeTarget(const std::string& dir, const std::string& name) {
- Label label(SourceDir(dir), name, tc_dir_, tc_name_);
- Target* target = new Target(&settings_, label);
-
- return target;
- }
-
- // Ownership of the config will be transfered to the builder, so no leaks.
- Config* MakeConfig(const std::string& dir, const std::string& name) {
- Label label(SourceDir(dir), name, tc_dir_, tc_name_);
- Config* config = new Config(&settings_, label);
-
- return config;
- }
-
- // Ownership of the pool will be transfered to the builder, so no leaks.
- Pool* MakePool(const std::string& dir, const std::string& name) {
- Label label(SourceDir(dir), name, tc_dir_, tc_name_);
- Pool* pool = new Pool(&settings_, label);
-
- return pool;
- }
-
- void RunAnalyzerTest(const std::string& input,
- const std::string& expected_output) {
- Analyzer analyzer(builder_, SourceFile("//build/config/BUILDCONFIG.gn"),
- SourceFile("//.gn"),
- {SourceFile("//out/debug/args.gn"),
- SourceFile("//build/default_args.gn")});
- Err err;
- std::string actual_output = analyzer.Analyze(input, &err);
- EXPECT_EQ(err.has_error(), false);
- EXPECT_EQ(expected_output, actual_output);
- }
-
- protected:
- scoped_refptr<MockLoader> loader_;
- Builder builder_;
- BuildSettings build_settings_;
- Settings settings_;
- SourceDir tc_dir_;
- std::string tc_name_;
-};
-
-// Tests that a target is marked as affected if its sources are modified.
-TEST_F(AnalyzerTest, TargetRefersToSources) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/file_name.cc" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- t->sources().push_back(SourceFile("//dir/file_name.cc"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/file_name.cc" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that a target is marked as affected if its public headers are modified.
-TEST_F(AnalyzerTest, TargetRefersToPublicHeaders) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/header_name.h" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- t->public_headers().push_back(SourceFile("//dir/header_name.h"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/header_name.h" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that a target is marked as affected if its inputs are modified.
-TEST_F(AnalyzerTest, TargetRefersToInputs) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/extra_input.cc" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- SourceFile extra_input(SourceFile("//dir/extra_input.cc"));
- t->config_values().inputs().push_back(extra_input);
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/extra_input.cc" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-
- t->config_values().inputs().clear();
- Config* c = MakeConfig("//dir", "config_name");
- builder_.ItemDefined(std::unique_ptr<Item>(c));
- c->own_values().inputs().push_back(extra_input);
- t->configs().push_back(LabelConfigPair(c));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/extra_input.cc" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that a target is marked as affected if its data are modified.
-TEST_F(AnalyzerTest, TargetRefersToData) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/data.html" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- t->data().push_back("//dir/data.html");
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/data.html" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that a target is marked as affected if the target is an action and its
-// action script is modified.
-TEST_F(AnalyzerTest, TargetRefersToActionScript) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- t->set_output_type(Target::ACTION);
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/script.py" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- t->action_values().set_script(SourceFile("//dir/script.py"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/script.py" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that a target is marked as affected if its build dependency files are
-// modified.
-TEST_F(AnalyzerTest, TargetRefersToBuildDependencyFiles) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- t->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that if a target is marked as affected, then it propagates to dependent
-// test_targets.
-TEST_F(AnalyzerTest, AffectedTargetpropagatesToDependentTargets) {
- Target* t1 = MakeTarget("//dir", "target_name1");
- Target* t2 = MakeTarget("//dir", "target_name2");
- Target* t3 = MakeTarget("//dir", "target_name3");
- t1->private_deps().push_back(LabelTargetPair(t2));
- t2->private_deps().push_back(LabelTargetPair(t3));
- builder_.ItemDefined(std::unique_ptr<Item>(t1));
- builder_.ItemDefined(std::unique_ptr<Item>(t2));
- builder_.ItemDefined(std::unique_ptr<Item>(t3));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- t3->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name1","//dir:target_name2"])"
- "}");
-}
-
-// Tests that if a config is marked as affected, then it propagates to dependent
-// test_targets.
-TEST_F(AnalyzerTest, AffectedConfigpropagatesToDependentTargets) {
- Config* c = MakeConfig("//dir", "config_name");
- Target* t = MakeTarget("//dir", "target_name");
- t->configs().push_back(LabelConfigPair(c));
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- builder_.ItemDefined(std::unique_ptr<Item>(c));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- c->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that if toolchain is marked as affected, then it propagates to
-// dependent test_targets.
-TEST_F(AnalyzerTest, AffectedToolchainpropagatesToDependentTargets) {
- Target* target = MakeTarget("//dir", "target_name");
- target->set_output_type(Target::EXECUTABLE);
- Toolchain* toolchain = new Toolchain(&settings_, settings_.toolchain_label());
-
- // The tool is not used anywhere, but is required to construct the dependency
- // between a target and the toolchain.
- std::unique_ptr<Tool> fake_tool = Tool::CreateTool(CTool::kCToolLink);
- fake_tool->set_outputs(
- SubstitutionList::MakeForTest("//out/debug/output.txt"));
- toolchain->SetTool(std::move(fake_tool));
- builder_.ItemDefined(std::unique_ptr<Item>(target));
- builder_.ItemDefined(std::unique_ptr<Item>(toolchain));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//tc/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- toolchain->build_dependency_files().insert(SourceFile("//tc/BUILD.gn"));
- RunAnalyzerTest(
- R"({
- "files": [ "//tc/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that if a pool is marked as affected, then it propagates to dependent
-// targets.
-TEST_F(AnalyzerTest, AffectedPoolpropagatesToDependentTargets) {
- Target* t = MakeTarget("//dir", "target_name");
- t->set_output_type(Target::ACTION);
- Pool* p = MakePool("//dir", "pool_name");
- t->action_values().set_pool(LabelPtrPair<Pool>(p));
-
- builder_.ItemDefined(std::unique_ptr<Item>(t));
- builder_.ItemDefined(std::unique_ptr<Item>(p));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-
- p->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["all"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Tests that when dependency was found, the "compile_targets" in the output is
-// not "all".
-TEST_F(AnalyzerTest, CompileTargetsAllWasPruned) {
- Target* t1 = MakeTarget("//dir", "target_name1");
- Target* t2 = MakeTarget("//dir", "target_name2");
- builder_.ItemDefined(std::unique_ptr<Item>(t1));
- builder_.ItemDefined(std::unique_ptr<Item>(t2));
- t2->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": []
- })",
- "{"
- R"("compile_targets":["//dir:target_name2"],)"
- R"/("status":"Found dependency",)/"
- R"("test_targets":[])"
- "}");
-}
-
-// Tests that output is "No dependency" when no dependency is found.
-TEST_F(AnalyzerTest, NoDependency) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//dir/BUILD.gn" ],
- "additional_compile_targets": [ "all" ],
- "test_targets": []
- })",
- "{"
- R"("compile_targets":[],)"
- R"/("status":"No dependency",)/"
- R"("test_targets":[])"
- "}");
-}
-
-// Tests that output is "No dependency" when no files or targets are provided.
-TEST_F(AnalyzerTest, NoFilesNoTargets) {
- RunAnalyzerTest(
- R"({
- "files": [],
- "additional_compile_targets": [],
- "test_targets": []
- })",
- "{"
- R"("compile_targets":[],)"
- R"("status":"No dependency",)"
- R"("test_targets":[])"
- "}");
-}
-
-// Tests that output displays proper error message when given files aren't
-// source-absolute or absolute path.
-TEST_F(AnalyzerTest, FilesArentSourceAbsolute) {
- RunAnalyzerTest(
- R"({
- "files": [ "a.cc" ],
- "additional_compile_targets": [],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("error":)"
- R"("\"a.cc\" is not a source-absolute or absolute path.",)"
- R"("invalid_targets":[])"
- "}");
-}
-
-// Tests that output displays proper error message when input is illy-formed.
-TEST_F(AnalyzerTest, WrongInputFields) {
- RunAnalyzerTest(
- R"({
- "files": [ "//a.cc" ],
- "compile_targets": [],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("error":)"
- R"("Input does not have a key named )"
- R"(\"additional_compile_targets\" with a list value.",)"
- R"("invalid_targets":[])"
- "}");
-}
-
-// Bails out early with "Found dependency (all)" if dot file is modified.
-TEST_F(AnalyzerTest, DotFileWasModified) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//.gn" ],
- "additional_compile_targets": [],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["//dir:target_name"],)"
- R"/("status":"Found dependency (all)",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Bails out early with "Found dependency (all)" if master build config file is
-// modified.
-TEST_F(AnalyzerTest, BuildConfigFileWasModified) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//build/config/BUILDCONFIG.gn" ],
- "additional_compile_targets": [],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["//dir:target_name"],)"
- R"/("status":"Found dependency (all)",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-// Bails out early with "Found dependency (all)" if a build args dependency file
-// is modified.
-TEST_F(AnalyzerTest, BuildArgsDependencyFileWasModified) {
- Target* t = MakeTarget("//dir", "target_name");
- builder_.ItemDefined(std::unique_ptr<Item>(t));
-
- RunAnalyzerTest(
- R"({
- "files": [ "//build/default_args.gn" ],
- "additional_compile_targets": [],
- "test_targets": [ "//dir:target_name" ]
- })",
- "{"
- R"("compile_targets":["//dir:target_name"],)"
- R"/("status":"Found dependency (all)",)/"
- R"("test_targets":["//dir:target_name"])"
- "}");
-}
-
-} // namespace gn_analyzer_unittest
diff --git a/gn/tools/gn/args.cc b/gn/tools/gn/args.cc
deleted file mode 100644
index 802c3731d5a..00000000000
--- a/gn/tools/gn/args.cc
+++ /dev/null
@@ -1,422 +0,0 @@
-// 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 "tools/gn/args.h"
-
-#include "tools/gn/settings.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/variables.h"
-#include "util/build_config.h"
-#include "util/sys_info.h"
-
-const char kBuildArgs_Help[] =
- R"(Build Arguments Overview
-
- Build arguments are variables passed in from outside of the build that build
- files can query to determine how the build works.
-
-How build arguments are set
-
- First, system default arguments are set based on the current system. The
- built-in arguments are:
- - host_cpu
- - host_os
- - current_cpu
- - current_os
- - target_cpu
- - target_os
-
- Next, project-specific overrides are applied. These are specified inside
- the default_args variable of //.gn. See "gn help dotfile" for more.
-
- If specified, arguments from the --args command line flag are used. If that
- flag is not specified, args from previous builds in the build directory will
- be used (this is in the file args.gn in the build directory).
-
- Last, for targets being compiled with a non-default toolchain, the toolchain
- overrides are applied. These are specified in the toolchain_args section of a
- toolchain definition. The use-case for this is that a toolchain may be
- building code for a different platform, and that it may want to always
- specify Posix, for example. See "gn help toolchain" for more.
-
- If you specify an override for a build argument that never appears in a
- "declare_args" call, a nonfatal error will be displayed.
-
-Examples
-
- gn args out/FooBar
- Create the directory out/FooBar and open an editor. You would type
- something like this into that file:
- enable_doom_melon=false
- os="android"
-
- gn gen out/FooBar --args="enable_doom_melon=true os=\"android\""
- This will overwrite the build directory with the given arguments. (Note
- that the quotes inside the args command will usually need to be escaped
- for your shell to pass through strings values.)
-
-How build arguments are used
-
- If you want to use an argument, you use declare_args() and specify default
- values. These default values will apply if none of the steps listed in the
- "How build arguments are set" section above apply to the given argument, but
- the defaults will not override any of these.
-
- Often, the root build config file will declare global arguments that will be
- passed to all buildfiles. Individual build files can also specify arguments
- that apply only to those files. It is also useful to specify build args in an
- "import"-ed file if you want such arguments to apply to multiple buildfiles.
-)";
-
-namespace {
-
-// Removes all entries in |overrides| that are in |declared_overrides|.
-void RemoveDeclaredOverrides(const Scope::KeyValueMap& declared_arguments,
- Scope::KeyValueMap* overrides) {
- for (Scope::KeyValueMap::iterator override = overrides->begin();
- override != overrides->end();) {
- if (declared_arguments.find(override->first) == declared_arguments.end())
- ++override;
- else
- overrides->erase(override++);
- }
-}
-
-} // namespace
-
-Args::ValueWithOverride::ValueWithOverride()
- : default_value(), has_override(false), override_value() {}
-
-Args::ValueWithOverride::ValueWithOverride(const Value& def_val)
- : default_value(def_val), has_override(false), override_value() {}
-
-Args::ValueWithOverride::~ValueWithOverride() = default;
-
-Args::Args() = default;
-
-Args::Args(const Args& other)
- : overrides_(other.overrides_),
- all_overrides_(other.all_overrides_),
- declared_arguments_per_toolchain_(
- other.declared_arguments_per_toolchain_),
- toolchain_overrides_(other.toolchain_overrides_) {}
-
-Args::~Args() = default;
-
-void Args::AddArgOverride(const char* name, const Value& value) {
- std::lock_guard<std::mutex> lock(lock_);
-
- overrides_[base::StringPiece(name)] = value;
- all_overrides_[base::StringPiece(name)] = value;
-}
-
-void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
- std::lock_guard<std::mutex> lock(lock_);
-
- for (const auto& cur_override : overrides) {
- overrides_[cur_override.first] = cur_override.second;
- all_overrides_[cur_override.first] = cur_override.second;
- }
-}
-
-void Args::AddDefaultArgOverrides(const Scope::KeyValueMap& overrides) {
- std::lock_guard<std::mutex> lock(lock_);
- for (const auto& cur_override : overrides)
- overrides_[cur_override.first] = cur_override.second;
-}
-
-const Value* Args::GetArgOverride(const char* name) const {
- std::lock_guard<std::mutex> lock(lock_);
-
- Scope::KeyValueMap::const_iterator found =
- all_overrides_.find(base::StringPiece(name));
- if (found == all_overrides_.end())
- return nullptr;
- return &found->second;
-}
-
-void Args::SetupRootScope(Scope* dest,
- const Scope::KeyValueMap& toolchain_overrides) const {
- std::lock_guard<std::mutex> lock(lock_);
-
- SetSystemVarsLocked(dest);
-
- // Apply overrides for already declared args.
- // (i.e. the system vars we set above)
- ApplyOverridesLocked(overrides_, dest);
- ApplyOverridesLocked(toolchain_overrides, dest);
-
- OverridesForToolchainLocked(dest) = toolchain_overrides;
-
- SaveOverrideRecordLocked(toolchain_overrides);
-}
-
-bool Args::DeclareArgs(const Scope::KeyValueMap& args,
- Scope* scope_to_set,
- Err* err) const {
- std::lock_guard<std::mutex> lock(lock_);
-
- Scope::KeyValueMap& declared_arguments(
- DeclaredArgumentsForToolchainLocked(scope_to_set));
-
- const Scope::KeyValueMap& toolchain_overrides(
- OverridesForToolchainLocked(scope_to_set));
-
- for (const auto& arg : args) {
- // Verify that the value hasn't already been declared. We want each value
- // to be declared only once.
- //
- // The tricky part is that a buildfile can be interpreted multiple times
- // when used from different toolchains, so we can't just check that we've
- // seen it before. Instead, we check that the location matches.
- Scope::KeyValueMap::iterator previously_declared =
- declared_arguments.find(arg.first);
- if (previously_declared != declared_arguments.end()) {
- if (previously_declared->second.origin() != arg.second.origin()) {
- // Declaration location mismatch.
- *err = Err(
- arg.second.origin(), "Duplicate build argument declaration.",
- "Here you're declaring an argument that was already declared "
- "elsewhere.\nYou can only declare each argument once in the entire "
- "build so there is one\ncanonical place for documentation and the "
- "default value. Either move this\nargument to the build config "
- "file (for visibility everywhere) or to a .gni file\nthat you "
- "\"import\" from the files where you need it (preferred).");
- err->AppendSubErr(Err(previously_declared->second.origin(),
- "Previous declaration.",
- "See also \"gn help buildargs\" for more on how "
- "build arguments work."));
- return false;
- }
- } else {
- declared_arguments.insert(arg);
- }
-
- // In all the cases below, mark the variable used. If a variable is set
- // that's only used in one toolchain, we don't want to report unused
- // variable errors in other toolchains. Also, in some cases it's reasonable
- // for the build file to overwrite the value with a different value based
- // on some other condition without dereferencing the value first.
-
- // Check whether this argument has been overridden on the toolchain level
- // and use the override instead.
- Scope::KeyValueMap::const_iterator toolchain_override =
- toolchain_overrides.find(arg.first);
- if (toolchain_override != toolchain_overrides.end()) {
- scope_to_set->SetValue(toolchain_override->first,
- toolchain_override->second,
- toolchain_override->second.origin());
- scope_to_set->MarkUsed(arg.first);
- continue;
- }
-
- // Check whether this argument has been overridden and use the override
- // instead.
- Scope::KeyValueMap::const_iterator override = overrides_.find(arg.first);
- if (override != overrides_.end()) {
- scope_to_set->SetValue(override->first, override->second,
- override->second.origin());
- scope_to_set->MarkUsed(override->first);
- continue;
- }
-
- scope_to_set->SetValue(arg.first, arg.second, arg.second.origin());
- scope_to_set->MarkUsed(arg.first);
- }
-
- return true;
-}
-
-bool Args::VerifyAllOverridesUsed(Err* err) const {
- std::lock_guard<std::mutex> lock(lock_);
- Scope::KeyValueMap unused_overrides(all_overrides_);
- for (const auto& map_pair : declared_arguments_per_toolchain_)
- RemoveDeclaredOverrides(map_pair.second, &unused_overrides);
-
- if (unused_overrides.empty())
- return true;
-
- // Some assignments in args.gn had no effect. Show an error for the first
- // unused assignment.
- base::StringPiece name = unused_overrides.begin()->first;
- const Value& value = unused_overrides.begin()->second;
-
- std::string err_help(
- "The variable \"" + name +
- "\" was set as a build argument\n"
- "but never appeared in a declare_args() block in any buildfile.\n\n"
- "To view all possible args, run \"gn args --list <out_dir>\"");
-
- // Use all declare_args for a spelling suggestion.
- std::vector<base::StringPiece> candidates;
- for (const auto& map_pair : declared_arguments_per_toolchain_) {
- for (const auto& declared_arg : map_pair.second)
- candidates.push_back(declared_arg.first);
- }
- base::StringPiece suggestion = SpellcheckString(name, candidates);
- if (!suggestion.empty())
- err_help = "Did you mean \"" + suggestion + "\"?\n\n" + err_help;
-
- *err = Err(value.origin(), "Build argument has no effect.", err_help);
- return false;
-}
-
-Args::ValueWithOverrideMap Args::GetAllArguments() const {
- ValueWithOverrideMap result;
-
- std::lock_guard<std::mutex> lock(lock_);
-
- // Sort the keys from declared_arguments_per_toolchain_ so
- // the return value will be deterministic.
- std::vector<const Settings*> keys;
- keys.reserve(declared_arguments_per_toolchain_.size());
- for (const auto& map_pair : declared_arguments_per_toolchain_) {
- keys.push_back(map_pair.first);
- }
- std::sort(keys.begin(), keys.end(),
- [](const Settings* a, const Settings* b) -> bool {
- return a->toolchain_label() < b->toolchain_label();
- });
-
- // Default values.
- for (const auto& key : keys) {
- const auto& value = declared_arguments_per_toolchain_[key];
- for (const auto& arg : value)
- result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second)));
- }
-
- // Merge in overrides.
- for (const auto& over : overrides_) {
- auto found = result.find(over.first);
- if (found != result.end()) {
- found->second.has_override = true;
- found->second.override_value = over.second;
- }
- }
-
- return result;
-}
-
-void Args::SetSystemVarsLocked(Scope* dest) const {
- // Host OS.
- const char* os = nullptr;
-#if defined(OS_WIN)
- os = "win";
-#elif defined(OS_MACOSX)
- os = "mac";
-#elif defined(OS_LINUX)
- os = "linux";
-#elif defined(OS_FREEBSD)
- os = "freebsd";
-#elif defined(OS_AIX)
- os = "aix";
-#elif defined(OS_OPENBSD)
- os = "openbsd";
-#else
-#error Unknown OS type.
-#endif
- // NOTE: Adding a new port? Please follow
- // https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
-
- // Host architecture.
- static const char kX86[] = "x86";
- static const char kX64[] = "x64";
- static const char kArm[] = "arm";
- static const char kArm64[] = "arm64";
- static const char kMips[] = "mipsel";
- static const char kMips64[] = "mips64el";
- static const char kS390X[] = "s390x";
- static const char kPPC64[] = "ppc64";
- const char* arch = nullptr;
-
- // Set the host CPU architecture based on the underlying OS, not
- // whatever the current bit-tedness of the GN binary is.
- std::string os_arch = OperatingSystemArchitecture();
- if (os_arch == "x86")
- arch = kX86;
- else if (os_arch == "x86_64")
- arch = kX64;
- else if (os_arch.substr(0, 3) == "arm")
- arch = kArm;
- else if (os_arch == "aarch64")
- arch = kArm64;
- else if (os_arch == "mips")
- arch = kMips;
- else if (os_arch == "mips64")
- arch = kMips64;
- else if (os_arch == "s390x")
- arch = kS390X;
- else if (os_arch == "ppc64" || os_arch == "ppc64le")
- // We handle the endianness inside //build/config/host_byteorder.gni.
- // This allows us to use the same toolchain as ppc64 BE
- // and specific flags are included using the host_byteorder logic.
- arch = kPPC64;
- else
- CHECK(false) << "OS architecture not handled. (" << os_arch << ")";
-
- // Save the OS and architecture as build arguments that are implicitly
- // declared. This is so they can be overridden in a toolchain build args
- // override, and so that they will appear in the "gn args" output.
- Value empty_string(nullptr, std::string());
-
- Value os_val(nullptr, std::string(os));
- dest->SetValue(variables::kHostOs, os_val, nullptr);
- dest->SetValue(variables::kTargetOs, empty_string, nullptr);
- dest->SetValue(variables::kCurrentOs, empty_string, nullptr);
-
- Value arch_val(nullptr, std::string(arch));
- dest->SetValue(variables::kHostCpu, arch_val, nullptr);
- dest->SetValue(variables::kTargetCpu, empty_string, nullptr);
- dest->SetValue(variables::kCurrentCpu, empty_string, nullptr);
-
- Scope::KeyValueMap& declared_arguments(
- DeclaredArgumentsForToolchainLocked(dest));
- declared_arguments[variables::kHostOs] = os_val;
- declared_arguments[variables::kCurrentOs] = empty_string;
- declared_arguments[variables::kTargetOs] = empty_string;
- declared_arguments[variables::kHostCpu] = arch_val;
- declared_arguments[variables::kCurrentCpu] = empty_string;
- declared_arguments[variables::kTargetCpu] = empty_string;
-
- // Mark these variables used so the build config file can override them
- // without geting a warning about overwriting an unused variable.
- dest->MarkUsed(variables::kHostCpu);
- dest->MarkUsed(variables::kCurrentCpu);
- dest->MarkUsed(variables::kTargetCpu);
- dest->MarkUsed(variables::kHostOs);
- dest->MarkUsed(variables::kCurrentOs);
- dest->MarkUsed(variables::kTargetOs);
-}
-
-void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
- Scope* scope) const {
- const Scope::KeyValueMap& declared_arguments(
- DeclaredArgumentsForToolchainLocked(scope));
-
- // Only set a value if it has been declared.
- for (const auto& val : values) {
- Scope::KeyValueMap::const_iterator declared =
- declared_arguments.find(val.first);
-
- if (declared == declared_arguments.end())
- continue;
-
- scope->SetValue(val.first, val.second, val.second.origin());
- }
-}
-
-void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const {
- for (const auto& val : values)
- all_overrides_[val.first] = val.second;
-}
-
-Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked(
- Scope* scope) const {
- return declared_arguments_per_toolchain_[scope->settings()];
-}
-
-Scope::KeyValueMap& Args::OverridesForToolchainLocked(Scope* scope) const {
- return toolchain_overrides_[scope->settings()];
-}
diff --git a/gn/tools/gn/args.h b/gn/tools/gn/args.h
deleted file mode 100644
index 4a4bc7c8b36..00000000000
--- a/gn/tools/gn/args.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// 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 TOOLS_GN_ARGS_H_
-#define TOOLS_GN_ARGS_H_
-
-#include <map>
-#include <mutex>
-#include <set>
-#include <unordered_map>
-
-#include "base/macros.h"
-#include "tools/gn/scope.h"
-
-class Err;
-class SourceFile;
-
-extern const char kBuildArgs_Help[];
-
-// Manages build arguments. It stores the global arguments specified on the
-// command line, and sets up the root scope with the proper values.
-//
-// This class tracks accesses so we can report errors about unused variables.
-// The use case is if the user specifies an override on the command line, but
-// no buildfile actually uses that variable. We want to be able to report that
-// the argument was unused.
-class Args {
- public:
- struct ValueWithOverride {
- ValueWithOverride();
- ValueWithOverride(const Value& def_val);
- ~ValueWithOverride();
-
- Value default_value; // Default value given in declare_args.
-
- bool has_override; // True indicates override_value is valid.
- Value override_value; // From .gn or the current build's "gn args".
- };
- using ValueWithOverrideMap = std::map<base::StringPiece, ValueWithOverride>;
-
- Args();
- Args(const Args& other);
- ~Args();
-
- // Specifies overrides of the build arguments. These are normally specified
- // on the command line.
- void AddArgOverride(const char* name, const Value& value);
- void AddArgOverrides(const Scope::KeyValueMap& overrides);
-
- // Specifies default overrides of the build arguments. These are normally
- // specified in the .gn file.
- void AddDefaultArgOverrides(const Scope::KeyValueMap& overrides);
-
- // Returns the value corresponding to the given argument name, or NULL if no
- // argument is set.
- const Value* GetArgOverride(const char* name) const;
-
- // Sets up the root scope for a toolchain. This applies the default system
- // flags and saves the toolchain overrides so they can be applied to
- // declare_args blocks that appear when loading files in that toolchain.
- void SetupRootScope(Scope* dest,
- const Scope::KeyValueMap& toolchain_overrides) const;
-
- // Sets up the given scope with arguments passed in.
- //
- // If the values specified in the args are not already set, the values in
- // the args list will be used (which are assumed to be the defaults), but
- // they will not override the system defaults or the current overrides.
- //
- // All args specified in the input will be marked as "used".
- //
- // On failure, the err will be set and it will return false.
- bool DeclareArgs(const Scope::KeyValueMap& args,
- Scope* scope_to_set,
- Err* err) const;
-
- // Checks to see if any of the overrides ever used were never declared as
- // arguments. If there are, this returns false and sets the error.
- bool VerifyAllOverridesUsed(Err* err) const;
-
- // Returns information about all arguments, both defaults and overrides.
- // This is used for the help system which is not performance critical. Use a
- // map instead of a hash map so the arguments are sorted alphabetically.
- ValueWithOverrideMap GetAllArguments() const;
-
- // Returns the set of build files that may affect the build arguments, please
- // refer to Scope for how this is determined.
- const std::set<SourceFile>& build_args_dependency_files() const {
- return build_args_dependency_files_;
- }
-
- void set_build_args_dependency_files(
- const std::set<SourceFile>& build_args_dependency_files) {
- build_args_dependency_files_ = build_args_dependency_files;
- }
-
- private:
- using ArgumentsPerToolchain =
- std::unordered_map<const Settings*, Scope::KeyValueMap>;
-
- // Sets the default config based on the current system.
- void SetSystemVarsLocked(Scope* scope) const;
-
- // Sets the given already declared vars on the given scope.
- void ApplyOverridesLocked(const Scope::KeyValueMap& values,
- Scope* scope) const;
-
- void SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const;
-
- // Returns the KeyValueMap used for arguments declared for the specified
- // toolchain.
- Scope::KeyValueMap& DeclaredArgumentsForToolchainLocked(Scope* scope) const;
-
- // Returns the KeyValueMap used for overrides for the specified
- // toolchain.
- Scope::KeyValueMap& OverridesForToolchainLocked(Scope* scope) const;
-
- // Since this is called during setup which we assume is single-threaded,
- // this is not protected by the lock. It should be set only during init.
- Scope::KeyValueMap overrides_;
-
- mutable std::mutex lock_;
-
- // Maintains a list of all overrides we've ever seen. This is the main
- // |overrides_| as well as toolchain overrides. Tracking this allows us to
- // check for overrides that were specified but never used.
- mutable Scope::KeyValueMap all_overrides_;
-
- // Maps from Settings (which corresponds to a toolchain) to the map of
- // declared variables. This is used to tracks all variables declared in any
- // buildfile. This is so we can see if the user set variables on the command
- // line that are not used anywhere. Each map is toolchain specific as each
- // toolchain may define variables in different locations.
- mutable ArgumentsPerToolchain declared_arguments_per_toolchain_;
-
- // Overrides for individual toolchains. This is necessary so we
- // can apply the correct override for the current toolchain, once
- // we see an argument declaration.
- mutable ArgumentsPerToolchain toolchain_overrides_;
-
- std::set<SourceFile> build_args_dependency_files_;
-
- DISALLOW_ASSIGN(Args);
-};
-
-#endif // TOOLS_GN_ARGS_H_
diff --git a/gn/tools/gn/args_unittest.cc b/gn/tools/gn/args_unittest.cc
deleted file mode 100644
index 49952295c91..00000000000
--- a/gn/tools/gn/args_unittest.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 "tools/gn/args.h"
-
-#include "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-// Assertions for VerifyAllOverridesUsed() and DeclareArgs() with multiple
-// toolchains.
-TEST(ArgsTest, VerifyAllOverridesUsed) {
- TestWithScope setup1, setup2;
- Args args;
- Scope::KeyValueMap key_value_map1;
- Err err;
- LiteralNode assignment1;
-
- setup1.scope()->SetValue("a", Value(nullptr, true), &assignment1);
- setup1.scope()->GetCurrentScopeValues(&key_value_map1);
- EXPECT_TRUE(args.DeclareArgs(key_value_map1, setup1.scope(), &err));
-
- LiteralNode assignment2;
- setup2.scope()->SetValue("b", Value(nullptr, true), &assignment2);
- Scope::KeyValueMap key_value_map2;
- setup2.scope()->GetCurrentScopeValues(&key_value_map2);
- EXPECT_TRUE(args.DeclareArgs(key_value_map2, setup2.scope(), &err));
-
- // Override "a", shouldn't see any errors as "a" was defined.
- args.AddArgOverride("a", Value(nullptr, true));
- EXPECT_TRUE(args.VerifyAllOverridesUsed(&err));
-
- // Override "a", & "b", shouldn't see any errors as both were defined.
- args.AddArgOverride("b", Value(nullptr, true));
- EXPECT_TRUE(args.VerifyAllOverridesUsed(&err));
-
- // Override "a", "b" and "c", should fail as "c" was not defined.
- args.AddArgOverride("c", Value(nullptr, true));
- EXPECT_FALSE(args.VerifyAllOverridesUsed(&err));
-}
-
-// Ensure that arg overrides get only set after the they were declared.
-TEST(ArgsTest, VerifyOverrideScope) {
- TestWithScope setup;
- Args args;
- Err err;
-
- args.AddArgOverride("a", Value(nullptr, "avalue"));
- args.AddArgOverride("current_os", Value(nullptr, "theiros"));
-
- Scope::KeyValueMap toolchain_overrides;
- toolchain_overrides["b"] = Value(nullptr, "bvalue");
- toolchain_overrides["current_os"] = Value(nullptr, "myos");
- args.SetupRootScope(setup.scope(), toolchain_overrides);
-
- // Overrides of arguments not yet declared aren't applied yet.
- EXPECT_EQ(nullptr, setup.scope()->GetValue("a"));
- EXPECT_EQ(nullptr, setup.scope()->GetValue("b"));
-
- // |current_os| is a system var. and already declared.
- // Thus it should have our override value.
- ASSERT_NE(nullptr, setup.scope()->GetValue("current_os"));
- EXPECT_EQ(Value(nullptr, "myos"), *setup.scope()->GetValue("current_os"));
-
- Scope::KeyValueMap key_value_map1;
- key_value_map1["a"] = Value(nullptr, "avalue2");
- key_value_map1["b"] = Value(nullptr, "bvalue2");
- key_value_map1["c"] = Value(nullptr, "cvalue2");
- EXPECT_TRUE(args.DeclareArgs(key_value_map1, setup.scope(), &err));
-
- ASSERT_NE(nullptr, setup.scope()->GetValue("a"));
- EXPECT_EQ(Value(nullptr, "avalue"), *setup.scope()->GetValue("a"));
-
- ASSERT_NE(nullptr, setup.scope()->GetValue("b"));
- EXPECT_EQ(Value(nullptr, "bvalue"), *setup.scope()->GetValue("b"));
-
- // This wasn't overwritten, so it should have the default value.
- ASSERT_NE(nullptr, setup.scope()->GetValue("c"));
- EXPECT_EQ(Value(nullptr, "cvalue2"), *setup.scope()->GetValue("c"));
-}
diff --git a/gn/tools/gn/binary_target_generator.cc b/gn/tools/gn/binary_target_generator.cc
deleted file mode 100644
index 4feadbabb68..00000000000
--- a/gn/tools/gn/binary_target_generator.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-// 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 "tools/gn/binary_target_generator.h"
-
-#include "tools/gn/config_values_generator.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/rust_values_generator.h"
-#include "tools/gn/rust_variables.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-BinaryTargetGenerator::BinaryTargetGenerator(
- Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Target::OutputType type,
- Err* err)
- : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
-
-BinaryTargetGenerator::~BinaryTargetGenerator() = default;
-
-void BinaryTargetGenerator::DoRun() {
- target_->set_output_type(output_type_);
-
- if (!FillOutputName())
- return;
-
- if (!FillOutputPrefixOverride())
- return;
-
- if (!FillOutputDir())
- return;
-
- if (!FillOutputExtension())
- return;
-
- if (!FillSources())
- return;
-
- if (!FillPublic())
- return;
-
- if (!FillFriends())
- return;
-
- if (!FillCheckIncludes())
- return;
-
- if (!FillConfigs())
- return;
-
- if (!FillAllowCircularIncludesFrom())
- return;
-
- if (!FillCompleteStaticLib())
- return;
-
- if (!ValidateSources())
- return;
-
- if (target_->source_types_used().RustSourceUsed()) {
- RustTargetGenerator rustgen(target_, scope_, function_call_, err_);
- rustgen.Run();
- if (err_->has_error())
- return;
- }
-
- // Config values (compiler flags, etc.) set directly on this target.
- ConfigValuesGenerator gen(&target_->config_values(), scope_,
- scope_->GetSourceDir(), err_);
- gen.Run();
- if (err_->has_error())
- return;
-}
-
-bool BinaryTargetGenerator::FillSources() {
- bool ret = TargetGenerator::FillSources();
- for (std::size_t i = 0; i < target_->sources().size(); ++i) {
- const auto& source = target_->sources()[i];
- switch (source.type()) {
- case SourceFile::SOURCE_CPP:
- case SourceFile::SOURCE_H:
- case SourceFile::SOURCE_C:
- case SourceFile::SOURCE_M:
- case SourceFile::SOURCE_MM:
- case SourceFile::SOURCE_S:
- case SourceFile::SOURCE_ASM:
- case SourceFile::SOURCE_O:
- case SourceFile::SOURCE_DEF:
- case SourceFile::SOURCE_GO:
- case SourceFile::SOURCE_RS:
- case SourceFile::SOURCE_RC:
- // These are allowed.
- break;
- case SourceFile::SOURCE_UNKNOWN:
- case SourceFile::SOURCE_NUMTYPES:
- *err_ =
- Err(scope_->GetValue(variables::kSources, true)->list_value()[i],
- std::string("Only source, header, and object files belong in "
- "the sources of a ") +
- Target::GetStringForOutputType(target_->output_type()) +
- ". " + source.value() + " is not one of the valid types.");
- }
-
- target_->source_types_used().Set(source.type());
- }
- return ret;
-}
-
-bool BinaryTargetGenerator::FillCompleteStaticLib() {
- if (target_->output_type() == Target::STATIC_LIBRARY) {
- const Value* value = scope_->GetValue(variables::kCompleteStaticLib, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
- return false;
- target_->set_complete_static_lib(value->boolean_value());
- }
- return true;
-}
-
-bool BinaryTargetGenerator::FillFriends() {
- const Value* value = scope_->GetValue(variables::kFriend, true);
- if (value) {
- return ExtractListOfLabelPatterns(*value, scope_->GetSourceDir(),
- &target_->friends(), err_);
- }
- return true;
-}
-
-bool BinaryTargetGenerator::FillOutputName() {
- const Value* value = scope_->GetValue(variables::kOutputName, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
- target_->set_output_name(value->string_value());
- return true;
-}
-
-bool BinaryTargetGenerator::FillOutputPrefixOverride() {
- const Value* value = scope_->GetValue(variables::kOutputPrefixOverride, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
- return false;
- target_->set_output_prefix_override(value->boolean_value());
- return true;
-}
-
-bool BinaryTargetGenerator::FillOutputDir() {
- const Value* value = scope_->GetValue(variables::kOutputDir, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- if (value->string_value().empty())
- return true; // Treat empty string as the default and do nothing.
-
- const BuildSettings* build_settings = scope_->settings()->build_settings();
- SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
- *value, err_, build_settings->root_path_utf8());
- if (err_->has_error())
- return false;
-
- if (!EnsureStringIsInOutputDir(build_settings->build_dir(), dir.value(),
- value->origin(), err_))
- return false;
- target_->set_output_dir(dir);
- return true;
-}
-
-bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
- const Value* value =
- scope_->GetValue(variables::kAllowCircularIncludesFrom, true);
- if (!value)
- return true;
-
- UniqueVector<Label> circular;
- ExtractListOfUniqueLabels(*value, scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), &circular, err_);
- if (err_->has_error())
- return false;
-
- // Validate that all circular includes entries are in the deps.
- for (const auto& cur : circular) {
- bool found_dep = false;
- for (const auto& dep_pair : target_->GetDeps(Target::DEPS_LINKED)) {
- if (dep_pair.label == cur) {
- found_dep = true;
- break;
- }
- }
- if (!found_dep) {
- *err_ = Err(*value, "Label not in deps.",
- "The label \"" + cur.GetUserVisibleName(false) +
- "\"\nwas not in the deps of this target. "
- "allow_circular_includes_from only allows\ntargets "
- "present in the "
- "deps.");
- return false;
- }
- }
-
- // Add to the set.
- for (const auto& cur : circular)
- target_->allow_circular_includes_from().insert(cur);
- return true;
-}
-
-bool BinaryTargetGenerator::ValidateSources() {
- // For Rust targets, if the only source file is the root `sources` can be
- // omitted/empty.
- if (scope_->GetValue(variables::kRustCrateRoot, false)) {
- target_->source_types_used().Set(SourceFile::SOURCE_RS);
- }
-
- if (target_->source_types_used().MixedSourceUsed()) {
- *err_ =
- Err(function_call_, "More than one language used in target sources.",
- "Mixed sources are not allowed, unless they are "
- "compilation-compatible (e.g. Objective C and C++).");
- return false;
- }
- return true;
-}
diff --git a/gn/tools/gn/binary_target_generator.h b/gn/tools/gn/binary_target_generator.h
deleted file mode 100644
index b88da50d128..00000000000
--- a/gn/tools/gn/binary_target_generator.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 TOOLS_GN_BINARY_TARGET_GENERATOR_H_
-#define TOOLS_GN_BINARY_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target.h"
-#include "tools/gn/target_generator.h"
-
-// Populates a Target with the values from a binary rule (executable, shared
-// library, or static library).
-class BinaryTargetGenerator : public TargetGenerator {
- public:
- BinaryTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Target::OutputType type,
- Err* err);
- ~BinaryTargetGenerator() override;
-
- protected:
- void DoRun() override;
- bool FillSources() override;
-
- private:
- bool FillCompleteStaticLib();
- bool FillFriends();
- bool FillOutputName();
- bool FillOutputPrefixOverride();
- bool FillOutputDir();
- bool FillAllowCircularIncludesFrom();
- bool ValidateSources();
-
- Target::OutputType output_type_;
-
- DISALLOW_COPY_AND_ASSIGN(BinaryTargetGenerator);
-};
-
-#endif // TOOLS_GN_BINARY_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/build_settings.cc b/gn/tools/gn/build_settings.cc
deleted file mode 100644
index 711c6b8c852..00000000000
--- a/gn/tools/gn/build_settings.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 "tools/gn/build_settings.h"
-
-#include <utility>
-
-#include "base/files/file_util.h"
-#include "tools/gn/filesystem_utils.h"
-
-BuildSettings::BuildSettings() = default;
-
-BuildSettings::BuildSettings(const BuildSettings& other)
- : dotfile_name_(other.dotfile_name_),
- root_path_(other.root_path_),
- root_path_utf8_(other.root_path_utf8_),
- secondary_source_path_(other.secondary_source_path_),
- python_path_(other.python_path_),
- build_config_file_(other.build_config_file_),
- arg_file_template_path_(other.arg_file_template_path_),
- build_dir_(other.build_dir_),
- build_args_(other.build_args_) {}
-
-void BuildSettings::SetRootTargetLabel(const Label& r) {
- root_target_label_ = r;
-}
-
-void BuildSettings::SetRootPath(const base::FilePath& r) {
- DCHECK(r.value()[r.value().size() - 1] != base::FilePath::kSeparators[0]);
- root_path_ = r.NormalizePathSeparatorsTo('/');
- root_path_utf8_ = FilePathToUTF8(root_path_);
-}
-
-void BuildSettings::SetSecondarySourcePath(const SourceDir& d) {
- secondary_source_path_ = GetFullPath(d).NormalizePathSeparatorsTo('/');
-}
-
-void BuildSettings::SetBuildDir(const SourceDir& d) {
- build_dir_ = d;
-}
-
-base::FilePath BuildSettings::GetFullPath(const SourceFile& file) const {
- return file.Resolve(root_path_).NormalizePathSeparatorsTo('/');
-}
-
-base::FilePath BuildSettings::GetFullPath(const SourceDir& dir) const {
- return dir.Resolve(root_path_).NormalizePathSeparatorsTo('/');
-}
-
-base::FilePath BuildSettings::GetFullPath(const std::string& path,
- bool as_file) const {
- return ResolvePath(path, as_file, root_path_).NormalizePathSeparatorsTo('/');
-}
-
-base::FilePath BuildSettings::GetFullPathSecondary(
- const SourceFile& file) const {
- return file.Resolve(secondary_source_path_).NormalizePathSeparatorsTo('/');
-}
-
-base::FilePath BuildSettings::GetFullPathSecondary(const SourceDir& dir) const {
- return dir.Resolve(secondary_source_path_).NormalizePathSeparatorsTo('/');
-}
-
-base::FilePath BuildSettings::GetFullPathSecondary(const std::string& path,
- bool as_file) const {
- return ResolvePath(path, as_file, secondary_source_path_)
- .NormalizePathSeparatorsTo('/');
-}
-
-void BuildSettings::ItemDefined(std::unique_ptr<Item> item) const {
- DCHECK(item);
- if (!item_defined_callback_.is_null())
- item_defined_callback_.Run(std::move(item));
-}
diff --git a/gn/tools/gn/build_settings.h b/gn/tools/gn/build_settings.h
deleted file mode 100644
index 646472f72d7..00000000000
--- a/gn/tools/gn/build_settings.h
+++ /dev/null
@@ -1,142 +0,0 @@
-// 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 TOOLS_GN_BUILD_SETTINGS_H_
-#define TOOLS_GN_BUILD_SETTINGS_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <utility>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "tools/gn/args.h"
-#include "tools/gn/label.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-
-class Item;
-
-// Settings for one build, which is one toplevel output directory. There
-// may be multiple Settings objects that refer to this, one for each toolchain.
-class BuildSettings {
- public:
- typedef base::Callback<void(std::unique_ptr<Item>)> ItemDefinedCallback;
- typedef base::Callback<void(const std::string&)> PrintCallback;
-
- BuildSettings();
- BuildSettings(const BuildSettings& other);
-
- // Root target label.
- const Label& root_target_label() const { return root_target_label_; }
- void SetRootTargetLabel(const Label& r);
-
- // Absolute path of the source root on the local system. Everything is
- // relative to this. Does not end in a [back]slash.
- const base::FilePath& root_path() const { return root_path_; }
- const base::FilePath& dotfile_name() const { return dotfile_name_; }
- const std::string& root_path_utf8() const { return root_path_utf8_; }
- void SetRootPath(const base::FilePath& r);
- void set_dotfile_name(const base::FilePath& d) { dotfile_name_ = d; }
-
- // When nonempty, specifies a parallel directory higherarchy in which to
- // search for buildfiles if they're not found in the root higherarchy. This
- // allows us to keep buildfiles in a separate tree during development.
- const base::FilePath& secondary_source_path() const {
- return secondary_source_path_;
- }
- void SetSecondarySourcePath(const SourceDir& d);
-
- // Path of the python executable to run scripts with.
- base::FilePath python_path() const { return python_path_; }
- void set_python_path(const base::FilePath& p) { python_path_ = p; }
-
- const SourceFile& build_config_file() const { return build_config_file_; }
- void set_build_config_file(const SourceFile& f) { build_config_file_ = f; }
-
- // Path to a file containing the default text to use when running `gn args`.
- const SourceFile& arg_file_template_path() const {
- return arg_file_template_path_;
- }
- void set_arg_file_template_path(const SourceFile& f) {
- arg_file_template_path_ = f;
- }
-
- // The build directory is the root of all output files. The default toolchain
- // files go into here, and non-default toolchains will have separate
- // toolchain-specific root directories inside this.
- const SourceDir& build_dir() const { return build_dir_; }
- void SetBuildDir(const SourceDir& dir);
-
- // The build args are normally specified on the command-line.
- Args& build_args() { return build_args_; }
- const Args& build_args() const { return build_args_; }
-
- // Returns the full absolute OS path cooresponding to the given file in the
- // root source tree.
- base::FilePath GetFullPath(const SourceFile& file) const;
- base::FilePath GetFullPath(const SourceDir& dir) const;
- // Works the same way as other GetFullPath.
- // Parameter as_file defines whether path should be treated as a
- // SourceFile or SourceDir value.
- base::FilePath GetFullPath(const std::string& path, bool as_file) const;
-
- // Returns the absolute OS path inside the secondary source path. Will return
- // an empty FilePath if the secondary source path is empty. When loading a
- // buildfile, the GetFullPath should always be consulted first.
- base::FilePath GetFullPathSecondary(const SourceFile& file) const;
- base::FilePath GetFullPathSecondary(const SourceDir& dir) const;
- // Works the same way as other GetFullPathSecondary.
- // Parameter as_file defines whether path should be treated as a
- // SourceFile or SourceDir value.
- base::FilePath GetFullPathSecondary(const std::string& path,
- bool as_file) const;
-
- // Called when an item is defined from a background thread.
- void ItemDefined(std::unique_ptr<Item> item) const;
- void set_item_defined_callback(ItemDefinedCallback cb) {
- item_defined_callback_ = cb;
- }
-
- // Defines a callback that will be used to override the behavior of the
- // print function. This is used in tests to collect print output. If the
- // callback is is_null() (the default) the output will be printed to the
- // console.
- const PrintCallback& print_callback() const { return print_callback_; }
- void set_print_callback(const PrintCallback& cb) { print_callback_ = cb; }
-
- // A list of files that can call exec_script(). If the returned pointer is
- // null, exec_script may be called from anywhere.
- const std::set<SourceFile>* exec_script_whitelist() const {
- return exec_script_whitelist_.get();
- }
- void set_exec_script_whitelist(std::unique_ptr<std::set<SourceFile>> list) {
- exec_script_whitelist_ = std::move(list);
- }
-
- private:
- Label root_target_label_;
- base::FilePath dotfile_name_;
- base::FilePath root_path_;
- std::string root_path_utf8_;
- base::FilePath secondary_source_path_;
- base::FilePath python_path_;
-
- SourceFile build_config_file_;
- SourceFile arg_file_template_path_;
- SourceDir build_dir_;
- Args build_args_;
-
- ItemDefinedCallback item_defined_callback_;
- PrintCallback print_callback_;
-
- std::unique_ptr<std::set<SourceFile>> exec_script_whitelist_;
-
- DISALLOW_ASSIGN(BuildSettings);
-};
-
-#endif // TOOLS_GN_BUILD_SETTINGS_H_
diff --git a/gn/tools/gn/builder.cc b/gn/tools/gn/builder.cc
deleted file mode 100644
index fc271c4a34c..00000000000
--- a/gn/tools/gn/builder.cc
+++ /dev/null
@@ -1,602 +0,0 @@
-// 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 "tools/gn/builder.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "tools/gn/action_values.h"
-#include "tools/gn/config.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-
-namespace {
-
-typedef BuilderRecord::BuilderRecordSet BuilderRecordSet;
-
-// Recursively looks in the tree for a given node, returning true if it
-// was found in the dependecy graph. This is used to see if a given node
-// participates in a cycle.
-//
-// If this returns true, the cycle will be in *path. This should point to an
-// empty vector for the first call. During computation, the path will contain
-// the full dependency path to the current node.
-//
-// Return false means no cycle was found.
-bool RecursiveFindCycle(const BuilderRecord* search_in,
- std::vector<const BuilderRecord*>* path) {
- path->push_back(search_in);
- for (auto* cur : search_in->unresolved_deps()) {
- std::vector<const BuilderRecord*>::iterator found =
- std::find(path->begin(), path->end(), cur);
- if (found != path->end()) {
- // This item is already in the set, we found the cycle. Everything before
- // the first definition of cur is irrelevant to the cycle.
- path->erase(path->begin(), found);
- path->push_back(cur);
- return true;
- }
-
- if (RecursiveFindCycle(cur, path))
- return true; // Found cycle.
- }
- path->pop_back();
- return false;
-}
-
-} // namespace
-
-Builder::Builder(Loader* loader) : loader_(loader) {}
-
-Builder::~Builder() = default;
-
-void Builder::ItemDefined(std::unique_ptr<Item> item) {
- ScopedTrace trace(TraceItem::TRACE_DEFINE_TARGET, item->label());
- trace.SetToolchain(item->settings()->toolchain_label());
-
- BuilderRecord::ItemType type = BuilderRecord::TypeOfItem(item.get());
-
- Err err;
- BuilderRecord* record =
- GetOrCreateRecordOfType(item->label(), item->defined_from(), type, &err);
- if (!record) {
- g_scheduler->FailWithError(err);
- return;
- }
-
- // Check that it's not been already defined.
- if (record->item()) {
- err = Err(item->defined_from(), "Duplicate definition.",
- "The item\n " + item->label().GetUserVisibleName(false) +
- "\nwas already defined.");
- err.AppendSubErr(
- Err(record->item()->defined_from(), "Previous definition:"));
- g_scheduler->FailWithError(err);
- return;
- }
-
- record->set_item(std::move(item));
-
- // Do target-specific dependency setup. This will also schedule dependency
- // loads for targets that are required.
- switch (type) {
- case BuilderRecord::ITEM_TARGET:
- TargetDefined(record, &err);
- break;
- case BuilderRecord::ITEM_CONFIG:
- ConfigDefined(record, &err);
- break;
- case BuilderRecord::ITEM_TOOLCHAIN:
- ToolchainDefined(record, &err);
- break;
- default:
- break;
- }
- if (err.has_error()) {
- g_scheduler->FailWithError(err);
- return;
- }
-
- if (record->can_resolve()) {
- if (!ResolveItem(record, &err)) {
- g_scheduler->FailWithError(err);
- return;
- }
- }
-}
-
-const Item* Builder::GetItem(const Label& label) const {
- const BuilderRecord* record = GetRecord(label);
- if (!record)
- return nullptr;
- return record->item();
-}
-
-const Toolchain* Builder::GetToolchain(const Label& label) const {
- const BuilderRecord* record = GetRecord(label);
- if (!record)
- return nullptr;
- if (!record->item())
- return nullptr;
- return record->item()->AsToolchain();
-}
-
-std::vector<const BuilderRecord*> Builder::GetAllRecords() const {
- std::vector<const BuilderRecord*> result;
- result.reserve(records_.size());
- for (const auto& record : records_)
- result.push_back(record.second.get());
- return result;
-}
-
-std::vector<const Item*> Builder::GetAllResolvedItems() const {
- std::vector<const Item*> result;
- result.reserve(records_.size());
- for (const auto& record : records_) {
- if (record.second->type() != BuilderRecord::ITEM_UNKNOWN &&
- record.second->should_generate() && record.second->item()) {
- result.push_back(record.second->item());
- }
- }
-
- return result;
-}
-
-std::vector<const Target*> Builder::GetAllResolvedTargets() const {
- std::vector<const Target*> result;
- result.reserve(records_.size());
- for (const auto& record : records_) {
- if (record.second->type() == BuilderRecord::ITEM_TARGET &&
- record.second->should_generate() && record.second->item())
- result.push_back(record.second->item()->AsTarget());
- }
- return result;
-}
-
-const BuilderRecord* Builder::GetRecord(const Label& label) const {
- // Forward to the non-const version.
- return const_cast<Builder*>(this)->GetRecord(label);
-}
-
-BuilderRecord* Builder::GetRecord(const Label& label) {
- auto found = records_.find(label);
- return (found != records_.end()) ? found->second.get() : nullptr;
-}
-
-bool Builder::CheckForBadItems(Err* err) const {
- // Look for errors where we find a defined node with an item that refers to
- // an undefined one with no item. There may be other nodes in turn depending
- // on our defined one, but listing those isn't helpful: we want to find the
- // broken link.
- //
- // This finds normal "missing dependency" errors but does not find circular
- // dependencies because in this case all items in the cycle will be GENERATED
- // but none will be resolved. If this happens, we'll check explicitly for
- // that below.
- std::vector<const BuilderRecord*> bad_records;
- std::string depstring;
- for (const auto& record_pair : records_) {
- const BuilderRecord* src = record_pair.second.get();
- if (!src->should_generate())
- continue; // Skip ungenerated nodes.
-
- if (!src->resolved()) {
- bad_records.push_back(src);
-
- // Check dependencies.
- for (auto* dest : src->unresolved_deps()) {
- if (!dest->item()) {
- depstring += src->label().GetUserVisibleName(true) + "\n needs " +
- dest->label().GetUserVisibleName(true) + "\n";
- }
- }
- }
- }
-
- if (!depstring.empty()) {
- *err = Err(Location(), "Unresolved dependencies.", depstring);
- return false;
- }
-
- if (!bad_records.empty()) {
- // Our logic above found a bad node but didn't identify the problem. This
- // normally means a circular dependency.
- depstring = CheckForCircularDependencies(bad_records);
- if (depstring.empty()) {
- // Something's very wrong, just dump out the bad nodes.
- depstring =
- "I have no idea what went wrong, but these are unresolved, "
- "possibly due to an\ninternal error:";
- for (auto* bad_record : bad_records) {
- depstring +=
- "\n\"" + bad_record->label().GetUserVisibleName(false) + "\"";
- }
- *err = Err(Location(), "", depstring);
- } else {
- *err = Err(Location(), "Dependency cycle:", depstring);
- }
- return false;
- }
-
- return true;
-}
-
-bool Builder::TargetDefined(BuilderRecord* record, Err* err) {
- Target* target = record->item()->AsTarget();
-
- if (!AddDeps(record, target->public_deps(), err) ||
- !AddDeps(record, target->private_deps(), err) ||
- !AddDeps(record, target->data_deps(), err) ||
- !AddDeps(record, target->configs().vector(), err) ||
- !AddDeps(record, target->all_dependent_configs(), err) ||
- !AddDeps(record, target->public_configs(), err) ||
- !AddActionValuesDep(record, target->action_values(), err) ||
- !AddToolchainDep(record, target, err))
- return false;
-
- // All targets in the default toolchain get generated by default. We also
- // check if this target was previously marked as "required" and force setting
- // the bit again so the target's dependencies (which we now know) get the
- // required bit pushed to them.
- if (record->should_generate() || target->settings()->is_default())
- RecursiveSetShouldGenerate(record, true);
-
- return true;
-}
-
-bool Builder::ConfigDefined(BuilderRecord* record, Err* err) {
- Config* config = record->item()->AsConfig();
- if (!AddDeps(record, config->configs(), err))
- return false;
-
- // Make sure all deps of this config are scheduled to be loaded. For other
- // item types like targets, the "should generate" flag is propagated around
- // to mark whether this should happen. We could call
- // RecursiveSetShouldGenerate to do this step here, but since configs nor
- // anything they depend on is actually written, the "generate" flag isn't
- // relevant and means extra book keeping. Just force load any deps of this
- // config.
- for (auto* cur : record->all_deps())
- ScheduleItemLoadIfNecessary(cur);
-
- return true;
-}
-
-bool Builder::ToolchainDefined(BuilderRecord* record, Err* err) {
- Toolchain* toolchain = record->item()->AsToolchain();
-
- if (!AddDeps(record, toolchain->deps(), err))
- return false;
-
- for (const auto& tool : toolchain->tools()) {
- if (tool.second->pool().label.is_null())
- continue;
-
- BuilderRecord* dep_record = GetOrCreateRecordOfType(
- tool.second->pool().label, tool.second->pool().origin,
- BuilderRecord::ITEM_POOL, err);
- if (!dep_record)
- return false;
- record->AddDep(dep_record);
- }
-
- // The default toolchain gets generated by default. Also propogate the
- // generate flag if it depends on items in a non-default toolchain.
- if (record->should_generate() ||
- toolchain->settings()->default_toolchain_label() == toolchain->label())
- RecursiveSetShouldGenerate(record, true);
-
- loader_->ToolchainLoaded(toolchain);
- return true;
-}
-
-BuilderRecord* Builder::GetOrCreateRecordOfType(const Label& label,
- const ParseNode* request_from,
- BuilderRecord::ItemType type,
- Err* err) {
- BuilderRecord* record = GetRecord(label);
- if (!record) {
- // Not seen this record yet, create a new one.
- auto new_record = std::make_unique<BuilderRecord>(type, label);
- new_record->set_originally_referenced_from(request_from);
- record = new_record.get();
- records_[label] = std::move(new_record);
- return record;
- }
-
- // Check types.
- if (record->type() != type) {
- std::string msg =
- "The type of " + label.GetUserVisibleName(false) + "\nhere is a " +
- BuilderRecord::GetNameForType(type) + " but was previously seen as a " +
- BuilderRecord::GetNameForType(record->type()) +
- ".\n\n"
- "The most common cause is that the label of a config was put in the\n"
- "in the deps section of a target (or vice-versa).";
- *err = Err(request_from, "Item type does not match.", msg);
- if (record->originally_referenced_from()) {
- err->AppendSubErr(
- Err(record->originally_referenced_from(), std::string()));
- }
- return nullptr;
- }
-
- return record;
-}
-
-BuilderRecord* Builder::GetResolvedRecordOfType(const Label& label,
- const ParseNode* origin,
- BuilderRecord::ItemType type,
- Err* err) {
- BuilderRecord* record = GetRecord(label);
- if (!record) {
- *err = Err(origin, "Item not found",
- "\"" + label.GetUserVisibleName(false) +
- "\" doesn't\n"
- "refer to an existent thing.");
- return nullptr;
- }
-
- const Item* item = record->item();
- if (!item) {
- *err = Err(
- origin, "Item not resolved.",
- "\"" + label.GetUserVisibleName(false) + "\" hasn't been resolved.\n");
- return nullptr;
- }
-
- if (!BuilderRecord::IsItemOfType(item, type)) {
- *err =
- Err(origin,
- std::string("This is not a ") + BuilderRecord::GetNameForType(type),
- "\"" + label.GetUserVisibleName(false) + "\" refers to a " +
- item->GetItemTypeName() + " instead of a " +
- BuilderRecord::GetNameForType(type) + ".");
- return nullptr;
- }
- return record;
-}
-
-bool Builder::AddDeps(BuilderRecord* record,
- const LabelConfigVector& configs,
- Err* err) {
- for (const auto& config : configs) {
- BuilderRecord* dep_record = GetOrCreateRecordOfType(
- config.label, config.origin, BuilderRecord::ITEM_CONFIG, err);
- if (!dep_record)
- return false;
- record->AddDep(dep_record);
- }
- return true;
-}
-
-bool Builder::AddDeps(BuilderRecord* record,
- const UniqueVector<LabelConfigPair>& configs,
- Err* err) {
- for (const auto& config : configs) {
- BuilderRecord* dep_record = GetOrCreateRecordOfType(
- config.label, config.origin, BuilderRecord::ITEM_CONFIG, err);
- if (!dep_record)
- return false;
- record->AddDep(dep_record);
- }
- return true;
-}
-
-bool Builder::AddDeps(BuilderRecord* record,
- const LabelTargetVector& targets,
- Err* err) {
- for (const auto& target : targets) {
- BuilderRecord* dep_record = GetOrCreateRecordOfType(
- target.label, target.origin, BuilderRecord::ITEM_TARGET, err);
- if (!dep_record)
- return false;
- record->AddDep(dep_record);
- }
- return true;
-}
-
-bool Builder::AddActionValuesDep(BuilderRecord* record,
- const ActionValues& action_values,
- Err* err) {
- if (action_values.pool().label.is_null())
- return true;
-
- BuilderRecord* pool_record = GetOrCreateRecordOfType(
- action_values.pool().label, action_values.pool().origin,
- BuilderRecord::ITEM_POOL, err);
- if (!pool_record)
- return false;
- record->AddDep(pool_record);
-
- return true;
-}
-
-bool Builder::AddToolchainDep(BuilderRecord* record,
- const Target* target,
- Err* err) {
- BuilderRecord* toolchain_record = GetOrCreateRecordOfType(
- target->settings()->toolchain_label(), target->defined_from(),
- BuilderRecord::ITEM_TOOLCHAIN, err);
- if (!toolchain_record)
- return false;
- record->AddDep(toolchain_record);
-
- return true;
-}
-
-void Builder::RecursiveSetShouldGenerate(BuilderRecord* record, bool force) {
- if (!record->should_generate()) {
- record->set_should_generate(true);
-
- // This may have caused the item to go into "resolved and generated" state.
- if (record->resolved() && !resolved_and_generated_callback_.is_null())
- resolved_and_generated_callback_.Run(record);
- } else if (!force) {
- return; // Already set and we're not required to iterate dependencies.
- }
-
- for (auto* cur : record->all_deps()) {
- if (!cur->should_generate()) {
- ScheduleItemLoadIfNecessary(cur);
- RecursiveSetShouldGenerate(cur, false);
- }
- }
-}
-
-void Builder::ScheduleItemLoadIfNecessary(BuilderRecord* record) {
- const ParseNode* origin = record->originally_referenced_from();
- loader_->Load(record->label(), origin ? origin->GetRange() : LocationRange());
-}
-
-bool Builder::ResolveItem(BuilderRecord* record, Err* err) {
- DCHECK(record->can_resolve() && !record->resolved());
-
- if (record->type() == BuilderRecord::ITEM_TARGET) {
- Target* target = record->item()->AsTarget();
- if (!ResolveDeps(&target->public_deps(), err) ||
- !ResolveDeps(&target->private_deps(), err) ||
- !ResolveDeps(&target->data_deps(), err) ||
- !ResolveConfigs(&target->configs(), err) ||
- !ResolveConfigs(&target->all_dependent_configs(), err) ||
- !ResolveConfigs(&target->public_configs(), err) ||
- !ResolveActionValues(&target->action_values(), err) ||
- !ResolveToolchain(target, err))
- return false;
- } else if (record->type() == BuilderRecord::ITEM_CONFIG) {
- Config* config = record->item()->AsConfig();
- if (!ResolveConfigs(&config->configs(), err))
- return false;
- } else if (record->type() == BuilderRecord::ITEM_TOOLCHAIN) {
- Toolchain* toolchain = record->item()->AsToolchain();
- if (!ResolveDeps(&toolchain->deps(), err))
- return false;
- if (!ResolvePools(toolchain, err))
- return false;
- }
-
- record->set_resolved(true);
-
- if (!record->item()->OnResolved(err))
- return false;
- if (record->should_generate() && !resolved_and_generated_callback_.is_null())
- resolved_and_generated_callback_.Run(record);
-
- // Recursively update everybody waiting on this item to be resolved.
- for (BuilderRecord* waiting : record->waiting_on_resolution()) {
- DCHECK(waiting->unresolved_deps().find(record) !=
- waiting->unresolved_deps().end());
- waiting->unresolved_deps().erase(record);
-
- if (waiting->can_resolve()) {
- if (!ResolveItem(waiting, err))
- return false;
- }
- }
- record->waiting_on_resolution().clear();
- return true;
-}
-
-bool Builder::ResolveDeps(LabelTargetVector* deps, Err* err) {
- for (LabelTargetPair& cur : *deps) {
- DCHECK(!cur.ptr);
-
- BuilderRecord* record = GetResolvedRecordOfType(
- cur.label, cur.origin, BuilderRecord::ITEM_TARGET, err);
- if (!record)
- return false;
- cur.ptr = record->item()->AsTarget();
- }
- return true;
-}
-
-bool Builder::ResolveConfigs(UniqueVector<LabelConfigPair>* configs, Err* err) {
- for (const auto& cur : *configs) {
- DCHECK(!cur.ptr);
-
- BuilderRecord* record = GetResolvedRecordOfType(
- cur.label, cur.origin, BuilderRecord::ITEM_CONFIG, err);
- if (!record)
- return false;
- const_cast<LabelConfigPair&>(cur).ptr = record->item()->AsConfig();
- }
- return true;
-}
-
-bool Builder::ResolveToolchain(Target* target, Err* err) {
- BuilderRecord* record = GetResolvedRecordOfType(
- target->settings()->toolchain_label(), target->defined_from(),
- BuilderRecord::ITEM_TOOLCHAIN, err);
- if (!record) {
- *err = Err(
- target->defined_from(), "Toolchain for target not defined.",
- "I was hoping to find a toolchain " +
- target->settings()->toolchain_label().GetUserVisibleName(false));
- return false;
- }
-
- if (!target->SetToolchain(record->item()->AsToolchain(), err))
- return false;
-
- return true;
-}
-
-bool Builder::ResolveActionValues(ActionValues* action_values, Err* err) {
- if (action_values->pool().label.is_null())
- return true;
-
- BuilderRecord* record = GetResolvedRecordOfType(
- action_values->pool().label, action_values->pool().origin,
- BuilderRecord::ITEM_POOL, err);
- if (!record)
- return false;
- action_values->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
-
- return true;
-}
-
-bool Builder::ResolvePools(Toolchain* toolchain, Err* err) {
- for (const auto& tool : toolchain->tools()) {
- if (tool.second->pool().label.is_null())
- continue;
-
- BuilderRecord* record = GetResolvedRecordOfType(
- tool.second->pool().label, toolchain->defined_from(),
- BuilderRecord::ITEM_POOL, err);
- if (!record) {
- *err = Err(tool.second->pool().origin, "Pool for tool not defined.",
- "I was hoping to find a pool " +
- tool.second->pool().label.GetUserVisibleName(false));
- return false;
- }
-
- tool.second->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
- }
-
- return true;
-}
-
-std::string Builder::CheckForCircularDependencies(
- const std::vector<const BuilderRecord*>& bad_records) const {
- std::vector<const BuilderRecord*> cycle;
- if (!RecursiveFindCycle(bad_records[0], &cycle))
- return std::string(); // Didn't find a cycle, something else is wrong.
-
- std::string ret;
- for (size_t i = 0; i < cycle.size(); i++) {
- ret += " " + cycle[i]->label().GetUserVisibleName(false);
- if (i != cycle.size() - 1)
- ret += " ->";
- ret += "\n";
- }
-
- return ret;
-}
diff --git a/gn/tools/gn/builder.h b/gn/tools/gn/builder.h
deleted file mode 100644
index 973ee6a39df..00000000000
--- a/gn/tools/gn/builder.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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 TOOLS_GN_BUILDER_H_
-#define TOOLS_GN_BUILDER_H_
-
-#include <map>
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "tools/gn/builder_record.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/unique_vector.h"
-
-class ActionValues;
-class Err;
-class Loader;
-class ParseNode;
-
-// The builder assembles the dependency tree. It is not threadsafe and runs on
-// the main thread only. See also BuilderRecord.
-class Builder {
- public:
- typedef base::Callback<void(const BuilderRecord*)> ResolvedGeneratedCallback;
-
- explicit Builder(Loader* loader);
- ~Builder();
-
- // The resolved callback is called when a target has been both resolved and
- // marked generated. This will be executed only on the main thread.
- void set_resolved_and_generated_callback(
- const ResolvedGeneratedCallback& cb) {
- resolved_and_generated_callback_ = cb;
- }
-
- Loader* loader() const { return loader_; }
-
- void ItemDefined(std::unique_ptr<Item> item);
-
- // Returns NULL if there is not a thing with the corresponding label.
- const Item* GetItem(const Label& label) const;
- const Toolchain* GetToolchain(const Label& label) const;
-
- std::vector<const BuilderRecord*> GetAllRecords() const;
-
- // Returns items which should be generated and which are defined.
- std::vector<const Item*> GetAllResolvedItems() const;
-
- // Returns targets which should be generated and which are defined.
- std::vector<const Target*> GetAllResolvedTargets() const;
-
- // Returns the record for the given label, or NULL if it doesn't exist.
- // Mostly used for unit tests.
- const BuilderRecord* GetRecord(const Label& label) const;
- BuilderRecord* GetRecord(const Label& label);
-
- // If there are any undefined references, returns false and sets the error.
- bool CheckForBadItems(Err* err) const;
-
- private:
- bool TargetDefined(BuilderRecord* record, Err* err);
- bool ConfigDefined(BuilderRecord* record, Err* err);
- bool ToolchainDefined(BuilderRecord* record, Err* err);
-
- // Returns the record associated with the given label. This function checks
- // that if we already have references for it, the type matches. If no record
- // exists yet, a new one will be created.
- //
- // If any of the conditions fail, the return value will be null and the error
- // will be set. request_from is used as the source of the error.
- BuilderRecord* GetOrCreateRecordOfType(const Label& label,
- const ParseNode* request_from,
- BuilderRecord::ItemType type,
- Err* err);
-
- // Returns the record associated with the given label. This function checks
- // that it's already been resolved to the correct type.
- //
- // If any of the conditions fail, the return value will be null and the error
- // will be set. request_from is used as the source of the error.
- BuilderRecord* GetResolvedRecordOfType(const Label& label,
- const ParseNode* request_from,
- BuilderRecord::ItemType type,
- Err* err);
-
- bool AddDeps(BuilderRecord* record,
- const LabelConfigVector& configs,
- Err* err);
- bool AddDeps(BuilderRecord* record,
- const UniqueVector<LabelConfigPair>& configs,
- Err* err);
- bool AddDeps(BuilderRecord* record,
- const LabelTargetVector& targets,
- Err* err);
- bool AddActionValuesDep(BuilderRecord* record,
- const ActionValues& action_values,
- Err* err);
- bool AddToolchainDep(BuilderRecord* record, const Target* target, Err* err);
-
- // Given a target, sets the "should generate" bit and pushes it through the
- // dependency tree. Any time the bit it set, we ensure that the given item is
- // scheduled to be loaded.
- //
- // If the force flag is set, we'll ignore the current state of the record's
- // should_generate flag, and set it on the dependents every time. This is
- // used when defining a target: the "should generate" may have been set
- // before the item was defined (if it is required by something that is
- // required). In this case, we need to re-push the "should generate" flag
- // to the item's dependencies.
- void RecursiveSetShouldGenerate(BuilderRecord* record, bool force);
-
- void ScheduleItemLoadIfNecessary(BuilderRecord* record);
-
- // This takes a BuilderRecord with resolved depdencies, and fills in the
- // target's Label*Vectors with the resolved pointers.
- bool ResolveItem(BuilderRecord* record, Err* err);
-
- // Fills in the pointers in the given vector based on the labels. We assume
- // that everything should be resolved by this point, so will return an error
- // if anything isn't found or if the type doesn't match.
- bool ResolveDeps(LabelTargetVector* deps, Err* err);
- bool ResolveConfigs(UniqueVector<LabelConfigPair>* configs, Err* err);
- bool ResolveActionValues(ActionValues* action_values, Err* err);
- bool ResolveToolchain(Target* target, Err* err);
- bool ResolvePools(Toolchain* toolchain, Err* err);
-
- // Given a list of unresolved records, tries to find any circular
- // dependencies and returns the string describing the problem. If no circular
- // deps were found, returns the empty string.
- std::string CheckForCircularDependencies(
- const std::vector<const BuilderRecord*>& bad_records) const;
-
- // Non owning pointer.
- Loader* loader_;
-
- std::map<Label, std::unique_ptr<BuilderRecord>> records_;
-
- ResolvedGeneratedCallback resolved_and_generated_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(Builder);
-};
-
-#endif // TOOLS_GN_BUILDER_H_
diff --git a/gn/tools/gn/builder_record.cc b/gn/tools/gn/builder_record.cc
deleted file mode 100644
index 14e87b6709f..00000000000
--- a/gn/tools/gn/builder_record.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 "tools/gn/builder_record.h"
-
-#include "tools/gn/item.h"
-
-BuilderRecord::BuilderRecord(ItemType type, const Label& label)
- : type_(type), label_(label) {}
-
-// static
-const char* BuilderRecord::GetNameForType(ItemType type) {
- switch (type) {
- case ITEM_TARGET:
- return "target";
- case ITEM_CONFIG:
- return "config";
- case ITEM_TOOLCHAIN:
- return "toolchain";
- case ITEM_POOL:
- return "pool";
- case ITEM_UNKNOWN:
- default:
- return "unknown";
- }
-}
-
-// static
-bool BuilderRecord::IsItemOfType(const Item* item, ItemType type) {
- switch (type) {
- case ITEM_TARGET:
- return !!item->AsTarget();
- case ITEM_CONFIG:
- return !!item->AsConfig();
- case ITEM_TOOLCHAIN:
- return !!item->AsToolchain();
- case ITEM_POOL:
- return !!item->AsPool();
- case ITEM_UNKNOWN:
- default:
- return false;
- }
-}
-
-// static
-BuilderRecord::ItemType BuilderRecord::TypeOfItem(const Item* item) {
- if (item->AsTarget())
- return ITEM_TARGET;
- if (item->AsConfig())
- return ITEM_CONFIG;
- if (item->AsToolchain())
- return ITEM_TOOLCHAIN;
- if (item->AsPool())
- return ITEM_POOL;
-
- NOTREACHED();
- return ITEM_UNKNOWN;
-}
-
-void BuilderRecord::AddDep(BuilderRecord* record) {
- all_deps_.insert(record);
- if (!record->resolved()) {
- unresolved_deps_.insert(record);
- record->waiting_on_resolution_.insert(this);
- }
-}
diff --git a/gn/tools/gn/builder_record.h b/gn/tools/gn/builder_record.h
deleted file mode 100644
index 9dcc3664f63..00000000000
--- a/gn/tools/gn/builder_record.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// 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 TOOLS_GN_BUILDER_RECORD_H_
-#define TOOLS_GN_BUILDER_RECORD_H_
-
-#include <memory>
-#include <set>
-#include <utility>
-
-#include "base/macros.h"
-#include "tools/gn/item.h"
-#include "tools/gn/location.h"
-
-class ParseNode;
-
-// This class is used by the builder to manage the loading of the dependency
-// tree. It holds a reference to an item and links to other records that the
-// item depends on, both resolved ones, and unresolved ones.
-//
-// If a target depends on another one that hasn't been defined yet, we'll make
-// a placeholder BuilderRecord with no item, and try to load the buildfile
-// associated with the new item. The item will get filled in when we encounter
-// the declaration for the item (or when we're done and realize there are
-// undefined items).
-//
-// You can also have null item pointers when the target is not required for
-// the current build (should_generate is false).
-class BuilderRecord {
- public:
- typedef std::set<BuilderRecord*> BuilderRecordSet;
-
- enum ItemType {
- ITEM_UNKNOWN,
- ITEM_TARGET,
- ITEM_CONFIG,
- ITEM_TOOLCHAIN,
- ITEM_POOL
- };
-
- BuilderRecord(ItemType type, const Label& label);
-
- ItemType type() const { return type_; }
- const Label& label() const { return label_; }
-
- // Returns a user-ready name for the given type. e.g. "target".
- static const char* GetNameForType(ItemType type);
-
- // Returns true if the given item is of the given type.
- static bool IsItemOfType(const Item* item, ItemType type);
-
- // Returns the type enum for the given item.
- static ItemType TypeOfItem(const Item* item);
-
- Item* item() { return item_.get(); }
- const Item* item() const { return item_.get(); }
- void set_item(std::unique_ptr<Item> item) { item_ = std::move(item); }
-
- // Indicates from where this item was originally referenced from that caused
- // it to be loaded. For targets for which we encountered the declaration
- // before a reference, this will be the empty range.
- const ParseNode* originally_referenced_from() const {
- return originally_referenced_from_;
- }
- void set_originally_referenced_from(const ParseNode* pn) {
- originally_referenced_from_ = pn;
- }
-
- bool should_generate() const { return should_generate_; }
- void set_should_generate(bool sg) { should_generate_ = sg; }
-
- bool resolved() const { return resolved_; }
- void set_resolved(bool r) { resolved_ = r; }
-
- bool can_resolve() const { return item_ && unresolved_deps_.empty(); }
-
- // All records this one is depending on.
- BuilderRecordSet& all_deps() { return all_deps_; }
- const BuilderRecordSet& all_deps() const { return all_deps_; }
-
- // Unresolved records this one is depending on. A subset of all... above.
- BuilderRecordSet& unresolved_deps() { return unresolved_deps_; }
- const BuilderRecordSet& unresolved_deps() const { return unresolved_deps_; }
-
- // Records that are waiting on this one to be resolved. This is the other
- // end of the "unresolved deps" arrow.
- BuilderRecordSet& waiting_on_resolution() { return waiting_on_resolution_; }
- const BuilderRecordSet& waiting_on_resolution() const {
- return waiting_on_resolution_;
- }
-
- void AddDep(BuilderRecord* record);
-
- private:
- ItemType type_;
- Label label_;
- std::unique_ptr<Item> item_;
- const ParseNode* originally_referenced_from_ = nullptr;
- bool should_generate_ = false;
- bool resolved_ = false;
-
- BuilderRecordSet all_deps_;
- BuilderRecordSet unresolved_deps_;
- BuilderRecordSet waiting_on_resolution_;
-
- DISALLOW_COPY_AND_ASSIGN(BuilderRecord);
-};
-
-#endif // TOOLS_GN_BUILDER_RECORD_H_
diff --git a/gn/tools/gn/builder_unittest.cc b/gn/tools/gn/builder_unittest.cc
deleted file mode 100644
index 08d8d34af05..00000000000
--- a/gn/tools/gn/builder_unittest.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// 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 "tools/gn/builder.h"
-#include "tools/gn/config.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/toolchain.h"
-#include "util/test/test.h"
-
-namespace gn_builder_unittest {
-
-class MockLoader : public Loader {
- public:
- MockLoader() = default;
-
- // Loader implementation:
- void Load(const SourceFile& file,
- const LocationRange& origin,
- const Label& toolchain_name) override {
- files_.push_back(file);
- }
- void ToolchainLoaded(const Toolchain* toolchain) override {}
- Label GetDefaultToolchain() const override { return Label(); }
- const Settings* GetToolchainSettings(const Label& label) const override {
- return nullptr;
- }
-
- bool HasLoadedNone() const { return files_.empty(); }
-
- // Returns true if one/two loads have been requested and they match the given
- // file(s). This will clear the records so it will be empty for the next call.
- bool HasLoadedOne(const SourceFile& file) {
- if (files_.size() != 1u) {
- files_.clear();
- return false;
- }
- bool match = (files_[0] == file);
- files_.clear();
- return match;
- }
- bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
- if (files_.size() != 2u) {
- files_.clear();
- return false;
- }
-
- bool match = ((files_[0] == a && files_[1] == b) ||
- (files_[0] == b && files_[1] == a));
- files_.clear();
- return match;
- }
-
- private:
- ~MockLoader() override = default;
-
- std::vector<SourceFile> files_;
-};
-
-class BuilderTest : public testing::Test {
- public:
- BuilderTest()
- : loader_(new MockLoader),
- builder_(loader_.get()),
- settings_(&build_settings_, std::string()),
- scope_(&settings_) {
- build_settings_.SetBuildDir(SourceDir("//out/"));
- settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
- settings_.set_default_toolchain_label(settings_.toolchain_label());
- }
-
- Toolchain* DefineToolchain() {
- Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
- TestWithScope::SetupToolchain(tc);
- builder_.ItemDefined(std::unique_ptr<Item>(tc));
- return tc;
- }
-
- protected:
- scoped_refptr<MockLoader> loader_;
- Builder builder_;
- BuildSettings build_settings_;
- Settings settings_;
- Scope scope_;
-};
-
-TEST_F(BuilderTest, BasicDeps) {
- SourceDir toolchain_dir = settings_.toolchain_label().dir();
- std::string toolchain_name = settings_.toolchain_label().name();
-
- // Construct a dependency chain: A -> B -> C. Define A first with a
- // forward-reference to B, then C, then B to test the different orders that
- // the dependencies are hooked up.
- Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
- Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
- Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
-
- // The builder will take ownership of the pointers.
- Target* a = new Target(&settings_, a_label);
- a->public_deps().push_back(LabelTargetPair(b_label));
- a->set_output_type(Target::EXECUTABLE);
- builder_.ItemDefined(std::unique_ptr<Item>(a));
-
- // Should have requested that B and the toolchain is loaded.
- EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
- SourceFile("//b/BUILD.gn")));
-
- // Define the toolchain.
- DefineToolchain();
- BuilderRecord* toolchain_record =
- builder_.GetRecord(settings_.toolchain_label());
- ASSERT_TRUE(toolchain_record);
- EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
-
- // A should be unresolved with an item
- BuilderRecord* a_record = builder_.GetRecord(a_label);
- EXPECT_TRUE(a_record->item());
- EXPECT_FALSE(a_record->resolved());
- EXPECT_FALSE(a_record->can_resolve());
-
- // B should be unresolved, have no item, and no deps.
- BuilderRecord* b_record = builder_.GetRecord(b_label);
- EXPECT_FALSE(b_record->item());
- EXPECT_FALSE(b_record->resolved());
- EXPECT_FALSE(b_record->can_resolve());
- EXPECT_TRUE(b_record->all_deps().empty());
-
- // A should have two deps: B and the toolchain. Only B should be unresolved.
- EXPECT_EQ(2u, a_record->all_deps().size());
- EXPECT_EQ(1u, a_record->unresolved_deps().size());
- EXPECT_NE(a_record->all_deps().end(),
- a_record->all_deps().find(toolchain_record));
- EXPECT_NE(a_record->all_deps().end(), a_record->all_deps().find(b_record));
- EXPECT_NE(a_record->unresolved_deps().end(),
- a_record->unresolved_deps().find(b_record));
-
- // B should be marked as having A waiting on it.
- EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
- EXPECT_NE(b_record->waiting_on_resolution().end(),
- b_record->waiting_on_resolution().find(a_record));
-
- // Add the C target.
- Target* c = new Target(&settings_, c_label);
- c->set_output_type(Target::STATIC_LIBRARY);
- c->visibility().SetPublic();
- builder_.ItemDefined(std::unique_ptr<Item>(c));
-
- // C only depends on the already-loaded toolchain so we shouldn't have
- // requested anything else.
- EXPECT_TRUE(loader_->HasLoadedNone());
-
- // Add the B target.
- Target* b = new Target(&settings_, b_label);
- a->public_deps().push_back(LabelTargetPair(c_label));
- b->set_output_type(Target::SHARED_LIBRARY);
- b->visibility().SetPublic();
- builder_.ItemDefined(std::unique_ptr<Item>(b));
-
- // B depends only on the already-loaded C and toolchain so we shouldn't have
- // requested anything else.
- EXPECT_TRUE(loader_->HasLoadedNone());
-
- // All targets should now be resolved.
- BuilderRecord* c_record = builder_.GetRecord(c_label);
- EXPECT_TRUE(a_record->resolved());
- EXPECT_TRUE(b_record->resolved());
- EXPECT_TRUE(c_record->resolved());
-
- EXPECT_TRUE(a_record->unresolved_deps().empty());
- EXPECT_TRUE(b_record->unresolved_deps().empty());
- EXPECT_TRUE(c_record->unresolved_deps().empty());
-
- EXPECT_TRUE(a_record->waiting_on_resolution().empty());
- EXPECT_TRUE(b_record->waiting_on_resolution().empty());
- EXPECT_TRUE(c_record->waiting_on_resolution().empty());
-}
-
-// Tests that the "should generate" flag is set and propagated properly.
-TEST_F(BuilderTest, ShouldGenerate) {
- DefineToolchain();
-
- // Define a secondary toolchain.
- Settings settings2(&build_settings_, "secondary/");
- Label toolchain_label2(SourceDir("//tc/"), "secondary");
- settings2.set_toolchain_label(toolchain_label2);
- Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
- TestWithScope::SetupToolchain(tc2);
- builder_.ItemDefined(std::unique_ptr<Item>(tc2));
-
- // Construct a dependency chain: A -> B. A is in the default toolchain, B
- // is not.
- Label a_label(SourceDir("//foo/"), "a", settings_.toolchain_label().dir(),
- "a");
- Label b_label(SourceDir("//foo/"), "b", toolchain_label2.dir(),
- toolchain_label2.name());
-
- // First define B.
- Target* b = new Target(&settings2, b_label);
- b->visibility().SetPublic();
- b->set_output_type(Target::EXECUTABLE);
- builder_.ItemDefined(std::unique_ptr<Item>(b));
-
- // B should not be marked generated by default.
- BuilderRecord* b_record = builder_.GetRecord(b_label);
- EXPECT_FALSE(b_record->should_generate());
-
- // Define A with a dependency on B.
- Target* a = new Target(&settings_, a_label);
- a->public_deps().push_back(LabelTargetPair(b_label));
- a->set_output_type(Target::EXECUTABLE);
- builder_.ItemDefined(std::unique_ptr<Item>(a));
-
- // A should have the generate bit set since it's in the default toolchain.
- BuilderRecord* a_record = builder_.GetRecord(a_label);
- EXPECT_TRUE(a_record->should_generate());
-
- // It should have gotten pushed to B.
- EXPECT_TRUE(b_record->should_generate());
-}
-
-// Tests that configs applied to a config get loaded (bug 536844).
-TEST_F(BuilderTest, ConfigLoad) {
- SourceDir toolchain_dir = settings_.toolchain_label().dir();
- std::string toolchain_name = settings_.toolchain_label().name();
-
- // Construct a dependency chain: A -> B -> C. Define A first with a
- // forward-reference to B, then C, then B to test the different orders that
- // the dependencies are hooked up.
- Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
- Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
- Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
-
- // The builder will take ownership of the pointers.
- Config* a = new Config(&settings_, a_label);
- a->configs().push_back(LabelConfigPair(b_label));
- builder_.ItemDefined(std::unique_ptr<Item>(a));
-
- // Should have requested that B is loaded.
- EXPECT_TRUE(loader_->HasLoadedOne(SourceFile("//b/BUILD.gn")));
-}
-
-} // namespace gn_builder_unittest
diff --git a/gn/tools/gn/bundle_data.cc b/gn/tools/gn/bundle_data.cc
deleted file mode 100644
index 4251c8fd641..00000000000
--- a/gn/tools/gn/bundle_data.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/bundle_data.h"
-
-#include "base/logging.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-
-namespace {
-
-// Return directory of |path| without the trailing directory separator.
-base::StringPiece FindDirNoTrailingSeparator(base::StringPiece path) {
- base::StringPiece::size_type pos = path.find_last_of("/\\");
- if (pos == base::StringPiece::npos)
- return base::StringPiece();
- return base::StringPiece(path.data(), pos);
-}
-
-bool IsSourceFileFromAssetsCatalog(base::StringPiece source,
- SourceFile* asset_catalog) {
- // Check whether |source| matches one of the following pattern:
- // .*\.xcassets/Contents.json
- // .*\.xcassets/[^/]*\.appiconset/[^/]*
- // .*\.xcassets/[^/]*\.imageset/[^/]*
- // .*\.xcassets/[^/]*\.launchimage/[^/]*
- bool is_file_from_asset_catalog = false;
- base::StringPiece dir = FindDirNoTrailingSeparator(source);
- if (source.ends_with("/Contents.json") && dir.ends_with(".xcassets")) {
- is_file_from_asset_catalog = true;
- } else if (dir.ends_with(".appiconset") || dir.ends_with(".imageset") ||
- dir.ends_with(".launchimage") || dir.ends_with(".colorset")) {
- dir = FindDirNoTrailingSeparator(dir);
- is_file_from_asset_catalog = dir.ends_with(".xcassets");
- }
- if (is_file_from_asset_catalog && asset_catalog) {
- std::string asset_catalog_path = dir.as_string();
- *asset_catalog = SourceFile(std::move(asset_catalog_path));
- }
- return is_file_from_asset_catalog;
-}
-
-} // namespace
-
-BundleData::BundleData() = default;
-
-BundleData::~BundleData() = default;
-
-void BundleData::AddBundleData(const Target* target) {
- DCHECK_EQ(target->output_type(), Target::BUNDLE_DATA);
- for (const auto& pattern : bundle_deps_filter_) {
- if (pattern.Matches(target->label()))
- return;
- }
- bundle_deps_.push_back(target);
-}
-
-void BundleData::OnTargetResolved(Target* owning_target) {
- // Only initialize file_rules_ and assets_catalog_sources for "create_bundle"
- // target (properties are only used by those targets).
- if (owning_target->output_type() != Target::CREATE_BUNDLE)
- return;
-
- UniqueVector<const Target*> assets_catalog_deps;
- UniqueVector<SourceFile> assets_catalog_sources;
-
- for (const Target* target : bundle_deps_) {
- SourceFiles file_rule_sources;
- for (const SourceFile& source_file : target->sources()) {
- SourceFile assets_catalog;
- if (IsSourceFileFromAssetsCatalog(source_file.value(), &assets_catalog)) {
- assets_catalog_sources.push_back(assets_catalog);
- assets_catalog_deps.push_back(target);
- } else {
- file_rule_sources.push_back(source_file);
- }
- }
-
- if (!file_rule_sources.empty()) {
- DCHECK_EQ(target->action_values().outputs().list().size(), 1u);
- file_rules_.push_back(
- BundleFileRule(target, file_rule_sources,
- target->action_values().outputs().list()[0]));
- }
- }
-
- assets_catalog_deps_.insert(assets_catalog_deps_.end(),
- assets_catalog_deps.begin(),
- assets_catalog_deps.end());
- assets_catalog_sources_.insert(assets_catalog_sources_.end(),
- assets_catalog_sources.begin(),
- assets_catalog_sources.end());
-
- GetSourceFiles(&owning_target->sources());
-}
-
-void BundleData::GetSourceFiles(SourceFiles* sources) const {
- for (const BundleFileRule& file_rule : file_rules_) {
- sources->insert(sources->end(), file_rule.sources().begin(),
- file_rule.sources().end());
- }
- sources->insert(sources->end(), assets_catalog_sources_.begin(),
- assets_catalog_sources_.end());
- if (!code_signing_script_.is_null()) {
- sources->insert(sources->end(), code_signing_sources_.begin(),
- code_signing_sources_.end());
- }
-}
-
-bool BundleData::GetOutputFiles(const Settings* settings,
- const Target* target,
- OutputFiles* outputs,
- Err* err) const {
- SourceFiles outputs_as_sources;
- if (!GetOutputsAsSourceFiles(settings, target, &outputs_as_sources, err))
- return false;
- for (const SourceFile& source_file : outputs_as_sources)
- outputs->push_back(OutputFile(settings->build_settings(), source_file));
- return true;
-}
-
-bool BundleData::GetOutputsAsSourceFiles(const Settings* settings,
- const Target* target,
- SourceFiles* outputs_as_source,
- Err* err) const {
- for (const BundleFileRule& file_rule : file_rules_) {
- for (const SourceFile& source : file_rule.sources()) {
- SourceFile expanded_source_file;
- if (!file_rule.ApplyPatternToSource(settings, target, *this, source,
- &expanded_source_file, err))
- return false;
- outputs_as_source->push_back(expanded_source_file);
- }
- }
-
- if (!assets_catalog_sources_.empty())
- outputs_as_source->push_back(GetCompiledAssetCatalogPath());
-
- if (!partial_info_plist_.is_null())
- outputs_as_source->push_back(partial_info_plist_);
-
- if (!code_signing_script_.is_null()) {
- std::vector<SourceFile> code_signing_output_files;
- SubstitutionWriter::GetListAsSourceFiles(code_signing_outputs_,
- &code_signing_output_files);
- outputs_as_source->insert(outputs_as_source->end(),
- code_signing_output_files.begin(),
- code_signing_output_files.end());
- }
-
- if (!root_dir_.is_null())
- outputs_as_source->push_back(GetBundleRootDirOutput(settings));
-
- return true;
-}
-
-SourceFile BundleData::GetCompiledAssetCatalogPath() const {
- DCHECK(!assets_catalog_sources_.empty());
- std::string assets_car_path = resources_dir_.value() + "/Assets.car";
- return SourceFile(std::move(assets_car_path));
-}
-
-SourceFile BundleData::GetBundleRootDirOutput(const Settings* settings) const {
- std::string root_dir_value = root_dir().value();
- size_t last_separator = root_dir_value.rfind('/');
- if (last_separator != std::string::npos)
- root_dir_value = root_dir_value.substr(0, last_separator);
-
- return SourceFile(std::move(root_dir_value));
-}
-
-SourceDir BundleData::GetBundleRootDirOutputAsDir(
- const Settings* settings) const {
- return SourceDir(GetBundleRootDirOutput(settings).value());
-}
diff --git a/gn/tools/gn/bundle_data.h b/gn/tools/gn/bundle_data.h
deleted file mode 100644
index 2379dbc568d..00000000000
--- a/gn/tools/gn/bundle_data.h
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_BUNDLE_DATA_H_
-#define TOOLS_GN_BUNDLE_DATA_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "tools/gn/action_values.h"
-#include "tools/gn/bundle_file_rule.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/unique_vector.h"
-
-class LabelPattern;
-class OutputFile;
-class Settings;
-class Target;
-
-// BundleData holds the information required by "create_bundle" target.
-class BundleData {
- public:
- using UniqueTargets = UniqueVector<const Target*>;
- using SourceFiles = std::vector<SourceFile>;
- using OutputFiles = std::vector<OutputFile>;
- using BundleFileRules = std::vector<BundleFileRule>;
-
- BundleData();
- ~BundleData();
-
- // Adds a bundle_data target to the recursive collection of all bundle_data
- // that the target depends on.
- void AddBundleData(const Target* target);
-
- // Called upon resolution of the target owning this instance of BundleData.
- // |owning_target| is the owning target.
- void OnTargetResolved(Target* owning_target);
-
- // Returns the list of inputs.
- void GetSourceFiles(SourceFiles* sources) const;
-
- // Returns the list of outputs.
- bool GetOutputFiles(const Settings* settings,
- const Target* target,
- OutputFiles* outputs,
- Err* err) const;
-
- // Returns the list of outputs as SourceFile.
- bool GetOutputsAsSourceFiles(const Settings* settings,
- const Target* target,
- SourceFiles* outputs_as_source,
- Err* err) const;
-
- // Returns the path to the compiled asset catalog. Only valid if
- // assets_catalog_sources() is not empty.
- SourceFile GetCompiledAssetCatalogPath() const;
-
- // Returns the path to the top-level directory of the bundle. This is
- // based on root_dir(), but since that can be Bundle.app/Contents/ or
- // any other subpath, this is just the most top-level directory (e.g.,
- // just Bundle.app/).
- //
- // Note that this is a SourceFile instead of a SourceDir. This is because
- // the output of a create_bundle rule is a single logical unit, even though
- // it is really a directory containing many outputs. This allows other
- // targets to treat the bundle as a single unit, rather than a collection
- // of its contents.
- SourceFile GetBundleRootDirOutput(const Settings* settings) const;
-
- // Performs GetBundleRootDirOutput but returns the result as a directory.
- SourceDir GetBundleRootDirOutputAsDir(const Settings* settings) const;
-
- // Returns the list of inputs for the compilation of the asset catalog.
- SourceFiles& assets_catalog_sources() { return assets_catalog_sources_; }
- const SourceFiles& assets_catalog_sources() const {
- return assets_catalog_sources_;
- }
-
- // Returns the list of dependencies for the compilation of the asset catalog.
- std::vector<const Target*> assets_catalog_deps() const {
- return assets_catalog_deps_;
- }
-
- BundleFileRules& file_rules() { return file_rules_; }
- const BundleFileRules& file_rules() const { return file_rules_; }
-
- SourceDir& root_dir() { return root_dir_; }
- const SourceDir& root_dir() const { return root_dir_; }
-
- SourceDir& contents_dir() { return contents_dir_; }
- const SourceDir& contents_dir() const { return contents_dir_; }
-
- SourceDir& resources_dir() { return resources_dir_; }
- const SourceDir& resources_dir() const { return resources_dir_; }
-
- SourceDir& executable_dir() { return executable_dir_; }
- const SourceDir& executable_dir() const { return executable_dir_; }
-
- std::map<std::string, std::string>& xcode_extra_attributes() {
- return xcode_extra_attributes_;
- }
- const std::map<std::string, std::string>& xcode_extra_attributes() const {
- return xcode_extra_attributes_;
- }
-
- std::string& product_type() { return product_type_; }
- const std::string& product_type() const { return product_type_; }
-
- std::string& xcode_test_application_name() {
- return xcode_test_application_name_;
- }
- const std::string& xcode_test_application_name() const {
- return xcode_test_application_name_;
- }
-
- void set_partial_info_plist(const SourceFile& partial_info_plist) {
- partial_info_plist_ = partial_info_plist;
- }
- const SourceFile& partial_info_plist() const { return partial_info_plist_; }
-
- void set_code_signing_script(const SourceFile& script_file) {
- code_signing_script_ = script_file;
- }
- const SourceFile& code_signing_script() const { return code_signing_script_; }
-
- std::vector<SourceFile>& code_signing_sources() {
- return code_signing_sources_;
- }
- const std::vector<SourceFile>& code_signing_sources() const {
- return code_signing_sources_;
- }
-
- SubstitutionList& code_signing_outputs() { return code_signing_outputs_; }
- const SubstitutionList& code_signing_outputs() const {
- return code_signing_outputs_;
- }
-
- SubstitutionList& code_signing_args() { return code_signing_args_; }
- const SubstitutionList& code_signing_args() const {
- return code_signing_args_;
- }
-
- std::vector<LabelPattern>& bundle_deps_filter() {
- return bundle_deps_filter_;
- }
- const std::vector<LabelPattern>& bundle_deps_filter() const {
- return bundle_deps_filter_;
- }
-
- // Recursive collection of all bundle_data that the target depends on.
- const UniqueTargets& bundle_deps() const { return bundle_deps_; }
-
- private:
- SourceFiles assets_catalog_sources_;
- std::vector<const Target*> assets_catalog_deps_;
- BundleFileRules file_rules_;
- UniqueTargets bundle_deps_;
- std::vector<LabelPattern> bundle_deps_filter_;
-
- // All those values are subdirectories relative to root_build_dir, and apart
- // from root_dir_, they are either equal to root_dir_ or subdirectories of it.
- SourceDir root_dir_;
- SourceDir contents_dir_;
- SourceDir resources_dir_;
- SourceDir executable_dir_;
-
- // The specified attributes will append to the build settings of the generated
- // Xcode target.
- std::map<std::string, std::string> xcode_extra_attributes_;
-
- // This is the target type as known to Xcode. This is only used to generate
- // the Xcode project file when using --ide=xcode.
- std::string product_type_;
-
- // Each Xcode unit test or ui test target must have a test application target,
- // and this value corresponds to the target name. This is only used to
- // generate the Xcode project when using --ide=xcode.
- std::string xcode_test_application_name_;
-
- // Path to the partial Info.plist generated by the asset catalog compiler
- // (corresponds to {{bundle_partial_info_plist}} expansion).
- SourceFile partial_info_plist_;
-
- // Holds the values (script name, sources, outputs, script arguments) for the
- // code signing step if defined.
- SourceFile code_signing_script_;
- std::vector<SourceFile> code_signing_sources_;
- SubstitutionList code_signing_outputs_;
- SubstitutionList code_signing_args_;
-
- DISALLOW_COPY_AND_ASSIGN(BundleData);
-};
-
-#endif // TOOLS_GN_BUNDLE_DATA_H_
diff --git a/gn/tools/gn/bundle_data_target_generator.cc b/gn/tools/gn/bundle_data_target_generator.cc
deleted file mode 100644
index cfd29031a10..00000000000
--- a/gn/tools/gn/bundle_data_target_generator.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/bundle_data_target_generator.h"
-
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/substitution_type.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value.h"
-#include "tools/gn/variables.h"
-
-BundleDataTargetGenerator::BundleDataTargetGenerator(
- Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err)
- : TargetGenerator(target, scope, function_call, err) {}
-
-BundleDataTargetGenerator::~BundleDataTargetGenerator() = default;
-
-void BundleDataTargetGenerator::DoRun() {
- target_->set_output_type(Target::BUNDLE_DATA);
-
- if (!FillSources())
- return;
- if (!FillOutputs())
- return;
-
- if (target_->sources().empty()) {
- *err_ = Err(function_call_,
- "Empty sources for bundle_data target."
- "You have to specify at least one file in the \"sources\".");
- return;
- }
- if (target_->action_values().outputs().list().size() != 1) {
- *err_ = Err(
- function_call_, "Target bundle_data must have exactly one ouput.",
- "You must specify exactly one value in the \"output\" array for the"
- "destination\ninto the generated bundle (see \"gn help bundle_data\"). "
- "If there are multiple\nsources to copy, use source expansion (see "
- "\"gn help source_expansion\").");
- return;
- }
-}
-
-bool BundleDataTargetGenerator::FillOutputs() {
- const Value* value = scope_->GetValue(variables::kOutputs, true);
- if (!value)
- return true;
-
- SubstitutionList& outputs = target_->action_values().outputs();
- if (!outputs.Parse(*value, err_))
- return false;
-
- // Check the substitutions used are valid for this purpose.
- for (const Substitution* type : outputs.required_types()) {
- if (!IsValidBundleDataSubstitution(type)) {
- *err_ = Err(value->origin(), "Invalid substitution type.",
- "The substitution " + std::string(type->name) +
- " isn't valid for something\n"
- "operating on a bundle_data file such as this.");
- return false;
- }
- }
-
- // Validate that outputs are in the bundle.
- CHECK(outputs.list().size() == value->list_value().size());
- for (size_t i = 0; i < outputs.list().size(); i++) {
- if (!EnsureSubstitutionIsInBundleDir(outputs.list()[i],
- value->list_value()[i]))
- return false;
- }
-
- return true;
-}
-
-bool BundleDataTargetGenerator::EnsureSubstitutionIsInBundleDir(
- const SubstitutionPattern& pattern,
- const Value& original_value) {
- if (pattern.ranges().empty()) {
- // Pattern is empty, error out (this prevents weirdness below).
- *err_ = Err(original_value, "This has an empty value in it.");
- return false;
- }
-
- if (SubstitutionIsInBundleDir(pattern.ranges()[0].type))
- return true;
-
- *err_ = Err(original_value, "File is not inside bundle directory.",
- "The given file should be in the output directory. Normally you\n"
- "would specify {{bundle_resources_dir}} or such substitution.");
- return false;
-}
diff --git a/gn/tools/gn/bundle_data_target_generator.h b/gn/tools/gn/bundle_data_target_generator.h
deleted file mode 100644
index e1788efd7b8..00000000000
--- a/gn/tools/gn/bundle_data_target_generator.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_BUNDLE_DATA_TARGET_GENERATOR_H_
-#define TOOLS_GN_BUNDLE_DATA_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target_generator.h"
-
-// Populates a Target with the values from a bundle_data rule.
-class BundleDataTargetGenerator : public TargetGenerator {
- public:
- BundleDataTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err);
- ~BundleDataTargetGenerator() override;
-
- protected:
- void DoRun() override;
-
- private:
- bool FillOutputs();
-
- bool EnsureSubstitutionIsInBundleDir(const SubstitutionPattern& pattern,
- const Value& original_value);
-
- DISALLOW_COPY_AND_ASSIGN(BundleDataTargetGenerator);
-};
-
-#endif // TOOLS_GN_BUNDLE_DATA_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/bundle_file_rule.cc b/gn/tools/gn/bundle_file_rule.cc
deleted file mode 100644
index 2b81cdfb008..00000000000
--- a/gn/tools/gn/bundle_file_rule.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/bundle_file_rule.h"
-
-#include "base/strings/stringprintf.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/substitution_pattern.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/variables.h"
-
-namespace {
-
-Err ErrMissingPropertyForExpansion(const Settings* settings,
- const Target* target,
- const BundleFileRule* bundle_file_rule,
- const char* property_name) {
- std::string label = bundle_file_rule->target()->label().GetUserVisibleName(
- settings->default_toolchain_label());
-
- return Err(target->defined_from(),
- base::StringPrintf("Property %s is required.", property_name),
- base::StringPrintf(
- "In order to expand {{%s}} in %s, the "
- "property needs to be defined in the create_bundle target.",
- property_name, label.c_str()));
-}
-
-} // namespace
-
-BundleFileRule::BundleFileRule(const Target* bundle_data_target,
- const std::vector<SourceFile> sources,
- const SubstitutionPattern& pattern)
- : target_(bundle_data_target), sources_(sources), pattern_(pattern) {
- // target_ may be null during testing.
- DCHECK(!target_ || target_->output_type() == Target::BUNDLE_DATA);
-}
-
-BundleFileRule::BundleFileRule(const BundleFileRule& other) = default;
-
-BundleFileRule::~BundleFileRule() = default;
-
-bool BundleFileRule::ApplyPatternToSource(const Settings* settings,
- const Target* target,
- const BundleData& bundle_data,
- const SourceFile& source_file,
- SourceFile* expanded_source_file,
- Err* err) const {
- std::string output_path;
- for (const auto& subrange : pattern_.ranges()) {
- if (subrange.type == &SubstitutionLiteral) {
- output_path.append(subrange.literal);
- } else if (subrange.type == &SubstitutionBundleRootDir) {
- if (bundle_data.contents_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleRootDir);
- return false;
- }
- output_path.append(bundle_data.root_dir().value());
- } else if (subrange.type == &SubstitutionBundleContentsDir) {
- if (bundle_data.contents_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleContentsDir);
- return false;
- }
- output_path.append(bundle_data.contents_dir().value());
- } else if (subrange.type == &SubstitutionBundleResourcesDir) {
- if (bundle_data.resources_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleResourcesDir);
- return false;
- }
- output_path.append(bundle_data.resources_dir().value());
- } else if (subrange.type == &SubstitutionBundleExecutableDir) {
- if (bundle_data.executable_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleExecutableDir);
- return false;
- }
- output_path.append(bundle_data.executable_dir().value());
- } else {
- output_path.append(SubstitutionWriter::GetSourceSubstitution(
- target_, target_->settings(), source_file, subrange.type,
- SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir()));
- }
- }
- *expanded_source_file = SourceFile(std::move(output_path));
- return true;
-}
-
-bool BundleFileRule::ApplyPatternToSourceAsOutputFile(
- const Settings* settings,
- const Target* target,
- const BundleData& bundle_data,
- const SourceFile& source_file,
- OutputFile* expanded_output_file,
- Err* err) const {
- SourceFile expanded_source_file;
- if (!ApplyPatternToSource(settings, target, bundle_data, source_file,
- &expanded_source_file, err)) {
- return false;
- }
-
- *expanded_output_file =
- OutputFile(settings->build_settings(), expanded_source_file);
- return true;
-}
diff --git a/gn/tools/gn/bundle_file_rule.h b/gn/tools/gn/bundle_file_rule.h
deleted file mode 100644
index b7a2428a600..00000000000
--- a/gn/tools/gn/bundle_file_rule.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_BUNDLE_FILE_RULE_H_
-#define TOOLS_GN_BUNDLE_FILE_RULE_H_
-
-#include <vector>
-
-#include "tools/gn/source_file.h"
-#include "tools/gn/substitution_pattern.h"
-
-class BundleData;
-class Settings;
-class SourceFile;
-class Target;
-class OutputFile;
-
-// BundleFileRule contains the information found in a "bundle_data" target.
-class BundleFileRule {
- public:
- BundleFileRule(const Target* bundle_data_target,
- const std::vector<SourceFile> sources,
- const SubstitutionPattern& pattern);
- BundleFileRule(const BundleFileRule& other);
- ~BundleFileRule();
-
- // Applies the substitution pattern to a source file, returning the result
- // as either a SourceFile or an OutputFile.
- bool ApplyPatternToSource(const Settings* settings,
- const Target* target,
- const BundleData& bundle_data,
- const SourceFile& source_file,
- SourceFile* expanded_source_file,
- Err* err) const;
- bool ApplyPatternToSourceAsOutputFile(const Settings* settings,
- const Target* target,
- const BundleData& bundle_data,
- const SourceFile& source_file,
- OutputFile* expanded_output_file,
- Err* err) const;
-
- // Returns the associated target (of type Target::BUNDLE_DATA). May be
- // null during testing.
- const Target* target() const { return target_; }
-
- // Returns the list of SourceFiles.
- const std::vector<SourceFile>& sources() const { return sources_; }
-
- private:
- const Target* target_;
- std::vector<SourceFile> sources_;
- SubstitutionPattern pattern_;
-};
-
-#endif // TOOLS_GN_BUNDLE_FILE_RULE_H_
diff --git a/gn/tools/gn/c_include_iterator.cc b/gn/tools/gn/c_include_iterator.cc
deleted file mode 100644
index 540d1189217..00000000000
--- a/gn/tools/gn/c_include_iterator.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// 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 "tools/gn/c_include_iterator.h"
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/location.h"
-
-namespace {
-
-enum IncludeType {
- INCLUDE_NONE,
- INCLUDE_SYSTEM, // #include <...>
- INCLUDE_USER // #include "..."
-};
-
-// Returns a new string piece referencing the same buffer as the argument, but
-// with leading space trimmed. This only checks for space and tab characters
-// since we're dealing with lines in C source files.
-base::StringPiece TrimLeadingWhitespace(const base::StringPiece& str) {
- size_t new_begin = 0;
- while (new_begin < str.size() &&
- (str[new_begin] == ' ' || str[new_begin] == '\t'))
- new_begin++;
- return str.substr(new_begin);
-}
-
-// We don't want to count comment lines and preprocessor lines toward our
-// "max lines to look at before giving up" since the beginnings of some files
-// may have a lot of comments.
-//
-// We only handle C-style "//" comments since this is the normal commenting
-// style used in Chrome, and do so pretty stupidly. We don't want to write a
-// full C++ parser here, we're just trying to get a good heuristic for checking
-// the file.
-//
-// We assume the line has leading whitespace trimmed. We also assume that empty
-// lines have already been filtered out.
-bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) {
- if (base::StartsWith(line, "//", base::CompareCase::SENSITIVE))
- return false; // Don't count comments.
- if (base::StartsWith(line, "/*", base::CompareCase::SENSITIVE) ||
- base::StartsWith(line, " *", base::CompareCase::SENSITIVE))
- return false; // C-style comment blocks with stars along the left side.
- if (base::StartsWith(line, "#", base::CompareCase::SENSITIVE))
- return false; // Don't count preprocessor.
- if (base::ContainsOnlyChars(line, base::kWhitespaceASCII))
- return false; // Don't count whitespace lines.
- return true; // Count everything else.
-}
-
-// Given a line, checks to see if it looks like an include or import and
-// extract the path. The type of include is returned. Returns INCLUDE_NONE on
-// error or if this is not an include line.
-//
-// The 1-based character number on the line that the include was found at
-// will be filled into *begin_char.
-IncludeType ExtractInclude(const base::StringPiece& line,
- base::StringPiece* path,
- int* begin_char) {
- static const char kInclude[] = "include";
- static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null.
- static const char kImport[] = "import";
- static const size_t kImportLen = arraysize(kImport) - 1; // No null.
-
- base::StringPiece trimmed = TrimLeadingWhitespace(line);
- if (trimmed.empty())
- return INCLUDE_NONE;
-
- if (trimmed[0] != '#')
- return INCLUDE_NONE;
-
- trimmed = TrimLeadingWhitespace(trimmed.substr(1));
-
- base::StringPiece contents;
- if (base::StartsWith(trimmed, base::StringPiece(kInclude, kIncludeLen),
- base::CompareCase::SENSITIVE))
- contents = TrimLeadingWhitespace(trimmed.substr(kIncludeLen));
- else if (base::StartsWith(trimmed, base::StringPiece(kImport, kImportLen),
- base::CompareCase::SENSITIVE))
- contents = TrimLeadingWhitespace(trimmed.substr(kImportLen));
-
- if (contents.empty())
- return INCLUDE_NONE;
-
- IncludeType type = INCLUDE_NONE;
- char terminating_char = 0;
- if (contents[0] == '"') {
- type = INCLUDE_USER;
- terminating_char = '"';
- } else if (contents[0] == '<') {
- type = INCLUDE_SYSTEM;
- terminating_char = '>';
- } else {
- return INCLUDE_NONE;
- }
-
- // Count everything to next "/> as the contents.
- size_t terminator_index = contents.find(terminating_char, 1);
- if (terminator_index == base::StringPiece::npos)
- return INCLUDE_NONE;
-
- *path = contents.substr(1, terminator_index - 1);
- // Note: one based so we do "+ 1".
- *begin_char = static_cast<int>(path->data() - line.data()) + 1;
- return type;
-}
-
-// Returns true if this line has a "nogncheck" comment associated with it.
-bool HasNoCheckAnnotation(const base::StringPiece& line) {
- return line.find("nogncheck") != base::StringPiece::npos;
-}
-
-} // namespace
-
-const int CIncludeIterator::kMaxNonIncludeLines = 10;
-
-CIncludeIterator::CIncludeIterator(const InputFile* input)
- : input_file_(input), file_(input->contents()) {}
-
-CIncludeIterator::~CIncludeIterator() = default;
-
-bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out,
- LocationRange* location) {
- base::StringPiece line;
- int cur_line_number = 0;
- while (lines_since_last_include_ <= kMaxNonIncludeLines &&
- GetNextLine(&line, &cur_line_number)) {
- base::StringPiece include_contents;
- int begin_char;
- IncludeType type = ExtractInclude(line, &include_contents, &begin_char);
- if (type == INCLUDE_USER && !HasNoCheckAnnotation(line)) {
- // Only count user includes for now.
- *out = include_contents;
- *location = LocationRange(
- Location(input_file_, cur_line_number, begin_char,
- -1 /* TODO(scottmg): Is this important? */),
- Location(input_file_, cur_line_number,
- begin_char + static_cast<int>(include_contents.size()),
- -1 /* TODO(scottmg): Is this important? */));
-
- lines_since_last_include_ = 0;
- return true;
- }
-
- if (ShouldCountTowardNonIncludeLines(line))
- lines_since_last_include_++;
- }
- return false;
-}
-
-bool CIncludeIterator::GetNextLine(base::StringPiece* line, int* line_number) {
- if (offset_ == file_.size())
- return false;
-
- size_t begin = offset_;
- while (offset_ < file_.size() && file_[offset_] != '\n')
- offset_++;
- line_number_++;
-
- *line = file_.substr(begin, offset_ - begin);
- *line_number = line_number_;
-
- // If we didn't hit EOF, skip past the newline for the next one.
- if (offset_ < file_.size())
- offset_++;
- return true;
-}
diff --git a/gn/tools/gn/c_include_iterator.h b/gn/tools/gn/c_include_iterator.h
deleted file mode 100644
index 86a0954a43c..00000000000
--- a/gn/tools/gn/c_include_iterator.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 TOOLS_GN_C_INCLUDE_ITERATOR_H_
-#define TOOLS_GN_C_INCLUDE_ITERATOR_H_
-
-#include <stddef.h>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-class InputFile;
-class LocationRange;
-
-// Iterates through #includes in C source and header files.
-//
-// This only returns includes we want to check, which is user includes with
-// double-quotes: #include "..."
-class CIncludeIterator {
- public:
- // The InputFile pointed to must outlive this class.
- explicit CIncludeIterator(const InputFile* input);
- ~CIncludeIterator();
-
- // Fills in the string with the contents of the next include, and the
- // location with where it came from, and returns true, or returns false if
- // there are no more includes.
- bool GetNextIncludeString(base::StringPiece* out, LocationRange* location);
-
- // Maximum numbef of non-includes we'll tolerate before giving up. This does
- // not count comments or preprocessor.
- static const int kMaxNonIncludeLines;
-
- private:
- // Returns false on EOF, otherwise fills in the given line and the one-based
- // line number into *line_number;
- bool GetNextLine(base::StringPiece* line, int* line_number);
-
- const InputFile* input_file_;
-
- // This just points into input_file_.contents() for convenience.
- base::StringPiece file_;
-
- // 0-based offset into the file.
- size_t offset_ = 0;
-
- int line_number_ = 0; // One-based. Indicates the last line we read.
-
- // Number of lines we've processed since seeing the last include (or the
- // beginning of the file) with some exceptions.
- int lines_since_last_include_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(CIncludeIterator);
-};
-
-#endif // TOOLS_GN_C_INCLUDE_ITERATOR_H_
diff --git a/gn/tools/gn/c_include_iterator_unittest.cc b/gn/tools/gn/c_include_iterator_unittest.cc
deleted file mode 100644
index a88537c9289..00000000000
--- a/gn/tools/gn/c_include_iterator_unittest.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// 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 <stddef.h>
-
-#include "tools/gn/c_include_iterator.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/location.h"
-#include "util/test/test.h"
-
-namespace {
-
-bool RangeIs(const LocationRange& range,
- int line,
- int begin_char,
- int end_char) {
- return range.begin().line_number() == line &&
- range.end().line_number() == line &&
- range.begin().column_number() == begin_char &&
- range.end().column_number() == end_char;
-}
-
-} // namespace
-
-TEST(CIncludeIterator, Basic) {
- std::string buffer;
- buffer.append("// Some comment\n");
- buffer.append("\n");
- buffer.append("#include \"foo/bar.h\"\n");
- buffer.append("\n");
- buffer.append("#include <stdio.h>\n");
- buffer.append("\n");
- buffer.append(" #include \"foo/baz.h\"\n"); // Leading whitespace
- buffer.append("#include \"la/deda.h\"\n");
- // Line annotated with "// nogncheck"
- buffer.append("#include \"should_be_skipped.h\" // nogncheck\n");
- buffer.append("#import \"weird_mac_import.h\"\n");
- buffer.append("\n");
- buffer.append("void SomeCode() {\n");
-
- InputFile file(SourceFile("//foo.cc"));
- file.SetContents(buffer);
-
- CIncludeIterator iter(&file);
-
- base::StringPiece contents;
- LocationRange range;
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("foo/bar.h", contents);
- EXPECT_TRUE(RangeIs(range, 3, 11, 20)) << range.begin().Describe(true);
-
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("foo/baz.h", contents);
- EXPECT_TRUE(RangeIs(range, 7, 12, 21)) << range.begin().Describe(true);
-
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("la/deda.h", contents);
- EXPECT_TRUE(RangeIs(range, 8, 11, 20)) << range.begin().Describe(true);
-
- // The line annotated with "nogncheck" should be skipped.
-
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("weird_mac_import.h", contents);
- EXPECT_TRUE(RangeIs(range, 10, 10, 28)) << range.begin().Describe(true);
-
- EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range));
-}
-
-// Tests that we don't search for includes indefinitely.
-TEST(CIncludeIterator, GiveUp) {
- std::string buffer;
- for (size_t i = 0; i < 1000; i++)
- buffer.append("x\n");
- buffer.append("#include \"foo/bar.h\"\n");
-
- InputFile file(SourceFile("//foo.cc"));
- file.SetContents(buffer);
-
- base::StringPiece contents;
- LocationRange range;
-
- CIncludeIterator iter(&file);
- EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_TRUE(contents.empty());
-}
-
-// Don't count blank lines, comments, and preprocessor when giving up.
-TEST(CIncludeIterator, DontGiveUp) {
- std::string buffer;
- for (size_t i = 0; i < 1000; i++)
- buffer.push_back('\n');
- for (size_t i = 0; i < 1000; i++)
- buffer.append("// comment\n");
- for (size_t i = 0; i < 1000; i++)
- buffer.append("#preproc\n");
- buffer.append("#include \"foo/bar.h\"\n");
-
- InputFile file(SourceFile("//foo.cc"));
- file.SetContents(buffer);
-
- base::StringPiece contents;
- LocationRange range;
-
- CIncludeIterator iter(&file);
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("foo/bar.h", contents);
-}
-
-// Tests that we'll tolerate some small numbers of non-includes interspersed
-// with real includes.
-TEST(CIncludeIterator, TolerateNonIncludes) {
- const size_t kSkip = CIncludeIterator::kMaxNonIncludeLines - 2;
- const size_t kGroupCount = 100;
-
- std::string include("foo/bar.h");
-
- // Allow a series of includes with blanks in between.
- std::string buffer;
- for (size_t group = 0; group < kGroupCount; group++) {
- for (size_t i = 0; i < kSkip; i++)
- buffer.append("foo\n");
- buffer.append("#include \"" + include + "\"\n");
- }
-
- InputFile file(SourceFile("//foo.cc"));
- file.SetContents(buffer);
-
- base::StringPiece contents;
- LocationRange range;
-
- CIncludeIterator iter(&file);
- for (size_t group = 0; group < kGroupCount; group++) {
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ(include, contents.as_string());
- }
- EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range));
-}
-
-// Tests that comments of the form
-// /*
-// *
-// */
-// are not counted toward the non-include line count.
-TEST(CIncludeIterator, CStyleComments) {
- std::string buffer("/*");
- for (size_t i = 0; i < 1000; i++)
- buffer.append(" *\n");
- buffer.append(" */\n\n");
- buffer.append("#include \"foo/bar.h\"\n");
-
- InputFile file(SourceFile("//foo.cc"));
- file.SetContents(buffer);
-
- base::StringPiece contents;
- LocationRange range;
-
- CIncludeIterator iter(&file);
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("foo/bar.h", contents);
-}
-
-// Tests that spaces between the hash and directive are ignored.
-TEST(CIncludeIterator, SpacesAfterHash) {
- std::string buffer("# include \"foo/bar.h\"\n");
-
- InputFile file(SourceFile("//foo.cc"));
- file.SetContents(buffer);
-
- base::StringPiece contents;
- LocationRange range;
-
- CIncludeIterator iter(&file);
- EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
- EXPECT_EQ("foo/bar.h", contents);
-
- EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range));
-}
diff --git a/gn/tools/gn/c_substitution_type.cc b/gn/tools/gn/c_substitution_type.cc
deleted file mode 100644
index eec8c66350a..00000000000
--- a/gn/tools/gn/c_substitution_type.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/c_substitution_type.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "tools/gn/err.h"
-
-const SubstitutionTypes CSubstitutions = {
- &CSubstitutionAsmFlags, &CSubstitutionCFlags,
- &CSubstitutionCFlagsC, &CSubstitutionCFlagsCc,
- &CSubstitutionCFlagsObjC, &CSubstitutionCFlagsObjCc,
- &CSubstitutionDefines, &CSubstitutionIncludeDirs,
-
- &CSubstitutionLinkerInputs, &CSubstitutionLinkerInputsNewline,
- &CSubstitutionLdFlags, &CSubstitutionLibs,
- &CSubstitutionOutputExtension,
- &CSubstitutionSoLibs,
-
- &CSubstitutionArFlags,
-};
-
-// Valid for compiler tools.
-const Substitution CSubstitutionAsmFlags = {"{{asmflags}}", "asmflags"};
-const Substitution CSubstitutionCFlags = {"{{cflags}}", "cflags"};
-const Substitution CSubstitutionCFlagsC = {"{{cflags_c}}", "cflags_c"};
-const Substitution CSubstitutionCFlagsCc = {"{{cflags_cc}}", "cflags_cc"};
-const Substitution CSubstitutionCFlagsObjC = {"{{cflags_objc}}", "cflags_objc"};
-const Substitution CSubstitutionCFlagsObjCc = {"{{cflags_objcc}}",
- "cflags_objcc"};
-const Substitution CSubstitutionDefines = {"{{defines}}", "defines"};
-const Substitution CSubstitutionIncludeDirs = {"{{include_dirs}}",
- "include_dirs"};
-
-// Valid for linker tools.
-const Substitution CSubstitutionLinkerInputs = {"{{inputs}}", "in"};
-const Substitution CSubstitutionLinkerInputsNewline = {"{{inputs_newline}}",
- "in_newline"};
-const Substitution CSubstitutionLdFlags = {"{{ldflags}}", "ldflags"};
-const Substitution CSubstitutionLibs = {"{{libs}}", "libs"};
-const Substitution CSubstitutionOutputExtension = {"{{output_extension}}",
- "output_extension"};
-const Substitution CSubstitutionSoLibs = {"{{solibs}}", "solibs"};
-
-// Valid for alink only.
-const Substitution CSubstitutionArFlags = {"{{arflags}}", "arflags"};
-
-bool IsValidCompilerSubstitution(const Substitution* type) {
- return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
- type == &SubstitutionSource || type == &CSubstitutionAsmFlags ||
- type == &CSubstitutionCFlags || type == &CSubstitutionCFlagsC ||
- type == &CSubstitutionCFlagsCc || type == &CSubstitutionCFlagsObjC ||
- type == &CSubstitutionCFlagsObjCc || type == &CSubstitutionDefines ||
- type == &CSubstitutionIncludeDirs;
-}
-
-bool IsValidCompilerOutputsSubstitution(const Substitution* type) {
- // All tool types except "output" (which would be infinitely recursive).
- return (IsValidToolSubstitution(type) && type != &SubstitutionOutput) ||
- IsValidSourceSubstitution(type);
-}
-
-bool IsValidLinkerSubstitution(const Substitution* type) {
- return IsValidToolSubstitution(type) || type == &SubstitutionOutputDir ||
- type == &CSubstitutionLinkerInputs ||
- type == &CSubstitutionLinkerInputsNewline ||
- type == &CSubstitutionLdFlags || type == &CSubstitutionLibs ||
- type == &CSubstitutionOutputExtension || type == &CSubstitutionSoLibs;
-}
-
-bool IsValidLinkerOutputsSubstitution(const Substitution* type) {
- // All valid compiler outputs plus the output extension.
- return IsValidCompilerOutputsSubstitution(type) ||
- type == &SubstitutionOutputDir || type == &CSubstitutionOutputExtension;
-}
-
-bool IsValidALinkSubstitution(const Substitution* type) {
- return IsValidToolSubstitution(type) || type == &SubstitutionOutputDir ||
- type == &CSubstitutionLinkerInputs ||
- type == &CSubstitutionLinkerInputsNewline ||
- type == &CSubstitutionArFlags || type == &CSubstitutionOutputExtension;
-}
diff --git a/gn/tools/gn/c_substitution_type.h b/gn/tools/gn/c_substitution_type.h
deleted file mode 100644
index ed543f0ad48..00000000000
--- a/gn/tools/gn/c_substitution_type.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_C_SUBSTITUTION_TYPE_H_
-#define TOOLS_GN_C_SUBSTITUTION_TYPE_H_
-
-#include <set>
-#include <vector>
-
-#include "tools/gn/substitution_type.h"
-
-// The set of substitutions available to all tools.
-extern const SubstitutionTypes CSubstitutions;
-
-// Valid for compiler tools.
-extern const Substitution CSubstitutionAsmFlags;
-extern const Substitution CSubstitutionCFlags;
-extern const Substitution CSubstitutionCFlagsC;
-extern const Substitution CSubstitutionCFlagsCc;
-extern const Substitution CSubstitutionCFlagsObjC;
-extern const Substitution CSubstitutionCFlagsObjCc;
-extern const Substitution CSubstitutionDefines;
-extern const Substitution CSubstitutionIncludeDirs;
-
-// Valid for linker tools.
-extern const Substitution CSubstitutionLinkerInputs;
-extern const Substitution CSubstitutionLinkerInputsNewline;
-extern const Substitution CSubstitutionLdFlags;
-extern const Substitution CSubstitutionLibs;
-extern const Substitution CSubstitutionOutputExtension;
-extern const Substitution CSubstitutionSoLibs;
-
-// Valid for alink only.
-extern const Substitution CSubstitutionArFlags;
-
-// Both compiler and linker tools.
-bool IsValidCompilerSubstitution(const Substitution* type);
-bool IsValidCompilerOutputsSubstitution(const Substitution* type);
-bool IsValidLinkerSubstitution(const Substitution* type);
-bool IsValidLinkerOutputsSubstitution(const Substitution* type);
-bool IsValidALinkSubstitution(const Substitution* type);
-
-#endif // TOOLS_GN_C_SUBSTITUTION_TYPE_H_
diff --git a/gn/tools/gn/c_tool.cc b/gn/tools/gn/c_tool.cc
deleted file mode 100644
index 59452fbe839..00000000000
--- a/gn/tools/gn/c_tool.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/c_tool.h"
-#include "tools/gn/c_substitution_type.h"
-#include "tools/gn/target.h"
-
-const char* CTool::kCToolCc = "cc";
-const char* CTool::kCToolCxx = "cxx";
-const char* CTool::kCToolObjC = "objc";
-const char* CTool::kCToolObjCxx = "objcxx";
-const char* CTool::kCToolRc = "rc";
-const char* CTool::kCToolAsm = "asm";
-const char* CTool::kCToolAlink = "alink";
-const char* CTool::kCToolSolink = "solink";
-const char* CTool::kCToolSolinkModule = "solink_module";
-const char* CTool::kCToolLink = "link";
-
-CTool::CTool(const char* n)
- : Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) {
- CHECK(ValidateName(n));
-}
-
-CTool::~CTool() = default;
-
-CTool* CTool::AsC() {
- return this;
-}
-const CTool* CTool::AsC() const {
- return this;
-}
-
-bool CTool::ValidateName(const char* name) const {
- return name == kCToolCc || name == kCToolCxx || name == kCToolObjC ||
- name == kCToolObjCxx || name == kCToolRc || name == kCToolAsm ||
- name == kCToolAlink || name == kCToolSolink ||
- name == kCToolSolinkModule || name == kCToolLink;
-}
-
-void CTool::SetComplete() {
- SetToolComplete();
- link_output_.FillRequiredTypes(&substitution_bits_);
- depend_output_.FillRequiredTypes(&substitution_bits_);
-}
-
-bool CTool::ValidateRuntimeOutputs(Err* err) {
- if (runtime_outputs().list().empty())
- return true; // Empty is always OK.
-
- if (name_ != kCToolSolink && name_ != kCToolSolinkModule &&
- name_ != kCToolLink) {
- *err = Err(defined_from(), "This tool specifies runtime_outputs.",
- "This is only valid for linker tools (alink doesn't count).");
- return false;
- }
-
- for (const SubstitutionPattern& pattern : runtime_outputs().list()) {
- if (!IsPatternInOutputList(outputs(), pattern)) {
- *err = Err(defined_from(), "This tool's runtime_outputs is bad.",
- "It must be a subset of the outputs. The bad one is:\n " +
- pattern.AsString());
- return false;
- }
- }
- return true;
-}
-
-// Validates either link_output or depend_output. To generalize to either, pass
-// the associated pattern, and the variable name that should appear in error
-// messages.
-bool CTool::ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
- const char* variable_name,
- Err* err) {
- if (pattern.empty())
- return true; // Empty is always OK.
-
- // It should only be specified for certain tool types.
- if (name_ != kCToolSolink && name_ != kCToolSolinkModule) {
- *err = Err(defined_from(),
- "This tool specifies a " + std::string(variable_name) + ".",
- "This is only valid for solink and solink_module tools.");
- return false;
- }
-
- if (!IsPatternInOutputList(outputs(), pattern)) {
- *err = Err(defined_from(), "This tool's link_output is bad.",
- "It must match one of the outputs.");
- return false;
- }
-
- return true;
-}
-
-bool CTool::ReadPrecompiledHeaderType(Scope* scope, Err* err) {
- const Value* value = scope->GetValue("precompiled_header_type", true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- if (value->string_value().empty())
- return true; // Accept empty string, do nothing (default is "no PCH").
-
- if (value->string_value() == "gcc") {
- set_precompiled_header_type(PCH_GCC);
- return true;
- } else if (value->string_value() == "msvc") {
- set_precompiled_header_type(PCH_MSVC);
- return true;
- }
- *err = Err(*value, "Invalid precompiled_header_type",
- "Must either be empty, \"gcc\", or \"msvc\".");
- return false;
-}
-
-bool CTool::ReadDepsFormat(Scope* scope, Err* err) {
- const Value* value = scope->GetValue("depsformat", true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- if (value->string_value() == "gcc") {
- set_depsformat(DEPS_GCC);
- } else if (value->string_value() == "msvc") {
- set_depsformat(DEPS_MSVC);
- } else {
- *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
- return false;
- }
- return true;
-}
-
-bool CTool::ReadOutputsPatternList(Scope* scope,
- const char* var,
- SubstitutionList* field,
- Err* err) {
- DCHECK(!complete_);
- const Value* value = scope->GetValue(var, true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::LIST, err))
- return false;
-
- SubstitutionList list;
- if (!list.Parse(*value, err))
- return false;
-
- // Validate the right kinds of patterns are used.
- if (list.list().empty()) {
- *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
- return false;
- }
-
- for (const auto& cur_type : list.required_types()) {
- if (!ValidateOutputSubstitution(cur_type)) {
- *err = Err(*value, "Pattern not valid here.",
- "You used the pattern " + std::string(cur_type->name) +
- " which is not valid\nfor this variable.");
- return false;
- }
- }
-
- *field = std::move(list);
- return true;
-}
-
-bool CTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
- // Initialize default vars.
- if (!Tool::InitTool(scope, toolchain, err)) {
- return false;
- }
-
- // All C tools should have outputs.
- if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
- return false;
- }
-
- if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) ||
- !ReadString(scope, "lib_switch", &lib_switch_, err) ||
- !ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) ||
- !ReadPattern(scope, "link_output", &link_output_, err) ||
- !ReadPattern(scope, "depend_output", &depend_output_, err)) {
- return false;
- }
-
- // Validate link_output and depend_output.
- if (!ValidateLinkAndDependOutput(link_output(), "link_output", err)) {
- return false;
- }
- if (!ValidateLinkAndDependOutput(depend_output(), "depend_output", err)) {
- return false;
- }
- if ((!link_output().empty() && depend_output().empty()) ||
- (link_output().empty() && !depend_output().empty())) {
- *err = Err(defined_from(),
- "Both link_output and depend_output should either "
- "be specified or they should both be empty.");
- return false;
- }
-
- if (!ValidateRuntimeOutputs(err)) {
- return false;
- }
- return true;
-}
-
-bool CTool::ValidateSubstitution(const Substitution* sub_type) const {
- if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
- name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
- return IsValidCompilerSubstitution(sub_type);
- else if (name_ == kCToolAlink)
- return IsValidALinkSubstitution(sub_type);
- else if (name_ == kCToolSolink || name_ == kCToolSolinkModule ||
- name_ == kCToolLink)
- return IsValidLinkerSubstitution(sub_type);
- NOTREACHED();
- return false;
-}
-
-bool CTool::ValidateOutputSubstitution(const Substitution* sub_type) const {
- if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
- name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
- return IsValidCompilerOutputsSubstitution(sub_type);
- // ALink uses the standard output file patterns as other linker tools.
- else if (name_ == kCToolAlink || name_ == kCToolSolink ||
- name_ == kCToolSolinkModule || name_ == kCToolLink)
- return IsValidLinkerOutputsSubstitution(sub_type);
- NOTREACHED();
- return false;
-}
diff --git a/gn/tools/gn/c_tool.h b/gn/tools/gn/c_tool.h
deleted file mode 100644
index 07dfcbe26e3..00000000000
--- a/gn/tools/gn/c_tool.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_C_TOOL_H_
-#define TOOLS_GN_C_TOOL_H_
-
-#include <string>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_pattern.h"
-#include "tools/gn/tool.h"
-#include "tools/gn/toolchain.h"
-
-class CTool : public Tool {
- public:
- // C compiler tools
- static const char* kCToolCc;
- static const char* kCToolCxx;
- static const char* kCToolObjC;
- static const char* kCToolObjCxx;
- static const char* kCToolRc;
- static const char* kCToolAsm;
-
- // C linker tools
- static const char* kCToolAlink;
- static const char* kCToolSolink;
- static const char* kCToolSolinkModule;
- static const char* kCToolLink;
-
- enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
-
- enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
-
- CTool(const char* n);
- ~CTool();
-
- // Manual RTTI and required functions ---------------------------------------
-
- bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
- bool ValidateName(const char* name) const override;
- void SetComplete() override;
- bool ValidateSubstitution(const Substitution* sub_type) const override;
-
- CTool* AsC() override;
- const CTool* AsC() const override;
-
- // Getters/setters ----------------------------------------------------------
- //
- // After the tool has had its attributes set, the caller must call
- // SetComplete(), at which point no other changes can be made.
-
- DepsFormat depsformat() const { return depsformat_; }
- void set_depsformat(DepsFormat f) {
- DCHECK(!complete_);
- depsformat_ = f;
- }
-
- PrecompiledHeaderType precompiled_header_type() const {
- return precompiled_header_type_;
- }
- void set_precompiled_header_type(PrecompiledHeaderType pch_type) {
- DCHECK(!complete_);
- precompiled_header_type_ = pch_type;
- }
-
- const std::string& lib_switch() const { return lib_switch_; }
- void set_lib_switch(std::string s) {
- DCHECK(!complete_);
- lib_switch_ = std::move(s);
- }
-
- const std::string& lib_dir_switch() const { return lib_dir_switch_; }
- void set_lib_dir_switch(std::string s) {
- DCHECK(!complete_);
- lib_dir_switch_ = std::move(s);
- }
-
- // Should match files in the outputs() if nonempty.
- const SubstitutionPattern& link_output() const { return link_output_; }
- void set_link_output(SubstitutionPattern link_out) {
- DCHECK(!complete_);
- link_output_ = std::move(link_out);
- }
-
- const SubstitutionPattern& depend_output() const { return depend_output_; }
- void set_depend_output(SubstitutionPattern dep_out) {
- DCHECK(!complete_);
- depend_output_ = std::move(dep_out);
- }
-
- // Other functions ----------------------------------------------------------
-
- // Returns true if this tool has separate outputs for dependency tracking
- // and linking.
- bool has_separate_solink_files() const {
- return !link_output_.empty() || !depend_output_.empty();
- }
-
- private:
- // Initialization functions -------------------------------------------------
- //
- // Initialization methods used by InitTool(). If successful, will set the
- // field and return true, otherwise will return false. Must be called before
- // SetComplete().
- bool ValidateOutputSubstitution(const Substitution* sub_type) const;
- bool ValidateRuntimeOutputs(Err* err);
- // Validates either link_output or depend_output. To generalize to either,
- // pass
- // the associated pattern, and the variable name that should appear in error
- // messages.
- bool ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
- const char* variable_name,
- Err* err);
- bool ReadOutputsPatternList(Scope* scope,
- const char* var,
- SubstitutionList* field,
- Err* err);
- bool ReadPrecompiledHeaderType(Scope* scope, Err* err);
- bool ReadDepsFormat(Scope* scope, Err* err);
-
- DepsFormat depsformat_;
- PrecompiledHeaderType precompiled_header_type_;
- std::string lib_switch_;
- std::string lib_dir_switch_;
- SubstitutionPattern link_output_;
- SubstitutionPattern depend_output_;
-
- DISALLOW_COPY_AND_ASSIGN(CTool);
-};
-
-#endif // TOOLS_GN_C_TOOL_H_
diff --git a/gn/tools/gn/command_analyze.cc b/gn/tools/gn/command_analyze.cc
deleted file mode 100644
index d3b24bf895e..00000000000
--- a/gn/tools/gn/command_analyze.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 <algorithm>
-#include <iterator>
-#include <set>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "tools/gn/analyzer.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/location.h"
-#include "tools/gn/setup.h"
-
-namespace commands {
-
-const char kAnalyze[] = "analyze";
-const char kAnalyze_HelpShort[] =
- "analyze: Analyze which targets are affected by a list of files.";
-const char kAnalyze_Help[] =
- R"(gn analyze <out_dir> <input_path> <output_path>
-
- Analyze which targets are affected by a list of files.
-
- This command takes three arguments:
-
- out_dir is the path to the build directory.
-
- input_path is a path to a file containing a JSON object with three fields:
-
- - "files": A list of the filenames to check.
-
- - "test_targets": A list of the labels for targets that are needed to run
- the tests we wish to run.
-
- - "additional_compile_targets": A list of the labels for targets that we
- wish to rebuild, but aren't necessarily needed for testing. The important
- difference between this field and "test_targets" is that if an item in
- the additional_compile_targets list refers to a group, then any
- dependencies of that group will be returned if they are out of date, but
- the group itself does not need to be. If the dependencies themselves are
- groups, the same filtering is repeated. This filtering can be used to
- avoid rebuilding dependencies of a group that are unaffected by the input
- files. The list may also contain the string "all" to refer to a
- pseudo-group that contains every root target in the build graph.
-
- This filtering behavior is also known as "pruning" the list of compile
- targets.
-
- output_path is a path indicating where the results of the command are to be
- written. The results will be a file containing a JSON object with one or more
- of following fields:
-
- - "compile_targets": A list of the labels derived from the input
- compile_targets list that are affected by the input files. Due to the way
- the filtering works for compile targets as described above, this list may
- contain targets that do not appear in the input list.
-
- - "test_targets": A list of the labels from the input test_targets list that
- are affected by the input files. This list will be a proper subset of the
- input list.
-
- - "invalid_targets": A list of any names from the input that do not exist in
- the build graph. If this list is non-empty, the "error" field will also be
- set to "Invalid targets".
-
- - "status": A string containing one of three values:
-
- - "Found dependency"
- - "No dependency"
- - "Found dependency (all) "
-
- In the first case, the lists returned in compile_targets and test_targets
- should be passed to ninja to build. In the second case, nothing was
- affected and no build is necessary. In the third case, GN could not
- determine the correct answer and returned the input as the output in order
- to be safe.
-
- - "error": This will only be present if an error occurred, and will contain
- a string describing the error. This includes cases where the input file is
- not in the right format, or contains invalid targets.
-
- The command returns 1 if it is unable to read the input file or write the
- output file, or if there is something wrong with the build such that gen
- would also fail, and 0 otherwise. In particular, it returns 0 even if the
- "error" key is non-empty and a non-fatal error occurred. In other words, it
- tries really hard to always write something to the output JSON and convey
- errors that way rather than via return codes.
-)";
-
-int RunAnalyze(const std::vector<std::string>& args) {
- if (args.size() != 3) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn analyze <out_dir> <input_path> <output_path>")
- .PrintToStdout();
- return 1;
- }
-
- std::string input;
- bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
- if (!ret) {
- Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
- return 1;
- }
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false) || !setup->Run())
- return 1;
-
- Err err;
- Analyzer analyzer(
- setup->builder(), setup->build_settings().build_config_file(),
- setup->GetDotFile(),
- setup->build_settings().build_args().build_args_dependency_files());
- std::string output = analyzer.Analyze(input, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return 1;
- }
-
- WriteFile(UTF8ToFilePath(args[2]), output, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return 1;
- }
-
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_args.cc b/gn/tools/gn/command_args.cc
deleted file mode 100644
index 9957508bb2a..00000000000
--- a/gn/tools/gn/command_args.cc
+++ /dev/null
@@ -1,511 +0,0 @@
-// 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 <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <map>
-
-#include "base/command_line.h"
-#include "base/environment.h"
-#include "base/files/file_util.h"
-#include "base/json/json_writer.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/tokenizer.h"
-#include "tools/gn/trace.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-
-#include <shellapi.h>
-#endif
-
-namespace commands {
-
-namespace {
-
-const char kSwitchList[] = "list";
-const char kSwitchShort[] = "short";
-const char kSwitchOverridesOnly[] = "overrides-only";
-const char kSwitchJson[] = "json";
-
-bool DoesLineBeginWithComment(const base::StringPiece& line) {
- // Skip whitespace.
- size_t i = 0;
- while (i < line.size() && base::IsAsciiWhitespace(line[i]))
- i++;
-
- return i < line.size() && line[i] == '#';
-}
-
-// Returns the offset of the beginning of the line identified by |offset|.
-size_t BackUpToLineBegin(const std::string& data, size_t offset) {
- // Degenerate case of an empty line. Below we'll try to return the
- // character after the newline, but that will be incorrect in this case.
- if (offset == 0 || Tokenizer::IsNewline(data, offset))
- return offset;
-
- size_t cur = offset;
- do {
- cur--;
- if (Tokenizer::IsNewline(data, cur))
- return cur + 1; // Want the first character *after* the newline.
- } while (cur > 0);
- return 0;
-}
-
-// Assumes DoesLineBeginWithComment(), this strips the # character from the
-// beginning and normalizes preceding whitespace.
-std::string StripHashFromLine(const base::StringPiece& line, bool pad) {
- // Replace the # sign and everything before it with 3 spaces, so that a
- // normal comment that has a space after the # will be indented 4 spaces
- // (which makes our formatting come out nicely). If the comment is indented
- // from there, we want to preserve that indenting.
- std::string line_stripped = line.substr(line.find('#') + 1).as_string();
- if (pad)
- return " " + line_stripped;
-
- // If not padding, strip the leading space if present.
- if (!line_stripped.empty() && line_stripped[0] == ' ')
- return line_stripped.substr(1);
- return line_stripped;
-}
-
-// Tries to find the comment before the setting of the given value.
-void GetContextForValue(const Value& value,
- std::string* location_str,
- int* line_no,
- std::string* comment,
- bool pad_comment = true) {
- Location location = value.origin()->GetRange().begin();
- const InputFile* file = location.file();
- if (!file)
- return;
-
- *location_str = file->name().value();
- *line_no = location.line_number();
-
- const std::string& data = file->contents();
- size_t line_off =
- Tokenizer::ByteOffsetOfNthLine(data, location.line_number());
-
- while (line_off > 1) {
- line_off -= 2; // Back up to end of previous line.
- size_t previous_line_offset = BackUpToLineBegin(data, line_off);
-
- base::StringPiece line(&data[previous_line_offset],
- line_off - previous_line_offset + 1);
- if (!DoesLineBeginWithComment(line))
- break;
-
- comment->insert(0, StripHashFromLine(line, pad_comment) + "\n");
- line_off = previous_line_offset;
- }
-}
-
-// Prints the value and origin for a default value. Default values always list
-// an origin and if there is no origin, print a message about it being
-// internally set. Overrides can't be internally set so the location handling
-// is a bit different.
-//
-// The default value also contains the docstring.
-void PrintDefaultValueInfo(base::StringPiece name, const Value& value) {
- OutputString(value.ToString(true) + "\n");
- if (value.origin()) {
- int line_no;
- std::string location, comment;
- GetContextForValue(value, &location, &line_no, &comment);
- OutputString(" From " + location + ":" + base::IntToString(line_no) +
- "\n");
- if (!comment.empty())
- OutputString("\n" + comment);
- } else {
- OutputString(" (Internally set; try `gn help " + name.as_string() +
- "`.)\n");
- }
-}
-
-// Override value is null if there is no override.
-void PrintArgHelp(const base::StringPiece& name,
- const Args::ValueWithOverride& val) {
- OutputString(name.as_string(), DECORATION_YELLOW);
- OutputString("\n");
-
- if (val.has_override) {
- // Override present, print both it and the default.
- OutputString(" Current value = " + val.override_value.ToString(true) +
- "\n");
- if (val.override_value.origin()) {
- int line_no;
- std::string location, comment;
- GetContextForValue(val.override_value, &location, &line_no, &comment);
- OutputString(" From " + location + ":" + base::IntToString(line_no) +
- "\n");
- }
- OutputString(" Overridden from the default = ");
- PrintDefaultValueInfo(name, val.default_value);
- } else {
- // No override.
- OutputString(" Current value (from the default) = ");
- PrintDefaultValueInfo(name, val.default_value);
- }
-}
-
-void BuildArgJson(base::Value& dict,
- const base::StringPiece& name,
- const Args::ValueWithOverride& arg,
- bool short_only) {
- assert(dict.is_dict());
-
- // Fetch argument name.
- dict.SetKey("name", base::Value(name));
-
- // Fetch overridden value inforrmation (if present).
- if (arg.has_override) {
- base::DictionaryValue override_dict;
- override_dict.SetKey("value",
- base::Value(arg.override_value.ToString(true)));
- if (arg.override_value.origin() && !short_only) {
- int line_no;
- std::string location, comment;
- GetContextForValue(arg.override_value, &location, &line_no, &comment,
- /*pad_comment=*/false);
- override_dict.SetKey("file", base::Value(location));
- override_dict.SetKey("line", base::Value(line_no));
- }
- dict.SetKey("current", std::move(override_dict));
- }
-
- // Fetch default value information, and comment (if present).
- base::DictionaryValue default_dict;
- std::string comment;
- default_dict.SetKey("value", base::Value(arg.default_value.ToString(true)));
- if (arg.default_value.origin() && !short_only) {
- int line_no;
- std::string location;
- GetContextForValue(arg.default_value, &location, &line_no, &comment,
- /*pad_comment=*/false);
- default_dict.SetKey("file", base::Value(location));
- default_dict.SetKey("line", base::Value(line_no));
- }
- dict.SetKey("default", std::move(default_dict));
- if (!comment.empty() && !short_only)
- dict.SetKey("comment", base::Value(comment));
-}
-
-int ListArgs(const std::string& build_dir) {
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(build_dir, false) || !setup->Run())
- return 1;
-
- Args::ValueWithOverrideMap args =
- setup->build_settings().build_args().GetAllArguments();
- std::string list_value =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kSwitchList);
- if (!list_value.empty()) {
- // List just the one specified as the parameter to --list.
- auto found = args.find(list_value);
- if (found == args.end()) {
- Err(Location(), "Unknown build argument.",
- "You asked for \"" + list_value +
- "\" which I didn't find in any "
- "build file\nassociated with this build.")
- .PrintToStdout();
- return 1;
- }
-
- // Delete everything from the map except the one requested.
- Args::ValueWithOverrideMap::value_type preserved = *found;
- args.clear();
- args.insert(preserved);
- }
-
- // Cache this to avoid looking it up for each |arg| in the loops below.
- const bool overrides_only =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchOverridesOnly);
- const bool short_only =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort);
-
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchJson)) {
- // Convert all args to JSON, serialize and print them
- auto list = std::make_unique<base::ListValue>();
- for (const auto& arg : args) {
- if (overrides_only && !arg.second.has_override)
- continue;
- list->GetList().emplace_back(base::DictionaryValue());
- BuildArgJson(list->GetList().back(), arg.first, arg.second, short_only);
- }
- std::string s;
- base::JSONWriter::WriteWithOptions(
- *list.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
- OutputString(s);
- return 0;
- }
-
- if (short_only) {
- // Short <key>=<current_value> output.
- for (const auto& arg : args) {
- if (overrides_only && !arg.second.has_override)
- continue;
- OutputString(arg.first.as_string());
- OutputString(" = ");
- if (arg.second.has_override)
- OutputString(arg.second.override_value.ToString(true));
- else
- OutputString(arg.second.default_value.ToString(true));
- OutputString("\n");
- }
- return 0;
- }
-
- // Long output.
- for (const auto& arg : args) {
- if (overrides_only && !arg.second.has_override)
- continue;
- PrintArgHelp(arg.first, arg.second);
- OutputString("\n");
- }
-
- return 0;
-}
-
-#if defined(OS_WIN)
-
-bool RunEditor(const base::FilePath& file_to_edit) {
- SHELLEXECUTEINFO info;
- memset(&info, 0, sizeof(info));
- info.cbSize = sizeof(info);
- info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_CLASSNAME;
- info.lpFile = file_to_edit.value().c_str();
- info.nShow = SW_SHOW;
- info.lpClass = L".txt";
- if (!::ShellExecuteEx(&info)) {
- Err(Location(), "Couldn't run editor.",
- "Just edit \"" + FilePathToUTF8(file_to_edit) + "\" manually instead.")
- .PrintToStdout();
- return false;
- }
-
- if (!info.hProcess) {
- // Windows re-used an existing process.
- OutputString("\"" + FilePathToUTF8(file_to_edit) +
- "\" opened in editor, save it and press <Enter> when done.\n");
- getchar();
- } else {
- OutputString("Waiting for editor on \"" + FilePathToUTF8(file_to_edit) +
- "\"...\n");
- ::WaitForSingleObject(info.hProcess, INFINITE);
- ::CloseHandle(info.hProcess);
- }
- return true;
-}
-
-#else // POSIX
-
-bool RunEditor(const base::FilePath& file_to_edit) {
- const char* editor_ptr = getenv("GN_EDITOR");
- if (!editor_ptr)
- editor_ptr = getenv("VISUAL");
- if (!editor_ptr)
- editor_ptr = getenv("EDITOR");
- if (!editor_ptr)
- editor_ptr = "vi";
-
- std::string cmd(editor_ptr);
- cmd.append(" \"");
-
- // Its impossible to do this properly since we don't know the user's shell,
- // but quoting and escaping internal quotes should handle 99.999% of all
- // cases.
- std::string escaped_name = file_to_edit.value();
- base::ReplaceSubstringsAfterOffset(&escaped_name, 0, "\"", "\\\"");
- cmd.append(escaped_name);
- cmd.push_back('"');
-
- OutputString("Waiting for editor on \"" + file_to_edit.value() + "\"...\n");
- return system(cmd.c_str()) == 0;
-}
-
-#endif
-
-int EditArgsFile(const std::string& build_dir) {
- {
- // Scope the setup. We only use it for some basic state. We'll do the
- // "real" build below in the gen command.
- Setup setup;
- // Don't fill build arguments. We're about to edit the file which supplies
- // these in the first place.
- setup.set_fill_arguments(false);
- if (!setup.DoSetup(build_dir, true))
- return 1;
-
- // Ensure the file exists. Need to normalize path separators since on
- // Windows they can come out as forward slashes here, and that confuses some
- // of the commands.
- BuildSettings build_settings = setup.build_settings();
- base::FilePath arg_file =
- build_settings.GetFullPath(setup.GetBuildArgFile())
- .NormalizePathSeparators();
- if (!base::PathExists(arg_file)) {
- std::string argfile_default_contents =
- "# Build arguments go here.\n"
- "# See \"gn args <out_dir> --list\" for available build "
- "arguments.\n";
-
- SourceFile template_path = build_settings.arg_file_template_path();
- if (!template_path.is_null()) {
- base::FilePath full_path =
- build_settings.GetFullPath(template_path).NormalizePathSeparators();
- if (!base::PathExists(full_path)) {
- Err err =
- Err(Location(), std::string("Can't load arg_file_template:\n ") +
- template_path.value());
- err.PrintToStdout();
- return 1;
- }
-
- // Ignore the return code; if the read fails (unlikely), we'll just
- // use the default contents.
- base::ReadFileToString(full_path, &argfile_default_contents);
- }
-#if defined(OS_WIN)
- // Use Windows lineendings for this file since it will often open in
- // Notepad which can't handle Unix ones.
- base::ReplaceSubstringsAfterOffset(&argfile_default_contents, 0, "\n",
- "\r\n");
-#endif
- base::CreateDirectory(arg_file.DirName());
- base::WriteFile(arg_file, argfile_default_contents.c_str(),
- static_cast<int>(argfile_default_contents.size()));
- }
-
- ScopedTrace editor_trace(TraceItem::TRACE_SETUP, "Waiting for editor");
- if (!RunEditor(arg_file))
- return 1;
- }
-
- // Now do a normal "gen" command.
- OutputString("Generating files...\n");
- std::vector<std::string> gen_commands;
- gen_commands.push_back(build_dir);
- return RunGen(gen_commands);
-}
-
-} // namespace
-
-const char kArgs[] = "args";
-const char kArgs_HelpShort[] =
- "args: Display or configure arguments declared by the build.";
-const char kArgs_Help[] =
- R"(gn args: (command-line tool)
-
- Display or configure arguments declared by the build.
-
- gn args <out_dir> [--list] [--short] [--args] [--overrides-only]
-
- See also "gn help buildargs" for a more high-level overview of how
- build arguments work.
-
-Usage
-
- gn args <out_dir>
- Open the arguments for the given build directory in an editor. If the
- given build directory doesn't exist, it will be created and an empty args
- file will be opened in the editor. You would type something like this
- into that file:
- enable_doom_melon=false
- os="android"
-
- To find your editor on Posix, GN will search the environment variables in
- order: GN_EDITOR, VISUAL, and EDITOR. On Windows GN will open the command
- associated with .txt files.
-
- Note: you can edit the build args manually by editing the file "args.gn"
- in the build directory and then running "gn gen <out_dir>".
-
- gn args <out_dir> --list[=<exact_arg>] [--short] [--overrides-only] [--json]
- Lists all build arguments available in the current configuration, or, if
- an exact_arg is specified for the list flag, just that one build
- argument.
-
- The output will list the declaration location, current value for the
- build, default value (if different than the current value), and comment
- preceding the declaration.
-
- If --short is specified, only the names and current values will be
- printed.
-
- If --overrides-only is specified, only the names and current values of
- arguments that have been overridden (i.e. non-default arguments) will
- be printed. Overrides come from the <out_dir>/args.gn file and //.gn
-
- If --json is specified, the output will be emitted in json format.
- JSON schema for output:
- [
- {
- "name": variable_name,
- "current": {
- "value": overridden_value,
- "file": file_name,
- "line": line_no
- },
- "default": {
- "value": default_value,
- "file": file_name,
- "line": line_no
- },
- "comment": comment_string
- },
- ...
- ]
-
-Examples
-
- gn args out/Debug
- Opens an editor with the args for out/Debug.
-
- gn args out/Debug --list --short
- Prints all arguments with their default values for the out/Debug
- build.
-
- gn args out/Debug --list --short --overrides-only
- Prints overridden arguments for the out/Debug build.
-
- gn args out/Debug --list=target_cpu
- Prints information about the "target_cpu" argument for the "
- "out/Debug
- build.
-
- gn args --list --args="os=\"android\" enable_doom_melon=true"
- Prints all arguments with the default values for a build with the
- given arguments set (which may affect the values of other
- arguments).
-)";
-
-int RunArgs(const std::vector<std::string>& args) {
- if (args.size() != 1) {
- Err(Location(), "Exactly one build dir needed.",
- "Usage: \"gn args <out_dir>\"\n"
- "Or see \"gn help args\" for more variants.")
- .PrintToStdout();
- return 1;
- }
-
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchList))
- return ListArgs(args[0]);
- return EditArgsFile(args[0]);
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_check.cc b/gn/tools/gn/command_check.cc
deleted file mode 100644
index 3c74fc1f58f..00000000000
--- a/gn/tools/gn/command_check.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/command_line.h"
-#include "base/strings/stringprintf.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/header_checker.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-
-namespace commands {
-
-const char kNoGnCheck_Help[] =
- R"(nogncheck: Skip an include line from checking.
-
- GN's header checker helps validate that the includes match the build
- dependency graph. Sometimes an include might be conditional or otherwise
- problematic, but you want to specifically allow it. In this case, it can be
- whitelisted.
-
- Include lines containing the substring "nogncheck" will be excluded from
- header checking. The most common case is a conditional include:
-
- #if defined(ENABLE_DOOM_MELON)
- #include "tools/doom_melon/doom_melon.h" // nogncheck
- #endif
-
- If the build file has a conditional dependency on the corresponding target
- that matches the conditional include, everything will always link correctly:
-
- source_set("mytarget") {
- ...
- if (enable_doom_melon) {
- defines = [ "ENABLE_DOOM_MELON" ]
- deps += [ "//tools/doom_melon" ]
- }
-
- But GN's header checker does not understand preprocessor directives, won't
- know it matches the build dependencies, and will flag this include as
- incorrect when the condition is false.
-
-More information
-
- The topic "gn help check" has general information on how checking works and
- advice on fixing problems. Targets can also opt-out of checking, see
- "gn help check_includes".
-)";
-
-const char kCheck[] = "check";
-const char kCheck_HelpShort[] = "check: Check header dependencies.";
-const char kCheck_Help[] =
- R"(gn check <out_dir> [<label_pattern>] [--force] [--check-generated]
-
- GN's include header checker validates that the includes for C-like source
- files match the build dependency graph.
-
- "gn check" is the same thing as "gn gen" with the "--check" flag except that
- this command does not write out any build files. It's intended to be an easy
- way to manually trigger include file checking.
-
- The <label_pattern> can take exact labels or patterns that match more than
- one (although not general regular expressions). If specified, only those
- matching targets will be checked. See "gn help label_pattern" for details.
-
-Command-specific switches
-
- --force
- Ignores specifications of "check_includes = false" and checks all
- target's files that match the target label.
-
- --check-generated
- Generated files are normally not checked since they do not exist
- until after a build. With this flag, those generated files that
- can be found on disk are also checked.
-
-What gets checked
-
- The .gn file may specify a list of targets to be checked in the list
- check_targets (see "gn help dotfile"). If a label pattern is specified
- on the command line, check_targets is not used.
-
- Targets can opt-out from checking with "check_includes = false" (see
- "gn help check_includes").
-
- For targets being checked:
-
- - GN opens all C-like source files in the targets to be checked and scans
- the top for includes.
-
- - Generated files (that might not exist yet) are ignored unless
- the --check-generated flag is provided.
-
- - Includes with a "nogncheck" annotation are skipped (see
- "gn help nogncheck").
-
- - Only includes using "quotes" are checked. <brackets> are assumed to be
- system includes.
-
- - Include paths are assumed to be relative to any of the "include_dirs" for
- the target (including the implicit current dir).
-
- - GN does not run the preprocessor so will not understand conditional
- includes.
-
- - Only includes matching known files in the build are checked: includes
- matching unknown paths are ignored.
-
- For an include to be valid:
-
- - The included file must be in the current target, or there must be a path
- following only public dependencies to a target with the file in it
- ("gn path" is a good way to diagnose problems).
-
- - There can be multiple targets with an included file: only one needs to be
- valid for the include to be allowed.
-
- - If there are only "sources" in a target, all are considered to be public
- and can be included by other targets with a valid public dependency path.
-
- - If a target lists files as "public", only those files are able to be
- included by other targets. Anything in the sources will be considered
- private and will not be includable regardless of dependency paths.
-
- - Outputs from actions are treated like public sources on that target.
-
- - A target can include headers from a target that depends on it if the
- other target is annotated accordingly. See "gn help
- allow_circular_includes_from".
-
-Advice on fixing problems
-
- If you have a third party project that is difficult to fix or doesn't care
- about include checks it's generally best to exclude that target from checking
- altogether via "check_includes = false".
-
- If you have conditional includes, make sure the build conditions and the
- preprocessor conditions match, and annotate the line with "nogncheck" (see
- "gn help nogncheck" for an example).
-
- If two targets are hopelessly intertwined, use the
- "allow_circular_includes_from" annotation. Ideally each should have identical
- dependencies so configs inherited from those dependencies are consistent (see
- "gn help allow_circular_includes_from").
-
- If you have a standalone header file or files that need to be shared between
- a few targets, you can consider making a source_set listing only those
- headers as public sources. With only header files, the source set will be a
- no-op from a build perspective, but will give a central place to refer to
- those headers. That source set's files will still need to pass "gn check" in
- isolation.
-
- In rare cases it makes sense to list a header in more than one target if it
- could be considered conceptually a member of both.
-
-Examples
-
- gn check out/Debug
- Check everything.
-
- gn check out/Default //foo:bar
- Check only the files in the //foo:bar target.
-
- gn check out/Default "//foo/*
- Check only the files in targets in the //foo directory tree.
-)";
-
-int RunCheck(const std::vector<std::string>& args) {
- if (args.size() != 1 && args.size() != 2) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn check <out_dir> [<target_label>]\"")
- .PrintToStdout();
- return 1;
- }
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup();
- if (!setup->DoSetup(args[0], false))
- return 1;
- if (!setup->Run())
- return 1;
-
- std::vector<const Target*> all_targets =
- setup->builder().GetAllResolvedTargets();
-
- bool filtered_by_build_config = false;
- std::vector<const Target*> targets_to_check;
- if (args.size() > 1) {
- // Compute the targets to check.
- std::vector<std::string> inputs(args.begin() + 1, args.end());
- UniqueVector<const Target*> target_matches;
- UniqueVector<const Config*> config_matches;
- UniqueVector<const Toolchain*> toolchain_matches;
- UniqueVector<SourceFile> file_matches;
- if (!ResolveFromCommandLineInput(setup, inputs, false, &target_matches,
- &config_matches, &toolchain_matches,
- &file_matches))
- return 1;
-
- if (target_matches.size() == 0) {
- OutputString("No matching targets.\n");
- return 1;
- }
- targets_to_check.insert(targets_to_check.begin(), target_matches.begin(),
- target_matches.end());
- } else {
- // No argument means to check everything allowed by the filter in
- // the build config file.
- if (setup->check_patterns()) {
- FilterTargetsByPatterns(all_targets, *setup->check_patterns(),
- &targets_to_check);
- filtered_by_build_config = targets_to_check.size() != all_targets.size();
- } else {
- // No global filter, check everything.
- targets_to_check = all_targets;
- }
- }
-
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- bool force = cmdline->HasSwitch("force");
- bool check_generated = cmdline->HasSwitch("check-generated");
-
- if (!CheckPublicHeaders(&setup->build_settings(), all_targets,
- targets_to_check, force, check_generated))
- return 1;
-
- if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) {
- if (filtered_by_build_config) {
- // Tell the user about the implicit filtering since this is obscure.
- OutputString(base::StringPrintf(
- "%d targets out of %d checked based on the check_targets defined in"
- " \".gn\".\n",
- static_cast<int>(targets_to_check.size()),
- static_cast<int>(all_targets.size())));
- }
- OutputString("Header dependency check OK\n", DECORATION_GREEN);
- }
- return 0;
-}
-
-bool CheckPublicHeaders(const BuildSettings* build_settings,
- const std::vector<const Target*>& all_targets,
- const std::vector<const Target*>& to_check,
- bool force_check, bool check_generated) {
- ScopedTrace trace(TraceItem::TRACE_CHECK_HEADERS, "Check headers");
-
- scoped_refptr<HeaderChecker> header_checker(
- new HeaderChecker(build_settings, all_targets, check_generated));
-
- std::vector<Err> header_errors;
- header_checker->Run(to_check, force_check, &header_errors);
- for (size_t i = 0; i < header_errors.size(); i++) {
- if (i > 0)
- OutputString("___________________\n", DECORATION_YELLOW);
- header_errors[i].PrintToStdout();
- }
- return header_errors.empty();
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_clean.cc b/gn/tools/gn/command_clean.cc
deleted file mode 100644
index b3540393aad..00000000000
--- a/gn/tools/gn/command_clean.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-// 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/files/file_enumerator.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/setup.h"
-
-namespace {
-
-// 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.
-std::string ExtractGNBuildCommands(const base::FilePath& build_ninja_file) {
- std::string file_contents;
- if (!base::ReadFileToString(build_ninja_file, &file_contents))
- return std::string();
-
- std::vector<base::StringPiece> lines = base::SplitStringPiece(
- file_contents, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-
- std::string result;
- int num_blank_lines = 0;
- for (const auto& line : lines) {
- line.AppendToString(&result);
- result.push_back('\n');
- if (line.empty())
- ++num_blank_lines;
- if (num_blank_lines == 3)
- break;
- }
-
- return result;
-}
-
-} // namespace
-
-namespace commands {
-
-const char kClean[] = "clean";
-const char kClean_HelpShort[] = "clean: Cleans the output directory.";
-const char kClean_Help[] =
- "gn clean <out_dir>\n"
- "\n"
- " Deletes the contents of the output directory except for args.gn and\n"
- " creates a Ninja build environment sufficient to regenerate the build.\n";
-
-int RunClean(const std::vector<std::string>& args) {
- if (args.size() != 1) {
- Err(Location(), "You're holding it wrong.", "Usage: \"gn clean <out_dir>\"")
- .PrintToStdout();
- return 1;
- }
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false))
- return 1;
-
- base::FilePath build_dir(setup->build_settings().GetFullPath(
- SourceDir(setup->build_settings().build_dir().value())));
-
- // NOTE: Not all GN builds have args.gn file hence we check here
- // if a build.ninja.d files exists instead.
- base::FilePath build_ninja_d_file = build_dir.AppendASCII("build.ninja.d");
- if (!base::PathExists(build_ninja_d_file)) {
- Err(Location(),
- base::StringPrintf(
- "%s does not look like a build directory.\n",
- FilePathToUTF8(build_ninja_d_file.DirName().value()).c_str()))
- .PrintToStdout();
- return 1;
- }
-
- // Erase everything but the args file, and write a dummy build.ninja file that
- // will automatically rerun GN the next time Ninja is run.
- base::FilePath build_ninja_file = build_dir.AppendASCII("build.ninja");
- std::string build_commands = ExtractGNBuildCommands(build_ninja_file);
- if (build_commands.empty()) {
- // Couldn't parse the build.ninja file.
- Err(Location(), "Couldn't read build.ninja in this directory.",
- "Try running \"gn gen\" on it and then re-running \"gn clean\".")
- .PrintToStdout();
- return 1;
- }
-
- base::FileEnumerator traversal(
- build_dir, false,
- base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
- for (base::FilePath current = traversal.Next(); !current.empty();
- current = traversal.Next()) {
- if (base::ToLowerASCII(current.BaseName().value()) !=
- FILE_PATH_LITERAL("args.gn")) {
- base::DeleteFile(current, true);
- }
- }
-
- // Write the build.ninja file sufficiently to regenerate itself.
- if (base::WriteFile(build_ninja_file, build_commands.data(),
- static_cast<int>(build_commands.size())) == -1) {
- Err(Location(), std::string("Failed to write build.ninja."))
- .PrintToStdout();
- return 1;
- }
-
- // Write a .d file for the build which references a nonexistant file.
- // This will make Ninja always mark the build as dirty.
- std::string dummy_content("build.ninja: nonexistant_file.gn\n");
- if (base::WriteFile(build_ninja_d_file, dummy_content.data(),
- static_cast<int>(dummy_content.size())) == -1) {
- Err(Location(), std::string("Failed to write build.ninja.d."))
- .PrintToStdout();
- return 1;
- }
-
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_desc.cc b/gn/tools/gn/command_desc.cc
deleted file mode 100644
index 6531a8e94a2..00000000000
--- a/gn/tools/gn/command_desc.cc
+++ /dev/null
@@ -1,687 +0,0 @@
-// 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 <stddef.h>
-
-#include <algorithm>
-#include <memory>
-#include <set>
-#include <sstream>
-
-#include "base/command_line.h"
-#include "base/json/json_writer.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/config.h"
-#include "tools/gn/desc_builder.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/variables.h"
-
-namespace commands {
-
-namespace {
-
-// Desc-specific command line switches.
-const char kBlame[] = "blame";
-const char kTree[] = "tree";
-const char kAll[] = "all";
-
-void PrintDictValue(const base::Value* value,
- int indentLevel,
- bool use_first_indent) {
- std::string indent(indentLevel * 2, ' ');
- const base::ListValue* list_value = nullptr;
- const base::DictionaryValue* dict_value = nullptr;
- std::string string_value;
- bool bool_value = false;
- int int_value = 0;
- if (use_first_indent)
- OutputString(indent);
- if (value->GetAsList(&list_value)) {
- OutputString("[\n");
- bool first = true;
- for (const auto& v : *list_value) {
- if (!first)
- OutputString(",\n");
- PrintDictValue(&v, indentLevel + 1, true);
- first = false;
- }
- OutputString("\n" + indent + "]");
- } else if (value->GetAsString(&string_value)) {
- OutputString("\"" + string_value + "\"");
- } else if (value->GetAsBoolean(&bool_value)) {
- OutputString(bool_value ? "true" : "false");
- } else if (value->GetAsDictionary(&dict_value)) {
- OutputString("{\n");
- std::string indent_plus_one((indentLevel + 1) * 2, ' ');
- base::DictionaryValue::Iterator iter(*dict_value);
- bool first = true;
- while (!iter.IsAtEnd()) {
- if (!first)
- OutputString(",\n");
- OutputString(indent_plus_one + iter.key() + " = ");
- PrintDictValue(&iter.value(), indentLevel + 1, false);
- iter.Advance();
- first = false;
- }
- OutputString("\n" + indent + "}");
- } else if (value->GetAsInteger(&int_value)) {
- OutputString(base::IntToString(int_value));
- } else if (value->is_none()) {
- OutputString("<null>");
- }
-}
-
-// Prints value with specified indentation level
-void PrintValue(const base::Value* value, int indentLevel) {
- std::string indent(indentLevel * 2, ' ');
- const base::ListValue* list_value = nullptr;
- const base::DictionaryValue* dict_value = nullptr;
- std::string string_value;
- bool bool_value = false;
- int int_value = 0;
- if (value->GetAsList(&list_value)) {
- for (const auto& v : *list_value) {
- PrintValue(&v, indentLevel);
- }
- } else if (value->GetAsString(&string_value)) {
- OutputString(indent);
- OutputString(string_value);
- OutputString("\n");
- } else if (value->GetAsBoolean(&bool_value)) {
- OutputString(indent);
- OutputString(bool_value ? "true" : "false");
- OutputString("\n");
- } else if (value->GetAsDictionary(&dict_value)) {
- base::DictionaryValue::Iterator iter(*dict_value);
- while (!iter.IsAtEnd()) {
- OutputString(indent + iter.key() + "\n");
- PrintValue(&iter.value(), indentLevel + 1);
- iter.Advance();
- }
- } else if (value->GetAsInteger(&int_value)) {
- OutputString(indent);
- OutputString(base::IntToString(int_value));
- OutputString("\n");
- } else if (value->is_none()) {
- OutputString(indent + "<null>\n");
- }
-}
-
-// Default handler for property
-void DefaultHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- if (value_only) {
- PrintValue(value, 0);
- return;
- }
- OutputString("\n");
- OutputString(name);
- OutputString("\n");
- PrintValue(value, 1);
-}
-
-// Specific handler for properties that need different treatment
-
-// Prints the dict in GN scope-sytle.
-void MetadataHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- if (value_only) {
- PrintDictValue(value, 0, true);
- OutputString("\n");
- return;
- }
- OutputString("\n");
- OutputString(name);
- OutputString("\n");
- PrintDictValue(value, 1, true);
- OutputString("\n");
-}
-
-// Prints label and property value on one line, capitalizing the label.
-void LabelHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- if (value_only) {
- PrintValue(value, 0);
- return;
- }
- std::string label = name;
- label[0] = base::ToUpperASCII(label[0]);
- std::string string_value;
- if (value->GetAsString(&string_value)) {
- OutputString(name + ": ", DECORATION_YELLOW);
- OutputString(string_value + "\n");
- }
-}
-
-void VisibilityHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- if (value_only) {
- PrintValue(value, 0);
- return;
- }
- const base::ListValue* list;
- if (value->GetAsList(&list)) {
- if (list->empty()) {
- base::Value str("(no visibility)");
- DefaultHandler(name, &str, value_only);
- } else {
- DefaultHandler(name, value, value_only);
- }
- }
-}
-
-void PublicHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- if (value_only) {
- PrintValue(value, 0);
- return;
- }
- std::string p;
- if (value->GetAsString(&p)) {
- if (p == "*") {
- base::Value str("[All headers listed in the sources are public.]");
- DefaultHandler(name, &str, value_only);
- return;
- }
- }
- DefaultHandler(name, value, value_only);
-}
-
-void ConfigsHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
- if (tree)
- DefaultHandler(name + " tree (in order applying)", value, value_only);
- else
- DefaultHandler(name + " (in order applying, try also --tree)", value,
- value_only);
-}
-
-void DepsHandler(const std::string& name,
- const base::Value* value,
- bool value_only) {
- bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
- bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
- if (tree) {
- DefaultHandler("Dependency tree", value, value_only);
- } else {
- if (!all) {
- DefaultHandler(
- "Direct dependencies "
- "(try also \"--all\", \"--tree\", or even \"--all --tree\")",
- value, value_only);
- } else {
- DefaultHandler("All recursive dependencies", value, value_only);
- }
- }
-}
-
-// Outputs need special processing when output patterns are present.
-void ProcessOutputs(base::DictionaryValue* target, bool files_only) {
- base::ListValue* patterns = nullptr;
- base::ListValue* outputs = nullptr;
- target->GetList("output_patterns", &patterns);
- target->GetList(variables::kOutputs, &outputs);
-
- int indent = 0;
- if (outputs || patterns) {
- if (!files_only) {
- OutputString("\noutputs\n");
- indent = 1;
- }
- if (patterns) {
- if (!files_only) {
- OutputString(" Output patterns\n");
- indent = 2;
- }
- PrintValue(patterns, indent);
- if (!files_only)
- OutputString("\n Resolved output file list\n");
- }
- if (outputs)
- PrintValue(outputs, indent);
-
- target->Remove("output_patterns", nullptr);
- target->Remove(variables::kOutputs, nullptr);
- }
-}
-
-using DescHandlerFunc = void (*)(const std::string& name,
- const base::Value* value,
- bool value_only);
-std::map<std::string, DescHandlerFunc> GetHandlers() {
- return {{"type", LabelHandler},
- {"toolchain", LabelHandler},
- {variables::kVisibility, VisibilityHandler},
- {variables::kMetadata, MetadataHandler},
- {variables::kTestonly, DefaultHandler},
- {variables::kCheckIncludes, DefaultHandler},
- {variables::kAllowCircularIncludesFrom, DefaultHandler},
- {variables::kSources, DefaultHandler},
- {variables::kPublic, PublicHandler},
- {variables::kInputs, DefaultHandler},
- {variables::kConfigs, ConfigsHandler},
- {variables::kPublicConfigs, ConfigsHandler},
- {variables::kAllDependentConfigs, ConfigsHandler},
- {variables::kScript, DefaultHandler},
- {variables::kArgs, DefaultHandler},
- {variables::kDepfile, DefaultHandler},
- {"bundle_data", DefaultHandler},
- {variables::kArflags, DefaultHandler},
- {variables::kAsmflags, DefaultHandler},
- {variables::kCflags, DefaultHandler},
- {variables::kCflagsC, DefaultHandler},
- {variables::kCflagsCC, DefaultHandler},
- {variables::kCflagsObjC, DefaultHandler},
- {variables::kCflagsObjCC, DefaultHandler},
- {variables::kDefines, DefaultHandler},
- {variables::kIncludeDirs, DefaultHandler},
- {variables::kLdflags, DefaultHandler},
- {variables::kPrecompiledHeader, DefaultHandler},
- {variables::kPrecompiledSource, DefaultHandler},
- {variables::kDeps, DepsHandler},
- {variables::kLibs, DefaultHandler},
- {variables::kLibDirs, DefaultHandler},
- {variables::kDataKeys, DefaultHandler},
- {variables::kRebase, DefaultHandler},
- {variables::kWalkKeys, DefaultHandler},
- {variables::kWriteOutputConversion, DefaultHandler},
- {"runtime_deps", DefaultHandler}};
-}
-
-void HandleProperty(const std::string& what,
- const std::map<std::string, DescHandlerFunc>& handler_map,
- std::unique_ptr<base::Value>& v,
- std::unique_ptr<base::DictionaryValue>& dict) {
- if (dict->Remove(what, &v)) {
- auto pair = handler_map.find(what);
- if (pair != handler_map.end())
- pair->second(what, v.get(), false);
- }
-}
-
-bool PrintTarget(const Target* target,
- const std::string& what,
- bool single_target,
- const std::map<std::string, DescHandlerFunc>& handler_map,
- bool all,
- bool tree,
- bool blame) {
- std::unique_ptr<base::DictionaryValue> dict =
- DescBuilder::DescriptionForTarget(target, what, all, tree, blame);
- if (!what.empty() && dict->empty()) {
- OutputString("Don't know how to display \"" + what + "\" for \"" +
- Target::GetStringForOutputType(target->output_type()) +
- "\".\n");
- return false;
- }
- // Print single value
- if (!what.empty() && dict->size() == 1 && single_target) {
- if (what == variables::kOutputs) {
- ProcessOutputs(dict.get(), true);
- return true;
- }
- base::DictionaryValue::Iterator iter(*dict);
- auto pair = handler_map.find(what);
- if (pair != handler_map.end())
- pair->second(what, &iter.value(), true);
- return true;
- }
-
- OutputString("Target ", DECORATION_YELLOW);
- OutputString(target->label().GetUserVisibleName(false));
- OutputString("\n");
-
- std::unique_ptr<base::Value> v;
- // Entries with DefaultHandler are present to enforce order
- HandleProperty("type", handler_map, v, dict);
- HandleProperty("toolchain", handler_map, v, dict);
- HandleProperty(variables::kVisibility, handler_map, v, dict);
- HandleProperty(variables::kMetadata, handler_map, v, dict);
- HandleProperty(variables::kTestonly, handler_map, v, dict);
- HandleProperty(variables::kCheckIncludes, handler_map, v, dict);
- HandleProperty(variables::kAllowCircularIncludesFrom, handler_map, v, dict);
- HandleProperty(variables::kSources, handler_map, v, dict);
- HandleProperty(variables::kPublic, handler_map, v, dict);
- HandleProperty(variables::kInputs, handler_map, v, dict);
- HandleProperty(variables::kConfigs, handler_map, v, dict);
- HandleProperty(variables::kPublicConfigs, handler_map, v, dict);
- HandleProperty(variables::kAllDependentConfigs, handler_map, v, dict);
- HandleProperty(variables::kScript, handler_map, v, dict);
- HandleProperty(variables::kArgs, handler_map, v, dict);
- HandleProperty(variables::kDepfile, handler_map, v, dict);
- ProcessOutputs(dict.get(), false);
- HandleProperty("bundle_data", handler_map, v, dict);
- HandleProperty(variables::kArflags, handler_map, v, dict);
- HandleProperty(variables::kAsmflags, handler_map, v, dict);
- HandleProperty(variables::kCflags, handler_map, v, dict);
- HandleProperty(variables::kCflagsC, handler_map, v, dict);
- HandleProperty(variables::kCflagsCC, handler_map, v, dict);
- HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
- HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
- HandleProperty(variables::kDefines, handler_map, v, dict);
- HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
- HandleProperty(variables::kLdflags, handler_map, v, dict);
- HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
- HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
- HandleProperty(variables::kDeps, handler_map, v, dict);
- HandleProperty(variables::kLibs, handler_map, v, dict);
- HandleProperty(variables::kLibDirs, handler_map, v, dict);
- HandleProperty(variables::kDataKeys, handler_map, v, dict);
- HandleProperty(variables::kRebase, handler_map, v, dict);
- HandleProperty(variables::kWalkKeys, handler_map, v, dict);
- HandleProperty(variables::kWriteOutputConversion, handler_map, v, dict);
-
-#undef HandleProperty
-
- // Process the rest (if any)
- base::DictionaryValue::Iterator iter(*dict);
- while (!iter.IsAtEnd()) {
- DefaultHandler(iter.key(), &iter.value(), false);
- iter.Advance();
- }
-
- return true;
-}
-
-bool PrintConfig(const Config* config,
- const std::string& what,
- bool single_config,
- const std::map<std::string, DescHandlerFunc>& handler_map) {
- std::unique_ptr<base::DictionaryValue> dict =
- DescBuilder::DescriptionForConfig(config, what);
- if (!what.empty() && dict->empty()) {
- OutputString("Don't know how to display \"" + what + "\" for a config.\n");
- return false;
- }
- // Print single value
- if (!what.empty() && dict->size() == 1 && single_config) {
- base::DictionaryValue::Iterator iter(*dict);
- auto pair = handler_map.find(what);
- if (pair != handler_map.end())
- pair->second(what, &iter.value(), true);
- return true;
- }
-
- OutputString("Config: ", DECORATION_YELLOW);
- OutputString(config->label().GetUserVisibleName(false));
- OutputString("\n");
-
- std::unique_ptr<base::Value> v;
- HandleProperty("toolchain", handler_map, v, dict);
- if (!config->configs().empty()) {
- OutputString(
- "(This is a composite config, the values below are after the\n"
- "expansion of the child configs.)\n");
- }
- HandleProperty(variables::kArflags, handler_map, v, dict);
- HandleProperty(variables::kAsmflags, handler_map, v, dict);
- HandleProperty(variables::kCflags, handler_map, v, dict);
- HandleProperty(variables::kCflagsC, handler_map, v, dict);
- HandleProperty(variables::kCflagsCC, handler_map, v, dict);
- HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
- HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
- HandleProperty(variables::kDefines, handler_map, v, dict);
- HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
- HandleProperty(variables::kInputs, handler_map, v, dict);
- HandleProperty(variables::kLdflags, handler_map, v, dict);
- HandleProperty(variables::kLibs, handler_map, v, dict);
- HandleProperty(variables::kLibDirs, handler_map, v, dict);
- HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
- HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
-
-#undef HandleProperty
-
- return true;
-}
-
-} // namespace
-
-// desc ------------------------------------------------------------------------
-
-const char kDesc[] = "desc";
-const char kDesc_HelpShort[] =
- "desc: Show lots of insightful information about a target or config.";
-const char kDesc_Help[] =
- R"(gn desc
-
- gn desc <out_dir> <label or pattern> [<what to show>] [--blame]
- [--format=json]
-
- Displays information about a given target or config. The build parameters
- will be taken for the build in the given <out_dir>.
-
- The <label or pattern> can be a target label, a config label, or a label
- pattern (see "gn help label_pattern"). A label pattern will only match
- targets.
-
-Possibilities for <what to show>
-
- (If unspecified an overall summary will be displayed.)
-
- all_dependent_configs
- allow_circular_includes_from
- arflags [--blame]
- args
- cflags [--blame]
- cflags_c [--blame]
- cflags_cc [--blame]
- check_includes
- configs [--tree] (see below)
- data_keys
- defines [--blame]
- depfile
- deps [--all] [--tree] (see below)
- include_dirs [--blame]
- inputs
- ldflags [--blame]
- lib_dirs
- libs
- metadata
- output_conversion
- outputs
- public_configs
- public
- rebase
- script
- sources
- testonly
- visibility
- walk_keys
-
- runtime_deps
- Compute all runtime deps for the given target. This is a computed list
- and does not correspond to any GN variable, unlike most other values
- here.
-
- The output is a list of file names relative to the build directory. See
- "gn help runtime_deps" for how this is computed. This also works with
- "--blame" to see the source of the dependency.
-
-Shared flags
-)"
-
- ALL_TOOLCHAINS_SWITCH_HELP
-
- R"(
- --format=json
- Format the output as JSON instead of text.
-
-Target flags
-
- --blame
- Used with any value specified on a config, this will name the config that
- causes that target to get the flag. This doesn't currently work for libs
- and lib_dirs because those are inherited and are more complicated to
- figure out the blame (patches welcome).
-
-Configs
-
- The "configs" section will list all configs that apply. For targets this will
- include configs specified in the "configs" variable of the target, and also
- configs pushed onto this target via public or "all dependent" configs.
-
- Configs can have child configs. Specifying --tree will show the hierarchy.
-
-Printing outputs
-
- The "outputs" section will list all outputs that apply, including the outputs
- computed from the tool definition (eg for "executable", "static_library", ...
- targets).
-
-Printing deps
-
- Deps will include all public, private, and data deps (TODO this could be
- clarified and enhanced) sorted in order applying. The following may be used:
-
- --all
- Collects all recursive dependencies and prints a sorted flat list. Also
- usable with --tree (see below).
-)"
-
- TARGET_PRINTING_MODE_COMMAND_LINE_HELP
- "\n" TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
-
- R"(
- --tree
- Print a dependency tree. By default, duplicates will be elided with "..."
- but when --all and -tree are used together, no eliding will be performed.
-
- The "deps", "public_deps", and "data_deps" will all be included in the
- tree.
-
- Tree output can not be used with the filtering or output flags: --as,
- --type, --testonly.
-)"
-
- TARGET_TYPE_FILTER_COMMAND_LINE_HELP
-
- R"(Note
-
- This command will show the full name of directories and source files, but
- when directories and source paths are written to the build file, they will be
- adjusted to be relative to the build directory. So the values for paths
- displayed by this command won't match (but should mean the same thing).
-
-Examples
-
- gn desc out/Debug //base:base
- Summarizes the given target.
-
- gn desc out/Foo :base_unittests deps --tree
- Shows a dependency tree of the "base_unittests" project in
- the current directory.
-
- gn desc out/Debug //base defines --blame
- Shows defines set for the //base:base target, annotated by where
- each one was set from.
-)";
-
-int RunDesc(const std::vector<std::string>& args) {
- if (args.size() != 2 && args.size() != 3) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn desc <out_dir> <target_name> [<what to display>]\"")
- .PrintToStdout();
- return 1;
- }
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false))
- return 1;
- if (!setup->Run())
- return 1;
-
- // Resolve target(s) and config from inputs.
- UniqueVector<const Target*> target_matches;
- UniqueVector<const Config*> config_matches;
- UniqueVector<const Toolchain*> toolchain_matches;
- UniqueVector<SourceFile> file_matches;
-
- std::vector<std::string> target_list;
- target_list.push_back(args[1]);
-
- if (!ResolveFromCommandLineInput(
- setup, target_list, cmdline->HasSwitch(switches::kAllToolchains),
- &target_matches, &config_matches, &toolchain_matches, &file_matches))
- return 1;
-
- std::string what_to_print;
- if (args.size() == 3)
- what_to_print = args[2];
-
- bool json = cmdline->GetSwitchValueASCII("format") == "json";
-
- if (target_matches.empty() && config_matches.empty()) {
- OutputString(
- "The input " + args[1] + " matches no targets, configs or files.\n",
- DECORATION_YELLOW);
- return 1;
- }
-
- if (json) {
- // Convert all targets/configs to JSON, serialize and print them
- auto res = std::make_unique<base::DictionaryValue>();
- if (!target_matches.empty()) {
- for (const auto* target : target_matches) {
- res->SetWithoutPathExpansion(
- target->label().GetUserVisibleName(
- target->settings()->default_toolchain_label()),
- DescBuilder::DescriptionForTarget(
- target, what_to_print, cmdline->HasSwitch(kAll),
- cmdline->HasSwitch(kTree), cmdline->HasSwitch(kBlame)));
- }
- } else if (!config_matches.empty()) {
- for (const auto* config : config_matches) {
- res->SetWithoutPathExpansion(
- config->label().GetUserVisibleName(false),
- DescBuilder::DescriptionForConfig(config, what_to_print));
- }
- }
- std::string s;
- base::JSONWriter::WriteWithOptions(
- *res.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
- OutputString(s);
- } else {
- // Regular (non-json) formatted output
- bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1;
- std::map<std::string, DescHandlerFunc> handlers = GetHandlers();
-
- bool printed_output = false;
- for (const Target* target : target_matches) {
- if (printed_output)
- OutputString("\n\n");
- printed_output = true;
-
- if (!PrintTarget(target, what_to_print, !multiple_outputs, handlers,
- cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree),
- cmdline->HasSwitch(kBlame)))
- return 1;
- }
- for (const Config* config : config_matches) {
- if (printed_output)
- OutputString("\n\n");
- printed_output = true;
-
- if (!PrintConfig(config, what_to_print, !multiple_outputs, handlers))
- return 1;
- }
- }
-
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_format.cc b/gn/tools/gn/command_format.cc
deleted file mode 100644
index 4249c2f91f1..00000000000
--- a/gn/tools/gn/command_format.cc
+++ /dev/null
@@ -1,1246 +0,0 @@
-// 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 "tools/gn/command_format.h"
-
-#include <stddef.h>
-
-#include <sstream>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/json/json_writer.h"
-#include "base/macros.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/tokenizer.h"
-
-namespace commands {
-
-const char kSwitchDryRun[] = "dry-run";
-const char kSwitchDumpTree[] = "dump-tree";
-const char kSwitchDumpTreeText[] = "text";
-const char kSwitchDumpTreeJSON[] = "json";
-const char kSwitchStdin[] = "stdin";
-
-const char kFormat[] = "format";
-const char kFormat_HelpShort[] = "format: Format .gn files.";
-const char kFormat_Help[] =
- R"(gn format [--dump-tree] (--stdin | <list of build_files...>)
-
- Formats .gn file to a standard format.
-
- The contents of some lists ('sources', 'deps', etc.) will be sorted to a
- canonical order. To suppress this, you can add a comment of the form "#
- NOSORT" immediately preceding the assignment. e.g.
-
- # NOSORT
- sources = [
- "z.cc",
- "a.cc",
- ]
-
-Arguments
-
- --dry-run
- Does not change or output anything, but sets the process exit code based
- on whether output would be different than what's on disk. This is useful
- for presubmit/lint-type checks.
- - Exit code 0: successful format, matches on disk.
- - Exit code 1: general failure (parse error, etc.)
- - Exit code 2: successful format, but differs from on disk.
-
- --dump-tree[=( text | json )]
- Dumps the parse tree to stdout and does not update the file or print
- formatted output. If no format is specified, text format will be used.
-
- --stdin
- Read input from stdin and write to stdout rather than update a file
- in-place.
-
-Examples
- gn format //some/BUILD.gn //some/other/BUILD.gn //and/another/BUILD.gn
- gn format some\\BUILD.gn
- gn format /abspath/some/BUILD.gn
- gn format --stdin
-)";
-
-namespace {
-
-const int kIndentSize = 2;
-const int kMaximumWidth = 80;
-
-const int kPenaltyLineBreak = 500;
-const int kPenaltyHorizontalSeparation = 100;
-const int kPenaltyExcess = 10000;
-const int kPenaltyBrokenLineOnOneLiner = 5000;
-
-enum Precedence {
- kPrecedenceLowest,
- kPrecedenceAssign,
- kPrecedenceOr,
- kPrecedenceAnd,
- kPrecedenceCompare,
- kPrecedenceAdd,
- kPrecedenceUnary,
- kPrecedenceSuffix,
-};
-
-int CountLines(const std::string& str) {
- return static_cast<int>(base::SplitStringPiece(str, "\n",
- base::KEEP_WHITESPACE,
- base::SPLIT_WANT_ALL)
- .size());
-}
-
-class Printer {
- public:
- Printer();
- ~Printer();
-
- void Block(const ParseNode* file);
-
- std::string String() const { return output_; }
-
- private:
- // Format a list of values using the given style.
- enum SequenceStyle {
- kSequenceStyleList,
- kSequenceStyleBracedBlock,
- kSequenceStyleBracedBlockAlreadyOpen,
- };
-
- struct Metrics {
- Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
- int first_length;
- int longest_length;
- bool multiline;
- };
-
- // Add to output.
- void Print(base::StringPiece str);
-
- // Add the current margin (as spaces) to the output.
- void PrintMargin();
-
- void TrimAndPrintToken(const Token& token);
-
- // End the current line, flushing end of line comments.
- void Newline();
-
- // Remove trailing spaces from the current line.
- void Trim();
-
- // Whether there's a blank separator line at the current position.
- bool HaveBlankLine();
-
- // Flag assignments to sources, deps, etc. to make their RHSs multiline.
- void AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop);
-
- // Sort a list on the RHS if the LHS is 'sources', 'deps' or 'public_deps'.
- // The 'sources' are sorted alphabetically while the 'deps' and 'public_deps'
- // are sorted putting first the relative targets and then the global ones
- // (both sorted alphabetically).
- void SortIfSourcesOrDeps(const BinaryOpNode* binop);
-
- // Sort contiguous import() function calls in the given ordered list of
- // statements (the body of a block or scope).
- template <class PARSENODE>
- void SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements);
-
- // Heuristics to decide if there should be a blank line added between two
- // items. For various "small" items, it doesn't look nice if there's too much
- // vertical whitespace added.
- bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
-
- // Get the 0-based x position on the current line.
- int CurrentColumn() const;
-
- // Get the current line in the output;
- int CurrentLine() const;
-
- // Adds an opening ( if prec is less than the outers (to maintain evalution
- // order for a subexpression). If an opening paren is emitted, *parenthesized
- // will be set so it can be closed at the end of the expression.
- void AddParen(int prec, int outer_prec, bool* parenthesized);
-
- // Print the expression to the output buffer. Returns the type of element
- // added to the output. The value of outer_prec gives the precedence of the
- // operator outside this Expr. If that operator binds tighter than root's,
- // Expr must introduce parentheses.
- int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
-
- // Generic penalties for exceeding maximum width, adding more lines, etc.
- int AssessPenalty(const std::string& output);
-
- // Tests if any lines exceed the maximum width.
- bool ExceedsMaximumWidth(const std::string& output);
-
- // Format a list of values using the given style.
- // |end| holds any trailing comments to be printed just before the closing
- // bracket.
- template <class PARSENODE> // Just for const covariance.
- void Sequence(SequenceStyle style,
- const std::vector<std::unique_ptr<PARSENODE>>& list,
- const ParseNode* end,
- bool force_multiline);
-
- // Returns the penalty.
- int FunctionCall(const FunctionCallNode* func_call,
- const std::string& suffix);
-
- // Create a clone of this Printer in a similar state (other than the output,
- // but including margins, etc.) to be used for dry run measurements.
- void InitializeSub(Printer* sub);
-
- template <class PARSENODE>
- bool ListWillBeMultiline(const std::vector<std::unique_ptr<PARSENODE>>& list,
- const ParseNode* end);
-
- std::string output_; // Output buffer.
- std::vector<Token> comments_; // Pending end-of-line comments.
- int margin() const { return stack_.back().margin; }
-
- int penalty_depth_;
- int GetPenaltyForLineBreak() const {
- return penalty_depth_ * kPenaltyLineBreak;
- }
-
- struct IndentState {
- IndentState()
- : margin(0),
- continuation_requires_indent(false),
- parent_is_boolean_or(false) {}
- IndentState(int margin,
- bool continuation_requires_indent,
- bool parent_is_boolean_or)
- : margin(margin),
- continuation_requires_indent(continuation_requires_indent),
- parent_is_boolean_or(parent_is_boolean_or) {}
-
- // The left margin (number of spaces).
- int margin;
-
- bool continuation_requires_indent;
-
- bool parent_is_boolean_or;
- };
- // Stack used to track
- std::vector<IndentState> stack_;
-
- // Gives the precedence for operators in a BinaryOpNode.
- std::map<base::StringPiece, Precedence> precedence_;
-
- DISALLOW_COPY_AND_ASSIGN(Printer);
-};
-
-Printer::Printer() : penalty_depth_(0) {
- output_.reserve(100 << 10);
- precedence_["="] = kPrecedenceAssign;
- precedence_["+="] = kPrecedenceAssign;
- precedence_["-="] = kPrecedenceAssign;
- precedence_["||"] = kPrecedenceOr;
- precedence_["&&"] = kPrecedenceAnd;
- precedence_["<"] = kPrecedenceCompare;
- precedence_[">"] = kPrecedenceCompare;
- precedence_["=="] = kPrecedenceCompare;
- precedence_["!="] = kPrecedenceCompare;
- precedence_["<="] = kPrecedenceCompare;
- precedence_[">="] = kPrecedenceCompare;
- precedence_["+"] = kPrecedenceAdd;
- precedence_["-"] = kPrecedenceAdd;
- precedence_["!"] = kPrecedenceUnary;
- stack_.push_back(IndentState());
-}
-
-Printer::~Printer() = default;
-
-void Printer::Print(base::StringPiece str) {
- str.AppendToString(&output_);
-}
-
-void Printer::PrintMargin() {
- output_ += std::string(margin(), ' ');
-}
-
-void Printer::TrimAndPrintToken(const Token& token) {
- std::string trimmed;
- TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
- Print(trimmed);
-}
-
-void Printer::Newline() {
- if (!comments_.empty()) {
- Print(" ");
- // Save the margin, and temporarily set it to where the first comment
- // starts so that multiple suffix comments are vertically aligned. This
- // will need to be fancier once we enforce 80 col.
- stack_.push_back(IndentState(CurrentColumn(), false, false));
- int i = 0;
- for (const auto& c : comments_) {
- if (i > 0) {
- Trim();
- Print("\n");
- PrintMargin();
- }
- TrimAndPrintToken(c);
- ++i;
- }
- stack_.pop_back();
- comments_.clear();
- }
- Trim();
- Print("\n");
- PrintMargin();
-}
-
-void Printer::Trim() {
- size_t n = output_.size();
- while (n > 0 && output_[n - 1] == ' ')
- --n;
- output_.resize(n);
-}
-
-bool Printer::HaveBlankLine() {
- size_t n = output_.size();
- while (n > 0 && output_[n - 1] == ' ')
- --n;
- return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
-}
-
-void Printer::AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop) {
- const IdentifierNode* ident = binop->left()->AsIdentifier();
- const ListNode* list = binop->right()->AsList();
- // This is somewhat arbitrary, but we include the 'deps'- and 'sources'-like
- // things, but not flags things.
- if (binop->op().value() == "=" && ident && list) {
- const base::StringPiece lhs = ident->value().value();
- if (lhs == "data" || lhs == "datadeps" || lhs == "data_deps" ||
- lhs == "deps" || lhs == "inputs" || lhs == "outputs" ||
- lhs == "public" || lhs == "public_deps" || lhs == "sources") {
- const_cast<ListNode*>(list)->set_prefer_multiline(true);
- }
- }
-}
-
-void Printer::SortIfSourcesOrDeps(const BinaryOpNode* binop) {
- if (const Comments* comments = binop->comments()) {
- const std::vector<Token>& before = comments->before();
- if (!before.empty() &&
- (before.front().value().as_string() == "# NOSORT" ||
- before.back().value().as_string() == "# NOSORT")) {
- // Allow disabling of sort for specific actions that might be
- // order-sensitive.
- return;
- }
- }
- const IdentifierNode* ident = binop->left()->AsIdentifier();
- const ListNode* list = binop->right()->AsList();
- if ((binop->op().value() == "=" || binop->op().value() == "+=" ||
- binop->op().value() == "-=") &&
- ident && list) {
- const base::StringPiece lhs = ident->value().value();
- if (lhs == "public" || lhs == "sources")
- const_cast<ListNode*>(list)->SortAsStringsList();
- else if (lhs == "deps" || lhs == "public_deps")
- const_cast<ListNode*>(list)->SortAsDepsList();
- }
-}
-
-template <class PARSENODE>
-void Printer::SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements) {
- // Build a set of ranges by indices of FunctionCallNode's that are imports.
-
- std::vector<std::vector<size_t>> import_statements;
-
- auto is_import = [](const PARSENODE* p) {
- const FunctionCallNode* func_call = p->AsFunctionCall();
- return func_call && func_call->function().value() == "import";
- };
-
- std::vector<size_t> current_group;
- for (size_t i = 0; i < statements.size(); ++i) {
- if (is_import(statements[i].get())) {
- if (i > 0 && (!is_import(statements[i - 1].get()) ||
- ShouldAddBlankLineInBetween(statements[i - 1].get(),
- statements[i].get()))) {
- if (!current_group.empty()) {
- import_statements.push_back(current_group);
- current_group.clear();
- }
- }
- current_group.push_back(i);
- }
- }
-
- if (!current_group.empty())
- import_statements.push_back(current_group);
-
- struct CompareByImportFile {
- bool operator()(const std::unique_ptr<PARSENODE>& a,
- const std::unique_ptr<PARSENODE>& b) const {
- const auto& a_args = a->AsFunctionCall()->args()->contents();
- const auto& b_args = b->AsFunctionCall()->args()->contents();
- base::StringPiece a_name;
- base::StringPiece b_name;
- if (!a_args.empty())
- a_name = a_args[0]->AsLiteral()->value().value();
- if (!b_args.empty())
- b_name = b_args[0]->AsLiteral()->value().value();
-
- auto is_absolute = [](base::StringPiece import) {
- return import.size() >= 3 && import[0] == '"' && import[1] == '/' &&
- import[2] == '/';
- };
- int a_is_rel = !is_absolute(a_name);
- int b_is_rel = !is_absolute(b_name);
-
- return std::tie(a_is_rel, a_name) < std::tie(b_is_rel, b_name);
- }
- };
-
- int line_after_previous = -1;
-
- for (const auto& group : import_statements) {
- size_t begin = group[0];
- size_t end = group.back() + 1;
-
- // Save the original line number so that ranges can be re-assigned. They're
- // contiguous because of the partitioning code above. Later formatting
- // relies on correct line number to know whether to insert blank lines,
- // which is why these need to be fixed up. Additionally, to handle multiple
- // imports on one line, they're assigned sequential line numbers, and
- // subsequent blocks will be gapped from them.
- int start_line =
- std::max(statements[begin]->GetRange().begin().line_number(),
- line_after_previous + 1);
-
- std::sort(statements.begin() + begin, statements.begin() + end,
- CompareByImportFile());
-
- const PARSENODE* prev = nullptr;
- for (size_t i = begin; i < end; ++i) {
- const PARSENODE* node = statements[i].get();
- int line_number =
- prev ? prev->GetRange().end().line_number() + 1 : start_line;
- if (node->comments() && !node->comments()->before().empty())
- line_number++;
- const_cast<FunctionCallNode*>(node->AsFunctionCall())
- ->SetNewLocation(line_number);
- prev = node;
- line_after_previous = line_number + 1;
- }
- }
-}
-
-bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
- const ParseNode* b) {
- LocationRange a_range = a->GetRange();
- LocationRange b_range = b->GetRange();
- // If they're already separated by 1 or more lines, then we want to keep a
- // blank line.
- return (b_range.begin().line_number() > a_range.end().line_number() + 1) ||
- // Always put a blank line before a block comment.
- b->AsBlockComment();
-}
-
-int Printer::CurrentColumn() const {
- int n = 0;
- while (n < static_cast<int>(output_.size()) &&
- output_[output_.size() - 1 - n] != '\n') {
- ++n;
- }
- return n;
-}
-
-int Printer::CurrentLine() const {
- int count = 1;
- for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != nullptr;) {
- ++count;
- ++p;
- }
- return count;
-}
-
-void Printer::Block(const ParseNode* root) {
- const BlockNode* block = root->AsBlock();
-
- if (block->comments()) {
- for (const auto& c : block->comments()->before()) {
- TrimAndPrintToken(c);
- Newline();
- }
- }
-
- SortImports(const_cast<std::vector<std::unique_ptr<ParseNode>>&>(
- block->statements()));
-
- size_t i = 0;
- for (const auto& stmt : block->statements()) {
- Expr(stmt.get(), kPrecedenceLowest, std::string());
- Newline();
- if (stmt->comments()) {
- // Why are before() not printed here too? before() are handled inside
- // Expr(), as are suffix() which are queued to the next Newline().
- // However, because it's a general expression handler, it doesn't insert
- // the newline itself, which only happens between block statements. So,
- // the after are handled explicitly here.
- for (const auto& c : stmt->comments()->after()) {
- TrimAndPrintToken(c);
- Newline();
- }
- }
- if (i < block->statements().size() - 1 &&
- (ShouldAddBlankLineInBetween(block->statements()[i].get(),
- block->statements()[i + 1].get()))) {
- Newline();
- }
- ++i;
- }
-
- if (block->comments()) {
- if (!block->statements().empty() &&
- block->statements().back()->AsBlockComment()) {
- // If the block ends in a comment, and there's a comment following it,
- // then the two comments were originally separate, so keep them that way.
- Newline();
- }
- for (const auto& c : block->comments()->after()) {
- TrimAndPrintToken(c);
- Newline();
- }
- }
-}
-
-int Printer::AssessPenalty(const std::string& output) {
- int penalty = 0;
- std::vector<std::string> lines = base::SplitString(
- output, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
- penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
- for (const auto& line : lines) {
- if (line.size() > kMaximumWidth)
- penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
- }
- return penalty;
-}
-
-bool Printer::ExceedsMaximumWidth(const std::string& output) {
- for (const auto& line : base::SplitString(output, "\n", base::KEEP_WHITESPACE,
- base::SPLIT_WANT_ALL)) {
- if (line.size() > kMaximumWidth)
- return true;
- }
- return false;
-}
-
-void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
- if (prec < outer_prec) {
- Print("(");
- *parenthesized = true;
- }
-}
-
-int Printer::Expr(const ParseNode* root,
- int outer_prec,
- const std::string& suffix) {
- std::string at_end = suffix;
- int penalty = 0;
- penalty_depth_++;
-
- if (root->comments()) {
- if (!root->comments()->before().empty()) {
- Trim();
- // If there's already other text on the line, start a new line.
- if (CurrentColumn() > 0)
- Print("\n");
- // We're printing a line comment, so we need to be at the current margin.
- PrintMargin();
- for (const auto& c : root->comments()->before()) {
- TrimAndPrintToken(c);
- Newline();
- }
- }
- }
-
- bool parenthesized = false;
-
- if (const AccessorNode* accessor = root->AsAccessor()) {
- AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
- Print(accessor->base().value());
- if (accessor->member()) {
- Print(".");
- Expr(accessor->member(), kPrecedenceLowest, std::string());
- } else {
- CHECK(accessor->index());
- Print("[");
- Expr(accessor->index(), kPrecedenceLowest, "]");
- }
- } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
- CHECK(precedence_.find(binop->op().value()) != precedence_.end());
- AnnotatePreferredMultilineAssignment(binop);
-
- SortIfSourcesOrDeps(binop);
-
- Precedence prec = precedence_[binop->op().value()];
-
- // Since binary operators format left-to-right, it is ok for the left side
- // use the same operator without parentheses, so the left uses prec. For the
- // same reason, the right side cannot reuse the same operator, or else "x +
- // (y + z)" would format as "x + y + z" which means "(x + y) + z". So, treat
- // the right expression as appearing one precedence level higher.
- // However, because the source parens are not in the parse tree, as a
- // special case for && and || we insert strictly-redundant-but-helpful-for-
- // human-readers parentheses.
- int prec_left = prec;
- int prec_right = prec + 1;
- if (binop->op().value() == "&&" && stack_.back().parent_is_boolean_or) {
- Print("(");
- parenthesized = true;
- } else {
- AddParen(prec_left, outer_prec, &parenthesized);
- }
-
- int start_line = CurrentLine();
- int start_column = CurrentColumn();
- bool is_assignment = binop->op().value() == "=" ||
- binop->op().value() == "+=" ||
- binop->op().value() == "-=";
-
- int indent_column = start_column;
- if (is_assignment) {
- // Default to a double-indent for wrapped assignments.
- indent_column = margin() + kIndentSize * 2;
-
- // A special case for the long lists and scope assignments that are
- // common in .gn files, don't indent them + 4, even though they're just
- // continuations when they're simple lists like "x = [ a, b, c, ... ]" or
- // scopes like "x = { a = 1 b = 2 }". Put back to "normal" indenting.
- const ListNode* right_as_list = binop->right()->AsList();
- if (right_as_list) {
- if (right_as_list->prefer_multiline() ||
- ListWillBeMultiline(right_as_list->contents(),
- right_as_list->End()))
- indent_column = start_column;
- } else {
- const BlockNode* right_as_block = binop->right()->AsBlock();
- if (right_as_block)
- indent_column = start_column;
- }
- }
- if (stack_.back().continuation_requires_indent)
- indent_column += kIndentSize * 2;
-
- stack_.push_back(IndentState(indent_column,
- stack_.back().continuation_requires_indent,
- binop->op().value() == "||"));
- Printer sub_left;
- InitializeSub(&sub_left);
- sub_left.Expr(binop->left(), prec_left,
- std::string(" ") + binop->op().value().as_string());
- bool left_is_multiline = CountLines(sub_left.String()) > 1;
- // Avoid walking the whole left redundantly times (see timing of Format.046)
- // so pull the output and comments from subprinter.
- Print(sub_left.String().substr(start_column));
- std::copy(sub_left.comments_.begin(), sub_left.comments_.end(),
- std::back_inserter(comments_));
-
- // Single line.
- Printer sub1;
- InitializeSub(&sub1);
- sub1.Print(" ");
- int penalty_current_line =
- sub1.Expr(binop->right(), prec_right, std::string());
- sub1.Print(suffix);
- penalty_current_line += AssessPenalty(sub1.String());
- if (!is_assignment && left_is_multiline) {
- // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
- // for trying to continue as if this were one line.
- penalty_current_line +=
- (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
- }
-
- // Break after operator.
- Printer sub2;
- InitializeSub(&sub2);
- sub2.Newline();
- int penalty_next_line =
- sub2.Expr(binop->right(), prec_right, std::string());
- sub2.Print(suffix);
- penalty_next_line += AssessPenalty(sub2.String());
-
- // Force a list on the RHS that would normally be a single line into
- // multiline.
- bool tried_rhs_multiline = false;
- Printer sub3;
- InitializeSub(&sub3);
- int penalty_multiline_rhs_list = std::numeric_limits<int>::max();
- const ListNode* rhs_list = binop->right()->AsList();
- if (is_assignment && rhs_list &&
- !ListWillBeMultiline(rhs_list->contents(), rhs_list->End())) {
- sub3.Print(" ");
- sub3.stack_.push_back(IndentState(start_column, false, false));
- sub3.Sequence(kSequenceStyleList, rhs_list->contents(), rhs_list->End(),
- true);
- sub3.stack_.pop_back();
- penalty_multiline_rhs_list = AssessPenalty(sub3.String());
- tried_rhs_multiline = true;
- }
-
- // If in all cases it was forced past 80col, then we don't break to avoid
- // breaking after '=' in the case of:
- // variable = "... very long string ..."
- // as breaking and indenting doesn't make things much more readable, even
- // though there's fewer characters past the maximum width.
- bool exceeds_maximum_all_ways =
- ExceedsMaximumWidth(sub1.String()) &&
- ExceedsMaximumWidth(sub2.String()) &&
- (!tried_rhs_multiline || ExceedsMaximumWidth(sub3.String()));
-
- if (penalty_current_line < penalty_next_line || exceeds_maximum_all_ways) {
- Print(" ");
- Expr(binop->right(), prec_right, std::string());
- } else if (tried_rhs_multiline &&
- penalty_multiline_rhs_list < penalty_next_line) {
- // Force a multiline list on the right.
- Print(" ");
- stack_.push_back(IndentState(start_column, false, false));
- Sequence(kSequenceStyleList, rhs_list->contents(), rhs_list->End(), true);
- stack_.pop_back();
- } else {
- // Otherwise, put first argument and op, and indent next.
- Newline();
- penalty += std::abs(CurrentColumn() - start_column) *
- kPenaltyHorizontalSeparation;
- Expr(binop->right(), prec_right, std::string());
- }
- stack_.pop_back();
- penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
- } else if (const BlockNode* block = root->AsBlock()) {
- Sequence(kSequenceStyleBracedBlock, block->statements(), block->End(),
- false);
- } else if (const ConditionNode* condition = root->AsConditionNode()) {
- Print("if (");
- Expr(condition->condition(), kPrecedenceLowest, ") {");
- Sequence(kSequenceStyleBracedBlockAlreadyOpen,
- condition->if_true()->statements(), condition->if_true()->End(),
- false);
- if (condition->if_false()) {
- Print(" else ");
- // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
- // ConditionNode::Execute.
- bool is_else_if = condition->if_false()->AsBlock() == nullptr;
- if (is_else_if) {
- Expr(condition->if_false(), kPrecedenceLowest, std::string());
- } else {
- Sequence(kSequenceStyleBracedBlock,
- condition->if_false()->AsBlock()->statements(),
- condition->if_false()->AsBlock()->End(), false);
- }
- }
- } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
- penalty += FunctionCall(func_call, at_end);
- at_end = "";
- } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
- Print(identifier->value().value());
- } else if (const ListNode* list = root->AsList()) {
- bool force_multiline =
- list->prefer_multiline() && !list->contents().empty();
- Sequence(kSequenceStyleList, list->contents(), list->End(),
- force_multiline);
- } else if (const LiteralNode* literal = root->AsLiteral()) {
- Print(literal->value().value());
- } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
- Print(unaryop->op().value());
- Expr(unaryop->operand(), kPrecedenceUnary, std::string());
- } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
- Print(block_comment->comment().value());
- } else if (const EndNode* end = root->AsEnd()) {
- Print(end->value().value());
- } else {
- CHECK(false) << "Unhandled case in Expr.";
- }
-
- if (parenthesized)
- Print(")");
-
- // Defer any end of line comment until we reach the newline.
- if (root->comments() && !root->comments()->suffix().empty()) {
- std::copy(root->comments()->suffix().begin(),
- root->comments()->suffix().end(), std::back_inserter(comments_));
- }
-
- Print(at_end);
-
- penalty_depth_--;
- return penalty;
-}
-
-template <class PARSENODE>
-void Printer::Sequence(SequenceStyle style,
- const std::vector<std::unique_ptr<PARSENODE>>& list,
- const ParseNode* end,
- bool force_multiline) {
- if (style == kSequenceStyleList) {
- Print("[");
- } else if (style == kSequenceStyleBracedBlock) {
- Print("{");
- } else if (style == kSequenceStyleBracedBlockAlreadyOpen) {
- style = kSequenceStyleBracedBlock;
- }
-
- if (style == kSequenceStyleBracedBlock) {
- force_multiline = true;
- SortImports(const_cast<std::vector<std::unique_ptr<PARSENODE>>&>(list));
- }
-
- force_multiline |= ListWillBeMultiline(list, end);
-
- if (list.size() == 0 && !force_multiline) {
- // No elements, and not forcing newlines, print nothing.
- } else if (list.size() == 1 && !force_multiline) {
- Print(" ");
- Expr(list[0].get(), kPrecedenceLowest, std::string());
- CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
- Print(" ");
- } else {
- stack_.push_back(IndentState(margin() + kIndentSize,
- style == kSequenceStyleList, false));
- size_t i = 0;
- for (const auto& x : list) {
- Newline();
- // If:
- // - we're going to output some comments, and;
- // - we haven't just started this multiline list, and;
- // - there isn't already a blank line here;
- // Then: insert one.
- if (i != 0 && x->comments() && !x->comments()->before().empty() &&
- !HaveBlankLine()) {
- Newline();
- }
- bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
- bool want_comma =
- body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
- Expr(x.get(), kPrecedenceLowest, want_comma ? "," : std::string());
- CHECK(!x->comments() || x->comments()->after().empty());
- if (body_of_list) {
- if (i < list.size() - 1 &&
- ShouldAddBlankLineInBetween(list[i].get(), list[i + 1].get()))
- Newline();
- }
- ++i;
- }
-
- // Trailing comments.
- if (end->comments() && !end->comments()->before().empty()) {
- if (list.size() >= 2)
- Newline();
- for (const auto& c : end->comments()->before()) {
- Newline();
- TrimAndPrintToken(c);
- }
- }
-
- stack_.pop_back();
- Newline();
-
- // Defer any end of line comment until we reach the newline.
- if (end->comments() && !end->comments()->suffix().empty()) {
- std::copy(end->comments()->suffix().begin(),
- end->comments()->suffix().end(), std::back_inserter(comments_));
- }
- }
-
- if (style == kSequenceStyleList)
- Print("]");
- else if (style == kSequenceStyleBracedBlock)
- Print("}");
-}
-
-int Printer::FunctionCall(const FunctionCallNode* func_call,
- const std::string& suffix) {
- int start_line = CurrentLine();
- int start_column = CurrentColumn();
- Print(func_call->function().value());
- Print("(");
-
- bool have_block = func_call->block() != nullptr;
- bool force_multiline = false;
-
- const auto& list = func_call->args()->contents();
- const ParseNode* end = func_call->args()->End();
-
- if (end->comments() && !end->comments()->before().empty())
- force_multiline = true;
-
- // If there's before line comments, make sure we have a place to put them.
- for (const auto& i : list) {
- if (i->comments() && !i->comments()->before().empty())
- force_multiline = true;
- }
-
- // Calculate the penalties for 3 possible layouts:
- // 1. all on same line;
- // 2. starting on same line, broken at each comma but paren aligned;
- // 3. broken to next line + 4, broken at each comma.
- std::string terminator = ")";
- if (have_block)
- terminator += " {";
- terminator += suffix;
-
- // Special case to make function calls of one arg taking a long list of
- // boolean operators not indent.
- bool continuation_requires_indent =
- list.size() != 1 || !list[0]->AsBinaryOp();
-
- // 1: Same line.
- Printer sub1;
- InitializeSub(&sub1);
- sub1.stack_.push_back(
- IndentState(CurrentColumn(), continuation_requires_indent, false));
- int penalty_one_line = 0;
- for (size_t i = 0; i < list.size(); ++i) {
- penalty_one_line += sub1.Expr(list[i].get(), kPrecedenceLowest,
- i < list.size() - 1 ? ", " : std::string());
- }
- sub1.Print(terminator);
- penalty_one_line += AssessPenalty(sub1.String());
- // This extra penalty prevents a short second argument from being squeezed in
- // after a first argument that went multiline (and instead preferring a
- // variant below).
- penalty_one_line +=
- (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
-
- // 2: Starting on same line, broken at commas.
- Printer sub2;
- InitializeSub(&sub2);
- sub2.stack_.push_back(
- IndentState(CurrentColumn(), continuation_requires_indent, false));
- int penalty_multiline_start_same_line = 0;
- for (size_t i = 0; i < list.size(); ++i) {
- penalty_multiline_start_same_line +=
- sub2.Expr(list[i].get(), kPrecedenceLowest,
- i < list.size() - 1 ? "," : std::string());
- if (i < list.size() - 1) {
- sub2.Newline();
- }
- }
- sub2.Print(terminator);
- penalty_multiline_start_same_line += AssessPenalty(sub2.String());
-
- // 3: Starting on next line, broken at commas.
- Printer sub3;
- InitializeSub(&sub3);
- sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2,
- continuation_requires_indent, false));
- sub3.Newline();
- int penalty_multiline_start_next_line = 0;
- for (size_t i = 0; i < list.size(); ++i) {
- if (i == 0) {
- penalty_multiline_start_next_line +=
- std::abs(sub3.CurrentColumn() - start_column) *
- kPenaltyHorizontalSeparation;
- }
- penalty_multiline_start_next_line +=
- sub3.Expr(list[i].get(), kPrecedenceLowest,
- i < list.size() - 1 ? "," : std::string());
- if (i < list.size() - 1) {
- sub3.Newline();
- }
- }
- sub3.Print(terminator);
- penalty_multiline_start_next_line += AssessPenalty(sub3.String());
-
- int penalty = penalty_multiline_start_next_line;
- bool fits_on_current_line = false;
- if (penalty_one_line < penalty_multiline_start_next_line ||
- penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
- fits_on_current_line = true;
- penalty = penalty_one_line;
- if (penalty_multiline_start_same_line < penalty_one_line) {
- penalty = penalty_multiline_start_same_line;
- force_multiline = true;
- }
- } else {
- force_multiline = true;
- }
-
- if (list.size() == 0 && !force_multiline) {
- // No elements, and not forcing newlines, print nothing.
- } else {
- if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
- stack_.push_back(IndentState(margin() + kIndentSize * 2,
- continuation_requires_indent, false));
- Newline();
- } else {
- stack_.push_back(
- IndentState(CurrentColumn(), continuation_requires_indent, false));
- }
-
- for (size_t i = 0; i < list.size(); ++i) {
- const auto& x = list[i];
- if (i > 0) {
- if (fits_on_current_line && !force_multiline)
- Print(" ");
- else
- Newline();
- }
- bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
- Expr(x.get(), kPrecedenceLowest, want_comma ? "," : std::string());
- CHECK(!x->comments() || x->comments()->after().empty());
- if (i < list.size() - 1) {
- if (!want_comma)
- Newline();
- }
- }
-
- // Trailing comments.
- if (end->comments() && !end->comments()->before().empty()) {
- if (!list.empty())
- Newline();
- for (const auto& c : end->comments()->before()) {
- Newline();
- TrimAndPrintToken(c);
- }
- Newline();
- }
- stack_.pop_back();
- }
-
- // Defer any end of line comment until we reach the newline.
- if (end->comments() && !end->comments()->suffix().empty()) {
- std::copy(end->comments()->suffix().begin(),
- end->comments()->suffix().end(), std::back_inserter(comments_));
- }
-
- Print(")");
- Print(suffix);
-
- if (have_block) {
- Print(" ");
- Sequence(kSequenceStyleBracedBlock, func_call->block()->statements(),
- func_call->block()->End(), false);
- }
- return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
-}
-
-void Printer::InitializeSub(Printer* sub) {
- sub->stack_ = stack_;
- sub->comments_ = comments_;
- sub->penalty_depth_ = penalty_depth_;
- sub->Print(std::string(CurrentColumn(), 'x'));
-}
-
-template <class PARSENODE>
-bool Printer::ListWillBeMultiline(
- const std::vector<std::unique_ptr<PARSENODE>>& list,
- const ParseNode* end) {
- if (list.size() > 1)
- return true;
-
- if (end && end->comments() && !end->comments()->before().empty())
- return true;
-
- // If there's before line comments, make sure we have a place to put them.
- for (const auto& i : list) {
- if (i->comments() && !i->comments()->before().empty())
- return true;
- }
-
- // When a scope is used as a list entry, it's too complicated to go one a
- // single line (the block will always be formatted multiline itself).
- if (list.size() >= 1 && list[0]->AsBlock())
- return true;
-
- return false;
-}
-
-void DoFormat(const ParseNode* root, TreeDumpMode dump_tree,
- std::string* output) {
- if (dump_tree == TreeDumpMode::kPlainText) {
- std::ostringstream os;
- RenderToText(root->GetJSONNode(), 0, os);
- fprintf(stderr, "%s", os.str().c_str());
- } else if (dump_tree == TreeDumpMode::kJSON) {
- std::string os;
- base::JSONWriter::WriteWithOptions(root->GetJSONNode(),
- base::JSONWriter::OPTIONS_PRETTY_PRINT, &os);
- fprintf(stderr, "%s", os.c_str());
- }
-
- Printer pr;
- pr.Block(root);
- *output = pr.String();
-}
-
-std::string ReadStdin() {
- static const int kBufferSize = 256;
- char buffer[kBufferSize];
- std::string result;
- while (true) {
- char* input = nullptr;
- input = fgets(buffer, kBufferSize, stdin);
- if (input == nullptr && feof(stdin))
- return result;
- int length = static_cast<int>(strlen(buffer));
- if (length == 0)
- return result;
- else
- result += std::string(buffer, length);
- }
-}
-
-} // namespace
-
-bool FormatFileToString(Setup* setup,
- const SourceFile& file,
- TreeDumpMode dump_tree,
- std::string* output) {
- Err err;
- const ParseNode* parse_node =
- setup->scheduler().input_file_manager()->SyncLoadFile(
- LocationRange(), &setup->build_settings(), file, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
- DoFormat(parse_node, dump_tree, output);
- return true;
-}
-
-bool FormatStringToString(const std::string& input,
- TreeDumpMode dump_tree,
- std::string* output) {
- SourceFile source_file;
- InputFile file(source_file);
- file.SetContents(input);
- Err err;
- // Tokenize.
- std::vector<Token> tokens = Tokenizer::Tokenize(&file, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- // Parse.
- std::unique_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- DoFormat(parse_node.get(), dump_tree, output);
- return true;
-}
-
-int RunFormat(const std::vector<std::string>& args) {
- bool dry_run =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
- TreeDumpMode dump_tree = TreeDumpMode::kInactive;
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree)) {
- std::string tree_type = base::CommandLine::ForCurrentProcess()->
- GetSwitchValueASCII(kSwitchDumpTree);
- if (tree_type == kSwitchDumpTreeJSON) {
- dump_tree = TreeDumpMode::kJSON;
- } else if (tree_type.empty() || tree_type == kSwitchDumpTreeText) {
- dump_tree = TreeDumpMode::kPlainText;
- } else {
- Err(Location(),
- tree_type + " is an invalid value for --dump-tree. Specify "
- "\"" + kSwitchDumpTreeText + "\" or \"" + kSwitchDumpTreeJSON +
- "\".\n")
- .PrintToStdout();
- return 1;
- }
- }
- bool from_stdin =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
-
- if (dry_run) {
- // --dry-run only works with an actual file to compare to.
- from_stdin = false;
- }
-
- bool quiet =
- base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet);
-
- if (from_stdin) {
- if (args.size() != 0) {
- Err(Location(), "Expecting no arguments when reading from stdin.\n")
- .PrintToStdout();
- return 1;
- }
- std::string input = ReadStdin();
- std::string output;
- if (!FormatStringToString(input, dump_tree, &output))
- return 1;
- printf("%s", output.c_str());
- return 0;
- }
-
- if (args.size() == 0) {
- Err(Location(), "Expecting one or more arguments, see `gn help format`.\n")
- .PrintToStdout();
- return 1;
- }
-
- Setup setup;
- SourceDir source_dir =
- SourceDirForCurrentDirectory(setup.build_settings().root_path());
-
- // TODO(scottmg): Eventually, this list of files should be processed in
- // parallel.
- for (const auto& arg : args) {
- Err err;
- SourceFile file =
- source_dir.ResolveRelativeFile(Value(nullptr, arg), &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return 1;
- }
-
- std::string output_string;
- if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
- if (dump_tree == TreeDumpMode::kInactive) {
- // Update the file in-place.
- base::FilePath to_write = setup.build_settings().GetFullPath(file);
- std::string original_contents;
- if (!base::ReadFileToString(to_write, &original_contents)) {
- Err(Location(), std::string("Couldn't read \"") +
- FilePathToUTF8(to_write) +
- std::string("\" for comparison."))
- .PrintToStdout();
- return 1;
- }
- if (dry_run)
- return original_contents == output_string ? 0 : 2;
- if (original_contents != output_string) {
- if (base::WriteFile(to_write, output_string.data(),
- static_cast<int>(output_string.size())) == -1) {
- Err(Location(),
- std::string("Failed to write formatted output back to \"") +
- FilePathToUTF8(to_write) + std::string("\"."))
- .PrintToStdout();
- return 1;
- }
- if (!quiet) {
- printf("Wrote formatted to '%s'.\n",
- FilePathToUTF8(to_write).c_str());
- }
- }
- }
- }
- }
-
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_format.h b/gn/tools/gn/command_format.h
deleted file mode 100644
index 785b0706be4..00000000000
--- a/gn/tools/gn/command_format.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_COMAND_FORMAT_H_
-#define TOOLS_GN_COMAND_FORMAT_H_
-
-#include <string>
-
-class Setup;
-class SourceFile;
-
-namespace commands {
-
-enum class TreeDumpMode {
- // Normal operation mode. Format the input file.
- kInactive,
-
- // Output the token tree with indented plain text. For debugging.
- kPlainText,
-
- // Output the token tree in JSON format. Used for exporting a tree to another
- // program.
- kJSON
-};
-
-bool FormatFileToString(Setup* setup,
- const SourceFile& file,
- TreeDumpMode dump_tree,
- std::string* output);
-
-bool FormatStringToString(const std::string& input,
- TreeDumpMode dump_tree,
- std::string* output);
-
-} // namespace commands
-
-#endif // TOOLS_GN_COMAND_FORMAT_H_
diff --git a/gn/tools/gn/command_format_unittest.cc b/gn/tools/gn/command_format_unittest.cc
deleted file mode 100644
index b00f6acb989..00000000000
--- a/gn/tools/gn/command_format_unittest.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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 "tools/gn/command_format.h"
-
-#include "base/files/file_util.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "util/exe_path.h"
-#include "util/test/test.h"
-
-using FormatTest = TestWithScheduler;
-
-#define FORMAT_TEST(n) \
- TEST_F(FormatTest, n) { \
- ::Setup setup; \
- std::string out; \
- std::string expected; \
- base::FilePath src_dir = \
- GetExePath().DirName().Append(FILE_PATH_LITERAL("..")); \
- base::SetCurrentDirectory(src_dir); \
- EXPECT_TRUE(commands::FormatFileToString( \
- &setup, SourceFile("//tools/gn/format_test_data/" #n ".gn"), \
- commands::TreeDumpMode::kInactive, &out)); \
- ASSERT_TRUE(base::ReadFileToString( \
- base::FilePath(FILE_PATH_LITERAL("tools/gn/format_test_data/") \
- FILE_PATH_LITERAL(#n) \
- FILE_PATH_LITERAL(".golden")), \
- &expected)); \
- EXPECT_EQ(expected, out); \
- /* Make sure formatting the output doesn't cause further changes. */ \
- std::string out_again; \
- EXPECT_TRUE(commands::FormatStringToString(out, \
- commands::TreeDumpMode::kInactive, &out_again)); \
- ASSERT_EQ(out, out_again); \
- }
-
-// These are expanded out this way rather than a runtime loop so that
-// --gtest_filter works as expected for individual test running.
-FORMAT_TEST(001)
-FORMAT_TEST(002)
-FORMAT_TEST(003)
-FORMAT_TEST(004)
-FORMAT_TEST(005)
-FORMAT_TEST(006)
-FORMAT_TEST(007)
-FORMAT_TEST(008)
-FORMAT_TEST(009)
-FORMAT_TEST(010)
-FORMAT_TEST(011)
-FORMAT_TEST(012)
-FORMAT_TEST(013)
-FORMAT_TEST(014)
-FORMAT_TEST(015)
-FORMAT_TEST(016)
-FORMAT_TEST(017)
-FORMAT_TEST(018)
-FORMAT_TEST(019)
-FORMAT_TEST(020)
-FORMAT_TEST(021)
-FORMAT_TEST(022)
-FORMAT_TEST(023)
-FORMAT_TEST(024)
-FORMAT_TEST(025)
-FORMAT_TEST(026)
-FORMAT_TEST(027)
-FORMAT_TEST(028)
-FORMAT_TEST(029)
-FORMAT_TEST(030)
-FORMAT_TEST(031)
-FORMAT_TEST(032)
-FORMAT_TEST(033)
-// TODO(scottmg): args+rebase_path unnecessarily split: FORMAT_TEST(034)
-FORMAT_TEST(035)
-FORMAT_TEST(036)
-FORMAT_TEST(037)
-FORMAT_TEST(038)
-FORMAT_TEST(039)
-// TODO(scottmg): Bad break, exceeding 80 col: FORMAT_TEST(040)
-FORMAT_TEST(041)
-FORMAT_TEST(042)
-FORMAT_TEST(043)
-// TODO(scottmg): Dewrapped caused exceeding 80 col: FORMAT_TEST(044)
-FORMAT_TEST(045)
-FORMAT_TEST(046)
-FORMAT_TEST(047)
-FORMAT_TEST(048)
-// TODO(scottmg): Eval is broken (!) and comment output might have extra ,
-// FORMAT_TEST(049)
-FORMAT_TEST(050)
-FORMAT_TEST(051)
-FORMAT_TEST(052)
-FORMAT_TEST(053)
-FORMAT_TEST(054)
-FORMAT_TEST(055)
-FORMAT_TEST(056)
-FORMAT_TEST(057)
-FORMAT_TEST(058)
-FORMAT_TEST(059)
-FORMAT_TEST(060)
-FORMAT_TEST(061)
-FORMAT_TEST(062)
-FORMAT_TEST(063)
-FORMAT_TEST(064)
-FORMAT_TEST(065)
-FORMAT_TEST(066)
-FORMAT_TEST(067)
-FORMAT_TEST(068)
-FORMAT_TEST(069)
-FORMAT_TEST(070)
-FORMAT_TEST(071)
-FORMAT_TEST(072)
-FORMAT_TEST(073)
-FORMAT_TEST(074)
-FORMAT_TEST(075)
diff --git a/gn/tools/gn/command_gen.cc b/gn/tools/gn/command_gen.cc
deleted file mode 100644
index 200aad80b29..00000000000
--- a/gn/tools/gn/command_gen.cc
+++ /dev/null
@@ -1,531 +0,0 @@
-// 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 <mutex>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/timer/elapsed_timer.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/compile_commands_writer.h"
-#include "tools/gn/eclipse_writer.h"
-#include "tools/gn/json_project_writer.h"
-#include "tools/gn/ninja_target_writer.h"
-#include "tools/gn/ninja_writer.h"
-#include "tools/gn/qt_creator_writer.h"
-#include "tools/gn/runtime_deps.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/visual_studio_writer.h"
-#include "tools/gn/xcode_writer.h"
-
-namespace commands {
-
-namespace {
-
-const char kSwitchCheck[] = "check";
-const char kSwitchFilters[] = "filters";
-const char kSwitchIde[] = "ide";
-const char kSwitchIdeValueEclipse[] = "eclipse";
-const char kSwitchIdeValueQtCreator[] = "qtcreator";
-const char kSwitchIdeValueVs[] = "vs";
-const char kSwitchIdeValueVs2013[] = "vs2013";
-const char kSwitchIdeValueVs2015[] = "vs2015";
-const char kSwitchIdeValueVs2017[] = "vs2017";
-const char kSwitchIdeValueVs2019[] = "vs2019";
-const char kSwitchIdeValueWinSdk[] = "winsdk";
-const char kSwitchIdeValueXcode[] = "xcode";
-const char kSwitchIdeValueJson[] = "json";
-const char kSwitchNinjaExtraArgs[] = "ninja-extra-args";
-const char kSwitchNoDeps[] = "no-deps";
-const char kSwitchRootTarget[] = "root-target";
-const char kSwitchSln[] = "sln";
-const char kSwitchWorkspace[] = "workspace";
-const char kSwitchJsonFileName[] = "json-file-name";
-const char kSwitchJsonIdeScript[] = "json-ide-script";
-const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
-const char kSwitchExportCompileCommands[] = "export-compile-commands";
-
-// Collects Ninja rules for each toolchain. The lock protectes the rules.
-struct TargetWriteInfo {
- std::mutex lock;
- NinjaWriter::PerToolchainRules rules;
-};
-
-// Called on worker thread to write the ninja file.
-void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
- std::string rule = NinjaTargetWriter::RunAndWriteFile(target);
- DCHECK(!rule.empty());
-
- {
- std::lock_guard<std::mutex> lock(write_info->lock);
- write_info->rules[target->toolchain()].emplace_back(target,
- std::move(rule));
- }
-}
-
-// Called on the main thread.
-void ItemResolvedAndGeneratedCallback(TargetWriteInfo* write_info,
- const BuilderRecord* record) {
- const Item* item = record->item();
- const Target* target = item->AsTarget();
- if (target) {
- g_scheduler->ScheduleWork(
- base::Bind(&BackgroundDoWrite, write_info, target));
- }
-}
-
-// Returns a pointer to the target with the given file as an output, or null
-// if no targets generate the file. This is brute force since this is an
-// error condition and performance shouldn't matter.
-const Target* FindTargetThatGeneratesFile(const Builder& builder,
- const SourceFile& file) {
- std::vector<const Target*> targets = builder.GetAllResolvedTargets();
- if (targets.empty())
- return nullptr;
-
- OutputFile output_file(targets[0]->settings()->build_settings(), file);
- for (const Target* target : targets) {
- for (const auto& cur_output : target->computed_outputs()) {
- if (cur_output == output_file)
- return target;
- }
- }
- return nullptr;
-}
-
-// Prints an error that the given file was present as a source or input in
-// the given target(s) but was not generated by any of its dependencies.
-void PrintInvalidGeneratedInput(const Builder& builder,
- const SourceFile& file,
- const std::vector<const Target*>& targets) {
- std::string err;
-
- // Only show the toolchain labels (which can be confusing) if something
- // isn't the default.
- bool show_toolchains = false;
- const Label& default_toolchain =
- targets[0]->settings()->default_toolchain_label();
- for (const Target* target : targets) {
- if (target->settings()->toolchain_label() != default_toolchain) {
- show_toolchains = true;
- break;
- }
- }
-
- const Target* generator = FindTargetThatGeneratesFile(builder, file);
- if (generator &&
- generator->settings()->toolchain_label() != default_toolchain)
- show_toolchains = true;
-
- const std::string target_str = targets.size() > 1 ? "targets" : "target";
- err += "The file:\n";
- err += " " + file.value() + "\n";
- err += "is listed as an input or source for the " + target_str + ":\n";
- for (const Target* target : targets)
- err += " " + target->label().GetUserVisibleName(show_toolchains) + "\n";
-
- if (generator) {
- err += "but this file was not generated by any dependencies of the " +
- target_str + ". The target\nthat generates the file is:\n ";
- err += generator->label().GetUserVisibleName(show_toolchains);
- } else {
- err += "but no targets in the build generate that file.";
- }
-
- Err(Location(), "Input to " + target_str + " not generated by a dependency.",
- err)
- .PrintToStdout();
-}
-
-bool CheckForInvalidGeneratedInputs(Setup* setup) {
- std::multimap<SourceFile, const Target*> unknown_inputs =
- g_scheduler->GetUnknownGeneratedInputs();
- if (unknown_inputs.empty())
- return true; // No bad files.
-
- int errors_found = 0;
- auto cur = unknown_inputs.begin();
- while (cur != unknown_inputs.end()) {
- errors_found++;
- auto end_of_range = unknown_inputs.upper_bound(cur->first);
-
- // Package the values more conveniently for printing.
- SourceFile bad_input = cur->first;
- std::vector<const Target*> targets;
- while (cur != end_of_range)
- targets.push_back((cur++)->second);
-
- PrintInvalidGeneratedInput(setup->builder(), bad_input, targets);
- OutputString("\n");
- }
-
- OutputString(
- "If you have generated inputs, there needs to be a dependency path "
- "between the\ntwo targets in addition to just listing the files. For "
- "indirect dependencies,\nthe intermediate ones must be public_deps. "
- "data_deps don't count since they're\nonly runtime dependencies. If "
- "you think a dependency chain exists, it might be\nbecause the chain "
- "is private. Try \"gn path\" to analyze.\n");
-
- if (errors_found > 1) {
- OutputString(base::StringPrintf("\n%d generated input errors found.\n",
- errors_found),
- DECORATION_YELLOW);
- }
- return false;
-}
-
-bool RunIdeWriter(const std::string& ide,
- const BuildSettings* build_settings,
- const Builder& builder,
- Err* err) {
- const base::CommandLine* command_line =
- base::CommandLine::ForCurrentProcess();
- bool quiet = command_line->HasSwitch(switches::kQuiet);
- base::ElapsedTimer timer;
-
- if (ide == kSwitchIdeValueEclipse) {
- bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err);
- if (res && !quiet) {
- OutputString("Generating Eclipse settings took " +
- base::Int64ToString(timer.Elapsed().InMilliseconds()) +
- "ms\n");
- }
- return res;
- } else if (ide == kSwitchIdeValueVs || ide == kSwitchIdeValueVs2013 ||
- ide == kSwitchIdeValueVs2015 || ide == kSwitchIdeValueVs2017 ||
- ide == kSwitchIdeValueVs2019) {
- VisualStudioWriter::Version version = VisualStudioWriter::Version::Vs2017;
- if (ide == kSwitchIdeValueVs2013)
- version = VisualStudioWriter::Version::Vs2013;
- else if (ide == kSwitchIdeValueVs2015)
- version = VisualStudioWriter::Version::Vs2015;
- else if (ide == kSwitchIdeValueVs2019)
- version = VisualStudioWriter::Version::Vs2019;
-
- std::string sln_name;
- if (command_line->HasSwitch(kSwitchSln))
- sln_name = command_line->GetSwitchValueASCII(kSwitchSln);
- std::string filters;
- if (command_line->HasSwitch(kSwitchFilters))
- filters = command_line->GetSwitchValueASCII(kSwitchFilters);
- std::string win_kit;
- if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
- win_kit = command_line->GetSwitchValueASCII(kSwitchIdeValueWinSdk);
- std::string ninja_extra_args;
- if (command_line->HasSwitch(kSwitchNinjaExtraArgs))
- ninja_extra_args =
- command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs);
- bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
- bool res = VisualStudioWriter::RunAndWriteFiles(
- build_settings, builder, version, sln_name, filters, win_kit,
- ninja_extra_args, no_deps, err);
- if (res && !quiet) {
- OutputString("Generating Visual Studio projects took " +
- base::Int64ToString(timer.Elapsed().InMilliseconds()) +
- "ms\n");
- }
- return res;
- } else if (ide == kSwitchIdeValueXcode) {
- bool res = XcodeWriter::RunAndWriteFiles(
- command_line->GetSwitchValueASCII(kSwitchWorkspace),
- command_line->GetSwitchValueASCII(kSwitchRootTarget),
- command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs),
- command_line->GetSwitchValueASCII(kSwitchFilters), build_settings,
- builder, err);
- if (res && !quiet) {
- OutputString("Generating Xcode projects took " +
- base::Int64ToString(timer.Elapsed().InMilliseconds()) +
- "ms\n");
- }
- return res;
- } else if (ide == kSwitchIdeValueQtCreator) {
- std::string root_target;
- if (command_line->HasSwitch(kSwitchRootTarget))
- root_target = command_line->GetSwitchValueASCII(kSwitchRootTarget);
- bool res = QtCreatorWriter::RunAndWriteFile(build_settings, builder, err,
- root_target);
- if (res && !quiet) {
- OutputString("Generating QtCreator projects took " +
- base::Int64ToString(timer.Elapsed().InMilliseconds()) +
- "ms\n");
- }
- return res;
- } else if (ide == kSwitchIdeValueJson) {
- std::string file_name =
- command_line->GetSwitchValueASCII(kSwitchJsonFileName);
- if (file_name.empty())
- file_name = "project.json";
- std::string exec_script =
- command_line->GetSwitchValueASCII(kSwitchJsonIdeScript);
- std::string exec_script_extra_args =
- command_line->GetSwitchValueASCII(kSwitchJsonIdeScriptArgs);
- std::string filters = command_line->GetSwitchValueASCII(kSwitchFilters);
-
- bool res = JSONProjectWriter::RunAndWriteFiles(
- build_settings, builder, file_name, exec_script, exec_script_extra_args,
- filters, quiet, err);
- if (res && !quiet) {
- OutputString("Generating JSON projects took " +
- base::Int64ToString(timer.Elapsed().InMilliseconds()) +
- "ms\n");
- }
- return res;
- }
-
- *err = Err(Location(), "Unknown IDE: " + ide);
- return false;
-}
-
-bool RunCompileCommandsWriter(const BuildSettings* build_settings,
- const Builder& builder,
- Err* err) {
- const base::CommandLine* command_line =
- base::CommandLine::ForCurrentProcess();
- bool quiet = command_line->HasSwitch(switches::kQuiet);
- base::ElapsedTimer timer;
-
- std::string file_name = "compile_commands.json";
- std::string target_filters =
- command_line->GetSwitchValueASCII(kSwitchExportCompileCommands);
-
- bool res = CompileCommandsWriter::RunAndWriteFiles(
- build_settings, builder, file_name, target_filters, quiet, err);
- if (res && !quiet) {
- OutputString("Generating compile_commands took " +
- base::Int64ToString(timer.Elapsed().InMilliseconds()) +
- "ms\n");
- }
- return res;
-}
-
-} // namespace
-
-const char kGen[] = "gen";
-const char kGen_HelpShort[] = "gen: Generate ninja files.";
-const char kGen_Help[] =
- R"(gn gen [--check] [<ide options>] <out_dir>
-
- Generates ninja files from the current tree and puts them in the given output
- directory.
-
- The output directory can be a source-repo-absolute path name such as:
- //out/foo
- Or it can be a directory relative to the current directory such as:
- out/foo
-
- "gn gen --check" is the same as running "gn check". See "gn help check"
- for documentation on that mode.
-
- See "gn help switches" for the common command-line switches.
-
-IDE options
-
- GN optionally generates files for IDE. Possibilities for <ide options>
-
- --ide=<ide_name>
- Generate files for an IDE. Currently supported values:
- "eclipse" - Eclipse CDT settings file.
- "vs" - Visual Studio project/solution files.
- (default Visual Studio version: 2017)
- "vs2013" - Visual Studio 2013 project/solution files.
- "vs2015" - Visual Studio 2015 project/solution files.
- "vs2017" - Visual Studio 2017 project/solution files.
- "vs2019" - Visual Studio 2019 project/solution files.
- "xcode" - Xcode workspace/solution files.
- "qtcreator" - QtCreator project files.
- "json" - JSON file containing target information
-
- --filters=<path_prefixes>
- Semicolon-separated list of label patterns used to limit the set of
- generated projects (see "gn help label_pattern"). Only matching targets
- and their dependencies will be included in the solution. Only used for
- Visual Studio, Xcode and JSON.
-
-Visual Studio Flags
-
- --sln=<file_name>
- Override default sln file name ("all"). Solution file is written to the
- root build directory.
-
- --no-deps
- Don't include targets dependencies to the solution. Changes the way how
- --filters option works. Only directly matching targets are included.
-
- --winsdk=<sdk_version>
- Use the specified Windows 10 SDK version to generate project files.
- As an example, "10.0.15063.0" can be specified to use Creators Update SDK
- instead of the default one.
-
- --ninja-extra-args=<string>
- This string is passed without any quoting to the ninja invocation
- command-line. Can be used to configure ninja flags, like "-j".
-
-Xcode Flags
-
- --workspace=<file_name>
- Override defaut workspace file name ("all"). The workspace file is
- written to the root build directory.
-
- --ninja-extra-args=<string>
- This string is passed without any quoting to the ninja invocation
- command-line. Can be used to configure ninja flags, like "-j".
-
- --root-target=<target_name>
- Name of the target corresponding to "All" target in Xcode. If unset,
- "All" invokes ninja without any target and builds everything.
-
-QtCreator Flags
-
- --root-target=<target_name>
- Name of the root target for which the QtCreator project will be generated
- to contain files of it and its dependencies. If unset, the whole build
- graph will be emitted.
-
-
-Eclipse IDE Support
-
- GN DOES NOT generate Eclipse CDT projects. Instead, it generates a settings
- file which can be imported into an Eclipse CDT project. The XML file contains
- a list of include paths and defines. Because GN does not generate a full
- .cproject definition, it is not possible to properly define includes/defines
- for each file individually. Instead, one set of includes/defines is generated
- for the entire project. This works fairly well but may still result in a few
- indexer issues here and there.
-
-Generic JSON Output
-
- Dumps target information to a JSON file and optionally invokes a
- python script on the generated file. See the comments at the beginning
- of json_project_writer.cc and desc_builder.cc for an overview of the JSON
- file format.
-
- --json-file-name=<json_file_name>
- Overrides default file name (project.json) of generated JSON file.
-
- --json-ide-script=<path_to_python_script>
- Executes python script after the JSON file is generated. Path can be
- project absolute (//), system absolute (/) or relative, in which case the
- output directory will be base. Path to generated JSON file will be first
- argument when invoking script.
-
- --json-ide-script-args=<argument>
- Optional second argument that will passed to executed script.
-
-Compilation Database
-
- --export-compile-commands[=<target_name1,target_name2...>]
- Produces a compile_commands.json file in the root of the build directory
- containing an array of “command objects”, where each command object
- specifies one way a translation unit is compiled in the project. If a list
- of target_name is supplied, only targets that are reachable from the list
- of target_name will be used for “command objects” generation, otherwise
- all available targets will be used. This is used for various Clang-based
- tooling, allowing for the replay of individual compilations independent
- of the build system.
-)";
-
-int RunGen(const std::vector<std::string>& args) {
- base::ElapsedTimer timer;
-
- if (args.size() != 1) {
- Err(Location(), "Need exactly one build directory to generate.",
- "I expected something more like \"gn gen out/foo\"\n"
- "You can also see \"gn help gen\".")
- .PrintToStdout();
- return 1;
- }
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup();
- // Generate an empty args.gn file if it does not exists
- if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kArgs)) {
- setup->set_gen_empty_args(true);
- }
- if (!setup->DoSetup(args[0], true))
- return 1;
-
- const base::CommandLine* command_line =
- base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(kSwitchCheck))
- setup->set_check_public_headers(true);
-
- // Cause the load to also generate the ninja files for each target.
- TargetWriteInfo write_info;
- setup->builder().set_resolved_and_generated_callback(
- base::Bind(&ItemResolvedAndGeneratedCallback, &write_info));
-
- // Do the actual load. This will also write out the target ninja files.
- if (!setup->Run())
- return 1;
-
- // Sort the targets in each toolchain according to their label. This makes
- // the ninja files have deterministic content.
- for (auto& cur_toolchain : write_info.rules) {
- std::sort(cur_toolchain.second.begin(), cur_toolchain.second.end(),
- [](const NinjaWriter::TargetRulePair& a,
- const NinjaWriter::TargetRulePair& b) {
- return a.first->label() < b.first->label();
- });
- }
-
- Err err;
- // Write the root ninja files.
- if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(), setup->builder(),
- write_info.rules, &err)) {
- err.PrintToStdout();
- return 1;
- }
-
- if (!WriteRuntimeDepsFilesIfNecessary(setup->builder(), &err)) {
- err.PrintToStdout();
- return 1;
- }
-
- if (!CheckForInvalidGeneratedInputs(setup))
- return 1;
-
- if (command_line->HasSwitch(kSwitchIde) &&
- !RunIdeWriter(command_line->GetSwitchValueASCII(kSwitchIde),
- &setup->build_settings(), setup->builder(), &err)) {
- err.PrintToStdout();
- return 1;
- }
-
- if (command_line->HasSwitch(kSwitchExportCompileCommands) &&
- !RunCompileCommandsWriter(&setup->build_settings(), setup->builder(),
- &err)) {
- err.PrintToStdout();
- return 1;
- }
-
- TickDelta elapsed_time = timer.Elapsed();
-
- if (!command_line->HasSwitch(switches::kQuiet)) {
- OutputString("Done. ", DECORATION_GREEN);
-
- size_t targets_collected = 0;
- for (const auto& rules : write_info.rules)
- targets_collected += rules.second.size();
-
- std::string stats =
- "Made " + base::NumberToString(targets_collected) + " targets from " +
- base::IntToString(
- setup->scheduler().input_file_manager()->GetInputFileCount()) +
- " files in " + base::Int64ToString(elapsed_time.InMilliseconds()) +
- "ms\n";
- OutputString(stats);
- }
-
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_help.cc b/gn/tools/gn/command_help.cc
deleted file mode 100644
index 7145da408c9..00000000000
--- a/gn/tools/gn/command_help.cc
+++ /dev/null
@@ -1,375 +0,0 @@
-// 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 <algorithm>
-#include <iostream>
-
-#include "base/command_line.h"
-#include "tools/gn/args.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/err.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/input_conversion.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/metadata.h"
-#include "tools/gn/ninja_build_writer.h"
-#include "tools/gn/output_conversion.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/runtime_deps.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/variables.h"
-
-namespace commands {
-
-namespace {
-
-// Some names exist in multiple sections, these prefixes are used for the
-// internal links to disambiguate when writing markdown.
-const char kCommandLinkPrefix[] = "cmd_";
-const char kFunctionLinkPrefix[] = "func_";
-const char kVariableLinkPrefix[] = "var_";
-
-void PrintToplevelHelp() {
- PrintSectionHelp("Commands", "<command>", "commands");
- for (const auto& cmd : commands::GetCommands())
- PrintShortHelp(cmd.second.help_short, kCommandLinkPrefix + cmd.first);
-
- // Target declarations.
- PrintSectionHelp("Target declarations", "<function>", "targets");
- for (const auto& func : functions::GetFunctions()) {
- if (func.second.is_target)
- PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
- }
-
- // Functions.
- PrintSectionHelp("Buildfile functions", "<function>", "functions");
- for (const auto& func : functions::GetFunctions()) {
- if (!func.second.is_target)
- PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
- }
-
- // Built-in variables.
- PrintSectionHelp("Built-in predefined variables", "<variable>",
- "predefined_variables");
- for (const auto& builtin : variables::GetBuiltinVariables()) {
- PrintShortHelp(builtin.second.help_short,
- kVariableLinkPrefix + builtin.first);
- }
-
- // Target variables.
- PrintSectionHelp("Variables you set in targets", "<variable>",
- "target_variables");
- for (const auto& target : variables::GetTargetVariables()) {
- PrintShortHelp(target.second.help_short,
- kVariableLinkPrefix + target.first);
- }
-
- PrintSectionHelp("Other help topics", "", "other");
- PrintShortHelp("all: Print all the help at once");
- PrintShortHelp("buildargs: How build arguments work.", "buildargs");
- PrintShortHelp("dotfile: Info about the toplevel .gn file.", "dotfile");
- PrintShortHelp("execution: Build graph and execution overview.", "execution");
- PrintShortHelp("grammar: Language and grammar for GN build files.",
- "grammar");
- PrintShortHelp(
- "input_conversion: Processing input from exec_script and read_file.",
- "io_conversion");
- PrintShortHelp("label_pattern: Matching more than one label.",
- "label_pattern");
- PrintShortHelp("labels: About labels.", "labels");
- PrintShortHelp("metadata_collection: About metadata and its collection.",
- "metadata_collection");
- PrintShortHelp("ninja_rules: How Ninja build rules are named.",
- "ninja_rules");
- PrintShortHelp("nogncheck: Annotating includes for checking.", "nogncheck");
- PrintShortHelp(
- "output_conversion: Specifies how to transform a value to output.",
- "io_conversion");
- PrintShortHelp("runtime_deps: How runtime dependency computation works.",
- "runtime_deps");
- PrintShortHelp("source_expansion: Map sources to outputs for scripts.",
- "source_expansion");
- PrintShortHelp("switches: Show available command-line switches.",
- "switch_list");
-}
-
-void PrintSwitchHelp() {
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
-
- // This uses "switch_list" for the tag because Markdown seems to generate
- // implicit tags for headings that match the strings, and some headings are
- // labeled "switches".
- PrintLongHelp(R"(Available global switches
-
- Do "gn help --the_switch_you_want_help_on" for more. Individual commands may
- take command-specific switches not listed here. See the help on your specific
- command for more.
-)",
- "switch_list");
-
- if (is_markdown)
- OutputString("```\n", DECORATION_NONE);
-
- for (const auto& s : switches::GetSwitches())
- PrintShortHelp(s.second.short_help);
-
- if (is_markdown)
- OutputString("```\n", DECORATION_NONE);
-
- OutputString("\n");
-}
-
-void PrintAllHelp() {
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
-
- if (is_markdown) {
- OutputString("# GN Reference\n\n");
- OutputString(
- "*This page is automatically generated from* "
- "`gn help --markdown all`.\n\n");
-
- // Generate our own table of contents so that we have more control
- // over what's in and out.
- OutputString("## Contents\n\n");
- }
-
- PrintToplevelHelp();
-
- OutputString("\n");
-
- if (is_markdown) {
- OutputString("## <a name=\"commands\"></a>Commands\n\n", DECORATION_NONE,
- NO_ESCAPING);
- }
- for (const auto& c : commands::GetCommands())
- PrintLongHelp(c.second.help, kCommandLinkPrefix + c.first);
-
- if (is_markdown) {
- OutputString("## <a name=\"targets\"></a>Target declarations\n\n",
- DECORATION_NONE, NO_ESCAPING);
- }
- for (const auto& f : functions::GetFunctions()) {
- if (f.second.is_target)
- PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
- }
-
- if (is_markdown) {
- OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n",
- DECORATION_NONE, NO_ESCAPING);
- }
- for (const auto& f : functions::GetFunctions()) {
- if (!f.second.is_target)
- PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
- }
-
- if (is_markdown) {
- OutputString(
- "## <a name=\"predefined_variables\"></a>"
- "Built-in predefined variables\n\n",
- DECORATION_NONE, NO_ESCAPING);
- }
- for (const auto& v : variables::GetBuiltinVariables())
- PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
-
- if (is_markdown) {
- OutputString(
- "## <a name=\"target_variables\"></a>"
- "Variables you set in targets\n\n",
- DECORATION_NONE, NO_ESCAPING);
- }
- for (const auto& v : variables::GetTargetVariables())
- PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
-
- if (is_markdown) {
- OutputString("## <a name=\"other\"></a>Other help topics\n\n",
- DECORATION_NONE, NO_ESCAPING);
- }
- PrintLongHelp(kBuildArgs_Help, "buildargs");
- PrintLongHelp(kDotfile_Help, "dotfile");
- PrintLongHelp(kExecution_Help, "execution");
- PrintLongHelp(kGrammar_Help, "grammar");
- PrintLongHelp(kInputOutputConversion_Help, "io_conversion");
- PrintLongHelp(kLabelPattern_Help, "label_pattern");
- PrintLongHelp(kLabels_Help, "labels");
- PrintLongHelp(kMetadata_Help, "metadata_collection");
- PrintLongHelp(kNinjaRules_Help, "ninja_rules");
- PrintLongHelp(kNoGnCheck_Help, "nogncheck");
- PrintLongHelp(kRuntimeDeps_Help, "runtime_deps");
- PrintLongHelp(kSourceExpansion_Help, "source_expansion");
-
- PrintSwitchHelp();
-}
-
-// Prints help on the given switch. There should be no leading hyphens. Returns
-// true if the switch was found and help was printed. False means the switch is
-// unknown.
-bool PrintHelpOnSwitch(const std::string& what) {
- const switches::SwitchInfoMap& all = switches::GetSwitches();
- switches::SwitchInfoMap::const_iterator found =
- all.find(base::StringPiece(what));
- if (found == all.end())
- return false;
- PrintLongHelp(found->second.long_help);
- return true;
-}
-
-// Special-case help for ambiguous "args" case.
-void PrintArgsHelp() {
- PrintLongHelp(
- "The string \"args\" is both a command and a variable for action "
- "targets.\nShowing help for both...\n\n");
- PrintLongHelp(commands::kArgs_Help);
- PrintLongHelp(
- "\n----------------------------------------------------------------------"
- "---------\n\n");
- PrintLongHelp(variables::kArgs_Help);
-}
-
-} // namespace
-
-const char kHelp[] = "help";
-const char kHelp_HelpShort[] = "help: Does what you think.";
-const char kHelp_Help[] =
- R"(gn help <anything>
-
- Yo dawg, I heard you like help on your help so I put help on the help in the
- help.
-
- You can also use "all" as the parameter to get all help at once.
-
-Switches
-
- --markdown
- Format output in markdown syntax.
-
-Example
-
- gn help --markdown all
- Dump all help to stdout in markdown format.
-)";
-
-int RunHelp(const std::vector<std::string>& args) {
- std::string what;
- if (args.size() == 0) {
- // If no argument is specified, check for switches to allow things like
- // "gn help --args" for help on the args switch.
- const base::CommandLine::SwitchMap& switches =
- base::CommandLine::ForCurrentProcess()->GetSwitches();
- if (switches.empty()) {
- // Still nothing, show help overview.
- PrintToplevelHelp();
- return 0;
- }
-
- // Switch help needs to be done separately. The CommandLine will strip the
- // switch separators so --args will come out as "args" which is then
- // ambiguous with the variable named "args".
- if (!PrintHelpOnSwitch(switches.begin()->first))
- PrintToplevelHelp();
- return 0;
- } else {
- what = args[0];
- }
-
- std::vector<base::StringPiece> all_help_topics;
-
- // Special-case ambiguous topics.
- if (what == "args") {
- PrintArgsHelp();
- return 0;
- }
-
- // Check commands.
- const commands::CommandInfoMap& command_map = commands::GetCommands();
- auto found_command = command_map.find(what);
- if (found_command != command_map.end()) {
- PrintLongHelp(found_command->second.help);
- return 0;
- }
- for (const auto& entry : command_map)
- all_help_topics.push_back(entry.first);
-
- // Check functions.
- const functions::FunctionInfoMap& function_map = functions::GetFunctions();
- auto found_function = function_map.find(what);
- if (found_function != function_map.end())
- PrintLongHelp(found_function->second.help);
- for (const auto& entry : function_map)
- all_help_topics.push_back(entry.first);
-
- // Builtin variables.
- const variables::VariableInfoMap& builtin_vars =
- variables::GetBuiltinVariables();
- auto found_builtin_var = builtin_vars.find(what);
- if (found_builtin_var != builtin_vars.end())
- PrintLongHelp(found_builtin_var->second.help);
- for (const auto& entry : builtin_vars)
- all_help_topics.push_back(entry.first);
-
- // Target variables.
- const variables::VariableInfoMap& target_vars =
- variables::GetTargetVariables();
- auto found_target_var = target_vars.find(what);
- if (found_target_var != target_vars.end())
- PrintLongHelp(found_target_var->second.help);
- for (const auto& entry : target_vars)
- all_help_topics.push_back(entry.first);
-
- if (found_function != function_map.end() ||
- found_builtin_var != builtin_vars.end() ||
- found_target_var != target_vars.end())
- return 0;
-
- // Random other topics.
- std::map<std::string, void (*)()> random_topics;
- random_topics["all"] = PrintAllHelp;
- random_topics["execution"] = []() { PrintLongHelp(kExecution_Help); };
- random_topics["buildargs"] = []() { PrintLongHelp(kBuildArgs_Help); };
- random_topics["dotfile"] = []() { PrintLongHelp(kDotfile_Help); };
- random_topics["grammar"] = []() { PrintLongHelp(kGrammar_Help); };
- random_topics["io_conversion"] = []() {
- PrintLongHelp(kInputOutputConversion_Help);
- };
- random_topics["label_pattern"] = []() { PrintLongHelp(kLabelPattern_Help); };
- random_topics["labels"] = []() { PrintLongHelp(kLabels_Help); };
- random_topics["metadata_collection"] = []() {
- PrintLongHelp(kMetadata_Help);
- };
- random_topics["ninja_rules"] = []() { PrintLongHelp(kNinjaRules_Help); };
- random_topics["nogncheck"] = []() { PrintLongHelp(kNoGnCheck_Help); };
- random_topics["runtime_deps"] = []() { PrintLongHelp(kRuntimeDeps_Help); };
- random_topics["source_expansion"] = []() {
- PrintLongHelp(kSourceExpansion_Help);
- };
- random_topics["switches"] = PrintSwitchHelp;
- auto found_random_topic = random_topics.find(what);
- if (found_random_topic != random_topics.end()) {
- found_random_topic->second();
- return 0;
- }
- for (const auto& entry : random_topics)
- all_help_topics.push_back(entry.first);
-
- // No help on this.
- Err(Location(), "No help on \"" + what + "\".").PrintToStdout();
- base::StringPiece suggestion = SpellcheckString(what, all_help_topics);
- if (suggestion.empty()) {
- OutputString("Run `gn help` for a list of available topics.\n",
- DECORATION_NONE);
- } else {
- OutputString("Did you mean `gn help " + suggestion.as_string() + "`?\n",
- DECORATION_NONE);
- }
- return 1;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_ls.cc b/gn/tools/gn/command_ls.cc
deleted file mode 100644
index 26094997508..00000000000
--- a/gn/tools/gn/command_ls.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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 <algorithm>
-#include <set>
-
-#include "base/command_line.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-
-namespace commands {
-
-const char kLs[] = "ls";
-const char kLs_HelpShort[] = "ls: List matching targets.";
-const char kLs_Help[] =
- R"(gn ls <out_dir> [<label_pattern>] [--all-toolchains] [--as=...]
- [--type=...] [--testonly=...]
-
- Lists all targets matching the given pattern for the given build directory.
- By default, only targets in the default toolchain will be matched unless a
- toolchain is explicitly supplied.
-
- If the label pattern is unspecified, list all targets. The label pattern is
- not a general regular expression (see "gn help label_pattern"). If you need
- more complex expressions, pipe the result through grep.
-
-Options
-
-)" TARGET_PRINTING_MODE_COMMAND_LINE_HELP "\n" ALL_TOOLCHAINS_SWITCH_HELP
- "\n" TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
- "\n" TARGET_TYPE_FILTER_COMMAND_LINE_HELP
- R"(
-Examples
-
- gn ls out/Debug
- Lists all targets in the default toolchain.
-
- gn ls out/Debug "//base/*"
- Lists all targets in the directory base and all subdirectories.
-
- gn ls out/Debug "//base:*"
- Lists all targets defined in //base/BUILD.gn.
-
- gn ls out/Debug //base --as=output
- Lists the build output file for //base:base
-
- gn ls out/Debug --type=executable
- Lists all executables produced by the build.
-
- gn ls out/Debug "//base/*" --as=output | xargs ninja -C out/Debug
- Builds all targets in //base and all subdirectories.
-
- gn ls out/Debug //base --all-toolchains
- Lists all variants of the target //base:base (it may be referenced
- in multiple toolchains).
-)";
-
-int RunLs(const std::vector<std::string>& args) {
- if (args.size() == 0) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn ls <build dir> [<label_pattern>]*\"")
- .PrintToStdout();
- return 1;
- }
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false) || !setup->Run())
- return 1;
-
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- bool all_toolchains = cmdline->HasSwitch(switches::kAllToolchains);
-
- std::vector<const Target*> matches;
- if (args.size() > 1) {
- // Some patterns or explicit labels were specified.
- std::vector<std::string> inputs(args.begin() + 1, args.end());
-
- UniqueVector<const Target*> target_matches;
- UniqueVector<const Config*> config_matches;
- UniqueVector<const Toolchain*> toolchain_matches;
- UniqueVector<SourceFile> file_matches;
- if (!ResolveFromCommandLineInput(setup, inputs, all_toolchains,
- &target_matches, &config_matches,
- &toolchain_matches, &file_matches))
- return 1;
- matches.insert(matches.begin(), target_matches.begin(),
- target_matches.end());
- } else if (all_toolchains) {
- // List all resolved targets.
- matches = setup->builder().GetAllResolvedTargets();
- } else {
- // List all resolved targets in the default toolchain.
- for (auto* target : setup->builder().GetAllResolvedTargets()) {
- if (target->settings()->is_default())
- matches.push_back(target);
- }
- }
- FilterAndPrintTargets(false, &matches);
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_meta.cc b/gn/tools/gn/command_meta.cc
deleted file mode 100644
index bb689e901e8..00000000000
--- a/gn/tools/gn/command_meta.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-// 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 <algorithm>
-#include <set>
-
-#include "base/command_line.h"
-#include "base/strings/string_split.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/metadata_walk.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-
-namespace commands {
-
-const char kMeta[] = "meta";
-const char kMeta_HelpShort[] = "meta: List target metadata collection results.";
-const char kMeta_Help[] =
- R"(gn meta
-
- gn meta <out_dir> <target>* --data=<key>[,<key>*]* [--walk=<key>[,<key>*]*]
- [--rebase=<dest dir>]
-
- Lists collected metaresults of all given targets for the given data key(s),
- collecting metadata dependencies as specified by the given walk key(s).
-
- See `gn help generated_file` for more information on the walk.
-
-Arguments
-
- <target(s)>
- A list of target labels from which to initiate the walk.
-
- --data
- A list of keys from which to extract data. In each target walked, its metadata
- scope is checked for the presence of these keys. If present, the contents of
- those variable in the scope are appended to the results list.
-
- --walk (optional)
- A list of keys from which to control the walk. In each target walked, its
- metadata scope is checked for the presence of any of these keys. If present,
- the contents of those variables is checked to ensure that it is a label of
- a valid dependency of the target and then added to the set of targets to walk.
- If the empty string ("") is present in any of these keys, all deps and data_deps
- are added to the walk set.
-
- --rebase (optional)
- A destination directory onto which to rebase any paths found. If set, all
- collected metadata will be rebased onto this path. This option will throw errors
- if collected metadata is not a list of strings.
-
-Examples
-
- gn meta out/Debug "//base/foo" --data=files
- Lists collected metaresults for the `files` key in the //base/foo:foo
- target and all of its dependency tree.
-
- gn meta out/Debug "//base/foo" --data=files --data=other
- Lists collected metaresults for the `files` and `other` keys in the
- //base/foo:foo target and all of its dependency tree.
-
- gn meta out/Debug "//base/foo" --data=files --walk=stop
- Lists collected metaresults for the `files` key in the //base/foo:foo
- target and all of the dependencies listed in the `stop` key (and so on).
-
- gn meta out/Debug "//base/foo" --data=files --rebase="/"
- Lists collected metaresults for the `files` key in the //base/foo:foo
- target and all of its dependency tree, rebasing the strings in the `files`
- key onto the source directory of the target's declaration relative to "/".
-)";
-
-int RunMeta(const std::vector<std::string>& args) {
- if (args.size() == 0) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn meta <out_dir> <target>* --data=<key>[,<key>*] "
- "[--walk=<key>[,<key>*]*] [--rebase=<dest dir>]\"")
- .PrintToStdout();
- return 1;
- }
-
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false) || !setup->Run())
- return 1;
-
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- std::string rebase_dir =
- cmdline->GetSwitchValueASCII(switches::kMetaRebaseFiles);
- std::string data_keys_str =
- cmdline->GetSwitchValueASCII(switches::kMetaDataKeys);
- std::string walk_keys_str =
- cmdline->GetSwitchValueASCII(switches::kMetaWalkKeys);
-
- std::vector<std::string> inputs(args.begin() + 1, args.end());
-
- UniqueVector<const Target*> targets;
- for (const auto& input : inputs) {
- const Target* target = ResolveTargetFromCommandLineString(setup, input);
- if (!target) {
- Err(Location(), "Unknown target " + input).PrintToStdout();
- return 1;
- }
- targets.push_back(target);
- }
-
- std::vector<std::string> data_keys = base::SplitString(
- data_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (data_keys.empty()) {
- return 1;
- }
- std::vector<std::string> walk_keys = base::SplitString(
- walk_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- Err err;
- std::set<const Target*> targets_walked;
- SourceDir rebase_source_dir(rebase_dir);
- // When SourceDir constructor is supplied with an empty string,
- // a trailing slash will be added. This prevent SourceDir::is_null()
- // from returning true. Explicitly remove this traling slash here.
- if (rebase_dir.empty()) {
- rebase_source_dir = SourceDir();
- }
- std::vector<Value> result =
- WalkMetadata(targets, data_keys, walk_keys, rebase_source_dir,
- &targets_walked, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return 1;
- }
-
- OutputString("Metadata values\n", DECORATION_DIM);
- for (const auto& value : result)
- OutputString("\n" + value.ToString(false) + "\n");
-
- // TODO(juliehockett): We should have better dep tracing and error support for
- // this. Also possibly data about where different values came from.
- OutputString("\nExtracted from:\n", DECORATION_DIM);
- bool first = true;
- for (const auto* target : targets_walked) {
- if (!first) {
- first = false;
- OutputString(", ", DECORATION_DIM);
- }
- OutputString(target->label().GetUserVisibleName(true) + "\n");
- }
- OutputString("\nusing data keys:\n", DECORATION_DIM);
- first = true;
- for (const auto& key : data_keys) {
- if (!first) {
- first = false;
- OutputString(", ");
- }
- OutputString(key + "\n");
- }
- if (!walk_keys.empty()) {
- OutputString("\nand using walk keys:\n", DECORATION_DIM);
- first = true;
- for (const auto& key : walk_keys) {
- if (!first) {
- first = false;
- OutputString(", ");
- }
- OutputString(key + "\n");
- }
- }
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_path.cc b/gn/tools/gn/command_path.cc
deleted file mode 100644
index 503ba6d9050..00000000000
--- a/gn/tools/gn/command_path.cc
+++ /dev/null
@@ -1,413 +0,0 @@
-// 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 <stddef.h>
-
-#include <algorithm>
-
-#include "base/command_line.h"
-#include "base/strings/stringprintf.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-
-namespace commands {
-
-namespace {
-
-enum class DepType { NONE, PUBLIC, PRIVATE, DATA };
-
-// The dependency paths are stored in a vector. Assuming the chain:
-// A --[public]--> B --[private]--> C
-// The stack will look like:
-// [0] = A, NONE (this has no dep type since nobody depends on it)
-// [1] = B, PUBLIC
-// [2] = C, PRIVATE
-using TargetDep = std::pair<const Target*, DepType>;
-using PathVector = std::vector<TargetDep>;
-
-// How to search.
-enum class PrivateDeps { INCLUDE, EXCLUDE };
-enum class DataDeps { INCLUDE, EXCLUDE };
-enum class PrintWhat { ONE, ALL };
-
-struct Options {
- Options()
- : print_what(PrintWhat::ONE), public_only(false), with_data(false) {}
-
- PrintWhat print_what;
- bool public_only;
- bool with_data;
-};
-
-typedef std::list<PathVector> WorkQueue;
-
-struct Stats {
- Stats() : public_paths(0), other_paths(0) {}
-
- int total_paths() const { return public_paths + other_paths; }
-
- int public_paths;
- int other_paths;
-
- // Stores targets that have a path to the destination, and whether that
- // path is public, private, or data.
- std::map<const Target*, DepType> found_paths;
-};
-
-// If the implicit_last_dep is not "none", this type indicates the
-// classification of the elided last part of path.
-DepType ClassifyPath(const PathVector& path, DepType implicit_last_dep) {
- DepType result;
- if (implicit_last_dep != DepType::NONE)
- result = implicit_last_dep;
- else
- result = DepType::PUBLIC;
-
- // Skip the 0th one since that is always NONE.
- for (size_t i = 1; i < path.size(); i++) {
- // PRIVATE overrides PUBLIC, and DATA overrides everything (the idea is
- // to find the worst link in the path).
- if (path[i].second == DepType::PRIVATE) {
- if (result == DepType::PUBLIC)
- result = DepType::PRIVATE;
- } else if (path[i].second == DepType::DATA) {
- result = DepType::DATA;
- }
- }
- return result;
-}
-
-const char* StringForDepType(DepType type) {
- switch (type) {
- case DepType::PUBLIC:
- return "public";
- case DepType::PRIVATE:
- return "private";
- case DepType::DATA:
- return "data";
- break;
- case DepType::NONE:
- default:
- return "";
- }
-}
-
-// Prints the given path. If the implicit_last_dep is not "none", the last
-// dependency will show an elided dependency with the given annotation.
-void PrintPath(const PathVector& path, DepType implicit_last_dep) {
- if (path.empty())
- return;
-
- // Don't print toolchains unless they differ from the first target.
- const Label& default_toolchain = path[0].first->label().GetToolchainLabel();
-
- for (size_t i = 0; i < path.size(); i++) {
- OutputString(path[i].first->label().GetUserVisibleName(default_toolchain));
-
- // Output dependency type.
- if (i == path.size() - 1) {
- // Last one either gets the implicit last dep type or nothing.
- if (implicit_last_dep != DepType::NONE) {
- OutputString(std::string(" --> see ") +
- StringForDepType(implicit_last_dep) +
- " chain printed above...",
- DECORATION_DIM);
- }
- } else {
- // Take type from the next entry.
- OutputString(
- std::string(" --[") + StringForDepType(path[i + 1].second) + "]-->",
- DECORATION_DIM);
- }
- OutputString("\n");
- }
-
- OutputString("\n");
-}
-
-void InsertTargetsIntoFoundPaths(const PathVector& path,
- DepType implicit_last_dep,
- Stats* stats) {
- DepType type = ClassifyPath(path, implicit_last_dep);
-
- bool inserted = false;
-
- // Don't try to insert the 0th item in the list which is the "from" target.
- // The search will be run more than once (for the different path types) and
- // if the "from" target was in the list, subsequent passes could never run
- // the starting point is alredy in the list of targets considered).
- //
- // One might imagine an alternate implementation where all items are counted
- // here but the "from" item is erased at the beginning of each search, but
- // that will mess up the metrics (the private search pass will find the
- // same public paths as the previous public pass, "inserted" will be true
- // here since the item wasn't found, and the public path will be
- // double-counted in the stats.
- for (size_t i = 1; i < path.size(); i++) {
- const auto& pair = path[i];
-
- // Don't overwrite an existing one. The algorithm works by first doing
- // public, then private, then data, so anything already there is guaranteed
- // at least as good as our addition.
- if (stats->found_paths.find(pair.first) == stats->found_paths.end()) {
- stats->found_paths.insert(std::make_pair(pair.first, type));
- inserted = true;
- }
- }
-
- if (inserted) {
- // Only count this path in the stats if any part of it was actually new.
- if (type == DepType::PUBLIC)
- stats->public_paths++;
- else
- stats->other_paths++;
- }
-}
-
-void BreadthFirstSearch(const Target* from,
- const Target* to,
- PrivateDeps private_deps,
- DataDeps data_deps,
- PrintWhat print_what,
- Stats* stats) {
- // Seed the initial stack with just the "from" target.
- PathVector initial_stack;
- initial_stack.emplace_back(from, DepType::NONE);
- WorkQueue work_queue;
- work_queue.push_back(initial_stack);
-
- // Track checked targets to avoid checking the same once more than once.
- std::set<const Target*> visited;
-
- while (!work_queue.empty()) {
- PathVector current_path = work_queue.front();
- work_queue.pop_front();
-
- const Target* current_target = current_path.back().first;
-
- if (current_target == to) {
- // Found a new path.
- if (stats->total_paths() == 0 || print_what == PrintWhat::ALL)
- PrintPath(current_path, DepType::NONE);
-
- // Insert all nodes on the path into the found paths list. Since we're
- // doing search breadth first, we know that the current path is the best
- // path for all nodes on it.
- InsertTargetsIntoFoundPaths(current_path, DepType::NONE, stats);
- } else {
- // Check for a path that connects to an already known-good one. Printing
- // this here will mean the results aren't strictly in depth-first order
- // since there could be many items on the found path this connects to.
- // Doing this here will mean that the output is sorted by length of items
- // printed (with the redundant parts of the path omitted) rather than
- // complete path length.
- const auto& found_current_target =
- stats->found_paths.find(current_target);
- if (found_current_target != stats->found_paths.end()) {
- if (stats->total_paths() == 0 || print_what == PrintWhat::ALL)
- PrintPath(current_path, found_current_target->second);
-
- // Insert all nodes on the path into the found paths list since we know
- // everything along this path also leads to the destination.
- InsertTargetsIntoFoundPaths(current_path, found_current_target->second,
- stats);
- continue;
- }
- }
-
- // If we've already checked this one, stop. This should be after the above
- // check for a known-good check, because known-good ones will always have
- // been previously visited.
- if (visited.find(current_target) == visited.end())
- visited.insert(current_target);
- else
- continue;
-
- // Add public deps for this target to the queue.
- for (const auto& pair : current_target->public_deps()) {
- work_queue.push_back(current_path);
- work_queue.back().push_back(TargetDep(pair.ptr, DepType::PUBLIC));
- }
-
- if (private_deps == PrivateDeps::INCLUDE) {
- // Add private deps.
- for (const auto& pair : current_target->private_deps()) {
- work_queue.push_back(current_path);
- work_queue.back().push_back(TargetDep(pair.ptr, DepType::PRIVATE));
- }
- }
-
- if (data_deps == DataDeps::INCLUDE) {
- // Add data deps.
- for (const auto& pair : current_target->data_deps()) {
- work_queue.push_back(current_path);
- work_queue.back().push_back(TargetDep(pair.ptr, DepType::DATA));
- }
- }
- }
-}
-
-void DoSearch(const Target* from,
- const Target* to,
- const Options& options,
- Stats* stats) {
- BreadthFirstSearch(from, to, PrivateDeps::EXCLUDE, DataDeps::EXCLUDE,
- options.print_what, stats);
- if (!options.public_only) {
- // Check private deps.
- BreadthFirstSearch(from, to, PrivateDeps::INCLUDE, DataDeps::EXCLUDE,
- options.print_what, stats);
- if (options.with_data) {
- // Check data deps.
- BreadthFirstSearch(from, to, PrivateDeps::INCLUDE, DataDeps::INCLUDE,
- options.print_what, stats);
- }
- }
-}
-
-} // namespace
-
-const char kPath[] = "path";
-const char kPath_HelpShort[] = "path: Find paths between two targets.";
-const char kPath_Help[] =
- R"(gn path <out_dir> <target_one> <target_two>
-
- Finds paths of dependencies between two targets. Each unique path will be
- printed in one group, and groups will be separate by newlines. The two
- targets can appear in either order (paths will be found going in either
- direction).
-
- By default, a single path will be printed. If there is a path with only
- public dependencies, the shortest public path will be printed. Otherwise, the
- shortest path using either public or private dependencies will be printed. If
- --with-data is specified, data deps will also be considered. If there are
- multiple shortest paths, an arbitrary one will be selected.
-
-Interesting paths
-
- In a large project, there can be 100's of millions of unique paths between a
- very high level and a common low-level target. To make the output more useful
- (and terminate in a reasonable time), GN will not revisit sub-paths
- previously known to lead to the target.
-
-Options
-
- --all
- Prints all "interesting" paths found rather than just the first one.
- Public paths will be printed first in order of increasing length, followed
- by non-public paths in order of increasing length.
-
- --public
- Considers only public paths. Can't be used with --with-data.
-
- --with-data
- Additionally follows data deps. Without this flag, only public and private
- linked deps will be followed. Can't be used with --public.
-
-Example
-
- gn path out/Default //base //tools/gn
-)";
-
-int RunPath(const std::vector<std::string>& args) {
- if (args.size() != 3) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn path <out_dir> <target_one> <target_two>\"")
- .PrintToStdout();
- return 1;
- }
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false))
- return 1;
- if (!setup->Run())
- return 1;
-
- const Target* target1 = ResolveTargetFromCommandLineString(setup, args[1]);
- if (!target1)
- return 1;
- const Target* target2 = ResolveTargetFromCommandLineString(setup, args[2]);
- if (!target2)
- return 1;
-
- Options options;
- options.print_what = base::CommandLine::ForCurrentProcess()->HasSwitch("all")
- ? PrintWhat::ALL
- : PrintWhat::ONE;
- options.public_only =
- base::CommandLine::ForCurrentProcess()->HasSwitch("public");
- options.with_data =
- base::CommandLine::ForCurrentProcess()->HasSwitch("with-data");
- if (options.public_only && options.with_data) {
- Err(Location(), "Can't use --public with --with-data for 'gn path'.",
- "Your zealous over-use of arguments has inevitably resulted in an "
- "invalid\ncombination of flags.")
- .PrintToStdout();
- return 1;
- }
-
- Stats stats;
- DoSearch(target1, target2, options, &stats);
- if (stats.total_paths() == 0) {
- // If we don't find a path going "forwards", try the reverse direction.
- // Deps can only go in one direction without having a cycle, which will
- // have caused a run failure above.
- DoSearch(target2, target1, options, &stats);
- }
-
- // This string is inserted in the results to annotate whether the result
- // is only public or includes data deps or not.
- const char* path_annotation = "";
- if (options.public_only)
- path_annotation = "public ";
- else if (!options.with_data)
- path_annotation = "non-data ";
-
- if (stats.total_paths() == 0) {
- // No results.
- OutputString(
- base::StringPrintf("No %spaths found between these two targets.\n",
- path_annotation),
- DECORATION_YELLOW);
- } else if (stats.total_paths() == 1) {
- // Exactly one result.
- OutputString(base::StringPrintf("1 %spath found.", path_annotation),
- DECORATION_YELLOW);
- if (!options.public_only) {
- if (stats.public_paths)
- OutputString(" It is public.");
- else
- OutputString(" It is not public.");
- }
- OutputString("\n");
- } else {
- if (options.print_what == PrintWhat::ALL) {
- // Showing all paths when there are many.
- OutputString(base::StringPrintf("%d \"interesting\" %spaths found.",
- stats.total_paths(), path_annotation),
- DECORATION_YELLOW);
- if (!options.public_only) {
- OutputString(
- base::StringPrintf(" %d of them are public.", stats.public_paths));
- }
- OutputString("\n");
- } else {
- // Showing one path when there are many.
- OutputString(
- base::StringPrintf("Showing one of %d \"interesting\" %spaths.",
- stats.total_paths(), path_annotation),
- DECORATION_YELLOW);
- if (!options.public_only) {
- OutputString(
- base::StringPrintf(" %d of them are public.", stats.public_paths));
- }
- OutputString("\nUse --all to print all paths.\n");
- }
- }
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/command_refs.cc b/gn/tools/gn/command_refs.cc
deleted file mode 100644
index 6364d813df4..00000000000
--- a/gn/tools/gn/command_refs.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-// 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 <stddef.h>
-
-#include <map>
-#include <set>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/item.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-
-namespace commands {
-
-namespace {
-
-typedef std::set<const Target*> TargetSet;
-typedef std::vector<const Target*> TargetVector;
-
-// Maps targets to the list of targets that depend on them.
-typedef std::multimap<const Target*, const Target*> DepMap;
-
-// Populates the reverse dependency map for the targets in the Setup.
-void FillDepMap(Setup* setup, DepMap* dep_map) {
- for (auto* target : setup->builder().GetAllResolvedTargets()) {
- for (const auto& dep_pair : target->GetDeps(Target::DEPS_ALL))
- dep_map->insert(std::make_pair(dep_pair.ptr, target));
- }
-}
-
-// Forward declaration for function below.
-size_t RecursivePrintTargetDeps(const DepMap& dep_map,
- const Target* target,
- TargetSet* seen_targets,
- int indent_level);
-
-// Prints the target and its dependencies in tree form. If the set is non-null,
-// new targets encountered will be added to the set, and if a ref is in the set
-// already, it will not be recused into. When the set is null, all refs will be
-// printed.
-//
-// Returns the number of items printed.
-size_t RecursivePrintTarget(const DepMap& dep_map,
- const Target* target,
- TargetSet* seen_targets,
- int indent_level) {
- std::string indent(indent_level * 2, ' ');
- size_t count = 1;
-
- // Only print the toolchain for non-default-toolchain targets.
- OutputString(indent + target->label().GetUserVisibleName(
- !target->settings()->is_default()));
-
- bool print_children = true;
- if (seen_targets) {
- if (seen_targets->find(target) == seen_targets->end()) {
- // New target, mark it visited.
- seen_targets->insert(target);
- } else {
- // Already seen.
- print_children = false;
- // Only print "..." if something is actually elided, which means that
- // the current target has children.
- if (dep_map.lower_bound(target) != dep_map.upper_bound(target))
- OutputString("...");
- }
- }
-
- OutputString("\n");
- if (print_children) {
- count += RecursivePrintTargetDeps(dep_map, target, seen_targets,
- indent_level + 1);
- }
- return count;
-}
-
-// Prints refs of the given target (not the target itself). See
-// RecursivePrintTarget.
-size_t RecursivePrintTargetDeps(const DepMap& dep_map,
- const Target* target,
- TargetSet* seen_targets,
- int indent_level) {
- DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
- DepMap::const_iterator dep_end = dep_map.upper_bound(target);
- size_t count = 0;
- for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
- cur_dep++) {
- count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets,
- indent_level);
- }
- return count;
-}
-
-void RecursiveCollectChildRefs(const DepMap& dep_map,
- const Target* target,
- TargetSet* results);
-
-// Recursively finds all targets that reference the given one, and additionally
-// adds the current one to the list.
-void RecursiveCollectRefs(const DepMap& dep_map,
- const Target* target,
- TargetSet* results) {
- if (results->find(target) != results->end())
- return; // Already found this target.
- results->insert(target);
- RecursiveCollectChildRefs(dep_map, target, results);
-}
-
-// Recursively finds all targets that reference the given one.
-void RecursiveCollectChildRefs(const DepMap& dep_map,
- const Target* target,
- TargetSet* results) {
- DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
- DepMap::const_iterator dep_end = dep_map.upper_bound(target);
- for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
- cur_dep++)
- RecursiveCollectRefs(dep_map, cur_dep->second, results);
-}
-
-bool TargetContainsFile(const Target* target, const SourceFile& file) {
- for (const auto& cur_file : target->sources()) {
- if (cur_file == file)
- return true;
- }
- for (const auto& cur_file : target->public_headers()) {
- if (cur_file == file)
- return true;
- }
- for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
- for (const auto& cur_file : iter.cur().inputs()) {
- if (cur_file == file)
- return true;
- }
- }
- for (const auto& cur_file : target->data()) {
- if (cur_file == file.value())
- return true;
- if (cur_file.back() == '/' &&
- base::StartsWith(file.value(), cur_file, base::CompareCase::SENSITIVE))
- return true;
- }
-
- if (target->action_values().script().value() == file.value())
- return true;
-
- std::vector<SourceFile> output_sources;
- target->action_values().GetOutputsAsSourceFiles(target, &output_sources);
- for (const auto& cur_file : output_sources) {
- if (cur_file == file)
- return true;
- }
-
- for (const auto& cur_file : target->computed_outputs()) {
- if (cur_file.AsSourceFile(target->settings()->build_settings()) == file)
- return true;
- }
- return false;
-}
-
-void GetTargetsContainingFile(Setup* setup,
- const std::vector<const Target*>& all_targets,
- const SourceFile& file,
- bool all_toolchains,
- UniqueVector<const Target*>* matches) {
- Label default_toolchain = setup->loader()->default_toolchain_label();
- for (auto* target : all_targets) {
- if (!all_toolchains) {
- // Only check targets in the default toolchain.
- if (target->label().GetToolchainLabel() != default_toolchain)
- continue;
- }
- if (TargetContainsFile(target, file))
- matches->push_back(target);
- }
-}
-
-bool TargetReferencesConfig(const Target* target, const Config* config) {
- for (const LabelConfigPair& cur : target->configs()) {
- if (cur.ptr == config)
- return true;
- }
- for (const LabelConfigPair& cur : target->public_configs()) {
- if (cur.ptr == config)
- return true;
- }
- return false;
-}
-
-void GetTargetsReferencingConfig(Setup* setup,
- const std::vector<const Target*>& all_targets,
- const Config* config,
- bool all_toolchains,
- UniqueVector<const Target*>* matches) {
- Label default_toolchain = setup->loader()->default_toolchain_label();
- for (auto* target : all_targets) {
- if (!all_toolchains) {
- // Only check targets in the default toolchain.
- if (target->label().GetToolchainLabel() != default_toolchain)
- continue;
- }
- if (TargetReferencesConfig(target, config))
- matches->push_back(target);
- }
-}
-
-// Returns the number of matches printed.
-size_t DoTreeOutput(const DepMap& dep_map,
- const UniqueVector<const Target*>& implicit_target_matches,
- const UniqueVector<const Target*>& explicit_target_matches,
- bool all) {
- TargetSet seen_targets;
- size_t count = 0;
-
- // Implicit targets don't get printed themselves.
- for (const Target* target : implicit_target_matches) {
- if (all)
- count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
- else
- count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
- }
-
- // Explicit targets appear in the output.
- for (const Target* target : implicit_target_matches) {
- if (all)
- count += RecursivePrintTarget(dep_map, target, nullptr, 0);
- else
- count += RecursivePrintTarget(dep_map, target, &seen_targets, 0);
- }
-
- return count;
-}
-
-// Returns the number of matches printed.
-size_t DoAllListOutput(
- const DepMap& dep_map,
- const UniqueVector<const Target*>& implicit_target_matches,
- const UniqueVector<const Target*>& explicit_target_matches) {
- // Output recursive dependencies, uniquified and flattened.
- TargetSet results;
-
- for (const Target* target : implicit_target_matches)
- RecursiveCollectChildRefs(dep_map, target, &results);
- for (const Target* target : explicit_target_matches) {
- // Explicit targets also get added to the output themselves.
- results.insert(target);
- RecursiveCollectChildRefs(dep_map, target, &results);
- }
-
- FilterAndPrintTargetSet(false, results);
- return results.size();
-}
-
-// Returns the number of matches printed.
-size_t DoDirectListOutput(
- const DepMap& dep_map,
- const UniqueVector<const Target*>& implicit_target_matches,
- const UniqueVector<const Target*>& explicit_target_matches) {
- TargetSet results;
-
- // Output everything that refers to the implicit ones.
- for (const Target* target : implicit_target_matches) {
- DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
- DepMap::const_iterator dep_end = dep_map.upper_bound(target);
- for (DepMap::const_iterator cur_dep = dep_begin; cur_dep != dep_end;
- cur_dep++)
- results.insert(cur_dep->second);
- }
-
- // And just output the explicit ones directly (these are the target matches
- // when referring to what references a file or config).
- for (const Target* target : explicit_target_matches)
- results.insert(target);
-
- FilterAndPrintTargetSet(false, results);
- return results.size();
-}
-
-} // namespace
-
-const char kRefs[] = "refs";
-const char kRefs_HelpShort[] = "refs: Find stuff referencing a target or file.";
-const char kRefs_Help[] =
- R"(gn refs
-
- gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)*
- [--all] [--all-toolchains] [--as=...] [--testonly=...] [--type=...]
-
- Finds reverse dependencies (which targets reference something). The input is
- a list containing:
-
- - Target label: The result will be which targets depend on it.
-
- - Config label: The result will be which targets list the given config in
- its "configs" or "public_configs" list.
-
- - Label pattern: The result will be which targets depend on any target
- matching the given pattern. Patterns will not match configs. These are not
- general regular expressions, see "gn help label_pattern" for details.
-
- - File name: The result will be which targets list the given file in its
- "inputs", "sources", "public", "data", or "outputs". Any input that does
- not contain wildcards and does not match a target or a config will be
- treated as a file.
-
- - Response file: If the input starts with an "@", it will be interpreted as
- a path to a file containing a list of labels or file names, one per line.
- This allows us to handle long lists of inputs without worrying about
- command line limits.
-
-Options
-
- --all
- When used without --tree, will recurse and display all unique
- dependencies of the given targets. For example, if the input is a target,
- this will output all targets that depend directly or indirectly on the
- input. If the input is a file, this will output all targets that depend
- directly or indirectly on that file.
-
- When used with --tree, turns off eliding to show a complete tree.
-)"
-
- ALL_TOOLCHAINS_SWITCH_HELP "\n" TARGET_PRINTING_MODE_COMMAND_LINE_HELP
-
- R"(
- -q
- Quiet. If nothing matches, don't print any output. Without this option, if
- there are no matches there will be an informational message printed which
- might interfere with scripts processing the output.
-)"
-
- TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
-
- R"(
- --tree
- Outputs a reverse dependency tree from the given target. Duplicates will
- be elided. Combine with --all to see a full dependency tree.
-
- Tree output can not be used with the filtering or output flags: --as,
- --type, --testonly.
-)"
-
- TARGET_TYPE_FILTER_COMMAND_LINE_HELP
-
- R"(
-
-Examples (target input)
-
- gn refs out/Debug //tools/gn:gn
- Find all targets depending on the given exact target name.
-
- gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim
- Edit all .gn files containing references to //base:i18n
-
- gn refs out/Debug //base --all
- List all targets depending directly or indirectly on //base:base.
-
- gn refs out/Debug "//base/*"
- List all targets depending directly on any target in //base or
- its subdirectories.
-
- gn refs out/Debug "//base:*"
- List all targets depending directly on any target in
- //base/BUILD.gn.
-
- gn refs out/Debug //base --tree
- Print a reverse dependency tree of //base:base
-
-Examples (file input)
-
- gn refs out/Debug //base/macros.h
- Print target(s) listing //base/macros.h as a source.
-
- gn refs out/Debug //base/macros.h --tree
- Display a reverse dependency tree to get to the given file. This
- will show how dependencies will reference that file.
-
- gn refs out/Debug //base/macros.h //base/at_exit.h --all
- Display all unique targets with some dependency path to a target
- containing either of the given files as a source.
-
- gn refs out/Debug //base/macros.h --testonly=true --type=executable
- --all --as=output
- Display the executable file names of all test executables
- potentially affected by a change to the given file.
-)";
-
-int RunRefs(const std::vector<std::string>& args) {
- if (args.size() <= 1) {
- Err(Location(), "You're holding it wrong.",
- "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"")
- .PrintToStdout();
- return 1;
- }
-
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- bool tree = cmdline->HasSwitch("tree");
- bool all = cmdline->HasSwitch("all");
- bool all_toolchains = cmdline->HasSwitch(switches::kAllToolchains);
-
- // Deliberately leaked to avoid expensive process teardown.
- Setup* setup = new Setup;
- if (!setup->DoSetup(args[0], false) || !setup->Run())
- return 1;
-
- // The inputs are everything but the first arg (which is the build dir).
- std::vector<std::string> inputs;
- for (size_t i = 1; i < args.size(); i++) {
- if (args[i][0] == '@') {
- // The argument is as a path to a response file.
- std::string contents;
- bool ret =
- base::ReadFileToString(UTF8ToFilePath(args[i].substr(1)), &contents);
- if (!ret) {
- Err(Location(), "Response file " + args[i].substr(1) + " not found.")
- .PrintToStdout();
- return 1;
- }
- for (const std::string& line : base::SplitString(
- contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
- if (!line.empty())
- inputs.push_back(line);
- }
- } else {
- // The argument is a label or a path.
- inputs.push_back(args[i]);
- }
- }
-
- // Get the matches for the command-line input.
- UniqueVector<const Target*> target_matches;
- UniqueVector<const Config*> config_matches;
- UniqueVector<const Toolchain*> toolchain_matches;
- UniqueVector<SourceFile> file_matches;
- if (!ResolveFromCommandLineInput(setup, inputs, all_toolchains,
- &target_matches, &config_matches,
- &toolchain_matches, &file_matches))
- return 1;
-
- // When you give a file or config as an input, you want the targets that are
- // associated with it. We don't want to just append this to the
- // target_matches, however, since these targets should actually be listed in
- // the output, while for normal targets you don't want to see the inputs,
- // only what refers to them.
- std::vector<const Target*> all_targets =
- setup->builder().GetAllResolvedTargets();
- UniqueVector<const Target*> explicit_target_matches;
- for (const auto& file : file_matches) {
- GetTargetsContainingFile(setup, all_targets, file, all_toolchains,
- &explicit_target_matches);
- }
- for (auto* config : config_matches) {
- GetTargetsReferencingConfig(setup, all_targets, config, all_toolchains,
- &explicit_target_matches);
- }
-
- // Tell the user if their input matches no files or labels. We need to check
- // both that it matched no targets and no configs. File input will already
- // have been converted to targets at this point. Configs will have been
- // converted to targets also, but there could be no targets referencing the
- // config, which is different than no config with that name.
- bool quiet = cmdline->HasSwitch("q");
- if (!quiet && config_matches.empty() && explicit_target_matches.empty() &&
- target_matches.empty()) {
- OutputString("The input matches no targets, configs, or files.\n",
- DECORATION_YELLOW);
- return 1;
- }
-
- // Construct the reverse dependency tree.
- DepMap dep_map;
- FillDepMap(setup, &dep_map);
-
- size_t cnt = 0;
- if (tree)
- cnt = DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
- else if (all)
- cnt = DoAllListOutput(dep_map, target_matches, explicit_target_matches);
- else
- cnt = DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
-
- // If you ask for the references of a valid target, but that target has
- // nothing referencing it, we'll get here without having printed anything.
- if (!quiet && cnt == 0)
- OutputString("Nothing references this.\n", DECORATION_YELLOW);
-
- return 0;
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/commands.cc b/gn/tools/gn/commands.cc
deleted file mode 100644
index cf387258dd7..00000000000
--- a/gn/tools/gn/commands.cc
+++ /dev/null
@@ -1,559 +0,0 @@
-// 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 "tools/gn/commands.h"
-
-#include "base/command_line.h"
-#include "base/environment.h"
-#include "base/strings/string_split.h"
-#include "base/values.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/item.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/setup.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/target.h"
-#include "util/build_config.h"
-
-namespace commands {
-
-namespace {
-
-// Like above but the input string can be a pattern that matches multiple
-// targets. If the input does not parse as a pattern, prints and error and
-// returns false. If the pattern is valid, fills the vector (which might be
-// empty if there are no matches) and returns true.
-//
-// If all_toolchains is false, a pattern with an unspecified toolchain will
-// match the default toolchain only. If true, all toolchains will be matched.
-bool ResolveTargetsFromCommandLinePattern(Setup* setup,
- const std::string& label_pattern,
- bool all_toolchains,
- std::vector<const Target*>* matches) {
- Value pattern_value(nullptr, label_pattern);
-
- Err err;
- LabelPattern pattern = LabelPattern::GetPattern(
- SourceDirForCurrentDirectory(setup->build_settings().root_path()),
- pattern_value, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- if (!all_toolchains) {
- // By default a pattern with an empty toolchain will match all toolchains.
- // If the caller wants to default to the main toolchain only, set it
- // explicitly.
- if (pattern.toolchain().is_null()) {
- // No explicit toolchain set.
- pattern.set_toolchain(setup->loader()->default_toolchain_label());
- }
- }
-
- std::vector<LabelPattern> pattern_vector;
- pattern_vector.push_back(pattern);
- FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(),
- pattern_vector, matches);
- return true;
-}
-
-// If there's an error, it will be printed and false will be returned.
-bool ResolveStringFromCommandLineInput(
- Setup* setup,
- const SourceDir& current_dir,
- const std::string& input,
- bool all_toolchains,
- UniqueVector<const Target*>* target_matches,
- UniqueVector<const Config*>* config_matches,
- UniqueVector<const Toolchain*>* toolchain_matches,
- UniqueVector<SourceFile>* file_matches) {
- if (LabelPattern::HasWildcard(input)) {
- // For now, only match patterns against targets. It might be nice in the
- // future to allow the user to specify which types of things they want to
- // match, but it should probably only match targets by default.
- std::vector<const Target*> target_match_vector;
- if (!ResolveTargetsFromCommandLinePattern(setup, input, all_toolchains,
- &target_match_vector))
- return false;
- for (const Target* target : target_match_vector)
- target_matches->push_back(target);
- return true;
- }
-
- // Try to figure out what this thing is.
- Err err;
- Label label =
- Label::Resolve(current_dir, setup->loader()->default_toolchain_label(),
- Value(nullptr, input), &err);
- if (err.has_error()) {
- // Not a valid label, assume this must be a file.
- err = Err();
- file_matches->push_back(current_dir.ResolveRelativeFile(
- Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
- return true;
- }
-
- const Item* item = setup->builder().GetItem(label);
- if (item) {
- if (const Config* as_config = item->AsConfig())
- config_matches->push_back(as_config);
- else if (const Target* as_target = item->AsTarget())
- target_matches->push_back(as_target);
- else if (const Toolchain* as_toolchain = item->AsToolchain())
- toolchain_matches->push_back(as_toolchain);
- } else {
- // Not an item, assume this must be a file.
- file_matches->push_back(current_dir.ResolveRelativeFile(
- Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
- }
-
- return true;
-}
-
-enum TargetPrintingMode {
- TARGET_PRINT_BUILDFILE,
- TARGET_PRINT_LABEL,
- TARGET_PRINT_OUTPUT,
-};
-
-// Retrieves the target printing mode based on the command line flags for the
-// current process. Returns true on success. On error, prints a message to the
-// console and returns false.
-bool GetTargetPrintingMode(TargetPrintingMode* mode) {
- std::string switch_key = "as";
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-
- if (!cmdline->HasSwitch(switch_key)) {
- // Default to labels.
- *mode = TARGET_PRINT_LABEL;
- return true;
- }
-
- std::string value = cmdline->GetSwitchValueASCII(switch_key);
- if (value == "buildfile") {
- *mode = TARGET_PRINT_BUILDFILE;
- return true;
- }
- if (value == "label") {
- *mode = TARGET_PRINT_LABEL;
- return true;
- }
- if (value == "output") {
- *mode = TARGET_PRINT_OUTPUT;
- return true;
- }
-
- Err(Location(), "Invalid value for \"--as\".",
- "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
- "said \"" +
- value + "\".")
- .PrintToStdout();
- return false;
-}
-
-// Returns the target type filter based on the command line flags for the
-// current process. Returns true on success. On error, prints a message to the
-// console and returns false.
-//
-// Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
-// will never be returned. Code applying the filters should apply Target::ACTION
-// to both ACTION and ACTION_FOREACH.
-bool GetTargetTypeFilter(Target::OutputType* type) {
- std::string switch_key = "type";
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-
- if (!cmdline->HasSwitch(switch_key)) {
- // Default to unknown -> no filtering.
- *type = Target::UNKNOWN;
- return true;
- }
-
- std::string value = cmdline->GetSwitchValueASCII(switch_key);
- if (value == "group") {
- *type = Target::GROUP;
- return true;
- }
- if (value == "executable") {
- *type = Target::EXECUTABLE;
- return true;
- }
- if (value == "shared_library") {
- *type = Target::SHARED_LIBRARY;
- return true;
- }
- if (value == "loadable_module") {
- *type = Target::LOADABLE_MODULE;
- return true;
- }
- if (value == "static_library") {
- *type = Target::STATIC_LIBRARY;
- return true;
- }
- if (value == "source_set") {
- *type = Target::SOURCE_SET;
- return true;
- }
- if (value == "copy") {
- *type = Target::COPY_FILES;
- return true;
- }
- if (value == "action") {
- *type = Target::ACTION;
- return true;
- }
-
- Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
- return false;
-}
-
-// Applies any testonly filtering specified on the command line to the given
-// target set. On failure, prints an error and returns false.
-bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- std::string testonly_key = "testonly";
-
- if (targets->empty() || !cmdline->HasSwitch(testonly_key))
- return true;
-
- std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
- bool testonly = false;
- if (testonly_value == "true") {
- testonly = true;
- } else if (testonly_value != "false") {
- Err(Location(), "Bad value for --testonly.",
- "I was expecting --testonly=true or --testonly=false.")
- .PrintToStdout();
- return false;
- }
-
- // Filter into a copy of the vector, then replace the output.
- std::vector<const Target*> result;
- result.reserve(targets->size());
-
- for (const Target* target : *targets) {
- if (target->testonly() == testonly)
- result.push_back(target);
- }
-
- *targets = std::move(result);
- return true;
-}
-
-// Applies any target type filtering specified on the command line to the given
-// target set. On failure, prints an error and returns false.
-bool ApplyTypeFilter(std::vector<const Target*>* targets) {
- Target::OutputType type = Target::UNKNOWN;
- if (!GetTargetTypeFilter(&type))
- return false;
- if (targets->empty() || type == Target::UNKNOWN)
- return true; // Nothing to filter out.
-
- // Filter into a copy of the vector, then replace the output.
- std::vector<const Target*> result;
- result.reserve(targets->size());
-
- for (const Target* target : *targets) {
- // Make "action" also apply to ACTION_FOREACH.
- if (target->output_type() == type ||
- (type == Target::ACTION &&
- target->output_type() == Target::ACTION_FOREACH))
- result.push_back(target);
- }
-
- *targets = std::move(result);
- return true;
-}
-
-// Returns the file path generating this item.
-base::FilePath BuildFileForItem(const Item* item) {
- return item->defined_from()->GetRange().begin().file()->physical_name();
-}
-
-void PrintTargetsAsBuildfiles(const std::vector<const Target*>& targets,
- base::ListValue* out) {
- // Output the set of unique source files.
- std::set<std::string> unique_files;
- for (const Target* target : targets)
- unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
-
- for (const std::string& file : unique_files) {
- out->AppendString(file);
- }
-}
-
-void PrintTargetsAsLabels(const std::vector<const Target*>& targets,
- base::ListValue* out) {
- // Putting the labels into a set automatically sorts them for us.
- std::set<Label> unique_labels;
- for (auto* target : targets)
- unique_labels.insert(target->label());
-
- // Grab the label of the default toolchain from the first target.
- Label default_tc_label = targets[0]->settings()->default_toolchain_label();
-
- for (const Label& label : unique_labels) {
- // Print toolchain only for ones not in the default toolchain.
- out->AppendString(label.GetUserVisibleName(label.GetToolchainLabel() !=
- default_tc_label));
- }
-}
-
-void PrintTargetsAsOutputs(const std::vector<const Target*>& targets,
- base::ListValue* out) {
- if (targets.empty())
- return;
-
- // Grab the build settings from a random target.
- const BuildSettings* build_settings =
- targets[0]->settings()->build_settings();
-
- for (const Target* target : targets) {
- // Use the link output file if there is one, otherwise fall back to the
- // dependency output file (for actions, for example).
- OutputFile output_file = target->link_output_file();
- if (output_file.value().empty())
- output_file = target->dependency_output_file();
-
- SourceFile output_as_source = output_file.AsSourceFile(build_settings);
- std::string result =
- RebasePath(output_as_source.value(), build_settings->build_dir(),
- build_settings->root_path_utf8());
- out->AppendString(result);
- }
-}
-
-#if defined(OS_WIN)
-// Git bash will remove the first "/" in "//" paths
-// This also happens for labels assigned to command line parameters, e.g.
-// --filters
-// Fix "//" paths, but not absolute and relative paths
-inline std::string FixGitBashLabelEdit(const std::string& label) {
- static std::unique_ptr<base::Environment> git_bash_env;
- if (!git_bash_env)
- git_bash_env = base::Environment::Create();
-
- std::string temp_label(label);
-
- if (git_bash_env->HasVar(
- "MSYSTEM") && // Only for MinGW based shells like Git Bash
- temp_label[0] == '/' && // Only fix for //foo paths, not /f:oo paths
- (temp_label.length() < 2 ||
- (temp_label[1] != '/' &&
- (temp_label.length() < 3 || temp_label[1] != ':'))))
- temp_label.insert(0, "/");
- return temp_label;
-}
-#else
-// Only repair on Windows
-inline std::string FixGitBashLabelEdit(const std::string& label) {
- return label;
-}
-#endif
-
-} // namespace
-
-CommandInfo::CommandInfo()
- : help_short(nullptr), help(nullptr), runner(nullptr) {}
-
-CommandInfo::CommandInfo(const char* in_help_short,
- const char* in_help,
- CommandRunner in_runner)
- : help_short(in_help_short), help(in_help), runner(in_runner) {}
-
-const CommandInfoMap& GetCommands() {
- static CommandInfoMap info_map;
- if (info_map.empty()) {
-#define INSERT_COMMAND(cmd) \
- info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, k##cmd##_Help, &Run##cmd);
-
- INSERT_COMMAND(Analyze)
- INSERT_COMMAND(Args)
- INSERT_COMMAND(Check)
- INSERT_COMMAND(Clean)
- INSERT_COMMAND(Desc)
- INSERT_COMMAND(Gen)
- INSERT_COMMAND(Format)
- INSERT_COMMAND(Help)
- INSERT_COMMAND(Meta)
- INSERT_COMMAND(Ls)
- INSERT_COMMAND(Path)
- INSERT_COMMAND(Refs)
-
-#undef INSERT_COMMAND
- }
- return info_map;
-}
-
-const Target* ResolveTargetFromCommandLineString(
- Setup* setup,
- const std::string& label_string) {
- // Need to resolve the label after we know the default toolchain.
- Label default_toolchain = setup->loader()->default_toolchain_label();
- Value arg_value(nullptr, FixGitBashLabelEdit(label_string));
- Err err;
- Label label = Label::Resolve(
- SourceDirForCurrentDirectory(setup->build_settings().root_path()),
- default_toolchain, arg_value, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return nullptr;
- }
-
- const Item* item = setup->builder().GetItem(label);
- if (!item) {
- Err(Location(), "Label not found.",
- label.GetUserVisibleName(false) + " not found.")
- .PrintToStdout();
- return nullptr;
- }
-
- const Target* target = item->AsTarget();
- if (!target) {
- Err(Location(), "Not a target.",
- "The \"" + label.GetUserVisibleName(false) +
- "\" thing\n"
- "is not a target. Somebody should probably implement this command "
- "for "
- "other\nitem types.")
- .PrintToStdout();
- return nullptr;
- }
-
- return target;
-}
-
-bool ResolveFromCommandLineInput(
- Setup* setup,
- const std::vector<std::string>& input,
- bool all_toolchains,
- UniqueVector<const Target*>* target_matches,
- UniqueVector<const Config*>* config_matches,
- UniqueVector<const Toolchain*>* toolchain_matches,
- UniqueVector<SourceFile>* file_matches) {
- if (input.empty()) {
- Err(Location(), "You need to specify a label, file, or pattern.")
- .PrintToStdout();
- return false;
- }
-
- SourceDir cur_dir =
- SourceDirForCurrentDirectory(setup->build_settings().root_path());
- for (const auto& cur : input) {
- if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur, all_toolchains,
- target_matches, config_matches,
- toolchain_matches, file_matches))
- return false;
- }
- return true;
-}
-
-void FilterTargetsByPatterns(const std::vector<const Target*>& input,
- const std::vector<LabelPattern>& filter,
- std::vector<const Target*>* output) {
- for (auto* target : input) {
- for (const auto& pattern : filter) {
- if (pattern.Matches(target->label())) {
- output->push_back(target);
- break;
- }
- }
- }
-}
-
-void FilterTargetsByPatterns(const std::vector<const Target*>& input,
- const std::vector<LabelPattern>& filter,
- UniqueVector<const Target*>* output) {
- for (auto* target : input) {
- for (const auto& pattern : filter) {
- if (pattern.Matches(target->label())) {
- output->push_back(target);
- break;
- }
- }
- }
-}
-
-bool FilterPatternsFromString(const BuildSettings* build_settings,
- const std::string& label_list_string,
- std::vector<LabelPattern>* filters,
- Err* err) {
- std::vector<std::string> tokens = base::SplitString(
- label_list_string, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- SourceDir root_dir("//");
-
- filters->reserve(tokens.size());
- for (const std::string& token : tokens) {
- LabelPattern pattern = LabelPattern::GetPattern(
- root_dir, Value(nullptr, FixGitBashLabelEdit(token)), err);
- if (err->has_error())
- return false;
- filters->push_back(pattern);
- }
-
- return true;
-}
-
-void FilterAndPrintTargets(std::vector<const Target*>* targets,
- base::ListValue* out) {
- if (targets->empty())
- return;
-
- if (!ApplyTestonlyFilter(targets))
- return;
- if (!ApplyTypeFilter(targets))
- return;
-
- TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
- if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
- return;
- switch (printing_mode) {
- case TARGET_PRINT_BUILDFILE:
- PrintTargetsAsBuildfiles(*targets, out);
- break;
- case TARGET_PRINT_LABEL:
- PrintTargetsAsLabels(*targets, out);
- break;
- case TARGET_PRINT_OUTPUT:
- PrintTargetsAsOutputs(*targets, out);
- break;
- }
-}
-
-void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
- base::ListValue tmp;
- FilterAndPrintTargets(targets, &tmp);
- for (const auto& value : tmp) {
- std::string string;
- value.GetAsString(&string);
- if (indent)
- OutputString(" ");
- OutputString(string);
- OutputString("\n");
- }
-}
-
-void FilterAndPrintTargetSet(bool indent,
- const std::set<const Target*>& targets) {
- std::vector<const Target*> target_vector(targets.begin(), targets.end());
- FilterAndPrintTargets(indent, &target_vector);
-}
-
-void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
- base::ListValue* out) {
- std::vector<const Target*> target_vector(targets.begin(), targets.end());
- FilterAndPrintTargets(&target_vector, out);
-}
-
-} // namespace commands
diff --git a/gn/tools/gn/commands.h b/gn/tools/gn/commands.h
deleted file mode 100644
index d6b672812e4..00000000000
--- a/gn/tools/gn/commands.h
+++ /dev/null
@@ -1,210 +0,0 @@
-// 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 TOOLS_GN_COMMANDS_H_
-#define TOOLS_GN_COMMANDS_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/strings/string_piece.h"
-#include "base/values.h"
-#include "tools/gn/target.h"
-#include "tools/gn/unique_vector.h"
-
-class BuildSettings;
-class Config;
-class LabelPattern;
-class Setup;
-class SourceFile;
-class Target;
-class Toolchain;
-
-// Each "Run" command returns the value we should return from main().
-
-namespace commands {
-
-typedef int (*CommandRunner)(const std::vector<std::string>&);
-
-extern const char kAnalyze[];
-extern const char kAnalyze_HelpShort[];
-extern const char kAnalyze_Help[];
-int RunAnalyze(const std::vector<std::string>& args);
-
-extern const char kArgs[];
-extern const char kArgs_HelpShort[];
-extern const char kArgs_Help[];
-int RunArgs(const std::vector<std::string>& args);
-
-extern const char kCheck[];
-extern const char kCheck_HelpShort[];
-extern const char kCheck_Help[];
-int RunCheck(const std::vector<std::string>& args);
-
-extern const char kClean[];
-extern const char kClean_HelpShort[];
-extern const char kClean_Help[];
-int RunClean(const std::vector<std::string>& args);
-
-extern const char kDesc[];
-extern const char kDesc_HelpShort[];
-extern const char kDesc_Help[];
-int RunDesc(const std::vector<std::string>& args);
-
-extern const char kGen[];
-extern const char kGen_HelpShort[];
-extern const char kGen_Help[];
-int RunGen(const std::vector<std::string>& args);
-
-extern const char kFormat[];
-extern const char kFormat_HelpShort[];
-extern const char kFormat_Help[];
-int RunFormat(const std::vector<std::string>& args);
-
-extern const char kHelp[];
-extern const char kHelp_HelpShort[];
-extern const char kHelp_Help[];
-int RunHelp(const std::vector<std::string>& args);
-
-extern const char kMeta[];
-extern const char kMeta_HelpShort[];
-extern const char kMeta_Help[];
-int RunMeta(const std::vector<std::string>& args);
-
-extern const char kLs[];
-extern const char kLs_HelpShort[];
-extern const char kLs_Help[];
-int RunLs(const std::vector<std::string>& args);
-
-extern const char kPath[];
-extern const char kPath_HelpShort[];
-extern const char kPath_Help[];
-int RunPath(const std::vector<std::string>& args);
-
-extern const char kRefs[];
-extern const char kRefs_HelpShort[];
-extern const char kRefs_Help[];
-int RunRefs(const std::vector<std::string>& args);
-
-// -----------------------------------------------------------------------------
-
-struct CommandInfo {
- CommandInfo();
- CommandInfo(const char* in_help_short,
- const char* in_help,
- CommandRunner in_runner);
-
- const char* help_short;
- const char* help;
- CommandRunner runner;
-};
-
-typedef std::map<base::StringPiece, CommandInfo> CommandInfoMap;
-
-const CommandInfoMap& GetCommands();
-
-// Helper functions for some commands ------------------------------------------
-
-// Given a setup that has already been run and some command-line input,
-// resolves that input as a target label and returns the corresponding target.
-// On failure, returns null and prints the error to the standard output.
-const Target* ResolveTargetFromCommandLineString(
- Setup* setup,
- const std::string& label_string);
-
-// Resolves a vector of command line inputs and figures out the full set of
-// things they resolve to.
-//
-// Patterns with wildcards will only match targets. The file_matches aren't
-// validated that they are real files or referenced by any targets. They're just
-// the set of things that didn't match anything else.
-bool ResolveFromCommandLineInput(
- Setup* setup,
- const std::vector<std::string>& input,
- bool all_toolchains,
- UniqueVector<const Target*>* target_matches,
- UniqueVector<const Config*>* config_matches,
- UniqueVector<const Toolchain*>* toolchain_matches,
- UniqueVector<SourceFile>* file_matches);
-
-// Runs the header checker. All targets in the build should be given in
-// all_targets, and the specific targets to check should be in to_check.
-//
-// force_check, if true, will override targets opting out of header checking
-// with "check_includes = false" and will check them anyway.
-//
-// Generated files are normally not checked since they do not exist
-// unless a build has been run, but passing true for |check_generated|
-// will attempt to check them anyway, assuming they exist.
-//
-// On success, returns true. If the check fails, the error(s) will be printed
-// to stdout and false will be returned.
-bool CheckPublicHeaders(const BuildSettings* build_settings,
- const std::vector<const Target*>& all_targets,
- const std::vector<const Target*>& to_check,
- bool force_check, bool check_generated);
-
-// Filters the given list of targets by the given pattern list.
-void FilterTargetsByPatterns(const std::vector<const Target*>& input,
- const std::vector<LabelPattern>& filter,
- std::vector<const Target*>* output);
-void FilterTargetsByPatterns(const std::vector<const Target*>& input,
- const std::vector<LabelPattern>& filter,
- UniqueVector<const Target*>* output);
-
-// Builds a list of pattern from a semicolon-separated list of labels.
-bool FilterPatternsFromString(const BuildSettings* build_settings,
- const std::string& label_list_string,
- std::vector<LabelPattern>* filters,
- Err* err);
-
-// These are the documentation strings for the command-line flags used by
-// FilterAndPrintTargets. Commands that call that function should incorporate
-// these into their help.
-#define TARGET_PRINTING_MODE_COMMAND_LINE_HELP \
- " --as=(buildfile|label|output)\n" \
- " How to print targets.\n" \
- "\n" \
- " buildfile\n" \
- " Prints the build files where the given target was declared as\n" \
- " file names.\n" \
- " label (default)\n" \
- " Prints the label of the target.\n" \
- " output\n" \
- " Prints the first output file for the target relative to the\n" \
- " root build directory.\n"
-#define TARGET_TYPE_FILTER_COMMAND_LINE_HELP \
- " --type=(action|copy|executable|group|loadable_module|shared_library|\n" \
- " source_set|static_library)\n" \
- " Restrict outputs to targets matching the given type. If\n" \
- " unspecified, no filtering will be performed.\n"
-#define TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP \
- " --testonly=(true|false)\n" \
- " Restrict outputs to targets with the testonly flag set\n" \
- " accordingly. When unspecified, the target's testonly flags are\n" \
- " ignored.\n"
-
-// Applies any testonly and type filters specified on the command line,
-// and prints the targets as specified by the --as command line flag.
-//
-// If indent is true, the results will be indented two spaces.
-//
-// The vector will be modified so that only the printed targets will remain.
-void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets);
-void FilterAndPrintTargets(std::vector<const Target*>* targets,
- base::ListValue* out);
-
-void FilterAndPrintTargetSet(bool indent,
- const std::set<const Target*>& targets);
-void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
- base::ListValue* out);
-
-// Extra help from command_check.cc
-extern const char kNoGnCheck_Help[];
-
-} // namespace commands
-
-#endif // TOOLS_GN_COMMANDS_H_
diff --git a/gn/tools/gn/compile_commands_writer.cc b/gn/tools/gn/compile_commands_writer.cc
deleted file mode 100644
index 208952d253c..00000000000
--- a/gn/tools/gn/compile_commands_writer.cc
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/compile_commands_writer.h"
-
-#include <sstream>
-
-#include "base/json/string_escape.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/c_substitution_type.h"
-#include "tools/gn/c_tool.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/path_output.h"
-#include "tools/gn/substitution_writer.h"
-
-// Structure of JSON output file
-// [
-// {
-// "directory": "The build directory."
-// "file": "The main source file processed by this compilation step.
-// Must be absolute or relative to the above build directory."
-// "command": "The compile command executed."
-// }
-// ...
-// ]
-
-namespace {
-
-#if defined(OS_WIN)
-const char kPrettyPrintLineEnding[] = "\r\n";
-#else
-const char kPrettyPrintLineEnding[] = "\n";
-#endif
-
-struct CompileFlags {
- std::string includes;
- std::string defines;
- std::string cflags;
- std::string cflags_c;
- std::string cflags_cc;
- std::string cflags_objc;
- std::string cflags_objcc;
-};
-
-void SetupCompileFlags(const Target* target,
- PathOutput& path_output,
- EscapeOptions opts,
- CompileFlags& flags) {
- bool has_precompiled_headers =
- target->config_values().has_precompiled_headers();
-
- std::ostringstream defines_out;
- RecursiveTargetConfigToStream<std::string>(
- target, &ConfigValues::defines,
- DefineWriter(ESCAPE_NINJA_PREFORMATTED_COMMAND, true), defines_out);
- base::EscapeJSONString(defines_out.str(), false, &flags.defines);
-
- std::ostringstream includes_out;
- RecursiveTargetConfigToStream<SourceDir>(target, &ConfigValues::include_dirs,
- IncludeWriter(path_output),
- includes_out);
- base::EscapeJSONString(includes_out.str(), false, &flags.includes);
-
- std::ostringstream cflags_out;
- WriteOneFlag(target, &CSubstitutionCFlags, false, Tool::kToolNone,
- &ConfigValues::cflags, opts, path_output, cflags_out,
- /*write_substitution=*/false);
- base::EscapeJSONString(cflags_out.str(), false, &flags.cflags);
-
- std::ostringstream cflags_c_out;
- WriteOneFlag(target, &CSubstitutionCFlagsC, has_precompiled_headers,
- CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output,
- cflags_c_out, /*write_substitution=*/false);
- base::EscapeJSONString(cflags_c_out.str(), false, &flags.cflags_c);
-
- std::ostringstream cflags_cc_out;
- WriteOneFlag(target, &CSubstitutionCFlagsCc, has_precompiled_headers,
- CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output,
- cflags_cc_out, /*write_substitution=*/false);
- base::EscapeJSONString(cflags_cc_out.str(), false, &flags.cflags_cc);
-
- std::ostringstream cflags_objc_out;
- WriteOneFlag(target, &CSubstitutionCFlagsObjC, has_precompiled_headers,
- CTool::kCToolObjC, &ConfigValues::cflags_objc, opts, path_output,
- cflags_objc_out,
- /*write_substitution=*/false);
- base::EscapeJSONString(cflags_objc_out.str(), false, &flags.cflags_objc);
-
- std::ostringstream cflags_objcc_out;
- WriteOneFlag(target, &CSubstitutionCFlagsObjCc, has_precompiled_headers,
- CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
- path_output, cflags_objcc_out, /*write_substitution=*/false);
- base::EscapeJSONString(cflags_objcc_out.str(), false, &flags.cflags_objcc);
-}
-
-void WriteFile(const SourceFile& source,
- PathOutput& path_output,
- std::string* compile_commands) {
- std::ostringstream rel_source_path;
- path_output.WriteFile(rel_source_path, source);
- compile_commands->append(" \"file\": \"");
- compile_commands->append(rel_source_path.str());
-}
-
-void WriteDirectory(std::string build_dir, std::string* compile_commands) {
- compile_commands->append("\",");
- compile_commands->append(kPrettyPrintLineEnding);
- compile_commands->append(" \"directory\": \"");
- compile_commands->append(build_dir);
- compile_commands->append("\",");
-}
-
-void WriteCommand(const Target* target,
- const SourceFile& source,
- const CompileFlags& flags,
- std::vector<OutputFile>& tool_outputs,
- PathOutput& path_output,
- SourceFile::Type source_type,
- const char* tool_name,
- EscapeOptions opts,
- std::string* compile_commands) {
- EscapeOptions no_quoting(opts);
- no_quoting.inhibit_quoting = true;
- const Tool* tool = target->toolchain()->GetTool(tool_name);
- std::ostringstream command_out;
-
- for (const auto& range : tool->command().ranges()) {
- // TODO: this is emitting a bonus space prior to each substitution.
- if (range.type == &SubstitutionLiteral) {
- EscapeStringToStream(command_out, range.literal, no_quoting);
- } else if (range.type == &SubstitutionOutput) {
- path_output.WriteFiles(command_out, tool_outputs);
- } else if (range.type == &CSubstitutionDefines) {
- command_out << flags.defines;
- } else if (range.type == &CSubstitutionIncludeDirs) {
- command_out << flags.includes;
- } else if (range.type == &CSubstitutionCFlags) {
- command_out << flags.cflags;
- } else if (range.type == &CSubstitutionCFlagsC) {
- if (source_type == SourceFile::SOURCE_C)
- command_out << flags.cflags_c;
- } else if (range.type == &CSubstitutionCFlagsCc) {
- if (source_type == SourceFile::SOURCE_CPP)
- command_out << flags.cflags_cc;
- } else if (range.type == &CSubstitutionCFlagsObjC) {
- if (source_type == SourceFile::SOURCE_M)
- command_out << flags.cflags_objc;
- } else if (range.type == &CSubstitutionCFlagsObjCc) {
- if (source_type == SourceFile::SOURCE_MM)
- command_out << flags.cflags_objcc;
- } else if (range.type == &SubstitutionLabel ||
- range.type == &SubstitutionLabelName ||
- range.type == &SubstitutionRootGenDir ||
- range.type == &SubstitutionRootOutDir ||
- range.type == &SubstitutionTargetGenDir ||
- range.type == &SubstitutionTargetOutDir ||
- range.type == &SubstitutionTargetOutputName ||
- range.type == &SubstitutionSource ||
- range.type == &SubstitutionSourceNamePart ||
- range.type == &SubstitutionSourceFilePart ||
- range.type == &SubstitutionSourceDir ||
- range.type == &SubstitutionSourceRootRelativeDir ||
- range.type == &SubstitutionSourceGenDir ||
- range.type == &SubstitutionSourceOutDir ||
- range.type == &SubstitutionSourceTargetRelative) {
- EscapeStringToStream(command_out,
- SubstitutionWriter::GetCompilerSubstitution(
- target, source, range.type),
- opts);
- } else {
- // Other flags shouldn't be relevant to compiling C/C++/ObjC/ObjC++
- // source files.
- NOTREACHED() << "Unsupported substitution for this type of target : "
- << range.type->name;
- continue;
- }
- }
- compile_commands->append(kPrettyPrintLineEnding);
- compile_commands->append(" \"command\": \"");
- compile_commands->append(command_out.str());
-}
-
-} // namespace
-
-void CompileCommandsWriter::RenderJSON(const BuildSettings* build_settings,
- std::vector<const Target*>& all_targets,
- std::string* compile_commands) {
- // TODO: Determine out an appropriate size to reserve.
- compile_commands->reserve(all_targets.size() * 100);
- compile_commands->append("[");
- compile_commands->append(kPrettyPrintLineEnding);
- bool first = true;
- auto build_dir = build_settings->GetFullPath(build_settings->build_dir())
- .StripTrailingSeparators();
- std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
-
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
-
- for (const auto* target : all_targets) {
- if (!target->IsBinary())
- continue;
-
- // Precompute values that are the same for all sources in a target to avoid
- // computing for every source.
-
- PathOutput path_output(
- target->settings()->build_settings()->build_dir(),
- target->settings()->build_settings()->root_path_utf8(),
- ESCAPE_NINJA_COMMAND);
-
- CompileFlags flags;
- SetupCompileFlags(target, path_output, opts, flags);
-
- for (const auto& source : target->sources()) {
- // If this source is not a C/C++/ObjC/ObjC++ source (not header) file,
- // continue as it does not belong in the compilation database.
- SourceFile::Type source_type = source.type();
- if (source_type != SourceFile::SOURCE_CPP &&
- source_type != SourceFile::SOURCE_C &&
- source_type != SourceFile::SOURCE_M &&
- source_type != SourceFile::SOURCE_MM)
- continue;
-
- const char* tool_name = Tool::kToolNone;
- if (!target->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
- continue;
-
- if (!first) {
- compile_commands->append(",");
- compile_commands->append(kPrettyPrintLineEnding);
- }
- first = false;
- compile_commands->append(" {");
- compile_commands->append(kPrettyPrintLineEnding);
-
- WriteFile(source, path_output, compile_commands);
- WriteDirectory(base::StringPrintf("%" PRIsFP, build_dir.value().c_str()),
- compile_commands);
- WriteCommand(target, source, flags, tool_outputs, path_output,
- source_type, tool_name, opts, compile_commands);
- compile_commands->append("\"");
- compile_commands->append(kPrettyPrintLineEnding);
- compile_commands->append(" }");
- }
- }
-
- compile_commands->append(kPrettyPrintLineEnding);
- compile_commands->append("]");
- compile_commands->append(kPrettyPrintLineEnding);
-}
-
-bool CompileCommandsWriter::RunAndWriteFiles(
- const BuildSettings* build_settings,
- const Builder& builder,
- const std::string& file_name,
- const std::string& target_filters,
- bool quiet,
- Err* err) {
- SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
- Value(nullptr, file_name), err);
- if (output_file.is_null())
- return false;
-
- base::FilePath output_path = build_settings->GetFullPath(output_file);
-
- std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
-
- std::set<std::string> target_filters_set;
- for (auto& target :
- base::SplitString(target_filters, ",", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY)) {
- target_filters_set.insert(target);
- }
- std::string json;
- if (target_filters_set.empty()) {
- RenderJSON(build_settings, all_targets, &json);
- } else {
- std::vector<const Target*> preserved_targets =
- FilterTargets(all_targets, target_filters_set);
- RenderJSON(build_settings, preserved_targets, &json);
- }
-
- if (!WriteFileIfChanged(output_path, json, err))
- return false;
- return true;
-}
-
-std::vector<const Target*> CompileCommandsWriter::FilterTargets(
- const std::vector<const Target*>& all_targets,
- const std::set<std::string>& target_filters_set) {
- std::vector<const Target*> preserved_targets;
-
- std::set<const Target*> visited;
- for (auto& target : all_targets) {
- if (target_filters_set.count(target->label().name())) {
- VisitDeps(target, &visited);
- }
- }
-
- preserved_targets.reserve(visited.size());
- // Preserve the original ordering of all_targets
- // to allow easier debugging and testing.
- for (auto& target : all_targets) {
- if (visited.count(target)) {
- preserved_targets.push_back(target);
- }
- }
- return preserved_targets;
-}
-
-void CompileCommandsWriter::VisitDeps(const Target* target,
- std::set<const Target*>* visited) {
- if (!visited->count(target)) {
- visited->insert(target);
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
- VisitDeps(pair.ptr, visited);
- }
- }
-}
diff --git a/gn/tools/gn/compile_commands_writer.h b/gn/tools/gn/compile_commands_writer.h
deleted file mode 100644
index 5c9f0d5b0b0..00000000000
--- a/gn/tools/gn/compile_commands_writer.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
-#define TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
-
-#include "tools/gn/err.h"
-#include "tools/gn/target.h"
-
-class Builder;
-class BuildSettings;
-
-class CompileCommandsWriter {
- public:
- // Write compile commands into a json file located by parameter file_name.
- //
- // Parameter target_filters should be in "target_name1,target_name2..."
- // format. If it is not empty, only targets that are reachable from targets
- // in target_filters are used to generate compile commands.
- //
- // Parameter quiet is not used.
- static bool RunAndWriteFiles(const BuildSettings* build_setting,
- const Builder& builder,
- const std::string& file_name,
- const std::string& target_filters,
- bool quiet,
- Err* err);
- static void RenderJSON(const BuildSettings* build_settings,
- std::vector<const Target*>& all_targets,
- std::string* compile_commands);
- static std::vector<const Target*> FilterTargets(
- const std::vector<const Target*>& all_targets,
- const std::set<std::string>& target_filters_set);
-
- private:
- // This fuction visits the deps graph of a target in a DFS fashion.
- static void VisitDeps(const Target* target, std::set<const Target*>* visited);
-};
-
-#endif // TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
diff --git a/gn/tools/gn/compile_commands_writer_unittest.cc b/gn/tools/gn/compile_commands_writer_unittest.cc
deleted file mode 100644
index a3e3fb5f53b..00000000000
--- a/gn/tools/gn/compile_commands_writer_unittest.cc
+++ /dev/null
@@ -1,647 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/compile_commands_writer.h"
-
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "tools/gn/config.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-namespace {
-
-// InputConversion needs a global scheduler object.
-class CompileCommandsTest : public TestWithScheduler {
- public:
- CompileCommandsTest() = default;
-
- const BuildSettings* build_settings() { return setup_.build_settings(); }
- const Settings* settings() { return setup_.settings(); }
- const TestWithScope& setup() { return setup_; }
- const Toolchain* toolchain() { return setup_.toolchain(); }
-
- private:
- TestWithScope setup_;
-};
-
-} // namespace
-
-TEST_F(CompileCommandsTest, SourceSet) {
- Err err;
-
- std::vector<const Target*> targets;
- Target target(settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- // Also test object files, which should be just passed through to the
- // dependents to link.
- target.sources().push_back(SourceFile("//foo/input3.o"));
- target.sources().push_back(SourceFile("//foo/input4.obj"));
- target.SetToolchain(toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
- targets.push_back(&target);
-
- // Source set itself.
- {
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "obj/foo/bar.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input2.cc -o "
- "obj/foo/bar.input2.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "obj/foo/bar.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input2.cc -o "
- "obj/foo/bar.input2.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(expected, out);
- }
-
- // A shared library that depends on the source set.
- Target shlib_target(settings(), Label(SourceDir("//foo/"), "shlib"));
- shlib_target.sources().push_back(SourceFile("//foo/input3.cc"));
- shlib_target.set_output_type(Target::SHARED_LIBRARY);
- shlib_target.public_deps().push_back(LabelTargetPair(&target));
- shlib_target.SetToolchain(toolchain());
- ASSERT_TRUE(shlib_target.OnResolved(&err));
- targets.push_back(&shlib_target);
-
- {
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "obj/foo/bar.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input2.cc -o "
- "obj/foo/bar.input2.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input3.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input3.cc -o "
- "obj/foo/libshlib.input3.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "obj/foo/bar.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input2.cc -o "
- "obj/foo/bar.input2.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input3.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input3.cc -o "
- "obj/foo/libshlib.input3.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(expected, out);
- }
-
- // A static library that depends on the source set (should not link it).
- Target stlib_target(settings(), Label(SourceDir("//foo/"), "stlib"));
- stlib_target.sources().push_back(SourceFile("//foo/input4.cc"));
- stlib_target.set_output_type(Target::STATIC_LIBRARY);
- stlib_target.public_deps().push_back(LabelTargetPair(&target));
- stlib_target.SetToolchain(toolchain());
- ASSERT_TRUE(stlib_target.OnResolved(&err));
- targets.push_back(&stlib_target);
-
- {
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "obj/foo/bar.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input2.cc -o "
- "obj/foo/bar.input2.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input3.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input3.cc -o "
- "obj/foo/libshlib.input3.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input4.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input4.cc -o "
- "obj/foo/libstlib.input4.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "obj/foo/bar.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input2.cc -o "
- "obj/foo/bar.input2.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input3.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input3.cc -o "
- "obj/foo/libshlib.input3.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input4.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input4.cc -o "
- "obj/foo/libstlib.input4.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(expected, out);
- }
-}
-
-TEST_F(CompileCommandsTest, EscapeDefines) {
- Err err;
-
- std::vector<const Target*> targets;
- TestTarget target(setup(), "//foo:bar", Target::STATIC_LIBRARY);
- target.sources().push_back(SourceFile("//foo/input.cc"));
- target.config_values().defines().push_back("BOOL_DEF");
- target.config_values().defines().push_back("INT_DEF=123");
- target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
- ASSERT_TRUE(target.OnResolved(&err));
- targets.push_back(&target);
-
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
- const char expected[] =
- "-DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\\\\\"ABCD-1\\\\\\\"";
- EXPECT_TRUE(out.find(expected) != std::string::npos);
-}
-
-TEST_F(CompileCommandsTest, WinPrecompiledHeaders) {
- Err err;
-
- // This setup's toolchain does not have precompiled headers defined.
- // A precompiled header toolchain.
- Settings pch_settings(build_settings(), "withpch/");
- Toolchain pch_toolchain(&pch_settings,
- Label(SourceDir("//toolchain/"), "withpch"));
- pch_settings.set_toolchain_label(pch_toolchain.label());
- pch_settings.set_default_toolchain_label(toolchain()->label());
-
- // Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
- CTool* cxx_tool = cxx->AsC();
- TestWithScope::SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool);
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
- pch_toolchain.SetTool(std::move(cxx));
-
- // Add a C compiler as well.
- std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
- CTool* cc_tool = cc->AsC();
- TestWithScope::SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool);
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
- pch_toolchain.SetTool(std::move(cc));
- pch_toolchain.ToolchainSetupComplete();
-
- // This target doesn't specify precompiled headers.
- {
- std::vector<const Target*> targets;
- Target no_pch_target(&pch_settings,
- Label(SourceDir("//foo/"), "no_pch_target"));
- no_pch_target.set_output_type(Target::SOURCE_SET);
- no_pch_target.visibility().SetPublic();
- no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- no_pch_target.config_values().cflags_c().push_back("-std=c99");
- no_pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(no_pch_target.OnResolved(&err));
- targets.push_back(&no_pch_target);
-
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char no_pch_expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.c\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
- "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char no_pch_expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "withpch/obj/foo/no_pch_target.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.c\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
- "withpch/obj/foo/no_pch_target.input2.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(no_pch_expected, out);
- }
-
- // This target specifies PCH.
- {
- std::vector<const Target*> targets;
- Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
- pch_target.config_values().set_precompiled_header("build/precompile.h");
- pch_target.config_values().set_precompiled_source(
- SourceFile("//build/precompile.cc"));
- pch_target.set_output_type(Target::SOURCE_SET);
- pch_target.visibility().SetPublic();
- pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(pch_target.OnResolved(&err));
- targets.push_back(&pch_target);
-
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char pch_win_expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc "
- "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h -o "
- "withpch/obj/foo/pch_target.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.c\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"cc ../../foo/input2.c "
- "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h -o "
- "withpch/obj/foo/pch_target.input2.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char pch_win_expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc "
- "/Fpwithpch/obj/foo/pch_target_cc.pch /Yubuild/precompile.h -o "
- "withpch/obj/foo/pch_target.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.c\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"cc ../../foo/input2.c "
- "/Fpwithpch/obj/foo/pch_target_c.pch /Yubuild/precompile.h -o "
- "withpch/obj/foo/pch_target.input2.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(pch_win_expected, out);
- }
-}
-
-TEST_F(CompileCommandsTest, GCCPrecompiledHeaders) {
- Err err;
-
- // This setup's toolchain does not have precompiled headers defined.
- // A precompiled header toolchain.
- Settings pch_settings(build_settings(), "withpch/");
- Toolchain pch_toolchain(&pch_settings,
- Label(SourceDir("//toolchain/"), "withpch"));
- pch_settings.set_toolchain_label(pch_toolchain.label());
- pch_settings.set_default_toolchain_label(toolchain()->label());
-
- // Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
- CTool* cxx_tool = cxx->AsC();
- TestWithScope::SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool);
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
- pch_toolchain.SetTool(std::move(cxx));
- pch_toolchain.ToolchainSetupComplete();
-
- // Add a C compiler as well.
- std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
- CTool* cc_tool = cc->AsC();
- TestWithScope::SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool);
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
- pch_toolchain.SetTool(std::move(cc));
- pch_toolchain.ToolchainSetupComplete();
-
- // This target doesn't specify precompiled headers.
- {
- std::vector<const Target*> targets;
- Target no_pch_target(&pch_settings,
- Label(SourceDir("//foo/"), "no_pch_target"));
- no_pch_target.set_output_type(Target::SOURCE_SET);
- no_pch_target.visibility().SetPublic();
- no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- no_pch_target.config_values().cflags_c().push_back("-std=c99");
- no_pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(no_pch_target.OnResolved(&err));
- targets.push_back(&no_pch_target);
-
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char no_pch_expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "withpch/obj/foo/no_pch_target.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.c\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
- "withpch/obj/foo/no_pch_target.input2.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char no_pch_expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc -o "
- "withpch/obj/foo/no_pch_target.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.c\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"cc ../../foo/input2.c -std=c99 -o "
- "withpch/obj/foo/no_pch_target.input2.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(no_pch_expected, out);
- }
-
- // This target specifies PCH.
- {
- std::vector<const Target*> targets;
- Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
- pch_target.config_values().set_precompiled_source(
- SourceFile("//build/precompile.h"));
- pch_target.config_values().cflags_c().push_back("-std=c99");
- pch_target.set_output_type(Target::SOURCE_SET);
- pch_target.visibility().SetPublic();
- pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(pch_target.OnResolved(&err));
- targets.push_back(&pch_target);
-
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char pch_gcc_expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.cc\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"c++ ../../foo/input1.cc -include "
- "withpch/obj/build/pch_target.precompile.h-cc -o "
- "withpch/obj/foo/pch_target.input1.o\"\r\n"
- " },\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input2.c\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"cc ../../foo/input2.c -std=c99 -include "
- "withpch/obj/build/pch_target.precompile.h-c -o "
- "withpch/obj/foo/pch_target.input2.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char pch_gcc_expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.cc\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"c++ ../../foo/input1.cc -include "
- "withpch/obj/build/pch_target.precompile.h-cc -o "
- "withpch/obj/foo/pch_target.input1.o\"\n"
- " },\n"
- " {\n"
- " \"file\": \"../../foo/input2.c\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"cc ../../foo/input2.c -std=c99 -include "
- "withpch/obj/build/pch_target.precompile.h-c -o "
- "withpch/obj/foo/pch_target.input2.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(pch_gcc_expected, out);
- }
-}
-
-TEST_F(CompileCommandsTest, EscapedFlags) {
- Err err;
-
- std::vector<const Target*> targets;
- Target target(settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.sources().push_back(SourceFile("//foo/input1.c"));
- target.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
- target.SetToolchain(toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
- targets.push_back(&target);
-
- std::string out;
- CompileCommandsWriter writer;
- writer.RenderJSON(build_settings(), targets, &out);
-
-#if defined(OS_WIN)
- const char expected[] =
- "[\r\n"
- " {\r\n"
- " \"file\": \"../../foo/input1.c\",\r\n"
- " \"directory\": \"out/Debug\",\r\n"
- " \"command\": \"cc ../../foo/input1.c -DCONFIG=\\\"/config\\\" "
- "-o obj/foo/bar.input1.o\"\r\n"
- " }\r\n"
- "]\r\n";
-#else
- const char expected[] =
- "[\n"
- " {\n"
- " \"file\": \"../../foo/input1.c\",\n"
- " \"directory\": \"out/Debug\",\n"
- " \"command\": \"cc ../../foo/input1.c -DCONFIG=\\\"/config\\\" "
- "-o obj/foo/bar.input1.o\"\n"
- " }\n"
- "]\n";
-#endif
- EXPECT_EQ(expected, out);
-}
-
-TEST_F(CompileCommandsTest, CompDBFilter) {
- Err err;
-
- std::vector<const Target*> targets;
- Target target1(settings(), Label(SourceDir("//foo/"), "bar1"));
- target1.set_output_type(Target::SOURCE_SET);
- target1.sources().push_back(SourceFile("//foo/input1.c"));
- target1.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
- target1.SetToolchain(toolchain());
- ASSERT_TRUE(target1.OnResolved(&err));
- targets.push_back(&target1);
-
- Target target2(settings(), Label(SourceDir("//foo/"), "bar2"));
- target2.set_output_type(Target::SOURCE_SET);
- target2.sources().push_back(SourceFile("//foo/input2.c"));
- target2.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
- target2.SetToolchain(toolchain());
- ASSERT_TRUE(target2.OnResolved(&err));
- targets.push_back(&target2);
-
- Target target3(settings(), Label(SourceDir("//foo/"), "bar3"));
- target3.set_output_type(Target::SOURCE_SET);
- target3.sources().push_back(SourceFile("//foo/input3.c"));
- target3.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
- target3.SetToolchain(toolchain());
- ASSERT_TRUE(target3.OnResolved(&err));
- targets.push_back(&target3);
-
- target1.private_deps().push_back(LabelTargetPair(&target2));
- target1.private_deps().push_back(LabelTargetPair(&target3));
-
- CompileCommandsWriter writer;
-
- std::set<std::string> filter1;
- std::vector<const Target*> test_results1 =
- writer.FilterTargets(targets, filter1);
- ASSERT_TRUE(test_results1.empty());
-
- std::set<std::string> filter2;
- filter2.insert(target1.label().name());
- std::vector<const Target*> test_results2 =
- writer.FilterTargets(targets, filter2);
- ASSERT_EQ(test_results2, targets);
-
- std::set<std::string> filter3;
- filter3.insert(target2.label().name());
- std::vector<const Target*> test_result3 =
- writer.FilterTargets(targets, filter3);
- std::vector<const Target*> expected_results3;
- expected_results3.push_back(&target2);
- ASSERT_EQ(test_result3, expected_results3);
-}
diff --git a/gn/tools/gn/config.cc b/gn/tools/gn/config.cc
deleted file mode 100644
index e28fd30ed68..00000000000
--- a/gn/tools/gn/config.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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 "tools/gn/config.h"
-
-#include "tools/gn/err.h"
-#include "tools/gn/input_file_manager.h"
-#include "tools/gn/scheduler.h"
-
-Config::Config(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files)
- : Item(settings, label, build_dependency_files) {}
-
-Config::~Config() = default;
-
-Config* Config::AsConfig() {
- return this;
-}
-
-const Config* Config::AsConfig() const {
- return this;
-}
-
-bool Config::OnResolved(Err* err) {
- DCHECK(!resolved_);
- resolved_ = true;
-
- if (!configs_.empty()) {
- // Subconfigs, flatten.
- //
- // Implementation note for the future: Flattening these here means we
- // lose the ability to de-dupe subconfigs. If a subconfig is listed as
- // a separate config or a subconfig that also applies to the target, the
- // subconfig's flags will be duplicated.
- //
- // If we want to be able to de-dupe these, here's one idea. As a config is
- // resolved, inline any sub-sub configs so the configs_ vector is a flat
- // list, much the same way that libs and lib_dirs are pushed through
- // targets. Do the same for Target.configs_ when a target is resolved. This
- // will naturally de-dupe and also prevents recursive config walking to
- // compute every possible flag, although it will expand the configs list on
- // a target nontrivially (depending on build configuration).
- composite_values_ = own_values_;
- for (const auto& pair : configs_)
- composite_values_.AppendValues(pair.ptr->resolved_values());
- }
- return true;
-}
diff --git a/gn/tools/gn/config.h b/gn/tools/gn/config.h
deleted file mode 100644
index 040d54efd7d..00000000000
--- a/gn/tools/gn/config.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 TOOLS_GN_CONFIG_H_
-#define TOOLS_GN_CONFIG_H_
-
-#include <set>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/config_values.h"
-#include "tools/gn/item.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/unique_vector.h"
-
-// Represents a named config in the dependency graph.
-//
-// A config can list other configs. We track both the data assigned directly
-// on the config, this list of sub-configs, and (when the config is resolved)
-// the resulting values of everything merged together. The flatten step
-// means we can avoid doing a recursive config walk for every target to compute
-// flags.
-class Config : public Item {
- public:
- // We track the set of build files that may affect this config, please refer
- // to Scope for how this is determined.
- Config(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files = {});
- ~Config() override;
-
- // Item implementation.
- Config* AsConfig() override;
- const Config* AsConfig() const override;
- bool OnResolved(Err* err) override;
-
- // The values set directly on this config. This will not contain data from
- // sub-configs.
- ConfigValues& own_values() { return own_values_; }
- const ConfigValues& own_values() const { return own_values_; }
-
- // The values that represent this config and all sub-configs combined into
- // one. This is only valid after the config is resolved (when we know the
- // contents of the sub-configs).
- const ConfigValues& resolved_values() const {
- DCHECK(resolved_);
- if (configs_.empty()) // No sub configs, just use the regular values.
- return own_values_;
- return composite_values_;
- }
-
- // List of sub-configs.
- const UniqueVector<LabelConfigPair>& configs() const { return configs_; }
- UniqueVector<LabelConfigPair>& configs() { return configs_; }
-
- private:
- ConfigValues own_values_;
-
- // Contains the own_values combined with sub-configs. Most configs don't have
- // sub-configs. So as an optimization, this is not populated if there are no
- // items in configs_. The resolved_values() getter handles this.
- bool resolved_ = false;
- ConfigValues composite_values_;
-
- UniqueVector<LabelConfigPair> configs_;
-
- DISALLOW_COPY_AND_ASSIGN(Config);
-};
-
-#endif // TOOLS_GN_CONFIG_H_
diff --git a/gn/tools/gn/config_unittest.cc b/gn/tools/gn/config_unittest.cc
deleted file mode 100644
index 4d8a41f8c5d..00000000000
--- a/gn/tools/gn/config_unittest.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 "tools/gn/config.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-// Tests that the "resolved" values are the same as "own" values when there
-// are no subconfigs.
-TEST(Config, ResolvedNoSub) {
- TestWithScope setup;
- Err err;
-
- Config config(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- config.own_values().defines().push_back("FOO");
- ASSERT_TRUE(config.OnResolved(&err));
-
- // The resolved values should be the same as the value we put in to
- // own_values().
- ASSERT_EQ(1u, config.resolved_values().defines().size());
- EXPECT_EQ("FOO", config.resolved_values().defines()[0]);
-
- // As an optimization, the string should actually refer to the original. This
- // isn't required to pass for semantic correctness, though.
- EXPECT_TRUE(&config.own_values() == &config.resolved_values());
-}
-
-// Tests that subconfigs are resolved in the correct order.
-TEST(Config, ResolvedSub) {
- TestWithScope setup;
- Err err;
-
- Config sub1(setup.settings(), Label(SourceDir("//foo/"), "1"));
- sub1.own_values().defines().push_back("ONE");
- ASSERT_TRUE(sub1.OnResolved(&err));
-
- Config sub2(setup.settings(), Label(SourceDir("//foo/"), "2"));
- sub2.own_values().defines().push_back("TWO");
- ASSERT_TRUE(sub2.OnResolved(&err));
-
- Config config(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- config.own_values().defines().push_back("FOO");
- config.configs().push_back(LabelConfigPair(&sub1));
- config.configs().push_back(LabelConfigPair(&sub2));
- ASSERT_TRUE(config.OnResolved(&err));
-
- // The resolved values should be the same as the value we put in to
- // own_values().
- ASSERT_EQ(3u, config.resolved_values().defines().size());
- EXPECT_EQ("FOO", config.resolved_values().defines()[0]);
- EXPECT_EQ("ONE", config.resolved_values().defines()[1]);
- EXPECT_EQ("TWO", config.resolved_values().defines()[2]);
-
- // The "own" values should be unchanged.
- ASSERT_EQ(1u, config.own_values().defines().size());
- EXPECT_EQ("FOO", config.own_values().defines()[0]);
-}
-
-// Tests that subconfigs of subconfigs are resolved properly.
-TEST(Config, SubSub) {
- TestWithScope setup;
- Err err;
-
- // Set up first -> middle -> last configs.
- Config last(setup.settings(), Label(SourceDir("//foo/"), "last"));
- last.own_values().defines().push_back("LAST");
- ASSERT_TRUE(last.OnResolved(&err));
-
- Config middle(setup.settings(), Label(SourceDir("//foo/"), "middle"));
- middle.own_values().defines().push_back("MIDDLE");
- middle.configs().push_back(LabelConfigPair(&last));
- ASSERT_TRUE(middle.OnResolved(&err));
-
- Config first(setup.settings(), Label(SourceDir("//foo/"), "first"));
- first.own_values().defines().push_back("FIRST");
- first.configs().push_back(LabelConfigPair(&middle));
- ASSERT_TRUE(first.OnResolved(&err));
-
- // Check final resolved defines on "first".
- ASSERT_EQ(3u, first.resolved_values().defines().size());
- EXPECT_EQ("FIRST", first.resolved_values().defines()[0]);
- EXPECT_EQ("MIDDLE", first.resolved_values().defines()[1]);
- EXPECT_EQ("LAST", first.resolved_values().defines()[2]);
-}
diff --git a/gn/tools/gn/config_values.cc b/gn/tools/gn/config_values.cc
deleted file mode 100644
index 85de7e4b6e4..00000000000
--- a/gn/tools/gn/config_values.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 "tools/gn/config_values.h"
-
-namespace {
-
-template <typename T>
-void VectorAppend(std::vector<T>* append_to,
- const std::vector<T>& append_this) {
- if (append_this.empty())
- return;
- append_to->insert(append_to->end(), append_this.begin(), append_this.end());
-}
-
-} // namespace
-
-ConfigValues::ConfigValues() = default;
-
-ConfigValues::~ConfigValues() = default;
-
-void ConfigValues::AppendValues(const ConfigValues& append) {
- VectorAppend(&asmflags_, append.asmflags_);
- VectorAppend(&arflags_, append.arflags_);
- VectorAppend(&cflags_, append.cflags_);
- VectorAppend(&cflags_c_, append.cflags_c_);
- VectorAppend(&cflags_cc_, append.cflags_cc_);
- VectorAppend(&cflags_objc_, append.cflags_objc_);
- VectorAppend(&cflags_objcc_, append.cflags_objcc_);
- VectorAppend(&defines_, append.defines_);
- VectorAppend(&include_dirs_, append.include_dirs_);
- VectorAppend(&inputs_, append.inputs_);
- VectorAppend(&ldflags_, append.ldflags_);
- VectorAppend(&lib_dirs_, append.lib_dirs_);
- VectorAppend(&libs_, append.libs_);
- VectorAppend(&rustflags_, append.rustflags_);
- VectorAppend(&rustenv_, append.rustenv_);
-
- // Only append precompiled header if there isn't one. It might be nice to
- // throw an error if there are conflicting precompiled headers, but that
- // requires piping through some context of the actual configs involved, and
- // conflicts here should be very unusual. Instead, use the first value.
- if (!append.precompiled_header_.empty() && !precompiled_header_.empty())
- precompiled_header_ = append.precompiled_header_;
- if (!append.precompiled_source_.is_null() && !precompiled_source_.is_null())
- precompiled_source_ = append.precompiled_source_;
-}
diff --git a/gn/tools/gn/config_values.h b/gn/tools/gn/config_values.h
deleted file mode 100644
index 86eea6cf3bc..00000000000
--- a/gn/tools/gn/config_values.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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 TOOLS_GN_CONFIG_VALUES_H_
-#define TOOLS_GN_CONFIG_VALUES_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "tools/gn/lib_file.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-
-// Holds the values (include_dirs, defines, compiler flags, etc.) for a given
-// config or target.
-class ConfigValues {
- public:
- ConfigValues();
- ~ConfigValues();
-
- // Appends the values from the given config to this one.
- void AppendValues(const ConfigValues& append);
-
-#define STRING_VALUES_ACCESSOR(name) \
- const std::vector<std::string>& name() const { return name##_; } \
- std::vector<std::string>& name() { return name##_; }
-#define DIR_VALUES_ACCESSOR(name) \
- const std::vector<SourceDir>& name() const { return name##_; } \
- std::vector<SourceDir>& name() { return name##_; }
-
- // =================================================================
- // IMPORTANT: If you add a new one, be sure to update AppendValues()
- // and command_desc.cc.
- // =================================================================
- STRING_VALUES_ACCESSOR(arflags)
- STRING_VALUES_ACCESSOR(asmflags)
- STRING_VALUES_ACCESSOR(cflags)
- STRING_VALUES_ACCESSOR(cflags_c)
- STRING_VALUES_ACCESSOR(cflags_cc)
- STRING_VALUES_ACCESSOR(cflags_objc)
- STRING_VALUES_ACCESSOR(cflags_objcc)
- STRING_VALUES_ACCESSOR(defines)
- DIR_VALUES_ACCESSOR(include_dirs)
- STRING_VALUES_ACCESSOR(ldflags)
- DIR_VALUES_ACCESSOR(lib_dirs)
- STRING_VALUES_ACCESSOR(rustflags)
- STRING_VALUES_ACCESSOR(rustenv)
- // =================================================================
- // IMPORTANT: If you add a new one, be sure to update AppendValues()
- // and command_desc.cc.
- // =================================================================
-
-#undef STRING_VALUES_ACCESSOR
-#undef DIR_VALUES_ACCESSOR
-
- const std::vector<SourceFile>& inputs() const { return inputs_; }
- std::vector<SourceFile>& inputs() { return inputs_; }
-
- const std::vector<LibFile>& libs() const { return libs_; }
- std::vector<LibFile>& libs() { return libs_; }
-
- bool has_precompiled_headers() const {
- return !precompiled_header_.empty() || !precompiled_source_.is_null();
- }
- const std::string& precompiled_header() const { return precompiled_header_; }
- void set_precompiled_header(const std::string& f) { precompiled_header_ = f; }
- const SourceFile& precompiled_source() const { return precompiled_source_; }
- void set_precompiled_source(const SourceFile& f) { precompiled_source_ = f; }
-
- private:
- std::vector<std::string> arflags_;
- std::vector<std::string> asmflags_;
- std::vector<std::string> cflags_;
- std::vector<std::string> cflags_c_;
- std::vector<std::string> cflags_cc_;
- std::vector<std::string> cflags_objc_;
- std::vector<std::string> cflags_objcc_;
- std::vector<std::string> defines_;
- std::vector<SourceDir> include_dirs_;
- std::vector<SourceFile> inputs_;
- std::vector<std::string> ldflags_;
- std::vector<SourceDir> lib_dirs_;
- std::vector<LibFile> libs_;
- std::vector<std::string> rustflags_;
- std::vector<std::string> rustenv_;
- // If you add a new one, be sure to update AppendValues().
-
- std::string precompiled_header_;
- SourceFile precompiled_source_;
-};
-
-#endif // TOOLS_GN_CONFIG_VALUES_H_
diff --git a/gn/tools/gn/config_values_extractors.cc b/gn/tools/gn/config_values_extractors.cc
deleted file mode 100644
index 248d8c47c88..00000000000
--- a/gn/tools/gn/config_values_extractors.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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 "tools/gn/config_values_extractors.h"
-
-#include "tools/gn/escape.h"
-
-namespace {
-
-class EscapedStringWriter {
- public:
- explicit EscapedStringWriter(const EscapeOptions& escape_options)
- : escape_options_(escape_options) {}
-
- void operator()(const std::string& s, std::ostream& out) const {
- out << " ";
- EscapeStringToStream(out, s, escape_options_);
- }
-
- private:
- const EscapeOptions& escape_options_;
-};
-
-} // namespace
-
-void RecursiveTargetConfigStringsToStream(
- const Target* target,
- const std::vector<std::string>& (ConfigValues::*getter)() const,
- const EscapeOptions& escape_options,
- std::ostream& out) {
- RecursiveTargetConfigToStream(target, getter,
- EscapedStringWriter(escape_options), out);
-}
diff --git a/gn/tools/gn/config_values_extractors.h b/gn/tools/gn/config_values_extractors.h
deleted file mode 100644
index cbcbed5d728..00000000000
--- a/gn/tools/gn/config_values_extractors.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// 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 TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
-#define TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
-
-#include <stddef.h>
-
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "tools/gn/config.h"
-#include "tools/gn/config_values.h"
-#include "tools/gn/target.h"
-
-struct EscapeOptions;
-
-// Provides a way to iterate through all ConfigValues applying to a given
-// target. This is more complicated than normal because the target has a list
-// of configs applying to it, and also config values on the target itself.
-//
-// This iterator allows one to iterate through all of these in a defined order
-// in one convenient loop. The order is defined to be the ConfigValues on the
-// target itself first, then the applying configs, in order.
-//
-// Example:
-// for (ConfigValueIterator iter(target); !iter.done(); iter.Next())
-// DoSomething(iter.cur());
-class ConfigValuesIterator {
- public:
- explicit ConfigValuesIterator(const Target* target) : target_(target) {}
-
- bool done() const {
- return cur_index_ >= static_cast<int>(target_->configs().size());
- }
-
- const ConfigValues& cur() const {
- if (cur_index_ == -1)
- return target_->config_values();
- return target_->configs()[cur_index_].ptr->resolved_values();
- }
-
- // Returns the origin of who added this config, if any. This will always be
- // null for the config values of a target itself.
- const ParseNode* origin() const {
- if (cur_index_ == -1)
- return nullptr;
- return target_->configs()[cur_index_].origin;
- }
-
- void Next() { cur_index_++; }
-
- // Returns the config holding the current config values, or NULL for those
- // config values associated with the target itself.
- const Config* GetCurrentConfig() const {
- if (cur_index_ == -1)
- return nullptr;
- return target_->configs()[cur_index_].ptr;
- }
-
- private:
- const Target* target_;
-
- // Represents an index into the target_'s configs() or, when -1, the config
- // values on the target itself.
- int cur_index_ = -1;
-};
-
-template <typename T, class Writer>
-inline void ConfigValuesToStream(const ConfigValues& values,
- const std::vector<T>& (ConfigValues::*getter)()
- const,
- const Writer& writer,
- std::ostream& out) {
- const std::vector<T>& v = (values.*getter)();
- for (size_t i = 0; i < v.size(); i++)
- writer(v[i], out);
-}
-
-// Writes a given config value that applies to a given target. This collects
-// all values from the target itself and all configs that apply, and writes
-// then in order.
-template <typename T, class Writer>
-inline void RecursiveTargetConfigToStream(
- const Target* target,
- const std::vector<T>& (ConfigValues::*getter)() const,
- const Writer& writer,
- std::ostream& out) {
- for (ConfigValuesIterator iter(target); !iter.done(); iter.Next())
- ConfigValuesToStream(iter.cur(), getter, writer, out);
-}
-
-// Writes the values out as strings with no transformation.
-void RecursiveTargetConfigStringsToStream(
- const Target* target,
- const std::vector<std::string>& (ConfigValues::*getter)() const,
- const EscapeOptions& escape_options,
- std::ostream& out);
-
-#endif // TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
diff --git a/gn/tools/gn/config_values_extractors_unittest.cc b/gn/tools/gn/config_values_extractors_unittest.cc
deleted file mode 100644
index 5cfca79bced..00000000000
--- a/gn/tools/gn/config_values_extractors_unittest.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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 <sstream>
-
-#include "tools/gn/config.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-struct FlagWriter {
- void operator()(const std::string& dir, std::ostream& out) const {
- out << dir << " ";
- }
-};
-
-struct IncludeWriter {
- void operator()(const SourceDir& dir, std::ostream& out) const {
- out << dir.value() << " ";
- }
-};
-
-} // namespace
-
-TEST(ConfigValuesExtractors, IncludeOrdering) {
- TestWithScope setup;
- Err err;
-
- // Construct a chain of dependencies: target -> dep1 -> dep2
- // Add representative values: cflags (opaque, always copied) and include_dirs
- // (uniquified) to each one so we can check what comes out the other end.
-
- // Set up dep2, direct and all dependent configs.
- Config dep2_all(setup.settings(), Label(SourceDir("//dep2/"), "all"));
- dep2_all.own_values().cflags().push_back("--dep2-all");
- dep2_all.own_values().include_dirs().push_back(SourceDir("//dep2/all/"));
- ASSERT_TRUE(dep2_all.OnResolved(&err));
-
- Config dep2_direct(setup.settings(), Label(SourceDir("//dep2/"), "direct"));
- dep2_direct.own_values().cflags().push_back("--dep2-direct");
- dep2_direct.own_values().include_dirs().push_back(
- SourceDir("//dep2/direct/"));
- ASSERT_TRUE(dep2_direct.OnResolved(&err));
-
- Target dep2(setup.settings(), Label(SourceDir("//dep2/"), "dep2"));
- dep2.set_output_type(Target::SOURCE_SET);
- dep2.visibility().SetPublic();
- dep2.SetToolchain(setup.toolchain());
- dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all));
- dep2.public_configs().push_back(LabelConfigPair(&dep2_direct));
-
- // Set up dep1, direct and all dependent configs. Also set up a subconfig
- // on "dep1_all" to test sub configs.
- Config dep1_all_sub(setup.settings(), Label(SourceDir("//dep1"), "allch"));
- dep1_all_sub.own_values().cflags().push_back("--dep1-all-sub");
- ASSERT_TRUE(dep1_all_sub.OnResolved(&err));
-
- Config dep1_all(setup.settings(), Label(SourceDir("//dep1/"), "all"));
- dep1_all.own_values().cflags().push_back("--dep1-all");
- dep1_all.own_values().include_dirs().push_back(SourceDir("//dep1/all/"));
- dep1_all.configs().push_back(LabelConfigPair(&dep1_all_sub));
- ASSERT_TRUE(dep1_all.OnResolved(&err));
-
- Config dep1_direct(setup.settings(), Label(SourceDir("//dep1/"), "direct"));
- dep1_direct.own_values().cflags().push_back("--dep1-direct");
- dep1_direct.own_values().include_dirs().push_back(
- SourceDir("//dep1/direct/"));
- ASSERT_TRUE(dep1_direct.OnResolved(&err));
-
- Target dep1(setup.settings(), Label(SourceDir("//dep1/"), "dep1"));
- dep1.set_output_type(Target::SOURCE_SET);
- dep1.visibility().SetPublic();
- dep1.SetToolchain(setup.toolchain());
- dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all));
- dep1.public_configs().push_back(LabelConfigPair(&dep1_direct));
- dep1.private_deps().push_back(LabelTargetPair(&dep2));
-
- // Set up target, direct and all dependent configs.
- Config target_all(setup.settings(), Label(SourceDir("//target/"), "all"));
- target_all.own_values().cflags().push_back("--target-all");
- target_all.own_values().include_dirs().push_back(SourceDir("//target/all/"));
- ASSERT_TRUE(target_all.OnResolved(&err));
-
- Config target_direct(setup.settings(),
- Label(SourceDir("//target/"), "direct"));
- target_direct.own_values().cflags().push_back("--target-direct");
- target_direct.own_values().include_dirs().push_back(
- SourceDir("//target/direct/"));
- ASSERT_TRUE(target_direct.OnResolved(&err));
-
- // This config is applied directly to target.
- Config target_config(setup.settings(),
- Label(SourceDir("//target/"), "config"));
- target_config.own_values().cflags().push_back("--target-config");
- target_config.own_values().include_dirs().push_back(
- SourceDir("//target/config/"));
- ASSERT_TRUE(target_config.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//target/"), "target"));
- target.set_output_type(Target::SOURCE_SET);
- target.SetToolchain(setup.toolchain());
- target.all_dependent_configs().push_back(LabelConfigPair(&target_all));
- target.public_configs().push_back(LabelConfigPair(&target_direct));
- target.configs().push_back(LabelConfigPair(&target_config));
- target.private_deps().push_back(LabelTargetPair(&dep1));
-
- // Additionally add some values directly on "target".
- target.config_values().cflags().push_back("--target");
- target.config_values().include_dirs().push_back(SourceDir("//target/"));
-
- // Mark targets resolved. This should push dependent configs.
- ASSERT_TRUE(dep2.OnResolved(&err));
- ASSERT_TRUE(dep1.OnResolved(&err));
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Verify cflags by serializing.
- std::ostringstream flag_out;
- FlagWriter flag_writer;
- RecursiveTargetConfigToStream<std::string, FlagWriter>(
- &target, &ConfigValues::cflags, flag_writer, flag_out);
- EXPECT_EQ(flag_out.str(),
- "--target --target-config --target-all --target-direct "
- "--dep1-all --dep1-all-sub --dep2-all --dep1-direct ");
-
- // Verify include dirs by serializing.
- std::ostringstream include_out;
- IncludeWriter include_writer;
- RecursiveTargetConfigToStream<SourceDir, IncludeWriter>(
- &target, &ConfigValues::include_dirs, include_writer, include_out);
- EXPECT_EQ(include_out.str(),
- "//target/ //target/config/ //target/all/ //target/direct/ "
- "//dep1/all/ //dep2/all/ //dep1/direct/ ");
-}
diff --git a/gn/tools/gn/config_values_generator.cc b/gn/tools/gn/config_values_generator.cc
deleted file mode 100644
index 5947274bbaa..00000000000
--- a/gn/tools/gn/config_values_generator.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-// 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 "tools/gn/config_values_generator.h"
-
-#include "base/strings/string_util.h"
-#include "tools/gn/config_values.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/value.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-namespace {
-
-void GetStringList(Scope* scope,
- const char* var_name,
- ConfigValues* config_values,
- std::vector<std::string>& (ConfigValues::*accessor)(),
- Err* err) {
- const Value* value = scope->GetValue(var_name, true);
- if (!value)
- return; // No value, empty input and succeed.
-
- ExtractListOfStringValues(*value, &(config_values->*accessor)(), err);
-}
-
-void GetDirList(Scope* scope,
- const char* var_name,
- ConfigValues* config_values,
- const SourceDir input_dir,
- std::vector<SourceDir>& (ConfigValues::*accessor)(),
- Err* err) {
- const Value* value = scope->GetValue(var_name, true);
- if (!value)
- return; // No value, empty input and succeed.
-
- std::vector<SourceDir> result;
- ExtractListOfRelativeDirs(scope->settings()->build_settings(), *value,
- input_dir, &result, err);
- (config_values->*accessor)().swap(result);
-}
-
-} // namespace
-
-ConfigValuesGenerator::ConfigValuesGenerator(ConfigValues* dest_values,
- Scope* scope,
- const SourceDir& input_dir,
- Err* err)
- : config_values_(dest_values),
- scope_(scope),
- input_dir_(input_dir),
- err_(err) {}
-
-ConfigValuesGenerator::~ConfigValuesGenerator() = default;
-
-void ConfigValuesGenerator::Run() {
-#define FILL_STRING_CONFIG_VALUE(name) \
- GetStringList(scope_, #name, config_values_, &ConfigValues::name, err_);
-#define FILL_DIR_CONFIG_VALUE(name) \
- GetDirList(scope_, #name, config_values_, input_dir_, &ConfigValues::name, \
- err_);
-
- FILL_STRING_CONFIG_VALUE(arflags)
- FILL_STRING_CONFIG_VALUE(asmflags)
- FILL_STRING_CONFIG_VALUE(cflags)
- FILL_STRING_CONFIG_VALUE(cflags_c)
- FILL_STRING_CONFIG_VALUE(cflags_cc)
- FILL_STRING_CONFIG_VALUE(cflags_objc)
- FILL_STRING_CONFIG_VALUE(cflags_objcc)
- FILL_STRING_CONFIG_VALUE(defines)
- FILL_DIR_CONFIG_VALUE(include_dirs)
- FILL_STRING_CONFIG_VALUE(ldflags)
- FILL_DIR_CONFIG_VALUE(lib_dirs)
- FILL_STRING_CONFIG_VALUE(rustflags)
- FILL_STRING_CONFIG_VALUE(rustenv)
-
-#undef FILL_STRING_CONFIG_VALUE
-#undef FILL_DIR_CONFIG_VALUE
-
- // Inputs
- const Value* inputs_value = scope_->GetValue(variables::kInputs, true);
- if (inputs_value) {
- ExtractListOfRelativeFiles(scope_->settings()->build_settings(),
- *inputs_value, input_dir_,
- &config_values_->inputs(), err_);
- }
-
- // Libs
- const Value* libs_value = scope_->GetValue("libs", true);
- if (libs_value) {
- ExtractListOfLibs(scope_->settings()->build_settings(), *libs_value,
- input_dir_, &config_values_->libs(), err_);
- }
-
- // Precompiled headers.
- const Value* precompiled_header_value =
- scope_->GetValue(variables::kPrecompiledHeader, true);
- if (precompiled_header_value) {
- if (!precompiled_header_value->VerifyTypeIs(Value::STRING, err_))
- return;
-
- // Check for common errors. This is a string and not a file.
- const std::string& pch_string = precompiled_header_value->string_value();
- if (base::StartsWith(pch_string, "//", base::CompareCase::SENSITIVE)) {
- *err_ = Err(
- *precompiled_header_value, "This precompiled_header value is wrong.",
- "You need to specify a string that the compiler will match against\n"
- "the #include lines rather than a GN-style file name.\n");
- return;
- }
- config_values_->set_precompiled_header(pch_string);
- }
-
- const Value* precompiled_source_value =
- scope_->GetValue(variables::kPrecompiledSource, true);
- if (precompiled_source_value) {
- config_values_->set_precompiled_source(input_dir_.ResolveRelativeFile(
- *precompiled_source_value, err_,
- scope_->settings()->build_settings()->root_path_utf8()));
- if (err_->has_error())
- return;
- }
-}
diff --git a/gn/tools/gn/config_values_generator.h b/gn/tools/gn/config_values_generator.h
deleted file mode 100644
index e6639912db0..00000000000
--- a/gn/tools/gn/config_values_generator.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
-#define TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/source_dir.h"
-
-class ConfigValues;
-class Err;
-class Scope;
-
-// This class fills in the config values from a given scope. It's shared
-// between the "config" function call and all the different binary target types
-// (shared library, static library, etc.) since all of these support the
-// various flags stored in the ConfigValues class.
-class ConfigValuesGenerator {
- public:
- ConfigValuesGenerator(ConfigValues* dest_values,
- Scope* scope,
- const SourceDir& input_dir,
- Err* err);
- ~ConfigValuesGenerator();
-
- // Sets the error passed to the constructor on failure.
- void Run();
-
- private:
- ConfigValues* config_values_;
- Scope* scope_;
- const SourceDir input_dir_;
- Err* err_;
-
- DISALLOW_COPY_AND_ASSIGN(ConfigValuesGenerator);
-};
-
-// For using in documentation for functions which use this.
-#define CONFIG_VALUES_VARS_HELP \
- " Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,\n" \
- " asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,\n" \
- " libs, precompiled_header, precompiled_source, rustflags,\n" \
- " rustenv\n"
-
-#endif // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/gn/tools/gn/copy_target_generator.cc b/gn/tools/gn/copy_target_generator.cc
deleted file mode 100644
index 4e40efb22fd..00000000000
--- a/gn/tools/gn/copy_target_generator.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 "tools/gn/copy_target_generator.h"
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/value.h"
-
-CopyTargetGenerator::CopyTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err)
- : TargetGenerator(target, scope, function_call, err) {}
-
-CopyTargetGenerator::~CopyTargetGenerator() = default;
-
-void CopyTargetGenerator::DoRun() {
- target_->set_output_type(Target::COPY_FILES);
-
- if (!FillSources())
- return;
- if (!FillOutputs(true))
- return;
-
- if (target_->sources().empty()) {
- *err_ = Err(
- function_call_, "Empty sources for copy command.",
- "You have to specify at least one file to copy in the \"sources\".");
- return;
- }
- if (target_->action_values().outputs().list().size() != 1) {
- *err_ = Err(
- function_call_, "Copy command must have exactly one output.",
- "You must specify exactly one value in the \"outputs\" array for the "
- "destination of the copy\n(see \"gn help copy\"). If there are "
- "multiple sources to copy, use source expansion\n(see \"gn help "
- "source_expansion\").");
- return;
- }
-}
diff --git a/gn/tools/gn/copy_target_generator.h b/gn/tools/gn/copy_target_generator.h
deleted file mode 100644
index 89751f98e83..00000000000
--- a/gn/tools/gn/copy_target_generator.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 TOOLS_GN_COPY_TARGET_GENERATOR_H_
-#define TOOLS_GN_COPY_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target_generator.h"
-
-// Populates a Target with the values from a copy rule.
-class CopyTargetGenerator : public TargetGenerator {
- public:
- CopyTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err);
- ~CopyTargetGenerator() override;
-
- protected:
- void DoRun() override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CopyTargetGenerator);
-};
-
-#endif // TOOLS_GN_COPY_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/create_bundle_target_generator.cc b/gn/tools/gn/create_bundle_target_generator.cc
deleted file mode 100644
index a54d17104aa..00000000000
--- a/gn/tools/gn/create_bundle_target_generator.cc
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/create_bundle_target_generator.h"
-
-#include <map>
-
-#include "base/logging.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/substitution_type.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-CreateBundleTargetGenerator::CreateBundleTargetGenerator(
- Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err)
- : TargetGenerator(target, scope, function_call, err) {}
-
-CreateBundleTargetGenerator::~CreateBundleTargetGenerator() = default;
-
-void CreateBundleTargetGenerator::DoRun() {
- target_->set_output_type(Target::CREATE_BUNDLE);
-
- BundleData& bundle_data = target_->bundle_data();
- if (!FillBundleDir(SourceDir(), variables::kBundleRootDir,
- &bundle_data.root_dir()))
- return;
- if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleContentsDir,
- &bundle_data.contents_dir()))
- return;
- if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleResourcesDir,
- &bundle_data.resources_dir()))
- return;
- if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleExecutableDir,
- &bundle_data.executable_dir()))
- return;
-
- if (!FillXcodeExtraAttributes())
- return;
-
- if (!FillProductType())
- return;
-
- if (!FillPartialInfoPlist())
- return;
-
- if (!FillXcodeTestApplicationName())
- return;
-
- if (!FillCodeSigningScript())
- return;
-
- if (!FillCodeSigningSources())
- return;
-
- if (!FillCodeSigningOutputs())
- return;
-
- if (!FillCodeSigningArgs())
- return;
-
- if (!FillBundleDepsFilter())
- return;
-}
-
-bool CreateBundleTargetGenerator::FillBundleDir(
- const SourceDir& bundle_root_dir,
- const base::StringPiece& name,
- SourceDir* bundle_dir) {
- // All bundle_foo_dir properties are optional. They are only required if they
- // are used in an expansion. The check is performed there.
- const Value* value = scope_->GetValue(name, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
- std::string str = value->string_value();
- if (!str.empty() && str[str.size() - 1] != '/')
- str.push_back('/');
- if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), str,
- value->origin(), err_))
- return false;
- if (str != bundle_root_dir.value() &&
- !IsStringInOutputDir(bundle_root_dir, str)) {
- *err_ = Err(
- value->origin(), "Path is not in bundle root dir.",
- "The given file should be in the bundle root directory or below.\n"
- "Normally you would do \"$bundle_root_dir/foo\". I interpreted this\n"
- "as \"" +
- str + "\".");
- return false;
- }
- *bundle_dir = SourceDir(std::move(str));
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() {
- // Need to get a mutable value to mark all values in the scope as used. This
- // cannot be done on a const Scope.
- Value* value = scope_->GetMutableValue(variables::kXcodeExtraAttributes,
- Scope::SEARCH_CURRENT, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::SCOPE, err_))
- return false;
-
- Scope* scope_value = value->scope_value();
-
- Scope::KeyValueMap value_map;
- scope_value->GetCurrentScopeValues(&value_map);
- scope_value->MarkAllUsed();
-
- std::map<std::string, std::string> xcode_extra_attributes;
- for (const auto& iter : value_map) {
- if (!iter.second.VerifyTypeIs(Value::STRING, err_))
- return false;
-
- xcode_extra_attributes.insert(
- std::make_pair(iter.first.as_string(), iter.second.string_value()));
- }
-
- target_->bundle_data().xcode_extra_attributes() =
- std::move(xcode_extra_attributes);
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillProductType() {
- const Value* value = scope_->GetValue(variables::kProductType, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- target_->bundle_data().product_type().assign(value->string_value());
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillPartialInfoPlist() {
- const Value* value = scope_->GetValue(variables::kPartialInfoPlist, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- const BuildSettings* build_settings = scope_->settings()->build_settings();
- SourceFile path = scope_->GetSourceDir().ResolveRelativeFile(
- *value, err_, build_settings->root_path_utf8());
-
- if (err_->has_error())
- return false;
-
- if (!EnsureStringIsInOutputDir(build_settings->build_dir(), path.value(),
- value->origin(), err_))
- return false;
-
- target_->bundle_data().set_partial_info_plist(path);
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillXcodeTestApplicationName() {
- const Value* value =
- scope_->GetValue(variables::kXcodeTestApplicationName, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- target_->bundle_data().xcode_test_application_name().assign(
- value->string_value());
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillCodeSigningScript() {
- const Value* value = scope_->GetValue(variables::kCodeSigningScript, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
- *value, err_, scope_->settings()->build_settings()->root_path_utf8());
- if (err_->has_error())
- return false;
-
- target_->bundle_data().set_code_signing_script(script_file);
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillCodeSigningSources() {
- const Value* value = scope_->GetValue(variables::kCodeSigningSources, true);
- if (!value)
- return true;
-
- if (target_->bundle_data().code_signing_script().is_null()) {
- *err_ = Err(
- function_call_,
- "No code signing script."
- "You must define code_signing_script if you use code_signing_sources.");
- return false;
- }
-
- Target::FileList script_sources;
- if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
- scope_->GetSourceDir(), &script_sources,
- err_))
- return false;
-
- target_->bundle_data().code_signing_sources() = std::move(script_sources);
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillCodeSigningOutputs() {
- const Value* value = scope_->GetValue(variables::kCodeSigningOutputs, true);
- if (!value)
- return true;
-
- if (target_->bundle_data().code_signing_script().is_null()) {
- *err_ = Err(
- function_call_,
- "No code signing script."
- "You must define code_signing_script if you use code_signing_outputs.");
- return false;
- }
-
- if (!value->VerifyTypeIs(Value::LIST, err_))
- return false;
-
- SubstitutionList& outputs = target_->bundle_data().code_signing_outputs();
- if (!outputs.Parse(*value, err_))
- return false;
-
- if (outputs.list().empty()) {
- *err_ =
- Err(function_call_,
- "Code signing script has no output."
- "If you have no outputs, the build system can not tell when your\n"
- "code signing script needs to be run.");
- return false;
- }
-
- // Validate that outputs are in the output dir.
- CHECK_EQ(value->list_value().size(), outputs.list().size());
- for (size_t i = 0; i < value->list_value().size(); ++i) {
- if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
- value->list_value()[i]))
- return false;
- }
-
- return true;
-}
-
-bool CreateBundleTargetGenerator::FillCodeSigningArgs() {
- const Value* value = scope_->GetValue(variables::kCodeSigningArgs, true);
- if (!value)
- return true;
-
- if (target_->bundle_data().code_signing_script().is_null()) {
- *err_ = Err(
- function_call_,
- "No code signing script."
- "You must define code_signing_script if you use code_signing_args.");
- return false;
- }
-
- if (!value->VerifyTypeIs(Value::LIST, err_))
- return false;
-
- return target_->bundle_data().code_signing_args().Parse(*value, err_);
-}
-
-bool CreateBundleTargetGenerator::FillBundleDepsFilter() {
- const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::LIST, err_))
- return false;
-
- const SourceDir& current_dir = scope_->GetSourceDir();
- std::vector<LabelPattern>& bundle_deps_filter =
- target_->bundle_data().bundle_deps_filter();
- for (const auto& item : value->list_value()) {
- bundle_deps_filter.push_back(
- LabelPattern::GetPattern(current_dir, item, err_));
- if (err_->has_error())
- return false;
- }
-
- return true;
-}
diff --git a/gn/tools/gn/create_bundle_target_generator.h b/gn/tools/gn/create_bundle_target_generator.h
deleted file mode 100644
index a7f1edca148..00000000000
--- a/gn/tools/gn/create_bundle_target_generator.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_CREATE_BUNDLE_TARGET_GENERATOR_H_
-#define TOOLS_GN_CREATE_BUNDLE_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target_generator.h"
-
-class SourceDir;
-
-// Populates a Target with the values from a create_bundle rule.
-class CreateBundleTargetGenerator : public TargetGenerator {
- public:
- CreateBundleTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err);
- ~CreateBundleTargetGenerator() override;
-
- protected:
- void DoRun() override;
-
- private:
- bool FillBundleDir(const SourceDir& bundle_root_dir,
- const base::StringPiece& name,
- SourceDir* bundle_dir);
-
- bool FillXcodeExtraAttributes();
-
- bool FillProductType();
- bool FillPartialInfoPlist();
- bool FillXcodeTestApplicationName();
-
- bool FillCodeSigningScript();
- bool FillCodeSigningSources();
- bool FillCodeSigningOutputs();
- bool FillCodeSigningArgs();
- bool FillBundleDepsFilter();
-
- DISALLOW_COPY_AND_ASSIGN(CreateBundleTargetGenerator);
-};
-
-#endif // TOOLS_GN_CREATE_BUNDLE_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/deps_iterator.cc b/gn/tools/gn/deps_iterator.cc
deleted file mode 100644
index 497ef04ffb5..00000000000
--- a/gn/tools/gn/deps_iterator.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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 "tools/gn/deps_iterator.h"
-
-#include "tools/gn/target.h"
-
-DepsIterator::DepsIterator() : current_index_(0) {
- vect_stack_[0] = nullptr;
- vect_stack_[1] = nullptr;
- vect_stack_[2] = nullptr;
-}
-
-DepsIterator::DepsIterator(const LabelTargetVector* a,
- const LabelTargetVector* b,
- const LabelTargetVector* c)
- : current_index_(0) {
- vect_stack_[0] = a;
- vect_stack_[1] = b;
- vect_stack_[2] = c;
-
- if (vect_stack_[0] && vect_stack_[0]->empty())
- operator++();
-}
-
-// Advance to the next position. This assumes there are more vectors.
-//
-// For internal use, this function tolerates an initial index equal to the
-// length of the current vector. In this case, it will advance to the next
-// one.
-DepsIterator& DepsIterator::operator++() {
- DCHECK(vect_stack_[0]);
-
- current_index_++;
- if (current_index_ >= vect_stack_[0]->size()) {
- // Advance to next vect. Shift the elements left by one.
- vect_stack_[0] = vect_stack_[1];
- vect_stack_[1] = vect_stack_[2];
- vect_stack_[2] = nullptr;
-
- current_index_ = 0;
-
- if (vect_stack_[0] && vect_stack_[0]->empty())
- operator++();
- }
- return *this;
-}
-
-DepsIteratorRange::DepsIteratorRange(const DepsIterator& b)
- : begin_(b), end_() {}
-
-DepsIteratorRange::~DepsIteratorRange() = default;
diff --git a/gn/tools/gn/deps_iterator.h b/gn/tools/gn/deps_iterator.h
deleted file mode 100644
index 413e83bb69e..00000000000
--- a/gn/tools/gn/deps_iterator.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 TOOLS_GN_DEPS_ITERATOR_H_
-#define TOOLS_GN_DEPS_ITERATOR_H_
-
-#include <stddef.h>
-
-#include "tools/gn/label_ptr.h"
-
-// Provides an iterator for iterating over multiple LabelTargetVectors to
-// make it convenient to iterate over all deps of a target.
-//
-// This works by maintaining a simple stack of vectors (since we have a fixed
-// number of deps types). When the stack is empty, we've reached the end. This
-// means that the default-constructed iterator == end() for any sequence.
-class DepsIterator {
- public:
- // Creates an empty iterator.
- DepsIterator();
-
- // Iterate over the deps in the given vectors. If passing less than three,
- // pad with nulls.
- DepsIterator(const LabelTargetVector* a,
- const LabelTargetVector* b,
- const LabelTargetVector* c);
-
- // Prefix increment operator. This assumes there are more items (i.e.
- // *this != DepsIterator()).
- //
- // For internal use, this function tolerates an initial index equal to the
- // length of the current vector. In this case, it will advance to the next
- // one.
- DepsIterator& operator++();
-
- // Comparison for STL-based loops.
- bool operator!=(const DepsIterator& other) const {
- return current_index_ != other.current_index_ ||
- vect_stack_[0] != other.vect_stack_[0] ||
- vect_stack_[1] != other.vect_stack_[1] ||
- vect_stack_[2] != other.vect_stack_[2];
- }
-
- // Dereference operator for STL-compatible iterators.
- const LabelTargetPair& operator*() const {
- DCHECK_LT(current_index_, vect_stack_[0]->size());
- return (*vect_stack_[0])[current_index_];
- }
-
- private:
- const LabelTargetVector* vect_stack_[3];
-
- size_t current_index_;
-};
-
-// Provides a virtual container implementing begin() and end() for a
-// sequence of deps. This can then be used in range-based for loops.
-class DepsIteratorRange {
- public:
- explicit DepsIteratorRange(const DepsIterator& b);
- ~DepsIteratorRange();
-
- const DepsIterator& begin() const { return begin_; }
- const DepsIterator& end() const { return end_; }
-
- private:
- DepsIterator begin_;
- DepsIterator end_;
-};
-
-#endif // TOOLS_GN_DEPS_ITERATOR_H_
diff --git a/gn/tools/gn/desc_builder.cc b/gn/tools/gn/desc_builder.cc
deleted file mode 100644
index 6ede896d61d..00000000000
--- a/gn/tools/gn/desc_builder.cc
+++ /dev/null
@@ -1,817 +0,0 @@
-// Copyright (c) 2016 The Chromium 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 <memory>
-#include <set>
-
-#include "base/json/json_writer.h"
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/config.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/desc_builder.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/runtime_deps.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/variables.h"
-
-// Example structure of Value for single target
-// (not applicable or empty fields will be ommitted depending on target type)
-//
-// target_properties = {
-// "type" : "output_type", // matching Target::GetStringForOutputType
-// "toolchain" : "toolchain_name",
-// "visibility" : [ list of visibility pattern descriptions ],
-// "test_only" : true or false,
-// "check_includes": true or false,
-// "allow_circular_includes_from": [ list of target names ],
-// "sources" : [ list of source files ],
-// "public" : either "*" or [ list of public headers],
-// "inputs" : [ list of inputs for target ],
-// "configs" : [ list of configs for this target ],
-// "public_configs" : [ list of public configs for this taget],
-// "all_dependent_configs", [ list of all dependent configs for this target],
-// "script" : "script for action targets",
-// "args" : [ argument list for action targets ],
-// "depfile : "file name for action input dependencies",
-// "outputs" : [ list of target outputs ],
-// "arflags", "asmflags", "cflags", "cflags_c",
-// "clfags_cc", "cflags_objc", "clfags_objcc" : [ list of flags],
-// "defines" : [ list of preprocessor definitions ],
-// "include_dirs" : [ list of include directories ],
-// "precompiled_header" : "name of precompiled header file",
-// "precompiled_source" : "path to precompiled source",
-// "deps : [ list of target dependencies ],
-// "libs" : [ list of libraries ],
-// "lib_dirs" : [ list of library directories ]
-// "metadata" : [ dictionary of target metadata values ]
-// "data_keys" : [ list of target data keys ]
-// "walk_keys" : [ list of target walk keys ]
-// "rebase" : true or false
-// "output_conversion" : "string for output conversion"
-// "response_file_contents": [ list of response file contents entries ]
-// }
-//
-// Optionally, if "what" is specified while generating description, two other
-// properties can be requested that are not included by default
-//
-// "runtime_deps" : [list of computed runtime dependencies]
-// "source_outputs" : {
-// "source_file x" : [ list of outputs for source file x ]
-// "source_file y" : [ list of outputs for source file y ]
-// ...
-// }
-
-namespace {
-
-std::string FormatSourceDir(const SourceDir& dir) {
-#if defined(OS_WIN)
- // On Windows we fix up system absolute paths to look like native ones.
- // Internally, they'll look like "/C:\foo\bar/"
- if (dir.is_system_absolute()) {
- std::string buf = dir.value();
- if (buf.size() > 3 && buf[2] == ':') {
- buf.erase(buf.begin()); // Erase beginning slash.
- return buf;
- }
- }
-#endif
- return dir.value();
-}
-
-void RecursiveCollectChildDeps(const Target* target,
- std::set<const Target*>* result);
-
-void RecursiveCollectDeps(const Target* target,
- std::set<const Target*>* result) {
- if (result->find(target) != result->end())
- return; // Already did this target.
- result->insert(target);
-
- RecursiveCollectChildDeps(target, result);
-}
-
-void RecursiveCollectChildDeps(const Target* target,
- std::set<const Target*>* result) {
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
- RecursiveCollectDeps(pair.ptr, result);
-}
-
-// Common functionality for target and config description builder
-class BaseDescBuilder {
- public:
- typedef std::unique_ptr<base::Value> ValuePtr;
-
- BaseDescBuilder(const std::set<std::string>& what,
- bool all,
- bool tree,
- bool blame)
- : what_(what), all_(all), tree_(tree), blame_(blame) {}
-
- protected:
- virtual Label GetToolchainLabel() const = 0;
-
- bool what(const std::string& w) const {
- return what_.empty() || what_.find(w) != what_.end();
- }
-
- template <typename T>
- ValuePtr RenderValue(const std::vector<T>& vector) {
- auto res = std::make_unique<base::ListValue>();
- for (const auto& v : vector)
- res->Append(RenderValue(v));
-
- return std::move(res);
- }
-
- ValuePtr RenderValue(const std::string& s, bool optional = false) {
- return (s.empty() && optional) ? std::make_unique<base::Value>()
- : ValuePtr(new base::Value(s));
- }
-
- ValuePtr RenderValue(const SourceDir& d) {
- return d.is_null() ? std::make_unique<base::Value>()
- : ValuePtr(new base::Value(FormatSourceDir(d)));
- }
-
- ValuePtr RenderValue(const SourceFile& f) {
- return f.is_null() ? std::make_unique<base::Value>()
- : ValuePtr(new base::Value(f.value()));
- }
-
- ValuePtr RenderValue(const SourceFile* f) { return RenderValue(*f); }
-
- ValuePtr RenderValue(const LibFile& lib) {
- if (lib.is_source_file())
- return RenderValue(lib.source_file());
- return RenderValue(lib.value());
- }
-
- template <typename T>
- base::Value ToBaseValue(const std::vector<T>& vector) {
- base::ListValue res;
- for (const auto& v : vector)
- res.GetList().emplace_back(ToBaseValue(v));
- return std::move(res);
- }
-
- base::Value ToBaseValue(const Scope* scope) {
- base::DictionaryValue res;
- Scope::KeyValueMap map;
- scope->GetCurrentScopeValues(&map);
- for (const auto& v : map)
- res.SetKey(v.first, ToBaseValue(v.second));
- return std::move(res);
- }
-
- base::Value ToBaseValue(const Value& val) {
- switch (val.type()) {
- case Value::STRING:
- return base::Value(val.string_value());
- case Value::INTEGER:
- return base::Value(int(val.int_value()));
- case Value::BOOLEAN:
- return base::Value(val.boolean_value());
- case Value::SCOPE:
- return ToBaseValue(val.scope_value());
- case Value::LIST:
- return ToBaseValue(val.list_value());
- case Value::NONE:
- return base::Value();
- }
- NOTREACHED();
- return base::Value();
- }
-
- template <class VectorType>
- void FillInConfigVector(base::ListValue* out,
- const VectorType& configs,
- int indent = 0) {
- for (const auto& config : configs) {
- std::string name(indent * 2, ' ');
- name.append(config.label.GetUserVisibleName(GetToolchainLabel()));
- out->AppendString(name);
- if (tree_)
- FillInConfigVector(out, config.ptr->configs(), indent + 1);
- }
- }
-
- void FillInPrecompiledHeader(base::DictionaryValue* out,
- const ConfigValues& values) {
- if (what(variables::kPrecompiledHeader) &&
- !values.precompiled_header().empty()) {
- out->SetWithoutPathExpansion(
- variables::kPrecompiledHeader,
- RenderValue(values.precompiled_header(), true));
- }
- if (what(variables::kPrecompiledSource) &&
- !values.precompiled_source().is_null()) {
- out->SetWithoutPathExpansion(variables::kPrecompiledSource,
- RenderValue(values.precompiled_source()));
- }
- }
-
- std::set<std::string> what_;
- bool all_;
- bool tree_;
- bool blame_;
-};
-
-class ConfigDescBuilder : public BaseDescBuilder {
- public:
- ConfigDescBuilder(const Config* config, const std::set<std::string>& what)
- : BaseDescBuilder(what, false, false, false), config_(config) {}
-
- std::unique_ptr<base::DictionaryValue> BuildDescription() {
- auto res = std::make_unique<base::DictionaryValue>();
- const ConfigValues& values = config_->resolved_values();
-
- if (what_.empty())
- res->SetKey(
- "toolchain",
- base::Value(
- config_->label().GetToolchainLabel().GetUserVisibleName(false)));
-
- if (what(variables::kConfigs) && !config_->configs().empty()) {
- auto configs = std::make_unique<base::ListValue>();
- FillInConfigVector(configs.get(), config_->configs().vector());
- res->SetWithoutPathExpansion(variables::kConfigs, std::move(configs));
- }
-
-#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
- if (what(#name)) { \
- ValuePtr ptr = \
- render_config_value_array<type>(values, &ConfigValues::name); \
- if (ptr) { \
- res->SetWithoutPathExpansion(#name, std::move(ptr)); \
- } \
- }
- CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(defines, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir)
- CONFIG_VALUE_ARRAY_HANDLER(inputs, SourceFile)
- CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(lib_dirs, SourceDir)
- CONFIG_VALUE_ARRAY_HANDLER(libs, LibFile)
-
-#undef CONFIG_VALUE_ARRAY_HANDLER
-
- FillInPrecompiledHeader(res.get(), values);
-
- return res;
- }
-
- protected:
- Label GetToolchainLabel() const override {
- return config_->label().GetToolchainLabel();
- }
-
- private:
- template <typename T>
- ValuePtr render_config_value_array(
- const ConfigValues& values,
- const std::vector<T>& (ConfigValues::*getter)() const) {
- auto res = std::make_unique<base::ListValue>();
-
- for (const T& cur : (values.*getter)())
- res->Append(RenderValue(cur));
-
- return res->empty() ? nullptr : std::move(res);
- }
-
- const Config* config_;
-};
-
-class TargetDescBuilder : public BaseDescBuilder {
- public:
- TargetDescBuilder(const Target* target,
- const std::set<std::string>& what,
- bool all,
- bool tree,
- bool blame)
- : BaseDescBuilder(what, all, tree, blame), target_(target) {}
-
- std::unique_ptr<base::DictionaryValue> BuildDescription() {
- auto res = std::make_unique<base::DictionaryValue>();
- bool is_binary_output = target_->IsBinary();
-
- if (what_.empty()) {
- res->SetKey(
- "type",
- base::Value(Target::GetStringForOutputType(target_->output_type())));
- res->SetKey(
- "toolchain",
- base::Value(
- target_->label().GetToolchainLabel().GetUserVisibleName(false)));
- }
-
- // General target meta variables.
-
- if (what(variables::kMetadata)) {
- base::DictionaryValue metadata;
- for (const auto& v : target_->metadata().contents())
- metadata.SetKey(v.first, ToBaseValue(v.second));
- res->SetKey(variables::kMetadata, std::move(metadata));
- }
-
- if (what(variables::kVisibility))
- res->SetWithoutPathExpansion(variables::kVisibility,
- target_->visibility().AsValue());
-
- if (what(variables::kTestonly))
- res->SetKey(variables::kTestonly, base::Value(target_->testonly()));
-
- if (is_binary_output) {
- if (what(variables::kCheckIncludes))
- res->SetKey(variables::kCheckIncludes,
- base::Value(target_->check_includes()));
-
- if (what(variables::kAllowCircularIncludesFrom)) {
- auto labels = std::make_unique<base::ListValue>();
- for (const auto& cur : target_->allow_circular_includes_from())
- labels->AppendString(cur.GetUserVisibleName(GetToolchainLabel()));
-
- res->SetWithoutPathExpansion(variables::kAllowCircularIncludesFrom,
- std::move(labels));
- }
- }
-
- if (what(variables::kSources) && !target_->sources().empty())
- res->SetWithoutPathExpansion(variables::kSources,
- RenderValue(target_->sources()));
-
- if (what(variables::kOutputName) && !target_->output_name().empty())
- res->SetKey(variables::kOutputName, base::Value(target_->output_name()));
-
- if (what(variables::kOutputDir) && !target_->output_dir().is_null())
- res->SetWithoutPathExpansion(variables::kOutputDir,
- RenderValue(target_->output_dir()));
-
- if (what(variables::kOutputExtension) && target_->output_extension_set())
- res->SetKey(variables::kOutputExtension,
- base::Value(target_->output_extension()));
-
- if (what(variables::kPublic)) {
- if (target_->all_headers_public())
- res->SetKey(variables::kPublic, base::Value("*"));
- else
- res->SetWithoutPathExpansion(variables::kPublic,
- RenderValue(target_->public_headers()));
- }
-
- if (what(variables::kInputs)) {
- std::vector<const SourceFile*> inputs;
- for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
- for (const auto& input : iter.cur().inputs())
- inputs.push_back(&input);
- }
- if (!inputs.empty())
- res->SetWithoutPathExpansion(variables::kInputs, RenderValue(inputs));
- }
-
- if (is_binary_output && what(variables::kConfigs) &&
- !target_->configs().empty()) {
- auto configs = std::make_unique<base::ListValue>();
- FillInConfigVector(configs.get(), target_->configs().vector());
- res->SetWithoutPathExpansion(variables::kConfigs, std::move(configs));
- }
-
- if (what(variables::kPublicConfigs) && !target_->public_configs().empty()) {
- auto configs = std::make_unique<base::ListValue>();
- FillInConfigVector(configs.get(), target_->public_configs());
- res->SetWithoutPathExpansion(variables::kPublicConfigs,
- std::move(configs));
- }
-
- if (what(variables::kAllDependentConfigs) &&
- !target_->all_dependent_configs().empty()) {
- auto configs = std::make_unique<base::ListValue>();
- FillInConfigVector(configs.get(), target_->all_dependent_configs());
- res->SetWithoutPathExpansion(variables::kAllDependentConfigs,
- std::move(configs));
- }
-
- // Action
- if (target_->output_type() == Target::ACTION ||
- target_->output_type() == Target::ACTION_FOREACH) {
- if (what(variables::kScript))
- res->SetKey(variables::kScript,
- base::Value(target_->action_values().script().value()));
-
- if (what(variables::kArgs)) {
- auto args = std::make_unique<base::ListValue>();
- for (const auto& elem : target_->action_values().args().list())
- args->AppendString(elem.AsString());
-
- res->SetWithoutPathExpansion(variables::kArgs, std::move(args));
- }
- if (what(variables::kResponseFileContents) &&
- !target_->action_values().rsp_file_contents().list().empty()) {
- auto rsp_file_contents = std::make_unique<base::ListValue>();
- for (const auto& elem :
- target_->action_values().rsp_file_contents().list())
- rsp_file_contents->AppendString(elem.AsString());
-
- res->SetWithoutPathExpansion(variables::kResponseFileContents,
- std::move(rsp_file_contents));
- }
- if (what(variables::kDepfile) &&
- !target_->action_values().depfile().empty()) {
- res->SetKey(variables::kDepfile,
- base::Value(target_->action_values().depfile().AsString()));
- }
- }
-
- if (target_->output_type() != Target::SOURCE_SET &&
- target_->output_type() != Target::GROUP &&
- target_->output_type() != Target::BUNDLE_DATA) {
- if (what(variables::kOutputs))
- FillInOutputs(res.get());
- }
-
- // Source outputs are only included when specifically asked for it
- if (what_.find("source_outputs") != what_.end())
- FillInSourceOutputs(res.get());
-
- if (target_->output_type() == Target::CREATE_BUNDLE && what("bundle_data"))
- FillInBundle(res.get());
-
- if (is_binary_output) {
-#define CONFIG_VALUE_ARRAY_HANDLER(name, type) \
- if (what(#name)) { \
- ValuePtr ptr = RenderConfigValues<type>(&ConfigValues::name); \
- if (ptr) { \
- res->SetWithoutPathExpansion(#name, std::move(ptr)); \
- } \
- }
- CONFIG_VALUE_ARRAY_HANDLER(arflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(asmflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_c, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_cc, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objc, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(cflags_objcc, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(defines, std::string)
- CONFIG_VALUE_ARRAY_HANDLER(include_dirs, SourceDir)
- CONFIG_VALUE_ARRAY_HANDLER(inputs, SourceFile)
- CONFIG_VALUE_ARRAY_HANDLER(ldflags, std::string)
-#undef CONFIG_VALUE_ARRAY_HANDLER
-
- // Libs and lib_dirs are handled specially below.
-
- FillInPrecompiledHeader(res.get(), target_->config_values());
- }
-
- // GeneratedFile vars.
- if (target_->output_type() == Target::GENERATED_FILE) {
- if (what(variables::kWriteOutputConversion)) {
- res->SetKey(variables::kWriteOutputConversion,
- std::move(ToBaseValue(target_->output_conversion())));
- }
- if (what(variables::kDataKeys)) {
- base::ListValue keys;
- for (const auto& k : target_->data_keys())
- keys.GetList().push_back(base::Value(k));
- res->SetKey(variables::kDataKeys, std::move(keys));
- }
- if (what(variables::kRebase)) {
- res->SetWithoutPathExpansion(variables::kRebase,
- RenderValue(target_->rebase()));
- }
- if (what(variables::kWalkKeys)) {
- base::ListValue keys;
- for (const auto& k : target_->walk_keys())
- keys.GetList().push_back(base::Value(k));
- res->SetKey(variables::kWalkKeys, std::move(keys));
- }
- }
-
- if (what(variables::kDeps))
- res->SetWithoutPathExpansion(variables::kDeps, RenderDeps());
-
- // Runtime deps are special, print only when explicitly asked for and not in
- // overview mode.
- if (what_.find("runtime_deps") != what_.end())
- res->SetWithoutPathExpansion("runtime_deps", RenderRuntimeDeps());
-
- // libs and lib_dirs are special in that they're inherited. We don't
- // currently
- // implement a blame feature for this since the bottom-up inheritance makes
- // this difficult.
-
- // Libs can be part of any target and get recursively pushed up the chain,
- // so display them regardless of target type.
- if (what(variables::kLibs)) {
- const OrderedSet<LibFile>& all_libs = target_->all_libs();
- if (!all_libs.empty()) {
- auto libs = std::make_unique<base::ListValue>();
- for (size_t i = 0; i < all_libs.size(); i++)
- libs->AppendString(all_libs[i].value());
- res->SetWithoutPathExpansion(variables::kLibs, std::move(libs));
- }
- }
-
- if (what(variables::kLibDirs)) {
- const OrderedSet<SourceDir>& all_lib_dirs = target_->all_lib_dirs();
- if (!all_lib_dirs.empty()) {
- auto lib_dirs = std::make_unique<base::ListValue>();
- for (size_t i = 0; i < all_lib_dirs.size(); i++)
- lib_dirs->AppendString(FormatSourceDir(all_lib_dirs[i]));
- res->SetWithoutPathExpansion(variables::kLibDirs, std::move(lib_dirs));
- }
- }
-
- return res;
- }
-
- private:
- // Prints dependencies of the given target (not the target itself). If the
- // set is non-null, new targets encountered will be added to the set, and if
- // a dependency is in the set already, it will not be recused into. When the
- // set is null, all dependencies will be printed.
- void RecursivePrintDeps(base::ListValue* out,
- const Target* target,
- std::set<const Target*>* seen_targets,
- int indent_level) {
- // Combine all deps into one sorted list.
- std::vector<LabelTargetPair> sorted_deps;
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
- sorted_deps.push_back(pair);
- std::sort(sorted_deps.begin(), sorted_deps.end());
-
- std::string indent(indent_level * 2, ' ');
-
- for (const auto& pair : sorted_deps) {
- const Target* cur_dep = pair.ptr;
- std::string str =
- indent + cur_dep->label().GetUserVisibleName(GetToolchainLabel());
-
- bool print_children = true;
- if (seen_targets) {
- if (seen_targets->find(cur_dep) == seen_targets->end()) {
- // New target, mark it visited.
- seen_targets->insert(cur_dep);
- } else {
- // Already seen.
- print_children = false;
- // Only print "..." if something is actually elided, which means that
- // the current target has children.
- if (!cur_dep->public_deps().empty() ||
- !cur_dep->private_deps().empty() || !cur_dep->data_deps().empty())
- str += "...";
- }
- }
-
- out->AppendString(str);
-
- if (print_children)
- RecursivePrintDeps(out, cur_dep, seen_targets, indent_level + 1);
- }
- }
-
- ValuePtr RenderDeps() {
- auto res = std::make_unique<base::ListValue>();
-
- // Tree mode is separate.
- if (tree_) {
- if (all_) {
- // Show all tree deps with no eliding.
- RecursivePrintDeps(res.get(), target_, nullptr, 0);
- } else {
- // Don't recurse into duplicates.
- std::set<const Target*> seen_targets;
- RecursivePrintDeps(res.get(), target_, &seen_targets, 0);
- }
- } else { // not tree
-
- // Collect the deps to display.
- if (all_) {
- // Show all dependencies.
- std::set<const Target*> all_deps;
- RecursiveCollectChildDeps(target_, &all_deps);
- commands::FilterAndPrintTargetSet(all_deps, res.get());
- } else {
- // Show direct dependencies only.
- std::vector<const Target*> deps;
- for (const auto& pair : target_->GetDeps(Target::DEPS_ALL))
- deps.push_back(pair.ptr);
- std::sort(deps.begin(), deps.end());
- commands::FilterAndPrintTargets(&deps, res.get());
- }
- }
-
- return std::move(res);
- }
-
- ValuePtr RenderRuntimeDeps() {
- auto res = std::make_unique<base::ListValue>();
-
- const Target* previous_from = NULL;
- for (const auto& pair : ComputeRuntimeDeps(target_)) {
- std::string str;
- if (blame_) {
- // Generally a target's runtime deps will be listed sequentially, so
- // group them and don't duplicate the "from" label for two in a row.
- if (previous_from == pair.second) {
- str = " ";
- } else {
- previous_from = pair.second;
- res->AppendString(
- str + "From " +
- pair.second->label().GetUserVisibleName(GetToolchainLabel()));
- str = " ";
- }
- }
-
- res->AppendString(str + pair.first.value());
- }
-
- return std::move(res);
- }
-
- void FillInSourceOutputs(base::DictionaryValue* res) {
- auto dict = std::make_unique<base::DictionaryValue>();
- for (const auto& source : target_->sources()) {
- std::vector<OutputFile> outputs;
- const char* tool_name = Tool::kToolNone;
- if (target_->GetOutputFilesForSource(source, &tool_name, &outputs)) {
- auto list = std::make_unique<base::ListValue>();
- for (const auto& output : outputs)
- list->AppendString(output.value());
-
- dict->SetWithoutPathExpansion(source.value(), std::move(list));
- }
- }
- res->SetWithoutPathExpansion("source_outputs", std::move(dict));
- }
-
- void FillInBundle(base::DictionaryValue* res) {
- auto data = std::make_unique<base::DictionaryValue>();
- const BundleData& bundle_data = target_->bundle_data();
- const Settings* settings = target_->settings();
- BundleData::SourceFiles sources;
- bundle_data.GetSourceFiles(&sources);
- data->SetWithoutPathExpansion("source_files", RenderValue(sources));
- data->SetKey(
- "root_dir_output",
- base::Value(bundle_data.GetBundleRootDirOutput(settings).value()));
- data->SetWithoutPathExpansion("root_dir",
- RenderValue(bundle_data.root_dir()));
- data->SetWithoutPathExpansion("resources_dir",
- RenderValue(bundle_data.resources_dir()));
- data->SetWithoutPathExpansion("executable_dir",
- RenderValue(bundle_data.executable_dir()));
- data->SetKey("product_type", base::Value(bundle_data.product_type()));
- data->SetWithoutPathExpansion(
- "partial_info_plist", RenderValue(bundle_data.partial_info_plist()));
-
- auto deps = std::make_unique<base::ListValue>();
- for (const auto* dep : bundle_data.bundle_deps())
- deps->AppendString(dep->label().GetUserVisibleName(GetToolchainLabel()));
-
- data->SetWithoutPathExpansion("deps", std::move(deps));
- res->SetWithoutPathExpansion("bundle_data", std::move(data));
- }
-
- void FillInOutputs(base::DictionaryValue* res) {
- if (target_->output_type() == Target::ACTION) {
- auto list = std::make_unique<base::ListValue>();
- for (const auto& elem : target_->action_values().outputs().list())
- list->AppendString(elem.AsString());
-
- res->SetWithoutPathExpansion(variables::kOutputs, std::move(list));
- } else if (target_->output_type() == Target::CREATE_BUNDLE ||
- target_->output_type() == Target::GENERATED_FILE) {
- Err err;
- std::vector<SourceFile> output_files;
- if (!target_->bundle_data().GetOutputsAsSourceFiles(
- target_->settings(), target_, &output_files, &err)) {
- err.PrintToStdout();
- }
- res->SetWithoutPathExpansion(variables::kOutputs,
- RenderValue(output_files));
- } else if (target_->output_type() == Target::ACTION_FOREACH ||
- target_->output_type() == Target::COPY_FILES) {
- const SubstitutionList& outputs = target_->action_values().outputs();
- if (!outputs.required_types().empty()) {
- auto patterns = std::make_unique<base::ListValue>();
- for (const auto& elem : outputs.list())
- patterns->AppendString(elem.AsString());
-
- res->SetWithoutPathExpansion("output_patterns", std::move(patterns));
- }
- std::vector<SourceFile> output_files;
- SubstitutionWriter::ApplyListToSources(target_, target_->settings(),
- outputs, target_->sources(),
- &output_files);
- res->SetWithoutPathExpansion(variables::kOutputs,
- RenderValue(output_files));
- } else {
- DCHECK(target_->IsBinary());
- const Tool* tool =
- target_->toolchain()->GetToolForTargetFinalOutput(target_);
-
- std::vector<OutputFile> output_files;
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- target_, tool, tool->outputs(), &output_files);
- std::vector<SourceFile> output_files_as_source_file;
- for (const OutputFile& output_file : output_files)
- output_files_as_source_file.push_back(
- output_file.AsSourceFile(target_->settings()->build_settings()));
-
- res->SetWithoutPathExpansion(variables::kOutputs,
- RenderValue(output_files_as_source_file));
- }
- }
-
- // Writes a given config value type to the string, optionally with
- // attribution.
- // This should match RecursiveTargetConfigToStream in the order it traverses.
- template <class T>
- ValuePtr RenderConfigValues(const std::vector<T>& (ConfigValues::*getter)()
- const) {
- auto res = std::make_unique<base::ListValue>();
- for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
- const std::vector<T>& vec = (iter.cur().*getter)();
-
- if (vec.empty())
- continue;
-
- if (blame_) {
- const Config* config = iter.GetCurrentConfig();
- if (config) {
- // Source of this value is a config.
- std::string from =
- "From " + config->label().GetUserVisibleName(false);
- res->AppendString(from);
- if (iter.origin()) {
- Location location = iter.origin()->GetRange().begin();
- from = " (Added by " + location.file()->name().value() + ":" +
- base::IntToString(location.line_number()) + ")";
- res->AppendString(from);
- }
- } else {
- // Source of this value is the target itself.
- std::string from =
- "From " + target_->label().GetUserVisibleName(false);
- res->AppendString(from);
- }
- }
-
- for (const T& val : vec) {
- ValuePtr rendered = RenderValue(val);
- std::string str;
- // Indent string values in blame mode
- if (blame_ && rendered->GetAsString(&str)) {
- str = " " + str;
- rendered = std::make_unique<base::Value>(str);
- }
- res->Append(std::move(rendered));
- }
- }
- return res->empty() ? nullptr : std::move(res);
- }
-
- Label GetToolchainLabel() const override {
- return target_->label().GetToolchainLabel();
- }
-
- const Target* target_;
-};
-
-} // namespace
-
-std::unique_ptr<base::DictionaryValue> DescBuilder::DescriptionForTarget(
- const Target* target,
- const std::string& what,
- bool all,
- bool tree,
- bool blame) {
- std::set<std::string> w;
- if (!what.empty())
- w.insert(what);
- TargetDescBuilder b(target, w, all, tree, blame);
- return b.BuildDescription();
-}
-
-std::unique_ptr<base::DictionaryValue> DescBuilder::DescriptionForConfig(
- const Config* config,
- const std::string& what) {
- std::set<std::string> w;
- if (!what.empty())
- w.insert(what);
- ConfigDescBuilder b(config, w);
- return b.BuildDescription();
-}
diff --git a/gn/tools/gn/desc_builder.h b/gn/tools/gn/desc_builder.h
deleted file mode 100644
index 1f0c395923f..00000000000
--- a/gn/tools/gn/desc_builder.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2016 The Chromium 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 TOOLS_GN_DESC_BUILDER_H_
-#define TOOLS_GN_DESC_BUILDER_H_
-
-#include "base/values.h"
-#include "tools/gn/target.h"
-
-class DescBuilder {
- public:
- // Creates Dictionary representation for given target
- static std::unique_ptr<base::DictionaryValue> DescriptionForTarget(
- const Target* target,
- const std::string& what,
- bool all,
- bool tree,
- bool blame);
-
- // Creates Dictionary representation for given config
- static std::unique_ptr<base::DictionaryValue> DescriptionForConfig(
- const Config* config,
- const std::string& what);
-};
-
-#endif
diff --git a/gn/tools/gn/eclipse_writer.cc b/gn/tools/gn/eclipse_writer.cc
deleted file mode 100644
index 328d5dacfe8..00000000000
--- a/gn/tools/gn/eclipse_writer.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/eclipse_writer.h"
-
-#include <fstream>
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/xml_element_writer.h"
-
-namespace {
-
-// Escapes |unescaped| for use in XML element content.
-std::string EscapeForXML(const std::string& unescaped) {
- std::string result;
- result.reserve(unescaped.length());
- for (const char c : unescaped) {
- if (c == '<')
- result += "&lt;";
- else if (c == '>')
- result += "&gt;";
- else if (c == '&')
- result += "&amp;";
- else
- result.push_back(c);
- }
- return result;
-}
-
-} // namespace
-
-EclipseWriter::EclipseWriter(const BuildSettings* build_settings,
- const Builder& builder,
- std::ostream& out)
- : build_settings_(build_settings), builder_(builder), out_(out) {
- languages_.push_back("C++ Source File");
- languages_.push_back("C Source File");
- languages_.push_back("Assembly Source File");
- languages_.push_back("GNU C++");
- languages_.push_back("GNU C");
- languages_.push_back("Assembly");
-}
-
-EclipseWriter::~EclipseWriter() = default;
-
-// static
-bool EclipseWriter::RunAndWriteFile(const BuildSettings* build_settings,
- const Builder& builder,
- Err* err) {
- base::FilePath file = build_settings->GetFullPath(build_settings->build_dir())
- .AppendASCII("eclipse-cdt-settings.xml");
- std::ofstream file_out;
- file_out.open(FilePathToUTF8(file).c_str(),
- std::ios_base::out | std::ios_base::binary);
- if (file_out.fail()) {
- *err =
- Err(Location(), "Couldn't open eclipse-cdt-settings.xml for writing");
- return false;
- }
-
- EclipseWriter gen(build_settings, builder, file_out);
- gen.Run();
- return true;
-}
-
-void EclipseWriter::Run() {
- GetAllIncludeDirs();
- GetAllDefines();
- WriteCDTSettings();
-}
-
-void EclipseWriter::GetAllIncludeDirs() {
- std::vector<const Target*> targets = builder_.GetAllResolvedTargets();
- for (const Target* target : targets) {
- if (!UsesDefaultToolchain(target))
- continue;
-
- for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
- for (const SourceDir& include_dir : it.cur().include_dirs()) {
- include_dirs_.insert(
- FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
- }
- }
- }
-}
-
-void EclipseWriter::GetAllDefines() {
- std::vector<const Target*> targets = builder_.GetAllResolvedTargets();
- for (const Target* target : targets) {
- if (!UsesDefaultToolchain(target))
- continue;
-
- for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
- for (const std::string& define : it.cur().defines()) {
- size_t equal_pos = define.find('=');
- std::string define_key;
- std::string define_value;
- if (equal_pos == std::string::npos) {
- define_key = define;
- } else {
- define_key = define.substr(0, equal_pos);
- define_value = define.substr(equal_pos + 1);
- }
- defines_[define_key] = define_value;
- }
- }
- }
-}
-
-bool EclipseWriter::UsesDefaultToolchain(const Target* target) const {
- return target->toolchain()->label() ==
- builder_.loader()->GetDefaultToolchain();
-}
-
-void EclipseWriter::WriteCDTSettings() {
- out_ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
- XmlElementWriter cdt_properties_element(out_, "cdtprojectproperties",
- XmlAttributes());
-
- {
- const char* kIncludesSectionName =
- "org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths";
- std::unique_ptr<XmlElementWriter> section_element =
- cdt_properties_element.SubElement(
- "section", XmlAttributes("name", kIncludesSectionName));
-
- section_element->SubElement(
- "language", XmlAttributes("name", "holder for library settings"));
-
- for (const std::string& language : languages_) {
- std::unique_ptr<XmlElementWriter> language_element =
- section_element->SubElement("language",
- XmlAttributes("name", language));
- for (const std::string& include_dir : include_dirs_) {
- language_element
- ->SubElement("includepath",
- XmlAttributes("workspace_path", "false"))
- ->Text(EscapeForXML(include_dir));
- }
- }
- }
-
- {
- const char* kMacrosSectionName =
- "org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros";
- std::unique_ptr<XmlElementWriter> section_element =
- cdt_properties_element.SubElement(
- "section", XmlAttributes("name", kMacrosSectionName));
-
- section_element->SubElement(
- "language", XmlAttributes("name", "holder for library settings"));
-
- for (const std::string& language : languages_) {
- std::unique_ptr<XmlElementWriter> language_element =
- section_element->SubElement("language",
- XmlAttributes("name", language));
- for (const auto& key_val : defines_) {
- std::unique_ptr<XmlElementWriter> macro_element =
- language_element->SubElement("macro");
- macro_element->SubElement("name")->Text(EscapeForXML(key_val.first));
- macro_element->SubElement("value")->Text(EscapeForXML(key_val.second));
- }
- }
- }
-}
diff --git a/gn/tools/gn/err.cc b/gn/tools/gn/err.cc
deleted file mode 100644
index fe4db20c969..00000000000
--- a/gn/tools/gn/err.cc
+++ /dev/null
@@ -1,192 +0,0 @@
-// 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 "tools/gn/err.h"
-
-#include <stddef.h>
-
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/tokenizer.h"
-#include "tools/gn/value.h"
-
-namespace {
-
-std::string GetNthLine(const base::StringPiece& data, int n) {
- size_t line_off = Tokenizer::ByteOffsetOfNthLine(data, n);
- size_t end = line_off + 1;
- while (end < data.size() && !Tokenizer::IsNewline(data, end))
- end++;
- return data.substr(line_off, end - line_off).as_string();
-}
-
-void FillRangeOnLine(const LocationRange& range,
- int line_number,
- std::string* line) {
- // Only bother if the range's begin or end overlaps the line. If the entire
- // line is highlighted as a result of this range, it's not very helpful.
- if (range.begin().line_number() != line_number &&
- range.end().line_number() != line_number)
- return;
-
- // Watch out, the char offsets in the location are 1-based, so we have to
- // subtract 1.
- int begin_char;
- if (range.begin().line_number() < line_number)
- begin_char = 0;
- else
- begin_char = range.begin().column_number() - 1;
-
- int end_char;
- if (range.end().line_number() > line_number)
- end_char = static_cast<int>(line->size()); // Ending is non-inclusive.
- else
- end_char = range.end().column_number() - 1;
-
- CHECK(end_char >= begin_char);
- CHECK(begin_char >= 0 && begin_char <= static_cast<int>(line->size()));
- CHECK(end_char >= 0 && end_char <= static_cast<int>(line->size()));
- for (int i = begin_char; i < end_char; i++)
- line->at(i) = '-';
-}
-
-// The line length is used to clip the maximum length of the markers we'll
-// make if the error spans more than one line (like unterminated literals).
-void OutputHighlighedPosition(const Location& location,
- const Err::RangeList& ranges,
- size_t line_length) {
- // Make a buffer of the line in spaces.
- std::string highlight;
- highlight.resize(line_length);
- for (size_t i = 0; i < line_length; i++)
- highlight[i] = ' ';
-
- // Highlight all the ranges on the line.
- for (const auto& range : ranges)
- FillRangeOnLine(range, location.line_number(), &highlight);
-
- // Allow the marker to be one past the end of the line for marking the end.
- highlight.push_back(' ');
- CHECK(location.column_number() - 1 >= 0 &&
- location.column_number() - 1 < static_cast<int>(highlight.size()));
- highlight[location.column_number() - 1] = '^';
-
- // Trim unused spaces from end of line.
- while (!highlight.empty() && highlight[highlight.size() - 1] == ' ')
- highlight.resize(highlight.size() - 1);
-
- highlight += "\n";
- OutputString(highlight, DECORATION_BLUE);
-}
-
-} // namespace
-
-Err::Err() : has_error_(false) {}
-
-Err::Err(const Location& location,
- const std::string& msg,
- const std::string& help)
- : has_error_(true), location_(location), message_(msg), help_text_(help) {}
-
-Err::Err(const LocationRange& range,
- const std::string& msg,
- const std::string& help)
- : has_error_(true),
- location_(range.begin()),
- message_(msg),
- help_text_(help) {
- ranges_.push_back(range);
-}
-
-Err::Err(const Token& token, const std::string& msg, const std::string& help)
- : has_error_(true),
- location_(token.location()),
- message_(msg),
- help_text_(help) {
- ranges_.push_back(token.range());
-}
-
-Err::Err(const ParseNode* node,
- const std::string& msg,
- const std::string& help_text)
- : has_error_(true), message_(msg), help_text_(help_text) {
- // Node will be null in certain tests.
- if (node) {
- LocationRange range = node->GetRange();
- location_ = range.begin();
- ranges_.push_back(range);
- }
-}
-
-Err::Err(const Value& value,
- const std::string msg,
- const std::string& help_text)
- : has_error_(true), message_(msg), help_text_(help_text) {
- if (value.origin()) {
- LocationRange range = value.origin()->GetRange();
- location_ = range.begin();
- ranges_.push_back(range);
- }
-}
-
-Err::Err(const Err& other) = default;
-
-Err::~Err() = default;
-
-void Err::PrintToStdout() const {
- InternalPrintToStdout(false, true);
-}
-
-void Err::PrintNonfatalToStdout() const {
- InternalPrintToStdout(false, false);
-}
-
-void Err::AppendSubErr(const Err& err) {
- sub_errs_.push_back(err);
-}
-
-void Err::InternalPrintToStdout(bool is_sub_err, bool is_fatal) const {
- DCHECK(has_error_);
-
- if (!is_sub_err) {
- if (is_fatal)
- OutputString("ERROR ", DECORATION_RED);
- else
- OutputString("WARNING ", DECORATION_RED);
- }
-
- // File name and location.
- const InputFile* input_file = location_.file();
- std::string loc_str = location_.Describe(true);
- if (!loc_str.empty()) {
- if (is_sub_err)
- loc_str.insert(0, "See ");
- else
- loc_str.insert(0, "at ");
- loc_str.append(": ");
- }
- OutputString(loc_str + message_ + "\n");
-
- // Quoted line.
- if (input_file) {
- std::string line =
- GetNthLine(input_file->contents(), location_.line_number());
- if (!base::ContainsOnlyChars(line, base::kWhitespaceASCII)) {
- OutputString(line + "\n", DECORATION_DIM);
- OutputHighlighedPosition(location_, ranges_, line.size());
- }
- }
-
- // Optional help text.
- if (!help_text_.empty())
- OutputString(help_text_ + "\n");
-
- // Sub errors.
- for (const auto& sub_err : sub_errs_)
- sub_err.InternalPrintToStdout(true, is_fatal);
-}
diff --git a/gn/tools/gn/err.h b/gn/tools/gn/err.h
deleted file mode 100644
index 90f31509310..00000000000
--- a/gn/tools/gn/err.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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 TOOLS_GN_ERR_H_
-#define TOOLS_GN_ERR_H_
-
-#include <string>
-#include <vector>
-
-#include "tools/gn/location.h"
-#include "tools/gn/token.h"
-
-class ParseNode;
-class Value;
-
-// Result of doing some operation. Check has_error() to see if an error
-// occurred.
-//
-// An error has a location and a message. Below that, is some optional help
-// text to go with the annotation of the location.
-//
-// An error can also have sub-errors which are additionally printed out
-// below. They can provide additional context.
-class Err {
- public:
- using RangeList = std::vector<LocationRange>;
-
- // Indicates no error.
- Err();
-
- // Error at a single point.
- Err(const Location& location,
- const std::string& msg,
- const std::string& help = std::string());
-
- // Error at a given range.
- Err(const LocationRange& range,
- const std::string& msg,
- const std::string& help = std::string());
-
- // Error at a given token.
- Err(const Token& token,
- const std::string& msg,
- const std::string& help_text = std::string());
-
- // Error at a given node.
- Err(const ParseNode* node,
- const std::string& msg,
- const std::string& help_text = std::string());
-
- // Error at a given value.
- Err(const Value& value,
- const std::string msg,
- const std::string& help_text = std::string());
-
- Err(const Err& other);
-
- ~Err();
-
- bool has_error() const { return has_error_; }
- const Location& location() const { return location_; }
- const std::string& message() const { return message_; }
- const std::string& help_text() const { return help_text_; }
-
- void AppendRange(const LocationRange& range) { ranges_.push_back(range); }
- const RangeList& ranges() const { return ranges_; }
-
- void AppendSubErr(const Err& err);
-
- void PrintToStdout() const;
-
- // Prints to standard out but uses a "WARNING" messaging instead of the
- // normal "ERROR" messaging. This is a property of the printing system rather
- // than of the Err class because there is no expectation that code calling a
- // function that take an Err check that the error is nonfatal and continue.
- // Generally all Err objects with has_error() set are fatal.
- //
- // In some very specific cases code will detect a condition and print a
- // nonfatal error to the screen instead of returning it. In these cases, that
- // code can decide at printing time whether it will continue (and use this
- // method) or not (and use PrintToStdout()).
- void PrintNonfatalToStdout() const;
-
- private:
- void InternalPrintToStdout(bool is_sub_err, bool is_fatal) const;
-
- bool has_error_;
- Location location_;
-
- std::vector<LocationRange> ranges_;
-
- std::string message_;
- std::string help_text_;
-
- std::vector<Err> sub_errs_;
-};
-
-#endif // TOOLS_GN_ERR_H_
diff --git a/gn/tools/gn/escape.cc b/gn/tools/gn/escape.cc
deleted file mode 100644
index d5e33c0c7d4..00000000000
--- a/gn/tools/gn/escape.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// 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 "tools/gn/escape.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "util/build_config.h"
-
-namespace {
-
-constexpr size_t kStackStringBufferSize = 1024;
-#if defined(OS_WIN)
-constexpr size_t kMaxEscapedCharsPerChar = 2;
-#else
-constexpr size_t kMaxEscapedCharsPerChar = 3;
-#endif
-
-// A "1" in this lookup table means that char is valid in the Posix shell.
-// clang-format off
-const char kShellValid[0x80] = {
-// 00-1f: all are invalid
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-// ' ' ! " # $ % & ' ( ) * + , - . /
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
-// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
-// @ A B C D E F G H I J K L M N O
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-// P Q R S T U V W X Y Z [ \ ] ^ _
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
-// ` a b c d e f g h i j k l m n o
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-// p q r s t u v w x y z { | } ~
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
-// clang-format on
-
-// Uses the stack if the space needed is small and the heap otherwise.
-class StackOrHeapBuffer {
- public:
- explicit StackOrHeapBuffer(size_t buf_size) {
- if (UNLIKELY(buf_size > kStackStringBufferSize))
- heap_buf.reset(new char[buf_size]);
- }
- operator char*() { return heap_buf ? heap_buf.get() : stack_buf; }
-
- private:
- char stack_buf[kStackStringBufferSize];
- std::unique_ptr<char[]> heap_buf;
-};
-
-// Ninja's escaping rules are very simple. We always escape colons even
-// though they're OK in many places, in case the resulting string is used on
-// the left-hand-side of a rule.
-inline bool ShouldEscapeCharForNinja(char ch) {
- return ch == '$' || ch == ' ' || ch == ':';
-}
-
-size_t EscapeStringToString_Ninja(const base::StringPiece& str,
- const EscapeOptions& options,
- char* dest,
- bool* needed_quoting) {
- size_t i = 0;
- for (const auto& elem : str) {
- if (ShouldEscapeCharForNinja(elem))
- dest[i++] = '$';
- dest[i++] = elem;
- }
- return i;
-}
-
-size_t EscapeStringToString_Depfile(const base::StringPiece& str,
- const EscapeOptions& options,
- char* dest,
- bool* needed_quoting) {
- size_t i = 0;
- for (const auto& elem : str) {
- // Escape all characters that ninja depfile parser can recognize as escaped,
- // even if some of them can work without escaping.
- if (elem == ' ' || elem == '\\' || elem == '#' || elem == '*' ||
- elem == '[' || elem == '|' || elem == ']')
- dest[i++] = '\\';
- else if (elem == '$') // Extra rule for $$
- dest[i++] = '$';
- dest[i++] = elem;
- }
- return i;
-}
-
-size_t EscapeStringToString_NinjaPreformatted(const base::StringPiece& str,
- char* dest) {
- // Only Ninja-escape $.
- size_t i = 0;
- for (const auto& elem : str) {
- if (elem == '$')
- dest[i++] = '$';
- dest[i++] = elem;
- }
- return i;
-}
-
-// Escape for CommandLineToArgvW and additionally escape Ninja characters.
-//
-// The basic algorithm is if the string doesn't contain any parse-affecting
-// characters, don't do anything (other than the Ninja processing). If it does,
-// quote the string, and backslash-escape all quotes and backslashes.
-// See:
-// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
-// http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
-size_t EscapeStringToString_WindowsNinjaFork(const base::StringPiece& str,
- const EscapeOptions& options,
- char* dest,
- bool* needed_quoting) {
- // We assume we don't have any whitespace chars that aren't spaces.
- DCHECK(str.find_first_of("\r\n\v\t") == std::string::npos);
-
- size_t i = 0;
- if (str.find_first_of(" \"") == std::string::npos) {
- // Simple case, don't quote.
- return EscapeStringToString_Ninja(str, options, dest, needed_quoting);
- } else {
- if (!options.inhibit_quoting)
- dest[i++] = '"';
-
- for (size_t j = 0; j < str.size(); j++) {
- // Count backslashes in case they're followed by a quote.
- size_t backslash_count = 0;
- while (j < str.size() && str[j] == '\\') {
- j++;
- backslash_count++;
- }
- if (j == str.size()) {
- // Backslashes at end of string. Backslash-escape all of them since
- // they'll be followed by a quote.
- memset(dest + i, '\\', backslash_count * 2);
- i += backslash_count * 2;
- } else if (str[j] == '"') {
- // 0 or more backslashes followed by a quote. Backslash-escape the
- // backslashes, then backslash-escape the quote.
- memset(dest + i, '\\', backslash_count * 2 + 1);
- i += backslash_count * 2 + 1;
- dest[i++] = '"';
- } else {
- // Non-special Windows character, just escape for Ninja. Also, add any
- // backslashes we read previously, these are literals.
- memset(dest + i, '\\', backslash_count);
- i += backslash_count;
- if (ShouldEscapeCharForNinja(str[j]))
- dest[i++] = '$';
- dest[i++] = str[j];
- }
- }
-
- if (!options.inhibit_quoting)
- dest[i++] = '"';
- if (needed_quoting)
- *needed_quoting = true;
- }
- return i;
-}
-
-size_t EscapeStringToString_PosixNinjaFork(const base::StringPiece& str,
- const EscapeOptions& options,
- char* dest,
- bool* needed_quoting) {
- size_t i = 0;
- for (const auto& elem : str) {
- if (elem == '$' || elem == ' ') {
- // Space and $ are special to both Ninja and the shell. '$' escape for
- // Ninja, then backslash-escape for the shell.
- dest[i++] = '\\';
- dest[i++] = '$';
- dest[i++] = elem;
- } else if (elem == ':') {
- // Colon is the only other Ninja special char, which is not special to
- // the shell.
- dest[i++] = '$';
- dest[i++] = ':';
- } else if (static_cast<unsigned>(elem) >= 0x80 ||
- !kShellValid[static_cast<int>(elem)]) {
- // All other invalid shell chars get backslash-escaped.
- dest[i++] = '\\';
- dest[i++] = elem;
- } else {
- // Everything else is a literal.
- dest[i++] = elem;
- }
- }
- return i;
-}
-
-// Escapes |str| into |dest| and returns the number of characters written.
-size_t EscapeStringToString(const base::StringPiece& str,
- const EscapeOptions& options,
- char* dest,
- bool* needed_quoting) {
- switch (options.mode) {
- case ESCAPE_NONE:
- strncpy(dest, str.data(), str.size());
- return str.size();
- case ESCAPE_NINJA:
- return EscapeStringToString_Ninja(str, options, dest, needed_quoting);
- case ESCAPE_DEPFILE:
- return EscapeStringToString_Depfile(str, options, dest, needed_quoting);
- case ESCAPE_NINJA_COMMAND:
- switch (options.platform) {
- case ESCAPE_PLATFORM_CURRENT:
-#if defined(OS_WIN)
- return EscapeStringToString_WindowsNinjaFork(str, options, dest,
- needed_quoting);
-#else
- return EscapeStringToString_PosixNinjaFork(str, options, dest,
- needed_quoting);
-#endif
- case ESCAPE_PLATFORM_WIN:
- return EscapeStringToString_WindowsNinjaFork(str, options, dest,
- needed_quoting);
- case ESCAPE_PLATFORM_POSIX:
- return EscapeStringToString_PosixNinjaFork(str, options, dest,
- needed_quoting);
- default:
- NOTREACHED();
- }
- case ESCAPE_NINJA_PREFORMATTED_COMMAND:
- return EscapeStringToString_NinjaPreformatted(str, dest);
- default:
- NOTREACHED();
- }
- return 0;
-}
-
-} // namespace
-
-std::string EscapeString(const base::StringPiece& str,
- const EscapeOptions& options,
- bool* needed_quoting) {
- StackOrHeapBuffer dest(str.size() * kMaxEscapedCharsPerChar);
- return std::string(dest,
- EscapeStringToString(str, options, dest, needed_quoting));
-}
-
-void EscapeStringToStream(std::ostream& out,
- const base::StringPiece& str,
- const EscapeOptions& options) {
- StackOrHeapBuffer dest(str.size() * kMaxEscapedCharsPerChar);
- out.write(dest, EscapeStringToString(str, options, dest, nullptr));
-}
diff --git a/gn/tools/gn/escape.h b/gn/tools/gn/escape.h
deleted file mode 100644
index af5f8b8febb..00000000000
--- a/gn/tools/gn/escape.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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 TOOLS_GN_ESCAPE_H_
-#define TOOLS_GN_ESCAPE_H_
-
-#include <iosfwd>
-
-#include "base/strings/string_piece.h"
-
-enum EscapingMode {
- // No escaping.
- ESCAPE_NONE,
-
- // Ninja string escaping.
- ESCAPE_NINJA,
-
- // Ninja/makefile depfile string escaping.
- ESCAPE_DEPFILE,
-
- // For writing commands to ninja files. This assumes the output is "one
- // thing" like a filename, so will escape or quote spaces as necessary for
- // both Ninja and the shell to keep that thing together.
- ESCAPE_NINJA_COMMAND,
-
- // For writing preformatted shell commands to Ninja files. This assumes the
- // output already has the proper quoting and may include special shell
- // characters which we want to pass to the shell (like when writing tool
- // commands). Only Ninja "$" are escaped.
- ESCAPE_NINJA_PREFORMATTED_COMMAND,
-};
-
-enum EscapingPlatform {
- // Do escaping for the current platform.
- ESCAPE_PLATFORM_CURRENT,
-
- // Force escaping for the given platform.
- ESCAPE_PLATFORM_POSIX,
- ESCAPE_PLATFORM_WIN,
-};
-
-struct EscapeOptions {
- EscapingMode mode = ESCAPE_NONE;
-
- // Controls how "fork" escaping is done. You will generally want to keep the
- // default "current" platform.
- EscapingPlatform platform = ESCAPE_PLATFORM_CURRENT;
-
- // When the escaping mode is ESCAPE_SHELL, the escaper will normally put
- // quotes around things with spaces. If this value is set to true, we'll
- // disable the quoting feature and just add the spaces.
- //
- // This mode is for when quoting is done at some higher-level. Defaults to
- // false. Note that Windows has strange behavior where the meaning of the
- // backslashes changes according to if it is followed by a quote. The
- // escaping rules assume that a double-quote will be appended to the result.
- bool inhibit_quoting = false;
-};
-
-// Escapes the given input, returnining the result.
-//
-// If needed_quoting is non-null, whether the string was or should have been
-// (if inhibit_quoting was set) quoted will be written to it. This value should
-// be initialized to false by the caller and will be written to only if it's
-// true (the common use-case is for chaining calls).
-std::string EscapeString(const base::StringPiece& str,
- const EscapeOptions& options,
- bool* needed_quoting);
-
-// Same as EscapeString but writes the results to the given stream, saving a
-// copy.
-void EscapeStringToStream(std::ostream& out,
- const base::StringPiece& str,
- const EscapeOptions& options);
-
-#endif // TOOLS_GN_ESCAPE_H_
diff --git a/gn/tools/gn/escape_unittest.cc b/gn/tools/gn/escape_unittest.cc
deleted file mode 100644
index d7ff23fda47..00000000000
--- a/gn/tools/gn/escape_unittest.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 "tools/gn/escape.h"
-#include "util/test/test.h"
-
-TEST(Escape, Ninja) {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA;
- std::string result = EscapeString("asdf: \"$\\bar", opts, nullptr);
- EXPECT_EQ("asdf$:$ \"$$\\bar", result);
-}
-
-TEST(Escape, Depfile) {
- EscapeOptions opts;
- opts.mode = ESCAPE_DEPFILE;
- std::string result = EscapeString("asdf:$ \\#*[|]bar", opts, nullptr);
- EXPECT_EQ("asdf:$$\\ \\\\\\#\\*\\[\\|\\]bar", result);
-}
-
-TEST(Escape, WindowsCommand) {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_COMMAND;
- opts.platform = ESCAPE_PLATFORM_WIN;
-
- // Regular string is passed, even if it has backslashes.
- EXPECT_EQ("foo\\bar", EscapeString("foo\\bar", opts, nullptr));
-
- // Spaces means the string is quoted, normal backslahes untouched.
- bool needs_quoting = false;
- EXPECT_EQ("\"foo\\$ bar\"", EscapeString("foo\\ bar", opts, &needs_quoting));
- EXPECT_TRUE(needs_quoting);
-
- // Inhibit quoting.
- needs_quoting = false;
- opts.inhibit_quoting = true;
- EXPECT_EQ("foo\\$ bar", EscapeString("foo\\ bar", opts, &needs_quoting));
- EXPECT_TRUE(needs_quoting);
- opts.inhibit_quoting = false;
-
- // Backslashes at the end of the string get escaped.
- EXPECT_EQ("\"foo$ bar\\\\\\\\\"", EscapeString("foo bar\\\\", opts, nullptr));
-
- // Backslashes preceding quotes are escaped, and the quote is escaped.
- EXPECT_EQ("\"foo\\\\\\\"$ bar\"", EscapeString("foo\\\" bar", opts, nullptr));
-}
-
-TEST(Escape, PosixCommand) {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_COMMAND;
- opts.platform = ESCAPE_PLATFORM_POSIX;
-
- // : and $ ninja escaped with $. Then Shell-escape backslashes and quotes.
- EXPECT_EQ("a$:\\$ \\\"\\$$\\\\b", EscapeString("a: \"$\\b", opts, nullptr));
-
- // Some more generic shell chars.
- EXPECT_EQ("a_\\;\\<\\*b", EscapeString("a_;<*b", opts, nullptr));
-
- // Curly braces must be escaped to avoid brace expansion on systems using
- // bash as default shell..
- EXPECT_EQ("\\{a,b\\}\\{c,d\\}", EscapeString("{a,b}{c,d}", opts, nullptr));
-}
-
-TEST(Escape, NinjaPreformatted) {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
-
- // Only $ is escaped.
- EXPECT_EQ("a: \"$$\\b<;", EscapeString("a: \"$\\b<;", opts, nullptr));
-}
diff --git a/gn/tools/gn/exec_process.cc b/gn/tools/gn/exec_process.cc
deleted file mode 100644
index 6c13558d686..00000000000
--- a/gn/tools/gn/exec_process.cc
+++ /dev/null
@@ -1,281 +0,0 @@
-// 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 "tools/gn/exec_process.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-
-#include "base/win/scoped_handle.h"
-#include "base/win/scoped_process_information.h"
-#else
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/signal.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "base/posix/eintr_wrapper.h"
-#include "base/posix/file_descriptor_shuffle.h"
-#endif
-
-namespace internal {
-
-#if defined(OS_WIN)
-bool ExecProcess(const base::CommandLine& cmdline,
- const base::FilePath& startup_dir,
- std::string* std_out,
- std::string* std_err,
- int* exit_code) {
- return ExecProcess(cmdline.GetCommandLineString(), startup_dir, std_out,
- std_err, exit_code);
-}
-
-bool ExecProcess(const base::string16& cmdline_str,
- const base::FilePath& startup_dir,
- std::string* std_out,
- std::string* std_err,
- int* exit_code) {
- SECURITY_ATTRIBUTES sa_attr;
- // Set the bInheritHandle flag so pipe handles are inherited.
- sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa_attr.bInheritHandle = TRUE;
- sa_attr.lpSecurityDescriptor = nullptr;
-
- // Create the pipe for the child process's STDOUT.
- HANDLE out_read = nullptr;
- HANDLE out_write = nullptr;
- if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
- NOTREACHED() << "Failed to create pipe";
- return false;
- }
- base::win::ScopedHandle scoped_out_read(out_read);
- base::win::ScopedHandle scoped_out_write(out_write);
-
- // Create the pipe for the child process's STDERR.
- HANDLE err_read = nullptr;
- HANDLE err_write = nullptr;
- if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
- NOTREACHED() << "Failed to create pipe";
- return false;
- }
- base::win::ScopedHandle scoped_err_read(err_read);
- base::win::ScopedHandle scoped_err_write(err_write);
-
- // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
- if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
- NOTREACHED() << "Failed to disable pipe inheritance";
- return false;
- }
- if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
- NOTREACHED() << "Failed to disable pipe inheritance";
- return false;
- }
-
- STARTUPINFO start_info = {};
-
- start_info.cb = sizeof(STARTUPINFO);
- start_info.hStdOutput = out_write;
- // Keep the normal stdin.
- start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
- // FIXME(brettw) set stderr here when we actually read it below.
- // start_info.hStdError = err_write;
- start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
- start_info.dwFlags |= STARTF_USESTDHANDLES;
-
- base::string16 cmdline_writable = cmdline_str;
-
- // Create the child process.
- PROCESS_INFORMATION temp_process_info = {};
- if (!CreateProcess(nullptr, &cmdline_writable[0], nullptr, nullptr,
- TRUE, // Handles are inherited.
- NORMAL_PRIORITY_CLASS, nullptr,
- startup_dir.value().c_str(), &start_info,
- &temp_process_info)) {
- return false;
- }
- base::win::ScopedProcessInformation proc_info(temp_process_info);
-
- // Close our writing end of pipes now. Otherwise later read would not be
- // able to detect end of child's output.
- scoped_out_write.Close();
- scoped_err_write.Close();
-
- // Read output from the child process's pipe for STDOUT
- const int kBufferSize = 1024;
- char buffer[kBufferSize];
-
- // FIXME(brettw) read from stderr here! This is complicated because we want
- // to read both of them at the same time, probably need overlapped I/O.
- // Also uncomment start_info code above.
- for (;;) {
- DWORD bytes_read = 0;
- BOOL success =
- ReadFile(out_read, buffer, kBufferSize, &bytes_read, nullptr);
- if (!success || bytes_read == 0)
- break;
- std_out->append(buffer, bytes_read);
- }
-
- // Let's wait for the process to finish.
- WaitForSingleObject(proc_info.process_handle(), INFINITE);
-
- DWORD dw_exit_code;
- GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
- *exit_code = static_cast<int>(dw_exit_code);
-
- return true;
-}
-#else
-// Reads from the provided file descriptor and appends to output. Returns false
-// if the fd is closed or there is an unexpected error (not
-// EINTR/EAGAIN/EWOULDBLOCK).
-bool ReadFromPipe(int fd, std::string* output) {
- char buffer[256];
- int bytes_read = HANDLE_EINTR(read(fd, buffer, sizeof(buffer)));
- if (bytes_read == -1) {
- return errno == EAGAIN || errno == EWOULDBLOCK;
- } else if (bytes_read <= 0) {
- return false;
- }
- output->append(buffer, bytes_read);
- return true;
-}
-
-bool WaitForExit(int pid, int* exit_code) {
- int status;
- if (waitpid(pid, &status, 0) < 0) {
- PLOG(ERROR) << "waitpid";
- return false;
- }
-
- if (WIFEXITED(status)) {
- *exit_code = WEXITSTATUS(status);
- return true;
- } else if (WIFSIGNALED(status)) {
- if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM ||
- WTERMSIG(status) == SIGHUP)
- return false;
- }
- return false;
-}
-
-bool ExecProcess(const base::CommandLine& cmdline,
- const base::FilePath& startup_dir,
- std::string* std_out,
- std::string* std_err,
- int* exit_code) {
- *exit_code = EXIT_FAILURE;
-
- std::vector<std::string> argv = cmdline.argv();
-
- int out_fd[2], err_fd[2];
- pid_t pid;
- base::InjectiveMultimap fd_shuffle1, fd_shuffle2;
- std::unique_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
-
- fd_shuffle1.reserve(3);
- fd_shuffle2.reserve(3);
-
- if (pipe(out_fd) < 0)
- return false;
- base::ScopedFD out_read(out_fd[0]), out_write(out_fd[1]);
-
- if (pipe(err_fd) < 0)
- return false;
- base::ScopedFD err_read(err_fd[0]), err_write(err_fd[1]);
-
- if (out_read.get() >= FD_SETSIZE || err_read.get() >= FD_SETSIZE)
- return false;
-
- switch (pid = fork()) {
- case -1: // error
- return false;
- case 0: // child
- {
- // DANGER: no calls to malloc are allowed from now on:
- // http://crbug.com/36678
- //
- // STL iterators are also not allowed (including those implied
- // by range-based for loops), since debug iterators use locks.
-
- // Obscure fork() rule: in the child, if you don't end up doing exec*(),
- // you call _exit() instead of exit(). This is because _exit() does not
- // call any previously-registered (in the parent) exit handlers, which
- // might do things like block waiting for threads that don't even exist
- // in the child.
- int dev_null = open("/dev/null", O_WRONLY);
- if (dev_null < 0)
- _exit(127);
-
- fd_shuffle1.push_back(
- base::InjectionArc(out_write.get(), STDOUT_FILENO, true));
- fd_shuffle1.push_back(
- base::InjectionArc(err_write.get(), STDERR_FILENO, true));
- fd_shuffle1.push_back(base::InjectionArc(dev_null, STDIN_FILENO, true));
- // Adding another element here? Remeber to increase the argument to
- // reserve(), above.
-
- // DANGER: Do NOT convert to range-based for loop!
- for (size_t i = 0; i < fd_shuffle1.size(); ++i)
- fd_shuffle2.push_back(fd_shuffle1[i]);
-
- if (!ShuffleFileDescriptors(&fd_shuffle1))
- _exit(127);
-
- base::SetCurrentDirectory(startup_dir);
-
- // TODO(brettw) the base version GetAppOutput does a
- // CloseSuperfluousFds call here. Do we need this?
-
- // DANGER: Do NOT convert to range-based for loop!
- for (size_t i = 0; i < argv.size(); i++)
- argv_cstr[i] = const_cast<char*>(argv[i].c_str());
- argv_cstr[argv.size()] = nullptr;
- execvp(argv_cstr[0], argv_cstr.get());
- _exit(127);
- }
- default: // parent
- {
- // Close our writing end of pipe now. Otherwise later read would not
- // be able to detect end of child's output (in theory we could still
- // write to the pipe).
- out_write.reset();
- err_write.reset();
-
- bool out_open = true, err_open = true;
- while (out_open || err_open) {
- fd_set read_fds;
- FD_ZERO(&read_fds);
- FD_SET(out_read.get(), &read_fds);
- FD_SET(err_read.get(), &read_fds);
- int res =
- HANDLE_EINTR(select(std::max(out_read.get(), err_read.get()) + 1,
- &read_fds, nullptr, nullptr, nullptr));
- if (res <= 0)
- break;
- if (FD_ISSET(out_read.get(), &read_fds))
- out_open = ReadFromPipe(out_read.get(), std_out);
- if (FD_ISSET(err_read.get(), &read_fds))
- err_open = ReadFromPipe(err_read.get(), std_err);
- }
-
- return WaitForExit(pid, exit_code);
- }
- }
-
- return false;
-}
-#endif
-
-} // namespace internal
diff --git a/gn/tools/gn/exec_process.h b/gn/tools/gn/exec_process.h
deleted file mode 100644
index 278d53928ed..00000000000
--- a/gn/tools/gn/exec_process.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 TOOLS_GN_EXEC_PROCESS_H_
-#define TOOLS_GN_EXEC_PROCESS_H_
-
-#include <string>
-
-#include "base/strings/string16.h"
-#include "util/build_config.h"
-
-namespace base {
-class CommandLine;
-class FilePath;
-} // namespace base
-
-namespace internal {
-
-bool ExecProcess(const base::CommandLine& cmdline,
- const base::FilePath& startup_dir,
- std::string* std_out,
- std::string* std_err,
- int* exit_code);
-
-#if defined(OS_WIN)
-bool ExecProcess(const base::string16& cmdline_str,
- const base::FilePath& startup_dir,
- std::string* std_out,
- std::string* std_err,
- int* exit_code);
-#endif // OS_WIN
-
-} // namespace internal
-
-#endif // TOOLS_GN_EXEC_PROCESS_H_
diff --git a/gn/tools/gn/exec_process_unittest.cc b/gn/tools/gn/exec_process_unittest.cc
deleted file mode 100644
index 993f2d73b70..00000000000
--- a/gn/tools/gn/exec_process_unittest.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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 "tools/gn/exec_process.h"
-
-#include "base/command_line.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_util.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-#if defined(OS_WIN)
-#include "base/strings/utf_string_conversions.h"
-#endif
-
-namespace internal {
-
-// TODO(cjhopman): Enable these tests when windows ExecProcess handles stderr.
-// 'python' is not runnable on Windows. Adding ["cmd", "/c"] fails because
-// CommandLine does unusual reordering of args.
-#if !defined(OS_WIN)
-namespace {
-bool ExecPython(const std::string& command,
- std::string* std_out,
- std::string* std_err,
- int* exit_code) {
- base::ScopedTempDir temp_dir;
- CHECK(temp_dir.CreateUniqueTempDir());
- base::CommandLine::StringVector args;
-#if defined(OS_WIN)
- args.push_back(L"python");
- args.push_back(L"-c");
- args.push_back(base::UTF8ToUTF16(command));
-#else
- args.push_back("python");
- args.push_back("-c");
- args.push_back(command);
-#endif
- return ExecProcess(base::CommandLine(args), temp_dir.GetPath(), std_out,
- std_err, exit_code);
-}
-} // namespace
-
-TEST(ExecProcessTest, TestExitCode) {
- std::string std_out, std_err;
- int exit_code;
-
- ASSERT_TRUE(
- ExecPython("import sys; sys.exit(0)", &std_out, &std_err, &exit_code));
- EXPECT_EQ(0, exit_code);
-
- ASSERT_TRUE(
- ExecPython("import sys; sys.exit(1)", &std_out, &std_err, &exit_code));
- EXPECT_EQ(1, exit_code);
-
- ASSERT_TRUE(
- ExecPython("import sys; sys.exit(253)", &std_out, &std_err, &exit_code));
- EXPECT_EQ(253, exit_code);
-
- ASSERT_TRUE(ExecPython("throw Exception()", &std_out, &std_err, &exit_code));
- EXPECT_EQ(1, exit_code);
-}
-
-// Test that large output is handled correctly. There are various ways that this
-// could potentially fail. For example, non-blocking Linux pipes have a 65536
-// byte buffer and, if stdout is non-blocking, python will throw an IOError when
-// a write exceeds the buffer size.
-TEST(ExecProcessTest, TestLargeOutput) {
- std::string std_out, std_err;
- int exit_code;
-
- ASSERT_TRUE(ExecPython("import sys; print('o' * 1000000)", &std_out, &std_err,
- &exit_code));
- EXPECT_EQ(0, exit_code);
- EXPECT_EQ(1000001u, std_out.size());
-}
-
-TEST(ExecProcessTest, TestStdoutAndStderrOutput) {
- std::string std_out, std_err;
- int exit_code;
-
- ASSERT_TRUE(
- ExecPython("from __future__ import print_function; import sys; print('o' "
- "* 10000); print('e' * 10000, file=sys.stderr)",
- &std_out, &std_err, &exit_code));
- EXPECT_EQ(0, exit_code);
- EXPECT_EQ(10001u, std_out.size());
- EXPECT_EQ(10001u, std_err.size());
-
- std_out.clear();
- std_err.clear();
- ASSERT_TRUE(
- ExecPython("from __future__ import print_function; import sys; print('e' "
- "* 10000, file=sys.stderr); print('o' * 10000)",
- &std_out, &std_err, &exit_code));
- EXPECT_EQ(0, exit_code);
- EXPECT_EQ(10001u, std_out.size());
- EXPECT_EQ(10001u, std_err.size());
-}
-
-TEST(ExecProcessTest, TestOneOutputClosed) {
- std::string std_out, std_err;
- int exit_code;
-
- ASSERT_TRUE(ExecPython("import sys; sys.stderr.close(); print('o' * 10000)",
- &std_out, &std_err, &exit_code));
- EXPECT_EQ(0, exit_code);
- EXPECT_EQ(10001u, std_out.size());
- EXPECT_EQ(std_err.size(), 0u);
-
- std_out.clear();
- std_err.clear();
- ASSERT_TRUE(
- ExecPython("from __future__ import print_function; import sys; "
- "sys.stdout.close(); print('e' * 10000, file=sys.stderr)",
- &std_out, &std_err, &exit_code));
- EXPECT_EQ(0, exit_code);
- EXPECT_EQ(0u, std_out.size());
- EXPECT_EQ(10001u, std_err.size());
-}
-#endif
-} // namespace internal
diff --git a/gn/tools/gn/filesystem_utils.cc b/gn/tools/gn/filesystem_utils.cc
deleted file mode 100644
index c378233b925..00000000000
--- a/gn/tools/gn/filesystem_utils.cc
+++ /dev/null
@@ -1,1103 +0,0 @@
-// 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 "tools/gn/filesystem_utils.h"
-
-#include <algorithm>
-
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/location.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_dir.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-namespace {
-
-enum DotDisposition {
- // The given dot is just part of a filename and is not special.
- NOT_A_DIRECTORY,
-
- // The given dot is the current directory.
- DIRECTORY_CUR,
-
- // The given dot is the first of a double dot that should take us up one.
- DIRECTORY_UP
-};
-
-// When we find a dot, this function is called with the character following
-// that dot to see what it is. The return value indicates what type this dot is
-// (see above). This code handles the case where the dot is at the end of the
-// input.
-//
-// |*consumed_len| will contain the number of characters in the input that
-// express what we found.
-DotDisposition ClassifyAfterDot(const std::string& path,
- size_t after_dot,
- size_t* consumed_len) {
- if (after_dot == path.size()) {
- // Single dot at the end.
- *consumed_len = 1;
- return DIRECTORY_CUR;
- }
- if (IsSlash(path[after_dot])) {
- // Single dot followed by a slash.
- *consumed_len = 2; // Consume the slash
- return DIRECTORY_CUR;
- }
-
- if (path[after_dot] == '.') {
- // Two dots.
- if (after_dot + 1 == path.size()) {
- // Double dot at the end.
- *consumed_len = 2;
- return DIRECTORY_UP;
- }
- if (IsSlash(path[after_dot + 1])) {
- // Double dot folowed by a slash.
- *consumed_len = 3;
- return DIRECTORY_UP;
- }
- }
-
- // The dots are followed by something else, not a directory.
- *consumed_len = 1;
- return NOT_A_DIRECTORY;
-}
-
-#if defined(OS_WIN)
-inline char NormalizeWindowsPathChar(char c) {
- if (c == '/')
- return '\\';
- return base::ToLowerASCII(c);
-}
-
-// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
-// paths.
-bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
- const base::StringPiece& b) {
- if (a.size() != b.size())
- return false;
-
- // For now, just do a case-insensitive ASCII comparison. We could convert to
- // UTF-16 and use ICU if necessary.
- for (size_t i = 0; i < a.size(); i++) {
- if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
- return false;
- }
- return true;
-}
-
-bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
- if (path.size() < 3)
- return false;
-
- // Check colon first, this will generally fail fastest.
- if (path[1] != ':')
- return false;
-
- // Check drive letter.
- if (!base::IsAsciiAlpha(path[0]))
- return false;
-
- if (!IsSlash(path[2]))
- return false;
- return true;
-}
-#endif
-
-// A wrapper around FilePath.GetComponents that works the way we need. This is
-// not super efficient since it does some O(n) transformations on the path. If
-// this is called a lot, we might want to optimize.
-std::vector<base::FilePath::StringType> GetPathComponents(
- const base::FilePath& path) {
- std::vector<base::FilePath::StringType> result;
- path.GetComponents(&result);
-
- if (result.empty())
- return result;
-
- // GetComponents will preserve the "/" at the beginning, which confuses us.
- // We don't expect to have relative paths in this function.
- // Don't use IsSeparator since we always want to allow backslashes.
- if (result[0] == FILE_PATH_LITERAL("/") ||
- result[0] == FILE_PATH_LITERAL("\\"))
- result.erase(result.begin());
-
-#if defined(OS_WIN)
- // On Windows, GetComponents will give us [ "C:", "/", "foo" ], and we
- // don't want the slash in there. This doesn't support input like "C:foo"
- // which means foo relative to the current directory of the C drive but
- // that's basically legacy DOS behavior we don't need to support.
- if (result.size() >= 2 && result[1].size() == 1 &&
- IsSlash(static_cast<char>(result[1][0])))
- result.erase(result.begin() + 1);
-#endif
-
- return result;
-}
-
-// Provides the equivalent of == for filesystem strings, trying to do
-// approximately the right thing with case.
-bool FilesystemStringsEqual(const base::FilePath::StringType& a,
- const base::FilePath::StringType& b) {
-#if defined(OS_WIN)
- // Assume case-insensitive filesystems on Windows. We use the CompareString
- // function to do a case-insensitive comparison based on the current locale
- // (we don't want GN to depend on ICU which is large and requires data
- // files). This isn't perfect, but getting this perfectly right is very
- // difficult and requires I/O, and this comparison should cover 99.9999% of
- // all cases.
- //
- // Note: The documentation for CompareString says it runs fastest on
- // null-terminated strings with -1 passed for the length, so we do that here.
- // There should not be embedded nulls in filesystem strings.
- return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE, a.c_str(),
- -1, b.c_str(), -1) == CSTR_EQUAL;
-#else
- // Assume case-sensitive filesystems on non-Windows.
- return a == b;
-#endif
-}
-
-// Helper function for computing subdirectories in the build directory
-// corresponding to absolute paths. This will try to resolve the absolute
-// path as a source-relative path first, and otherwise it creates a
-// special subdirectory for absolute paths to keep them from colliding with
-// other generated sources and outputs.
-void AppendFixedAbsolutePathSuffix(const BuildSettings* build_settings,
- const SourceDir& source_dir,
- OutputFile* result) {
- const std::string& build_dir = build_settings->build_dir().value();
-
- if (base::StartsWith(source_dir.value(), build_dir,
- base::CompareCase::SENSITIVE)) {
- size_t build_dir_size = build_dir.size();
- result->value().append(&source_dir.value()[build_dir_size],
- source_dir.value().size() - build_dir_size);
- } else {
- result->value().append("ABS_PATH");
-#if defined(OS_WIN)
- // Windows absolute path contains ':' after drive letter. Remove it to
- // avoid inserting ':' in the middle of path (eg. "ABS_PATH/C:/").
- std::string src_dir_value = source_dir.value();
- const auto colon_pos = src_dir_value.find(':');
- if (colon_pos != std::string::npos)
- src_dir_value.erase(src_dir_value.begin() + colon_pos);
-#else
- const std::string& src_dir_value = source_dir.value();
-#endif
- result->value().append(src_dir_value);
- }
-}
-
-size_t AbsPathLenWithNoTrailingSlash(const base::StringPiece& path) {
- size_t len = path.size();
-#if defined(OS_WIN)
- size_t min_len = 3;
-#else
- // On posix system. The minimal abs path is "/".
- size_t min_len = 1;
-#endif
- for (; len > min_len && IsSlash(path[len - 1]); len--)
- ;
- return len;
-}
-} // namespace
-
-std::string FilePathToUTF8(const base::FilePath::StringType& str) {
-#if defined(OS_WIN)
- return base::WideToUTF8(str);
-#else
- return str;
-#endif
-}
-
-base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
-#if defined(OS_WIN)
- return base::FilePath(base::UTF8ToWide(sp));
-#else
- return base::FilePath(sp.as_string());
-#endif
-}
-
-size_t FindExtensionOffset(const std::string& path) {
- for (int i = static_cast<int>(path.size()); i >= 0; i--) {
- if (IsSlash(path[i]))
- break;
- if (path[i] == '.')
- return i + 1;
- }
- return std::string::npos;
-}
-
-base::StringPiece FindExtension(const std::string* path) {
- size_t extension_offset = FindExtensionOffset(*path);
- if (extension_offset == std::string::npos)
- return base::StringPiece();
- return base::StringPiece(&path->data()[extension_offset],
- path->size() - extension_offset);
-}
-
-size_t FindFilenameOffset(const std::string& path) {
- for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
- if (IsSlash(path[i]))
- return i + 1;
- }
- return 0; // No filename found means everything was the filename.
-}
-
-base::StringPiece FindFilename(const std::string* path) {
- size_t filename_offset = FindFilenameOffset(*path);
- if (filename_offset == 0)
- return base::StringPiece(*path); // Everything is the file name.
- return base::StringPiece(&(*path).data()[filename_offset],
- path->size() - filename_offset);
-}
-
-base::StringPiece FindFilenameNoExtension(const std::string* path) {
- if (path->empty())
- return base::StringPiece();
- size_t filename_offset = FindFilenameOffset(*path);
- size_t extension_offset = FindExtensionOffset(*path);
-
- size_t name_len;
- if (extension_offset == std::string::npos)
- name_len = path->size() - filename_offset;
- else
- name_len = extension_offset - filename_offset - 1;
-
- return base::StringPiece(&(*path).data()[filename_offset], name_len);
-}
-
-void RemoveFilename(std::string* path) {
- path->resize(FindFilenameOffset(*path));
-}
-
-bool EndsWithSlash(const std::string& s) {
- return !s.empty() && IsSlash(s[s.size() - 1]);
-}
-
-base::StringPiece FindDir(const std::string* path) {
- size_t filename_offset = FindFilenameOffset(*path);
- if (filename_offset == 0u)
- return base::StringPiece();
- return base::StringPiece(path->data(), filename_offset);
-}
-
-base::StringPiece FindLastDirComponent(const SourceDir& dir) {
- const std::string& dir_string = dir.value();
-
- if (dir_string.empty())
- return base::StringPiece();
- int cur = static_cast<int>(dir_string.size()) - 1;
- DCHECK(dir_string[cur] == '/');
- int end = cur;
- cur--; // Skip before the last slash.
-
- for (; cur >= 0; cur--) {
- if (dir_string[cur] == '/')
- return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
- }
- return base::StringPiece(&dir_string[0], end);
-}
-
-bool IsStringInOutputDir(const SourceDir& output_dir, const std::string& str) {
- // This check will be wrong for all proper prefixes "e.g. "/output" will
- // match "/out" but we don't really care since this is just a sanity check.
- const std::string& dir_str = output_dir.value();
- return str.compare(0, dir_str.length(), dir_str) == 0;
-}
-
-bool EnsureStringIsInOutputDir(const SourceDir& output_dir,
- const std::string& str,
- const ParseNode* origin,
- Err* err) {
- if (IsStringInOutputDir(output_dir, str))
- return true; // Output directory is hardcoded.
-
- *err = Err(
- origin, "File is not inside output directory.",
- "The given file should be in the output directory. Normally you would "
- "specify\n\"$target_out_dir/foo\" or "
- "\"$target_gen_dir/foo\". I interpreted this as\n\"" +
- str + "\".");
- return false;
-}
-
-bool IsPathAbsolute(const base::StringPiece& path) {
- if (path.empty())
- return false;
-
- if (!IsSlash(path[0])) {
-#if defined(OS_WIN)
- // Check for Windows system paths like "C:\foo".
- if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
- return true;
-#endif
- return false; // Doesn't begin with a slash, is relative.
- }
-
- // Double forward slash at the beginning means source-relative (we don't
- // allow backslashes for denoting this).
- if (path.size() > 1 && path[1] == '/')
- return false;
-
- return true;
-}
-
-bool IsPathSourceAbsolute(const base::StringPiece& path) {
- return (path.size() >= 2 && path[0] == '/' && path[1] == '/');
-}
-
-bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
- const base::StringPiece& path,
- std::string* dest) {
- DCHECK(IsPathAbsolute(source_root));
- DCHECK(IsPathAbsolute(path));
-
- dest->clear();
-
- // There is no specification of how many slashes may be at the end of
- // source_root or path. Trim them off for easier string manipulation.
- size_t path_len = AbsPathLenWithNoTrailingSlash(path);
- size_t source_root_len = AbsPathLenWithNoTrailingSlash(source_root);
-
- if (source_root_len > path_len)
- return false; // The source root is longer: the path can never be inside.
-#if defined(OS_WIN)
- // Source root should be canonical on Windows. Note that the initial slash
- // must be forward slash, but that the other ones can be either forward or
- // backward.
- DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
- source_root[1] == ':' && IsSlash(source_root[2]));
-
- size_t after_common_index = std::string::npos;
- if (DoesBeginWindowsDriveLetter(path)) {
- // Handle "C:\foo"
- if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
- path.substr(0, source_root_len))) {
- after_common_index = source_root_len;
- if (path_len == source_root_len) {
- *dest = "//";
- return true;
- }
- } else {
- return false;
- }
- } else if (path[0] == '/' && source_root_len <= path_len - 1 &&
- DoesBeginWindowsDriveLetter(path.substr(1))) {
- // Handle "/C:/foo"
- if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
- path.substr(1, source_root_len))) {
- after_common_index = source_root_len + 1;
- if (path_len + 1 == source_root_len) {
- *dest = "//";
- return true;
- }
- } else {
- return false;
- }
- } else {
- return false;
- }
-
- // If we get here, there's a match and after_common_index identifies the
- // part after it.
-
- if (!IsSlash(path[after_common_index])) {
- // path is ${source-root}SUFIX/...
- return false;
- }
- // A source-root relative path, The input may have an unknown number of
- // slashes after the previous match. Skip over them.
- size_t first_after_slash = after_common_index + 1;
- while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
- first_after_slash++;
- dest->assign("//"); // Result is source root relative.
- dest->append(&path.data()[first_after_slash],
- path.size() - first_after_slash);
- return true;
-
-#else
-
- // On non-Windows this is easy. Since we know both are absolute, just do a
- // prefix check.
-
- if (path.substr(0, source_root_len) ==
- source_root.substr(0, source_root_len)) {
- if (path_len == source_root_len) {
- // path is equivalent to source_root.
- *dest = "//";
- return true;
- } else if (!IsSlash(path[source_root_len])) {
- // path is ${source-root}SUFIX/...
- return false;
- }
- // A source-root relative path, The input may have an unknown number of
- // slashes after the previous match. Skip over them.
- size_t first_after_slash = source_root_len + 1;
- while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
- first_after_slash++;
-
- dest->assign("//"); // Result is source root relative.
- dest->append(&path.data()[first_after_slash],
- path.size() - first_after_slash);
- return true;
- }
- return false;
-#endif
-}
-
-base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
- const base::FilePath& base,
- const base::FilePath& target) {
- DCHECK(base.IsAbsolute());
- DCHECK(target.IsAbsolute());
- std::vector<base::FilePath::StringType> base_components;
- std::vector<base::FilePath::StringType> target_components;
- base.GetComponents(&base_components);
- target.GetComponents(&target_components);
-#if defined(OS_WIN)
- // On Windows, it's impossible to have a relative path from C:\foo to D:\bar,
- // so return the target as an absolute path instead.
- if (base_components[0] != target_components[0])
- return target;
-
- // GetComponents() returns the first slash after the root. Set it to the
- // same value in both component lists so that relative paths between
- // "C:/foo/..." and "C:\foo\..." are computed correctly.
- target_components[1] = base_components[1];
-#endif
- size_t i;
- for (i = 0; i < base_components.size() && i < target_components.size(); i++) {
- if (base_components[i] != target_components[i])
- break;
- }
- std::vector<base::FilePath::StringType> relative_components;
- for (size_t j = i; j < base_components.size(); j++)
- relative_components.push_back(base::FilePath::kParentDirectory);
- for (size_t j = i; j < target_components.size(); j++)
- relative_components.push_back(target_components[j]);
- if (relative_components.size() <= 1) {
- // In case the file pointed-to is an executable, prepend the current
- // directory to the path -- if the path was "gn", use "./gn" instead. If
- // the file path is used in a command, this prevents issues where "gn" might
- // not be in the PATH (or it is in the path, and the wrong gn is used).
- relative_components.insert(relative_components.begin(),
- base::FilePath::kCurrentDirectory);
- }
- // base::FilePath::Append(component) replaces the file path with |component|
- // if the path is base::Filepath::kCurrentDirectory. We want to preserve the
- // leading "./", so we build the path ourselves and use that to construct the
- // base::FilePath.
- base::FilePath::StringType separator(&base::FilePath::kSeparators[0], 1);
- return base::FilePath(base::JoinString(relative_components, separator));
-}
-
-void NormalizePath(std::string* path, const base::StringPiece& source_root) {
- char* pathbuf = path->empty() ? nullptr : &(*path)[0];
-
- // top_index is the first character we can modify in the path. Anything
- // before this indicates where the path is relative to.
- size_t top_index = 0;
- bool is_relative = true;
- if (!path->empty() && pathbuf[0] == '/') {
- is_relative = false;
-
- if (path->size() > 1 && pathbuf[1] == '/') {
- // Two leading slashes, this is a path into the source dir.
- top_index = 2;
- } else {
- // One leading slash, this is a system-absolute path.
- top_index = 1;
- }
- }
-
- size_t dest_i = top_index;
- for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
- if (pathbuf[src_i] == '.') {
- if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
- // Slash followed by a dot, see if it's something special.
- size_t consumed_len;
- switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
- case NOT_A_DIRECTORY:
- // Copy the dot to the output, it means nothing special.
- pathbuf[dest_i++] = pathbuf[src_i++];
- break;
- case DIRECTORY_CUR:
- // Current directory, just skip the input.
- src_i += consumed_len;
- break;
- case DIRECTORY_UP:
- // Back up over previous directory component. If we're already
- // at the top, preserve the "..".
- if (dest_i > top_index) {
- // The previous char was a slash, remove it.
- dest_i--;
- }
-
- if (dest_i == top_index) {
- if (is_relative) {
- // We're already at the beginning of a relative input, copy the
- // ".." and continue. We need the trailing slash if there was
- // one before (otherwise we're at the end of the input).
- pathbuf[dest_i++] = '.';
- pathbuf[dest_i++] = '.';
- if (consumed_len == 3)
- pathbuf[dest_i++] = '/';
-
- // This also makes a new "root" that we can't delete by going
- // up more levels. Otherwise "../.." would collapse to
- // nothing.
- top_index = dest_i;
- } else if (top_index == 2 && !source_root.empty()) {
- // |path| was passed in as a source-absolute path. Prepend
- // |source_root| to make |path| absolute. |source_root| must not
- // end with a slash unless we are at root.
- DCHECK(source_root.size() == 1u ||
- !IsSlash(source_root[source_root.size() - 1u]));
- size_t source_root_len = source_root.size();
-
-#if defined(OS_WIN)
- // On Windows, if the source_root does not start with a slash,
- // append one here for consistency.
- if (!IsSlash(source_root[0])) {
- path->insert(0, "/" + source_root.as_string());
- source_root_len++;
- } else {
- path->insert(0, source_root.data(), source_root_len);
- }
-
- // Normalize slashes in source root portion.
- for (size_t i = 0; i < source_root_len; ++i) {
- if ((*path)[i] == '\\')
- (*path)[i] = '/';
- }
-#else
- path->insert(0, source_root.data(), source_root_len);
-#endif
-
- // |path| is now absolute, so |top_index| is 1. |dest_i| and
- // |src_i| should be incremented to keep the same relative
- // position. Comsume the leading "//" by decrementing |dest_i|.
- top_index = 1;
- pathbuf = &(*path)[0];
- dest_i += source_root_len - 2;
- src_i += source_root_len;
-
- // Just find the previous slash or the beginning of input.
- while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
- dest_i--;
- }
- // Otherwise we're at the beginning of a system-absolute path, or
- // a source-absolute path for which we don't know the absolute
- // path. Don't allow ".." to go up another level, and just eat it.
- } else {
- // Just find the previous slash or the beginning of input.
- while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
- dest_i--;
- }
- src_i += consumed_len;
- }
- } else {
- // Dot not preceeded by a slash, copy it literally.
- pathbuf[dest_i++] = pathbuf[src_i++];
- }
- } else if (IsSlash(pathbuf[src_i])) {
- if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
- // Two slashes in a row, skip over it.
- src_i++;
- } else {
- // Just one slash, copy it, normalizing to foward slash.
- pathbuf[dest_i] = '/';
- dest_i++;
- src_i++;
- }
- } else {
- // Input nothing special, just copy it.
- pathbuf[dest_i++] = pathbuf[src_i++];
- }
- }
- path->resize(dest_i);
-}
-
-void ConvertPathToSystem(std::string* path) {
-#if defined(OS_WIN)
- for (size_t i = 0; i < path->size(); i++) {
- if ((*path)[i] == '/')
- (*path)[i] = '\\';
- }
-#endif
-}
-
-std::string MakeRelativePath(const std::string& input,
- const std::string& dest) {
-#if defined(OS_WIN)
- // Make sure that absolute |input| path starts with a slash if |dest| path
- // does. Otherwise skipping common prefixes won't work properly. Ensure the
- // same for |dest| path too.
- if (IsPathAbsolute(input) && !IsSlash(input[0]) && IsSlash(dest[0])) {
- std::string corrected_input(1, dest[0]);
- corrected_input.append(input);
- return MakeRelativePath(corrected_input, dest);
- }
- if (IsPathAbsolute(dest) && !IsSlash(dest[0]) && IsSlash(input[0])) {
- std::string corrected_dest(1, input[0]);
- corrected_dest.append(dest);
- return MakeRelativePath(input, corrected_dest);
- }
-
- // Make sure that both absolute paths use the same drive letter case.
- if (IsPathAbsolute(input) && IsPathAbsolute(dest) && input.size() > 1 &&
- dest.size() > 1) {
- int letter_pos = base::IsAsciiAlpha(input[0]) ? 0 : 1;
- if (input[letter_pos] != dest[letter_pos] &&
- base::ToUpperASCII(input[letter_pos]) ==
- base::ToUpperASCII(dest[letter_pos])) {
- std::string corrected_input = input;
- corrected_input[letter_pos] = dest[letter_pos];
- return MakeRelativePath(corrected_input, dest);
- }
- }
-#endif
-
- std::string ret;
-
- // Skip the common prefixes of the source and dest as long as they end in
- // a [back]slash.
- size_t common_prefix_len = 0;
- size_t max_common_length = std::min(input.size(), dest.size());
- for (size_t i = common_prefix_len; i < max_common_length; i++) {
- if (IsSlash(input[i]) && IsSlash(dest[i]))
- common_prefix_len = i + 1;
- else if (input[i] != dest[i])
- break;
- }
-
- // Invert the dest dir starting from the end of the common prefix.
- for (size_t i = common_prefix_len; i < dest.size(); i++) {
- if (IsSlash(dest[i]))
- ret.append("../");
- }
-
- // Append any remaining unique input.
- ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
-
- // If the result is still empty, the paths are the same.
- if (ret.empty())
- ret.push_back('.');
-
- return ret;
-}
-
-std::string RebasePath(const std::string& input,
- const SourceDir& dest_dir,
- const base::StringPiece& source_root) {
- std::string ret;
- DCHECK(source_root.empty() || !source_root.ends_with("/"));
-
- bool input_is_source_path =
- (input.size() >= 2 && input[0] == '/' && input[1] == '/');
-
- if (!source_root.empty() &&
- (!input_is_source_path || !dest_dir.is_source_absolute())) {
- std::string input_full;
- std::string dest_full;
- if (input_is_source_path) {
- source_root.AppendToString(&input_full);
- input_full.push_back('/');
- input_full.append(input, 2, std::string::npos);
- } else {
- input_full.append(input);
- }
- if (dest_dir.is_source_absolute()) {
- source_root.AppendToString(&dest_full);
- dest_full.push_back('/');
- dest_full.append(dest_dir.value(), 2, std::string::npos);
- } else {
-#if defined(OS_WIN)
- // On Windows, SourceDir system-absolute paths start
- // with /, e.g. "/C:/foo/bar".
- const std::string& value = dest_dir.value();
- if (value.size() > 2 && value[2] == ':')
- dest_full.append(dest_dir.value().substr(1));
- else
- dest_full.append(dest_dir.value());
-#else
- dest_full.append(dest_dir.value());
-#endif
- }
- bool remove_slash = false;
- if (!EndsWithSlash(input_full)) {
- input_full.push_back('/');
- remove_slash = true;
- }
- ret = MakeRelativePath(input_full, dest_full);
- if (remove_slash && ret.size() > 1)
- ret.resize(ret.size() - 1);
- return ret;
- }
-
- ret = MakeRelativePath(input, dest_dir.value());
- return ret;
-}
-
-base::FilePath ResolvePath(const std::string& value,
- bool as_file,
- const base::FilePath& source_root) {
- if (value.empty())
- return base::FilePath();
-
- std::string converted;
- if (!IsPathSourceAbsolute(value)) {
- if (value.size() > 2 && value[2] == ':') {
- // Windows path, strip the leading slash.
- converted.assign(&value[1], value.size() - 1);
- } else {
- converted.assign(value);
- }
- return base::FilePath(UTF8ToFilePath(converted));
- }
-
- // String the double-leading slash for source-relative paths.
- converted.assign(&value[2], value.size() - 2);
-
- if (as_file && source_root.empty())
- return UTF8ToFilePath(converted).NormalizePathSeparatorsTo('/');
-
- return source_root.Append(UTF8ToFilePath(converted))
- .NormalizePathSeparatorsTo('/');
-}
-
-template <typename StringType>
-std::string ResolveRelative(const StringType& input,
- const std::string& value,
- bool as_file,
- const base::StringPiece& source_root) {
- std::string result;
-
- if (input.size() >= 2 && input[0] == '/' && input[1] == '/') {
- // Source-relative.
- result.assign(input.data(), input.size());
- if (!as_file) {
- if (!EndsWithSlash(result))
- result.push_back('/');
- }
- NormalizePath(&result, source_root);
- return result;
- } else if (IsPathAbsolute(input)) {
- if (source_root.empty() ||
- !MakeAbsolutePathRelativeIfPossible(source_root, input, &result)) {
-#if defined(OS_WIN)
- if (input[0] != '/') // See the file case for why we do this check.
- result = "/";
-#endif
- result.append(input.data(), input.size());
- }
- NormalizePath(&result);
- if (!as_file) {
- if (!EndsWithSlash(result))
- result.push_back('/');
- }
- return result;
- }
-
- if (!source_root.empty()) {
- std::string absolute =
- FilePathToUTF8(ResolvePath(value, as_file, UTF8ToFilePath(source_root))
- .AppendASCII(input)
- .value());
- NormalizePath(&absolute);
- if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute, &result)) {
-#if defined(OS_WIN)
- if (absolute[0] != '/') // See the file case for why we do this check.
- result = "/";
-#endif
- result.append(absolute.data(), absolute.size());
- }
- if (!as_file && !EndsWithSlash(result))
- result.push_back('/');
- return result;
- }
-
- // With no source_root, there's nothing we can do about
- // e.g. input=../../../path/to/file and value=//source and we'll
- // errornously return //file.
- result.reserve(value.size() + input.size());
- result.assign(value);
- result.append(input.data(), input.size());
-
- NormalizePath(&result);
- if (!as_file && !EndsWithSlash(result))
- result.push_back('/');
-
- return result;
-}
-
-// Explicit template instantiation
-template std::string ResolveRelative(const base::StringPiece& input,
- const std::string& value,
- bool as_file,
- const base::StringPiece& source_root);
-
-template std::string ResolveRelative(const std::string& input,
- const std::string& value,
- bool as_file,
- const base::StringPiece& source_root);
-
-std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
- std::string ret;
-
- if (dir.value().empty()) {
- // Just keep input the same.
- } else if (dir.value() == "/") {
- ret.assign("/.");
- } else if (dir.value() == "//") {
- ret.assign("//.");
- } else {
- ret.assign(dir.value());
- ret.resize(ret.size() - 1);
- }
- return ret;
-}
-
-SourceDir SourceDirForPath(const base::FilePath& source_root,
- const base::FilePath& path) {
- std::vector<base::FilePath::StringType> source_comp =
- GetPathComponents(source_root);
- std::vector<base::FilePath::StringType> path_comp = GetPathComponents(path);
-
- // See if path is inside the source root by looking for each of source root's
- // components at the beginning of path.
- bool is_inside_source;
- if (path_comp.size() < source_comp.size() || source_root.empty()) {
- // Too small to fit.
- is_inside_source = false;
- } else {
- is_inside_source = true;
- for (size_t i = 0; i < source_comp.size(); i++) {
- if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
- is_inside_source = false;
- break;
- }
- }
- }
-
- std::string result_str;
- size_t initial_path_comp_to_use;
- if (is_inside_source) {
- // Construct a source-relative path beginning in // and skip all of the
- // shared directories.
- result_str = "//";
- initial_path_comp_to_use = source_comp.size();
- } else {
- // Not inside source code, construct a system-absolute path.
- result_str = "/";
- initial_path_comp_to_use = 0;
- }
-
- for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
- result_str.append(FilePathToUTF8(path_comp[i]));
- result_str.push_back('/');
- }
- return SourceDir(std::move(result_str));
-}
-
-SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
- base::FilePath cd;
- base::GetCurrentDirectory(&cd);
- return SourceDirForPath(source_root, cd);
-}
-
-std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
- // The default toolchain has no subdir.
- if (is_default)
- return std::string();
-
- // For now just assume the toolchain name is always a valid dir name. We may
- // want to clean up the in the future.
- return toolchain_label.name() + "/";
-}
-
-bool ContentsEqual(const base::FilePath& file_path, const std::string& data) {
- // Compare file and stream sizes first. Quick and will save us some time if
- // they are different sizes.
- int64_t file_size;
- if (!base::GetFileSize(file_path, &file_size) ||
- static_cast<size_t>(file_size) != data.size()) {
- return false;
- }
-
- std::string file_data;
- file_data.resize(file_size);
- if (!base::ReadFileToString(file_path, &file_data))
- return false;
-
- return file_data == data;
-}
-
-bool WriteFileIfChanged(const base::FilePath& file_path,
- const std::string& data,
- Err* err) {
- if (ContentsEqual(file_path, data))
- return true;
-
- return WriteFile(file_path, data, err);
-}
-
-bool WriteFile(const base::FilePath& file_path,
- const std::string& data,
- Err* err) {
- // Create the directory if necessary.
- if (!base::CreateDirectory(file_path.DirName())) {
- if (err) {
- *err =
- Err(Location(), "Unable to create directory.",
- "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
- }
- return false;
- }
-
- int size = static_cast<int>(data.size());
- bool write_success = false;
-
-#if defined(OS_WIN)
- // On Windows, provide a custom implementation of base::WriteFile. Sometimes
- // the base version fails, especially on the bots. The guess is that Windows
- // Defender or other antivirus programs still have the file open (after
- // checking for the read) when the write happens immediately after. This
- // version opens with FILE_SHARE_READ (normally not what you want when
- // replacing the entire contents of the file) which lets us continue even if
- // another program has the file open for reading. See http://crbug.com/468437
- base::win::ScopedHandle file(::CreateFile(file_path.value().c_str(),
- GENERIC_WRITE, FILE_SHARE_READ,
- NULL, CREATE_ALWAYS, 0, NULL));
- if (file.IsValid()) {
- DWORD written;
- BOOL result = ::WriteFile(file.Get(), data.c_str(), size, &written, NULL);
- if (result) {
- if (static_cast<int>(written) == size) {
- write_success = true;
- } else {
- // Didn't write all the bytes.
- LOG(ERROR) << "wrote" << written << " bytes to "
- << base::UTF16ToUTF8(file_path.value()) << " expected "
- << size;
- }
- } else {
- // WriteFile failed.
- PLOG(ERROR) << "writing file " << base::UTF16ToUTF8(file_path.value())
- << " failed";
- }
- } else {
- PLOG(ERROR) << "CreateFile failed for path "
- << base::UTF16ToUTF8(file_path.value());
- }
-#else
- write_success = base::WriteFile(file_path, data.c_str(), size) == size;
-#endif
-
- if (!write_success && err) {
- *err = Err(Location(), "Unable to write file.",
- "I was writing \"" + FilePathToUTF8(file_path) + "\".");
- }
-
- return write_success;
-}
-
-BuildDirContext::BuildDirContext(const Target* target)
- : BuildDirContext(target->settings()) {}
-
-BuildDirContext::BuildDirContext(const Settings* settings)
- : BuildDirContext(settings->build_settings(),
- settings->toolchain_label(),
- settings->is_default()) {}
-
-BuildDirContext::BuildDirContext(const Scope* execution_scope)
- : BuildDirContext(execution_scope->settings()) {}
-
-BuildDirContext::BuildDirContext(const Scope* execution_scope,
- const Label& toolchain_label)
- : BuildDirContext(execution_scope->settings()->build_settings(),
- toolchain_label,
- execution_scope->settings()->default_toolchain_label() ==
- toolchain_label) {}
-
-BuildDirContext::BuildDirContext(const BuildSettings* in_build_settings,
- const Label& in_toolchain_label,
- bool in_is_default_toolchain)
- : build_settings(in_build_settings),
- toolchain_label(in_toolchain_label),
- is_default_toolchain(in_is_default_toolchain) {}
-
-SourceDir GetBuildDirAsSourceDir(const BuildDirContext& context,
- BuildDirType type) {
- return GetBuildDirAsOutputFile(context, type)
- .AsSourceDir(context.build_settings);
-}
-
-OutputFile GetBuildDirAsOutputFile(const BuildDirContext& context,
- BuildDirType type) {
- OutputFile result(GetOutputSubdirName(context.toolchain_label,
- context.is_default_toolchain));
- DCHECK(result.value().empty() || result.value().back() == '/');
-
- if (type == BuildDirType::GEN)
- result.value().append("gen/");
- else if (type == BuildDirType::OBJ)
- result.value().append("obj/");
- return result;
-}
-
-SourceDir GetSubBuildDirAsSourceDir(const BuildDirContext& context,
- const SourceDir& source_dir,
- BuildDirType type) {
- return GetSubBuildDirAsOutputFile(context, source_dir, type)
- .AsSourceDir(context.build_settings);
-}
-
-OutputFile GetSubBuildDirAsOutputFile(const BuildDirContext& context,
- const SourceDir& source_dir,
- BuildDirType type) {
- DCHECK(type != BuildDirType::TOOLCHAIN_ROOT);
- OutputFile result = GetBuildDirAsOutputFile(context, type);
-
- if (source_dir.is_source_absolute()) {
- // The source dir is source-absolute, so we trim off the two leading
- // slashes to append to the toolchain object directory.
- result.value().append(&source_dir.value()[2],
- source_dir.value().size() - 2);
- } else {
- // System-absolute.
- AppendFixedAbsolutePathSuffix(context.build_settings, source_dir, &result);
- }
- return result;
-}
-
-SourceDir GetBuildDirForTargetAsSourceDir(const Target* target,
- BuildDirType type) {
- return GetSubBuildDirAsSourceDir(BuildDirContext(target),
- target->label().dir(), type);
-}
-
-OutputFile GetBuildDirForTargetAsOutputFile(const Target* target,
- BuildDirType type) {
- return GetSubBuildDirAsOutputFile(BuildDirContext(target),
- target->label().dir(), type);
-}
-
-SourceDir GetScopeCurrentBuildDirAsSourceDir(const Scope* scope,
- BuildDirType type) {
- if (type == BuildDirType::TOOLCHAIN_ROOT)
- return GetBuildDirAsSourceDir(BuildDirContext(scope), type);
- return GetSubBuildDirAsSourceDir(BuildDirContext(scope),
- scope->GetSourceDir(), type);
-}
diff --git a/gn/tools/gn/filesystem_utils.h b/gn/tools/gn/filesystem_utils.h
deleted file mode 100644
index 220c4f83ada..00000000000
--- a/gn/tools/gn/filesystem_utils.h
+++ /dev/null
@@ -1,305 +0,0 @@
-// 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 TOOLS_GN_FILESYSTEM_UTILS_H_
-#define TOOLS_GN_FILESYSTEM_UTILS_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/strings/string_piece.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target.h"
-
-class Err;
-
-std::string FilePathToUTF8(const base::FilePath::StringType& str);
-inline std::string FilePathToUTF8(const base::FilePath& path) {
- return FilePathToUTF8(path.value());
-}
-base::FilePath UTF8ToFilePath(const base::StringPiece& sp);
-
-// Extensions -----------------------------------------------------------------
-
-// Returns the index of the extension (character after the last dot not after a
-// slash). Returns std::string::npos if not found. Returns path.size() if the
-// file ends with a dot.
-size_t FindExtensionOffset(const std::string& path);
-
-// Returns a string piece pointing into the input string identifying the
-// extension. Note that the input pointer must outlive the output.
-base::StringPiece FindExtension(const std::string* path);
-
-// Filename parts -------------------------------------------------------------
-
-// Returns the offset of the character following the last slash, or
-// 0 if no slash was found. Returns path.size() if the path ends with a slash.
-// Note that the input pointer must outlive the output.
-size_t FindFilenameOffset(const std::string& path);
-
-// Returns a string piece pointing into the input string identifying the
-// file name (following the last slash, including the extension). Note that the
-// input pointer must outlive the output.
-base::StringPiece FindFilename(const std::string* path);
-
-// Like FindFilename but does not include the extension.
-base::StringPiece FindFilenameNoExtension(const std::string* path);
-
-// Removes everything after the last slash. The last slash, if any, will be
-// preserved.
-void RemoveFilename(std::string* path);
-
-// Returns if the given character is a slash. This allows both slashes and
-// backslashes for consistency between Posix and Windows (as opposed to
-// FilePath::IsSeparator which is based on the current platform).
-inline bool IsSlash(const char ch) {
- return ch == '/' || ch == '\\';
-}
-
-// Returns true if the given path ends with a slash.
-bool EndsWithSlash(const std::string& s);
-
-// Path parts -----------------------------------------------------------------
-
-// Returns a string piece pointing into the input string identifying the
-// directory name of the given path, including the last slash. Note that the
-// input pointer must outlive the output.
-base::StringPiece FindDir(const std::string* path);
-
-// Returns the substring identifying the last component of the dir, or the
-// empty substring if none. For example "//foo/bar/" -> "bar".
-base::StringPiece FindLastDirComponent(const SourceDir& dir);
-
-// Returns true if the given string is in the given output dir. This is pretty
-// stupid and doesn't handle "." and "..", etc., it is designed for a sanity
-// check to keep people from writing output files to the source directory
-// accidentally.
-bool IsStringInOutputDir(const SourceDir& output_dir, const std::string& str);
-
-// Verifies that the given string references a file inside of the given
-// directory. This just uses IsStringInOutputDir above.
-//
-// The origin will be blamed in the error.
-//
-// If the file isn't in the dir, returns false and sets the error. Otherwise
-// returns true and leaves the error untouched.
-bool EnsureStringIsInOutputDir(const SourceDir& output_dir,
- const std::string& str,
- const ParseNode* origin,
- Err* err);
-
-// ----------------------------------------------------------------------------
-
-// Returns true if the input string is absolute. Double-slashes at the
-// beginning are treated as source-relative paths. On Windows, this handles
-// paths of both the native format: "C:/foo" and ours "/C:/foo"
-bool IsPathAbsolute(const base::StringPiece& path);
-
-// Returns true if the input string is source-absolute. Source-absolute
-// paths begin with two forward slashes and resolve as if they are
-// relative to the source root.
-bool IsPathSourceAbsolute(const base::StringPiece& path);
-
-// Given an absolute path, checks to see if is it is inside the source root.
-// If it is, fills a source-absolute path into the given output and returns
-// true. If it isn't, clears the dest and returns false.
-//
-// The source_root should be a base::FilePath converted to UTF-8. On Windows,
-// it should begin with a "C:/" rather than being our SourceFile's style
-// ("/C:/"). The source root can end with a slash or not.
-//
-// Note that this does not attempt to normalize slashes in the output.
-bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
- const base::StringPiece& path,
- std::string* dest);
-
-// Given two absolute paths |base| and |target|, returns a relative path to
-// |target| as if the current directory was |base|. The relative path returned
-// is minimal. For example, if "../../a/b/" and "../b" are both valid, then the
-// latter will be returned. On Windows, it's impossible to have a relative path
-// from C:\foo to D:\bar, so the absolute path |target| is returned instead for
-// this case.
-base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
- const base::FilePath& base,
- const base::FilePath& target);
-
-// Collapses "." and sequential "/"s and evaluates "..". |path| may be
-// system-absolute, source-absolute, or relative. If |path| is source-absolute
-// and |source_root| is non-empty, |path| may be system absolute after this
-// function returns, if |path| references the filesystem outside of
-// |source_root| (ex. path = "//.."). In this case on Windows, |path| will have
-// a leading slash. Otherwise, |path| will retain its relativity. |source_root|
-// must not end with a slash.
-void NormalizePath(std::string* path,
- const base::StringPiece& source_root = base::StringPiece());
-
-// Converts slashes to backslashes for Windows. Keeps the string unchanged
-// for other systems.
-void ConvertPathToSystem(std::string* path);
-
-// Takes a path, |input|, and makes it relative to the given directory
-// |dest_dir|. Both inputs may be source-relative (e.g. begins with
-// with "//") or may be absolute.
-//
-// If supplied, the |source_root| parameter is the absolute path to
-// the source root and not end in a slash. Unless you know that the
-// inputs are always source relative, this should be supplied.
-std::string RebasePath(
- const std::string& input,
- const SourceDir& dest_dir,
- const base::StringPiece& source_root = base::StringPiece());
-
-// Resolves a file or dir name (parameter input) relative to
-// value directory. Will return an empty SourceDir/File on error
-// and set the give *err pointer (required). Empty input is always an error.
-// Returned value can be used to set value in either SourceFile or SourceDir
-// (based on as_file parameter).
-//
-// Parameter as_file defines whether result path will look like a file path
-// or it should be treated as a directory (contains "/" and the end
-// of the string).
-//
-// If source_root is supplied, these functions will additionally handle the
-// case where the input is a system-absolute but still inside the source
-// tree. This is the case for some external tools.
-template <typename StringType>
-std::string ResolveRelative(const StringType& input,
- const std::string& value,
- bool as_file,
- const base::StringPiece& source_root);
-
-// Resolves source file or directory relative to some given source root. Returns
-// an empty file path on error.
-base::FilePath ResolvePath(const std::string& value,
- bool as_file,
- const base::FilePath& source_root);
-
-// Returns the given directory with no terminating slash at the end, such that
-// appending a slash and more stuff will produce a valid path.
-//
-// If the directory refers to either the source or system root, we'll append
-// a "." so this remains valid.
-std::string DirectoryWithNoLastSlash(const SourceDir& dir);
-
-// Returns the "best" SourceDir representing the given path. If it's inside the
-// given source_root, a source-relative directory will be returned (e.g.
-// "//foo/bar.cc". If it's outside of the source root or the source root is
-// empty, a system-absolute directory will be returned.
-SourceDir SourceDirForPath(const base::FilePath& source_root,
- const base::FilePath& path);
-
-// Like SourceDirForPath but returns the SourceDir representing the current
-// directory.
-SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root);
-
-// Given the label of a toolchain and whether that toolchain is the default
-// toolchain, returns the name of the subdirectory for that toolchain's
-// output. This will be the empty string to indicate that the toolchain outputs
-// go in the root build directory. Otherwise, the result will end in a slash.
-std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default);
-
-// Returns true if the contents of the file and stream given are equal, false
-// otherwise.
-bool ContentsEqual(const base::FilePath& file_path, const std::string& data);
-
-// Writes given stream contents to the given file if it differs from existing
-// file contents. Returns true if new contents was successfully written or
-// existing file contents doesn't need updating, false on write error. |err| is
-// set on write error if not nullptr.
-bool WriteFileIfChanged(const base::FilePath& file_path,
- const std::string& data,
- Err* err);
-
-// Writes given stream contents to the given file. Returns true if data was
-// successfully written, false otherwise. |err| is set on error if not nullptr.
-bool WriteFile(const base::FilePath& file_path,
- const std::string& data,
- Err* err);
-
-// -----------------------------------------------------------------------------
-
-enum class BuildDirType {
- // Returns the root toolchain dir rather than the generated or output
- // subdirectories. This is valid only for the toolchain directory getters.
- // Asking for this for a target or source dir makes no sense.
- TOOLCHAIN_ROOT,
-
- // Generated file directory.
- GEN,
-
- // Output file directory.
- OBJ,
-};
-
-// In different contexts, different information is known about the toolchain in
-// question. If you have a Target or settings object, everything can be
-// extracted from there. But when querying label information on something in
-// another toolchain, for example, the only thing known (it may not even exist)
-// is the toolchain label string and whether it matches the default toolchain.
-//
-// This object extracts the relevant information from a variety of input
-// types for the convenience of the caller.
-class BuildDirContext {
- public:
- // Extracts toolchain information associated with the given target.
- explicit BuildDirContext(const Target* target);
-
- // Extracts toolchain information associated with the given settings object.
- explicit BuildDirContext(const Settings* settings);
-
- // Extrats toolchain information from the current toolchain of the scope.
- explicit BuildDirContext(const Scope* execution_scope);
-
- // Extracts the default toolchain information from the given execution
- // scope. The toolchain you want to query must be passed in. This doesn't
- // use the settings object from the Scope so one can query other toolchains.
- // If you want to use the scope's current toolchain, use the version above.
- BuildDirContext(const Scope* execution_scope, const Label& toolchain_label);
-
- // Specify all information manually.
- BuildDirContext(const BuildSettings* build_settings,
- const Label& toolchain_label,
- bool is_default_toolchain);
-
- const BuildSettings* build_settings;
- const Label& toolchain_label;
- bool is_default_toolchain;
-};
-
-// Returns the root, object, or generated file directory for the toolchain.
-//
-// The toolchain object file root is never exposed in GN (there is no
-// root_obj_dir variable) so BuildDirType::OBJ would normally never be passed
-// to this function except when it's called by one of the variants below that
-// append paths to it.
-SourceDir GetBuildDirAsSourceDir(const BuildDirContext& context,
- BuildDirType type);
-OutputFile GetBuildDirAsOutputFile(const BuildDirContext& context,
- BuildDirType type);
-
-// Returns the output or generated file directory corresponding to the given
-// source directory.
-SourceDir GetSubBuildDirAsSourceDir(const BuildDirContext& context,
- const SourceDir& source_dir,
- BuildDirType type);
-OutputFile GetSubBuildDirAsOutputFile(const BuildDirContext& context,
- const SourceDir& source_dir,
- BuildDirType type);
-
-// Returns the output or generated file directory corresponding to the given
-// target.
-SourceDir GetBuildDirForTargetAsSourceDir(const Target* target,
- BuildDirType type);
-OutputFile GetBuildDirForTargetAsOutputFile(const Target* target,
- BuildDirType type);
-
-// Returns the scope's current directory.
-SourceDir GetScopeCurrentBuildDirAsSourceDir(const Scope* scope,
- BuildDirType type);
-// Lack of OutputDir version is due only to it not currently being needed,
-// please add one if you need it.
-
-#endif // TOOLS_GN_FILESYSTEM_UTILS_H_
diff --git a/gn/tools/gn/filesystem_utils_unittest.cc b/gn/tools/gn/filesystem_utils_unittest.cc
deleted file mode 100644
index 433ea367381..00000000000
--- a/gn/tools/gn/filesystem_utils_unittest.cc
+++ /dev/null
@@ -1,868 +0,0 @@
-// 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 "tools/gn/filesystem_utils.h"
-
-#include <thread>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/target.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-TEST(FilesystemUtils, FileExtensionOffset) {
- EXPECT_EQ(std::string::npos, FindExtensionOffset(""));
- EXPECT_EQ(std::string::npos, FindExtensionOffset("foo/bar/baz"));
- EXPECT_EQ(4u, FindExtensionOffset("foo."));
- EXPECT_EQ(4u, FindExtensionOffset("f.o.bar"));
- EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/"));
- EXPECT_EQ(std::string::npos, FindExtensionOffset("foo.bar/baz"));
-}
-
-TEST(FilesystemUtils, FindExtension) {
- std::string input;
- EXPECT_EQ("", FindExtension(&input).as_string());
- input = "foo/bar/baz";
- EXPECT_EQ("", FindExtension(&input).as_string());
- input = "foo.";
- EXPECT_EQ("", FindExtension(&input).as_string());
- input = "f.o.bar";
- EXPECT_EQ("bar", FindExtension(&input).as_string());
- input = "foo.bar/";
- EXPECT_EQ("", FindExtension(&input).as_string());
- input = "foo.bar/baz";
- EXPECT_EQ("", FindExtension(&input).as_string());
-}
-
-TEST(FilesystemUtils, FindFilenameOffset) {
- EXPECT_EQ(0u, FindFilenameOffset(""));
- EXPECT_EQ(0u, FindFilenameOffset("foo"));
- EXPECT_EQ(4u, FindFilenameOffset("foo/"));
- EXPECT_EQ(4u, FindFilenameOffset("foo/bar"));
-}
-
-TEST(FilesystemUtils, RemoveFilename) {
- std::string s;
-
- RemoveFilename(&s);
- EXPECT_STREQ("", s.c_str());
-
- s = "foo";
- RemoveFilename(&s);
- EXPECT_STREQ("", s.c_str());
-
- s = "/";
- RemoveFilename(&s);
- EXPECT_STREQ("/", s.c_str());
-
- s = "foo/bar";
- RemoveFilename(&s);
- EXPECT_STREQ("foo/", s.c_str());
-
- s = "foo/bar/baz.cc";
- RemoveFilename(&s);
- EXPECT_STREQ("foo/bar/", s.c_str());
-}
-
-TEST(FilesystemUtils, FindDir) {
- std::string input;
- EXPECT_EQ("", FindDir(&input));
- input = "/";
- EXPECT_EQ("/", FindDir(&input));
- input = "foo/";
- EXPECT_EQ("foo/", FindDir(&input));
- input = "foo/bar/baz";
- EXPECT_EQ("foo/bar/", FindDir(&input));
-}
-
-TEST(FilesystemUtils, FindLastDirComponent) {
- SourceDir empty;
- EXPECT_EQ("", FindLastDirComponent(empty));
-
- SourceDir root("/");
- EXPECT_EQ("", FindLastDirComponent(root));
-
- SourceDir srcroot("//");
- EXPECT_EQ("", FindLastDirComponent(srcroot));
-
- SourceDir regular1("//foo/");
- EXPECT_EQ("foo", FindLastDirComponent(regular1));
-
- SourceDir regular2("//foo/bar/");
- EXPECT_EQ("bar", FindLastDirComponent(regular2));
-}
-
-TEST(FilesystemUtils, EnsureStringIsInOutputDir) {
- SourceDir output_dir("//out/Debug/");
-
- // Some outside.
- Err err;
- EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "//foo", nullptr, &err));
- EXPECT_TRUE(err.has_error());
- err = Err();
- EXPECT_FALSE(
- EnsureStringIsInOutputDir(output_dir, "//out/Debugit", nullptr, &err));
- EXPECT_TRUE(err.has_error());
-
- // Some inside.
- err = Err();
- EXPECT_TRUE(
- EnsureStringIsInOutputDir(output_dir, "//out/Debug/", nullptr, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(
- EnsureStringIsInOutputDir(output_dir, "//out/Debug/foo", nullptr, &err));
- EXPECT_FALSE(err.has_error());
-
- // Pattern but no template expansions are allowed.
- EXPECT_FALSE(EnsureStringIsInOutputDir(output_dir, "{{source_gen_dir}}",
- nullptr, &err));
- EXPECT_TRUE(err.has_error());
-}
-
-TEST(FilesystemUtils, IsPathAbsolute) {
- EXPECT_TRUE(IsPathAbsolute("/foo/bar"));
- EXPECT_TRUE(IsPathAbsolute("/"));
- EXPECT_FALSE(IsPathAbsolute(""));
- EXPECT_FALSE(IsPathAbsolute("//"));
- EXPECT_FALSE(IsPathAbsolute("//foo/bar"));
-
-#if defined(OS_WIN)
- EXPECT_TRUE(IsPathAbsolute("C:/foo"));
- EXPECT_TRUE(IsPathAbsolute("C:/"));
- EXPECT_TRUE(IsPathAbsolute("C:\\foo"));
- EXPECT_TRUE(IsPathAbsolute("C:\\"));
- EXPECT_TRUE(IsPathAbsolute("/C:/foo"));
- EXPECT_TRUE(IsPathAbsolute("/C:\\foo"));
-#endif
-}
-
-TEST(FilesystemUtils, MakeAbsolutePathRelativeIfPossible) {
- std::string dest;
-
-#if defined(OS_WIN)
- EXPECT_TRUE(
- MakeAbsolutePathRelativeIfPossible("C:\\base", "C:\\base\\foo", &dest));
- EXPECT_EQ("//foo", dest);
- EXPECT_TRUE(
- MakeAbsolutePathRelativeIfPossible("C:\\base", "/C:/base/foo", &dest));
- EXPECT_EQ("//foo", dest);
- EXPECT_TRUE(
- MakeAbsolutePathRelativeIfPossible("c:\\base", "C:\\base\\foo\\", &dest));
- EXPECT_EQ("//foo\\", dest);
-
- EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("C:\\base", "C:\\ba", &dest));
- EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("C:\\base",
- "C:\\/notbase/foo", &dest));
-#else
-
- EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("/base", "/base/foo/", &dest));
- EXPECT_EQ("//foo/", dest);
- EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("/base", "/base/foo", &dest));
- EXPECT_EQ("//foo", dest);
- EXPECT_TRUE(
- MakeAbsolutePathRelativeIfPossible("/base/", "/base/foo/", &dest));
- EXPECT_EQ("//foo/", dest);
-
- EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("/base", "/ba", &dest));
- EXPECT_FALSE(
- MakeAbsolutePathRelativeIfPossible("/base", "/notbase/foo", &dest));
-#endif
-}
-
-TEST(FilesystemUtils, MakeAbsoluteFilePathRelativeIfPossible) {
-#if defined(OS_WIN)
- EXPECT_EQ(
- base::FilePath(L"out\\Debug"),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath(L"C:\\src"), base::FilePath(L"C:\\src\\out\\Debug")));
- EXPECT_EQ(base::FilePath(L".\\gn"),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath(L"C:\\src\\out\\Debug"),
- base::FilePath(L"C:\\src\\out\\Debug\\gn")));
- EXPECT_EQ(
- base::FilePath(L"..\\.."),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath(L"C:\\src\\out\\Debug"), base::FilePath(L"C:\\src")));
- EXPECT_EQ(
- base::FilePath(L"..\\.."),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath(L"C:\\src\\out\\Debug"), base::FilePath(L"C:/src")));
- EXPECT_EQ(base::FilePath(L"."),
- MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(L"C:\\src"),
- base::FilePath(L"C:\\src")));
- EXPECT_EQ(base::FilePath(L"..\\..\\..\\u\\v\\w"),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath(L"C:\\a\\b\\c\\x\\y\\z"),
- base::FilePath(L"C:\\a\\b\\c\\u\\v\\w")));
- EXPECT_EQ(base::FilePath(L"D:\\bar"),
- MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(L"C:\\foo"),
- base::FilePath(L"D:\\bar")));
-#else
- EXPECT_EQ(base::FilePath("out/Debug"),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath("/src"), base::FilePath("/src/out/Debug")));
- EXPECT_EQ(base::FilePath("./gn"), MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath("/src/out/Debug"),
- base::FilePath("/src/out/Debug/gn")));
- EXPECT_EQ(base::FilePath("../.."),
- MakeAbsoluteFilePathRelativeIfPossible(
- base::FilePath("/src/out/Debug"), base::FilePath("/src")));
- EXPECT_EQ(base::FilePath("."),
- MakeAbsoluteFilePathRelativeIfPossible(base::FilePath("/src"),
- base::FilePath("/src")));
- EXPECT_EQ(
- base::FilePath("../../../u/v/w"),
- MakeAbsoluteFilePathRelativeIfPossible(base::FilePath("/a/b/c/x/y/z"),
- base::FilePath("/a/b/c/u/v/w")));
-#endif
-}
-
-TEST(FilesystemUtils, NormalizePath) {
- std::string input;
-
- NormalizePath(&input);
- EXPECT_EQ("", input);
-
- input = "foo/bar.txt";
- NormalizePath(&input);
- EXPECT_EQ("foo/bar.txt", input);
-
- input = ".";
- NormalizePath(&input);
- EXPECT_EQ("", input);
-
- input = "..";
- NormalizePath(&input);
- EXPECT_EQ("..", input);
-
- input = "foo//bar";
- NormalizePath(&input);
- EXPECT_EQ("foo/bar", input);
-
- input = "//foo";
- NormalizePath(&input);
- EXPECT_EQ("//foo", input);
-
- input = "foo/..//bar";
- NormalizePath(&input);
- EXPECT_EQ("bar", input);
-
- input = "foo/../../bar";
- NormalizePath(&input);
- EXPECT_EQ("../bar", input);
-
- input = "/../foo"; // Don't go above the root dir.
- NormalizePath(&input);
- EXPECT_EQ("/foo", input);
-
- input = "//../foo"; // Don't go above the root dir.
- NormalizePath(&input);
- EXPECT_EQ("//foo", input);
-
- input = "../foo";
- NormalizePath(&input);
- EXPECT_EQ("../foo", input);
-
- input = "..";
- NormalizePath(&input);
- EXPECT_EQ("..", input);
-
- input = "./././.";
- NormalizePath(&input);
- EXPECT_EQ("", input);
-
- input = "../../..";
- NormalizePath(&input);
- EXPECT_EQ("../../..", input);
-
- input = "../";
- NormalizePath(&input);
- EXPECT_EQ("../", input);
-
- // Backslash normalization.
- input = "foo\\..\\..\\bar";
- NormalizePath(&input);
- EXPECT_EQ("../bar", input);
-
- // Trailing slashes should get preserved.
- input = "//foo/bar/";
- NormalizePath(&input);
- EXPECT_EQ("//foo/bar/", input);
-
-#if defined(OS_WIN)
- // Go above and outside of the source root.
- input = "//../foo";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/C:/source/foo", input);
-
- input = "//../foo";
- NormalizePath(&input, "C:\\source\\root");
- EXPECT_EQ("/C:/source/foo", input);
-
- input = "//../";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/C:/source/", input);
-
- input = "//../foo.txt";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/C:/source/foo.txt", input);
-
- input = "//../foo/bar/";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/C:/source/foo/bar/", input);
-
- // Go above and back into the source root. This should return a system-
- // absolute path. We could arguably return this as a source-absolute path,
- // but that would require additional handling to account for a rare edge
- // case.
- input = "//../root/foo";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/C:/source/root/foo", input);
-
- input = "//../root/foo/bar/";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/C:/source/root/foo/bar/", input);
-
- // Stay inside the source root
- input = "//foo/bar";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("//foo/bar", input);
-
- input = "//foo/bar/";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("//foo/bar/", input);
-
- // The path should not go above the system root. Note that on Windows, this
- // will consume the drive (C:).
- input = "//../../../../../foo/bar";
- NormalizePath(&input, "/C:/source/root");
- EXPECT_EQ("/foo/bar", input);
-
- // Test when the source root is the letter drive.
- input = "//../foo";
- NormalizePath(&input, "/C:");
- EXPECT_EQ("/foo", input);
-
- input = "//../foo";
- NormalizePath(&input, "C:");
- EXPECT_EQ("/foo", input);
-
- input = "//../foo";
- NormalizePath(&input, "/");
- EXPECT_EQ("/foo", input);
-
- input = "//../";
- NormalizePath(&input, "\\C:");
- EXPECT_EQ("/", input);
-
- input = "//../foo.txt";
- NormalizePath(&input, "/C:");
- EXPECT_EQ("/foo.txt", input);
-#else
- // Go above and outside of the source root.
- input = "//../foo";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/source/foo", input);
-
- input = "//../";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/source/", input);
-
- input = "//../foo.txt";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/source/foo.txt", input);
-
- input = "//../foo/bar/";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/source/foo/bar/", input);
-
- // Go above and back into the source root. This should return a system-
- // absolute path. We could arguably return this as a source-absolute path,
- // but that would require additional handling to account for a rare edge
- // case.
- input = "//../root/foo";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/source/root/foo", input);
-
- input = "//../root/foo/bar/";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/source/root/foo/bar/", input);
-
- // Stay inside the source root
- input = "//foo/bar";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("//foo/bar", input);
-
- input = "//foo/bar/";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("//foo/bar/", input);
-
- // The path should not go above the system root.
- input = "//../../../../../foo/bar";
- NormalizePath(&input, "/source/root");
- EXPECT_EQ("/foo/bar", input);
-
- // Test when the source root is the system root.
- input = "//../foo/bar/";
- NormalizePath(&input, "/");
- EXPECT_EQ("/foo/bar/", input);
-
- input = "//../";
- NormalizePath(&input, "/");
- EXPECT_EQ("/", input);
-
- input = "//../foo.txt";
- NormalizePath(&input, "/");
- EXPECT_EQ("/foo.txt", input);
-
-#endif
-}
-
-TEST(FilesystemUtils, RebasePath) {
- base::StringPiece source_root("/source/root");
-
- // Degenerate case.
- EXPECT_EQ(".", RebasePath("//", SourceDir("//"), source_root));
- EXPECT_EQ(".",
- RebasePath("//foo/bar/", SourceDir("//foo/bar/"), source_root));
-
- // Going up the tree.
- EXPECT_EQ("../foo", RebasePath("//foo", SourceDir("//bar/"), source_root));
- EXPECT_EQ("../foo/", RebasePath("//foo/", SourceDir("//bar/"), source_root));
- EXPECT_EQ("../../foo",
- RebasePath("//foo", SourceDir("//bar/moo"), source_root));
- EXPECT_EQ("../../foo/",
- RebasePath("//foo/", SourceDir("//bar/moo"), source_root));
-
- // Going down the tree.
- EXPECT_EQ("foo/bar", RebasePath("//foo/bar", SourceDir("//"), source_root));
- EXPECT_EQ("foo/bar/", RebasePath("//foo/bar/", SourceDir("//"), source_root));
-
- // Going up and down the tree.
- EXPECT_EQ("../../foo/bar",
- RebasePath("//foo/bar", SourceDir("//a/b/"), source_root));
- EXPECT_EQ("../../foo/bar/",
- RebasePath("//foo/bar/", SourceDir("//a/b/"), source_root));
-
- // Sharing prefix.
- EXPECT_EQ("foo", RebasePath("//a/foo", SourceDir("//a/"), source_root));
- EXPECT_EQ("foo/", RebasePath("//a/foo/", SourceDir("//a/"), source_root));
- EXPECT_EQ("foo", RebasePath("//a/b/foo", SourceDir("//a/b/"), source_root));
- EXPECT_EQ("foo/", RebasePath("//a/b/foo/", SourceDir("//a/b/"), source_root));
- EXPECT_EQ("foo/bar",
- RebasePath("//a/b/foo/bar", SourceDir("//a/b/"), source_root));
- EXPECT_EQ("foo/bar/",
- RebasePath("//a/b/foo/bar/", SourceDir("//a/b/"), source_root));
-
- // One could argue about this case. Since the input doesn't have a slash it
- // would normally not be treated like a directory and we'd go up, which is
- // simpler. However, since it matches the output directory's name, we could
- // potentially infer that it's the same and return "." for this.
- EXPECT_EQ("../bar",
- RebasePath("//foo/bar", SourceDir("//foo/bar/"), source_root));
-
- // Check when only |input| is system-absolute
- EXPECT_EQ("foo", RebasePath("/source/root/foo", SourceDir("//"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("foo/", RebasePath("/source/root/foo/", SourceDir("//"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../../builddir/Out/Debug",
- RebasePath("/builddir/Out/Debug", SourceDir("//"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../../../builddir/Out/Debug",
- RebasePath("/builddir/Out/Debug", SourceDir("//"),
- base::StringPiece("/source/root/foo")));
- EXPECT_EQ("../../../builddir/Out/Debug/",
- RebasePath("/builddir/Out/Debug/", SourceDir("//"),
- base::StringPiece("/source/root/foo")));
- EXPECT_EQ("../../path/to/foo", RebasePath("/path/to/foo", SourceDir("//"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../../../path/to/foo",
- RebasePath("/path/to/foo", SourceDir("//a"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("/path/to/foo", SourceDir("//a/b"),
- base::StringPiece("/source/root")));
-
- // Check when only |dest_dir| is system-absolute.
- EXPECT_EQ(".", RebasePath("//", SourceDir("/source/root"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("foo", RebasePath("//foo", SourceDir("/source/root"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../foo", RebasePath("//foo", SourceDir("/source/root/bar"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../../../source/root/foo",
- RebasePath("//foo", SourceDir("/other/source/root"),
- base::StringPiece("/source/root")));
- EXPECT_EQ("../../../../source/root/foo",
- RebasePath("//foo", SourceDir("/other/source/root/bar"),
- base::StringPiece("/source/root")));
-
- // Check when |input| and |dest_dir| are both system-absolute. Also,
- // in this case |source_root| is never used so set it to a dummy
- // value.
- EXPECT_EQ("foo", RebasePath("/source/root/foo", SourceDir("/source/root"),
- base::StringPiece("/x/y/z")));
- EXPECT_EQ("foo/", RebasePath("/source/root/foo/", SourceDir("/source/root"),
- base::StringPiece("/x/y/z")));
- EXPECT_EQ("../../builddir/Out/Debug",
- RebasePath("/builddir/Out/Debug", SourceDir("/source/root"),
- base::StringPiece("/x/y/z")));
- EXPECT_EQ("../../../builddir/Out/Debug",
- RebasePath("/builddir/Out/Debug", SourceDir("/source/root/foo"),
- base::StringPiece("/source/root/foo")));
- EXPECT_EQ("../../../builddir/Out/Debug/",
- RebasePath("/builddir/Out/Debug/", SourceDir("/source/root/foo"),
- base::StringPiece("/source/root/foo")));
- EXPECT_EQ("../../path/to/foo",
- RebasePath("/path/to/foo", SourceDir("/source/root"),
- base::StringPiece("/x/y/z")));
- EXPECT_EQ("../../../path/to/foo",
- RebasePath("/path/to/foo", SourceDir("/source/root/a"),
- base::StringPiece("/x/y/z")));
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("/path/to/foo", SourceDir("/source/root/a/b"),
- base::StringPiece("/x/y/z")));
-
-#if defined(OS_WIN)
- // Test corrections while rebasing Windows-style absolute paths.
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("C:/path/to/foo", SourceDir("//a/b"),
- base::StringPiece("/C:/source/root")));
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("/C:/path/to/foo", SourceDir("//a/b"),
- base::StringPiece("C:/source/root")));
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("/C:/path/to/foo", SourceDir("//a/b"),
- base::StringPiece("/c:/source/root")));
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("/c:/path/to/foo", SourceDir("//a/b"),
- base::StringPiece("c:/source/root")));
- EXPECT_EQ("../../../../path/to/foo",
- RebasePath("/c:/path/to/foo", SourceDir("//a/b"),
- base::StringPiece("C:/source/root")));
-#endif
-}
-
-TEST(FilesystemUtils, DirectoryWithNoLastSlash) {
- EXPECT_EQ("", DirectoryWithNoLastSlash(SourceDir()));
- EXPECT_EQ("/.", DirectoryWithNoLastSlash(SourceDir("/")));
- EXPECT_EQ("//.", DirectoryWithNoLastSlash(SourceDir("//")));
- EXPECT_EQ("//foo", DirectoryWithNoLastSlash(SourceDir("//foo/")));
- EXPECT_EQ("/bar", DirectoryWithNoLastSlash(SourceDir("/bar/")));
-}
-
-TEST(FilesystemUtils, SourceDirForPath) {
-#if defined(OS_WIN)
- base::FilePath root(L"C:\\source\\foo\\");
- EXPECT_EQ("/C:/foo/bar/",
- SourceDirForPath(root, base::FilePath(L"C:\\foo\\bar")).value());
- EXPECT_EQ("/", SourceDirForPath(root, base::FilePath(L"/")).value());
- EXPECT_EQ("//",
- SourceDirForPath(root, base::FilePath(L"C:\\source\\foo")).value());
- EXPECT_EQ("//bar/",
- SourceDirForPath(root, base::FilePath(L"C:\\source\\foo\\bar\\"))
- .value());
- EXPECT_EQ("//bar/baz/",
- SourceDirForPath(root, base::FilePath(L"C:\\source\\foo\\bar\\baz"))
- .value());
-
- // Should be case-and-slash-insensitive.
- EXPECT_EQ(
- "//baR/",
- SourceDirForPath(root, base::FilePath(L"c:/SOURCE\\Foo/baR/")).value());
-
- // Some "weird" Windows paths.
- EXPECT_EQ("/foo/bar/",
- SourceDirForPath(root, base::FilePath(L"/foo/bar/")).value());
- EXPECT_EQ("/C:/foo/bar/",
- SourceDirForPath(root, base::FilePath(L"C:foo/bar/")).value());
-
- // Also allow absolute GN-style Windows paths.
- EXPECT_EQ("/C:/foo/bar/",
- SourceDirForPath(root, base::FilePath(L"/C:/foo/bar")).value());
- EXPECT_EQ(
- "//bar/",
- SourceDirForPath(root, base::FilePath(L"/C:/source/foo/bar")).value());
-
- // Empty source dir.
- base::FilePath empty;
- EXPECT_EQ(
- "/C:/source/foo/",
- SourceDirForPath(empty, base::FilePath(L"C:\\source\\foo")).value());
-#else
- base::FilePath root("/source/foo/");
- EXPECT_EQ("/foo/bar/",
- SourceDirForPath(root, base::FilePath("/foo/bar/")).value());
- EXPECT_EQ("/", SourceDirForPath(root, base::FilePath("/")).value());
- EXPECT_EQ("//",
- SourceDirForPath(root, base::FilePath("/source/foo")).value());
- EXPECT_EQ("//bar/",
- SourceDirForPath(root, base::FilePath("/source/foo/bar/")).value());
- EXPECT_EQ(
- "//bar/baz/",
- SourceDirForPath(root, base::FilePath("/source/foo/bar/baz/")).value());
-
- // Should be case-sensitive.
- EXPECT_EQ("/SOURCE/foo/bar/",
- SourceDirForPath(root, base::FilePath("/SOURCE/foo/bar/")).value());
-
- // Empty source dir.
- base::FilePath empty;
- EXPECT_EQ("/source/foo/",
- SourceDirForPath(empty, base::FilePath("/source/foo")).value());
-#endif
-}
-
-TEST(FilesystemUtils, ContentsEqual) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
- std::string data = "foo";
-
- base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt");
- base::WriteFile(file_path, data.c_str(), static_cast<int>(data.size()));
-
- EXPECT_TRUE(ContentsEqual(file_path, data));
-
- // Different length and contents.
- data += "bar";
- EXPECT_FALSE(ContentsEqual(file_path, data));
-
- // The same length, different contents.
- EXPECT_FALSE(ContentsEqual(file_path, "bar"));
-}
-
-TEST(FilesystemUtils, WriteFileIfChanged) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
- std::string data = "foo";
-
- // Write if file doesn't exist. Create also directory.
- base::FilePath file_path =
- temp_dir.GetPath().AppendASCII("bar").AppendASCII("foo.txt");
- EXPECT_TRUE(WriteFileIfChanged(file_path, data, nullptr));
-
- base::File::Info file_info;
- ASSERT_TRUE(base::GetFileInfo(file_path, &file_info));
- Ticks last_modified = file_info.last_modified;
-
- {
- using namespace std::chrono_literals;
-#if defined(OS_MACOSX)
- // Modification times are in seconds in HFS on Mac.
- std::this_thread::sleep_for(1s);
-#else
- std::this_thread::sleep_for(1ms);
-#endif
- }
-
- // Don't write if contents is the same.
- EXPECT_TRUE(WriteFileIfChanged(file_path, data, nullptr));
- ASSERT_TRUE(base::GetFileInfo(file_path, &file_info));
- EXPECT_EQ(last_modified, file_info.last_modified);
-
- // Write if contents changed.
- EXPECT_TRUE(WriteFileIfChanged(file_path, "bar", nullptr));
- std::string file_data;
- ASSERT_TRUE(base::ReadFileToString(file_path, &file_data));
- EXPECT_EQ("bar", file_data);
-}
-
-TEST(FilesystemUtils, GetToolchainDirs) {
- BuildSettings build_settings;
- build_settings.SetBuildDir(SourceDir("//out/Debug/"));
-
- // The default toolchain.
- Settings default_settings(&build_settings, "");
- Label default_toolchain_label(SourceDir("//toolchain/"), "default");
- default_settings.set_toolchain_label(default_toolchain_label);
- default_settings.set_default_toolchain_label(default_toolchain_label);
- BuildDirContext default_context(&default_settings);
-
- // Default toolchain out dir as source dirs.
- EXPECT_EQ("//out/Debug/", GetBuildDirAsSourceDir(default_context,
- BuildDirType::TOOLCHAIN_ROOT)
- .value());
- EXPECT_EQ("//out/Debug/obj/",
- GetBuildDirAsSourceDir(default_context, BuildDirType::OBJ).value());
- EXPECT_EQ("//out/Debug/gen/",
- GetBuildDirAsSourceDir(default_context, BuildDirType::GEN).value());
-
- // Default toolchain our dir as output files.
- EXPECT_EQ(
- "", GetBuildDirAsOutputFile(default_context, BuildDirType::TOOLCHAIN_ROOT)
- .value());
- EXPECT_EQ(
- "obj/",
- GetBuildDirAsOutputFile(default_context, BuildDirType::OBJ).value());
- EXPECT_EQ(
- "gen/",
- GetBuildDirAsOutputFile(default_context, BuildDirType::GEN).value());
-
- // Check a secondary toolchain.
- Settings other_settings(&build_settings, "two/");
- Label other_toolchain_label(SourceDir("//toolchain/"), "two");
- other_settings.set_toolchain_label(other_toolchain_label);
- other_settings.set_default_toolchain_label(default_toolchain_label);
- BuildDirContext other_context(&other_settings);
-
- // Secondary toolchain out dir as source dirs.
- EXPECT_EQ("//out/Debug/two/",
- GetBuildDirAsSourceDir(other_context, BuildDirType::TOOLCHAIN_ROOT)
- .value());
- EXPECT_EQ("//out/Debug/two/obj/",
- GetBuildDirAsSourceDir(other_context, BuildDirType::OBJ).value());
- EXPECT_EQ("//out/Debug/two/gen/",
- GetBuildDirAsSourceDir(other_context, BuildDirType::GEN).value());
-
- // Secondary toolchain out dir as output files.
- EXPECT_EQ("two/",
- GetBuildDirAsOutputFile(other_context, BuildDirType::TOOLCHAIN_ROOT)
- .value());
- EXPECT_EQ("two/obj/",
- GetBuildDirAsOutputFile(other_context, BuildDirType::OBJ).value());
- EXPECT_EQ("two/gen/",
- GetBuildDirAsOutputFile(other_context, BuildDirType::GEN).value());
-}
-
-TEST(FilesystemUtils, GetSubBuildDir) {
- BuildSettings build_settings;
- build_settings.SetBuildDir(SourceDir("//out/Debug/"));
-
- // Test the default toolchain.
- Label default_toolchain_label(SourceDir("//toolchain/"), "default");
- Settings default_settings(&build_settings, "");
- default_settings.set_toolchain_label(default_toolchain_label);
- default_settings.set_default_toolchain_label(default_toolchain_label);
- BuildDirContext default_context(&default_settings);
-
- // Target in the root.
- EXPECT_EQ("//out/Debug/obj/",
- GetSubBuildDirAsSourceDir(default_context, SourceDir("//"),
- BuildDirType::OBJ)
- .value());
- EXPECT_EQ("gen/", GetSubBuildDirAsOutputFile(default_context, SourceDir("//"),
- BuildDirType::GEN)
- .value());
-
- // Target in another directory.
- EXPECT_EQ("//out/Debug/obj/foo/bar/",
- GetSubBuildDirAsSourceDir(default_context, SourceDir("//foo/bar/"),
- BuildDirType::OBJ)
- .value());
- EXPECT_EQ("gen/foo/bar/",
- GetSubBuildDirAsOutputFile(default_context, SourceDir("//foo/bar/"),
- BuildDirType::GEN)
- .value());
-
- // Secondary toolchain.
- Settings other_settings(&build_settings, "two/");
- other_settings.set_toolchain_label(Label(SourceDir("//toolchain/"), "two"));
- other_settings.set_default_toolchain_label(default_toolchain_label);
- BuildDirContext other_context(&other_settings);
-
- // Target in the root.
- EXPECT_EQ("//out/Debug/two/obj/",
- GetSubBuildDirAsSourceDir(other_context, SourceDir("//"),
- BuildDirType::OBJ)
- .value());
- EXPECT_EQ("two/gen/", GetSubBuildDirAsOutputFile(
- other_context, SourceDir("//"), BuildDirType::GEN)
- .value());
-
- // Target in another directory.
- EXPECT_EQ("//out/Debug/two/obj/foo/bar/",
- GetSubBuildDirAsSourceDir(other_context, SourceDir("//foo/bar/"),
- BuildDirType::OBJ)
- .value());
- EXPECT_EQ("two/gen/foo/bar/",
- GetSubBuildDirAsOutputFile(other_context, SourceDir("//foo/bar/"),
- BuildDirType::GEN)
- .value());
-
- // Absolute source path
- EXPECT_EQ("//out/Debug/obj/ABS_PATH/abs/",
- GetSubBuildDirAsSourceDir(default_context, SourceDir("/abs"),
- BuildDirType::OBJ)
- .value());
- EXPECT_EQ("gen/ABS_PATH/abs/",
- GetSubBuildDirAsOutputFile(default_context, SourceDir("/abs"),
- BuildDirType::GEN)
- .value());
-#if defined(OS_WIN)
- EXPECT_EQ("//out/Debug/obj/ABS_PATH/C/abs/",
- GetSubBuildDirAsSourceDir(default_context, SourceDir("/C:/abs"),
- BuildDirType::OBJ)
- .value());
- EXPECT_EQ("gen/ABS_PATH/C/abs/",
- GetSubBuildDirAsOutputFile(default_context, SourceDir("/C:/abs"),
- BuildDirType::GEN)
- .value());
-#endif
-}
-
-TEST(FilesystemUtils, GetBuildDirForTarget) {
- BuildSettings build_settings;
- build_settings.SetBuildDir(SourceDir("//out/Debug/"));
- Settings settings(&build_settings, "");
-
- Target a(&settings, Label(SourceDir("//foo/bar/"), "baz"));
- EXPECT_EQ("//out/Debug/obj/foo/bar/",
- GetBuildDirForTargetAsSourceDir(&a, BuildDirType::OBJ).value());
- EXPECT_EQ("obj/foo/bar/",
- GetBuildDirForTargetAsOutputFile(&a, BuildDirType::OBJ).value());
- EXPECT_EQ("//out/Debug/gen/foo/bar/",
- GetBuildDirForTargetAsSourceDir(&a, BuildDirType::GEN).value());
- EXPECT_EQ("gen/foo/bar/",
- GetBuildDirForTargetAsOutputFile(&a, BuildDirType::GEN).value());
-}
-
-// Tests handling of output dirs when build dir is the same as the root.
-TEST(FilesystemUtils, GetDirForEmptyBuildDir) {
- BuildSettings build_settings;
- build_settings.SetBuildDir(SourceDir("//"));
- Settings settings(&build_settings, "");
-
- BuildDirContext context(&settings);
-
- EXPECT_EQ(
- "//",
- GetBuildDirAsSourceDir(context, BuildDirType::TOOLCHAIN_ROOT).value());
- EXPECT_EQ("//gen/",
- GetBuildDirAsSourceDir(context, BuildDirType::GEN).value());
- EXPECT_EQ("//obj/",
- GetBuildDirAsSourceDir(context, BuildDirType::OBJ).value());
-
- EXPECT_EQ(
- "",
- GetBuildDirAsOutputFile(context, BuildDirType::TOOLCHAIN_ROOT).value());
- EXPECT_EQ("gen/",
- GetBuildDirAsOutputFile(context, BuildDirType::GEN).value());
- EXPECT_EQ("obj/",
- GetBuildDirAsOutputFile(context, BuildDirType::OBJ).value());
-}
-
-TEST(FilesystemUtils, ResolveRelativeTest) {
- std::string result;
-#ifndef OS_WIN
- EXPECT_TRUE(
- MakeAbsolutePathRelativeIfPossible("/some/dir", "/some/dir/a", &result));
- EXPECT_EQ(result, "//a");
-
- EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
- "/some/dir", "/some/dir-sufix/a", &result));
-#else
- EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("C:/some/dir",
- "/C:/some/dir/a", &result));
- EXPECT_EQ(result, "//a");
- EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
- "C:/some/dir", "C:/some/dir-sufix/a", &result));
-#endif
-}
diff --git a/gn/tools/gn/format_test_data/003.golden b/gn/tools/gn/format_test_data/003.golden
deleted file mode 100644
index 2fdcb9e38ba..00000000000
--- a/gn/tools/gn/format_test_data/003.golden
+++ /dev/null
@@ -1,10 +0,0 @@
-executable("test") {
- sources = [
- "stuff.cc",
- "things.cc",
- ]
-
- deps = [
- "//base",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/004.golden b/gn/tools/gn/format_test_data/004.golden
deleted file mode 100644
index 68373d10144..00000000000
--- a/gn/tools/gn/format_test_data/004.golden
+++ /dev/null
@@ -1,13 +0,0 @@
-# This is a block comment that goes at the top of the file and is attached to
-# the top level target.
-executable("test") {
- sources = [
- "stuff.cc", # Comment attached to list item.
- "things.cc",
- ]
-
- # Comment attached to statement.
- deps = [
- "//base",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/007.golden b/gn/tools/gn/format_test_data/007.golden
deleted file mode 100644
index 610fa3fead5..00000000000
--- a/gn/tools/gn/format_test_data/007.golden
+++ /dev/null
@@ -1,11 +0,0 @@
-executable("test") {
- sources = [
- "a.cc",
- ]
-
- # This is an unusual comment that's a header for the stuff that comes after
- # it. We want to make sure that it's not connected to the next element in
- # the file.
-
- cflags = [ "-Wee" ]
-}
diff --git a/gn/tools/gn/format_test_data/008.golden b/gn/tools/gn/format_test_data/008.golden
deleted file mode 100644
index aec281ab952..00000000000
--- a/gn/tools/gn/format_test_data/008.golden
+++ /dev/null
@@ -1,5 +0,0 @@
-if (is_win) {
- sources = [
- "win.cc",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/009.golden b/gn/tools/gn/format_test_data/009.golden
deleted file mode 100644
index af27d797005..00000000000
--- a/gn/tools/gn/format_test_data/009.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-if (is_win) {
- sources = [
- "win.cc",
- ]
-} else {
- sources = [
- "linux.cc",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/010.golden b/gn/tools/gn/format_test_data/010.golden
deleted file mode 100644
index 64fabb9ebc5..00000000000
--- a/gn/tools/gn/format_test_data/010.golden
+++ /dev/null
@@ -1,9 +0,0 @@
-if (is_win) {
- sources = [
- "win.cc",
- ]
-} else if (is_linux) {
- sources = [
- "linux.cc",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/011.golden b/gn/tools/gn/format_test_data/011.golden
deleted file mode 100644
index 4ce009ea1dc..00000000000
--- a/gn/tools/gn/format_test_data/011.golden
+++ /dev/null
@@ -1,13 +0,0 @@
-if (is_win) {
- sources = [
- "win.cc",
- ]
-} else if (is_linux) {
- sources = [
- "linux.cc",
- ]
-} else {
- sources = [
- "wha.cc",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/012.golden b/gn/tools/gn/format_test_data/012.golden
deleted file mode 100644
index a0049b2d55d..00000000000
--- a/gn/tools/gn/format_test_data/012.golden
+++ /dev/null
@@ -1,22 +0,0 @@
-# (A sample top level block comment)
-# 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_win) {
- # This is some special stuff for Windows
- sources = [
- "win.cc",
- ]
-} else if (is_linux) {
- # This is a block comment inside the linux block, but not attached.
-
- sources = [
- "linux.cc",
- ]
-} else {
- # A comment with trailing spaces
- sources = [
- "wha.cc",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/015.golden b/gn/tools/gn/format_test_data/015.golden
deleted file mode 100644
index 553f01ce105..00000000000
--- a/gn/tools/gn/format_test_data/015.golden
+++ /dev/null
@@ -1,6 +0,0 @@
-if (is_win) {
- sources = [
- "a.cc",
- ]
- # Some comment at end.
-}
diff --git a/gn/tools/gn/format_test_data/019.gn b/gn/tools/gn/format_test_data/019.gn
deleted file mode 100644
index 48772b9ded8..00000000000
--- a/gn/tools/gn/format_test_data/019.gn
+++ /dev/null
@@ -1,23 +0,0 @@
-# Make sure blank lines are maintained before comments in lists.
-
-deps = [
- "//pdf", # Not compiled on Android in GYP yet, either.
- "//ppapi:ppapi_c",
- "//third_party/libusb",
- "//ui/keyboard", # Blocked on content.
-
- # Seems to not be compiled on Android. Otherwise it will need a config.h.
- "//third_party/libxslt",
-
- # Not relevant to Android.
- "//ash",
- "//tools/gn",
-
- # Multiple line
- # comment
- # here.
- "//ui/aura",
- "//ui/display",
- "//ui/views",
- "//ui/views/controls/webview",
-]
diff --git a/gn/tools/gn/format_test_data/019.golden b/gn/tools/gn/format_test_data/019.golden
deleted file mode 100644
index c800ed1fe42..00000000000
--- a/gn/tools/gn/format_test_data/019.golden
+++ /dev/null
@@ -1,23 +0,0 @@
-# Make sure blank lines are maintained before comments in lists.
-
-deps = [
- "//pdf", # Not compiled on Android in GYP yet, either.
- "//ppapi:ppapi_c",
- "//third_party/libusb",
- "//ui/keyboard", # Blocked on content.
-
- # Seems to not be compiled on Android. Otherwise it will need a config.h.
- "//third_party/libxslt",
-
- # Not relevant to Android.
- "//ash",
- "//tools/gn",
-
- # Multiple line
- # comment
- # here.
- "//ui/aura",
- "//ui/display",
- "//ui/views",
- "//ui/views/controls/webview",
-]
diff --git a/gn/tools/gn/format_test_data/040.gn b/gn/tools/gn/format_test_data/040.gn
deleted file mode 100644
index 1f4754e2794..00000000000
--- a/gn/tools/gn/format_test_data/040.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Dewrapping shouldn't cause 80 col to be exceed.
-# 80 ---------------------------------------------------------------------------
-if (true) {
- if (is_win) {
- cflags = [
- "/wd4267", # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- ]
- }
-}
diff --git a/gn/tools/gn/format_test_data/042.golden b/gn/tools/gn/format_test_data/042.golden
deleted file mode 100644
index 1968d0acd94..00000000000
--- a/gn/tools/gn/format_test_data/042.golden
+++ /dev/null
@@ -1,110 +0,0 @@
-# Test zero, one, and multiple element for specifically named LHSs.
-if (true) {
- cflags = []
- cflags_c = []
- cflags_cc = []
- data = []
- datadeps = []
- defines = []
- deps = []
- include_dirs = []
- inputs = []
- ldflags = []
- outputs = []
- public_deps = []
- sources = []
-} else if (true) {
- cflags = [ "x" ]
- cflags_c = [ "x" ]
- cflags_cc = [ "x" ]
- data = [
- "x",
- ]
- datadeps = [
- "x",
- ]
- defines = [ "x" ]
- deps = [
- "x",
- ]
- include_dirs = [ "x" ]
- inputs = [
- "x",
- ]
- ldflags = [ "x" ]
- outputs = [
- "x",
- ]
- public_deps = [
- "x",
- ]
- sources = [
- "x",
- ]
-} else {
- cflags = [
- "x",
- "y",
- "z",
- ]
- cflags_c = [
- "x",
- "y",
- "z",
- ]
- cflags_cc = [
- "x",
- "y",
- "z",
- ]
- data = [
- "x",
- "y",
- "z",
- ]
- datadeps = [
- "x",
- "y",
- "z",
- ]
- defines = [
- "x",
- "y",
- "z",
- ]
- deps = [
- "x",
- "y",
- "z",
- ]
- include_dirs = [
- "x",
- "y",
- "z",
- ]
- inputs = [
- "x",
- "y",
- "z",
- ]
- ldflags = [
- "x",
- "y",
- "z",
- ]
- outputs = [
- "x",
- "y",
- "z",
- ]
- public_deps = [
- "x",
- "y",
- "z",
- ]
- sources = [
- "x",
- "y",
- "z",
- ]
-}
diff --git a/gn/tools/gn/format_test_data/044.gn b/gn/tools/gn/format_test_data/044.gn
deleted file mode 100644
index fe09617912d..00000000000
--- a/gn/tools/gn/format_test_data/044.gn
+++ /dev/null
@@ -1,10 +0,0 @@
-config("compiler") {
- if (is_win) {
- if (is_component_build) {
- cflags += [
- "/EHsc", # Assume C functions can't throw exceptions and don't catch
- # structured exceptions (only C++ ones).
- ]
- }
- }
-}
diff --git a/gn/tools/gn/format_test_data/048.gn b/gn/tools/gn/format_test_data/048.gn
deleted file mode 100644
index 7d39efb68c4..00000000000
--- a/gn/tools/gn/format_test_data/048.gn
+++ /dev/null
@@ -1,19 +0,0 @@
-# No blank inserted after libs (caused by trailing comment on 'else').
-component("google_toolbox_for_mac") {
- if (!is_ios) {
- sources += [
- "src/AddressBook/GTMABAddressBook.h",
- "src/AddressBook/GTMABAddressBook.m",
- ]
-
- libs = [
- "AddressBook.framework",
- "AppKit.framework",
- ]
- } else { # is_ios
- sources += [
- "src/iPhone/GTMFadeTruncatingLabel.h",
- "src/iPhone/GTMFadeTruncatingLabel.m",
- ]
- }
-}
diff --git a/gn/tools/gn/format_test_data/048.golden b/gn/tools/gn/format_test_data/048.golden
deleted file mode 100644
index 7d39efb68c4..00000000000
--- a/gn/tools/gn/format_test_data/048.golden
+++ /dev/null
@@ -1,19 +0,0 @@
-# No blank inserted after libs (caused by trailing comment on 'else').
-component("google_toolbox_for_mac") {
- if (!is_ios) {
- sources += [
- "src/AddressBook/GTMABAddressBook.h",
- "src/AddressBook/GTMABAddressBook.m",
- ]
-
- libs = [
- "AddressBook.framework",
- "AppKit.framework",
- ]
- } else { # is_ios
- sources += [
- "src/iPhone/GTMFadeTruncatingLabel.h",
- "src/iPhone/GTMFadeTruncatingLabel.m",
- ]
- }
-}
diff --git a/gn/tools/gn/format_test_data/062.gn b/gn/tools/gn/format_test_data/062.gn
deleted file mode 100644
index 8c4cc7d8423..00000000000
--- a/gn/tools/gn/format_test_data/062.gn
+++ /dev/null
@@ -1,122 +0,0 @@
-# Sorting, making sure we don't detach comments.
-
-sources = []
-
-sources = ["x.cc"]
-
-sources = [
- "/a",
- "/b",
- "/c",
- # End of block.
-]
-
-sources += [
- # Start of block, separate.
-
- "c",
- "a",
- "b",
-]
-
-sources += [
- "z",
- "z2",
- # Attached comment.
- "y.h",
- "y.cc",
- "y.mm",
- "y.rc",
- "a"
-]
-
-sources += [
- "z",
- "z2",
-
- # Block comment.
-
- "y.h",
- "y.cc",
- "y.mm",
- "y.rc",
- "a"
-]
-
-sources += [
- "z",
- "z2",
-
- #
- # Multiline block comment.
- #
-
- "y.h",
- "y.cc",
- "y.mm",
- "y.rc",
- "a"
-]
-
-# With identifiers.
-sources += [
- "a",
- "b",
- "c",
- some_other_thing,
- abcd,
-]
-
-# With accessors.
-sources += [
- "a",
- wee[0],
- "b",
- invoker.stuff,
- "c",
-]
-
-# Various separated blocks.
-sources -= [
- # Fix this test to build on Windows.
- "focus_cycler_unittest.cc",
-
- # All tests for multiple displays: not supported on Windows Ash.
- "wm/drag_window_resizer_unittest.cc",
-
- # Accelerometer is only available on Chrome OS.
- "wm/maximize_mode/maximize_mode_controller_unittest.cc",
-
- # Can't resize on Windows Ash. http://crbug.com/165962
- "autoclick/autoclick_unittest.cc",
- "magnifier/magnification_controller_unittest.cc",
- # Attached 1.
- # Attached 2.
- "wm/workspace/workspace_window_resizer_unittest.cc",
- "sticky_keys/sticky_keys_overlay_unittest.cc",
- "system/tray/media_security/multi_profile_media_tray_item_unittest.cc",
- "virtual_keyboard_controller_unittest.cc",
-
- # Separated at end.
- "zzzzzzzzzzzzzz.cc",
-]
-
-sources += [
- "srtp/crypto/include/xfm.h",
-
- # sources
- "srtp/srtp/ekt.c",
- "srtp/srtp/srtp.c",
- "srtp/crypto/rng/prng.c",
- "srtp/crypto/rng/rand_source.c",
-]
-
-# Try "public" too. It should be treated the same.
-public = [
- # Let's sort
- "this", "into", "word", "salad",
-
- # But leave
- "these", "two"
- # alone!
-]
diff --git a/gn/tools/gn/format_test_data/062.golden b/gn/tools/gn/format_test_data/062.golden
deleted file mode 100644
index b55451011f7..00000000000
--- a/gn/tools/gn/format_test_data/062.golden
+++ /dev/null
@@ -1,132 +0,0 @@
-# Sorting, making sure we don't detach comments.
-
-sources = []
-
-sources = [
- "x.cc",
-]
-
-sources = [
- "/a",
- "/b",
- "/c",
-
- # End of block.
-]
-
-sources += [
- # Start of block, separate.
-
- "a",
- "b",
- "c",
-]
-
-sources += [
- "a",
- "y.cc",
-
- # Attached comment.
- "y.h",
- "y.mm",
- "y.rc",
- "z",
- "z2",
-]
-
-sources += [
- "z",
- "z2",
-
- # Block comment.
-
- "a",
- "y.cc",
- "y.h",
- "y.mm",
- "y.rc",
-]
-
-sources += [
- "z",
- "z2",
-
- #
- # Multiline block comment.
- #
-
- "a",
- "y.cc",
- "y.h",
- "y.mm",
- "y.rc",
-]
-
-# With identifiers.
-sources += [
- "a",
- "b",
- "c",
- abcd,
- some_other_thing,
-]
-
-# With accessors.
-sources += [
- "a",
- "b",
- "c",
- invoker.stuff,
- wee[0],
-]
-
-# Various separated blocks.
-sources -= [
- # Fix this test to build on Windows.
- "focus_cycler_unittest.cc",
-
- # All tests for multiple displays: not supported on Windows Ash.
- "wm/drag_window_resizer_unittest.cc",
-
- # Accelerometer is only available on Chrome OS.
- "wm/maximize_mode/maximize_mode_controller_unittest.cc",
-
- # Can't resize on Windows Ash. http://crbug.com/165962
- "autoclick/autoclick_unittest.cc",
- "magnifier/magnification_controller_unittest.cc",
- "sticky_keys/sticky_keys_overlay_unittest.cc",
- "system/tray/media_security/multi_profile_media_tray_item_unittest.cc",
- "virtual_keyboard_controller_unittest.cc",
-
- # Attached 1.
- # Attached 2.
- "wm/workspace/workspace_window_resizer_unittest.cc",
-
- # Separated at end.
- "zzzzzzzzzzzzzz.cc",
-]
-
-sources += [
- "srtp/crypto/include/xfm.h",
-
- # sources
- "srtp/crypto/rng/prng.c",
- "srtp/crypto/rng/rand_source.c",
- "srtp/srtp/ekt.c",
- "srtp/srtp/srtp.c",
-]
-
-# Try "public" too. It should be treated the same.
-public = [
- # Let's sort
- "into",
- "salad",
- "this",
- "word",
-
- # But leave
- "these",
- "two",
-
- # alone!
-]
diff --git a/gn/tools/gn/format_test_data/063.gn b/gn/tools/gn/format_test_data/063.gn
deleted file mode 100644
index 9fd8211b692..00000000000
--- a/gn/tools/gn/format_test_data/063.gn
+++ /dev/null
@@ -1,36 +0,0 @@
-source_set("test") {
- a = "a"
- b = "b"
- deps = [
- "//a",
- "//a/a",
- "//a/b",
- "//a:a",
- "//a:b",
- "//b",
- ":a",
- ":b",
- "a",
- "a/a",
- "a/b",
- "a:a",
- "a:b",
- "b",
- a,
- b,
- ]
-
- public_deps = []
- if (condition) {
- public_deps += [
- "//a",
- "//a/a",
- "//a:a",
- ":a",
- "a",
- "a/a",
- "a:a",
- a,
- ]
- }
-}
diff --git a/gn/tools/gn/format_test_data/063.golden b/gn/tools/gn/format_test_data/063.golden
deleted file mode 100644
index 3dc4bedfae7..00000000000
--- a/gn/tools/gn/format_test_data/063.golden
+++ /dev/null
@@ -1,36 +0,0 @@
-source_set("test") {
- a = "a"
- b = "b"
- deps = [
- ":a",
- ":b",
- "a",
- "a:a",
- "a:b",
- "a/a",
- "a/b",
- "b",
- "//a",
- "//a:a",
- "//a:b",
- "//a/a",
- "//a/b",
- "//b",
- a,
- b,
- ]
-
- public_deps = []
- if (condition) {
- public_deps += [
- ":a",
- "a",
- "a:a",
- "a/a",
- "//a",
- "//a:a",
- "//a/a",
- a,
- ]
- }
-}
diff --git a/gn/tools/gn/format_test_data/064.golden b/gn/tools/gn/format_test_data/064.golden
deleted file mode 100644
index 3ff56f6490f..00000000000
--- a/gn/tools/gn/format_test_data/064.golden
+++ /dev/null
@@ -1,5 +0,0 @@
-source_set("test") {
- deps = [
- rebase_path(sdk_dep, ".", mojo_root),
- ]
-}
diff --git a/gn/tools/gn/format_test_data/066.golden b/gn/tools/gn/format_test_data/066.golden
deleted file mode 100644
index 9b7bb5cd09b..00000000000
--- a/gn/tools/gn/format_test_data/066.golden
+++ /dev/null
@@ -1,30 +0,0 @@
-# Suppress sorting based on comment.
-
-# NOSORT
-sources = []
-
-# NOSORT
-sources = [
- "a",
-]
-
-# NOSORT
-sources += [ "a" ]
-
-# NOSORT
-# This is NOSORT because reason.
-sources = [
- "z",
- "z2",
- "a",
- "y.cc",
-]
-
-# This is NOSORT because reason:
-# NOSORT
-sources += [
- "z",
- "z2",
- "a",
- "y.cc",
-]
diff --git a/gn/tools/gn/function_exec_script.cc b/gn/tools/gn/function_exec_script.cc
deleted file mode 100644
index 3ef7646a859..00000000000
--- a/gn/tools/gn/function_exec_script.cc
+++ /dev/null
@@ -1,271 +0,0 @@
-// 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 "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/err.h"
-#include "tools/gn/exec_process.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/input_conversion.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/trace.h"
-#include "tools/gn/value.h"
-#include "util/build_config.h"
-#include "util/ticks.h"
-
-namespace functions {
-
-namespace {
-
-bool CheckExecScriptPermissions(const BuildSettings* build_settings,
- const FunctionCallNode* function,
- Err* err) {
- const std::set<SourceFile>* whitelist =
- build_settings->exec_script_whitelist();
- if (!whitelist)
- return true; // No whitelist specified, don't check.
-
- LocationRange function_range = function->GetRange();
- if (!function_range.begin().file())
- return true; // No file, might be some internal thing, implicitly pass.
-
- if (whitelist->find(function_range.begin().file()->name()) !=
- whitelist->end())
- return true; // Whitelisted, this is OK.
-
- // Disallowed case.
- *err = Err(
- function, "Disallowed exec_script call.",
- "The use of exec_script use is restricted in this build. exec_script\n"
- "is discouraged because it can slow down the GN run and is easily\n"
- "abused.\n"
- "\n"
- "Generally nontrivial work should be done as build steps rather than\n"
- "when GN is run. For example, if you need to compute a nontrivial\n"
- "preprocessor define, it will be better to have an action target\n"
- "generate a header containing the define rather than blocking the GN\n"
- "run to compute the value.\n"
- "\n"
- "The allowed callers of exec_script is maintained in the \"//.gn\" file\n"
- "if you need to modify the whitelist.");
- return false;
-}
-
-} // namespace
-
-const char kExecScript[] = "exec_script";
-const char kExecScript_HelpShort[] =
- "exec_script: Synchronously run a script and return the output.";
-const char kExecScript_Help[] =
- R"(exec_script: Synchronously run a script and return the output.
-
- exec_script(filename,
- arguments = [],
- input_conversion = "",
- file_dependencies = [])
-
- Runs the given script, returning the stdout of the script. The build
- generation will fail if the script does not exist or returns a nonzero exit
- code.
-
- The current directory when executing the script will be the root build
- directory. If you are passing file names, you will want to use the
- rebase_path() function to make file names relative to this path (see "gn help
- rebase_path").
-
- The default script interpreter is Python ("python" on POSIX, "python.exe" or
- "python.bat" on Windows). This can be configured by the script_executable
- variable, see "gn help dotfile".
-
-Arguments:
-
- filename:
- File name of script to execute. Non-absolute names will be treated as
- relative to the current build file.
-
- arguments:
- A list of strings to be passed to the script as arguments. May be
- unspecified or the empty list which means no arguments.
-
- input_conversion:
- Controls how the file is read and parsed. See "gn help io_conversion".
-
- If unspecified, defaults to the empty string which causes the script
- result to be discarded. exec script will return None.
-
- dependencies:
- (Optional) A list of files that this script reads or otherwise depends
- on. These dependencies will be added to the build result such that if any
- of them change, the build will be regenerated and the script will be
- re-run.
-
- The script itself will be an implicit dependency so you do not need to
- list it.
-
-Example
-
- all_lines = exec_script(
- "myscript.py", [some_input], "list lines",
- [ rebase_path("data_file.txt", root_build_dir) ])
-
- # This example just calls the script with no arguments and discards the
- # result.
- exec_script("//foo/bar/myscript.py")
-)";
-
-Value RunExecScript(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() < 1 || args.size() > 4) {
- *err = Err(function->function(), "Wrong number of arguments to exec_script",
- "I expected between one and four arguments.");
- return Value();
- }
-
- const Settings* settings = scope->settings();
- const BuildSettings* build_settings = settings->build_settings();
- const SourceDir& cur_dir = scope->GetSourceDir();
-
- if (!CheckExecScriptPermissions(build_settings, function, err))
- return Value();
-
- // Find the script to run.
- std::string script_source_path = cur_dir.ResolveRelativeAs(
- true, args[0], err,
- scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
- return Value();
- base::FilePath script_path =
- build_settings->GetFullPath(script_source_path, true);
- if (!build_settings->secondary_source_path().empty() &&
- !base::PathExists(script_path)) {
- // Fall back to secondary source root when the file doesn't exist.
- script_path =
- build_settings->GetFullPathSecondary(script_source_path, true);
- }
-
- ScopedTrace trace(TraceItem::TRACE_SCRIPT_EXECUTE, script_source_path);
- trace.SetToolchain(settings->toolchain_label());
-
- // Add all dependencies of this script, including the script itself, to the
- // build deps.
- g_scheduler->AddGenDependency(script_path);
- if (args.size() == 4) {
- const Value& deps_value = args[3];
- if (!deps_value.VerifyTypeIs(Value::LIST, err))
- return Value();
-
- for (const auto& dep : deps_value.list_value()) {
- if (!dep.VerifyTypeIs(Value::STRING, err))
- return Value();
- g_scheduler->AddGenDependency(build_settings->GetFullPath(
- cur_dir.ResolveRelativeAs(
- true, dep, err,
- scope->settings()->build_settings()->root_path_utf8()),
- true));
- if (err->has_error())
- return Value();
- }
- }
-
- // Make the command line.
- const base::FilePath& interpreter_path = build_settings->python_path();
- base::CommandLine cmdline(interpreter_path);
-
- // CommandLine tries to interpret arguments by default. Disable that so
- // that the arguments will be passed through exactly as specified.
- cmdline.SetParseSwitches(false);
-
- cmdline.AppendArgPath(script_path);
-
- if (args.size() >= 2) {
- // Optional command-line arguments to the script.
- const Value& script_args = args[1];
- if (!script_args.VerifyTypeIs(Value::LIST, err))
- return Value();
- for (const auto& arg : script_args.list_value()) {
- if (!arg.VerifyTypeIs(Value::STRING, err))
- return Value();
- cmdline.AppendArg(arg.string_value());
- }
- }
-
- // Log command line for debugging help.
- trace.SetCommandLine(cmdline);
- Ticks begin_exec = 0;
- if (g_scheduler->verbose_logging()) {
-#if defined(OS_WIN)
- g_scheduler->Log("Executing",
- base::UTF16ToUTF8(cmdline.GetCommandLineString()));
-#else
- g_scheduler->Log("Executing", cmdline.GetCommandLineString());
-#endif
- begin_exec = TicksNow();
- }
-
- base::FilePath startup_dir =
- build_settings->GetFullPath(build_settings->build_dir());
- // The first time a build is run, no targets will have been written so the
- // build output directory won't exist. We need to make sure it does before
- // running any scripts with this as its startup directory, although it will
- // be relatively rare that the directory won't exist by the time we get here.
- //
- // If this shows up on benchmarks, we can cache whether we've done this
- // or not and skip creating the directory.
- base::CreateDirectory(startup_dir);
-
- // Execute the process.
- // TODO(brettw) set the environment block.
- std::string output;
- std::string stderr_output;
- int exit_code = 0;
- {
- if (!internal::ExecProcess(cmdline, startup_dir, &output, &stderr_output,
- &exit_code)) {
- *err = Err(
- function->function(), "Could not execute interpreter.",
- "I was trying to execute \"" + FilePathToUTF8(interpreter_path) +
- "\".");
- return Value();
- }
- }
- if (g_scheduler->verbose_logging()) {
- g_scheduler->Log(
- "Executing",
- script_source_path + " took " +
- base::Int64ToString(
- TicksDelta(TicksNow(), begin_exec).InMilliseconds()) +
- "ms");
- }
-
- if (exit_code != 0) {
- std::string msg =
- "Current dir: " + FilePathToUTF8(startup_dir) +
- "\nCommand: " + FilePathToUTF8(cmdline.GetCommandLineString()) +
- "\nReturned " + base::IntToString(exit_code);
- if (!output.empty())
- msg += " and printed out:\n\n" + output;
- else
- msg += ".";
- if (!stderr_output.empty())
- msg += "\nstderr:\n\n" + stderr_output;
-
- *err =
- Err(function->function(), "Script returned non-zero exit code.", msg);
- return Value();
- }
-
- // Default to None value for the input conversion if unspecified.
- return ConvertInputToValue(scope->settings(), output, function,
- args.size() >= 3 ? args[2] : Value(), err);
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_foreach.cc b/gn/tools/gn/function_foreach.cc
deleted file mode 100644
index 24ffaaba617..00000000000
--- a/gn/tools/gn/function_foreach.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// 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 "tools/gn/err.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-
-namespace functions {
-
-const char kForEach[] = "foreach";
-const char kForEach_HelpShort[] = "foreach: Iterate over a list.";
-const char kForEach_Help[] =
- R"(foreach: Iterate over a list.
-
- foreach(<loop_var>, <list>) {
- <loop contents>
- }
-
- Executes the loop contents block over each item in the list, assigning the
- loop_var to each item in sequence. The <loop_var> will be a copy so assigning
- to it will not mutate the list. The loop will iterate over a copy of <list>
- so mutating it inside the loop will not affect iteration.
-
- The block does not introduce a new scope, so that variable assignments inside
- the loop will be visible once the loop terminates.
-
- The loop variable will temporarily shadow any existing variables with the
- same name for the duration of the loop. After the loop terminates the loop
- variable will no longer be in scope, and the previous value (if any) will be
- restored.
-
-Example
-
- mylist = [ "a", "b", "c" ]
- foreach(i, mylist) {
- print(i)
- }
-
- Prints:
- a
- b
- c
-)";
-
-Value RunForEach(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err) {
- const auto& args_vector = args_list->contents();
- if (args_vector.size() != 2) {
- *err = Err(function, "Wrong number of arguments to foreach().",
- "Expecting exactly two.");
- return Value();
- }
-
- // Extract the loop variable.
- const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
- if (!identifier) {
- *err =
- Err(args_vector[0].get(), "Expected an identifier for the loop var.");
- return Value();
- }
- base::StringPiece loop_var(identifier->value().value());
-
- // Extract the list to iterate over. Always copy in case the code changes
- // the list variable inside the loop.
- Value list_value = args_vector[1]->Execute(scope, err);
- if (err->has_error())
- return Value();
- list_value.VerifyTypeIs(Value::Type::LIST, err);
- if (err->has_error())
- return Value();
- const std::vector<Value>& list = list_value.list_value();
-
- // Block to execute.
- const BlockNode* block = function->block();
- if (!block) {
- *err = Err(function, "Expected { after foreach.");
- return Value();
- }
-
- // If the loop variable was previously defined in this scope, save it so we
- // can put it back after the loop is done.
- const Value* old_loop_value_ptr = scope->GetValue(loop_var);
- Value old_loop_value;
- if (old_loop_value_ptr)
- old_loop_value = *old_loop_value_ptr;
-
- for (const auto& cur : list) {
- scope->SetValue(loop_var, cur, function);
- block->Execute(scope, err);
- if (err->has_error())
- return Value();
- }
-
- // Put back loop var.
- if (old_loop_value_ptr) {
- // Put back old value. Use the copy we made, rather than use the pointer,
- // which will probably point to the new value now in the scope.
- scope->SetValue(loop_var, std::move(old_loop_value),
- old_loop_value.origin());
- } else {
- // Loop variable was undefined before loop, delete it.
- scope->RemoveIdentifier(loop_var);
- }
-
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_foreach_unittest.cc b/gn/tools/gn/function_foreach_unittest.cc
deleted file mode 100644
index e4c289d887d..00000000000
--- a/gn/tools/gn/function_foreach_unittest.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// 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 "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(FunctionForeach, CollisionOnLoopVar) {
- TestWithScope setup;
- TestParseInput input(
- "a = 5\n"
- "i = 6\n"
- "foreach(i, [1, 2, 3]) {\n" // Use same loop var name previously defined.
- " print(\"$a $i\")\n"
- " a = a + 1\n" // Test for side effects inside loop.
- "}\n"
- "print(\"$a $i\")"); // Make sure that i goes back to original value.
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("5 1\n6 2\n7 3\n8 6\n", setup.print_output());
-}
-
-TEST(FunctionForeach, UniqueLoopVar) {
- TestWithScope setup;
- TestParseInput input_good(
- "foreach(i, [1, 2, 3]) {\n"
- " print(i)\n"
- "}\n");
- ASSERT_FALSE(input_good.has_error());
-
- Err err;
- input_good.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("1\n2\n3\n", setup.print_output());
- setup.print_output().clear();
-
- // Same thing but try to use the loop var after loop is done. It should be
- // undefined and throw an error.
- TestParseInput input_bad(
- "foreach(i, [1, 2, 3]) {\n"
- " print(i)\n"
- "}\n"
- "print(i)");
- ASSERT_FALSE(input_bad.has_error()); // Should parse OK.
-
- input_bad.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()); // Shouldn't actually run.
-}
-
-// Checks that the identifier used as the list is marked as "used".
-TEST(FunctionForeach, MarksIdentAsUsed) {
- TestWithScope setup;
- TestParseInput input_good(
- "a = [1, 2]\n"
- "foreach(i, a) {\n"
- " print(i)\n"
- "}\n");
- ASSERT_FALSE(input_good.has_error());
-
- Err err;
- input_good.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("1\n2\n", setup.print_output());
- setup.print_output().clear();
-
- // Check for unused vars.
- EXPECT_TRUE(setup.scope()->CheckForUnusedVars(&err));
- EXPECT_FALSE(err.has_error());
-}
-
-// Checks that the list can be modified during iteration without crashing.
-TEST(FunctionForeach, ListModification) {
- TestWithScope setup;
- TestParseInput input_grow(
- "a = [1, 2]\n"
- "foreach(i, a) {\n"
- " print(i)\n"
- " if (i <= 8) {\n"
- " a += [ i + 2 ]\n"
- " }\n"
- "}\n"
- "print(a)");
- ASSERT_FALSE(input_grow.has_error());
-
- Err err;
- input_grow.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // The result of the loop should have been unaffected by the mutations of
- // the list variable inside the loop, but the modifications made to it
- // should have been persisted.
- EXPECT_EQ("1\n2\n[1, 2, 3, 4]\n", setup.print_output());
- setup.print_output().clear();
-}
diff --git a/gn/tools/gn/function_forward_variables_from.cc b/gn/tools/gn/function_forward_variables_from.cc
deleted file mode 100644
index 875af91b0bd..00000000000
--- a/gn/tools/gn/function_forward_variables_from.cc
+++ /dev/null
@@ -1,249 +0,0 @@
-// 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 "tools/gn/err.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-
-namespace functions {
-
-namespace {
-
-void ForwardAllValues(const FunctionCallNode* function,
- Scope* source,
- Scope* dest,
- const std::set<std::string>& exclusion_set,
- Err* err) {
- Scope::MergeOptions options;
- // This function needs to clobber existing for it to be useful. It will be
- // called in a template to forward all values, but there will be some
- // default stuff like configs set up in both scopes, so it would always
- // fail if it didn't clobber.
- options.clobber_existing = true;
- options.skip_private_vars = true;
- options.mark_dest_used = false;
- options.excluded_values = exclusion_set;
- source->NonRecursiveMergeTo(dest, options, function, "source scope", err);
- source->MarkAllUsed();
-}
-
-void ForwardValuesFromList(Scope* source,
- Scope* dest,
- const std::vector<Value>& list,
- const std::set<std::string>& exclusion_set,
- Err* err) {
- for (const Value& cur : list) {
- if (!cur.VerifyTypeIs(Value::STRING, err))
- return;
- if (exclusion_set.find(cur.string_value()) != exclusion_set.end())
- continue;
- const Value* value = source->GetValue(cur.string_value(), true);
- if (value) {
- // Use the storage key for the original value rather than the string in
- // "cur" because "cur" is a temporary that will be deleted, and Scopes
- // expect a persistent StringPiece (it won't copy). Not doing this will
- // lead the scope's key to point to invalid memory after this returns.
- base::StringPiece storage_key = source->GetStorageKey(cur.string_value());
- if (storage_key.empty()) {
- // Programmatic value, don't allow copying.
- *err =
- Err(cur, "This value can't be forwarded.",
- "The variable \"" + cur.string_value() + "\" is a built-in.");
- return;
- }
-
- // Don't allow clobbering existing values.
- const Value* existing_value = dest->GetValue(storage_key);
- if (existing_value) {
- *err = Err(
- cur, "Clobbering existing value.",
- "The current scope already defines a value \"" +
- cur.string_value() +
- "\".\nforward_variables_from() won't clobber "
- "existing values. If you want to\nmerge lists, you'll need to "
- "do this explicitly.");
- err->AppendSubErr(Err(*existing_value, "value being clobbered."));
- return;
- }
-
- // Keep the origin information from the original value. The normal
- // usage is for this to be used in a template, and if there's an error,
- // the user expects to see the line where they set the variable
- // blamed, rather than a template call to forward_variables_from().
- dest->SetValue(storage_key, *value, value->origin());
- }
- }
-}
-
-} // namespace
-
-const char kForwardVariablesFrom[] = "forward_variables_from";
-const char kForwardVariablesFrom_HelpShort[] =
- "forward_variables_from: Copies variables from a different scope.";
-const char kForwardVariablesFrom_Help[] =
- R"(forward_variables_from: Copies variables from a different scope.
-
- forward_variables_from(from_scope, variable_list_or_star,
- variable_to_not_forward_list = [])
-
- Copies the given variables from the given scope to the local scope if they
- exist. This is normally used in the context of templates to use the values of
- variables defined in the template invocation to a template-defined target.
-
- The variables in the given variable_list will be copied if they exist in the
- given scope or any enclosing scope. If they do not exist, nothing will happen
- and they be left undefined in the current scope.
-
- As a special case, if the variable_list is a string with the value of "*",
- all variables from the given scope will be copied. "*" only copies variables
- set directly on the from_scope, not enclosing ones. Otherwise it would
- duplicate all global variables.
-
- When an explicit list of variables is supplied, if the variable exists in the
- current (destination) scope already, an error will be thrown. If "*" is
- specified, variables in the current scope will be clobbered (the latter is
- important because most targets have an implicit configs list, which means it
- wouldn't work at all if it didn't clobber).
-
- The sources assignment filter (see "gn help set_sources_assignment_filter")
- is never applied by this function. It's assumed than any desired filtering
- was already done when sources was set on the from_scope.
-
- If variables_to_not_forward_list is non-empty, then it must contains a list
- of variable names that will not be forwarded. This is mostly useful when
- variable_list_or_star has a value of "*".
-
-Examples
-
- # forward_variables_from(invoker, ["foo"])
- # is equivalent to:
- assert(!defined(foo))
- if (defined(invoker.foo)) {
- foo = invoker.foo
- }
-
- # This is a common action template. It would invoke a script with some given
- # parameters, and wants to use the various types of deps and the visibility
- # from the invoker if it's defined. It also injects an additional dependency
- # to all targets.
- template("my_test") {
- action(target_name) {
- forward_variables_from(invoker, [ "data_deps", "deps",
- "public_deps", "visibility"])
- # Add our test code to the dependencies.
- # "deps" may or may not be defined at this point.
- if (defined(deps)) {
- deps += [ "//tools/doom_melon" ]
- } else {
- deps = [ "//tools/doom_melon" ]
- }
- }
- }
-
- # This is a template around a target whose type depends on a global variable.
- # It forwards all values from the invoker.
- template("my_wrapper") {
- target(my_wrapper_target_type, target_name) {
- forward_variables_from(invoker, "*")
- }
- }
-
- # A template that wraps another. It adds behavior based on one
- # variable, and forwards all others to the nested target.
- template("my_ios_test_app") {
- ios_test_app(target_name) {
- forward_variables_from(invoker, "*", ["test_bundle_name"])
- if (!defined(extra_substitutions)) {
- extra_substitutions = []
- }
- extra_substitutions += [ "BUNDLE_ID_TEST_NAME=$test_bundle_name" ]
- }
- }
-)";
-
-// This function takes a ListNode rather than a resolved vector of values
-// both avoid copying the potentially-large source scope, and so the variables
-// in the source scope can be marked as used.
-Value RunForwardVariablesFrom(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err) {
- const auto& args_vector = args_list->contents();
- if (args_vector.size() != 2 && args_vector.size() != 3) {
- *err = Err(function, "Wrong number of arguments.",
- "Expecting two or three arguments.");
- return Value();
- }
-
- Value* value = nullptr; // Value to use, may point to result_value.
- Value result_value; // Storage for the "evaluate" case.
- const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
- if (identifier) {
- // Optimize the common case where the input scope is an identifier. This
- // prevents a copy of a potentially large Scope object.
- value = scope->GetMutableValue(identifier->value().value(),
- Scope::SEARCH_NESTED, true);
- if (!value) {
- *err = Err(identifier, "Undefined identifier.");
- return Value();
- }
- } else {
- // Non-optimized case, just evaluate the argument.
- result_value = args_vector[0]->Execute(scope, err);
- if (err->has_error())
- return Value();
- value = &result_value;
- }
-
- // Extract the source scope.
- if (!value->VerifyTypeIs(Value::SCOPE, err))
- return Value();
- Scope* source = value->scope_value();
-
- // Extract the exclusion list if defined.
- std::set<std::string> exclusion_set;
- if (args_vector.size() == 3) {
- Value exclusion_value = args_vector[2]->Execute(scope, err);
- if (err->has_error())
- return Value();
-
- if (exclusion_value.type() != Value::LIST) {
- *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
- "Expecting a list of strings.");
- return Value();
- }
-
- for (const Value& cur : exclusion_value.list_value()) {
- if (!cur.VerifyTypeIs(Value::STRING, err))
- return Value();
-
- exclusion_set.insert(cur.string_value());
- }
- }
-
- // Extract the list. If all_values is not set, the what_value will be a list.
- Value what_value = args_vector[1]->Execute(scope, err);
- if (err->has_error())
- return Value();
- if (what_value.type() == Value::STRING) {
- if (what_value.string_value() == "*") {
- ForwardAllValues(function, source, scope, exclusion_set, err);
- return Value();
- }
- } else {
- if (what_value.type() == Value::LIST) {
- ForwardValuesFromList(source, scope, what_value.list_value(),
- exclusion_set, err);
- return Value();
- }
- }
-
- // Not the right type of argument.
- *err = Err(what_value, "Not a valid list of variables to copy.",
- "Expecting either the string \"*\" or a list of strings.");
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_forward_variables_from_unittest.cc b/gn/tools/gn/function_forward_variables_from_unittest.cc
deleted file mode 100644
index 20baea7566c..00000000000
--- a/gn/tools/gn/function_forward_variables_from_unittest.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// 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 "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using FunctionForwardVariablesFromTest = TestWithScheduler;
-
-TEST_F(FunctionForwardVariablesFromTest, List) {
- Err err;
- std::string program =
- "template(\"a\") {\n"
- " forward_variables_from(invoker, [\"x\", \"y\", \"z\"])\n"
- " assert(!defined(z))\n" // "z" should still be undefined.
- " print(\"$target_name, $x, $y\")\n"
- "}\n"
- "a(\"target\") {\n"
- " x = 1\n"
- " y = 2\n"
- "}\n";
-
- {
- TestWithScope setup;
-
- // Defines a template and copy the two x and y, and z values out.
- TestParseInput input(program);
- ASSERT_FALSE(input.has_error());
-
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("target, 1, 2\n", setup.print_output());
- setup.print_output().clear();
- }
-
- {
- TestWithScope setup;
-
- // Test that the same input but forwarding a variable with the name of
- // something in the given scope throws an error rather than clobbering it.
- // This uses the same known-good program as before, but adds another
- // variable in the scope before it.
- TestParseInput clobber("x = 1\n" + program);
- ASSERT_FALSE(clobber.has_error());
-
- clobber.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()); // Should thow a clobber error.
- EXPECT_EQ("Clobbering existing value.", err.message());
- }
-}
-
-TEST_F(FunctionForwardVariablesFromTest, LiteralList) {
- TestWithScope setup;
-
- // Forwards all variables from a literal scope into another scope definition.
- TestParseInput input(
- "a = {\n"
- " forward_variables_from({x = 1 y = 2}, \"*\")\n"
- " z = 3\n"
- "}\n"
- "print(\"${a.x} ${a.y} ${a.z}\")\n");
-
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("1 2 3\n", setup.print_output());
- setup.print_output().clear();
-}
-
-TEST_F(FunctionForwardVariablesFromTest, ListWithExclusion) {
- TestWithScope setup;
-
- // Defines a template and copy the two x and y, and z values out.
- TestParseInput input(
- "template(\"a\") {\n"
- " forward_variables_from(invoker, [\"x\", \"y\", \"z\"], [\"z\"])\n"
- " assert(!defined(z))\n" // "z" should still be undefined.
- " print(\"$target_name, $x, $y\")\n"
- "}\n"
- "a(\"target\") {\n"
- " x = 1\n"
- " y = 2\n"
- " z = 3\n"
- " print(\"$z\")\n"
- "}\n");
-
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("3\ntarget, 1, 2\n", setup.print_output());
- setup.print_output().clear();
-}
-
-TEST_F(FunctionForwardVariablesFromTest, ErrorCases) {
- TestWithScope setup;
-
- // Type check the source scope.
- TestParseInput invalid_source(
- "template(\"a\") {\n"
- " forward_variables_from(42, [\"x\"])\n"
- " print(\"$target_name\")\n" // Prevent unused var error.
- "}\n"
- "a(\"target\") {\n"
- "}\n");
- ASSERT_FALSE(invalid_source.has_error());
- Err err;
- invalid_source.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("This is not a scope.", err.message());
-
- // Type check the list. We need to use a new template name each time since
- // all of these invocations are executing in sequence in the same scope.
- TestParseInput invalid_list(
- "template(\"b\") {\n"
- " forward_variables_from(invoker, 42)\n"
- " print(\"$target_name\")\n"
- "}\n"
- "b(\"target\") {\n"
- "}\n");
- ASSERT_FALSE(invalid_list.has_error());
- err = Err();
- invalid_list.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Not a valid list of variables to copy.", err.message());
-
- // Type check the exclusion list.
- TestParseInput invalid_exclusion_list(
- "template(\"c\") {\n"
- " forward_variables_from(invoker, \"*\", 42)\n"
- " print(\"$target_name\")\n"
- "}\n"
- "c(\"target\") {\n"
- "}\n");
- ASSERT_FALSE(invalid_exclusion_list.has_error());
- err = Err();
- invalid_exclusion_list.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Not a valid list of variables to exclude.", err.message());
-
- // Programmatic values should error.
- TestParseInput prog(
- "template(\"d\") {\n"
- " forward_variables_from(invoker, [\"root_out_dir\"])\n"
- " print(\"$target_name\")\n"
- "}\n"
- "d(\"target\") {\n"
- "}\n");
- ASSERT_FALSE(prog.has_error());
- err = Err();
- prog.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("This value can't be forwarded.", err.message());
-
- // Not enough arguments.
- TestParseInput not_enough_arguments(
- "template(\"e\") {\n"
- " forward_variables_from(invoker)\n"
- " print(\"$target_name\")\n"
- "}\n"
- "e(\"target\") {\n"
- "}\n");
- ASSERT_FALSE(not_enough_arguments.has_error());
- err = Err();
- not_enough_arguments.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Wrong number of arguments.", err.message());
-
- // Too many arguments.
- TestParseInput too_many_arguments(
- "template(\"f\") {\n"
- " forward_variables_from(invoker, \"*\", [], [])\n"
- " print(\"$target_name\")\n"
- "}\n"
- "f(\"target\") {\n"
- "}\n");
- ASSERT_FALSE(too_many_arguments.has_error());
- err = Err();
- too_many_arguments.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Wrong number of arguments.", err.message());
-}
-
-TEST_F(FunctionForwardVariablesFromTest, Star) {
- TestWithScope setup;
-
- // Defines a template and copy the two x and y values out. The "*" behavior
- // should clobber existing variables with the same name.
- TestParseInput input(
- "template(\"a\") {\n"
- " x = 1000000\n" // Should be clobbered.
- " forward_variables_from(invoker, \"*\")\n"
- " print(\"$target_name, $x, $y\")\n"
- "}\n"
- "a(\"target\") {\n"
- " x = 1\n"
- " y = 2\n"
- "}\n");
-
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("target, 1, 2\n", setup.print_output());
- setup.print_output().clear();
-}
-
-TEST_F(FunctionForwardVariablesFromTest, StarWithExclusion) {
- TestWithScope setup;
-
- // Defines a template and copy all values except z value. The "*" behavior
- // should clobber existing variables with the same name.
- TestParseInput input(
- "template(\"a\") {\n"
- " x = 1000000\n" // Should be clobbered.
- " forward_variables_from(invoker, \"*\", [\"z\"])\n"
- " print(\"$target_name, $x, $y\")\n"
- "}\n"
- "a(\"target\") {\n"
- " x = 1\n"
- " y = 2\n"
- " z = 3\n"
- " print(\"$z\")\n"
- "}\n");
-
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("3\ntarget, 1, 2\n", setup.print_output());
- setup.print_output().clear();
-}
diff --git a/gn/tools/gn/function_get_label_info.cc b/gn/tools/gn/function_get_label_info.cc
deleted file mode 100644
index 0d0c77a3c0c..00000000000
--- a/gn/tools/gn/function_get_label_info.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// 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 "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/label.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/value.h"
-
-namespace functions {
-
-const char kGetLabelInfo[] = "get_label_info";
-const char kGetLabelInfo_HelpShort[] =
- "get_label_info: Get an attribute from a target's label.";
-const char kGetLabelInfo_Help[] =
- R"*(get_label_info: Get an attribute from a target's label.
-
- get_label_info(target_label, what)
-
- Given the label of a target, returns some attribute of that target. The
- target need not have been previously defined in the same file, since none of
- the attributes depend on the actual target definition, only the label itself.
-
- See also "gn help get_target_outputs".
-
-Possible values for the "what" parameter
-
- "name"
- The short name of the target. This will match the value of the
- "target_name" variable inside that target's declaration. For the label
- "//foo/bar:baz" this will return "baz".
-
- "dir"
- The directory containing the target's definition, with no slash at the
- end. For the label "//foo/bar:baz" this will return "//foo/bar".
-
- "target_gen_dir"
- The generated file directory for the target. This will match the value of
- the "target_gen_dir" variable when inside that target's declaration.
-
- "root_gen_dir"
- The root of the generated file tree for the target. This will match the
- value of the "root_gen_dir" variable when inside that target's
- declaration.
-
- "target_out_dir
- The output directory for the target. This will match the value of the
- "target_out_dir" variable when inside that target's declaration.
-
- "root_out_dir"
- The root of the output file tree for the target. This will match the
- value of the "root_out_dir" variable when inside that target's
- declaration.
-
- "label_no_toolchain"
- The fully qualified version of this label, not including the toolchain.
- For the input ":bar" it might return "//foo:bar".
-
- "label_with_toolchain"
- The fully qualified version of this label, including the toolchain. For
- the input ":bar" it might return "//foo:bar(//toolchain:x64)".
-
- "toolchain"
- The label of the toolchain. This will match the value of the
- "current_toolchain" variable when inside that target's declaration.
-
-Examples
-
- get_label_info(":foo", "name")
- # Returns string "foo".
-
- get_label_info("//foo/bar:baz", "target_gen_dir")
- # Returns string "//out/Debug/gen/foo/bar".
-)*";
-
-Value RunGetLabelInfo(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 2) {
- *err = Err(function, "Expected two arguments.");
- return Value();
- }
-
- // Resolve the requested label.
- Label label = Label::Resolve(scope->GetSourceDir(),
- ToolchainLabelForScope(scope), args[0], err);
- if (label.is_null())
- return Value();
-
- // Extract the "what" parameter.
- if (!args[1].VerifyTypeIs(Value::STRING, err))
- return Value();
- const std::string& what = args[1].string_value();
-
- Value result(function, Value::STRING);
- if (what == "name") {
- result.string_value() = label.name();
-
- } else if (what == "dir") {
- result.string_value() = DirectoryWithNoLastSlash(label.dir());
-
- } else if (what == "target_gen_dir") {
- result.string_value() = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(scope, label.GetToolchainLabel()), label.dir(),
- BuildDirType::GEN));
-
- } else if (what == "root_gen_dir") {
- result.string_value() = DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
- BuildDirContext(scope, label.GetToolchainLabel()), BuildDirType::GEN));
-
- } else if (what == "target_out_dir") {
- result.string_value() = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(scope, label.GetToolchainLabel()), label.dir(),
- BuildDirType::OBJ));
-
- } else if (what == "root_out_dir") {
- result.string_value() = DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
- BuildDirContext(scope, label.GetToolchainLabel()),
- BuildDirType::TOOLCHAIN_ROOT));
-
- } else if (what == "toolchain") {
- result.string_value() = label.GetToolchainLabel().GetUserVisibleName(false);
-
- } else if (what == "label_no_toolchain") {
- result.string_value() =
- label.GetWithNoToolchain().GetUserVisibleName(false);
-
- } else if (what == "label_with_toolchain") {
- result.string_value() = label.GetUserVisibleName(true);
-
- } else {
- *err = Err(args[1], "Unknown value for \"what\" parameter.");
- return Value();
- }
-
- return result;
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_get_label_info_unittest.cc b/gn/tools/gn/function_get_label_info_unittest.cc
deleted file mode 100644
index 46eb7f2c940..00000000000
--- a/gn/tools/gn/function_get_label_info_unittest.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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 "tools/gn/functions.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-class GetLabelInfoTest : public testing::Test {
- public:
- GetLabelInfoTest() : testing::Test() {
- setup_.scope()->set_source_dir(SourceDir("//src/foo/"));
- }
-
- // Convenience wrapper to call GetLabelInfo.
- std::string Call(const std::string& label, const std::string& what) {
- FunctionCallNode function;
-
- std::vector<Value> args;
- args.push_back(Value(nullptr, label));
- args.push_back(Value(nullptr, what));
-
- Err err;
- Value result =
- functions::RunGetLabelInfo(setup_.scope(), &function, args, &err);
- if (err.has_error()) {
- EXPECT_TRUE(result.type() == Value::NONE);
- return std::string();
- }
- return result.string_value();
- }
-
- protected:
- // Note: TestWithScope's default toolchain is "//toolchain:default" and
- // output dir is "//out/Debug".
- TestWithScope setup_;
-};
-
-} // namespace
-
-TEST_F(GetLabelInfoTest, BadInput) {
- EXPECT_EQ("", Call(":name", "incorrect_value"));
- EXPECT_EQ("", Call("", "name"));
-}
-
-TEST_F(GetLabelInfoTest, Name) {
- EXPECT_EQ("name", Call(":name", "name"));
- EXPECT_EQ("name", Call("//foo/bar:name", "name"));
- EXPECT_EQ("name", Call("//foo/bar:name(//other:tc)", "name"));
-}
-
-TEST_F(GetLabelInfoTest, Dir) {
- EXPECT_EQ("//src/foo", Call(":name", "dir"));
- EXPECT_EQ("//foo/bar", Call("//foo/bar:baz", "dir"));
- EXPECT_EQ("//foo/bar", Call("//foo/bar:baz(//other:tc)", "dir"));
-}
-
-TEST_F(GetLabelInfoTest, RootOutDir) {
- EXPECT_EQ("//out/Debug", Call(":name", "root_out_dir"));
- EXPECT_EQ("//out/Debug/random",
- Call(":name(//toolchain:random)", "root_out_dir"));
-}
-
-TEST_F(GetLabelInfoTest, RootGenDir) {
- EXPECT_EQ("//out/Debug/gen", Call(":name", "root_gen_dir"));
- EXPECT_EQ("//out/Debug/gen",
- Call(":name(//toolchain:default)", "root_gen_dir"));
- EXPECT_EQ("//out/Debug/random/gen",
- Call(":name(//toolchain:random)", "root_gen_dir"));
-}
-
-TEST_F(GetLabelInfoTest, TargetOutDir) {
- EXPECT_EQ("//out/Debug/obj/src/foo", Call(":name", "target_out_dir"));
- EXPECT_EQ("//out/Debug/obj/foo",
- Call("//foo:name(//toolchain:default)", "target_out_dir"));
- EXPECT_EQ("//out/Debug/random/obj/foo",
- Call("//foo:name(//toolchain:random)", "target_out_dir"));
-}
-
-TEST_F(GetLabelInfoTest, TargetGenDir) {
- EXPECT_EQ("//out/Debug/gen/src/foo", Call(":name", "target_gen_dir"));
- EXPECT_EQ("//out/Debug/gen/foo",
- Call("//foo:name(//toolchain:default)", "target_gen_dir"));
- EXPECT_EQ("//out/Debug/random/gen/foo",
- Call("//foo:name(//toolchain:random)", "target_gen_dir"));
-}
-
-TEST_F(GetLabelInfoTest, LabelNoToolchain) {
- EXPECT_EQ("//src/foo:name", Call(":name", "label_no_toolchain"));
- EXPECT_EQ("//src/foo:name",
- Call("//src/foo:name(//toolchain:random)", "label_no_toolchain"));
-}
-
-TEST_F(GetLabelInfoTest, LabelWithToolchain) {
- EXPECT_EQ("//src/foo:name(//toolchain:default)",
- Call(":name", "label_with_toolchain"));
- EXPECT_EQ("//src/foo:name(//toolchain:random)",
- Call(":name(//toolchain:random)", "label_with_toolchain"));
-}
-
-TEST_F(GetLabelInfoTest, Toolchain) {
- EXPECT_EQ("//toolchain:default", Call(":name", "toolchain"));
- EXPECT_EQ("//toolchain:random",
- Call(":name(//toolchain:random)", "toolchain"));
-}
diff --git a/gn/tools/gn/function_get_path_info.cc b/gn/tools/gn/function_get_path_info.cc
deleted file mode 100644
index 08f7e8c850d..00000000000
--- a/gn/tools/gn/function_get_path_info.cc
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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 <stddef.h>
-
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/value.h"
-
-namespace functions {
-
-namespace {
-
-// Corresponds to the various values of "what" in the function call.
-enum What {
- WHAT_FILE,
- WHAT_NAME,
- WHAT_EXTENSION,
- WHAT_DIR,
- WHAT_ABSPATH,
- WHAT_GEN_DIR,
- WHAT_OUT_DIR,
-};
-
-// Returns the directory containing the input (resolving it against the
-// |current_dir|), regardless of whether the input is a directory or a file.
-SourceDir DirForInput(const Settings* settings,
- const SourceDir& current_dir,
- const Value& input,
- Err* err) {
- // Input should already have been validated as a string.
- const std::string& input_string = input.string_value();
-
- if (!input_string.empty() && input_string[input_string.size() - 1] == '/') {
- // Input is a directory.
- return current_dir.ResolveRelativeDir(
- input, err, settings->build_settings()->root_path_utf8());
- }
-
- // Input is a file.
- return current_dir
- .ResolveRelativeFile(input, err,
- settings->build_settings()->root_path_utf8())
- .GetDir();
-}
-
-std::string GetOnePathInfo(const Settings* settings,
- const SourceDir& current_dir,
- What what,
- const Value& input,
- Err* err) {
- if (!input.VerifyTypeIs(Value::STRING, err))
- return std::string();
- const std::string& input_string = input.string_value();
- if (input_string.empty()) {
- *err = Err(input, "Calling get_path_info on an empty string.");
- return std::string();
- }
-
- switch (what) {
- case WHAT_FILE: {
- return FindFilename(&input_string).as_string();
- }
- case WHAT_NAME: {
- std::string file = FindFilename(&input_string).as_string();
- size_t extension_offset = FindExtensionOffset(file);
- if (extension_offset == std::string::npos)
- return file;
- // Trim extension and dot.
- return file.substr(0, extension_offset - 1);
- }
- case WHAT_EXTENSION: {
- return FindExtension(&input_string).as_string();
- }
- case WHAT_DIR: {
- base::StringPiece dir_incl_slash = FindDir(&input_string);
- if (dir_incl_slash.empty())
- return std::string(".");
- // Trim slash since this function doesn't return trailing slashes. The
- // times we don't do this are if the result is "/" and "//" since those
- // slashes can't be trimmed.
- if (dir_incl_slash == "/")
- return std::string("/.");
- if (dir_incl_slash == "//")
- return std::string("//.");
- return dir_incl_slash.substr(0, dir_incl_slash.size() - 1).as_string();
- }
- case WHAT_GEN_DIR: {
- return DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(settings),
- DirForInput(settings, current_dir, input, err), BuildDirType::GEN));
- }
- case WHAT_OUT_DIR: {
- return DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(settings),
- DirForInput(settings, current_dir, input, err), BuildDirType::OBJ));
- }
- case WHAT_ABSPATH: {
- bool as_dir =
- !input_string.empty() && input_string[input_string.size() - 1] == '/';
-
- return current_dir.ResolveRelativeAs(
- !as_dir, input, err, settings->build_settings()->root_path_utf8(),
- &input_string);
- }
- default:
- NOTREACHED();
- return std::string();
- }
-}
-
-} // namespace
-
-const char kGetPathInfo[] = "get_path_info";
-const char kGetPathInfo_HelpShort[] =
- "get_path_info: Extract parts of a file or directory name.";
-const char kGetPathInfo_Help[] =
- R"(get_path_info: Extract parts of a file or directory name.
-
- get_path_info(input, what)
-
- The first argument is either a string representing a file or directory name,
- or a list of such strings. If the input is a list the return value will be a
- list containing the result of applying the rule to each item in the input.
-
-Possible values for the "what" parameter
-
- "file"
- The substring after the last slash in the path, including the name and
- extension. If the input ends in a slash, the empty string will be
- returned.
- "foo/bar.txt" => "bar.txt"
- "bar.txt" => "bar.txt"
- "foo/" => ""
- "" => ""
-
- "name"
- The substring of the file name not including the extension.
- "foo/bar.txt" => "bar"
- "foo/bar" => "bar"
- "foo/" => ""
-
- "extension"
- The substring following the last period following the last slash, or the
- empty string if not found. The period is not included.
- "foo/bar.txt" => "txt"
- "foo/bar" => ""
-
- "dir"
- The directory portion of the name, not including the slash.
- "foo/bar.txt" => "foo"
- "//foo/bar" => "//foo"
- "foo" => "."
-
- The result will never end in a slash, so if the resulting is empty, the
- system ("/") or source ("//") roots, a "." will be appended such that it
- is always legal to append a slash and a filename and get a valid path.
-
- "out_dir"
- The output file directory corresponding to the path of the given file,
- not including a trailing slash.
- "//foo/bar/baz.txt" => "//out/Default/obj/foo/bar"
-
- "gen_dir"
- The generated file directory corresponding to the path of the given file,
- not including a trailing slash.
- "//foo/bar/baz.txt" => "//out/Default/gen/foo/bar"
-
- "abspath"
- The full absolute path name to the file or directory. It will be resolved
- relative to the current directory, and then the source- absolute version
- will be returned. If the input is system- absolute, the same input will
- be returned.
- "foo/bar.txt" => "//mydir/foo/bar.txt"
- "foo/" => "//mydir/foo/"
- "//foo/bar" => "//foo/bar" (already absolute)
- "/usr/include" => "/usr/include" (already absolute)
-
- If you want to make the path relative to another directory, or to be
- system-absolute, see rebase_path().
-
-Examples
- sources = [ "foo.cc", "foo.h" ]
- result = get_path_info(source, "abspath")
- # result will be [ "//mydir/foo.cc", "//mydir/foo.h" ]
-
- result = get_path_info("//foo/bar/baz.cc", "dir")
- # result will be "//foo/bar"
-
- # Extract the source-absolute directory name,
- result = get_path_info(get_path_info(path, "dir"), "abspath")
-)";
-
-Value RunGetPathInfo(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 2) {
- *err = Err(function, "Expecting two arguments to get_path_info.");
- return Value();
- }
-
- // Extract the "what".
- if (!args[1].VerifyTypeIs(Value::STRING, err))
- return Value();
- What what;
- if (args[1].string_value() == "file") {
- what = WHAT_FILE;
- } else if (args[1].string_value() == "name") {
- what = WHAT_NAME;
- } else if (args[1].string_value() == "extension") {
- what = WHAT_EXTENSION;
- } else if (args[1].string_value() == "dir") {
- what = WHAT_DIR;
- } else if (args[1].string_value() == "out_dir") {
- what = WHAT_OUT_DIR;
- } else if (args[1].string_value() == "gen_dir") {
- what = WHAT_GEN_DIR;
- } else if (args[1].string_value() == "abspath") {
- what = WHAT_ABSPATH;
- } else {
- *err = Err(args[1], "Unknown value for 'what'.");
- return Value();
- }
-
- const SourceDir& current_dir = scope->GetSourceDir();
- if (args[0].type() == Value::STRING) {
- return Value(function, GetOnePathInfo(scope->settings(), current_dir, what,
- args[0], err));
- } else if (args[0].type() == Value::LIST) {
- const std::vector<Value>& input_list = args[0].list_value();
- Value result(function, Value::LIST);
- for (const auto& cur : input_list) {
- result.list_value().push_back(Value(
- function,
- GetOnePathInfo(scope->settings(), current_dir, what, cur, err)));
- if (err->has_error())
- return Value();
- }
- return result;
- }
-
- *err = Err(args[0], "Path must be a string or a list of strings.");
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_get_path_info_unittest.cc b/gn/tools/gn/function_get_path_info_unittest.cc
deleted file mode 100644
index 6e4a8da6e55..00000000000
--- a/gn/tools/gn/function_get_path_info_unittest.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// 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 "tools/gn/functions.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-namespace {
-
-class GetPathInfoTest : public testing::Test {
- public:
- GetPathInfoTest() : testing::Test() {
- setup_.scope()->set_source_dir(SourceDir("//src/foo/"));
- }
-
- // Convenience wrapper to call GetLabelInfo.
- std::string Call(const std::string& input, const std::string& what) {
- FunctionCallNode function;
-
- std::vector<Value> args;
- args.push_back(Value(nullptr, input));
- args.push_back(Value(nullptr, what));
-
- Err err;
- Value result =
- functions::RunGetPathInfo(setup_.scope(), &function, args, &err);
- if (err.has_error()) {
- EXPECT_TRUE(result.type() == Value::NONE);
- return std::string();
- }
- return result.string_value();
- }
-
- protected:
- TestWithScope setup_;
-};
-
-} // namespace
-
-TEST_F(GetPathInfoTest, File) {
- EXPECT_EQ("bar.txt", Call("foo/bar.txt", "file"));
- EXPECT_EQ("bar.txt", Call("bar.txt", "file"));
- EXPECT_EQ("bar.txt", Call("/bar.txt", "file"));
- EXPECT_EQ("", Call("foo/", "file"));
- EXPECT_EQ("", Call("//", "file"));
- EXPECT_EQ("", Call("/", "file"));
-}
-
-TEST_F(GetPathInfoTest, Name) {
- EXPECT_EQ("bar", Call("foo/bar.txt", "name"));
- EXPECT_EQ("bar", Call("bar.", "name"));
- EXPECT_EQ("", Call("/.txt", "name"));
- EXPECT_EQ("", Call("foo/", "name"));
- EXPECT_EQ("", Call("//", "name"));
- EXPECT_EQ("", Call("/", "name"));
-}
-
-TEST_F(GetPathInfoTest, Extension) {
- EXPECT_EQ("txt", Call("foo/bar.txt", "extension"));
- EXPECT_EQ("", Call("bar.", "extension"));
- EXPECT_EQ("txt", Call("/.txt", "extension"));
- EXPECT_EQ("", Call("f.oo/", "extension"));
- EXPECT_EQ("", Call("//", "extension"));
- EXPECT_EQ("", Call("/", "extension"));
-}
-
-TEST_F(GetPathInfoTest, Dir) {
- EXPECT_EQ("foo", Call("foo/bar.txt", "dir"));
- EXPECT_EQ(".", Call("bar.txt", "dir"));
- EXPECT_EQ("foo/bar", Call("foo/bar/baz", "dir"));
- EXPECT_EQ("//foo", Call("//foo/", "dir"));
- EXPECT_EQ("//.", Call("//", "dir"));
- EXPECT_EQ("/foo", Call("/foo/", "dir"));
- EXPECT_EQ("/.", Call("/", "dir"));
-}
-
-// Note "current dir" is "//src/foo"
-TEST_F(GetPathInfoTest, AbsPath) {
- EXPECT_EQ("//src/foo/foo/bar.txt", Call("foo/bar.txt", "abspath"));
- EXPECT_EQ("//src/foo/bar.txt", Call("bar.txt", "abspath"));
- EXPECT_EQ("//src/foo/bar/", Call("bar/", "abspath"));
- EXPECT_EQ("//foo", Call("//foo", "abspath"));
- EXPECT_EQ("//foo/", Call("//foo/", "abspath"));
- EXPECT_EQ("//", Call("//", "abspath"));
- EXPECT_EQ("/foo", Call("/foo", "abspath"));
- EXPECT_EQ("/foo/", Call("/foo/", "abspath"));
- EXPECT_EQ("/", Call("/", "abspath"));
-}
-
-// Note build dir is "//out/Debug/".
-TEST_F(GetPathInfoTest, OutDir) {
- EXPECT_EQ("//out/Debug/obj/src/foo/foo", Call("foo/bar.txt", "out_dir"));
- EXPECT_EQ("//out/Debug/obj/src/foo/bar", Call("bar/", "out_dir"));
- EXPECT_EQ("//out/Debug/obj/src/foo", Call(".", "out_dir"));
- EXPECT_EQ("//out/Debug/obj/src/foo", Call("bar", "out_dir"));
- EXPECT_EQ("//out/Debug/obj/foo", Call("//foo/bar.txt", "out_dir"));
- // System paths go into the ABS_PATH obj directory.
- EXPECT_EQ("//out/Debug/obj/ABS_PATH/foo", Call("/foo/bar.txt", "out_dir"));
-#if defined(OS_WIN)
- EXPECT_EQ("//out/Debug/obj/ABS_PATH/C/foo",
- Call("/C:/foo/bar.txt", "out_dir"));
-#endif
-}
-
-// Note build dir is "//out/Debug/".
-TEST_F(GetPathInfoTest, GenDir) {
- EXPECT_EQ("//out/Debug/gen/src/foo/foo", Call("foo/bar.txt", "gen_dir"));
- EXPECT_EQ("//out/Debug/gen/src/foo/bar", Call("bar/", "gen_dir"));
- EXPECT_EQ("//out/Debug/gen/src/foo", Call(".", "gen_dir"));
- EXPECT_EQ("//out/Debug/gen/src/foo", Call("bar", "gen_dir"));
- EXPECT_EQ("//out/Debug/gen/foo", Call("//foo/bar.txt", "gen_dir"));
- // System paths go into the ABS_PATH gen directory
- EXPECT_EQ("//out/Debug/gen/ABS_PATH/foo", Call("/foo/bar.txt", "gen_dir"));
-#if defined(OS_WIN)
- EXPECT_EQ("//out/Debug/gen/ABS_PATH/C/foo",
- Call("/C:/foo/bar.txt", "gen_dir"));
-#endif
-}
diff --git a/gn/tools/gn/function_get_target_outputs.cc b/gn/tools/gn/function_get_target_outputs.cc
deleted file mode 100644
index 83e7356fb3d..00000000000
--- a/gn/tools/gn/function_get_target_outputs.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-// 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 "tools/gn/build_settings.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value.h"
-
-namespace functions {
-
-const char kGetTargetOutputs[] = "get_target_outputs";
-const char kGetTargetOutputs_HelpShort[] =
- "get_target_outputs: [file list] Get the list of outputs from a target.";
-const char kGetTargetOutputs_Help[] =
- R"(get_target_outputs: [file list] Get the list of outputs from a target.
-
- get_target_outputs(target_label)
-
- Returns a list of output files for the named target. The named target must
- have been previously defined in the current file before this function is
- called (it can't reference targets in other files because there isn't a
- defined execution order, and it obviously can't reference targets that are
- defined after the function call).
-
- Only copy, generated_file, and action targets are supported. The outputs from
- binary targets will depend on the toolchain definition which won't
- necessarily have been loaded by the time a given line of code has run, and
- source sets and groups have no useful output file.
-
-Return value
-
- The names in the resulting list will be absolute file paths (normally like
- "//out/Debug/bar.exe", depending on the build directory).
-
- action, copy, and generated_file targets: this will just return the files
- specified in the "outputs" variable of the target.
-
- action_foreach targets: this will return the result of applying the output
- template to the sources (see "gn help source_expansion"). This will be the
- same result (though with guaranteed absolute file paths), as
- process_file_template will return for those inputs (see "gn help
- process_file_template").
-
- source sets and groups: this will return a list containing the path of the
- "stamp" file that Ninja will produce once all outputs are generated. This
- probably isn't very useful.
-
-Example
-
- # Say this action generates a bunch of C source files.
- action_foreach("my_action") {
- sources = [ ... ]
- outputs = [ ... ]
- }
-
- # Compile the resulting source files into a source set.
- source_set("my_lib") {
- sources = get_target_outputs(":my_action")
- }
-)";
-
-Value RunGetTargetOutputs(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 1) {
- *err = Err(function, "Expected one argument.");
- return Value();
- }
-
- // Resolve the requested label.
- Label label = Label::Resolve(scope->GetSourceDir(),
- ToolchainLabelForScope(scope), args[0], err);
- if (label.is_null())
- return Value();
-
- // Find the referenced target. The targets previously encountered in this
- // scope will have been stashed in the item collector (they'll be dispatched
- // when this file is done running) so we can look through them.
- const Target* target = nullptr;
- Scope::ItemVector* collector = scope->GetItemCollector();
- if (!collector) {
- *err = Err(function, "No targets defined in this context.");
- return Value();
- }
- for (const auto& item : *collector) {
- if (item->label() != label)
- continue;
-
- const Target* as_target = item->AsTarget();
- if (!as_target) {
- *err = Err(function, "Label does not refer to a target.",
- label.GetUserVisibleName(false) + "\nrefers to a " +
- item->GetItemTypeName());
- return Value();
- }
- target = as_target;
- break;
- }
-
- if (!target) {
- *err = Err(function, "Target not found in this context.",
- label.GetUserVisibleName(false) +
- "\nwas not found. get_target_outputs() can only be used for "
- "targets\n"
- "previously defined in the current file.");
- return Value();
- }
-
- // Compute the output list.
- std::vector<SourceFile> files;
- if (target->output_type() == Target::ACTION ||
- target->output_type() == Target::COPY_FILES ||
- target->output_type() == Target::ACTION_FOREACH ||
- target->output_type() == Target::GENERATED_FILE) {
- target->action_values().GetOutputsAsSourceFiles(target, &files);
- } else {
- // Other types of targets are not supported.
- *err =
- Err(args[0],
- "Target is not an action, action_foreach, generated_file, or copy.",
- "Only these target types are supported by get_target_outputs.");
- return Value();
- }
-
- // Convert to Values.
- Value ret(function, Value::LIST);
- ret.list_value().reserve(files.size());
- for (const auto& file : files)
- ret.list_value().push_back(Value(function, file.value()));
-
- return ret;
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_get_target_outputs_unittest.cc b/gn/tools/gn/function_get_target_outputs_unittest.cc
deleted file mode 100644
index e94477bae67..00000000000
--- a/gn/tools/gn/function_get_target_outputs_unittest.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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 <memory>
-#include <utility>
-
-#include "tools/gn/functions.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-class GetTargetOutputsTest : public testing::Test {
- public:
- GetTargetOutputsTest() { setup_.scope()->set_item_collector(&items_); }
-
- Value GetTargetOutputs(const std::string& name, Err* err) {
- FunctionCallNode function;
- std::vector<Value> args;
- args.push_back(Value(nullptr, name));
- return functions::RunGetTargetOutputs(setup_.scope(), &function, args, err);
- }
-
- // Shortcut to get a label with the current toolchain.
- Label GetLabel(const std::string& dir, const std::string& name) {
- return Label(SourceDir(dir), name, setup_.toolchain()->label().dir(),
- setup_.toolchain()->label().name());
- }
-
- // Asserts that the given list contains a single string with the given value.
- void AssertSingleStringEquals(const Value& list,
- const std::string& expected) {
- ASSERT_TRUE(list.type() == Value::LIST);
- ASSERT_EQ(1u, list.list_value().size());
- ASSERT_TRUE(list.list_value()[0].type() == Value::STRING);
- ASSERT_EQ(expected, list.list_value()[0].string_value());
- }
-
- void AssertTwoStringsEqual(const Value& list,
- const std::string& expected1,
- const std::string& expected2) {
- ASSERT_TRUE(list.type() == Value::LIST);
- ASSERT_EQ(2u, list.list_value().size());
- ASSERT_TRUE(list.list_value()[0].type() == Value::STRING);
- ASSERT_EQ(expected1, list.list_value()[0].string_value());
- ASSERT_TRUE(list.list_value()[1].type() == Value::STRING);
- ASSERT_EQ(expected2, list.list_value()[1].string_value());
- }
-
- protected:
- TestWithScope setup_;
-
- Scope::ItemVector items_;
-};
-
-} // namespace
-
-TEST_F(GetTargetOutputsTest, Copy) {
- auto action =
- std::make_unique<Target>(setup_.settings(), GetLabel("//foo/", "bar"));
- action->set_output_type(Target::COPY_FILES);
- action->sources().push_back(SourceFile("//file.txt"));
- action->action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one");
-
- items_.push_back(std::move(action));
-
- Err err;
- Value result = GetTargetOutputs("//foo:bar", &err);
- ASSERT_FALSE(err.has_error());
- AssertSingleStringEquals(result, "//out/Debug/file.txt.one");
-}
-
-TEST_F(GetTargetOutputsTest, Action) {
- auto action =
- std::make_unique<Target>(setup_.settings(), GetLabel("//foo/", "bar"));
- action->set_output_type(Target::ACTION);
- action->action_values().outputs() =
- SubstitutionList::MakeForTest("//output1.txt", "//output2.txt");
-
- items_.push_back(std::move(action));
-
- Err err;
- Value result = GetTargetOutputs("//foo:bar", &err);
- ASSERT_FALSE(err.has_error());
- AssertTwoStringsEqual(result, "//output1.txt", "//output2.txt");
-}
-
-TEST_F(GetTargetOutputsTest, ActionForeach) {
- auto action =
- std::make_unique<Target>(setup_.settings(), GetLabel("//foo/", "bar"));
- action->set_output_type(Target::ACTION_FOREACH);
- action->sources().push_back(SourceFile("//file.txt"));
- action->action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one",
- "//out/Debug/{{source_file_part}}.two");
-
- items_.push_back(std::move(action));
-
- Err err;
- Value result = GetTargetOutputs("//foo:bar", &err);
- ASSERT_FALSE(err.has_error());
- AssertTwoStringsEqual(result, "//out/Debug/file.txt.one",
- "//out/Debug/file.txt.two");
-}
diff --git a/gn/tools/gn/function_process_file_template.cc b/gn/tools/gn/function_process_file_template.cc
deleted file mode 100644
index 7035503380d..00000000000
--- a/gn/tools/gn/function_process_file_template.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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 "base/stl_util.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value_extractors.h"
-
-namespace functions {
-
-const char kProcessFileTemplate[] = "process_file_template";
-const char kProcessFileTemplate_HelpShort[] =
- "process_file_template: Do template expansion over a list of files.";
-const char kProcessFileTemplate_Help[] =
- R"(process_file_template: Do template expansion over a list of files.
-
- process_file_template(source_list, template)
-
- process_file_template applies a template list to a source file list,
- returning the result of applying each template to each source. This is
- typically used for computing output file names from input files.
-
- In most cases, get_target_outputs() will give the same result with shorter,
- more maintainable code. This function should only be used when that function
- can't be used (like there's no target or the target is defined in another
- build file).
-
-Arguments
-
- The source_list is a list of file names.
-
- The template can be a string or a list. If it is a list, multiple output
- strings are generated for each input.
-
- The template should contain source expansions to which each name in the
- source list is applied. See "gn help source_expansion".
-
-Example
-
- sources = [
- "foo.idl",
- "bar.idl",
- ]
- myoutputs = process_file_template(
- sources,
- [ "$target_gen_dir/{{source_name_part}}.cc",
- "$target_gen_dir/{{source_name_part}}.h" ])
-
- The result in this case will be:
- [ "//out/Debug/foo.cc"
- "//out/Debug/foo.h"
- "//out/Debug/bar.cc"
- "//out/Debug/bar.h" ]
-)";
-
-Value RunProcessFileTemplate(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 2) {
- *err = Err(function->function(), "Expected two arguments");
- return Value();
- }
-
- // Source list.
- Target::FileList input_files;
- if (!ExtractListOfRelativeFiles(scope->settings()->build_settings(), args[0],
- scope->GetSourceDir(), &input_files, err))
- return Value();
-
- std::vector<std::string> result_files;
- SubstitutionList subst;
-
- // Template.
- const Value& template_arg = args[1];
- if (template_arg.type() == Value::STRING) {
- // Convert the string to a SubstitutionList with one pattern in it to
- // simplify the code below.
- std::vector<std::string> list;
- list.push_back(template_arg.string_value());
- if (!subst.Parse(list, template_arg.origin(), err))
- return Value();
- } else if (template_arg.type() == Value::LIST) {
- if (!subst.Parse(template_arg, err))
- return Value();
- } else {
- *err = Err(template_arg, "Not a string or a list.");
- return Value();
- }
-
- auto& types = subst.required_types();
- if (base::ContainsValue(types, &SubstitutionSourceTargetRelative)) {
- *err = Err(template_arg, "Not a valid substitution type for the function.");
- return Value();
- }
-
- SubstitutionWriter::ApplyListToSourcesAsString(
- nullptr, scope->settings(), subst, input_files, &result_files);
-
- // Convert the list of strings to the return Value.
- Value ret(function, Value::LIST);
- ret.list_value().reserve(result_files.size());
- for (const auto& file : result_files)
- ret.list_value().push_back(Value(function, file));
-
- return ret;
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_process_file_template_unittest.cc b/gn/tools/gn/function_process_file_template_unittest.cc
deleted file mode 100644
index 1f7bd76dd5c..00000000000
--- a/gn/tools/gn/function_process_file_template_unittest.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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 "tools/gn/functions.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(FunctionProcessFileTemplates, SingleString) {
- TestWithScope setup;
-
- std::vector<Value> args;
-
- Value sources(nullptr, Value::LIST);
- sources.list_value().push_back(Value(nullptr, "//src/foo.txt"));
- args.push_back(sources);
-
- Value expansion(nullptr, "1234{{source_name_part}}5678");
- args.push_back(expansion);
-
- Err err;
- Value result =
- functions::RunProcessFileTemplate(setup.scope(), nullptr, args, &err);
- EXPECT_FALSE(err.has_error());
-
- ASSERT_TRUE(result.type() == Value::LIST);
- ASSERT_EQ(1u, result.list_value().size());
- ASSERT_TRUE(result.list_value()[0].type() == Value::STRING);
- ASSERT_EQ("1234foo5678", result.list_value()[0].string_value());
-}
-
-TEST(FunctionProcessFileTemplates, MultipleStrings) {
- TestWithScope setup;
-
- std::vector<Value> args;
-
- Value sources(nullptr, Value::LIST);
- sources.list_value().push_back(Value(nullptr, "//src/one.txt"));
- sources.list_value().push_back(Value(nullptr, "//src/two.txt"));
- args.push_back(sources);
-
- Value expansions(nullptr, Value::LIST);
- expansions.list_value().push_back(
- Value(nullptr, "1234{{source_name_part}}5678"));
- expansions.list_value().push_back(
- Value(nullptr, "ABCD{{source_file_part}}EFGH"));
- args.push_back(expansions);
-
- Err err;
- Value result =
- functions::RunProcessFileTemplate(setup.scope(), nullptr, args, &err);
- EXPECT_FALSE(err.has_error());
-
- ASSERT_TRUE(result.type() == Value::LIST);
- ASSERT_EQ(4u, result.list_value().size());
- ASSERT_TRUE(result.list_value()[0].type() == Value::STRING);
- ASSERT_TRUE(result.list_value()[1].type() == Value::STRING);
- ASSERT_TRUE(result.list_value()[2].type() == Value::STRING);
- ASSERT_TRUE(result.list_value()[3].type() == Value::STRING);
- ASSERT_EQ("1234one5678", result.list_value()[0].string_value());
- ASSERT_EQ("ABCDone.txtEFGH", result.list_value()[1].string_value());
- ASSERT_EQ("1234two5678", result.list_value()[2].string_value());
- ASSERT_EQ("ABCDtwo.txtEFGH", result.list_value()[3].string_value());
-}
diff --git a/gn/tools/gn/function_read_file.cc b/gn/tools/gn/function_read_file.cc
deleted file mode 100644
index 8f34483492d..00000000000
--- a/gn/tools/gn/function_read_file.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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 "base/files/file_util.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/input_conversion.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/scheduler.h"
-
-// TODO(brettw) consider removing this. I originally wrote it for making the
-// WebKit bindings but misundersood what was required, and didn't need to
-// use this. This seems to have a high potential for misuse.
-
-namespace functions {
-
-const char kReadFile[] = "read_file";
-const char kReadFile_HelpShort[] = "read_file: Read a file into a variable.";
-const char kReadFile_Help[] =
- R"(read_file: Read a file into a variable.
-
- read_file(filename, input_conversion)
-
- Whitespace will be trimmed from the end of the file. Throws an error if the
- file can not be opened.
-
-Arguments
-
- filename
- Filename to read, relative to the build file.
-
- input_conversion
- Controls how the file is read and parsed. See "gn help io_conversion".
-
-Example
-
- lines = read_file("foo.txt", "list lines")
-)";
-
-Value RunReadFile(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 2) {
- *err = Err(function->function(), "Wrong number of arguments to read_file",
- "I expected two arguments.");
- return Value();
- }
- if (!args[0].VerifyTypeIs(Value::STRING, err))
- return Value();
-
- // Compute the file name.
- const SourceDir& cur_dir = scope->GetSourceDir();
- SourceFile source_file = cur_dir.ResolveRelativeFile(
- args[0], err, scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
- return Value();
- base::FilePath file_path =
- scope->settings()->build_settings()->GetFullPath(source_file);
-
- // Ensure that everything is recomputed if the read file changes.
- g_scheduler->AddGenDependency(file_path);
-
- // Read contents.
- std::string file_contents;
- if (!base::ReadFileToString(file_path, &file_contents)) {
- *err = Err(args[0], "Could not read file.",
- "I resolved this to \"" + FilePathToUTF8(file_path) + "\".");
- return Value();
- }
-
- return ConvertInputToValue(scope->settings(), file_contents, function,
- args[1], err);
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_rebase_path.cc b/gn/tools/gn/function_rebase_path.cc
deleted file mode 100644
index 945d6413681..00000000000
--- a/gn/tools/gn/function_rebase_path.cc
+++ /dev/null
@@ -1,296 +0,0 @@
-// 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 <stddef.h>
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/value.h"
-
-namespace functions {
-
-namespace {
-
-// We want the output to match the input in terms of ending in a slash or not.
-// Through all the transformations, these can get added or removed in various
-// cases.
-void MakeSlashEndingMatchInput(const std::string& input, std::string* output) {
- if (EndsWithSlash(input)) {
- if (!EndsWithSlash(*output)) // Preserve same slash type as input.
- output->push_back(input[input.size() - 1]);
- } else {
- if (EndsWithSlash(*output))
- output->resize(output->size() - 1);
- }
-}
-
-// Returns true if the given value looks like a directory, otherwise we'll
-// assume it's a file.
-bool ValueLooksLikeDir(const std::string& value) {
- if (value.empty())
- return true;
- size_t value_size = value.size();
-
- // Count the number of dots at the end of the string.
- size_t num_dots = 0;
- while (num_dots < value_size && value[value_size - num_dots - 1] == '.')
- num_dots++;
-
- if (num_dots == value.size())
- return true; // String is all dots.
-
- if (IsSlash(value[value_size - num_dots - 1]))
- return true; // String is a [back]slash followed by 0 or more dots.
-
- // Anything else.
- return false;
-}
-
-Value ConvertOnePath(const Scope* scope,
- const FunctionCallNode* function,
- const Value& value,
- const SourceDir& from_dir,
- const SourceDir& to_dir,
- bool convert_to_system_absolute,
- Err* err) {
- Value result; // Ensure return value optimization.
-
- if (!value.VerifyTypeIs(Value::STRING, err))
- return result;
- const std::string& string_value = value.string_value();
-
- bool looks_like_dir = ValueLooksLikeDir(string_value);
-
- // System-absolute output special case.
- if (convert_to_system_absolute) {
- base::FilePath system_path;
- if (looks_like_dir) {
- system_path = scope->settings()->build_settings()->GetFullPath(
- from_dir.ResolveRelativeDir(
- value, err,
- scope->settings()->build_settings()->root_path_utf8()));
- } else {
- system_path = scope->settings()->build_settings()->GetFullPath(
- from_dir.ResolveRelativeFile(
- value, err,
- scope->settings()->build_settings()->root_path_utf8()));
- }
- if (err->has_error())
- return Value();
-
- result = Value(function, FilePathToUTF8(system_path));
- if (looks_like_dir)
- MakeSlashEndingMatchInput(string_value, &result.string_value());
- return result;
- }
-
- result = Value(function, Value::STRING);
- if (looks_like_dir) {
- result.string_value() = RebasePath(
- from_dir
- .ResolveRelativeDir(
- value, err,
- scope->settings()->build_settings()->root_path_utf8())
- .value(),
- to_dir, scope->settings()->build_settings()->root_path_utf8());
- MakeSlashEndingMatchInput(string_value, &result.string_value());
- } else {
- SourceFile resolved_file = from_dir.ResolveRelativeFile(
- value, err, scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
- return Value();
- // Special case:
- // rebase_path("//foo", "//bar") ==> "../foo"
- // rebase_path("//foo", "//foo") ==> "." and not "../foo"
- if (resolved_file.value() ==
- to_dir.value().substr(0, to_dir.value().size() - 1)) {
- result.string_value() = ".";
- } else {
- result.string_value() =
- RebasePath(resolved_file.value(), to_dir,
- scope->settings()->build_settings()->root_path_utf8());
- }
- }
-
- return result;
-}
-
-} // namespace
-
-const char kRebasePath[] = "rebase_path";
-const char kRebasePath_HelpShort[] =
- "rebase_path: Rebase a file or directory to another location.";
-const char kRebasePath_Help[] =
- R"(rebase_path: Rebase a file or directory to another location.
-
- converted = rebase_path(input,
- new_base = "",
- current_base = ".")
-
- Takes a string argument representing a file name, or a list of such strings
- and converts it/them to be relative to a different base directory.
-
- When invoking the compiler or scripts, GN will automatically convert sources
- and include directories to be relative to the build directory. However, if
- you're passing files directly in the "args" array or doing other manual
- manipulations where GN doesn't know something is a file name, you will need
- to convert paths to be relative to what your tool is expecting.
-
- The common case is to use this to convert paths relative to the current
- directory to be relative to the build directory (which will be the current
- directory when executing scripts).
-
- If you want to convert a file path to be source-absolute (that is, beginning
- with a double slash like "//foo/bar"), you should use the get_path_info()
- function. This function won't work because it will always make relative
- paths, and it needs to support making paths relative to the source root, so
- can't also generate source-absolute paths without more special-cases.
-
-Arguments
-
- input
- A string or list of strings representing file or directory names These
- can be relative paths ("foo/bar.txt"), system absolute paths
- ("/foo/bar.txt"), or source absolute paths ("//foo/bar.txt").
-
- new_base
- The directory to convert the paths to be relative to. This can be an
- absolute path or a relative path (which will be treated as being relative
- to the current BUILD-file's directory).
-
- As a special case, if new_base is the empty string (the default), all
- paths will be converted to system-absolute native style paths with system
- path separators. This is useful for invoking external programs.
-
- current_base
- Directory representing the base for relative paths in the input. If this
- is not an absolute path, it will be treated as being relative to the
- current build file. Use "." (the default) to convert paths from the
- current BUILD-file's directory.
-
-Return value
-
- The return value will be the same type as the input value (either a string or
- a list of strings). All relative and source-absolute file names will be
- converted to be relative to the requested output System-absolute paths will
- be unchanged.
-
- Whether an output path will end in a slash will match whether the
- corresponding input path ends in a slash. It will return "." or "./"
- (depending on whether the input ends in a slash) to avoid returning empty
- strings. This means if you want a root path ("//" or "/") not ending in a
- slash, you can add a dot ("//.").
-
-Example
-
- # Convert a file in the current directory to be relative to the build
- # directory (the current dir when executing compilers and scripts).
- foo = rebase_path("myfile.txt", root_build_dir)
- # might produce "../../project/myfile.txt".
-
- # Convert a file to be system absolute:
- foo = rebase_path("myfile.txt")
- # Might produce "D:\\source\\project\\myfile.txt" on Windows or
- # "/home/you/source/project/myfile.txt" on Linux.
-
- # Typical usage for converting to the build directory for a script.
- action("myscript") {
- # Don't convert sources, GN will automatically convert these to be relative
- # to the build directory when it constructs the command line for your
- # script.
- sources = [ "foo.txt", "bar.txt" ]
-
- # Extra file args passed manually need to be explicitly converted
- # to be relative to the build directory:
- args = [
- "--data",
- rebase_path("//mything/data/input.dat", root_build_dir),
- "--rel",
- rebase_path("relative_path.txt", root_build_dir)
- ] + rebase_path(sources, root_build_dir)
- }
-)";
-
-Value RunRebasePath(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- Value result;
-
- // Argument indices.
- static const size_t kArgIndexInputs = 0;
- static const size_t kArgIndexDest = 1;
- static const size_t kArgIndexFrom = 2;
-
- // Inputs.
- if (args.size() < 1 || args.size() > 3) {
- *err = Err(function->function(), "Wrong # of arguments for rebase_path.");
- return result;
- }
- const Value& inputs = args[kArgIndexInputs];
-
- // To path.
- bool convert_to_system_absolute = true;
- SourceDir to_dir;
- const SourceDir& current_dir = scope->GetSourceDir();
- if (args.size() > kArgIndexDest) {
- if (!args[kArgIndexDest].VerifyTypeIs(Value::STRING, err))
- return result;
- if (!args[kArgIndexDest].string_value().empty()) {
- to_dir = current_dir.ResolveRelativeDir(
- args[kArgIndexDest], err,
- scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
- return Value();
- convert_to_system_absolute = false;
- }
- }
-
- // From path.
- SourceDir from_dir;
- if (args.size() > kArgIndexFrom) {
- if (!args[kArgIndexFrom].VerifyTypeIs(Value::STRING, err))
- return result;
- from_dir = current_dir.ResolveRelativeDir(
- args[kArgIndexFrom], err,
- scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
- return Value();
- } else {
- // Default to current directory if unspecified.
- from_dir = current_dir;
- }
-
- // Path conversion.
- if (inputs.type() == Value::STRING) {
- return ConvertOnePath(scope, function, inputs, from_dir, to_dir,
- convert_to_system_absolute, err);
-
- } else if (inputs.type() == Value::LIST) {
- result = Value(function, Value::LIST);
- result.list_value().reserve(inputs.list_value().size());
-
- for (const auto& input : inputs.list_value()) {
- result.list_value().push_back(
- ConvertOnePath(scope, function, input, from_dir, to_dir,
- convert_to_system_absolute, err));
- if (err->has_error()) {
- result = Value();
- return result;
- }
- }
- return result;
- }
-
- *err = Err(function->function(), "rebase_path requires a list or a string.");
- return result;
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_rebase_path_unittest.cc b/gn/tools/gn/function_rebase_path_unittest.cc
deleted file mode 100644
index fdd09dc9eec..00000000000
--- a/gn/tools/gn/function_rebase_path_unittest.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// 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 "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-namespace {
-
-std::string RebaseOne(Scope* scope,
- const char* input,
- const char* to_dir,
- const char* from_dir) {
- std::vector<Value> args;
- args.push_back(Value(nullptr, input));
- args.push_back(Value(nullptr, to_dir));
- args.push_back(Value(nullptr, from_dir));
-
- Err err;
- FunctionCallNode function;
- Value result = functions::RunRebasePath(scope, &function, args, &err);
- bool is_string = result.type() == Value::STRING;
- EXPECT_TRUE(is_string);
-
- return result.string_value();
-}
-
-} // namespace
-
-TEST(RebasePath, Strings) {
- TestWithScope setup;
- Scope* scope = setup.scope();
- scope->set_source_dir(SourceDir("//tools/gn/"));
-
- // Build-file relative paths.
- EXPECT_EQ("../../tools/gn", RebaseOne(scope, ".", "//out/Debug", "."));
- EXPECT_EQ("../../tools/gn/", RebaseOne(scope, "./", "//out/Debug", "."));
- EXPECT_EQ("../../tools/gn/foo", RebaseOne(scope, "foo", "//out/Debug", "."));
- EXPECT_EQ("../..", RebaseOne(scope, "../..", "//out/Debug", "."));
- EXPECT_EQ("../../", RebaseOne(scope, "../../", "//out/Debug", "."));
-
- // Without a source root defined, we cannot move out of the source tree.
- EXPECT_EQ("../..", RebaseOne(scope, "../../..", "//out/Debug", "."));
-
- // Source-absolute input paths.
- EXPECT_EQ("./", RebaseOne(scope, "//", "//", "//"));
- EXPECT_EQ("foo", RebaseOne(scope, "//foo", "//", "//"));
- EXPECT_EQ("foo/", RebaseOne(scope, "//foo/", "//", "//"));
- EXPECT_EQ("../../foo/bar", RebaseOne(scope, "//foo/bar", "//out/Debug", "."));
- EXPECT_EQ("./", RebaseOne(scope, "//foo/", "//foo/", "//"));
- EXPECT_EQ(".", RebaseOne(scope, "//foo", "//foo", "//"));
-
- // Test slash conversion.
- EXPECT_EQ("foo/bar", RebaseOne(scope, "foo/bar", ".", "."));
- EXPECT_EQ("foo/bar", RebaseOne(scope, "foo\\bar", ".", "."));
-
-// Test system path output.
-#if defined(OS_WIN)
- setup.build_settings()->SetRootPath(base::FilePath(L"C:/path/to/src"));
- EXPECT_EQ("C:/path/to/src", RebaseOne(scope, ".", "", "//"));
- EXPECT_EQ("C:/path/to/src/", RebaseOne(scope, "//", "", "//"));
- EXPECT_EQ("C:/path/to/src/foo", RebaseOne(scope, "foo", "", "//"));
- EXPECT_EQ("C:/path/to/src/foo/", RebaseOne(scope, "foo/", "", "//"));
- EXPECT_EQ("C:/path/to/src/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
- EXPECT_EQ("C:/path/to/other/tools",
- RebaseOne(scope, "//../other/tools", "", "//"));
- EXPECT_EQ("C:/path/to/src/foo/bar",
- RebaseOne(scope, "//../src/foo/bar", "", "//"));
- EXPECT_EQ("C:/path/to", RebaseOne(scope, "//..", "", "//"));
- EXPECT_EQ("C:/path", RebaseOne(scope, "../../../..", "", "."));
- EXPECT_EQ("C:/path/to/external/dir/",
- RebaseOne(scope, "//../external/dir/", "", "//"));
-
-#else
- setup.build_settings()->SetRootPath(base::FilePath("/path/to/src"));
- EXPECT_EQ("/path/to/src", RebaseOne(scope, ".", "", "//"));
- EXPECT_EQ("/path/to/src/", RebaseOne(scope, "//", "", "//"));
- EXPECT_EQ("/path/to/src/foo", RebaseOne(scope, "foo", "", "//"));
- EXPECT_EQ("/path/to/src/foo/", RebaseOne(scope, "foo/", "", "//"));
- EXPECT_EQ("/path/to/src/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
- EXPECT_EQ("/path/to/other/tools",
- RebaseOne(scope, "//../other/tools", "", "//"));
- EXPECT_EQ("/path/to/src/foo/bar",
- RebaseOne(scope, "//../src/foo/bar", "", "//"));
- EXPECT_EQ("/path/to", RebaseOne(scope, "//..", "", "//"));
- EXPECT_EQ("/path", RebaseOne(scope, "../../../..", "", "."));
- EXPECT_EQ("/path/to/external/dir/",
- RebaseOne(scope, "//../external/dir/", "", "//"));
-#endif
-}
-
-TEST(RebasePath, StringsSystemPaths) {
- TestWithScope setup;
- Scope* scope = setup.scope();
-
-#if defined(OS_WIN)
- setup.build_settings()->SetBuildDir(SourceDir("C:/ssd/out/Debug"));
- setup.build_settings()->SetRootPath(base::FilePath(L"C:/hdd/src"));
-
- // Test system absolute to-dir.
- EXPECT_EQ("../../ssd/out/Debug",
- RebaseOne(scope, ".", "//", "C:/ssd/out/Debug"));
- EXPECT_EQ("../../ssd/out/Debug/",
- RebaseOne(scope, "./", "//", "C:/ssd/out/Debug"));
- EXPECT_EQ("../../ssd/out/Debug/foo",
- RebaseOne(scope, "foo", "//", "C:/ssd/out/Debug"));
- EXPECT_EQ("../../ssd/out/Debug/foo/",
- RebaseOne(scope, "foo/", "//", "C:/ssd/out/Debug"));
-
- // Test system absolute from-dir.
- EXPECT_EQ("../../../hdd/src",
- RebaseOne(scope, ".", "C:/ssd/out/Debug", "//"));
- EXPECT_EQ("../../../hdd/src/",
- RebaseOne(scope, "./", "C:/ssd/out/Debug", "//"));
- EXPECT_EQ("../../../hdd/src/foo",
- RebaseOne(scope, "foo", "C:/ssd/out/Debug", "//"));
- EXPECT_EQ("../../../hdd/src/foo/",
- RebaseOne(scope, "foo/", "C:/ssd/out/Debug", "//"));
-#else
- setup.build_settings()->SetBuildDir(SourceDir("/ssd/out/Debug"));
- setup.build_settings()->SetRootPath(base::FilePath("/ssd/out/Debug"));
- EXPECT_EQ("../Debug-suffix/a", RebaseOne(scope, "/ssd/out/Debug-suffix/a",
- "/ssd/out/Debug", "/ssd/out/Debug"));
- setup.build_settings()->SetRootPath(base::FilePath("/hdd/src"));
-
- // Test system absolute to-dir.
- EXPECT_EQ("../../ssd/out/Debug",
- RebaseOne(scope, ".", "//", "/ssd/out/Debug"));
- EXPECT_EQ("../../ssd/out/Debug/",
- RebaseOne(scope, "./", "//", "/ssd/out/Debug"));
- EXPECT_EQ("../../ssd/out/Debug/foo",
- RebaseOne(scope, "foo", "//", "/ssd/out/Debug"));
- EXPECT_EQ("../../ssd/out/Debug/foo/",
- RebaseOne(scope, "foo/", "//", "/ssd/out/Debug"));
-
- // Test system absolute from-dir.
- EXPECT_EQ("../../../hdd/src", RebaseOne(scope, ".", "/ssd/out/Debug", "//"));
- EXPECT_EQ("../../../hdd/src/",
- RebaseOne(scope, "./", "/ssd/out/Debug", "//"));
- EXPECT_EQ("../../../hdd/src/foo",
- RebaseOne(scope, "foo", "/ssd/out/Debug", "//"));
- EXPECT_EQ("../../../hdd/src/foo/",
- RebaseOne(scope, "foo/", "/ssd/out/Debug", "//"));
-#endif
-}
-
-// Test list input.
-TEST(RebasePath, List) {
- TestWithScope setup;
- setup.scope()->set_source_dir(SourceDir("//tools/gn/"));
-
- std::vector<Value> args;
- args.push_back(Value(nullptr, Value::LIST));
- args[0].list_value().push_back(Value(nullptr, "foo.txt"));
- args[0].list_value().push_back(Value(nullptr, "bar.txt"));
- args.push_back(Value(nullptr, "//out/Debug/"));
- args.push_back(Value(nullptr, "."));
-
- Err err;
- FunctionCallNode function;
- Value ret = functions::RunRebasePath(setup.scope(), &function, args, &err);
- EXPECT_FALSE(err.has_error());
-
- ASSERT_EQ(Value::LIST, ret.type());
- ASSERT_EQ(2u, ret.list_value().size());
-
- EXPECT_EQ("../../tools/gn/foo.txt", ret.list_value()[0].string_value());
- EXPECT_EQ("../../tools/gn/bar.txt", ret.list_value()[1].string_value());
-}
-
-TEST(RebasePath, Errors) {
- TestWithScope setup;
-
- // No arg input should issue an error.
- Err err;
- std::vector<Value> args;
- FunctionCallNode function;
- Value ret = functions::RunRebasePath(setup.scope(), &function, args, &err);
- EXPECT_TRUE(err.has_error());
-}
diff --git a/gn/tools/gn/function_set_default_toolchain.cc b/gn/tools/gn/function_set_default_toolchain.cc
deleted file mode 100644
index e8b1885d27e..00000000000
--- a/gn/tools/gn/function_set_default_toolchain.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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 "tools/gn/build_settings.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-
-namespace functions {
-
-const char kSetDefaultToolchain[] = "set_default_toolchain";
-const char kSetDefaultToolchain_HelpShort[] =
- "set_default_toolchain: Sets the default toolchain name.";
-const char kSetDefaultToolchain_Help[] =
- R"(set_default_toolchain: Sets the default toolchain name.
-
- set_default_toolchain(toolchain_label)
-
- The given label should identify a toolchain definition (see "gn help
- toolchain"). This toolchain will be used for all targets unless otherwise
- specified.
-
- This function is only valid to call during the processing of the build
- configuration file. Since the build configuration file is processed
- separately for each toolchain, this function will be a no-op when called
- under any non-default toolchains.
-
- For example, the default toolchain should be appropriate for the current
- environment. If the current environment is 32-bit and somebody references a
- target with a 64-bit toolchain, we wouldn't want processing of the build
- config file for the 64-bit toolchain to reset the default toolchain to
- 64-bit, we want to keep it 32-bits.
-
-Argument
-
- toolchain_label
- Toolchain name.
-
-Example
-
- # Set default toolchain only has an effect when run in the context of the
- # default toolchain. Pick the right one according to the current CPU
- # architecture.
- if (target_cpu == "x64") {
- set_default_toolchain("//toolchains:64")
- } else if (target_cpu == "x86") {
- set_default_toolchain("//toolchains:32")
- }
-)";
-
-Value RunSetDefaultToolchain(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (!scope->IsProcessingBuildConfig()) {
- *err = Err(
- function->function(), "Must be called from build config.",
- "set_default_toolchain can only be called from the build configuration "
- "file.");
- return Value();
- }
-
- // When the loader is expecting the default toolchain to be set, it will set
- // this key on the scope to point to the destination.
- Label* default_toolchain_dest = static_cast<Label*>(
- scope->GetProperty(Loader::kDefaultToolchainKey, nullptr));
- if (!default_toolchain_dest)
- return Value();
-
- const SourceDir& current_dir = scope->GetSourceDir();
- const Label& default_toolchain = ToolchainLabelForScope(scope);
-
- if (!EnsureSingleStringArg(function, args, err))
- return Value();
- Label toolchain_label(
- Label::Resolve(current_dir, default_toolchain, args[0], err));
- if (toolchain_label.is_null())
- return Value();
-
- *default_toolchain_dest = toolchain_label;
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_set_defaults.cc b/gn/tools/gn/function_set_defaults.cc
deleted file mode 100644
index 8fbc49040a3..00000000000
--- a/gn/tools/gn/function_set_defaults.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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 "tools/gn/err.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-
-namespace functions {
-
-const char kSetDefaults[] = "set_defaults";
-const char kSetDefaults_HelpShort[] =
- "set_defaults: Set default values for a target type.";
-const char kSetDefaults_Help[] =
- R"(set_defaults: Set default values for a target type.
-
- set_defaults(<target_type_name>) { <values...> }
-
- Sets the default values for a given target type. Whenever target_type_name is
- seen in the future, the values specified in set_default's block will be
- copied into the current scope.
-
- When the target type is used, the variable copying is very strict. If a
- variable with that name is already in scope, the build will fail with an
- error.
-
- set_defaults can be used for built-in target types ("executable",
- "shared_library", etc.) and custom ones defined via the "template" command.
- It can be called more than once and the most recent call in any scope will
- apply, but there is no way to refer to the previous defaults and modify them
- (each call to set_defaults must supply a complete list of all defaults it
- wants). If you want to share defaults, store them in a separate variable.
-
-Example
-
- set_defaults("static_library") {
- configs = [ "//tools/mything:settings" ]
- }
-
- static_library("mylib") {
- # The configs will be auto-populated as above. You can remove it if
- # you don't want the default for a particular default:
- configs -= [ "//tools/mything:settings" ]
- }
-)";
-
-Value RunSetDefaults(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- if (!EnsureSingleStringArg(function, args, err))
- return Value();
- const std::string& target_type(args[0].string_value());
-
- if (!block) {
- FillNeedsBlockError(function, err);
- return Value();
- }
-
- // Run the block for the rule invocation.
- Scope block_scope(scope);
- block->Execute(&block_scope, err);
- if (err->has_error())
- return Value();
-
- // Now copy the values set on the scope we made into the free-floating one
- // (with no containing scope) used to hold the target defaults.
- Scope* dest = scope->MakeTargetDefaults(target_type);
- block_scope.NonRecursiveMergeTo(dest, Scope::MergeOptions(), function,
- "<SHOULD NOT FAIL>", err);
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_template.cc b/gn/tools/gn/function_template.cc
deleted file mode 100644
index aa23dc6d63a..00000000000
--- a/gn/tools/gn/function_template.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// 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 "tools/gn/functions.h"
-
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/template.h"
-#include "tools/gn/value.h"
-
-namespace functions {
-
-const char kTemplate[] = "template";
-const char kTemplate_HelpShort[] = "template: Define a template rule.";
-const char kTemplate_Help[] =
- R"(template: Define a template rule.
-
- A template defines a custom name that acts like a function. It provides a way
- to add to the built-in target types.
-
- The template() function is used to declare a template. To invoke the
- template, just use the name of the template like any other target type.
-
- Often you will want to declare your template in a special file that other
- files will import (see "gn help import") so your template rule can be shared
- across build files.
-
-Variables and templates:
-
- When you call template() it creates a closure around all variables currently
- in scope with the code in the template block. When the template is invoked,
- the closure will be executed.
-
- When the template is invoked, the code in the caller is executed and passed
- to the template code as an implicit "invoker" variable. The template uses
- this to read state out of the invoking code.
-
- One thing explicitly excluded from the closure is the "current directory"
- against which relative file names are resolved. The current directory will be
- that of the invoking code, since typically that code specifies the file
- names. This means all files internal to the template should use absolute
- names.
-
- A template will typically forward some or all variables from the invoking
- scope to a target that it defines. Often, such variables might be optional.
- Use the pattern:
-
- if (defined(invoker.deps)) {
- deps = invoker.deps
- }
-
- The function forward_variables_from() provides a shortcut to forward one or
- more or possibly all variables in this manner:
-
- forward_variables_from(invoker, ["deps", "public_deps"])
-
-Target naming
-
- Your template should almost always define a built-in target with the name the
- template invoker specified. For example, if you have an IDL template and
- somebody does:
- idl("foo") {...
- you will normally want this to expand to something defining a source_set or
- static_library named "foo" (among other things you may need). This way, when
- another target specifies a dependency on "foo", the static_library or
- source_set will be linked.
-
- It is also important that any other targets your template expands to have
- unique names, or you will get collisions.
-
- Access the invoking name in your template via the implicit "target_name"
- variable. This should also be the basis for how other targets that a template
- expands to ensure uniqueness.
-
- A typical example would be a template that defines an action to generate some
- source files, and a source_set to compile that source. Your template would
- name the source_set "target_name" because that's what you want external
- targets to depend on to link your code. And you would name the action
- something like "${target_name}_action" to make it unique. The source set
- would have a dependency on the action to make it run.
-
-Overriding builtin targets
-
- You can use template to redefine a built-in target in which case your template
- takes a precedence over the built-in one. All uses of the target from within
- the template definition will refer to the built-in target which makes it
- possible to extend the behavior of the built-in target:
-
- template("shared_library") {
- shared_library(shlib) {
- forward_variables_from(invoker, "*")
- ...
- }
- }
-
-Example of defining a template
-
- template("my_idl") {
- # Be nice and help callers debug problems by checking that the variables
- # the template requires are defined. This gives a nice message rather than
- # giving the user an error about an undefined variable in the file defining
- # the template
- #
- # You can also use defined() to give default values to variables
- # unspecified by the invoker.
- assert(defined(invoker.sources),
- "Need sources in $target_name listing the idl files.")
-
- # Name of the intermediate target that does the code gen. This must
- # incorporate the target name so it's unique across template
- # instantiations.
- code_gen_target_name = target_name + "_code_gen"
-
- # Intermediate target to convert IDL to C source. Note that the name is
- # based on the name the invoker of the template specified. This way, each
- # time the template is invoked we get a unique intermediate action name
- # (since all target names are in the global scope).
- action_foreach(code_gen_target_name) {
- # Access the scope defined by the invoker via the implicit "invoker"
- # variable.
- sources = invoker.sources
-
- # Note that we need an absolute path for our script file name. The
- # current directory when executing this code will be that of the invoker
- # (this is why we can use the "sources" directly above without having to
- # rebase all of the paths). But if we need to reference a script relative
- # to the template file, we'll need to use an absolute path instead.
- script = "//tools/idl/idl_code_generator.py"
-
- # Tell GN how to expand output names given the sources.
- # See "gn help source_expansion" for more.
- outputs = [ "$target_gen_dir/{{source_name_part}}.cc",
- "$target_gen_dir/{{source_name_part}}.h" ]
- }
-
- # Name the source set the same as the template invocation so instancing
- # this template produces something that other targets can link to in their
- # deps.
- source_set(target_name) {
- # Generates the list of sources, we get these from the action_foreach
- # above.
- sources = get_target_outputs(":$code_gen_target_name")
-
- # This target depends on the files produced by the above code gen target.
- deps = [ ":$code_gen_target_name" ]
- }
- }
-
-Example of invoking the resulting template
-
- # This calls the template code above, defining target_name to be
- # "foo_idl_files" and "invoker" to be the set of stuff defined in the curly
- # brackets.
- my_idl("foo_idl_files") {
- # Goes into the template as "invoker.sources".
- sources = [ "foo.idl", "bar.idl" ]
- }
-
- # Here is a target that depends on our template.
- executable("my_exe") {
- # Depend on the name we gave the template call above. Internally, this will
- # produce a dependency from executable to the source_set inside the
- # template (since it has this name), which will in turn depend on the code
- # gen action.
- deps = [ ":foo_idl_files" ]
- }
-)";
-
-Value RunTemplate(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- // Of course you can have configs and targets in a template. But here, we're
- // not actually executing the block, only declaring it. Marking the template
- // declaration as non-nestable means that you can't put it inside a target,
- // for example.
- NonNestableBlock non_nestable(scope, function, "template");
- if (!non_nestable.Enter(err))
- return Value();
-
- // TODO(brettw) determine if the function is built-in and throw an error if
- // it is.
- if (args.size() != 1) {
- *err =
- Err(function->function(), "Need exactly one string arg to template.");
- return Value();
- }
- if (!args[0].VerifyTypeIs(Value::STRING, err))
- return Value();
- std::string template_name = args[0].string_value();
-
- const Template* existing_template = scope->GetTemplate(template_name);
- if (existing_template) {
- *err = Err(function, "Duplicate template definition.",
- "A template with this name was already defined.");
- err->AppendSubErr(
- Err(existing_template->GetDefinitionRange(), "Previous definition."));
- return Value();
- }
-
- scope->AddTemplate(template_name, new Template(scope, function));
-
- // The template object above created a closure around the variables in the
- // current scope. The template code will execute in that context when it's
- // invoked. But this means that any variables defined above that are used
- // by the template won't get marked used just by defining the template. The
- // result can be spurious unused variable errors.
- //
- // The "right" thing to do would be to walk the syntax tree inside the
- // template, find all identifier references, and mark those variables used.
- // This is annoying and error-prone to implement and takes extra time to run
- // for this narrow use case.
- //
- // Templates are most often defined in .gni files which don't get
- // used-variable checking anyway, and this case is annoying enough that the
- // incremental value of unused variable checking isn't worth the
- // alternatives. So all values in scope before this template definition are
- // exempted from unused variable checking.
- scope->MarkAllUsed();
-
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_template_unittest.cc b/gn/tools/gn/function_template_unittest.cc
deleted file mode 100644
index 0525974c373..00000000000
--- a/gn/tools/gn/function_template_unittest.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-// Checks that variables used inside template definitions aren't reported
-// unused if they were declared above the template.
-TEST(FunctionTemplate, MarkUsed) {
- TestWithScope setup;
- TestParseInput input(
- "a = 1\n" // Unused outside of template.
- "template(\"templ\") {\n"
- " print(a)\n"
- "}\n");
- ASSERT_FALSE(input.has_error()) << input.parse_err().message();
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // Normally the loader calls CheckForUnusedVars() when it loads a file
- // since normal blocks don't do this check. To avoid having to make this
- // test much more complicated, just explicitly do the check to make sure
- // things are marked properly.
- setup.scope()->CheckForUnusedVars(&err);
- EXPECT_FALSE(err.has_error());
-}
diff --git a/gn/tools/gn/function_toolchain.cc b/gn/tools/gn/function_toolchain.cc
deleted file mode 100644
index cb686121a6f..00000000000
--- a/gn/tools/gn/function_toolchain.cc
+++ /dev/null
@@ -1,831 +0,0 @@
-// 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 <algorithm>
-#include <limits>
-#include <memory>
-#include <utility>
-
-#include "tools/gn/c_tool.h"
-#include "tools/gn/err.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/tool.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-namespace functions {
-
-namespace {
-
-// This is just a unique value to take the address of to use as the key for
-// the toolchain property on a scope.
-const int kToolchainPropertyKey = 0;
-
-} // namespace
-
-// toolchain -------------------------------------------------------------------
-
-const char kToolchain[] = "toolchain";
-const char kToolchain_HelpShort[] = "toolchain: Defines a toolchain.";
-const char kToolchain_Help[] =
- R"*(toolchain: Defines a toolchain.
-
- A toolchain is a set of commands and build flags used to compile the source
- code. The toolchain() function defines these commands.
-
-Toolchain overview
-
- You can have more than one toolchain in use at once in a build and a target
- can exist simultaneously in multiple toolchains. A build file is executed
- once for each toolchain it is referenced in so the GN code can vary all
- parameters of each target (or which targets exist) on a per-toolchain basis.
-
- When you have a simple build with only one toolchain, the build config file
- is loaded only once at the beginning of the build. It must call
- set_default_toolchain() (see "gn help set_default_toolchain") to tell GN the
- label of the toolchain definition to use. The "toolchain_args" section of the
- toolchain definition is ignored.
-
- When a target has a dependency on a target using different toolchain (see "gn
- help labels" for how to specify this), GN will start a build using that
- secondary toolchain to resolve the target. GN will load the build config file
- with the build arguments overridden as specified in the toolchain_args.
- Because the default toolchain is already known, calls to
- set_default_toolchain() are ignored.
-
- To load a file in an alternate toolchain, GN does the following:
-
- 1. Loads the file with the toolchain definition in it (as determined by the
- toolchain label).
- 2. Re-runs the master build configuration file, applying the arguments
- specified by the toolchain_args section of the toolchain definition.
- 3. Loads the destination build file in the context of the configuration file
- in the previous step.
-
- The toolchain configuration is two-way. In the default toolchain (i.e. the
- main build target) the configuration flows from the build config file to the
- toolchain. The build config file looks at the state of the build (OS type,
- CPU architecture, etc.) and decides which toolchain to use (via
- set_default_toolchain()). In secondary toolchains, the configuration flows
- from the toolchain to the build config file: the "toolchain_args" in the
- toolchain definition specifies the arguments to re-invoke the build.
-
-Functions and variables
-
- tool()
- The tool() function call specifies the commands to run for a given step. See
- "gn help tool".
-
- toolchain_args [scope]
- Overrides for build arguments to pass to the toolchain when invoking it.
- This is a variable of type "scope" where the variable names correspond to
- variables in declare_args() blocks.
-
- When you specify a target using an alternate toolchain, the master build
- configuration file is re-interpreted in the context of that toolchain.
- toolchain_args allows you to control the arguments passed into this
- alternate invocation of the build.
-
- Any default system arguments or arguments passed in via "gn args" will also
- be passed to the alternate invocation unless explicitly overridden by
- toolchain_args.
-
- The toolchain_args will be ignored when the toolchain being defined is the
- default. In this case, it's expected you want the default argument values.
-
- See also "gn help buildargs" for an overview of these arguments.
-
- propagates_configs [boolean, default=false]
- Determines whether public_configs and all_dependent_configs in this
- toolchain propagate to targets in other toolchains.
-
- When false (the default), this toolchain will not propagate any configs to
- targets in other toolchains that depend on it targets inside this
- toolchain. This matches the most common usage of toolchains where they
- represent different architectures or compilers and the settings that apply
- to one won't necessarily apply to others.
-
- When true, configs (public and all-dependent) will cross the boundary out
- of this toolchain as if the toolchain boundary wasn't there. This only
- affects one direction of dependencies: a toolchain can't control whether
- it accepts such configs, only whether it pushes them. The build is
- responsible for ensuring that any external targets depending on targets in
- this toolchain are compatible with the compiler flags, etc. that may be
- propagated.
-
- deps [string list]
- Dependencies of this toolchain. These dependencies will be resolved before
- any target in the toolchain is compiled. To avoid circular dependencies
- these must be targets defined in another toolchain.
-
- This is expressed as a list of targets, and generally these targets will
- always specify a toolchain:
- deps = [ "//foo/bar:baz(//build/toolchain:bootstrap)" ]
-
- This concept is somewhat inefficient to express in Ninja (it requires a lot
- of duplicate of rules) so should only be used when absolutely necessary.
-
-Example of defining a toolchain
-
- toolchain("32") {
- tool("cc") {
- command = "gcc {{source}}"
- ...
- }
-
- toolchain_args = {
- use_doom_melon = true # Doom melon always required for 32-bit builds.
- current_cpu = "x86"
- }
- }
-
- toolchain("64") {
- tool("cc") {
- command = "gcc {{source}}"
- ...
- }
-
- toolchain_args = {
- # use_doom_melon is not overridden here, it will take the default.
- current_cpu = "x64"
- }
- }
-
-Example of cross-toolchain dependencies
-
- If a 64-bit target wants to depend on a 32-bit binary, it would specify a
- dependency using data_deps (data deps are like deps that are only needed at
- runtime and aren't linked, since you can't link a 32-bit and a 64-bit
- library).
-
- executable("my_program") {
- ...
- if (target_cpu == "x64") {
- # The 64-bit build needs this 32-bit helper.
- data_deps = [ ":helper(//toolchains:32)" ]
- }
- }
-
- if (target_cpu == "x86") {
- # Our helper library is only compiled in 32-bits.
- shared_library("helper") {
- ...
- }
- }
-)*";
-
-Value RunToolchain(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- NonNestableBlock non_nestable(scope, function, "toolchain");
- if (!non_nestable.Enter(err))
- return Value();
-
- if (!EnsureNotProcessingImport(function, scope, err) ||
- !EnsureNotProcessingBuildConfig(function, scope, err))
- return Value();
-
- // Note that we don't want to use MakeLabelForScope since that will include
- // the toolchain name in the label, and toolchain labels don't themselves
- // have toolchain names.
- const SourceDir& input_dir = scope->GetSourceDir();
- Label label(input_dir, args[0].string_value());
- if (g_scheduler->verbose_logging())
- g_scheduler->Log("Defining toolchain", label.GetUserVisibleName(false));
-
- // This object will actually be copied into the one owned by the toolchain
- // manager, but that has to be done in the lock.
- std::unique_ptr<Toolchain> toolchain = std::make_unique<Toolchain>(
- scope->settings(), label, scope->build_dependency_files());
- toolchain->set_defined_from(function);
- toolchain->visibility().SetPublic();
-
- Scope block_scope(scope);
- block_scope.SetProperty(&kToolchainPropertyKey, toolchain.get());
- block->Execute(&block_scope, err);
- block_scope.SetProperty(&kToolchainPropertyKey, nullptr);
- if (err->has_error())
- return Value();
-
- // Read deps (if any).
- const Value* deps_value = block_scope.GetValue(variables::kDeps, true);
- if (deps_value) {
- ExtractListOfLabels(*deps_value, block_scope.GetSourceDir(),
- ToolchainLabelForScope(&block_scope),
- &toolchain->deps(), err);
- if (err->has_error())
- return Value();
- }
-
- // Read toolchain args (if any).
- const Value* toolchain_args = block_scope.GetValue("toolchain_args", true);
- if (toolchain_args) {
- if (!toolchain_args->VerifyTypeIs(Value::SCOPE, err))
- return Value();
-
- Scope::KeyValueMap values;
- toolchain_args->scope_value()->GetCurrentScopeValues(&values);
- toolchain->args() = values;
- }
-
- // Read propagates_configs (if present).
- const Value* propagates_configs =
- block_scope.GetValue("propagates_configs", true);
- if (propagates_configs) {
- if (!propagates_configs->VerifyTypeIs(Value::BOOLEAN, err))
- return Value();
- toolchain->set_propagates_configs(propagates_configs->boolean_value());
- }
-
- if (!block_scope.CheckForUnusedVars(err))
- return Value();
-
- // Save this toolchain.
- toolchain->ToolchainSetupComplete();
- Scope::ItemVector* collector = scope->GetItemCollector();
- if (!collector) {
- *err = Err(function, "Can't define a toolchain in this context.");
- return Value();
- }
- collector->push_back(std::move(toolchain));
- return Value();
-}
-
-// tool ------------------------------------------------------------------------
-
-const char kTool[] = "tool";
-const char kTool_HelpShort[] = "tool: Specify arguments to a toolchain tool.";
-const char kTool_Help[] =
- R"(tool: Specify arguments to a toolchain tool.
-
-Usage
-
- tool(<tool type>) {
- <tool variables...>
- }
-
-Tool types
-
- Compiler tools:
- "cc": C compiler
- "cxx": C++ compiler
- "objc": Objective C compiler
- "objcxx": Objective C++ compiler
- "rc": Resource compiler (Windows .rc files)
- "asm": Assembler
-
- Linker tools:
- "alink": Linker for static libraries (archives)
- "solink": Linker for shared libraries
- "link": Linker for executables
-
- Other tools:
- "stamp": Tool for creating stamp files
- "copy": Tool to copy files.
- "action": Defaults for actions
-
- Platform specific tools:
- "copy_bundle_data": [iOS, macOS] Tool to copy files in a bundle.
- "compile_xcassets": [iOS, macOS] Tool to compile asset catalogs.
-
- Rust tools:
- "rustc": Rust compiler and linker
-
-Tool variables
-
- command [string with substitutions]
- Valid for: all tools except "action" (required)
-
- The command to run.
-
- command_launcher [string]
- Valid for: all tools except "action" (optional)
-
- The prefix with which to launch the command (e.g. the path to a Goma or
- CCache compiler launcher).
-
- Note that this prefix will not be included in the compilation database or
- IDE files generated from the build.
-
- default_output_dir [string with substitutions]
- Valid for: linker tools
-
- Default directory name for the output file relative to the
- root_build_dir. It can contain other substitution patterns. This will
- be the default value for the {{output_dir}} expansion (discussed below)
- but will be overridden by the "output_dir" variable in a target, if one
- is specified.
-
- GN doesn't do anything with this string other than pass it along,
- potentially with target-specific overrides. It is the tool's job to use
- the expansion so that the files will be in the right place.
-
- default_output_extension [string]
- Valid for: linker tools
-
- Extension for the main output of a linkable tool. It includes the
- leading dot. This will be the default value for the
- {{output_extension}} expansion (discussed below) but will be overridden
- by by the "output extension" variable in a target, if one is specified.
- Empty string means no extension.
-
- GN doesn't actually do anything with this extension other than pass it
- along, potentially with target-specific overrides. One would typically
- use the {{output_extension}} value in the "outputs" to read this value.
-
- Example: default_output_extension = ".exe"
-
- depfile [string with substitutions]
- Valid for: compiler tools (optional)
-
- If the tool can write ".d" files, this specifies the name of the
- resulting file. These files are used to list header file dependencies
- (or other implicit input dependencies) that are discovered at build
- time. See also "depsformat".
-
- Example: depfile = "{{output}}.d"
-
- depsformat [string]
- Valid for: compiler tools (when depfile is specified)
-
- Format for the deps outputs. This is either "gcc" or "msvc". See the
- ninja documentation for "deps" for more information.
-
- Example: depsformat = "gcc"
-
- description [string with substitutions, optional]
- Valid for: all tools
-
- What to print when the command is run.
-
- Example: description = "Compiling {{source}}"
-
- exe_output_extension [string, optional, rust tools only]
- rlib_output_extension [string, optional, rust tools only]
- dylib_output_extension [string, optional, rust tools only]
- cdylib_output_extension [string, optional, rust tools only]
- proc_macro_output_extension [string, optional, rust tools only]
- Valid for: Rust tools
-
- These specify the default tool output for each of the crate types.
- The default is empty for executables, shared, and static libraries and
- ".rlib" for rlibs. Note that the Rust compiler complains with an error
- if external crates do not take the form `lib<name>.rlib` or
- `lib<name>.<shared_extension>`, where `<shared_extension>` is `.so`,
- `.dylib`, or `.dll` as appropriate for the platform.
-
- lib_switch [string, optional, link tools only]
- lib_dir_switch [string, optional, link tools only]
- Valid for: Linker tools except "alink"
-
- These strings will be prepended to the libraries and library search
- directories, respectively, because linkers differ on how specify them.
- If you specified:
- lib_switch = "-l"
- lib_dir_switch = "-L"
- then the "{{libs}}" expansion for [ "freetype", "expat"] would be
- "-lfreetype -lexpat".
-
- outputs [list of strings with substitutions]
- Valid for: Linker and compiler tools (required)
-
- An array of names for the output files the tool produces. These are
- relative to the build output directory. There must always be at least
- one output file. There can be more than one output (a linker might
- produce a library and an import library, for example).
-
- This array just declares to GN what files the tool will produce. It is
- your responsibility to specify the tool command that actually produces
- these files.
-
- If you specify more than one output for shared library links, you
- should consider setting link_output, depend_output, and
- runtime_outputs.
-
- Example for a compiler tool that produces .obj files:
- outputs = [
- "{{source_out_dir}}/{{source_name_part}}.obj"
- ]
-
- Example for a linker tool that produces a .dll and a .lib. The use of
- {{target_output_name}}, {{output_extension}} and {{output_dir}} allows
- the target to override these values.
- outputs = [
- "{{output_dir}}/{{target_output_name}}"
- "{{output_extension}}",
- "{{output_dir}}/{{target_output_name}}.lib",
- ]
-
- pool [label, optional]
- Valid for: all tools (optional)
-
- Label of the pool to use for the tool. Pools are used to limit the
- number of tasks that can execute concurrently during the build.
-
- See also "gn help pool".
-
- link_output [string with substitutions]
- depend_output [string with substitutions]
- Valid for: "solink" only (optional)
-
- These two files specify which of the outputs from the solink tool
- should be used for linking and dependency tracking. These should match
- entries in the "outputs". If unspecified, the first item in the
- "outputs" array will be used for all. See "Separate linking and
- dependencies for shared libraries" below for more.
-
- On Windows, where the tools produce a .dll shared library and a .lib
- import library, you will want the first two to be the import library
- and the third one to be the .dll file. On Linux, if you're not doing
- the separate linking/dependency optimization, all of these should be
- the .so output.
-
- output_prefix [string]
- Valid for: Linker tools (optional)
-
- Prefix to use for the output name. Defaults to empty. This prefix will
- be prepended to the name of the target (or the output_name if one is
- manually specified for it) if the prefix is not already there. The
- result will show up in the {{output_name}} substitution pattern.
-
- Individual targets can opt-out of the output prefix by setting:
- output_prefix_override = true
- (see "gn help output_prefix_override").
-
- This is typically used to prepend "lib" to libraries on
- Posix systems:
- output_prefix = "lib"
-
- precompiled_header_type [string]
- Valid for: "cc", "cxx", "objc", "objcxx"
-
- Type of precompiled headers. If undefined or the empty string,
- precompiled headers will not be used for this tool. Otherwise use "gcc"
- or "msvc".
-
- For precompiled headers to be used for a given target, the target (or a
- config applied to it) must also specify a "precompiled_header" and, for
- "msvc"-style headers, a "precompiled_source" value. If the type is
- "gcc", then both "precompiled_header" and "precompiled_source" must
- resolve to the same file, despite the different formats required for
- each."
-
- See "gn help precompiled_header" for more.
-
- restat [boolean]
- Valid for: all tools (optional, defaults to false)
-
- Requests that Ninja check the file timestamp after this tool has run to
- determine if anything changed. Set this if your tool has the ability to
- skip writing output if the output file has not changed.
-
- Normally, Ninja will assume that when a tool runs the output be new and
- downstream dependents must be rebuild. When this is set to trye, Ninja
- can skip rebuilding downstream dependents for input changes that don't
- actually affect the output.
-
- Example:
- restat = true
-
- rspfile [string with substitutions]
- Valid for: all tools except "action" (optional)
-
- Name of the response file. If empty, no response file will be
- used. See "rspfile_content".
-
- rspfile_content [string with substitutions]
- Valid for: all tools except "action" (required when "rspfile" is used)
-
- The contents to be written to the response file. This may include all
- or part of the command to send to the tool which allows you to get
- around OS command-line length limits.
-
- This example adds the inputs and libraries to a response file, but
- passes the linker flags directly on the command line:
- tool("link") {
- command = "link -o {{output}} {{ldflags}} @{{output}}.rsp"
- rspfile = "{{output}}.rsp"
- rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
- }
-
- runtime_outputs [string list with substitutions]
- Valid for: linker tools
-
- If specified, this list is the subset of the outputs that should be
- added to runtime deps (see "gn help runtime_deps"). By default (if
- runtime_outputs is empty or unspecified), it will be the link_output.
-
-)" // String break to prevent overflowing the 16K max VC string length.
- R"(Expansions for tool variables
-
- All paths are relative to the root build directory, which is the current
- directory for running all tools. These expansions are available to all tools:
-
- {{label}}
- The label of the current target. This is typically used in the
- "description" field for link tools. The toolchain will be omitted from
- the label for targets in the default toolchain, and will be included
- for targets in other toolchains.
-
- {{label_name}}
- The short name of the label of the target. This is the part after the
- colon. For "//foo/bar:baz" this will be "baz". Unlike
- {{target_output_name}}, this is not affected by the "output_prefix" in
- the tool or the "output_name" set on the target.
-
- {{output}}
- The relative path and name of the output(s) of the current build step.
- If there is more than one output, this will expand to a list of all of
- them. Example: "out/base/my_file.o"
-
- {{target_gen_dir}}
- {{target_out_dir}}
- The directory of the generated file and output directories,
- respectively, for the current target. There is no trailing slash. See
- also {{output_dir}} for linker tools. Example: "out/base/test"
-
- {{target_output_name}}
- The short name of the current target with no path information, or the
- value of the "output_name" variable if one is specified in the target.
- This will include the "output_prefix" if any. See also {{label_name}}.
-
- Example: "libfoo" for the target named "foo" and an output prefix for
- the linker tool of "lib".
-
- Compiler tools have the notion of a single input and a single output, along
- with a set of compiler-specific flags. The following expansions are
- available:
-
- {{asmflags}}
- {{cflags}}
- {{cflags_c}}
- {{cflags_cc}}
- {{cflags_objc}}
- {{cflags_objcc}}
- {{defines}}
- {{include_dirs}}
- Strings correspond that to the processed flags/defines/include
- directories specified for the target.
- Example: "--enable-foo --enable-bar"
-
- Defines will be prefixed by "-D" and include directories will be
- prefixed by "-I" (these work with Posix tools as well as Microsoft
- ones).
-
- {{source}}
- The relative path and name of the current input file.
- Example: "../../base/my_file.cc"
-
- {{source_file_part}}
- The file part of the source including the extension (with no directory
- information).
- Example: "foo.cc"
-
- {{source_name_part}}
- The filename part of the source file with no directory or extension.
- Example: "foo"
-
- {{source_gen_dir}}
- {{source_out_dir}}
- The directory in the generated file and output directories,
- respectively, for the current input file. If the source file is in the
- same directory as the target is declared in, they will will be the same
- as the "target" versions above. Example: "gen/base/test"
-
- Linker tools have multiple inputs and (potentially) multiple outputs. The
- static library tool ("alink") is not considered a linker tool. The following
- expansions are available:
-
- {{inputs}}
- {{inputs_newline}}
- Expands to the inputs to the link step. This will be a list of object
- files and static libraries.
- Example: "obj/foo.o obj/bar.o obj/somelibrary.a"
-
- The "_newline" version will separate the input files with newlines
- instead of spaces. This is useful in response files: some linkers can
- take a "-filelist" flag which expects newline separated files, and some
- Microsoft tools have a fixed-sized buffer for parsing each line of a
- response file.
-
- {{ldflags}}
- Expands to the processed set of ldflags and library search paths
- specified for the target.
- Example: "-m64 -fPIC -pthread -L/usr/local/mylib"
-
- {{libs}}
- Expands to the list of system libraries to link to. Each will be
- prefixed by the "lib_switch".
-
- As a special case to support Mac, libraries with names ending in
- ".framework" will be added to the {{libs}} with "-framework" preceding
- it, and the lib prefix will be ignored.
-
- Example: "-lfoo -lbar"
-
- {{output_dir}}
- The value of the "output_dir" variable in the target, or the the value
- of the "default_output_dir" value in the tool if the target does not
- override the output directory. This will be relative to the
- root_build_dir and will not end in a slash. Will be "." for output to
- the root_build_dir.
-
- This is subtly different than {{target_out_dir}} which is defined by GN
- based on the target's path and not overridable. {{output_dir}} is for
- the final output, {{target_out_dir}} is generally for object files and
- other outputs.
-
- Usually {{output_dir}} would be defined in terms of either
- {{target_out_dir}} or {{root_out_dir}}
-
- {{output_extension}}
- The value of the "output_extension" variable in the target, or the
- value of the "default_output_extension" value in the tool if the target
- does not specify an output extension.
- Example: ".so"
-
- {{solibs}}
- Extra libraries from shared library dependencies not specified in the
- {{inputs}}. This is the list of link_output files from shared libraries
- (if the solink tool specifies a "link_output" variable separate from
- the "depend_output").
-
- These should generally be treated the same as libs by your tool.
-
- Example: "libfoo.so libbar.so"
-
-)" // String break to prevent overflowing the 16K max VC string length.
- R"( The static library ("alink") tool allows {{arflags}} plus the common tool
- substitutions.
-
- The copy tool allows the common compiler/linker substitutions, plus
- {{source}} which is the source of the copy. The stamp tool allows only the
- common tool substitutions.
-
- The copy_bundle_data and compile_xcassets tools only allows the common tool
- substitutions. Both tools are required to create iOS/macOS bundles and need
- only be defined on those platforms.
-
- The copy_bundle_data tool will be called with one source and needs to copy
- (optionally optimizing the data representation) to its output. It may be
- called with a directory as input and it needs to be recursively copied.
-
- The compile_xcassets tool will be called with one or more source (each an
- asset catalog) that needs to be compiled to a single output. The following
- substitutions are available:
-
- {{inputs}}
- Expands to the list of .xcassets to use as input to compile the asset
- catalog.
-
- {{bundle_product_type}}
- Expands to the product_type of the bundle that will contain the
- compiled asset catalog. Usually corresponds to the product_type
- property of the corresponding create_bundle target.
-
- {{bundle_partial_info_plist}}
- Expands to the path to the partial Info.plist generated by the
- assets catalog compiler. Usually based on the target_name of
- the create_bundle target.
-
- Rust tools have the notion of a single input and a single output, along
- with a set of compiler-specific flags. The following expansions are
- available:
-
- {{crate_name}}
- Expands to the string representing the crate name of target under
- compilation.
-
- {{crate_type}}
- Expands to the string representing the type of crate for the target
- under compilation.
-
- {{externs}}
- Expands to the list of --extern flags needed to include addition Rust
- libraries in this target. Includes any specified renamed dependencies.
-
- {{rustc_output_extension}}
- Expands to the output extension for this target's crate type.
-
- {{rustc_output_prefix}}
- Expands to the prefix for shared and static libraries. This should
- generally be "lib". Empty for executable targets.
-
- {{rustdeps}}
- Expands to the list of -Ldependency=<path> strings needed to compile
- this target.
-
- {{rustenv}}
- Expands to the list of environment variables.
-
- {{rustflags}}
- Expands to the list of strings representing Rust compiler flags.
-
-Separate linking and dependencies for shared libraries
-
- Shared libraries are special in that not all changes to them require that
- dependent targets be re-linked. If the shared library is changed but no
- imports or exports are different, dependent code needn't be relinked, which
- can speed up the build.
-
- If your link step can output a list of exports from a shared library and
- writes the file only if the new one is different, the timestamp of this file
- can be used for triggering re-links, while the actual shared library would be
- used for linking.
-
- You will need to specify
- restat = true
- in the linker tool to make this work, so Ninja will detect if the timestamp
- of the dependency file has changed after linking (otherwise it will always
- assume that running a command updates the output):
-
- tool("solink") {
- command = "..."
- outputs = [
- "{{output_dir}}/{{target_output_name}}{{output_extension}}",
- "{{output_dir}}/{{target_output_name}}"
- "{{output_extension}}.TOC",
- ]
- link_output =
- "{{output_dir}}/{{target_output_name}}{{output_extension}}"
- depend_output =
- "{{output_dir}}/{{target_output_name}}"
- "{{output_extension}}.TOC"
- restat = true
- }
-
-Example
-
- toolchain("my_toolchain") {
- # Put these at the top to apply to all tools below.
- lib_switch = "-l"
- lib_dir_switch = "-L"
-
- tool("cc") {
- command = "gcc {{source}} -o {{output}}"
- outputs = [ "{{source_out_dir}}/{{source_name_part}}.o" ]
- description = "GCC {{source}}"
- }
- tool("cxx") {
- command = "g++ {{source}} -o {{output}}"
- outputs = [ "{{source_out_dir}}/{{source_name_part}}.o" ]
- description = "G++ {{source}}"
- }
- };
-)";
-
-Value RunTool(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- // Find the toolchain definition we're executing inside of. The toolchain
- // function will set a property pointing to it that we'll pick up.
- Toolchain* toolchain = reinterpret_cast<Toolchain*>(
- scope->GetProperty(&kToolchainPropertyKey, nullptr));
- if (!toolchain) {
- *err = Err(function->function(), "tool() called outside of toolchain().",
- "The tool() function can only be used inside a toolchain() "
- "definition.");
- return Value();
- }
-
- if (!EnsureSingleStringArg(function, args, err))
- return Value();
- const std::string& tool_name = args[0].string_value();
-
- // Run the tool block.
- Scope block_scope(scope);
- block->Execute(&block_scope, err);
- if (err->has_error())
- return Value();
-
- std::unique_ptr<Tool> tool =
- Tool::CreateTool(function, tool_name, &block_scope, toolchain, err);
-
- if (!tool) {
- return Value();
- }
-
- tool->set_defined_from(function);
- toolchain->SetTool(std::move(tool));
-
- // Make sure there weren't any vars set in this tool that were unused.
- if (!block_scope.CheckForUnusedVars(err))
- return Value();
-
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_toolchain_unittest.cc b/gn/tools/gn/function_toolchain_unittest.cc
deleted file mode 100644
index baf2e26ce13..00000000000
--- a/gn/tools/gn/function_toolchain_unittest.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/functions.h"
-#include "tools/gn/rust_tool.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using FunctionToolchain = TestWithScheduler;
-
-TEST_F(FunctionToolchain, RuntimeOutputs) {
- TestWithScope setup;
-
- // These runtime outputs are a subset of the outputs so are OK.
- {
- TestParseInput input(
- R"(toolchain("good") {
- tool("link") {
- outputs = [ "foo" ]
- runtime_outputs = [ "foo" ]
- }
- })");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // It should have generated a toolchain.
- ASSERT_EQ(1u, setup.items().size());
- const Toolchain* toolchain = setup.items()[0]->AsToolchain();
- ASSERT_TRUE(toolchain);
-
- // The toolchain should have a link tool with the two outputs.
- const Tool* link = toolchain->GetTool(CTool::kCToolLink);
- ASSERT_TRUE(link);
- ASSERT_EQ(1u, link->outputs().list().size());
- EXPECT_EQ("foo", link->outputs().list()[0].AsString());
- ASSERT_EQ(1u, link->runtime_outputs().list().size());
- EXPECT_EQ("foo", link->runtime_outputs().list()[0].AsString());
- }
-
- // This one is not a subset so should throw an error.
- {
- TestParseInput input(
- R"(toolchain("bad") {
- tool("link") {
- outputs = [ "foo" ]
- runtime_outputs = [ "bar" ]
- }
- })");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()) << err.message();
- }
-}
-
-TEST_F(FunctionToolchain, Rust) {
- TestWithScope setup;
-
- // These runtime outputs are a subset of the outputs so are OK.
- {
- TestParseInput input(
- R"(toolchain("rust") {
- tool("rustc") {
- command = "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin {{rustflags}} -o {{output}} {{externs}} {{source}}"
- description = "RUST {{output}}"
- }
- })");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // It should have generated a toolchain.
- ASSERT_EQ(1u, setup.items().size());
- const Toolchain* toolchain = setup.items()[0]->AsToolchain();
- ASSERT_TRUE(toolchain);
-
- const Tool* rust = toolchain->GetTool(RustTool::kRsToolRustc);
- ASSERT_TRUE(rust);
- ASSERT_EQ(rust->command().AsString(),
- "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin "
- "{{rustflags}} -o {{output}} {{externs}} {{source}}");
- ASSERT_EQ(rust->description().AsString(), "RUST {{output}}");
- }
-}
-
-TEST_F(FunctionToolchain, CommandLauncher) {
- TestWithScope setup;
-
- TestParseInput input(
- R"(toolchain("good") {
- tool("cxx") {
- command_launcher = "/usr/goma/gomacc"
- }
- })");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // It should have generated a toolchain.
- ASSERT_EQ(1u, setup.items().size());
- const Toolchain* toolchain = setup.items()[0]->AsToolchain();
- ASSERT_TRUE(toolchain);
-
- // The toolchain should have a link tool with the two outputs.
- const Tool* link = toolchain->GetTool(CTool::kCToolCxx);
- ASSERT_TRUE(link);
- EXPECT_EQ("/usr/goma/gomacc", link->command_launcher());
-} \ No newline at end of file
diff --git a/gn/tools/gn/function_write_file.cc b/gn/tools/gn/function_write_file.cc
deleted file mode 100644
index d56724d440f..00000000000
--- a/gn/tools/gn/function_write_file.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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 <iostream>
-#include <sstream>
-
-#include "base/files/file_util.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/output_conversion.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "util/build_config.h"
-
-namespace functions {
-
-const char kWriteFile[] = "write_file";
-const char kWriteFile_HelpShort[] = "write_file: Write a file to disk.";
-const char kWriteFile_Help[] =
- R"(write_file: Write a file to disk.
-
- write_file(filename, data, output_conversion = "")
-
- If data is a list, the list will be written one-item-per-line with no quoting
- or brackets.
-
- If the file exists and the contents are identical to that being written, the
- file will not be updated. This will prevent unnecessary rebuilds of targets
- that depend on this file.
-
- One use for write_file is to write a list of inputs to an script that might
- be too long for the command line. However, it is preferable to use response
- files for this purpose. See "gn help response_file_contents".
-
-Arguments
-
- filename
- Filename to write. This must be within the output directory.
-
- data
- The list or string to write.
-
- output_conversion
- Controls how the output is written. See "gn help io_conversion".
-)";
-
-Value RunWriteFile(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 3 && args.size() != 2) {
- *err = Err(function->function(), "Wrong number of arguments to write_file",
- "I expected two or three arguments.");
- return Value();
- }
-
- // Compute the file name and make sure it's in the output dir.
- const SourceDir& cur_dir = scope->GetSourceDir();
- SourceFile source_file = cur_dir.ResolveRelativeFile(
- args[0], err, scope->settings()->build_settings()->root_path_utf8());
- if (err->has_error())
- return Value();
- if (!EnsureStringIsInOutputDir(
- scope->settings()->build_settings()->build_dir(), source_file.value(),
- args[0].origin(), err))
- return Value();
- g_scheduler->AddWrittenFile(source_file); // Track that we wrote this file.
-
- // Track how to recreate this file, since we write it a gen time.
- // Note this is a hack since the correct output is not a dependency proper,
- // but an addition of this file to the output of the gn rule that writes it.
- // This dependency will, however, cause the gen step to be re-run and the
- // build restarted if the file is missing.
- g_scheduler->AddGenDependency(
- scope->settings()->build_settings()->GetFullPath(source_file));
-
- // Extract conversion value.
- Value output_conversion;
- if (args.size() != 3)
- output_conversion = Value();
- else
- output_conversion = args[2];
-
- // Compute output.
- std::ostringstream contents;
- ConvertValueToOutput(scope->settings(), args[1], output_conversion, contents,
- err);
- if (err->has_error())
- return Value();
-
- base::FilePath file_path =
- scope->settings()->build_settings()->GetFullPath(source_file);
-
- // Make sure we're not replacing the same contents.
- if (!WriteFileIfChanged(file_path, contents.str(), err))
- *err = Err(function->function(), err->message(), err->help_text());
-
- return Value();
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/function_write_file_unittest.cc b/gn/tools/gn/function_write_file_unittest.cc
deleted file mode 100644
index 4e317e167e0..00000000000
--- a/gn/tools/gn/function_write_file_unittest.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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 <stdint.h>
-
-#include "base/files/file.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-#if defined(OS_LINUX) || defined(OS_MACOSX)
-#include <sys/time.h>
-#endif
-
-namespace {
-
-// Returns true on success, false if write_file signaled an error.
-bool CallWriteFile(Scope* scope,
- const std::string& filename,
- const Value& data) {
- Err err;
-
- std::vector<Value> args;
- args.push_back(Value(nullptr, filename));
- args.push_back(data);
-
- FunctionCallNode function_call;
- Value result = functions::RunWriteFile(scope, &function_call, args, &err);
- EXPECT_EQ(Value::NONE, result.type()); // Should always return none.
-
- return !err.has_error();
-}
-
-} // namespace
-
-using WriteFileTest = TestWithScheduler;
-
-TEST_F(WriteFileTest, WithData) {
- TestWithScope setup;
-
- // Make a real directory for writing the files.
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- setup.build_settings()->SetRootPath(temp_dir.GetPath());
- setup.build_settings()->SetBuildDir(SourceDir("//out/"));
-
- Value some_string(nullptr, "some string contents");
-
- // Should refuse to write files outside of the output dir.
- EXPECT_FALSE(CallWriteFile(setup.scope(), "//in_root.txt", some_string));
- EXPECT_FALSE(
- CallWriteFile(setup.scope(), "//other_dir/foo.txt", some_string));
-
- // Should be able to write to a new dir inside the out dir.
- EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_string));
- base::FilePath foo_name = temp_dir.GetPath()
- .Append(FILE_PATH_LITERAL("out"))
- .Append(FILE_PATH_LITERAL("foo.txt"));
- std::string result_contents;
- EXPECT_TRUE(base::ReadFileToString(foo_name, &result_contents));
- EXPECT_EQ(some_string.string_value(), result_contents);
-
- // Update the contents with a list of a string and a number.
- Value some_list(nullptr, Value::LIST);
- some_list.list_value().push_back(Value(nullptr, "line 1"));
- some_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(2)));
- EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_list));
- EXPECT_TRUE(base::ReadFileToString(foo_name, &result_contents));
- EXPECT_EQ("line 1\n2\n", result_contents);
-
- // Test that the file is not rewritten if the contents are not changed.
- base::File foo_file(foo_name, base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_WRITE);
- ASSERT_TRUE(foo_file.IsValid());
-
- // Start by setting the modified time to something old to avoid clock
- // resolution issues.
-#if defined(OS_WIN)
- FILETIME last_access_filetime = {};
- FILETIME last_modified_filetime = {};
- ASSERT_TRUE(::SetFileTime(foo_file.GetPlatformFile(), nullptr,
- &last_access_filetime, &last_modified_filetime));
-#elif defined(OS_AIX)
- struct timeval times[2] = {};
- ASSERT_EQ(utimes(foo_name.value().c_str(), times), 0);
-#else
- struct timeval times[2] = {};
- ASSERT_EQ(futimes(foo_file.GetPlatformFile(), times), 0);
-#endif
-
- // Read the current time to avoid timer resolution issues when comparing
- // below.
- base::File::Info original_info;
- foo_file.GetInfo(&original_info);
-
- EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_list));
-
- // Verify that the last modified time is the same as before.
- base::File::Info new_info;
- foo_file.GetInfo(&new_info);
- EXPECT_EQ(original_info.last_modified, new_info.last_modified);
-}
diff --git a/gn/tools/gn/functions.cc b/gn/tools/gn/functions.cc
deleted file mode 100644
index 05033c82008..00000000000
--- a/gn/tools/gn/functions.cc
+++ /dev/null
@@ -1,1391 +0,0 @@
-// 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 "tools/gn/functions.h"
-
-#include <stddef.h>
-#include <iostream>
-#include <memory>
-#include <regex>
-#include <utility>
-
-#include "base/environment.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/config.h"
-#include "tools/gn/config_values_generator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_node_value_adapter.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/template.h"
-#include "tools/gn/token.h"
-#include "tools/gn/value.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-namespace {
-
-// Some functions take a {} following them, and some don't. For the ones that
-// don't, this is used to verify that the given block node is null and will
-// set the error accordingly if it's not. Returns true if the block is null.
-bool VerifyNoBlockForFunctionCall(const FunctionCallNode* function,
- const BlockNode* block,
- Err* err) {
- if (!block)
- return true;
-
- *err =
- Err(block, "Unexpected '{'.",
- "This function call doesn't take a {} block following it, and you\n"
- "can't have a {} block that's not connected to something like an if\n"
- "statement or a target declaration.");
- err->AppendRange(function->function().range());
- return false;
-}
-
-// This key is set as a scope property on the scope of a declare_args() block,
-// in order to prevent reading a variable defined earlier in the same call
-// (see `gn help declare_args` for more).
-const void* kInDeclareArgsKey = nullptr;
-
-} // namespace
-
-bool EnsureNotReadingFromSameDeclareArgs(const ParseNode* node,
- const Scope* cur_scope,
- const Scope* val_scope,
- Err* err) {
- // If the value didn't come from a scope at all, we're safe.
- if (!val_scope)
- return true;
-
- const Scope* val_args_scope = nullptr;
- val_scope->GetProperty(&kInDeclareArgsKey, &val_args_scope);
-
- const Scope* cur_args_scope = nullptr;
- cur_scope->GetProperty(&kInDeclareArgsKey, &cur_args_scope);
- if (!val_args_scope || !cur_args_scope || (val_args_scope != cur_args_scope))
- return true;
-
- *err =
- Err(node,
- "Reading a variable defined in the same declare_args() call.\n"
- "\n"
- "If you need to set the value of one arg based on another, put\n"
- "them in two separate declare_args() calls, one after the other.\n");
- return false;
-}
-
-bool EnsureNotProcessingImport(const ParseNode* node,
- const Scope* scope,
- Err* err) {
- if (scope->IsProcessingImport()) {
- *err =
- Err(node, "Not valid from an import.",
- "Imports are for defining defaults, variables, and rules. The\n"
- "appropriate place for this kind of thing is really in a normal\n"
- "BUILD file.");
- return false;
- }
- return true;
-}
-
-bool EnsureNotProcessingBuildConfig(const ParseNode* node,
- const Scope* scope,
- Err* err) {
- if (scope->IsProcessingBuildConfig()) {
- *err = Err(node, "Not valid from the build config.",
- "You can't do this kind of thing from the build config script, "
- "silly!\nPut it in a regular BUILD file.");
- return false;
- }
- return true;
-}
-
-bool FillTargetBlockScope(const Scope* scope,
- const FunctionCallNode* function,
- const std::string& target_type,
- const BlockNode* block,
- const std::vector<Value>& args,
- Scope* block_scope,
- Err* err) {
- if (!block) {
- FillNeedsBlockError(function, err);
- return false;
- }
-
- // Copy the target defaults, if any, into the scope we're going to execute
- // the block in.
- const Scope* default_scope = scope->GetTargetDefaults(target_type);
- if (default_scope) {
- Scope::MergeOptions merge_options;
- merge_options.skip_private_vars = true;
- if (!default_scope->NonRecursiveMergeTo(block_scope, merge_options,
- function, "target defaults", err))
- return false;
- }
-
- // The name is the single argument to the target function.
- if (!EnsureSingleStringArg(function, args, err))
- return false;
-
- // Set the target name variable to the current target, and mark it used
- // because we don't want to issue an error if the script ignores it.
- const base::StringPiece target_name(variables::kTargetName);
- block_scope->SetValue(target_name, Value(function, args[0].string_value()),
- function);
- block_scope->MarkUsed(target_name);
- return true;
-}
-
-void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
- *err = Err(function->function(), "This function call requires a block.",
- "The block's \"{\" must be on the same line as the function "
- "call's \")\".");
-}
-
-bool EnsureSingleStringArg(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 1) {
- *err = Err(function->function(), "Incorrect arguments.",
- "This function requires a single string argument.");
- return false;
- }
- return args[0].VerifyTypeIs(Value::STRING, err);
-}
-
-const Label& ToolchainLabelForScope(const Scope* scope) {
- return scope->settings()->toolchain_label();
-}
-
-Label MakeLabelForScope(const Scope* scope,
- const FunctionCallNode* function,
- const std::string& name) {
- const Label& toolchain_label = ToolchainLabelForScope(scope);
- return Label(scope->GetSourceDir(), name, toolchain_label.dir(),
- toolchain_label.name());
-}
-
-// static
-const int NonNestableBlock::kKey = 0;
-
-NonNestableBlock::NonNestableBlock(Scope* scope,
- const FunctionCallNode* function,
- const char* type_description)
- : scope_(scope),
- function_(function),
- type_description_(type_description),
- key_added_(false) {}
-
-NonNestableBlock::~NonNestableBlock() {
- if (key_added_)
- scope_->SetProperty(&kKey, nullptr);
-}
-
-bool NonNestableBlock::Enter(Err* err) {
- void* scope_value = scope_->GetProperty(&kKey, nullptr);
- if (scope_value) {
- // Existing block.
- const NonNestableBlock* existing =
- reinterpret_cast<const NonNestableBlock*>(scope_value);
- *err = Err(function_, "Can't nest these things.",
- std::string("You are trying to nest a ") + type_description_ +
- " inside a " + existing->type_description_ + ".");
- err->AppendSubErr(Err(existing->function_, "The enclosing block."));
- return false;
- }
-
- scope_->SetProperty(&kKey, this);
- key_added_ = true;
- return true;
-}
-
-namespace functions {
-
-// assert ----------------------------------------------------------------------
-
-const char kAssert[] = "assert";
-const char kAssert_HelpShort[] =
- "assert: Assert an expression is true at generation time.";
-const char kAssert_Help[] =
- R"(assert: Assert an expression is true at generation time.
-
- assert(<condition> [, <error string>])
-
- If the condition is false, the build will fail with an error. If the
- optional second argument is provided, that string will be printed
- with the error message.
-
-Examples
-
- assert(is_win)
- assert(defined(sources), "Sources must be defined");
-)";
-
-Value RunAssert(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 1 && args.size() != 2) {
- *err = Err(function->function(), "Wrong number of arguments.",
- "assert() takes one or two argument, "
- "were you expecting somethig else?");
- } else if (args[0].type() != Value::BOOLEAN) {
- *err = Err(function->function(), "Assertion value not a bool.");
- } else if (!args[0].boolean_value()) {
- if (args.size() == 2) {
- // Optional string message.
- if (args[1].type() != Value::STRING) {
- *err = Err(function->function(), "Assertion failed.",
- "<<<ERROR MESSAGE IS NOT A STRING>>>");
- } else {
- *err = Err(function->function(), "Assertion failed.",
- args[1].string_value());
- }
- } else {
- *err = Err(function->function(), "Assertion failed.");
- }
-
- if (args[0].origin()) {
- // If you do "assert(foo)" we'd ideally like to show you where foo was
- // set, and in this case the origin of the args will tell us that.
- // However, if you do "assert(foo && bar)" the source of the value will
- // be the assert like, which isn't so helpful.
- //
- // So we try to see if the args are from the same line or not. This will
- // break if you do "assert(\nfoo && bar)" and we may show the second line
- // as the source, oh well. The way around this is to check to see if the
- // origin node is inside our function call block.
- Location origin_location = args[0].origin()->GetRange().begin();
- if (origin_location.file() != function->function().location().file() ||
- origin_location.line_number() !=
- function->function().location().line_number()) {
- err->AppendSubErr(
- Err(args[0].origin()->GetRange(), "", "This is where it was set."));
- }
- }
- }
- return Value();
-}
-
-// config ----------------------------------------------------------------------
-
-const char kConfig[] = "config";
-const char kConfig_HelpShort[] = "config: Defines a configuration object.";
-const char kConfig_Help[] =
- R"(config: Defines a configuration object.
-
- Configuration objects can be applied to targets and specify sets of compiler
- flags, includes, defines, etc. They provide a way to conveniently group sets
- of this configuration information.
-
- A config is referenced by its label just like a target.
-
- The values in a config are additive only. If you want to remove a flag you
- need to remove the corresponding config that sets it. The final set of flags,
- defines, etc. for a target is generated in this order:
-
- 1. The values specified directly on the target (rather than using a config.
- 2. The configs specified in the target's "configs" list, in order.
- 3. Public_configs from a breadth-first traversal of the dependency tree in
- the order that the targets appear in "deps".
- 4. All dependent configs from a breadth-first traversal of the dependency
- tree in the order that the targets appear in "deps".
-
-More background
-
- Configs solve a problem where the build system needs to have a higher-level
- understanding of various compiler settings. For example, some compiler flags
- have to appear in a certain order relative to each other, some settings like
- defines and flags logically go together, and the build system needs to
- de-duplicate flags even though raw command-line parameters can't always be
- operated on in that way.
-
- The config gives a name to a group of settings that can then be reasoned
- about by GN. GN can know that configs with the same label are the same thing
- so can be de-duplicated. It allows related settings to be grouped so they
- are added or removed as a unit. And it allows targets to refer to settings
- with conceptual names ("no_rtti", "enable_exceptions", etc.) rather than
- having to hard-coding every compiler's flags each time they are referred to.
-
-Variables valid in a config definition
-
-)"
-
- CONFIG_VALUES_VARS_HELP
-
- R"( Nested configs: configs
-
-Variables on a target used to apply configs
-
- all_dependent_configs, configs, public_configs
-
-Example
-
- config("myconfig") {
- include_dirs = [ "include/common" ]
- defines = [ "ENABLE_DOOM_MELON" ]
- }
-
- executable("mything") {
- configs = [ ":myconfig" ]
- }
-)";
-
-Value RunConfig(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* scope,
- Err* err) {
- NonNestableBlock non_nestable(scope, function, "config");
- if (!non_nestable.Enter(err))
- return Value();
-
- if (!EnsureSingleStringArg(function, args, err) ||
- !EnsureNotProcessingImport(function, scope, err))
- return Value();
-
- Label label(MakeLabelForScope(scope, function, args[0].string_value()));
-
- if (g_scheduler->verbose_logging())
- g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
-
- // Create the new config.
- std::unique_ptr<Config> config = std::make_unique<Config>(
- scope->settings(), label, scope->build_dependency_files());
- config->set_defined_from(function);
- if (!Visibility::FillItemVisibility(config.get(), scope, err))
- return Value();
-
- // Fill the flags and such.
- const SourceDir& input_dir = scope->GetSourceDir();
- ConfigValuesGenerator gen(&config->own_values(), scope, input_dir, err);
- gen.Run();
- if (err->has_error())
- return Value();
-
- // Read sub-configs.
- const Value* configs_value = scope->GetValue(variables::kConfigs, true);
- if (configs_value) {
- ExtractListOfUniqueLabels(*configs_value, scope->GetSourceDir(),
- ToolchainLabelForScope(scope), &config->configs(),
- err);
- }
- if (err->has_error())
- return Value();
-
- // Save the generated item.
- Scope::ItemVector* collector = scope->GetItemCollector();
- if (!collector) {
- *err = Err(function, "Can't define a config in this context.");
- return Value();
- }
- collector->push_back(std::move(config));
-
- return Value();
-}
-
-// declare_args ----------------------------------------------------------------
-
-const char kDeclareArgs[] = "declare_args";
-const char kDeclareArgs_HelpShort[] = "declare_args: Declare build arguments.";
-const char kDeclareArgs_Help[] =
- R"(declare_args: Declare build arguments.
-
- Introduces the given arguments into the current scope. If they are not
- specified on the command line or in a toolchain's arguments, the default
- values given in the declare_args block will be used. However, these defaults
- will not override command-line values.
-
- See also "gn help buildargs" for an overview.
-
- The precise behavior of declare args is:
-
- 1. The declare_args() block executes. Any variable defined in the enclosing
- scope is available for reading, but any variable defined earlier in
- the current scope is not (since the overrides haven't been applied yet).
-
- 2. At the end of executing the block, any variables set within that scope
- are saved globally as build arguments, with their current values being
- saved as the "default value" for that argument.
-
- 3. User-defined overrides are applied. Anything set in "gn args" now
- overrides any default values. The resulting set of variables is promoted
- to be readable from the following code in the file.
-
- This has some ramifications that may not be obvious:
-
- - You should not perform difficult work inside a declare_args block since
- this only sets a default value that may be discarded. In particular,
- don't use the result of exec_script() to set the default value. If you
- want to have a script-defined default, set some default "undefined" value
- like [], "", or -1, and after the declare_args block, call exec_script if
- the value is unset by the user.
-
- - Because you cannot read the value of a variable defined in the same
- block, if you need to make the default value of one arg depend
- on the possibly-overridden value of another, write two separate
- declare_args() blocks:
-
- declare_args() {
- enable_foo = true
- }
- declare_args() {
- # Bar defaults to same user-overridden state as foo.
- enable_bar = enable_foo
- }
-
-Example
-
- declare_args() {
- enable_teleporter = true
- enable_doom_melon = false
- }
-
- If you want to override the (default disabled) Doom Melon:
- gn --args="enable_doom_melon=true enable_teleporter=true"
- This also sets the teleporter, but it's already defaulted to on so it will
- have no effect.
-)";
-
-Value RunDeclareArgs(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- NonNestableBlock non_nestable(scope, function, "declare_args");
- if (!non_nestable.Enter(err))
- return Value();
-
- Scope block_scope(scope);
- block_scope.SetProperty(&kInDeclareArgsKey, &block_scope);
- block->Execute(&block_scope, err);
- if (err->has_error())
- return Value();
-
- // Pass the values from our scope into the Args object for adding to the
- // scope with the proper values (taking into account the defaults given in
- // the block_scope, and arguments passed into the build).
- Scope::KeyValueMap values;
- block_scope.GetCurrentScopeValues(&values);
- scope->settings()->build_settings()->build_args().DeclareArgs(values, scope,
- err);
- return Value();
-}
-
-// defined ---------------------------------------------------------------------
-
-const char kDefined[] = "defined";
-const char kDefined_HelpShort[] =
- "defined: Returns whether an identifier is defined.";
-const char kDefined_Help[] =
- R"(defined: Returns whether an identifier is defined.
-
- Returns true if the given argument is defined. This is most useful in
- templates to assert that the caller set things up properly.
-
- You can pass an identifier:
- defined(foo)
- which will return true or false depending on whether foo is defined in the
- current scope.
-
- You can also check a named scope:
- defined(foo.bar)
- which will return true or false depending on whether bar is defined in the
- named scope foo. It will throw an error if foo is not defined or is not a
- scope.
-
-Example
-
- template("mytemplate") {
- # To help users call this template properly...
- assert(defined(invoker.sources), "Sources must be defined")
-
- # If we want to accept an optional "values" argument, we don't
- # want to dereference something that may not be defined.
- if (defined(invoker.values)) {
- values = invoker.values
- } else {
- values = "some default value"
- }
- }
-)";
-
-Value RunDefined(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err) {
- const auto& args_vector = args_list->contents();
- if (args_vector.size() != 1) {
- *err = Err(function, "Wrong number of arguments to defined().",
- "Expecting exactly one.");
- return Value();
- }
-
- const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
- if (identifier) {
- // Passed an identifier "defined(foo)".
- if (scope->GetValue(identifier->value().value()))
- return Value(function, true);
- return Value(function, false);
- }
-
- const AccessorNode* accessor = args_vector[0]->AsAccessor();
- if (accessor) {
- // Passed an accessor "defined(foo.bar)".
- if (accessor->member()) {
- // The base of the accessor must be a scope if it's defined.
- const Value* base = scope->GetValue(accessor->base().value());
- if (!base) {
- *err = Err(accessor, "Undefined identifier");
- return Value();
- }
- if (!base->VerifyTypeIs(Value::SCOPE, err))
- return Value();
-
- // Check the member inside the scope to see if its defined.
- if (base->scope_value()->GetValue(accessor->member()->value().value()))
- return Value(function, true);
- return Value(function, false);
- }
- }
-
- // Argument is invalid.
- *err = Err(function, "Bad thing passed to defined().",
- "It should be of the form defined(foo) or defined(foo.bar).");
- return Value();
-}
-
-// getenv ----------------------------------------------------------------------
-
-const char kGetEnv[] = "getenv";
-const char kGetEnv_HelpShort[] = "getenv: Get an environment variable.";
-const char kGetEnv_Help[] =
- R"(getenv: Get an environment variable.
-
- value = getenv(env_var_name)
-
- Returns the value of the given environment variable. If the value is not
- found, it will try to look up the variable with the "opposite" case (based on
- the case of the first letter of the variable), but is otherwise
- case-sensitive.
-
- If the environment variable is not found, the empty string will be returned.
- Note: it might be nice to extend this if we had the concept of "none" in the
- language to indicate lookup failure.
-
-Example
-
- home_dir = getenv("HOME")
-)";
-
-Value RunGetEnv(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (!EnsureSingleStringArg(function, args, err))
- return Value();
-
- std::unique_ptr<base::Environment> env(base::Environment::Create());
-
- std::string result;
- if (!env->GetVar(args[0].string_value().c_str(), &result))
- return Value(function, ""); // Not found, return empty string.
- return Value(function, result);
-}
-
-// import ----------------------------------------------------------------------
-
-const char kImport[] = "import";
-const char kImport_HelpShort[] =
- "import: Import a file into the current scope.";
-const char kImport_Help[] =
- R"(import: Import a file into the current scope.
-
- The import command loads the rules and variables resulting from executing the
- given file into the current scope.
-
- By convention, imported files are named with a .gni extension.
-
- An import is different than a C++ "include". The imported file is executed in
- a standalone environment from the caller of the import command. The results
- of this execution are cached for other files that import the same .gni file.
-
- Note that you can not import a BUILD.gn file that's otherwise used in the
- build. Files must either be imported or implicitly loaded as a result of deps
- rules, but not both.
-
- The imported file's scope will be merged with the scope at the point import
- was called. If there is a conflict (both the current scope and the imported
- file define some variable or rule with the same name but different value), a
- runtime error will be thrown. Therefore, it's good practice to minimize the
- stuff that an imported file defines.
-
- Variables and templates beginning with an underscore '_' are considered
- private and will not be imported. Imported files can use such variables for
- internal computation without affecting other files.
-
-Examples
-
- import("//build/rules/idl_compilation_rule.gni")
-
- # Looks in the current directory.
- import("my_vars.gni")
-)";
-
-Value RunImport(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (!EnsureSingleStringArg(function, args, err))
- return Value();
-
- const SourceDir& input_dir = scope->GetSourceDir();
- SourceFile import_file = input_dir.ResolveRelativeFile(
- args[0], err, scope->settings()->build_settings()->root_path_utf8());
- scope->AddBuildDependencyFile(import_file);
- if (!err->has_error()) {
- scope->settings()->import_manager().DoImport(import_file, function, scope,
- err);
- }
- return Value();
-}
-
-// not_needed -----------------------------------------------------------------
-
-const char kNotNeeded[] = "not_needed";
-const char kNotNeeded_HelpShort[] =
- "not_needed: Mark variables from scope as not needed.";
-const char kNotNeeded_Help[] =
- R"(not_needed: Mark variables from scope as not needed.
-
- not_needed(variable_list_or_star, variable_to_ignore_list = [])
- not_needed(from_scope, variable_list_or_star,
- variable_to_ignore_list = [])
-
- Mark the variables in the current or given scope as not needed, which means
- you will not get an error about unused variables for these. The
- variable_to_ignore_list allows excluding variables from "all matches" if
- variable_list_or_star is "*".
-
-Example
-
- not_needed("*", [ "config" ])
- not_needed([ "data_deps", "deps" ])
- not_needed(invoker, "*", [ "config" ])
- not_needed(invoker, [ "data_deps", "deps" ])
-)";
-
-Value RunNotNeeded(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err) {
- const auto& args_vector = args_list->contents();
- if (args_vector.size() < 1 || args_vector.size() > 3) {
- *err = Err(function, "Wrong number of arguments.",
- "Expecting one, two or three arguments.");
- return Value();
- }
- auto args_cur = args_vector.begin();
-
- Value* value = nullptr; // Value to use, may point to result_value.
- Value result_value; // Storage for the "evaluate" case.
- Value scope_value; // Storage for an evaluated scope.
- const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
- if (identifier) {
- // Optimize the common case where the input scope is an identifier. This
- // prevents a copy of a potentially large Scope object.
- value = scope->GetMutableValue(identifier->value().value(),
- Scope::SEARCH_NESTED, true);
- if (!value) {
- *err = Err(identifier, "Undefined identifier.");
- return Value();
- }
- } else {
- // Non-optimized case, just evaluate the argument.
- result_value = (*args_cur)->Execute(scope, err);
- if (err->has_error())
- return Value();
- value = &result_value;
- }
- args_cur++;
-
- // Extract the source scope if different from current one.
- Scope* source = scope;
- if (value->type() == Value::SCOPE) {
- if (args_cur == args_vector.end()) {
- *err = Err(
- function, "Wrong number of arguments.",
- "The first argument is a scope, expecting two or three arguments.");
- return Value();
- }
- // Copy the scope value if it will be overridden.
- if (value == &result_value) {
- scope_value = Value(nullptr, value->scope_value()->MakeClosure());
- source = scope_value.scope_value();
- } else {
- source = value->scope_value();
- }
- result_value = (*args_cur)->Execute(scope, err);
- if (err->has_error())
- return Value();
- value = &result_value;
- args_cur++;
- } else if (args_vector.size() > 2) {
- *err = Err(
- function, "Wrong number of arguments.",
- "The first argument is not a scope, expecting one or two arguments.");
- return Value();
- }
-
- // Extract the exclusion list if defined.
- Value exclusion_value;
- std::set<std::string> exclusion_set;
- if (args_cur != args_vector.end()) {
- exclusion_value = (*args_cur)->Execute(source, err);
- if (err->has_error())
- return Value();
-
- if (exclusion_value.type() != Value::LIST) {
- *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
- "Expecting a list of strings.");
- return Value();
- }
-
- for (const Value& cur : exclusion_value.list_value()) {
- if (!cur.VerifyTypeIs(Value::STRING, err))
- return Value();
-
- exclusion_set.insert(cur.string_value());
- }
- }
-
- if (value->type() == Value::STRING) {
- if (value->string_value() == "*") {
- source->MarkAllUsed(exclusion_set);
- return Value();
- }
- } else if (value->type() == Value::LIST) {
- if (exclusion_value.type() != Value::NONE) {
- *err = Err(exclusion_value, "Not supported with a variable list.",
- "Exclusion list can only be used with the string \"*\".");
- return Value();
- }
- for (const Value& cur : value->list_value()) {
- if (!cur.VerifyTypeIs(Value::STRING, err))
- return Value();
- if (!source->GetValue(cur.string_value(), true)) {
- *err = Err(cur, "Undefined identifier");
- return Value();
- }
- }
- return Value();
- }
-
- // Not the right type of argument.
- *err = Err(*value, "Not a valid list of variables.",
- "Expecting either the string \"*\" or a list of strings.");
- return Value();
-}
-
-// set_sources_assignment_filter -----------------------------------------------
-
-const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
-const char kSetSourcesAssignmentFilter_HelpShort[] =
- "set_sources_assignment_filter: Set a pattern to filter source files.";
-const char kSetSourcesAssignmentFilter_Help[] =
- R"(set_sources_assignment_filter: Set a pattern to filter source files.
-
- The sources assignment filter is a list of patterns that remove files from
- the list implicitly whenever the "sources" variable is assigned to. This will
- do nothing for non-lists.
-
- This is intended to be used to globally filter out files with
- platform-specific naming schemes when they don't apply, for example you may
- want to filter out all "*_win.cc" files on non-Windows platforms.
-
- Typically this will be called once in the master build config script to set
- up the filter for the current platform. Subsequent calls will overwrite the
- previous values.
-
- If you want to bypass the filter and add a file even if it might be filtered
- out, call set_sources_assignment_filter([]) to clear the list of filters.
- This will apply until the current scope exits
-
-How to use patterns
-
- File patterns are VERY limited regular expressions. They must match the
- entire input string to be counted as a match. In regular expression parlance,
- there is an implicit "^...$" surrounding your input. If you want to match a
- substring, you need to use wildcards at the beginning and end.
-
- There are only two special tokens understood by the pattern matcher.
- Everything else is a literal.
-
- - "*" Matches zero or more of any character. It does not depend on the
- preceding character (in regular expression parlance it is equivalent to
- ".*").
-
- - "\b" Matches a path boundary. This will match the beginning or end of a
- string, or a slash.
-
-Pattern examples
-
- "*asdf*"
- Matches a string containing "asdf" anywhere.
-
- "asdf"
- Matches only the exact string "asdf".
-
- "*.cc"
- Matches strings ending in the literal ".cc".
-
- "\bwin/*"
- Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
-
-Sources assignment example
-
- # Filter out all _win files.
- set_sources_assignment_filter([ "*_win.cc", "*_win.h" ])
- sources = [ "a.cc", "b_win.cc" ]
- print(sources)
- # Will print [ "a.cc" ]. b_win one was filtered out.
-)";
-
-Value RunSetSourcesAssignmentFilter(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() != 1) {
- *err = Err(function, "set_sources_assignment_filter takes one argument.");
- } else {
- std::unique_ptr<PatternList> f = std::make_unique<PatternList>();
- f->SetFromValue(args[0], err);
- if (!err->has_error())
- scope->set_sources_assignment_filter(std::move(f));
- }
- return Value();
-}
-
-// pool ------------------------------------------------------------------------
-
-const char kPool[] = "pool";
-const char kPool_HelpShort[] = "pool: Defines a pool object.";
-const char kPool_Help[] =
- R"*(pool: Defines a pool object.
-
- Pool objects can be applied to a tool to limit the parallelism of the
- build. This object has a single property "depth" corresponding to
- the number of tasks that may run simultaneously.
-
- As the file containing the pool definition may be executed in the
- context of more than one toolchain it is recommended to specify an
- explicit toolchain when defining and referencing a pool.
-
- A pool named "console" defined in the root build file represents Ninja's
- console pool. Targets using this pool will have access to the console's
- stdin and stdout, and output will not be buffered. This special pool must
- have a depth of 1. Pools not defined in the root must not be named "console".
- The console pool can only be defined for the default toolchain.
- Refer to the Ninja documentation on the console pool for more info.
-
- A pool is referenced by its label just like a target.
-
-Variables
-
- depth*
- * = required
-
-Example
-
- if (current_toolchain == default_toolchain) {
- pool("link_pool") {
- depth = 1
- }
- }
-
- toolchain("toolchain") {
- tool("link") {
- command = "..."
- pool = ":link_pool($default_toolchain)"
- }
- }
-)*";
-
-const char kDepth[] = "depth";
-
-Value RunPool(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* scope,
- Err* err) {
- NonNestableBlock non_nestable(scope, function, "pool");
- if (!non_nestable.Enter(err))
- return Value();
-
- if (!EnsureSingleStringArg(function, args, err) ||
- !EnsureNotProcessingImport(function, scope, err))
- return Value();
-
- Label label(MakeLabelForScope(scope, function, args[0].string_value()));
-
- if (g_scheduler->verbose_logging())
- g_scheduler->Log("Defining pool", label.GetUserVisibleName(true));
-
- // Get the pool depth. It is an error to define a pool without a depth,
- // so check first for the presence of the value.
- const Value* depth = scope->GetValue(kDepth, true);
- if (!depth) {
- *err = Err(function, "Can't define a pool without depth.");
- return Value();
- }
-
- if (!depth->VerifyTypeIs(Value::INTEGER, err))
- return Value();
-
- if (depth->int_value() < 0) {
- *err = Err(*depth, "depth must be positive or 0.");
- return Value();
- }
-
- // Create the new pool.
- std::unique_ptr<Pool> pool = std::make_unique<Pool>(
- scope->settings(), label, scope->build_dependency_files());
-
- if (label.name() == "console") {
- const Settings* settings = scope->settings();
- if (!settings->is_default()) {
- *err = Err(
- function,
- "\"console\" pool must be defined only in the default toolchain.");
- return Value();
- }
- if (label.dir() != settings->build_settings()->root_target_label().dir()) {
- *err = Err(function, "\"console\" pool must be defined in the root //.");
- return Value();
- }
- if (depth->int_value() != 1) {
- *err = Err(*depth, "\"console\" pool must have depth 1.");
- return Value();
- }
- }
- pool->set_depth(depth->int_value());
-
- // Save the generated item.
- Scope::ItemVector* collector = scope->GetItemCollector();
- if (!collector) {
- *err = Err(function, "Can't define a pool in this context.");
- return Value();
- }
- collector->push_back(std::move(pool));
-
- return Value();
-}
-
-// print -----------------------------------------------------------------------
-
-const char kPrint[] = "print";
-const char kPrint_HelpShort[] = "print: Prints to the console.";
-const char kPrint_Help[] =
- R"(print: Prints to the console.
-
- Prints all arguments to the console separated by spaces. A newline is
- automatically appended to the end.
-
- This function is intended for debugging. Note that build files are run in
- parallel so you may get interleaved prints. A buildfile may also be executed
- more than once in parallel in the context of different toolchains so the
- prints from one file may be duplicated or
- interleaved with itself.
-
-Examples
-
- print("Hello world")
-
- print(sources, deps)
-)";
-
-Value RunPrint(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- std::string output;
- for (size_t i = 0; i < args.size(); i++) {
- if (i != 0)
- output.push_back(' ');
- output.append(args[i].ToString(false));
- }
- output.push_back('\n');
-
- const BuildSettings::PrintCallback& cb =
- scope->settings()->build_settings()->print_callback();
- if (cb.is_null()) {
- printf("%s", output.c_str());
- fflush(stdout);
- } else
- cb.Run(output);
-
- return Value();
-}
-
-// split_list ------------------------------------------------------------------
-
-const char kSplitList[] = "split_list";
-const char kSplitList_HelpShort[] =
- "split_list: Splits a list into N different sub-lists.";
-const char kSplitList_Help[] =
- R"(split_list: Splits a list into N different sub-lists.
-
- result = split_list(input, n)
-
- Given a list and a number N, splits the list into N sub-lists of
- approximately equal size. The return value is a list of the sub-lists. The
- result will always be a list of size N. If N is greater than the number of
- elements in the input, it will be padded with empty lists.
-
- The expected use is to divide source files into smaller uniform chunks.
-
-Example
-
- The code:
- mylist = [1, 2, 3, 4, 5, 6]
- print(split_list(mylist, 3))
-
- Will print:
- [[1, 2], [3, 4], [5, 6]
-)";
-Value RunSplitList(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err) {
- const auto& args_vector = args_list->contents();
- if (args_vector.size() != 2) {
- *err = Err(function, "Wrong number of arguments to split_list().",
- "Expecting exactly two.");
- return Value();
- }
-
- ParseNodeValueAdapter list_adapter;
- if (!list_adapter.InitForType(scope, args_vector[0].get(), Value::LIST, err))
- return Value();
- const std::vector<Value>& input = list_adapter.get().list_value();
-
- ParseNodeValueAdapter count_adapter;
- if (!count_adapter.InitForType(scope, args_vector[1].get(), Value::INTEGER,
- err))
- return Value();
- int64_t count = count_adapter.get().int_value();
- if (count <= 0) {
- *err = Err(function, "Requested result size is not positive.");
- return Value();
- }
-
- Value result(function, Value::LIST);
- result.list_value().resize(count);
-
- // Every result list gets at least this many items in it.
- int64_t min_items_per_list = static_cast<int64_t>(input.size()) / count;
-
- // This many result lists get an extra item which is the remainder from above.
- int64_t extra_items = static_cast<int64_t>(input.size()) % count;
-
- // Allocate all lists that have a remainder assigned to them (max items).
- int64_t max_items_per_list = min_items_per_list + 1;
- auto last_item_end = input.begin();
- for (int64_t i = 0; i < extra_items; i++) {
- result.list_value()[i] = Value(function, Value::LIST);
-
- auto begin_add = last_item_end;
- last_item_end += max_items_per_list;
- result.list_value()[i].list_value().assign(begin_add, last_item_end);
- }
-
- // Allocate all smaller items that don't have a remainder.
- for (int64_t i = extra_items; i < count; i++) {
- result.list_value()[i] = Value(function, Value::LIST);
-
- auto begin_add = last_item_end;
- last_item_end += min_items_per_list;
- result.list_value()[i].list_value().assign(begin_add, last_item_end);
- }
-
- return result;
-}
-
-// string_replace --------------------------------------------------------------
-
-const char kStringReplace[] = "string_replace";
-const char kStringReplace_HelpShort[] =
- "string_replace: Replaces substring in the given string.";
-const char kStringReplace_Help[] =
- R"(string_replace: Replaces substring in the given string.
-
- result = string_replace(str, old, new[, max])
-
- Returns a copy of the string str in which the occurrences of old have been
- replaced with new, optionally restricting the number of replacements. The
- replacement is performed sequentially, so if new contains old, it won't be
- replaced.
-
-Example
-
- The code:
- mystr = "Hello, world!"
- print(string_replace(mystr, "world", "GN"))
-
- Will print:
- Hello, GN!
-)";
-
-Value RunStringReplace(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err) {
- if (args.size() < 3 || args.size() > 4) {
- *err = Err(function, "Wrong number of arguments to string_replace().");
- return Value();
- }
-
- if (!args[0].VerifyTypeIs(Value::STRING, err))
- return Value();
- const std::string str = args[0].string_value();
-
- if (!args[1].VerifyTypeIs(Value::STRING, err))
- return Value();
- const std::string& old = args[1].string_value();
-
- if (!args[2].VerifyTypeIs(Value::STRING, err))
- return Value();
- const std::string& new_ = args[2].string_value();
-
- int64_t max = INT64_MAX;
- if (args.size() > 3) {
- if (!args[3].VerifyTypeIs(Value::INTEGER, err))
- return Value();
- max = args[3].int_value();
- if (max <= 0) {
- *err = Err(function, "Requested number of replacements is not positive.");
- return Value();
- }
- }
-
- int64_t n = 0;
- std::string val(str);
- size_t start_pos = 0;
- while((start_pos = val.find(old, start_pos)) != std::string::npos) {
- val.replace(start_pos, old.length(), new_);
- start_pos += new_.length();
- if (++n >= max)
- break;
- }
- return Value(function, std::move(val));
-}
-
-// -----------------------------------------------------------------------------
-
-FunctionInfo::FunctionInfo()
- : self_evaluating_args_runner(nullptr),
- generic_block_runner(nullptr),
- executed_block_runner(nullptr),
- no_block_runner(nullptr),
- help_short(nullptr),
- help(nullptr),
- is_target(false) {}
-
-FunctionInfo::FunctionInfo(SelfEvaluatingArgsFunction seaf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target)
- : self_evaluating_args_runner(seaf),
- generic_block_runner(nullptr),
- executed_block_runner(nullptr),
- no_block_runner(nullptr),
- help_short(in_help_short),
- help(in_help),
- is_target(in_is_target) {}
-
-FunctionInfo::FunctionInfo(GenericBlockFunction gbf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target)
- : self_evaluating_args_runner(nullptr),
- generic_block_runner(gbf),
- executed_block_runner(nullptr),
- no_block_runner(nullptr),
- help_short(in_help_short),
- help(in_help),
- is_target(in_is_target) {}
-
-FunctionInfo::FunctionInfo(ExecutedBlockFunction ebf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target)
- : self_evaluating_args_runner(nullptr),
- generic_block_runner(nullptr),
- executed_block_runner(ebf),
- no_block_runner(nullptr),
- help_short(in_help_short),
- help(in_help),
- is_target(in_is_target) {}
-
-FunctionInfo::FunctionInfo(NoBlockFunction nbf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target)
- : self_evaluating_args_runner(nullptr),
- generic_block_runner(nullptr),
- executed_block_runner(nullptr),
- no_block_runner(nbf),
- help_short(in_help_short),
- help(in_help),
- is_target(in_is_target) {}
-
-// Setup the function map via a static initializer. We use this because it
-// avoids race conditions without having to do some global setup function or
-// locking-heavy singleton checks at runtime. In practice, we always need this
-// before we can do anything interesting, so it's OK to wait for the
-// initializer.
-struct FunctionInfoInitializer {
- FunctionInfoMap map;
-
- FunctionInfoInitializer() {
-#define INSERT_FUNCTION(command, is_target) \
- map[k##command] = FunctionInfo(&Run##command, k##command##_HelpShort, \
- k##command##_Help, is_target);
-
- INSERT_FUNCTION(Action, true)
- INSERT_FUNCTION(ActionForEach, true)
- INSERT_FUNCTION(BundleData, true)
- INSERT_FUNCTION(CreateBundle, true)
- INSERT_FUNCTION(Copy, true)
- INSERT_FUNCTION(Executable, true)
- INSERT_FUNCTION(Group, true)
- INSERT_FUNCTION(LoadableModule, true)
- INSERT_FUNCTION(SharedLibrary, true)
- INSERT_FUNCTION(SourceSet, true)
- INSERT_FUNCTION(StaticLibrary, true)
- INSERT_FUNCTION(Target, true)
- INSERT_FUNCTION(GeneratedFile, true)
- INSERT_FUNCTION(RustLibrary, true)
-
- INSERT_FUNCTION(Assert, false)
- INSERT_FUNCTION(Config, false)
- INSERT_FUNCTION(DeclareArgs, false)
- INSERT_FUNCTION(Defined, false)
- INSERT_FUNCTION(ExecScript, false)
- INSERT_FUNCTION(ForEach, false)
- INSERT_FUNCTION(ForwardVariablesFrom, false)
- INSERT_FUNCTION(GetEnv, false)
- INSERT_FUNCTION(GetLabelInfo, false)
- INSERT_FUNCTION(GetPathInfo, false)
- INSERT_FUNCTION(GetTargetOutputs, false)
- INSERT_FUNCTION(Import, false)
- INSERT_FUNCTION(NotNeeded, false)
- INSERT_FUNCTION(Pool, false)
- INSERT_FUNCTION(Print, false)
- INSERT_FUNCTION(ProcessFileTemplate, false)
- INSERT_FUNCTION(ReadFile, false)
- INSERT_FUNCTION(RebasePath, false)
- INSERT_FUNCTION(SetDefaults, false)
- INSERT_FUNCTION(SetDefaultToolchain, false)
- INSERT_FUNCTION(SetSourcesAssignmentFilter, false)
- INSERT_FUNCTION(SplitList, false)
- INSERT_FUNCTION(StringReplace, false)
- INSERT_FUNCTION(Template, false)
- INSERT_FUNCTION(Tool, false)
- INSERT_FUNCTION(Toolchain, false)
- INSERT_FUNCTION(WriteFile, false)
-
-#undef INSERT_FUNCTION
- }
-};
-const FunctionInfoInitializer function_info;
-
-const FunctionInfoMap& GetFunctions() {
- return function_info.map;
-}
-
-Value RunFunction(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- BlockNode* block,
- Err* err) {
- const Token& name = function->function();
-
- std::string template_name = function->function().value().as_string();
- const Template* templ = scope->GetTemplate(template_name);
- if (templ) {
- Value args = args_list->Execute(scope, err);
- if (err->has_error())
- return Value();
- return templ->Invoke(scope, function, template_name, args.list_value(),
- block, err);
- }
-
- // No template matching this, check for a built-in function.
- const FunctionInfoMap& function_map = GetFunctions();
- FunctionInfoMap::const_iterator found_function =
- function_map.find(name.value());
- if (found_function == function_map.end()) {
- *err = Err(name, "Unknown function.");
- return Value();
- }
-
- if (found_function->second.self_evaluating_args_runner) {
- // Self evaluating args functions are special weird built-ins like foreach.
- // Rather than force them all to check that they have a block or no block
- // and risk bugs for new additions, check a whitelist here.
- if (found_function->second.self_evaluating_args_runner != &RunForEach) {
- if (!VerifyNoBlockForFunctionCall(function, block, err))
- return Value();
- }
- return found_function->second.self_evaluating_args_runner(scope, function,
- args_list, err);
- }
-
- // All other function types take a pre-executed set of args.
- Value args = args_list->Execute(scope, err);
- if (err->has_error())
- return Value();
-
- if (found_function->second.generic_block_runner) {
- if (!block) {
- FillNeedsBlockError(function, err);
- return Value();
- }
- return found_function->second.generic_block_runner(
- scope, function, args.list_value(), block, err);
- }
-
- if (found_function->second.executed_block_runner) {
- if (!block) {
- FillNeedsBlockError(function, err);
- return Value();
- }
-
- Scope block_scope(scope);
- block->Execute(&block_scope, err);
- if (err->has_error())
- return Value();
-
- Value result = found_function->second.executed_block_runner(
- function, args.list_value(), &block_scope, err);
- if (err->has_error())
- return Value();
-
- if (!block_scope.CheckForUnusedVars(err))
- return Value();
- return result;
- }
-
- // Otherwise it's a no-block function.
- if (!VerifyNoBlockForFunctionCall(function, block, err))
- return Value();
- return found_function->second.no_block_runner(scope, function,
- args.list_value(), err);
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/functions.h b/gn/tools/gn/functions.h
deleted file mode 100644
index 9dc2cdc52fd..00000000000
--- a/gn/tools/gn/functions.h
+++ /dev/null
@@ -1,538 +0,0 @@
-// 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 TOOLS_GN_FUNCTIONS_H_
-#define TOOLS_GN_FUNCTIONS_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/strings/string_piece.h"
-
-class Err;
-class BlockNode;
-class FunctionCallNode;
-class Label;
-class ListNode;
-class ParseNode;
-class Scope;
-class Value;
-
-// -----------------------------------------------------------------------------
-
-namespace functions {
-
-// This type of function invocation has no block and evaluates its arguments
-// itself rather than taking a pre-executed list. This allows us to implement
-// certain built-in functions.
-typedef Value (*SelfEvaluatingArgsFunction)(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err);
-
-// This type of function invocation takes a block node that it will execute.
-typedef Value (*GenericBlockFunction)(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-// This type of function takes a block, but does not need to control execution
-// of it. The dispatch function will pre-execute the block and pass the
-// resulting block_scope to the function.
-typedef Value (*ExecutedBlockFunction)(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* block_scope,
- Err* err);
-
-// This type of function does not take a block. It just has arguments.
-typedef Value (*NoBlockFunction)(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kAction[];
-extern const char kAction_HelpShort[];
-extern const char kAction_Help[];
-Value RunAction(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kActionForEach[];
-extern const char kActionForEach_HelpShort[];
-extern const char kActionForEach_Help[];
-Value RunActionForEach(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kAssert[];
-extern const char kAssert_HelpShort[];
-extern const char kAssert_Help[];
-Value RunAssert(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kBundleData[];
-extern const char kBundleData_HelpShort[];
-extern const char kBundleData_Help[];
-Value RunBundleData(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kCreateBundle[];
-extern const char kCreateBundle_HelpShort[];
-extern const char kCreateBundle_Help[];
-Value RunCreateBundle(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kConfig[];
-extern const char kConfig_HelpShort[];
-extern const char kConfig_Help[];
-Value RunConfig(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* block_scope,
- Err* err);
-
-extern const char kCopy[];
-extern const char kCopy_HelpShort[];
-extern const char kCopy_Help[];
-Value RunCopy(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* block_scope,
- Err* err);
-
-extern const char kDeclareArgs[];
-extern const char kDeclareArgs_HelpShort[];
-extern const char kDeclareArgs_Help[];
-Value RunDeclareArgs(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kDefined[];
-extern const char kDefined_HelpShort[];
-extern const char kDefined_Help[];
-Value RunDefined(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err);
-
-extern const char kExecScript[];
-extern const char kExecScript_HelpShort[];
-extern const char kExecScript_Help[];
-Value RunExecScript(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kExecutable[];
-extern const char kExecutable_HelpShort[];
-extern const char kExecutable_Help[];
-Value RunExecutable(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kForEach[];
-extern const char kForEach_HelpShort[];
-extern const char kForEach_Help[];
-Value RunForEach(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err);
-
-extern const char kForwardVariablesFrom[];
-extern const char kForwardVariablesFrom_HelpShort[];
-extern const char kForwardVariablesFrom_Help[];
-Value RunForwardVariablesFrom(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err);
-
-extern const char kGetEnv[];
-extern const char kGetEnv_HelpShort[];
-extern const char kGetEnv_Help[];
-Value RunGetEnv(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kGetLabelInfo[];
-extern const char kGetLabelInfo_HelpShort[];
-extern const char kGetLabelInfo_Help[];
-Value RunGetLabelInfo(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kGetPathInfo[];
-extern const char kGetPathInfo_HelpShort[];
-extern const char kGetPathInfo_Help[];
-Value RunGetPathInfo(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kGetTargetOutputs[];
-extern const char kGetTargetOutputs_HelpShort[];
-extern const char kGetTargetOutputs_Help[];
-Value RunGetTargetOutputs(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kGeneratedFile[];
-extern const char kGeneratedFile_HelpShort[];
-extern const char kGeneratedFile_Help[];
-Value RunGeneratedFile(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kGroup[];
-extern const char kGroup_HelpShort[];
-extern const char kGroup_Help[];
-Value RunGroup(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kImport[];
-extern const char kImport_HelpShort[];
-extern const char kImport_Help[];
-Value RunImport(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kLoadableModule[];
-extern const char kLoadableModule_HelpShort[];
-extern const char kLoadableModule_Help[];
-Value RunLoadableModule(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kNotNeeded[];
-extern const char kNotNeeded_HelpShort[];
-extern const char kNotNeeded_Help[];
-Value RunNotNeeded(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err);
-
-extern const char kPool[];
-extern const char kPool_HelpShort[];
-extern const char kPool_Help[];
-Value RunPool(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* block_scope,
- Err* err);
-
-extern const char kPrint[];
-extern const char kPrint_HelpShort[];
-extern const char kPrint_Help[];
-Value RunPrint(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kProcessFileTemplate[];
-extern const char kProcessFileTemplate_HelpShort[];
-extern const char kProcessFileTemplate_Help[];
-Value RunProcessFileTemplate(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kReadFile[];
-extern const char kReadFile_HelpShort[];
-extern const char kReadFile_Help[];
-Value RunReadFile(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kRebasePath[];
-extern const char kRebasePath_HelpShort[];
-extern const char kRebasePath_Help[];
-Value RunRebasePath(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kRustLibrary[];
-extern const char kRustLibrary_HelpShort[];
-extern const char kRustLibrary_Help[];
-Value RunRustLibrary(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kSetDefaults[];
-extern const char kSetDefaults_HelpShort[];
-extern const char kSetDefaults_Help[];
-Value RunSetDefaults(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kSetDefaultToolchain[];
-extern const char kSetDefaultToolchain_HelpShort[];
-extern const char kSetDefaultToolchain_Help[];
-Value RunSetDefaultToolchain(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kSetSourcesAssignmentFilter[];
-extern const char kSetSourcesAssignmentFilter_HelpShort[];
-extern const char kSetSourcesAssignmentFilter_Help[];
-Value RunSetSourcesAssignmentFilter(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-extern const char kSharedLibrary[];
-extern const char kSharedLibrary_HelpShort[];
-extern const char kSharedLibrary_Help[];
-Value RunSharedLibrary(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kSourceSet[];
-extern const char kSourceSet_HelpShort[];
-extern const char kSourceSet_Help[];
-Value RunSourceSet(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kSplitList[];
-extern const char kSplitList_HelpShort[];
-extern const char kSplitList_Help[];
-Value RunSplitList(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- Err* err);
-
-extern const char kStaticLibrary[];
-extern const char kStaticLibrary_HelpShort[];
-extern const char kStaticLibrary_Help[];
-Value RunStaticLibrary(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kReplaceSubstr[];
-extern const char kReplaceSubstr_HelpShort[];
-extern const char kReplaceSubstr_Help[];
-Value RunReplaceSubstr(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args_list,
- Err* err);
-
-extern const char kTarget[];
-extern const char kTarget_HelpShort[];
-extern const char kTarget_Help[];
-Value RunTarget(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kTemplate[];
-extern const char kTemplate_HelpShort[];
-extern const char kTemplate_Help[];
-Value RunTemplate(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kTool[];
-extern const char kTool_HelpShort[];
-extern const char kTool_Help[];
-Value RunTool(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kToolchain[];
-extern const char kToolchain_HelpShort[];
-extern const char kToolchain_Help[];
-Value RunToolchain(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err);
-
-extern const char kWriteFile[];
-extern const char kWriteFile_HelpShort[];
-extern const char kWriteFile_Help[];
-Value RunWriteFile(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-// -----------------------------------------------------------------------------
-
-// One function record. Only one of the given runner types will be non-null
-// which indicates the type of function it is.
-struct FunctionInfo {
- FunctionInfo();
- FunctionInfo(SelfEvaluatingArgsFunction seaf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target);
- FunctionInfo(GenericBlockFunction gbf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target);
- FunctionInfo(ExecutedBlockFunction ebf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target);
- FunctionInfo(NoBlockFunction nbf,
- const char* in_help_short,
- const char* in_help,
- bool in_is_target);
-
- SelfEvaluatingArgsFunction self_evaluating_args_runner;
- GenericBlockFunction generic_block_runner;
- ExecutedBlockFunction executed_block_runner;
- NoBlockFunction no_block_runner;
-
- const char* help_short;
- const char* help;
-
- bool is_target;
-};
-
-typedef std::map<base::StringPiece, FunctionInfo> FunctionInfoMap;
-
-// Returns the mapping of all built-in functions.
-const FunctionInfoMap& GetFunctions();
-
-// Runs the given function.
-Value RunFunction(Scope* scope,
- const FunctionCallNode* function,
- const ListNode* args_list,
- BlockNode* block, // Optional.
- Err* err);
-
-} // namespace functions
-
-// Helper functions -----------------------------------------------------------
-
-// Validates that the scope that a value is defined in is not the scope
-// of the current declare_args() call, if that's what we're in. It is
-// illegal to read a value from inside the same declare_args() call, since
-// the overrides will not have been applied yet (see `gn help declare_args`
-// for more).
-bool EnsureNotReadingFromSameDeclareArgs(const ParseNode* node,
- const Scope* cur_scope,
- const Scope* val_scope,
- Err* err);
-
-// Verifies that the current scope is not processing an import. If it is, it
-// will set the error, blame the given parse node for it, and return false.
-bool EnsureNotProcessingImport(const ParseNode* node,
- const Scope* scope,
- Err* err);
-
-// Like EnsureNotProcessingImport but checks for running the build config.
-bool EnsureNotProcessingBuildConfig(const ParseNode* node,
- const Scope* scope,
- Err* err);
-
-// Sets up the |block_scope| for executing a target (or something like it).
-// The |scope| is the containing scope. It should have been already set as the
-// parent for the |block_scope| when the |block_scope| was created.
-//
-// This will set up the target defaults and set the |target_name| variable in
-// the block scope to the current target name, which is assumed to be the first
-// argument to the function.
-//
-// On success, returns true. On failure, sets the error and returns false.
-bool FillTargetBlockScope(const Scope* scope,
- const FunctionCallNode* function,
- const std::string& target_type,
- const BlockNode* block,
- const std::vector<Value>& args,
- Scope* block_scope,
- Err* err);
-
-// Sets the given error to a message explaining that the function call requires
-// a block.
-void FillNeedsBlockError(const FunctionCallNode* function, Err* err);
-
-// Validates that the given function call has one string argument. This is
-// the most common function signature, so it saves space to have this helper.
-// Returns false and sets the error on failure.
-bool EnsureSingleStringArg(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Err* err);
-
-// Returns the name of the toolchain for the given scope.
-const Label& ToolchainLabelForScope(const Scope* scope);
-
-// Generates a label for the given scope, using the current directory and
-// toolchain, and the given name.
-Label MakeLabelForScope(const Scope* scope,
- const FunctionCallNode* function,
- const std::string& name);
-
-// Some types of blocks can't be nested inside other ones. For such cases,
-// instantiate this object upon entering the block and Enter() will fail if
-// there is already another non-nestable block on the stack.
-class NonNestableBlock {
- public:
- // type_description is a string that will be used in error messages
- // describing the type of the block, for example, "template" or "config".
- NonNestableBlock(Scope* scope,
- const FunctionCallNode* function,
- const char* type_description);
- ~NonNestableBlock();
-
- bool Enter(Err* err);
-
- private:
- // Used as a void* key for the Scope to track our property. The actual value
- // is never used.
- static const int kKey;
-
- Scope* scope_;
- const FunctionCallNode* function_;
- const char* type_description_;
-
- // Set to true when the key is added to the scope so we don't try to
- // delete nonexistant keys which will cause assertions.
- bool key_added_;
-};
-
-#endif // TOOLS_GN_FUNCTIONS_H_
diff --git a/gn/tools/gn/functions_target.cc b/gn/tools/gn/functions_target.cc
deleted file mode 100644
index c20c1e681e6..00000000000
--- a/gn/tools/gn/functions_target.cc
+++ /dev/null
@@ -1,984 +0,0 @@
-// 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 "tools/gn/functions.h"
-
-#include "tools/gn/config_values_generator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/target_generator.h"
-#include "tools/gn/template.h"
-#include "tools/gn/value.h"
-#include "tools/gn/variables.h"
-
-#define DEPENDENT_CONFIG_VARS \
- " Dependent configs: all_dependent_configs, public_configs\n"
-#define DEPS_VARS " Deps: data_deps, deps, public_deps\n"
-#define GENERAL_TARGET_VARS \
- " General: check_includes, configs, data, friend, inputs, metadata,\n" \
- " output_name, output_extension, public, sources, testonly,\n" \
- " visibility\n"
-#define RUST_VARS \
- " Rust variables: aliased_deps, crate_root, crate_name, edition\n"
-#define RUST_SHARED_VARS \
- " Rust variables: aliased_deps, crate_root, crate_name, crate_type, " \
- "edition\n"
-
-namespace functions {
-
-namespace {
-
-Value ExecuteGenericTarget(const char* target_type,
- Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- NonNestableBlock non_nestable(scope, function, "target");
- if (!non_nestable.Enter(err))
- return Value();
-
- if (!EnsureNotProcessingImport(function, scope, err) ||
- !EnsureNotProcessingBuildConfig(function, scope, err))
- return Value();
- Scope block_scope(scope);
- if (!FillTargetBlockScope(scope, function, target_type, block, args,
- &block_scope, err))
- return Value();
-
- block->Execute(&block_scope, err);
- if (err->has_error())
- return Value();
-
- TargetGenerator::GenerateTarget(&block_scope, function, args, target_type,
- err);
- if (err->has_error())
- return Value();
-
- block_scope.CheckForUnusedVars(err);
- return Value();
-}
-
-} // namespace
-
-// action ----------------------------------------------------------------------
-
-// Common help paragraph on script runtime execution directories.
-#define SCRIPT_EXECUTION_CONTEXT \
- "\n" \
- " The script will be executed with the given arguments with the current\n" \
- " directory being that of the root build directory. If you pass files\n" \
- " to your script, see \"gn help rebase_path\" for how to convert\n" \
- " file names to be relative to the build directory (file names in the\n" \
- " sources, outputs, and inputs will be all treated as relative to the\n" \
- " current build file and converted as needed automatically).\n"
-
-// Common help paragraph on script output directories.
-#define SCRIPT_EXECUTION_OUTPUTS \
- "\n" \
- " All output files must be inside the output directory of the build.\n" \
- " You would generally use |$target_out_dir| or |$target_gen_dir| to\n" \
- " reference the output or generated intermediate file directories,\n" \
- " respectively.\n"
-
-#define ACTION_DEPS \
- "\n" \
- " The \"deps\" and \"public_deps\" for an action will always be\n" \
- " completed before any part of the action is run so it can depend on\n" \
- " the output of previous steps. The \"data_deps\" will be built if the\n" \
- " action is built, but may not have completed before all steps of the\n" \
- " action are started. This can give additional parallelism in the build\n" \
- " for runtime-only dependencies.\n"
-
-// Common help paragraph on targets that can use different languages.
-#define LANGUAGE_HELP \
- "\n" \
- " The tools and commands used to create this target type will be\n" \
- " determined by the source files in its sources. Targets containing\n" \
- " multiple compiler-incompatible languages are not allowed (e.g. a\n" \
- " target containing both C and C++ sources is acceptable, but a\n" \
- " target containing C and Rust sources is not).\n"
-
-const char kAction[] = "action";
-const char kAction_HelpShort[] =
- "action: Declare a target that runs a script a single time.";
-const char kAction_Help[] =
- R"(action: Declare a target that runs a script a single time.
-
- This target type allows you to run a script a single time to produce one or
- more output files. If you want to run a script once for each of a set of
- input files, see "gn help action_foreach".
-
-Inputs
-
- In an action the "sources" and "inputs" are treated the same: they're both
- input dependencies on script execution with no special handling. If you want
- to pass the sources to your script, you must do so explicitly by including
- them in the "args". Note also that this means there is no special handling of
- paths since GN doesn't know which of the args are paths and not. You will
- want to use rebase_path() to convert paths to be relative to the
- root_build_dir.
-
- You can dynamically write input dependencies (for incremental rebuilds if an
- input file changes) by writing a depfile when the script is run (see "gn help
- depfile"). This is more flexible than "inputs".
-
- If the command line length is very long, you can use response files to pass
- args to your script. See "gn help response_file_contents".
-
- It is recommended you put inputs to your script in the "sources" variable,
- and stuff like other Python files required to run your script in the "inputs"
- variable.
-)"
-
- ACTION_DEPS
-
- R"(
-Outputs
-
- You should specify files created by your script by specifying them in the
- "outputs".
-)"
-
- SCRIPT_EXECUTION_CONTEXT
-
- R"(
-File name handling
-)"
-
- SCRIPT_EXECUTION_OUTPUTS
-
- R"(
-Variables
-
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources
- * = required
-
-Example
-
- action("run_this_guy_once") {
- script = "doprocessing.py"
- sources = [ "my_configuration.txt" ]
- outputs = [ "$target_gen_dir/insightful_output.txt" ]
-
- # Our script imports this Python file so we want to rebuild if it changes.
- inputs = [ "helper_library.py" ]
-
- # Note that we have to manually pass the sources to our script if the
- # script needs them as inputs.
- args = [ "--out", rebase_path(target_gen_dir, root_build_dir) ] +
- rebase_path(sources, root_build_dir)
- }
-)";
-
-Value RunAction(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kAction, scope, function, args, block,
- err);
-}
-
-// action_foreach --------------------------------------------------------------
-
-const char kActionForEach[] = "action_foreach";
-const char kActionForEach_HelpShort[] =
- "action_foreach: Declare a target that runs a script over a set of files.";
-const char kActionForEach_Help[] =
- R"(action_foreach: Declare a target that runs a script over a set of files.
-
- This target type allows you to run a script once-per-file over a set of
- sources. If you want to run a script once that takes many files as input, see
- "gn help action".
-
-Inputs
-
- The script will be run once per file in the "sources" variable. The "outputs"
- variable should specify one or more files with a source expansion pattern in
- it (see "gn help source_expansion"). The output file(s) for each script
- invocation should be unique. Normally you use "{{source_name_part}}" in each
- output file.
-
- If your script takes additional data as input, such as a shared configuration
- file or a Python module it uses, those files should be listed in the "inputs"
- variable. These files are treated as dependencies of each script invocation.
-
- If the command line length is very long, you can use response files to pass
- args to your script. See "gn help response_file_contents".
-
- You can dynamically write input dependencies (for incremental rebuilds if an
- input file changes) by writing a depfile when the script is run (see "gn help
- depfile"). This is more flexible than "inputs".
-)" ACTION_DEPS
- R"(
-Outputs
-)" SCRIPT_EXECUTION_CONTEXT
- R"(
-File name handling
-)" SCRIPT_EXECUTION_OUTPUTS
- R"(
-Variables
-
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources*
- * = required
-
-Example
-
- # Runs the script over each IDL file. The IDL script will generate both a .cc
- # and a .h file for each input.
- action_foreach("my_idl") {
- script = "idl_processor.py"
- sources = [ "foo.idl", "bar.idl" ]
-
- # Our script reads this file each time, so we need to list it as a
- # dependency so we can rebuild if it changes.
- inputs = [ "my_configuration.txt" ]
-
- # Transformation from source file name to output file names.
- outputs = [ "$target_gen_dir/{{source_name_part}}.h",
- "$target_gen_dir/{{source_name_part}}.cc" ]
-
- # Note that since "args" is opaque to GN, if you specify paths here, you
- # will need to convert it to be relative to the build directory using
- # rebase_path().
- args = [
- "{{source}}",
- "-o",
- rebase_path(relative_target_gen_dir, root_build_dir) +
- "/{{source_name_part}}.h" ]
- }
-)";
-
-Value RunActionForEach(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kActionForEach, scope, function, args,
- block, err);
-}
-
-// bundle_data -----------------------------------------------------------------
-
-const char kBundleData[] = "bundle_data";
-const char kBundleData_HelpShort[] =
- "bundle_data: [iOS/macOS] Declare a target without output.";
-const char kBundleData_Help[] =
- R"(bundle_data: [iOS/macOS] Declare a target without output.
-
- This target type allows to declare data that is required at runtime. It is
- used to inform "create_bundle" targets of the files to copy into generated
- bundle, see "gn help create_bundle" for help.
-
- The target must define a list of files as "sources" and a single "outputs".
- If there are multiple files, source expansions must be used to express the
- output. The output must reference a file inside of {{bundle_root_dir}}.
-
- This target can be used on all platforms though it is designed only to
- generate iOS/macOS bundle. In cross-platform projects, it is advised to put it
- behind iOS/macOS conditionals.
-
- See "gn help create_bundle" for more information.
-
-Variables
-
- sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
- * = required
-
-Examples
-
- bundle_data("icudata") {
- sources = [ "sources/data/in/icudtl.dat" ]
- outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
- }
-
- bundle_data("base_unittests_bundle_data]") {
- sources = [ "test/data" ]
- outputs = [
- "{{bundle_resources_dir}}/{{source_root_relative_dir}}/" +
- "{{source_file_part}}"
- ]
- }
-
- bundle_data("material_typography_bundle_data") {
- sources = [
- "src/MaterialTypography.bundle/Roboto-Bold.ttf",
- "src/MaterialTypography.bundle/Roboto-Italic.ttf",
- "src/MaterialTypography.bundle/Roboto-Regular.ttf",
- "src/MaterialTypography.bundle/Roboto-Thin.ttf",
- ]
- outputs = [
- "{{bundle_resources_dir}}/MaterialTypography.bundle/"
- "{{source_file_part}}"
- ]
- }
-)";
-
-Value RunBundleData(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kBundleData, scope, function, args,
- block, err);
-}
-
-// create_bundle ---------------------------------------------------------------
-
-const char kCreateBundle[] = "create_bundle";
-const char kCreateBundle_HelpShort[] =
- "create_bundle: [iOS/macOS] Build an iOS or macOS bundle.";
-const char kCreateBundle_Help[] =
- R"(create_bundle: [ios/macOS] Build an iOS or macOS bundle.
-
- This target generates an iOS or macOS bundle (which is a directory with a
- well-know structure). This target does not define any sources, instead they
- are computed from all "bundle_data" target this one depends on transitively
- (the recursion stops at "create_bundle" targets).
-
- The "bundle_*_dir" are be used for the expansion of {{bundle_*_dir}} rules in
- "bundle_data" outputs. The properties are optional but must be defined if any
- of the "bundle_data" target use them.
-
- This target can be used on all platforms though it is designed only to
- generate iOS or macOS bundle. In cross-platform projects, it is advised to put
- it behind iOS/macOS conditionals.
-
- If a create_bundle is specified as a data_deps for another target, the bundle
- is considered a leaf, and its public and private dependencies will not
- contribute to any data or data_deps. Required runtime dependencies should be
- placed in the bundle. A create_bundle can declare its own explicit data and
- data_deps, however.
-
-Code signing
-
- Some bundle needs to be code signed as part of the build (on iOS all
- application needs to be code signed to run on a device). The code signature
- can be configured via the code_signing_script variable.
-
- If set, code_signing_script is the path of a script that invoked after all
- files have been moved into the bundle. The script must not change any file in
- the bundle, but may add new files.
-
- If code_signing_script is defined, then code_signing_outputs must also be
- defined and non-empty to inform when the script needs to be re-run. The
- code_signing_args will be passed as is to the script (so path have to be
- rebased) and additional inputs may be listed with the variable
- code_signing_sources.
-
-Variables
-
- bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
- bundle_executable_dir, bundle_deps_filter, deps, data_deps, public_deps,
- visibility, product_type, code_signing_args, code_signing_script,
- code_signing_sources, code_signing_outputs, xcode_extra_attributes,
- xcode_test_application_name, partial_info_plist, metadata
-
-Example
-
- # Defines a template to create an application. On most platform, this is just
- # an alias for an "executable" target, but on iOS/macOS, it builds an
- # application bundle.
- template("app") {
- if (!is_ios && !is_mac) {
- executable(target_name) {
- forward_variables_from(invoker, "*")
- }
- } else {
- app_name = target_name
- gen_path = target_gen_dir
-
- action("${app_name}_generate_info_plist") {
- script = [ "//build/ios/ios_gen_plist.py" ]
- sources = [ "templates/Info.plist" ]
- outputs = [ "$gen_path/Info.plist" ]
- args = rebase_path(sources, root_build_dir) +
- rebase_path(outputs, root_build_dir)
- }
-
- bundle_data("${app_name}_bundle_info_plist") {
- public_deps = [ ":${app_name}_generate_info_plist" ]
- sources = [ "$gen_path/Info.plist" ]
- outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
- }
-
- executable("${app_name}_generate_executable") {
- forward_variables_from(invoker, "*", [
- "output_name",
- "visibility",
- ])
- output_name =
- rebase_path("$gen_path/$app_name", root_build_dir)
- }
-
- code_signing =
- defined(invoker.code_signing) && invoker.code_signing
-
- if (!is_ios || !code_signing) {
- bundle_data("${app_name}_bundle_executable") {
- public_deps = [ ":${app_name}_generate_executable" ]
- sources = [ "$gen_path/$app_name" ]
- outputs = [ "{{bundle_executable_dir}}/$app_name" ]
- }
- }
-
- create_bundle("$app_name.app") {
- product_type = "com.apple.product-type.application"
-
- if (is_ios) {
- bundle_root_dir = "$root_build_dir/$target_name"
- bundle_contents_dir = bundle_root_dir
- bundle_resources_dir = bundle_contents_dir
- bundle_executable_dir = bundle_contents_dir
-
- extra_attributes = {
- ONLY_ACTIVE_ARCH = "YES"
- DEBUG_INFORMATION_FORMAT = "dwarf"
- }
- } else {
- bundle_root_dir = "$root_build_dir/$target_name"
- bundle_contents_dir = "$bundle_root_dir/Contents"
- bundle_resources_dir = "$bundle_contents_dir/Resources"
- bundle_executable_dir = "$bundle_contents_dir/MacOS"
- }
- deps = [ ":${app_name}_bundle_info_plist" ]
- if (is_ios && code_signing) {
- deps += [ ":${app_name}_generate_executable" ]
- code_signing_script = "//build/config/ios/codesign.py"
- code_signing_sources = [
- invoker.entitlements_path,
- "$target_gen_dir/$app_name",
- ]
- code_signing_outputs = [
- "$bundle_root_dir/$app_name",
- "$bundle_root_dir/_CodeSignature/CodeResources",
- "$bundle_root_dir/embedded.mobileprovision",
- "$target_gen_dir/$app_name.xcent",
- ]
- code_signing_args = [
- "-i=" + ios_code_signing_identity,
- "-b=" + rebase_path(
- "$target_gen_dir/$app_name", root_build_dir),
- "-e=" + rebase_path(
- invoker.entitlements_path, root_build_dir),
- "-e=" + rebase_path(
- "$target_gen_dir/$app_name.xcent", root_build_dir),
- rebase_path(bundle_root_dir, root_build_dir),
- ]
- } else {
- deps += [ ":${app_name}_bundle_executable" ]
- }
- }
- }
- }
-)";
-
-Value RunCreateBundle(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kCreateBundle, scope, function, args,
- block, err);
-}
-
-// copy ------------------------------------------------------------------------
-
-const char kCopy[] = "copy";
-const char kCopy_HelpShort[] = "copy: Declare a target that copies files.";
-const char kCopy_Help[] =
- R"(copy: Declare a target that copies files.
-
-File name handling
-
- All output files must be inside the output directory of the build. You would
- generally use |$target_out_dir| or |$target_gen_dir| to reference the output
- or generated intermediate file directories, respectively.
-
- Both "sources" and "outputs" must be specified. Sources can include as many
- files as you want, but there can only be one item in the outputs list (plural
- is used for the name for consistency with other target types).
-
- If there is more than one source file, your output name should specify a
- mapping from each source file to an output file name using source expansion
- (see "gn help source_expansion"). The placeholders will look like
- "{{source_name_part}}", for example.
-
-Examples
-
- # Write a rule that copies a checked-in DLL to the output directory.
- copy("mydll") {
- sources = [ "mydll.dll" ]
- outputs = [ "$target_out_dir/mydll.dll" ]
- }
-
- # Write a rule to copy several files to the target generated files directory.
- copy("myfiles") {
- sources = [ "data1.dat", "data2.dat", "data3.dat" ]
-
- # Use source expansion to generate output files with the corresponding file
- # names in the gen dir. This will just copy each file.
- outputs = [ "$target_gen_dir/{{source_file_part}}" ]
- }
-)";
-
-Value RunCopy(const FunctionCallNode* function,
- const std::vector<Value>& args,
- Scope* scope,
- Err* err) {
- if (!EnsureNotProcessingImport(function, scope, err) ||
- !EnsureNotProcessingBuildConfig(function, scope, err))
- return Value();
- TargetGenerator::GenerateTarget(scope, function, args, functions::kCopy, err);
- return Value();
-}
-
-// executable ------------------------------------------------------------------
-
-const char kExecutable[] = "executable";
-const char kExecutable_HelpShort[] =
- "executable: Declare an executable target.";
-const char kExecutable_Help[] =
- R"(executable: Declare an executable target.
-
-Language and compilation
-)" LANGUAGE_HELP
- R"(
-
-Variables
-
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
- RUST_VARS;
-
-Value RunExecutable(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kExecutable, scope, function, args,
- block, err);
-}
-
-// group -----------------------------------------------------------------------
-
-const char kGroup[] = "group";
-const char kGroup_HelpShort[] = "group: Declare a named group of targets.";
-const char kGroup_Help[] =
- R"(group: Declare a named group of targets.
-
- This target type allows you to create meta-targets that just collect a set of
- dependencies into one named target. Groups can additionally specify configs
- that apply to their dependents.
-
-Variables
-
-)" DEPS_VARS DEPENDENT_CONFIG_VARS
-
- R"(
-Example
-
- group("all") {
- deps = [
- "//project:runner",
- "//project:unit_tests",
- ]
- }
-)";
-
-Value RunGroup(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kGroup, scope, function, args, block,
- err);
-}
-
-// loadable_module -------------------------------------------------------------
-
-const char kLoadableModule[] = "loadable_module";
-const char kLoadableModule_HelpShort[] =
- "loadable_module: Declare a loadable module target.";
-const char kLoadableModule_Help[] =
- R"(loadable_module: Declare a loadable module target.
-
- This target type allows you to create an object file that is (and can only
- be) loaded and unloaded at runtime.
-
- A loadable module will be specified on the linker line for targets listing
- the loadable module in its "deps". If you don't want this (if you don't need
- to dynamically load the library at runtime), then you should use a
- "shared_library" target type instead.
-
-Language and compilation
-)" LANGUAGE_HELP
- R"(
-
-Variables
-
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
- RUST_SHARED_VARS;
-
-Value RunLoadableModule(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kLoadableModule, scope, function, args,
- block, err);
-}
-
-// rust_library ----------------------------------------------------------------
-
-const char kRustLibrary[] = "rust_library";
-const char kRustLibrary_HelpShort[] =
- "rust_library: Declare a Rust library target.";
-const char kRustLibrary_Help[] =
- R"(rust_library: Declare a Rust library target.
-
- A Rust library is an archive containing additional rust-c provided metadata.
- These are the files produced by the rustc compiler with the `.rlib`
- extension, and are the intermediate step for most Rust-based binaries.
-
-Language and compilation
-)" LANGUAGE_HELP
- R"(
-
-Variables
-
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
- RUST_VARS;
-Value RunRustLibrary(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kRustLibrary, scope, function, args,
- block, err);
-}
-
-// shared_library --------------------------------------------------------------
-
-const char kSharedLibrary[] = "shared_library";
-const char kSharedLibrary_HelpShort[] =
- "shared_library: Declare a shared library target.";
-const char kSharedLibrary_Help[] =
- R"(shared_library: Declare a shared library target.
-
- A shared library will be specified on the linker line for targets listing the
- shared library in its "deps". If you don't want this (say you dynamically
- load the library at runtime), then you should depend on the shared library
- via "data_deps" or, on Darwin platforms, use a "loadable_module" target type
- instead.
-
-Language and compilation
-)" LANGUAGE_HELP
- R"(
-
-Variables
-
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
- RUST_SHARED_VARS;
-
-Value RunSharedLibrary(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kSharedLibrary, scope, function, args,
- block, err);
-}
-
-// source_set ------------------------------------------------------------------
-
-const char kSourceSet[] = "source_set";
-const char kSourceSet_HelpShort[] = "source_set: Declare a source set target.";
-const char kSourceSet_Help[] =
- R"(source_set: Declare a source set target.
-
- The language of a source_set target is determined by the extensions present
- in its sources.
-
-C-language source_sets
-
- A source set is a collection of sources that get compiled, but are not linked
- to produce any kind of library. Instead, the resulting object files are
- implicitly added to the linker line of all targets that depend on the source
- set.
-
- In most cases, a source set will behave like a static library, except no
- actual library file will be produced. This will make the build go a little
- faster by skipping creation of a large static library, while maintaining the
- organizational benefits of focused build targets.
-
- The main difference between a source set and a static library is around
- handling of exported symbols. Most linkers assume declaring a function
- exported means exported from the static library. The linker can then do dead
- code elimination to delete code not reachable from exported functions.
-
- A source set will not do this code elimination since there is no link step.
- This allows you to link many source sets into a shared library and have the
- "exported symbol" notation indicate "export from the final shared library and
- not from the intermediate targets." There is no way to express this concept
- when linking multiple static libraries into a shared library.
-
-Rust-language source_sets
-
- A Rust source set is a collection of sources that get passed along to the
- final target that depends on it. No compilation is performed, and the source
- files are simply added as dependencies on the eventual rustc invocation that
- would produce a binary.
-
-Variables
-
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
-
-Value RunSourceSet(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kSourceSet, scope, function, args,
- block, err);
-}
-
-// static_library --------------------------------------------------------------
-
-const char kStaticLibrary[] = "static_library";
-const char kStaticLibrary_HelpShort[] =
- "static_library: Declare a static library target.";
-const char kStaticLibrary_Help[] =
- R"(static_library: Declare a static library target.
-
- Make a ".a" / ".lib" file.
-
- If you only need the static library for intermediate results in the build,
- you should consider a source_set instead since it will skip the (potentially
- slow) step of creating the intermediate library file.
-
-Variables
-
- complete_static_lib
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
- RUST_VARS LANGUAGE_HELP;
-
-Value RunStaticLibrary(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kStaticLibrary, scope, function, args,
- block, err);
-}
-
-// target ---------------------------------------------------------------------
-
-const char kTarget[] = "target";
-const char kTarget_HelpShort[] =
- "target: Declare an target with the given programmatic type.";
-const char kTarget_Help[] =
- R"(target: Declare an target with the given programmatic type.
-
- target(target_type_string, target_name_string) { ... }
-
- The target() function is a way to invoke a built-in target or template with a
- type determined at runtime. This is useful for cases where the type of a
- target might not be known statically.
-
- Only templates and built-in target functions are supported for the
- target_type_string parameter. Arbitrary functions, configs, and toolchains
- are not supported.
-
- The call:
- target("source_set", "doom_melon") {
- Is equivalent to:
- source_set("doom_melon") {
-
-Example
-
- if (foo_build_as_shared) {
- my_type = "shared_library"
- } else {
- my_type = "source_set"
- }
-
- target(my_type, "foo") {
- ...
- }
-)";
-Value RunTarget(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- if (args.size() != 2) {
- *err = Err(function, "Expected two arguments.", "Try \"gn help target\".");
- return Value();
- }
-
- // The first argument must be a string (the target type). Don't type-check
- // the second argument since the target-specific function will do that.
- if (!args[0].VerifyTypeIs(Value::STRING, err))
- return Value();
- const std::string& target_type = args[0].string_value();
-
- // The rest of the args are passed to the function.
- std::vector<Value> sub_args(args.begin() + 1, args.end());
-
- // Run a template if it is one.
- const Template* templ = scope->GetTemplate(target_type);
- if (templ)
- return templ->Invoke(scope, function, target_type, sub_args, block, err);
-
- // Otherwise, assume the target is a built-in target type.
- return ExecuteGenericTarget(target_type.c_str(), scope, function, sub_args,
- block, err);
-}
-
-const char kGeneratedFile[] = "generated_file";
-const char kGeneratedFile_HelpShort[] =
- "generated_file: Declare a generated_file target.";
-const char kGeneratedFile_Help[] =
- R"(generated_file: Declare a generated_file target.
-
- Writes data value(s) to disk on resolution. This target type mirrors some
- functionality of the write_file() function, but also provides the ability to
- collect metadata from its dependencies on resolution rather than writing out
- parse time.
-
- The `outputs` variable is required to be a list with a single element,
- specifying the intended location of the output file.
-
- The `output_conversion` variable specified the format to write the
- value. See `gn help output_conversion`.
-
- One of `contents` or `data_keys` must be specified; use of `data` will write
- the contents of that value to file, while use of `data_keys` will trigger a
- metadata collection walk based on the dependencies of the target and the
- optional values of the `rebase` and `walk_keys` variables. See
- `gn help metadata`.
-
- Collected metadata, if specified, will be returned in postorder of
- dependencies. See the example for details.
-
-Example (metadata collection)
-
- Given the following targets defined in //base/BUILD.gn, where A depends on B
- and B depends on C and D:
-
- group("a") {
- metadata = {
- doom_melon = [ "enable" ]
- my_files = [ "foo.cpp" ]
-
- // Note: this is functionally equivalent to not defining `my_barrier`
- // at all in this target's metadata.
- my_barrier = [ "" ]
- }
-
- deps = [ ":b" ]
- }
-
- group("b") {
- metadata = {
- my_files = [ "bar.cpp" ]
- my_barrier = [ ":c" ]
- }
-
- deps = [ ":c", ":d" ]
- }
-
- group("c") {
- metadata = {
- doom_melon = [ "disable" ]
- my_files = [ "baz.cpp" ]
- }
- }
-
- group("d") {
- metadata = {
- my_files = [ "missing.cpp" ]
- }
- }
-
- If the following generated_file target is defined:
-
- generated_file("my_files_metadata") {
- outputs = [ "$root_build_dir/my_files.json" ]
- data_keys = [ "my_files" ]
-
- deps = [ "//base:a" ]
- }
-
- The following will be written to "$root_build_dir/my_files.json" (less the
- comments):
- [
- "baz.cpp", // from //base:c via //base:b
- "missing.cpp" // from //base:d via //base:b
- "bar.cpp", // from //base:b via //base:a
- "foo.cpp", // from //base:a
- ]
-
- Alternatively, as an example of using walk_keys, if the following
- generated_file target is defined:
-
- generated_file("my_files_metadata") {
- outputs = [ "$root_build_dir/my_files.json" ]
- data_keys = [ "my_files" ]
- walk_keys = [ "my_barrier" ]
-
- deps = [ "//base:a" ]
- }
-
- The following will be written to "$root_build_dir/my_files.json" (again less
- the comments):
- [
- "baz.cpp", // from //base:c via //base:b
- "bar.cpp", // from //base:b via //base:a
- "foo.cpp", // from //base:a
- ]
-
- If `rebase` is used in the following generated_file target:
-
- generated_file("my_files_metadata") {
- outputs = [ "$root_build_dir/my_files.json" ]
- data_keys = [ "my_files" ]
- walk_keys = [ "my_barrier" ]
- rebase = root_build_dir
-
- deps = [ "//base:a" ]
- }
-
- The following will be written to "$root_build_dir/my_files.json" (again less
- the comments) (assuming root_build_dir = "//out"):
- [
- "../base/baz.cpp", // from //base:c via //base:b
- "../base/bar.cpp", // from //base:b via //base:a
- "../base/foo.cpp", // from //base:a
- ]
-
-
-Variables
-
- contents
- data_keys
- rebase
- walk_keys
- output_conversion
-)" DEPS_VARS DEPENDENT_CONFIG_VARS;
-
-Value RunGeneratedFile(Scope* scope,
- const FunctionCallNode* function,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) {
- return ExecuteGenericTarget(functions::kGeneratedFile, scope, function, args,
- block, err);
-}
-
-} // namespace functions
diff --git a/gn/tools/gn/functions_target_rust_unittest.cc b/gn/tools/gn/functions_target_rust_unittest.cc
deleted file mode 100644
index 37bd8b1f3ea..00000000000
--- a/gn/tools/gn/functions_target_rust_unittest.cc
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/config.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using RustFunctionsTarget = TestWithScheduler;
-
-// Checks that the appropriate crate type is used.
-TEST_F(RustFunctionsTarget, CrateName) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput exe_input(
- "executable(\"foo\") {\n"
- " crate_name = \"foo_crate\"\n"
- " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(exe_input.has_error());
- Err err;
- exe_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
- "foo_crate");
-
- TestParseInput lib_input(
- "executable(\"foo\") {\n"
- " sources = [ \"lib.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(lib_input.has_error());
- err = Err();
- lib_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
- "foo")
- << item_collector.back()->AsTarget()->rust_values().crate_name();
-}
-
-// Checks that the appropriate crate root is found.
-TEST_F(RustFunctionsTarget, CrateRootFind) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput normal_input(
- "executable(\"foo\") {\n"
- " crate_root = \"foo.rs\""
- " sources = [ \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(normal_input.has_error());
- Err err;
- normal_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(
- item_collector.back()->AsTarget()->rust_values().crate_root().value(),
- "/foo.rs");
-
- TestParseInput normal_shlib_input(
- "shared_library(\"foo\") {\n"
- " crate_root = \"foo.rs\""
- " crate_type = \"dylib\"\n"
- " sources = [ \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(normal_shlib_input.has_error());
- err = Err();
- normal_shlib_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(
- item_collector.back()->AsTarget()->rust_values().crate_root().value(),
- "/foo.rs");
-
- TestParseInput exe_input(
- "executable(\"foo\") {\n"
- " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(exe_input.has_error());
- err = Err();
- exe_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(
- item_collector.back()->AsTarget()->rust_values().crate_root().value(),
- "/main.rs");
-
- TestParseInput lib_input(
- "rust_library(\"libfoo\") {\n"
- " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(lib_input.has_error());
- err = Err();
- lib_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(
- item_collector.back()->AsTarget()->rust_values().crate_root().value(),
- "/lib.rs");
-
- TestParseInput singlesource_input(
- "executable(\"bar\") {\n"
- " sources = [ \"bar.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(singlesource_input.has_error());
- err = Err();
- singlesource_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(
- item_collector.back()->AsTarget()->rust_values().crate_root().value(),
- "/bar.rs");
-
- TestParseInput error_input(
- "rust_library(\"foo\") {\n"
- " sources = [ \"foo.rs\", \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(error_input.has_error());
- err = Err();
- error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Missing \"crate_root\" and missing \"lib.rs\" in sources.",
- err.message());
-
- TestParseInput nosources_input(
- "executable(\"bar\") {\n"
- " crate_root = \"bar.rs\"\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(nosources_input.has_error());
- err = Err();
- nosources_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(
- item_collector.back()->AsTarget()->rust_values().crate_root().value(),
- "/bar.rs");
-}
-
-// Checks that the appropriate crate type is used.
-TEST_F(RustFunctionsTarget, CrateTypeSelection) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput lib_input(
- "shared_library(\"libfoo\") {\n"
- " crate_type = \"dylib\"\n"
- " sources = [ \"lib.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(lib_input.has_error());
- Err err;
- lib_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
- RustValues::CRATE_DYLIB);
-
- TestParseInput exe_non_default_input(
- "executable(\"foo\") {\n"
- " crate_type = \"rlib\"\n"
- " sources = [ \"main.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(exe_non_default_input.has_error());
- err = Err();
- exe_non_default_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
- RustValues::CRATE_RLIB);
-
- TestParseInput lib_error_input(
- "shared_library(\"foo\") {\n"
- " crate_type = \"bad\"\n"
- " sources = [ \"lib.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(lib_error_input.has_error());
- err = Err();
- lib_error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Inadmissible crate type \"bad\".", err.message()) << err.message();
-
- TestParseInput lib_missing_error_input(
- "shared_library(\"foo\") {\n"
- " sources = [ \"lib.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(lib_missing_error_input.has_error());
- err = Err();
- lib_missing_error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Must set \"crate_type\" on a Rust \"shared_library\".",
- err.message());
-}
-
-// Checks that the appropriate config values are propagated.
-TEST_F(RustFunctionsTarget, ConfigValues) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput exe_input(
- "config(\"foo\") {\n"
- " rustflags = [ \"-Cdebuginfo=2\" ]\n"
- " rustenv = [ \"RUST_BACKTRACE=1\" ]"
- "}\n");
- ASSERT_FALSE(exe_input.has_error());
- Err err;
- exe_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags().size(),
- 1U);
- EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags()[0],
- "-Cdebuginfo=2");
- EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv().size(),
- 1U);
- EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv()[0],
- "RUST_BACKTRACE=1");
-}
-
-// Checks that set_defaults works properly.
-TEST_F(RustFunctionsTarget, SetDefaults) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput exe_input(
- "config(\"foo\") {\n"
- " rustflags = [ \"-Cdebuginfo=2\" ]\n"
- " rustenv = [ \"RUST_BACKTRACE=1\" ]"
- "}\n"
- "set_defaults(\"rust_library\") {\n"
- " configs = [ \":foo\" ]\n"
- "}\n");
- ASSERT_FALSE(exe_input.has_error());
- Err err;
- exe_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message() << err.message();
-
- EXPECT_EQ(setup.scope()
- ->GetTargetDefaults("rust_library")
- ->GetValue("configs")
- ->type(),
- Value::LIST);
- EXPECT_EQ(setup.scope()
- ->GetTargetDefaults("rust_library")
- ->GetValue("configs")
- ->list_value()
- .size(),
- 1U);
- EXPECT_EQ(setup.scope()
- ->GetTargetDefaults("rust_library")
- ->GetValue("configs")
- ->list_value()[0]
- .type(),
- Value::STRING);
- EXPECT_EQ(setup.scope()
- ->GetTargetDefaults("rust_library")
- ->GetValue("configs")
- ->list_value()[0]
- .string_value(),
- ":foo");
-}
-
-// Checks that the dition gets propagated correctly.
-TEST_F(RustFunctionsTarget, Edition) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput lib_input(
- "shared_library(\"libfoo\") {\n"
- " crate_type = \"dylib\"\n"
- " sources = [ \"lib.rs\" ]\n"
- " edition = \"2018\""
- "}\n");
- ASSERT_FALSE(lib_input.has_error());
- Err err;
- lib_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
- ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().edition(), "2018");
-
- TestParseInput error_input(
- "shared_library(\"foo\") {\n"
- " crate_type = \"dylib\"\n"
- " sources = [ \"lib.rs\" ]\n"
- "}\n");
- ASSERT_FALSE(error_input.has_error());
- err = Err();
- error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Missing \"edition\" in Rust target.", err.message())
- << err.message();
-}
-
-// Checks aliased_deps parsing.
-TEST_F(RustFunctionsTarget, AliasedDeps) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput exe_input(
- "executable(\"foo\") {\n"
- " sources = [ \"main.rs\" ]\n"
- " deps = [ \"//bar\", \"//baz\" ]\n"
- " edition = \"2018\""
- " aliased_deps = {\n"
- " bar_renamed = \"//bar\"\n"
- " baz_renamed = \"//baz:baz\"\n"
- " }\n"
- "}\n");
- ASSERT_FALSE(exe_input.has_error());
- Err err;
- exe_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ(
- item_collector.back()->AsTarget()->rust_values().aliased_deps().size(),
- 2U);
-}
-
-TEST_F(RustFunctionsTarget, PublicConfigs) {
- TestWithScope setup;
-
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
- setup.scope()->set_source_dir(SourceDir("/"));
-
- TestParseInput exe_input(
- "config(\"bar\") {\n"
- " defines = [ \"DOOM_MELON\" ]"
- "}\n"
- "executable(\"foo\") {\n"
- " crate_name = \"foo_crate\"\n"
- " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
- " edition = \"2018\""
- " public_configs = [ \":bar\" ]"
- "}\n");
- ASSERT_FALSE(exe_input.has_error());
- Err err;
- exe_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-}
diff --git a/gn/tools/gn/functions_target_unittest.cc b/gn/tools/gn/functions_target_unittest.cc
deleted file mode 100644
index 40c6fde852e..00000000000
--- a/gn/tools/gn/functions_target_unittest.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// 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 "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using FunctionsTarget = TestWithScheduler;
-
-// Checks that we find unused identifiers in targets.
-TEST_F(FunctionsTarget, CheckUnused) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
-
- // Test a good one first.
- TestParseInput good_input(
- "source_set(\"foo\") {\n"
- "}\n");
- ASSERT_FALSE(good_input.has_error());
- Err err;
- good_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- // Test a source set with an unused variable.
- TestParseInput source_set_input(
- "source_set(\"foo\") {\n"
- " unused = 5\n"
- "}\n");
- ASSERT_FALSE(source_set_input.has_error());
- err = Err();
- source_set_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
-}
-
-// Checks that we find uses of identifiers marked as not needed.
-TEST_F(FunctionsTarget, CheckNotNeeded) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
-
- TestParseInput nonscoped_input(
- "source_set(\"foo\") {\n"
- " a = 1\n"
- " not_needed([ \"a\" ])\n"
- "}\n");
- ASSERT_FALSE(nonscoped_input.has_error());
- Err err;
- nonscoped_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- TestParseInput scoped_input(
- "source_set(\"foo\") {\n"
- " a = {x = 1 y = 2}\n"
- " not_needed(a, \"*\")\n"
- "}\n");
- ASSERT_FALSE(scoped_input.has_error());
- err = Err();
- scoped_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- TestParseInput exclusion_input(
- "source_set(\"foo\") {\n"
- " x = 1\n"
- " y = 2\n"
- " not_needed(\"*\", [ \"y\" ])\n"
- "}\n");
- ASSERT_FALSE(exclusion_input.has_error());
- err = Err();
- exclusion_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error()) << err.message();
- EXPECT_EQ("Assignment had no effect.", err.message());
-
- TestParseInput error_input(
- "source_set(\"foo\") {\n"
- " a = {x = 1 y = 2}\n"
- " not_needed(a, [ \"x \"], [ \"y\" ])\n"
- "}\n");
- ASSERT_FALSE(error_input.has_error());
- err = Err();
- error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Not supported with a variable list.", err.message());
-
- TestParseInput argcount_error_input(
- "source_set(\"foo\") {\n"
- " not_needed()\n"
- "}\n");
- ASSERT_FALSE(argcount_error_input.has_error());
- err = Err();
- argcount_error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Wrong number of arguments.", err.message());
-
- TestParseInput scope_error_input(
- "source_set(\"foo\") {\n"
- " a = {x = 1 y = 2}\n"
- " not_needed(a)\n"
- "}\n");
- ASSERT_FALSE(scope_error_input.has_error());
- err = Err();
- scope_error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Wrong number of arguments.", err.message());
-
- TestParseInput string_error_input(
- "source_set(\"foo\") {\n"
- " not_needed(\"*\", {}, \"*\")\n"
- "}\n");
- ASSERT_FALSE(string_error_input.has_error());
- err = Err();
- string_error_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Wrong number of arguments.", err.message());
-
- TestParseInput template_input(
- R"(# Test that not_needed() propagates through templates correctly;
- # no error should arise from not using "a".
- template("inner_templ") {
- source_set(target_name) {
- not_needed(invoker, [ "a" ])
- }
- }
- template("outer_templ") {
- inner_templ(target_name) {
- forward_variables_from(invoker, "*")
- }
- }
- outer_templ("foo") {
- a = 1
- })");
- ASSERT_FALSE(template_input.has_error());
- err = Err();
- template_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-}
-
-// Checks that the defaults applied to a template invoked by target() use
-// the name of the template, rather than the string "target" (which is the
-// name of the actual function being called).
-TEST_F(FunctionsTarget, TemplateDefaults) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
-
- // Test a good one first.
- TestParseInput good_input(
- R"(# Make a template with defaults set.
- template("my_templ") {
- source_set(target_name) {
- forward_variables_from(invoker, "*")
- }
- }
- set_defaults("my_templ") {
- default_value = 1
- }
-
- # Invoke the template with target(). This will fail to execute if the
- # defaults were not set properly, because "default_value" won't exist.
- target("my_templ", "foo") {
- print(default_value)
- })");
- ASSERT_FALSE(good_input.has_error());
- Err err;
- good_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-}
-
-// Checks that we find unused identifiers in targets.
-TEST_F(FunctionsTarget, MixedSourceError) {
- TestWithScope setup;
-
- // The target generator needs a place to put the targets or it will fail.
- Scope::ItemVector item_collector;
- setup.scope()->set_item_collector(&item_collector);
-
- // Test a good one first.
- TestParseInput good_input(
- "source_set(\"foo\") {\n"
- " sources = [ \"cpp.cc\", \"rust.rs\" ]"
- "}\n");
- ASSERT_FALSE(good_input.has_error());
- Err err;
- good_input.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- ASSERT_EQ(err.message(), "More than one language used in target sources.");
-} \ No newline at end of file
diff --git a/gn/tools/gn/functions_unittest.cc b/gn/tools/gn/functions_unittest.cc
deleted file mode 100644
index 8725c2ecd9d..00000000000
--- a/gn/tools/gn/functions_unittest.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-// 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 "tools/gn/functions.h"
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-TEST(Functions, Defined) {
- TestWithScope setup;
-
- FunctionCallNode function_call;
- Err err;
-
- // Test an undefined identifier.
- Token undefined_token(Location(), Token::IDENTIFIER, "undef");
- ListNode args_list_identifier_undefined;
- args_list_identifier_undefined.append_item(
- std::make_unique<IdentifierNode>(undefined_token));
- Value result = functions::RunDefined(setup.scope(), &function_call,
- &args_list_identifier_undefined, &err);
- ASSERT_EQ(Value::BOOLEAN, result.type());
- EXPECT_FALSE(result.boolean_value());
-
- // Define a value that's itself a scope value.
- const char kDef[] = "def"; // Defined variable name.
- setup.scope()->SetValue(
- kDef, Value(nullptr, std::make_unique<Scope>(setup.scope())), nullptr);
-
- // Test the defined identifier.
- Token defined_token(Location(), Token::IDENTIFIER, kDef);
- ListNode args_list_identifier_defined;
- args_list_identifier_defined.append_item(
- std::make_unique<IdentifierNode>(defined_token));
- result = functions::RunDefined(setup.scope(), &function_call,
- &args_list_identifier_defined, &err);
- ASSERT_EQ(Value::BOOLEAN, result.type());
- EXPECT_TRUE(result.boolean_value());
-
- // Should also work by passing an accessor node so you can do
- // "defined(def.foo)" to see if foo is defined on the def scope.
- std::unique_ptr<AccessorNode> undef_accessor =
- std::make_unique<AccessorNode>();
- undef_accessor->set_base(defined_token);
- undef_accessor->set_member(std::make_unique<IdentifierNode>(undefined_token));
- ListNode args_list_accessor_defined;
- args_list_accessor_defined.append_item(std::move(undef_accessor));
- result = functions::RunDefined(setup.scope(), &function_call,
- &args_list_accessor_defined, &err);
- ASSERT_EQ(Value::BOOLEAN, result.type());
- EXPECT_FALSE(result.boolean_value());
-}
-
-// Tests that an error is thrown when a {} is supplied to a function that
-// doesn't take one.
-TEST(Functions, FunctionsWithBlock) {
- TestWithScope setup;
- Err err;
-
- // No scope to print() is OK.
- TestParseInput print_no_scope("print(6)");
- EXPECT_FALSE(print_no_scope.has_error());
- Value result = print_no_scope.parsed()->Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Passing a scope should pass parsing (it doesn't know about what kind of
- // function it is) and then throw an error during execution.
- TestParseInput print_with_scope("print(foo) {}");
- EXPECT_FALSE(print_with_scope.has_error());
- result = print_with_scope.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- err = Err();
-
- // defined() is a special function so test it separately.
- TestParseInput defined_no_scope("defined(foo)");
- EXPECT_FALSE(defined_no_scope.has_error());
- result = defined_no_scope.parsed()->Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // A block to defined should fail.
- TestParseInput defined_with_scope("defined(foo) {}");
- EXPECT_FALSE(defined_with_scope.has_error());
- result = defined_with_scope.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
-}
-
-TEST(Functions, SplitList) {
- TestWithScope setup;
-
- TestParseInput input(
- // Empty input with varying result items.
- "out1 = split_list([], 1)\n"
- "out2 = split_list([], 3)\n"
- "print(\"empty = $out1 $out2\")\n"
-
- // One item input.
- "out3 = split_list([1], 1)\n"
- "out4 = split_list([1], 2)\n"
- "print(\"one = $out3 $out4\")\n"
-
- // Multiple items.
- "out5 = split_list([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)\n"
- "print(\"many = $out5\")\n"
-
- // Rounding.
- "out6 = split_list([1, 2, 3, 4, 5, 6], 4)\n"
- "print(\"rounding = $out6\")\n");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ(
- "empty = [[]] [[], [], []]\n"
- "one = [[1]] [[1], []]\n"
- "many = [[1, 2, 3, 4, 5], [6, 7, 8, 9]]\n"
- "rounding = [[1, 2], [3, 4], [5], [6]]\n",
- setup.print_output());
-}
-
-TEST(Functions, StringReplace) {
- TestWithScope setup;
-
- TestParseInput input(
- // Replace all occurrences of string.
- "out1 = string_replace(\"abbcc\", \"b\", \"d\")\n"
- "print(out1)\n"
-
- // Replace only the first occurrence.
- "out2 = string_replace(\"abbcc\", \"b\", \"d\", 1)\n"
- "print(out2)\n"
-
- // Duplicate string to be replaced.
- "out3 = string_replace(\"abbcc\", \"b\", \"bb\")\n"
- "print(out3)\n"
-
- // Handle overlapping occurrences.
- "out4 = string_replace(\"aaa\", \"aa\", \"b\")\n"
- "print(out4)\n");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ(
- "addcc\n"
- "adbcc\n"
- "abbbbcc\n"
- "ba\n",
- setup.print_output());
-}
-
-TEST(Functions, DeclareArgs) {
- TestWithScope setup;
- Err err;
-
- // It is not legal to read the value of an argument declared in a
- // declare_args() from inside the call, but outside the call and in
- // a separate call should work.
-
- TestParseInput reading_from_same_call(R"(
- declare_args() {
- foo = true
- bar = foo
- })");
- reading_from_same_call.parsed()->Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
-
- TestParseInput reading_from_outside_call(R"(
- declare_args() {
- foo = true
- }
-
- bar = foo
- assert(bar)
- )");
- err = Err();
- reading_from_outside_call.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error());
-
- TestParseInput reading_from_different_call(R"(
- declare_args() {
- foo = true
- }
-
- declare_args() {
- bar = foo
- }
-
- assert(bar)
- )");
- err = Err();
- TestWithScope setup2;
- reading_from_different_call.parsed()->Execute(setup2.scope(), &err);
- ASSERT_FALSE(err.has_error());
-}
-
-TEST(Functions, NotNeeded) {
- TestWithScope setup;
-
- TestParseInput input("not_needed({ a = 1 }, \"*\")");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error())
- << err.message() << err.location().Describe(true);
-}
diff --git a/gn/tools/gn/general_tool.cc b/gn/tools/gn/general_tool.cc
deleted file mode 100644
index a0f285ddd91..00000000000
--- a/gn/tools/gn/general_tool.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/general_tool.h"
-#include "tools/gn/target.h"
-
-const char* GeneralTool::kGeneralToolStamp = "stamp";
-const char* GeneralTool::kGeneralToolCopy = "copy";
-const char* GeneralTool::kGeneralToolCopyBundleData = "copy_bundle_data";
-const char* GeneralTool::kGeneralToolCompileXCAssets = "compile_xcassets";
-const char* GeneralTool::kGeneralToolAction = "action";
-
-GeneralTool::GeneralTool(const char* n) : Tool(n) {
- CHECK(ValidateName(n));
-}
-
-GeneralTool::~GeneralTool() = default;
-
-GeneralTool* GeneralTool::AsGeneral() {
- return this;
-}
-const GeneralTool* GeneralTool::AsGeneral() const {
- return this;
-}
-
-bool GeneralTool::ValidateName(const char* name) const {
- return name == kGeneralToolStamp || name == kGeneralToolCopy ||
- name == kGeneralToolCopyBundleData ||
- name == kGeneralToolCompileXCAssets || name == kGeneralToolAction;
-}
-
-void GeneralTool::SetComplete() {
- SetToolComplete();
-}
-
-bool GeneralTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
- // Initialize default vars.
- return Tool::InitTool(scope, toolchain, err);
-}
-
-bool GeneralTool::ValidateSubstitution(const Substitution* sub_type) const {
- if (name_ == kGeneralToolStamp || name_ == kGeneralToolAction)
- return IsValidToolSubstitution(sub_type);
- else if (name_ == kGeneralToolCopy || name_ == kGeneralToolCopyBundleData)
- return IsValidCopySubstitution(sub_type);
- else if (name_ == kGeneralToolCompileXCAssets)
- return IsValidCompileXCassetsSubstitution(sub_type);
- NOTREACHED();
- return false;
-}
diff --git a/gn/tools/gn/general_tool.h b/gn/tools/gn/general_tool.h
deleted file mode 100644
index 827a48dca06..00000000000
--- a/gn/tools/gn/general_tool.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_GENERAL_TOOL_H_
-#define TOOLS_GN_GENERAL_TOOL_H_
-
-#include <string>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_pattern.h"
-#include "tools/gn/tool.h"
-
-class GeneralTool : public Tool {
- public:
- // General tools
- static const char* kGeneralToolStamp;
- static const char* kGeneralToolCopy;
- static const char* kGeneralToolAction;
-
- // Platform-specific tools
- static const char* kGeneralToolCopyBundleData;
- static const char* kGeneralToolCompileXCAssets;
-
- GeneralTool(const char* n);
- ~GeneralTool();
-
- // Manual RTTI and required functions ---------------------------------------
-
- bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
- bool ValidateName(const char* name) const override;
- void SetComplete() override;
- bool ValidateSubstitution(const Substitution* sub_type) const override;
-
- GeneralTool* AsGeneral() override;
- const GeneralTool* AsGeneral() const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(GeneralTool);
-};
-
-#endif // TOOLS_GN_GENERAL_TOOL_H_
diff --git a/gn/tools/gn/generated_file_target_generator.cc b/gn/tools/gn/generated_file_target_generator.cc
deleted file mode 100644
index 29712216cf5..00000000000
--- a/gn/tools/gn/generated_file_target_generator.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/generated_file_target_generator.h"
-
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/target.h"
-#include "tools/gn/variables.h"
-
-GeneratedFileTargetGenerator::GeneratedFileTargetGenerator(
- Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Target::OutputType type,
- Err* err)
- : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
-
-GeneratedFileTargetGenerator::~GeneratedFileTargetGenerator() = default;
-
-void GeneratedFileTargetGenerator::DoRun() {
- target_->set_output_type(output_type_);
-
- if (!FillOutputs(false))
- return;
- if (target_->action_values().outputs().list().size() != 1) {
- *err_ = Err(
- function_call_, "generated_file target must have exactly one output.",
- "You must specify exactly one value in the \"outputs\" array for the "
- "destination of the write\n(see \"gn help generated_file\").");
- return;
- }
-
- if (!FillContents())
- return;
- if (!FillDataKeys())
- return;
-
- // One of data and data_keys should be defined.
- if (!contents_defined_ && !data_keys_defined_) {
- *err_ = Err(
- function_call_, "Either contents or data_keys should be set.",
- "The generated_file target requires either the \"contents\" variable "
- "or the \"data_keys\" variable be set. See \"gn help "
- "generated_file\".");
- return;
- }
-
- if (!FillRebase())
- return;
- if (!FillWalkKeys())
- return;
-
- if (!FillOutputConversion())
- return;
-}
-
-bool GeneratedFileTargetGenerator::FillContents() {
- const Value* value = scope_->GetValue(variables::kWriteValueContents, true);
- if (!value)
- return true;
- target_->set_contents(*value);
- contents_defined_ = true;
- return true;
-}
-
-bool GeneratedFileTargetGenerator::IsMetadataCollectionTarget(
- const base::StringPiece& variable,
- const ParseNode* origin) {
- if (contents_defined_) {
- *err_ =
- Err(origin, variable.as_string() + " won't be used.",
- "\"contents\" is defined on this target, and so setting " +
- variable.as_string() +
- " will have no effect as no metdata collection will occur.");
- return false;
- }
- return true;
-}
-
-bool GeneratedFileTargetGenerator::FillOutputConversion() {
- const Value* value =
- scope_->GetValue(variables::kWriteOutputConversion, true);
- if (!value) {
- target_->set_output_conversion(Value(function_call_, ""));
- return true;
- }
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- // Otherwise, the value itself will be checked when the conversion is done.
- target_->set_output_conversion(*value);
- return true;
-}
-
-bool GeneratedFileTargetGenerator::FillRebase() {
- const Value* value = scope_->GetValue(variables::kRebase, true);
- if (!value)
- return true;
- if (!IsMetadataCollectionTarget(variables::kRebase, value->origin()))
- return false;
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- if (value->string_value().empty())
- return true; // Treat empty string as the default and do nothing.
-
- const BuildSettings* build_settings = scope_->settings()->build_settings();
- SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
- *value, err_, build_settings->root_path_utf8());
- if (err_->has_error())
- return false;
-
- target_->set_rebase(dir);
- return true;
-}
-
-bool GeneratedFileTargetGenerator::FillDataKeys() {
- const Value* value = scope_->GetValue(variables::kDataKeys, true);
- if (!value)
- return true;
- if (!IsMetadataCollectionTarget(variables::kDataKeys, value->origin()))
- return false;
- if (!value->VerifyTypeIs(Value::LIST, err_))
- return false;
-
- for (const Value& v : value->list_value()) {
- // Keys must be strings.
- if (!v.VerifyTypeIs(Value::STRING, err_))
- return false;
- target_->data_keys().push_back(v.string_value());
- }
-
- data_keys_defined_ = true;
- return true;
-}
-
-bool GeneratedFileTargetGenerator::FillWalkKeys() {
- const Value* value = scope_->GetValue(variables::kWalkKeys, true);
- // If we define this and contents, that's an error.
- if (value &&
- !IsMetadataCollectionTarget(variables::kWalkKeys, value->origin()))
- return false;
-
- // If we don't define it, we want the default value which is a list
- // containing the empty string.
- if (!value) {
- target_->walk_keys().push_back("");
- return true;
- }
-
- // Otherwise, pull and validate the specified value.
- if (!value->VerifyTypeIs(Value::LIST, err_))
- return false;
- for (const Value& v : value->list_value()) {
- // Keys must be strings.
- if (!v.VerifyTypeIs(Value::STRING, err_))
- return false;
- target_->walk_keys().push_back(v.string_value());
- }
- return true;
-}
diff --git a/gn/tools/gn/generated_file_target_generator.h b/gn/tools/gn/generated_file_target_generator.h
deleted file mode 100644
index 206555f9f8d..00000000000
--- a/gn/tools/gn/generated_file_target_generator.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
-#define TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target.h"
-#include "tools/gn/target_generator.h"
-
-// Collects and writes specified data.
-class GeneratedFileTargetGenerator : public TargetGenerator {
- public:
- GeneratedFileTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Target::OutputType type,
- Err* err);
- ~GeneratedFileTargetGenerator() override;
-
- protected:
- void DoRun() override;
-
- private:
- bool FillGeneratedFileOutput();
- bool FillOutputConversion();
- bool FillContents();
- bool FillDataKeys();
- bool FillWalkKeys();
- bool FillRebase();
-
- // Returns false if `contents` is defined (i.e. if this target was provided
- // with explicit contents to write). Returns false otherwise, indicating that
- // it is okay to set metadata collection variables on this target.
- //
- // Should be called before FillContents().
- bool IsMetadataCollectionTarget(const base::StringPiece& variable,
- const ParseNode* origin);
-
- bool contents_defined_ = false;
- bool data_keys_defined_ = false;
-
- Target::OutputType output_type_;
-
- DISALLOW_COPY_AND_ASSIGN(GeneratedFileTargetGenerator);
-};
-
-#endif // TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/gn_main.cc b/gn/tools/gn/gn_main.cc
deleted file mode 100644
index a61a7527701..00000000000
--- a/gn/tools/gn/gn_main.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 <algorithm>
-#include <string>
-
-#include "base/command_line.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/err.h"
-#include "tools/gn/location.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "util/build_config.h"
-#include "util/msg_loop.h"
-#include "util/sys_info.h"
-
-#include "last_commit_position.h"
-
-namespace {
-
-std::vector<std::string> GetArgs(const base::CommandLine& cmdline) {
- base::CommandLine::StringVector in_args = cmdline.GetArgs();
-#if defined(OS_WIN)
- std::vector<std::string> out_args;
- for (const auto& arg : in_args)
- out_args.push_back(base::WideToUTF8(arg));
- return out_args;
-#else
- return in_args;
-#endif
-}
-
-} // namespace
-
-int main(int argc, char** argv) {
-#if defined(OS_WIN)
- base::CommandLine::set_slash_is_not_a_switch();
-#endif
- base::CommandLine::Init(argc, argv);
-
- const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
- std::vector<std::string> args = GetArgs(cmdline);
-
- std::string command;
- if (cmdline.HasSwitch("help") || cmdline.HasSwitch("h")) {
- // Make "-h" and "--help" default to help command.
- command = commands::kHelp;
- } else if (cmdline.HasSwitch(switches::kVersion)) {
- // Make "--version" print the version and exit.
- OutputString(std::string(LAST_COMMIT_POSITION) + "\n");
- exit(0);
- } else if (args.empty()) {
- // No command, print error and exit.
- Err(Location(), "No command specified.",
- "Most commonly you want \"gn gen <out_dir>\" to make a build dir.\n"
- "Or try \"gn help\" for more commands.")
- .PrintToStdout();
- return 1;
- } else {
- command = args[0];
- args.erase(args.begin());
- }
-
- const commands::CommandInfoMap& command_map = commands::GetCommands();
- commands::CommandInfoMap::const_iterator found_command =
- command_map.find(command);
-
- int retval;
- if (found_command != command_map.end()) {
- MsgLoop msg_loop;
- retval = found_command->second.runner(args);
- } else {
- Err(Location(), "Command \"" + command + "\" unknown.").PrintToStdout();
- OutputString(
- "Available commands (type \"gn help <command>\" for more details):\n");
- for (const auto& cmd : commands::GetCommands())
- PrintShortHelp(cmd.second.help_short);
-
- retval = 1;
- }
-
- exit(retval); // Don't free memory, it can be really slow!
-}
diff --git a/gn/tools/gn/group_target_generator.cc b/gn/tools/gn/group_target_generator.cc
deleted file mode 100644
index 2219e5e4e6f..00000000000
--- a/gn/tools/gn/group_target_generator.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 "tools/gn/group_target_generator.h"
-
-#include "tools/gn/target.h"
-#include "tools/gn/variables.h"
-
-GroupTargetGenerator::GroupTargetGenerator(
- Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err)
- : TargetGenerator(target, scope, function_call, err) {}
-
-GroupTargetGenerator::~GroupTargetGenerator() = default;
-
-void GroupTargetGenerator::DoRun() {
- target_->set_output_type(Target::GROUP);
- // Groups only have the default types filled in by the target generator
- // base class.
-}
diff --git a/gn/tools/gn/group_target_generator.h b/gn/tools/gn/group_target_generator.h
deleted file mode 100644
index 502f40b8db4..00000000000
--- a/gn/tools/gn/group_target_generator.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 TOOLS_GN_GROUP_TARGET_GENERATOR_H_
-#define TOOLS_GN_GROUP_TARGET_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target_generator.h"
-
-// Populates a Target with the values for a group rule.
-class GroupTargetGenerator : public TargetGenerator {
- public:
- GroupTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err);
- ~GroupTargetGenerator() override;
-
- protected:
- void DoRun() override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(GroupTargetGenerator);
-};
-
-#endif // TOOLS_GN_GROUP_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/header_checker.cc b/gn/tools/gn/header_checker.cc
deleted file mode 100644
index dca7302d679..00000000000
--- a/gn/tools/gn/header_checker.cc
+++ /dev/null
@@ -1,625 +0,0 @@
-// 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 "tools/gn/header_checker.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/containers/queue.h"
-#include "base/files/file_util.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/c_include_iterator.h"
-#include "tools/gn/config.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-#include "util/worker_pool.h"
-
-namespace {
-
-struct PublicGeneratedPair {
- PublicGeneratedPair() : is_public(false), is_generated(false) {}
- bool is_public;
- bool is_generated;
-};
-
-// This class makes InputFiles on the stack as it reads files to check. When
-// we throw an error, the Err indicates a locatin which has a pointer to
-// an InputFile that must persist as long as the Err does.
-//
-// To make this work, this function creates a clone of the InputFile managed
-// by the InputFileManager so the error can refer to something that
-// persists. This means that the current file contents will live as long as
-// the program, but this is OK since we're erroring out anyway.
-LocationRange CreatePersistentRange(const InputFile& input_file,
- const LocationRange& range) {
- InputFile* clone_input_file;
- std::vector<Token>* tokens; // Don't care about this.
- std::unique_ptr<ParseNode>* parse_root; // Don't care about this.
-
- g_scheduler->input_file_manager()->AddDynamicInput(
- input_file.name(), &clone_input_file, &tokens, &parse_root);
- clone_input_file->SetContents(input_file.contents());
-
- return LocationRange(
- Location(clone_input_file, range.begin().line_number(),
- range.begin().column_number(), -1 /* TODO(scottmg) */),
- Location(clone_input_file, range.end().line_number(),
- range.end().column_number(), -1 /* TODO(scottmg) */));
-}
-
-// Given a reverse dependency chain where the target chain[0]'s includes are
-// being used by chain[end] and not all deps are public, returns the string
-// describing the error.
-std::string GetDependencyChainPublicError(const HeaderChecker::Chain& chain) {
- std::string ret =
- "The target:\n " +
- chain[chain.size() - 1].target->label().GetUserVisibleName(false) +
- "\nis including a file from the target:\n " +
- chain[0].target->label().GetUserVisibleName(false) + "\n";
-
- // Invalid chains should always be 0 (no chain) or more than two
- // (intermediate private dependencies). 1 and 2 are impossible because a
- // target can always include headers from itself and its direct dependents.
- DCHECK(chain.size() != 1 && chain.size() != 2);
- if (chain.empty()) {
- ret += "There is no dependency chain between these targets.";
- } else {
- // Indirect dependency chain, print the chain.
- ret +=
- "\nIt's usually best to depend directly on the destination target.\n"
- "In some cases, the destination target is considered a subcomponent\n"
- "of an intermediate target. In this case, the intermediate target\n"
- "should depend publicly on the destination to forward the ability\n"
- "to include headers.\n"
- "\n"
- "Dependency chain (there may also be others):\n";
-
- for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
- ret.append(" " + chain[i].target->label().GetUserVisibleName(false));
- if (i != 0) {
- // Identify private dependencies so the user can see where in the
- // dependency chain things went bad. Don't list this for the first link
- // in the chain since direct dependencies are OK, and listing that as
- // "private" may make people feel like they need to fix it.
- if (i == static_cast<int>(chain.size()) - 1 || chain[i - 1].is_public)
- ret.append(" -->");
- else
- ret.append(" --[private]-->");
- }
- ret.append("\n");
- }
- }
- return ret;
-}
-
-// Returns true if the two targets have the same label not counting the
-// toolchain.
-bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) {
- return a->label().dir() == b->label().dir() &&
- a->label().name() == b->label().name();
-}
-
-// Returns true if the target |annotation_on| includes a friend annotation
-// that allows |is_marked_friend| as a friend.
-bool FriendMatches(const Target* annotation_on,
- const Target* is_marked_friend) {
- return LabelPattern::VectorMatches(annotation_on->friends(),
- is_marked_friend->label());
-}
-
-} // namespace
-
-HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
- const std::vector<const Target*>& targets,
- bool check_generated)
- : build_settings_(build_settings), check_generated_(check_generated),
- lock_(), task_count_cv_() {
- for (auto* target : targets)
- AddTargetToFileMap(target, &file_map_);
-}
-
-HeaderChecker::~HeaderChecker() = default;
-
-bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
- bool force_check,
- std::vector<Err>* errors) {
- FileMap files_to_check;
- for (auto* check : to_check) {
- // This function will get called with all target types, but check only
- // applies to binary targets.
- if (check->IsBinary())
- AddTargetToFileMap(check, &files_to_check);
- }
- RunCheckOverFiles(files_to_check, force_check);
-
- if (errors_.empty())
- return true;
- *errors = errors_;
- return false;
-}
-
-void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
- WorkerPool pool;
-
- for (const auto& file : files) {
- // Only check C-like source files (RC files also have includes).
- SourceFile::Type type = file.first.type();
- if (type != SourceFile::SOURCE_CPP && type != SourceFile::SOURCE_H &&
- type != SourceFile::SOURCE_C && type != SourceFile::SOURCE_M &&
- type != SourceFile::SOURCE_MM && type != SourceFile::SOURCE_RC)
- continue;
-
- if (!check_generated_) {
- // If any target marks it as generated, don't check it. We have to check
- // file_map_, which includes all known files; files only includes those
- // being checked.
- bool is_generated = false;
- for (const auto& vect_i : file_map_[file.first])
- is_generated |= vect_i.is_generated;
- if (is_generated)
- continue;
- }
-
- for (const auto& vect_i : file.second) {
- if (vect_i.target->check_includes()) {
- task_count_.Increment();
- pool.PostTask(base::BindOnce(&HeaderChecker::DoWork, this,
- vect_i.target, file.first));
- }
- }
- }
-
- // Wait for all tasks posted by this method to complete.
- std::unique_lock<std::mutex> auto_lock(lock_);
- while (!task_count_.IsZero())
- task_count_cv_.wait(auto_lock);
-}
-
-void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
- std::vector<Err> errors;
- if (!CheckFile(target, file, &errors)) {
- std::lock_guard<std::mutex> lock(lock_);
- errors_.insert(errors_.end(), errors.begin(), errors.end());
- }
-
- if (!task_count_.Decrement()) {
- // Signal |task_count_cv_| when |task_count_| becomes zero.
- std::unique_lock<std::mutex> auto_lock(lock_);
- task_count_cv_.notify_one();
- }
-}
-
-// static
-void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
- // Files in the sources have this public bit by default.
- bool default_public = target->all_headers_public();
-
- std::map<SourceFile, PublicGeneratedPair> files_to_public;
-
- // First collect the normal files, they get the default visibility. If you
- // depend on the compiled target, it should be enough to be able to include
- // the header.
- for (const auto& source : target->sources()) {
- files_to_public[source].is_public = default_public;
- }
-
- // Add in the public files, forcing them to public. This may overwrite some
- // entries, and it may add new ones.
- if (default_public) // List only used when default is not public.
- DCHECK(target->public_headers().empty());
- for (const auto& source : target->public_headers()) {
- files_to_public[source].is_public = true;
- }
-
- // Add in outputs from actions. These are treated as public (since if other
- // targets can't use them, then there wouldn't be any point in outputting).
- std::vector<SourceFile> outputs;
- target->action_values().GetOutputsAsSourceFiles(target, &outputs);
- for (const auto& output : outputs) {
- PublicGeneratedPair* pair = &files_to_public[output];
- pair->is_public = true;
- pair->is_generated = true;
- }
-
- // Add the merged list to the master list of all files.
- for (const auto& cur : files_to_public) {
- (*dest)[cur.first].push_back(
- TargetInfo(target, cur.second.is_public, cur.second.is_generated));
- }
-}
-
-bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
- const std::string& build_dir = build_settings_->build_dir().value();
- return file.value().compare(0, build_dir.size(), build_dir) == 0;
-}
-
-SourceFile HeaderChecker::SourceFileForInclude(
- const base::StringPiece& relative_file_path,
- const std::vector<SourceDir>& include_dirs,
- const InputFile& source_file,
- const LocationRange& range,
- Err* err) const {
- using base::FilePath;
-
- Value relative_file_value(nullptr, relative_file_path.as_string());
- auto it = std::find_if(
- include_dirs.begin(), include_dirs.end(),
- [relative_file_value, err, this](const SourceDir& dir) -> bool {
- SourceFile include_file =
- dir.ResolveRelativeFile(relative_file_value, err);
- return file_map_.find(include_file) != file_map_.end();
- });
-
- if (it != include_dirs.end())
- return it->ResolveRelativeFile(relative_file_value, err);
-
- return SourceFile();
-}
-
-bool HeaderChecker::CheckFile(const Target* from_target,
- const SourceFile& file,
- std::vector<Err>* errors) const {
- ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
-
- // Sometimes you have generated source files included as sources in another
- // target. These won't exist at checking time. Since we require all generated
- // files to be somewhere in the output tree, we can just check the name to
- // see if they should be skipped.
- if (!check_generated_ && IsFileInOuputDir(file))
- return true;
-
- base::FilePath path = build_settings_->GetFullPath(file);
- std::string contents;
- if (!base::ReadFileToString(path, &contents)) {
- // A missing (not yet) generated file is an acceptable problem
- // considering this code does not understand conditional includes.
- if (IsFileInOuputDir(file))
- return true;
-
- errors->emplace_back(from_target->defined_from(), "Source file not found.",
- "The target:\n " +
- from_target->label().GetUserVisibleName(false) +
- "\nhas a source file:\n " + file.value() +
- "\nwhich was not found.");
- return false;
- }
-
- InputFile input_file(file);
- input_file.SetContents(contents);
-
- std::vector<SourceDir> include_dirs;
- include_dirs.push_back(file.GetDir());
- for (ConfigValuesIterator iter(from_target); !iter.done(); iter.Next()) {
- const std::vector<SourceDir>& target_include_dirs =
- iter.cur().include_dirs();
- include_dirs.insert(include_dirs.end(), target_include_dirs.begin(),
- target_include_dirs.end());
- }
-
- size_t error_count_before = errors->size();
- CIncludeIterator iter(&input_file);
- base::StringPiece current_include;
- LocationRange range;
-
- std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
- while (iter.GetNextIncludeString(&current_include, &range)) {
- Err err;
- SourceFile include = SourceFileForInclude(current_include, include_dirs,
- input_file, range, &err);
- if (!include.is_null())
- CheckInclude(from_target, input_file, include, range,
- &no_dependency_cache, errors);
- }
-
- return errors->size() == error_count_before;
-}
-
-// If the file exists:
-// - The header must be in the public section of a target, or it must
-// be in the sources with no public list (everything is implicitly public).
-// - The dependency path to the included target must follow only public_deps.
-// - If there are multiple targets with the header in it, only one need be
-// valid for the check to pass.
-void HeaderChecker::CheckInclude(
- const Target* from_target,
- const InputFile& source_file,
- const SourceFile& include_file,
- const LocationRange& range,
- std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
- std::vector<Err>* errors) const {
- // Assume if the file isn't declared in our sources that we don't need to
- // check it. It would be nice if we could give an error if this happens, but
- // our include finder is too primitive and returns all includes, even if
- // they're in a #if not executed in the current build. In that case, it's
- // not unusual for the buildfiles to not specify that header at all.
- FileMap::const_iterator found = file_map_.find(include_file);
- if (found == file_map_.end())
- return;
-
- const TargetVector& targets = found->second;
- Chain chain; // Prevent reallocating in the loop.
-
- // If the file is unknown in the current toolchain (rather than being private
- // or in a target not visible to the current target), ignore it. This is a
- // bit of a hack to account for the fact that the include finder doesn't
- // understand the preprocessor.
- //
- // When not cross-compiling, if a platform specific header is conditionally
- // included in the build, and preprocessor conditions around #includes of
- // that match the build conditions, everything will be OK because the file
- // won't be known to GN even though the #include finder identified the file.
- //
- // Cross-compiling breaks this. When compiling Android on Linux, for example,
- // we might see both Linux and Android definitions of a target and know
- // about the union of all headers in the build. Since the #include finder
- // ignores preprocessor, we will find the Linux headers in the Android
- // build and note that a dependency from the Android target to the Linux
- // one is missing (these might even be the same target in different
- // toolchains!).
- bool present_in_current_toolchain = false;
- for (const auto& target : targets) {
- if (from_target->label().ToolchainsEqual(target.target->label())) {
- present_in_current_toolchain = true;
- break;
- }
- }
- if (!present_in_current_toolchain)
- return;
-
- // For all targets containing this file, we require that at least one be
- // a direct or public dependency of the current target, and either (1) the
- // header is public within the target, or (2) there is a friend definition
- // whitelisting the includor.
- //
- // If there is more than one target containing this header, we may encounter
- // some error cases before finding a good one. This error stores the previous
- // one encountered, which we may or may not throw away.
- Err last_error;
-
- bool found_dependency = false;
- for (const auto& target : targets) {
- // We always allow source files in a target to include headers also in that
- // target.
- const Target* to_target = target.target;
- if (to_target == from_target)
- return;
-
- bool is_permitted_chain = false;
-
- bool cached_no_dependency =
- no_dependency_cache->find(std::make_pair(to_target, from_target)) !=
- no_dependency_cache->end();
-
- bool add_to_cache = !cached_no_dependency;
-
- if (!cached_no_dependency &&
- IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
- add_to_cache = false;
-
- DCHECK(chain.size() >= 2);
- DCHECK(chain[0].target == to_target);
- DCHECK(chain[chain.size() - 1].target == from_target);
-
- found_dependency = true;
-
- bool effectively_public =
- target.is_public || FriendMatches(to_target, from_target);
-
- if (effectively_public && is_permitted_chain) {
- // This one is OK, we're done.
- last_error = Err();
- break;
- }
-
- // Diagnose the error.
- if (!effectively_public) {
- // Danger: must call CreatePersistentRange to put in Err.
- last_error = Err(CreatePersistentRange(source_file, range),
- "Including a private header.",
- "This file is private to the target " +
- target.target->label().GetUserVisibleName(false));
- } else if (!is_permitted_chain) {
- last_error = Err(CreatePersistentRange(source_file, range),
- "Can't include this header from here.",
- GetDependencyChainPublicError(chain));
- } else {
- NOTREACHED();
- }
- } else if (to_target->allow_circular_includes_from().find(
- from_target->label()) !=
- to_target->allow_circular_includes_from().end()) {
- // Not a dependency, but this include is whitelisted from the destination.
- found_dependency = true;
- last_error = Err();
- break;
- }
-
- if (add_to_cache) {
- no_dependency_cache->emplace(to_target, from_target);
- }
- }
-
- if (!found_dependency || last_error.has_error()) {
- if (!found_dependency) {
- DCHECK(!last_error.has_error());
- Err err = MakeUnreachableError(source_file, range, from_target, targets);
- errors->push_back(std::move(err));
- } else {
- // Found at least one dependency chain above, but it had an error.
- errors->push_back(std::move(last_error));
- }
- return;
- }
-
- // One thing we didn't check for is targets that expose their dependents
- // headers in their own public headers.
- //
- // Say we have A -> B -> C. If C has public_configs, everybody getting headers
- // from C should get the configs also or things could be out-of-sync. Above,
- // we check for A including C's headers directly, but A could also include a
- // header from B that in turn includes a header from C.
- //
- // There are two ways to solve this:
- // - If a public header in B includes C, force B to publicly depend on C.
- // This is possible to check, but might be super annoying because most
- // targets (especially large leaf-node targets) don't declare
- // public/private headers and you'll get lots of false positives.
- //
- // - Save the includes found in each file and actually compute the graph of
- // includes to detect when A implicitly includes C's header. This will not
- // have the annoying false positive problem, but is complex to write.
-}
-
-bool HeaderChecker::IsDependencyOf(const Target* search_for,
- const Target* search_from,
- Chain* chain,
- bool* is_permitted) const {
- if (search_for == search_from) {
- // A target is always visible from itself.
- *is_permitted = true;
- return false;
- }
-
- // Find the shortest public dependency chain.
- if (IsDependencyOf(search_for, search_from, true, chain)) {
- *is_permitted = true;
- return true;
- }
-
- // If not, try to find any dependency chain at all.
- if (IsDependencyOf(search_for, search_from, false, chain)) {
- *is_permitted = false;
- return true;
- }
-
- *is_permitted = false;
- return false;
-}
-
-bool HeaderChecker::IsDependencyOf(const Target* search_for,
- const Target* search_from,
- bool require_permitted,
- Chain* chain) const {
- // This method conducts a breadth-first search through the dependency graph
- // to find a shortest chain from search_from to search_for.
- //
- // work_queue maintains a queue of targets which need to be considered as
- // part of this chain, in the order they were first traversed.
- //
- // Each time a new transitive dependency of search_from is discovered for
- // the first time, it is added to work_queue and a "breadcrumb" is added,
- // indicating which target it was reached from when first discovered.
- //
- // Once this search finds search_for, the breadcrumbs are used to reconstruct
- // a shortest dependency chain (in reverse order) from search_from to
- // search_for.
-
- std::map<const Target*, ChainLink> breadcrumbs;
- base::queue<ChainLink> work_queue;
- work_queue.push(ChainLink(search_from, true));
-
- bool first_time = true;
- while (!work_queue.empty()) {
- ChainLink cur_link = work_queue.front();
- const Target* target = cur_link.target;
- work_queue.pop();
-
- if (target == search_for) {
- // Found it! Reconstruct the chain.
- chain->clear();
- while (target != search_from) {
- chain->push_back(cur_link);
- cur_link = breadcrumbs[target];
- target = cur_link.target;
- }
- chain->push_back(ChainLink(search_from, true));
- return true;
- }
-
- // Always consider public dependencies as possibilities.
- for (const auto& dep : target->public_deps()) {
- if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
- work_queue.push(ChainLink(dep.ptr, true));
- }
-
- if (first_time || !require_permitted) {
- // Consider all dependencies since all target paths are allowed, so add
- // in private ones. Also do this the first time through the loop, since
- // a target can include headers from its direct deps regardless of
- // public/private-ness.
- first_time = false;
- for (const auto& dep : target->private_deps()) {
- if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
- work_queue.push(ChainLink(dep.ptr, false));
- }
- }
- }
-
- return false;
-}
-
-Err HeaderChecker::MakeUnreachableError(const InputFile& source_file,
- const LocationRange& range,
- const Target* from_target,
- const TargetVector& targets) {
- // Normally the toolchains will all match, but when cross-compiling, we can
- // get targets with more than one toolchain in the list of possibilities.
- std::vector<const Target*> targets_with_matching_toolchains;
- std::vector<const Target*> targets_with_other_toolchains;
- for (const TargetInfo& candidate : targets) {
- if (candidate.target->toolchain() == from_target->toolchain())
- targets_with_matching_toolchains.push_back(candidate.target);
- else
- targets_with_other_toolchains.push_back(candidate.target);
- }
-
- // It's common when cross-compiling to have a target with the same file in
- // more than one toolchain. We could output all of them, but this is
- // generally confusing to people (most end-users won't understand toolchains
- // well).
- //
- // So delete any candidates in other toolchains that also appear in the same
- // toolchain as the from_target.
- for (int other_index = 0;
- other_index < static_cast<int>(targets_with_other_toolchains.size());
- other_index++) {
- for (const Target* cur_matching : targets_with_matching_toolchains) {
- if (TargetLabelsMatchExceptToolchain(
- cur_matching, targets_with_other_toolchains[other_index])) {
- // Found a duplicate, erase it.
- targets_with_other_toolchains.erase(
- targets_with_other_toolchains.begin() + other_index);
- other_index--;
- break;
- }
- }
- }
-
- // Only display toolchains on labels if they don't all match.
- bool include_toolchain = !targets_with_other_toolchains.empty();
-
- std::string msg = "It is not in any dependency of\n " +
- from_target->label().GetUserVisibleName(include_toolchain);
- msg += "\nThe include file is in the target(s):\n";
- for (auto* target : targets_with_matching_toolchains)
- msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
- for (auto* target : targets_with_other_toolchains)
- msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
- if (targets_with_other_toolchains.size() +
- targets_with_matching_toolchains.size() >
- 1)
- msg += "at least one of ";
- msg += "which should somehow be reachable.";
-
- // Danger: must call CreatePersistentRange to put in Err.
- return Err(CreatePersistentRange(source_file, range), "Include not allowed.",
- msg);
-}
diff --git a/gn/tools/gn/header_checker.h b/gn/tools/gn/header_checker.h
deleted file mode 100644
index 3932001f8fb..00000000000
--- a/gn/tools/gn/header_checker.h
+++ /dev/null
@@ -1,206 +0,0 @@
-// 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 TOOLS_GN_HEADER_CHECKER_H_
-#define TOOLS_GN_HEADER_CHECKER_H_
-
-#include <condition_variable>
-#include <map>
-#include <mutex>
-#include <set>
-#include <vector>
-
-#include "base/atomic_ref_count.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string_piece.h"
-#include "tools/gn/err.h"
-#include "tools/gn/source_dir.h"
-
-class BuildSettings;
-class InputFile;
-class LocationRange;
-class SourceFile;
-class Target;
-
-namespace base {
-class FilePath;
-}
-
-class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> {
- public:
- // Represents a dependency chain.
- struct ChainLink {
- ChainLink() : target(nullptr), is_public(false) {}
- ChainLink(const Target* t, bool p) : target(t), is_public(p) {}
-
- const Target* target;
-
- // True when the dependency on this target is public.
- bool is_public;
-
- // Used for testing.
- bool operator==(const ChainLink& other) const {
- return target == other.target && is_public == other.is_public;
- }
- };
- typedef std::vector<ChainLink> Chain;
-
- // check_generated, if true, will also check generated
- // files. Something that can only be done after running a build that
- // has generated them.
- HeaderChecker(const BuildSettings* build_settings,
- const std::vector<const Target*>& targets,
- bool check_generated = false);
-
- // Runs the check. The targets in to_check will be checked.
- //
- // This assumes that the current thread already has a message loop. On
- // error, fills the given vector with the errors and returns false. Returns
- // true on success.
- //
- // force_check, if true, will override targets opting out of header checking
- // with "check_includes = false" and will check them anyway.
- bool Run(const std::vector<const Target*>& to_check,
- bool force_check,
- std::vector<Err>* errors);
-
- private:
- friend class base::RefCountedThreadSafe<HeaderChecker>;
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, IsDependencyOf);
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckInclude);
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, PublicFirst);
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, CheckIncludeAllowCircular);
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, SourceFileForInclude);
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest,
- SourceFileForInclude_FileNotFound);
- FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, Friend);
-
- ~HeaderChecker();
-
- struct TargetInfo {
- TargetInfo() : target(nullptr), is_public(false), is_generated(false) {}
- TargetInfo(const Target* t, bool is_pub, bool is_gen)
- : target(t), is_public(is_pub), is_generated(is_gen) {}
-
- const Target* target;
-
- // True if the file is public in the given target.
- bool is_public;
-
- // True if this file is generated and won't actually exist on disk.
- bool is_generated;
- };
-
- typedef std::vector<TargetInfo> TargetVector;
- typedef std::map<SourceFile, TargetVector> FileMap;
- typedef base::RepeatingCallback<bool(const base::FilePath& path)>
- PathExistsCallback;
-
- // Backend for Run() that takes the list of files to check. The errors_ list
- // will be populate on failure.
- void RunCheckOverFiles(const FileMap& flies, bool force_check);
-
- void DoWork(const Target* target, const SourceFile& file);
-
- // Adds the sources and public files from the given target to the given map.
- static void AddTargetToFileMap(const Target* target, FileMap* dest);
-
- // Returns true if the given file is in the output directory.
- bool IsFileInOuputDir(const SourceFile& file) const;
-
- // Resolves the contents of an include to a SourceFile.
- SourceFile SourceFileForInclude(const base::StringPiece& relative_file_path,
- const std::vector<SourceDir>& include_dirs,
- const InputFile& source_file,
- const LocationRange& range,
- Err* err) const;
-
- // from_target is the target the file was defined from. It will be used in
- // error messages.
- bool CheckFile(const Target* from_target,
- const SourceFile& file,
- std::vector<Err>* err) const;
-
- // Checks that the given file in the given target can include the
- // given include file. If disallowed, adds the error or errors to
- // the errors array. The range indicates the location of the
- // include in the file for error reporting.
- // |no_depeency_cache| is used to cache or check whether there is no
- // dependency from |from_target| to target having |include_file|.
- void CheckInclude(
- const Target* from_target,
- const InputFile& source_file,
- const SourceFile& include_file,
- const LocationRange& range,
- std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
- std::vector<Err>* errors) const;
-
- // Returns true if the given search_for target is a dependency of
- // search_from.
- //
- // If found, the vector given in "chain" will be filled with the reverse
- // dependency chain from the dest target (chain[0] = search_for) to the src
- // target (chain[chain.size() - 1] = search_from).
- //
- // Chains with permitted dependencies will be considered first. If a
- // permitted match is found, *is_permitted will be set to true. A chain with
- // indirect, non-public dependencies will only be considered if there are no
- // public or direct chains. In this case, *is_permitted will be false.
- //
- // A permitted dependency is a sequence of public dependencies. The first
- // one may be private, since a direct dependency always allows headers to be
- // included.
- bool IsDependencyOf(const Target* search_for,
- const Target* search_from,
- Chain* chain,
- bool* is_permitted) const;
-
- // For internal use by the previous override of IsDependencyOf. If
- // require_public is true, only public dependency chains are searched.
- bool IsDependencyOf(const Target* search_for,
- const Target* search_from,
- bool require_permitted,
- Chain* chain) const;
-
- // Makes a very descriptive error message for when an include is disallowed
- // from a given from_target, with a missing dependency to one of the given
- // targets.
- static Err MakeUnreachableError(const InputFile& source_file,
- const LocationRange& range,
- const Target* from_target,
- const TargetVector& targets);
-
- // Non-locked variables ------------------------------------------------------
- //
- // These are initialized during construction (which happens on one thread)
- // and are not modified after, so any thread can read these without locking.
-
- const BuildSettings* build_settings_;
-
- bool check_generated_;
-
- // Maps source files to targets it appears in (usually just one target).
- FileMap file_map_;
-
- // Number of tasks posted by RunCheckOverFiles() that haven't completed their
- // execution.
- base::AtomicRefCount task_count_;
-
- // Locked variables ----------------------------------------------------------
- //
- // These are mutable during runtime and require locking.
-
- std::mutex lock_;
-
- std::vector<Err> errors_;
-
- // Signaled when |task_count_| becomes zero.
- std::condition_variable task_count_cv_;
-
- DISALLOW_COPY_AND_ASSIGN(HeaderChecker);
-};
-
-#endif // TOOLS_GN_HEADER_CHECKER_H_
diff --git a/gn/tools/gn/header_checker_unittest.cc b/gn/tools/gn/header_checker_unittest.cc
deleted file mode 100644
index c6529d51dfa..00000000000
--- a/gn/tools/gn/header_checker_unittest.cc
+++ /dev/null
@@ -1,403 +0,0 @@
-// 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 <ostream>
-#include <vector>
-
-#include "base/bind.h"
-#include "tools/gn/config.h"
-#include "tools/gn/header_checker.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-class HeaderCheckerTest : public TestWithScheduler {
- public:
- HeaderCheckerTest()
- : a_(setup_.settings(), Label(SourceDir("//a/"), "a")),
- b_(setup_.settings(), Label(SourceDir("//b/"), "b")),
- c_(setup_.settings(), Label(SourceDir("//c/"), "c")),
- d_(setup_.settings(), Label(SourceDir("//d/"), "d")) {
- a_.set_output_type(Target::SOURCE_SET);
- b_.set_output_type(Target::SOURCE_SET);
- c_.set_output_type(Target::SOURCE_SET);
- d_.set_output_type(Target::SOURCE_SET);
-
- Err err;
- a_.SetToolchain(setup_.toolchain(), &err);
- b_.SetToolchain(setup_.toolchain(), &err);
- c_.SetToolchain(setup_.toolchain(), &err);
- d_.SetToolchain(setup_.toolchain(), &err);
-
- a_.public_deps().push_back(LabelTargetPair(&b_));
- b_.public_deps().push_back(LabelTargetPair(&c_));
-
- // Start with all public visibility.
- a_.visibility().SetPublic();
- b_.visibility().SetPublic();
- c_.visibility().SetPublic();
- d_.visibility().SetPublic();
-
- d_.OnResolved(&err);
- c_.OnResolved(&err);
- b_.OnResolved(&err);
- a_.OnResolved(&err);
-
- targets_.push_back(&a_);
- targets_.push_back(&b_);
- targets_.push_back(&c_);
- targets_.push_back(&d_);
- }
-
- protected:
- TestWithScope setup_;
-
- // Some headers that are automatically set up with a public dependency chain.
- // a -> b -> c. D is unconnected.
- Target a_;
- Target b_;
- Target c_;
- Target d_;
-
- std::vector<const Target*> targets_;
-};
-
-} // namespace
-
-void PrintTo(const SourceFile& source_file, ::std::ostream* os) {
- *os << source_file.value();
-}
-
-TEST_F(HeaderCheckerTest, IsDependencyOf) {
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
-
- // Add a target P ("private") that privately depends on C, and hook up the
- // chain so that A -> P -> C. A will depend on C via two different paths.
- Err err;
- Target p(setup_.settings(), Label(SourceDir("//p/"), "p"));
- p.set_output_type(Target::SOURCE_SET);
- p.SetToolchain(setup_.toolchain(), &err);
- EXPECT_FALSE(err.has_error());
- p.private_deps().push_back(LabelTargetPair(&c_));
- p.visibility().SetPublic();
- p.OnResolved(&err);
-
- a_.public_deps().push_back(LabelTargetPair(&p));
-
- // A does not depend on itself.
- bool is_permitted = false;
- HeaderChecker::Chain chain;
- EXPECT_FALSE(checker->IsDependencyOf(&a_, &a_, &chain, &is_permitted));
-
- // A depends publicly on B.
- chain.clear();
- is_permitted = false;
- EXPECT_TRUE(checker->IsDependencyOf(&b_, &a_, &chain, &is_permitted));
- ASSERT_EQ(2u, chain.size());
- EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[0]);
- EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[1]);
- EXPECT_TRUE(is_permitted);
-
- // A indirectly depends on C. The "public" dependency path through B should
- // be identified.
- chain.clear();
- is_permitted = false;
- EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
- ASSERT_EQ(3u, chain.size());
- EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[0]);
- EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[1]);
- EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
- EXPECT_TRUE(is_permitted);
-
- // C does not depend on A.
- chain.clear();
- is_permitted = false;
- EXPECT_FALSE(checker->IsDependencyOf(&a_, &c_, &chain, &is_permitted));
- EXPECT_TRUE(chain.empty());
- EXPECT_FALSE(is_permitted);
-
- // Remove the B -> C public dependency, leaving P's private dep on C the only
- // path from A to C. This should now be found.
- chain.clear();
- EXPECT_EQ(&c_, b_.public_deps()[0].ptr); // Validate it's the right one.
- b_.public_deps().erase(b_.public_deps().begin());
- EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
- EXPECT_EQ(3u, chain.size());
- EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
- EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
- EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
- EXPECT_FALSE(is_permitted);
-
- // P privately depends on C. That dependency should be OK since it's only
- // one hop.
- chain.clear();
- is_permitted = false;
- EXPECT_TRUE(checker->IsDependencyOf(&c_, &p, &chain, &is_permitted));
- ASSERT_EQ(2u, chain.size());
- EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
- EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
- EXPECT_TRUE(is_permitted);
-}
-
-TEST_F(HeaderCheckerTest, CheckInclude) {
- InputFile input_file(SourceFile("//some_file.cc"));
- input_file.SetContents(std::string());
- LocationRange range; // Dummy value.
-
- // Add a disconnected target d with a header to check that you have to have
- // to depend on a target listing a header.
- SourceFile d_header("//d_header.h");
- d_.sources().push_back(SourceFile(d_header));
-
- // Add a header on B and say everything in B is public.
- SourceFile b_public("//b_public.h");
- b_.sources().push_back(b_public);
- c_.set_all_headers_public(true);
-
- // Add a public and private header on C.
- SourceFile c_public("//c_public.h");
- SourceFile c_private("//c_private.h");
- c_.sources().push_back(c_private);
- c_.public_headers().push_back(c_public);
- c_.set_all_headers_public(false);
-
- // Create another toolchain.
- Settings other_settings(setup_.build_settings(), "other/");
- Toolchain other_toolchain(&other_settings,
- Label(SourceDir("//toolchain/"), "other"));
- TestWithScope::SetupToolchain(&other_toolchain);
- other_settings.set_toolchain_label(other_toolchain.label());
- other_settings.set_default_toolchain_label(setup_.toolchain()->label());
-
- // Add a target in the other toolchain with a header in it that is not
- // connected to any targets in the main toolchain.
- Target otc(&other_settings,
- Label(SourceDir("//p/"), "otc", other_toolchain.label().dir(),
- other_toolchain.label().name()));
- otc.set_output_type(Target::SOURCE_SET);
- Err err;
- EXPECT_TRUE(otc.SetToolchain(&other_toolchain, &err));
- otc.visibility().SetPublic();
- targets_.push_back(&otc);
-
- SourceFile otc_header("//otc_header.h");
- otc.sources().push_back(otc_header);
- EXPECT_TRUE(otc.OnResolved(&err));
-
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
-
- std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
- // A file in target A can't include a header from D because A has no
- // dependency on D.
- std::vector<Err> errors;
- checker->CheckInclude(&a_, input_file, d_header, range, &no_dependency_cache,
- &errors);
- EXPECT_GT(errors.size(), 0);
-
- // A can include the public header in B.
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&a_, input_file, b_public, range, &no_dependency_cache,
- &errors);
- EXPECT_EQ(errors.size(), 0);
-
- // Check A depending on the public and private headers in C.
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&a_, input_file, c_public, range, &no_dependency_cache,
- &errors);
- EXPECT_EQ(errors.size(), 0);
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
- &errors);
- EXPECT_GT(errors.size(), 0);
-
- // A can depend on a random file unknown to the build.
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&a_, input_file, SourceFile("//random.h"), range,
- &no_dependency_cache, &errors);
- EXPECT_EQ(errors.size(), 0);
-
- // A can depend on a file present only in another toolchain even with no
- // dependency path.
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&a_, input_file, otc_header, range,
- &no_dependency_cache, &errors);
- EXPECT_EQ(errors.size(), 0);
-}
-
-// A public chain of dependencies should always be identified first, even if
-// it is longer than a private one.
-TEST_F(HeaderCheckerTest, PublicFirst) {
- // Now make a A -> Z -> D private dependency chain (one shorter than the
- // public one to get to D).
- Target z(setup_.settings(), Label(SourceDir("//a/"), "a"));
- z.set_output_type(Target::SOURCE_SET);
- Err err;
- EXPECT_TRUE(z.SetToolchain(setup_.toolchain(), &err));
- z.private_deps().push_back(LabelTargetPair(&d_));
- EXPECT_TRUE(z.OnResolved(&err));
- targets_.push_back(&z);
-
- a_.private_deps().push_back(LabelTargetPair(&z));
-
- // Check that D can be found from A, but since it's private, it will be
- // marked as not permitted.
- bool is_permitted = false;
- HeaderChecker::Chain chain;
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
- EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
-
- EXPECT_FALSE(is_permitted);
- ASSERT_EQ(3u, chain.size());
- EXPECT_EQ(HeaderChecker::ChainLink(&d_, false), chain[0]);
- EXPECT_EQ(HeaderChecker::ChainLink(&z, false), chain[1]);
- EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
-
- // Hook up D to the existing public A -> B -> C chain to make a long one, and
- // search for D again.
- c_.public_deps().push_back(LabelTargetPair(&d_));
- checker = new HeaderChecker(setup_.build_settings(), targets_);
- chain.clear();
- EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
-
- // This should have found the long public one.
- EXPECT_TRUE(is_permitted);
- ASSERT_EQ(4u, chain.size());
- EXPECT_EQ(HeaderChecker::ChainLink(&d_, true), chain[0]);
- EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[1]);
- EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[2]);
- EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[3]);
-}
-
-// Checks that the allow_circular_includes_from list works.
-TEST_F(HeaderCheckerTest, CheckIncludeAllowCircular) {
- InputFile input_file(SourceFile("//some_file.cc"));
- input_file.SetContents(std::string());
- LocationRange range; // Dummy value.
-
- // Add an include file to A.
- SourceFile a_public("//a_public.h");
- a_.sources().push_back(a_public);
-
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
-
- // A depends on B. So B normally can't include headers from A.
- std::vector<Err> errors;
- std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
- checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
- &errors);
- EXPECT_GT(errors.size(), 0);
-
- // Add an allow_circular_includes_from on A that lists B.
- a_.allow_circular_includes_from().insert(b_.label());
-
- // Now the include from B to A should be allowed.
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
- &errors);
- EXPECT_EQ(errors.size(), 0);
-}
-
-TEST_F(HeaderCheckerTest, SourceFileForInclude) {
- using base::FilePath;
- const std::vector<SourceDir> kIncludeDirs = {
- SourceDir("/c/custom_include/"), SourceDir("//"), SourceDir("//subdir")};
- a_.sources().push_back(SourceFile("//lib/header1.h"));
- b_.sources().push_back(SourceFile("/c/custom_include/header2.h"));
-
- InputFile dummy_input_file(SourceFile("//some_file.cc"));
- dummy_input_file.SetContents(std::string());
- LocationRange dummy_range;
-
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
- {
- Err err;
- SourceFile source_file = checker->SourceFileForInclude(
- "lib/header1.h", kIncludeDirs, dummy_input_file, dummy_range, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(SourceFile("//lib/header1.h"), source_file);
- }
-
- {
- Err err;
- SourceFile source_file = checker->SourceFileForInclude(
- "header2.h", kIncludeDirs, dummy_input_file, dummy_range, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(SourceFile("/c/custom_include/header2.h"), source_file);
- }
-}
-
-TEST_F(HeaderCheckerTest, SourceFileForInclude_FileNotFound) {
- using base::FilePath;
- const char kFileContents[] = "Some dummy contents";
- const std::vector<SourceDir> kIncludeDirs = {SourceDir("//")};
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
-
- Err err;
- InputFile input_file(SourceFile("//input.cc"));
- input_file.SetContents(std::string(kFileContents));
- const int kLineNumber = 10;
- const int kColumnNumber = 16;
- const int kLength = 8;
- const int kByteNumber = 100;
- LocationRange range(
- Location(&input_file, kLineNumber, kColumnNumber, kByteNumber),
- Location(&input_file, kLineNumber, kColumnNumber + kLength, kByteNumber));
-
- SourceFile source_file = checker->SourceFileForInclude(
- "header.h", kIncludeDirs, input_file, range, &err);
- EXPECT_TRUE(source_file.is_null());
- EXPECT_FALSE(err.has_error());
-}
-
-TEST_F(HeaderCheckerTest, Friend) {
- // Note: we have a public dependency chain A -> B -> C set up already.
- InputFile input_file(SourceFile("//some_file.cc"));
- input_file.SetContents(std::string());
- LocationRange range; // Dummy value.
-
- // Add a private header on C.
- SourceFile c_private("//c_private.h");
- c_.sources().push_back(c_private);
- c_.set_all_headers_public(false);
-
- // List A as a friend of C.
- Err err;
- c_.friends().push_back(
- LabelPattern::GetPattern(SourceDir("//"), Value(nullptr, "//a:*"), &err));
- ASSERT_FALSE(err.has_error());
-
- // Must be after setting everything up for it to find the files.
- scoped_refptr<HeaderChecker> checker(
- new HeaderChecker(setup_.build_settings(), targets_));
-
- // B should not be allowed to include C's private header.
- std::vector<Err> errors;
- std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
- checker->CheckInclude(&b_, input_file, c_private, range, &no_dependency_cache,
- &errors);
- EXPECT_GT(errors.size(), 0);
-
- // A should be able to because of the friend declaration.
- errors.clear();
- no_dependency_cache.clear();
- checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
- &errors);
- EXPECT_EQ(errors.size(), 0);
-}
diff --git a/gn/tools/gn/import_manager.cc b/gn/tools/gn/import_manager.cc
deleted file mode 100644
index 9339ff56584..00000000000
--- a/gn/tools/gn/import_manager.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// 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 "tools/gn/import_manager.h"
-
-#include <memory>
-
-#include "tools/gn/err.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope_per_file_provider.h"
-#include "tools/gn/trace.h"
-#include "util/ticks.h"
-
-namespace {
-
-// Returns a newly-allocated scope on success, null on failure.
-std::unique_ptr<Scope> UncachedImport(const Settings* settings,
- const SourceFile& file,
- const ParseNode* node_for_err,
- Err* err) {
- ScopedTrace load_trace(TraceItem::TRACE_IMPORT_LOAD, file.value());
- load_trace.SetToolchain(settings->toolchain_label());
-
- const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
- node_for_err->GetRange(), settings->build_settings(), file, err);
- if (!node)
- return nullptr;
-
- std::unique_ptr<Scope> scope =
- std::make_unique<Scope>(settings->base_config());
- scope->set_source_dir(file.GetDir());
-
- // Don't allow ScopePerFileProvider to provide target-related variables.
- // These will be relative to the imported file, which is probably not what
- // people mean when they use these.
- ScopePerFileProvider per_file_provider(scope.get(), false);
-
- scope->SetProcessingImport();
- node->Execute(scope.get(), err);
- if (err->has_error()) {
- // If there was an error, append the caller location so the error message
- // displays a why the file was imported (esp. useful for failed asserts).
- err->AppendSubErr(Err(node_for_err, "whence it was imported."));
- return nullptr;
- }
- scope->ClearProcessingImport();
-
- return scope;
-}
-
-} // namespace
-
-struct ImportManager::ImportInfo {
- ImportInfo() = default;
- ~ImportInfo() = default;
-
- // This lock protects the unique_ptr. Once the scope is computed,
- // it is const and can be accessed read-only outside of the lock.
- std::mutex load_lock;
-
- std::unique_ptr<const Scope> scope;
-
- // The result of loading the import. If the load failed, the scope will be
- // null but this will be set to error. In this case the thread should not
- // attempt to load the file, even if the scope is null.
- Err load_result;
-};
-
-ImportManager::ImportManager() = default;
-
-ImportManager::~ImportManager() = default;
-
-bool ImportManager::DoImport(const SourceFile& file,
- const ParseNode* node_for_err,
- Scope* scope,
- Err* err) {
- // Key for the current import on the current thread in imports_in_progress_.
- std::stringstream ss;
- ss << std::this_thread::get_id() << file.value();
- std::string key = ss.str();
-
- // See if we have a cached import, but be careful to actually do the scope
- // copying outside of the lock.
- ImportInfo* import_info = nullptr;
- {
- std::lock_guard<std::mutex> lock(imports_lock_);
- std::unique_ptr<ImportInfo>& info_ptr = imports_[file];
- if (!info_ptr)
- info_ptr = std::make_unique<ImportInfo>();
-
- // Promote the ImportInfo to outside of the imports lock.
- import_info = info_ptr.get();
-
- if (imports_in_progress_.find(key) != imports_in_progress_.end()) {
- *err = Err(Location(), file.value() + " is part of an import loop.");
- return false;
- }
- imports_in_progress_.insert(key);
- }
-
- // Now use the per-import-file lock to block this thread if another thread
- // is already processing the import.
- const Scope* import_scope = nullptr;
- {
- Ticks import_block_begin = TicksNow();
- std::lock_guard<std::mutex> lock(import_info->load_lock);
-
- if (!import_info->scope) {
- // Only load if the import hasn't already failed.
- if (!import_info->load_result.has_error()) {
- import_info->scope = UncachedImport(
- scope->settings(), file, node_for_err, &import_info->load_result);
- }
- if (import_info->load_result.has_error()) {
- *err = import_info->load_result;
- return false;
- }
- } else {
- // Add trace if this thread was blocked for a long period of time and did
- // not load the import itself.
- Ticks import_block_end = TicksNow();
- constexpr auto kImportBlockTraceThresholdMS = 20;
- if (TracingEnabled() &&
- TicksDelta(import_block_end, import_block_begin).InMilliseconds() >
- kImportBlockTraceThresholdMS) {
- auto* import_block_trace =
- new TraceItem(TraceItem::TRACE_IMPORT_BLOCK, file.value(),
- std::this_thread::get_id());
- import_block_trace->set_begin(import_block_begin);
- import_block_trace->set_end(import_block_end);
- import_block_trace->set_toolchain(
- scope->settings()->toolchain_label().GetUserVisibleName(false));
- AddTrace(import_block_trace);
- }
- }
-
- // Promote the now-read-only scope to outside the load lock.
- import_scope = import_info->scope.get();
- }
-
- Scope::MergeOptions options;
- options.skip_private_vars = true;
- options.mark_dest_used = true; // Don't require all imported values be used.
-
- {
- std::lock_guard<std::mutex> lock(imports_lock_);
- imports_in_progress_.erase(key);
- }
-
- return import_scope->NonRecursiveMergeTo(scope, options, node_for_err,
- "import", err);
-}
-
-std::vector<SourceFile> ImportManager::GetImportedFiles() const {
- std::vector<SourceFile> imported_files;
- imported_files.resize(imports_.size());
- std::transform(imports_.begin(), imports_.end(), imported_files.begin(),
- [](const ImportMap::value_type& val) { return val.first; });
- return imported_files;
-}
diff --git a/gn/tools/gn/import_manager.h b/gn/tools/gn/import_manager.h
deleted file mode 100644
index d72d5a79185..00000000000
--- a/gn/tools/gn/import_manager.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 TOOLS_GN_IMPORT_MANAGER_H_
-#define TOOLS_GN_IMPORT_MANAGER_H_
-
-#include <map>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "base/macros.h"
-
-class Err;
-class ParseNode;
-class Scope;
-class SourceFile;
-
-// Provides a cache of the results of importing scopes so the results can
-// be re-used rather than running the imported files multiple times.
-class ImportManager {
- public:
- ImportManager();
- ~ImportManager();
-
- // Does an import of the given file into the given scope. On error, sets the
- // error and returns false.
- bool DoImport(const SourceFile& file,
- const ParseNode* node_for_err,
- Scope* scope,
- Err* err);
-
- std::vector<SourceFile> GetImportedFiles() const;
-
- private:
- struct ImportInfo;
-
- // Protects access to imports_ and imports_in_progress_. Do not hold when
- // actually executing imports.
- std::mutex imports_lock_;
-
- // Owning pointers to the scopes.
- typedef std::map<SourceFile, std::unique_ptr<ImportInfo>> ImportMap;
- ImportMap imports_;
-
- std::unordered_set<std::string> imports_in_progress_;
-
- DISALLOW_COPY_AND_ASSIGN(ImportManager);
-};
-
-#endif // TOOLS_GN_IMPORT_MANAGER_H_
diff --git a/gn/tools/gn/inherited_libraries.cc b/gn/tools/gn/inherited_libraries.cc
deleted file mode 100644
index 482256f48e0..00000000000
--- a/gn/tools/gn/inherited_libraries.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// 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 "tools/gn/inherited_libraries.h"
-
-#include "tools/gn/target.h"
-
-InheritedLibraries::InheritedLibraries() = default;
-
-InheritedLibraries::~InheritedLibraries() = default;
-
-std::vector<const Target*> InheritedLibraries::GetOrdered() const {
- std::vector<const Target*> result;
- result.resize(map_.size());
-
- // The indices in the map should be from 0 to the number of items in the
- // map, so insert directly into the result (with some sanity checks).
- for (const auto& pair : map_) {
- size_t index = pair.second.index;
- DCHECK(index < result.size());
- DCHECK(!result[index]);
- result[index] = pair.first;
- }
-
- return result;
-}
-
-std::vector<std::pair<const Target*, bool>>
-InheritedLibraries::GetOrderedAndPublicFlag() const {
- std::vector<std::pair<const Target*, bool>> result;
- result.resize(map_.size());
-
- for (const auto& pair : map_) {
- size_t index = pair.second.index;
- DCHECK(index < result.size());
- DCHECK(!result[index].first);
- result[index] = std::make_pair(pair.first, pair.second.is_public);
- }
-
- return result;
-}
-
-void InheritedLibraries::Append(const Target* target, bool is_public) {
- // Try to insert a new node.
- auto insert_result =
- map_.insert(std::make_pair(target, Node(map_.size(), is_public)));
-
- if (!insert_result.second) {
- // Element already present, insert failed and insert_result indicates the
- // old one. The old one may need to have its public flag updated.
- if (is_public) {
- Node& existing_node = insert_result.first->second;
- existing_node.is_public = true;
- }
- }
-}
-
-void InheritedLibraries::AppendInherited(const InheritedLibraries& other,
- bool is_public) {
- // Append all items in order, mark them public only if the're already public
- // and we're adding them publically.
- for (const auto& cur : other.GetOrderedAndPublicFlag())
- Append(cur.first, is_public && cur.second);
-}
-
-void InheritedLibraries::AppendPublicSharedLibraries(
- const InheritedLibraries& other,
- bool is_public) {
- for (const auto& cur : other.GetOrderedAndPublicFlag()) {
- if (cur.first->output_type() == Target::SHARED_LIBRARY && cur.second)
- Append(cur.first, is_public);
- }
-}
diff --git a/gn/tools/gn/inherited_libraries.h b/gn/tools/gn/inherited_libraries.h
deleted file mode 100644
index e8568b26766..00000000000
--- a/gn/tools/gn/inherited_libraries.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 TOOLS_GN_INHERITED_LIBRARIES_H_
-#define TOOLS_GN_INHERITED_LIBRARIES_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-
-class Target;
-
-// Represents an ordered uniquified set of all shared/static libraries for
-// a given target. These are pushed up the dependency tree.
-//
-// Maintaining the order is important so GN links all libraries in the same
-// order specified in the build files.
-//
-// Since this list is uniquified, appending to the list will not actually
-// append a new item if the target already exists. However, the existing one
-// may have its is_public flag updated. "Public" always wins, so is_public will
-// be true if any dependency with that name has been set to public.
-class InheritedLibraries {
- public:
- InheritedLibraries();
- ~InheritedLibraries();
-
- // Returns the list of dependencies in order, optionally with the flag
- // indicating whether the dependency is public.
- std::vector<const Target*> GetOrdered() const;
- std::vector<std::pair<const Target*, bool>> GetOrderedAndPublicFlag() const;
-
- // Adds a single dependency to the end of the list. See note on adding above.
- void Append(const Target* target, bool is_public);
-
- // Appends all items from the "other" list to the current one. The is_public
- // parameter indicates how the current target depends on the items in
- // "other". If is_public is true, the existing public flags of the appended
- // items will be preserved (propogating the public-ness up the dependency
- // chain). If is_public is false, all deps will be added as private since
- // the current target isn't forwarding them.
- void AppendInherited(const InheritedLibraries& other, bool is_public);
-
- // Like AppendInherited but only appends the items in "other" that are of
- // type SHARED_LIBRARY and only when they're marked public. This is used
- // to push shared libraries up the dependency chain, following only public
- // deps, to dependent targets that need to use them.
- void AppendPublicSharedLibraries(const InheritedLibraries& other,
- bool is_public);
-
- private:
- struct Node {
- Node() : index(static_cast<size_t>(-1)), is_public(false) {}
- Node(size_t i, bool p) : index(i), is_public(p) {}
-
- size_t index;
- bool is_public;
- };
-
- typedef std::map<const Target*, Node> LibraryMap;
- LibraryMap map_;
-
- DISALLOW_COPY_AND_ASSIGN(InheritedLibraries);
-};
-
-#endif // TOOLS_GN_INHERITED_LIBRARIES_H_
diff --git a/gn/tools/gn/inherited_libraries_unittest.cc b/gn/tools/gn/inherited_libraries_unittest.cc
deleted file mode 100644
index 185b60bebac..00000000000
--- a/gn/tools/gn/inherited_libraries_unittest.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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 "tools/gn/inherited_libraries.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-// In these tests, Pair can't be used conveniently because the
-// "const" won't be inferred and the types won't match. This helper makes the
-// right type of pair with the const Target.
-std::pair<const Target*, bool> Pair(const Target* t, bool b) {
- return std::pair<const Target*, bool>(t, b);
-}
-
-} // namespace
-
-TEST(InheritedLibraries, Unique) {
- TestWithScope setup;
-
- Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
- Target b(setup.settings(), Label(SourceDir("//foo/"), "b"));
-
- // Setup, add the two targets as private.
- InheritedLibraries libs;
- libs.Append(&a, false);
- libs.Append(&b, false);
- auto result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(2u, result.size());
- EXPECT_EQ(Pair(&a, false), result[0]);
- EXPECT_EQ(Pair(&b, false), result[1]);
-
- // Add again as private, this should be a NOP.
- libs.Append(&a, false);
- libs.Append(&b, false);
- result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(2u, result.size());
- EXPECT_EQ(Pair(&a, false), result[0]);
- EXPECT_EQ(Pair(&b, false), result[1]);
-
- // Add as public, this should make both public.
- libs.Append(&a, true);
- libs.Append(&b, true);
- result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(2u, result.size());
- EXPECT_EQ(Pair(&a, true), result[0]);
- EXPECT_EQ(Pair(&b, true), result[1]);
-
- // Add again private, they should stay public.
- libs.Append(&a, false);
- libs.Append(&b, false);
- result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(2u, result.size());
- EXPECT_EQ(Pair(&a, true), result[0]);
- EXPECT_EQ(Pair(&b, true), result[1]);
-}
-
-TEST(InheritedLibraries, AppendInherited) {
- TestWithScope setup;
-
- Target a(setup.settings(), Label(SourceDir("//foo/"), "a"));
- Target b(setup.settings(), Label(SourceDir("//foo/"), "b"));
- Target w(setup.settings(), Label(SourceDir("//foo/"), "w"));
- Target x(setup.settings(), Label(SourceDir("//foo/"), "x"));
- Target y(setup.settings(), Label(SourceDir("//foo/"), "y"));
- Target z(setup.settings(), Label(SourceDir("//foo/"), "z"));
-
- InheritedLibraries libs;
- libs.Append(&a, false);
- libs.Append(&b, false);
-
- // Appending these things with private inheritance should make them private,
- // no matter how they're listed in the appended class.
- InheritedLibraries append_private;
- append_private.Append(&a, true);
- append_private.Append(&b, false);
- append_private.Append(&w, true);
- append_private.Append(&x, false);
- libs.AppendInherited(append_private, false);
-
- auto result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(4u, result.size());
- EXPECT_EQ(Pair(&a, false), result[0]);
- EXPECT_EQ(Pair(&b, false), result[1]);
- EXPECT_EQ(Pair(&w, false), result[2]);
- EXPECT_EQ(Pair(&x, false), result[3]);
-
- // Appending these things with public inheritance should convert them.
- InheritedLibraries append_public;
- append_public.Append(&a, true);
- append_public.Append(&b, false);
- append_public.Append(&y, true);
- append_public.Append(&z, false);
- libs.AppendInherited(append_public, true);
-
- result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(6u, result.size());
- EXPECT_EQ(Pair(&a, true), result[0]); // Converted to public.
- EXPECT_EQ(Pair(&b, false), result[1]);
- EXPECT_EQ(Pair(&w, false), result[2]);
- EXPECT_EQ(Pair(&x, false), result[3]);
- EXPECT_EQ(Pair(&y, true), result[4]); // Appended as public.
- EXPECT_EQ(Pair(&z, false), result[5]);
-}
-
-TEST(InheritedLibraries, AppendPublicSharedLibraries) {
- TestWithScope setup;
- InheritedLibraries append;
-
- // Two source sets.
- Target set_pub(setup.settings(), Label(SourceDir("//foo/"), "set_pub"));
- set_pub.set_output_type(Target::SOURCE_SET);
- append.Append(&set_pub, true);
- Target set_priv(setup.settings(), Label(SourceDir("//foo/"), "set_priv"));
- set_priv.set_output_type(Target::SOURCE_SET);
- append.Append(&set_priv, false);
-
- // Two shared libraries.
- Target sh_pub(setup.settings(), Label(SourceDir("//foo/"), "sh_pub"));
- sh_pub.set_output_type(Target::SHARED_LIBRARY);
- append.Append(&sh_pub, true);
- Target sh_priv(setup.settings(), Label(SourceDir("//foo/"), "sh_priv"));
- sh_priv.set_output_type(Target::SHARED_LIBRARY);
- append.Append(&sh_priv, false);
-
- InheritedLibraries libs;
- libs.AppendPublicSharedLibraries(append, true);
-
- auto result = libs.GetOrderedAndPublicFlag();
- ASSERT_EQ(1u, result.size());
- EXPECT_EQ(Pair(&sh_pub, true), result[0]);
-}
diff --git a/gn/tools/gn/input_conversion.cc b/gn/tools/gn/input_conversion.cc
deleted file mode 100644
index 270d437fdd9..00000000000
--- a/gn/tools/gn/input_conversion.cc
+++ /dev/null
@@ -1,359 +0,0 @@
-// 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 "tools/gn/input_conversion.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/json/json_reader.h"
-#include "base/macros.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/err.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/label.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/tokenizer.h"
-#include "tools/gn/value.h"
-
-namespace {
-
-enum ValueOrScope {
- PARSE_VALUE, // Treat the input as an expression.
- PARSE_SCOPE, // Treat the input as code and return the resulting scope.
-};
-
-// Sets the origin of the value and any nested values with the given node.
-Value ParseValueOrScope(const Settings* settings,
- const std::string& input,
- ValueOrScope what,
- const ParseNode* origin,
- Err* err) {
- // The memory for these will be kept around by the input file manager
- // so the origin parse nodes for the values will be preserved.
- InputFile* input_file;
- std::vector<Token>* tokens;
- std::unique_ptr<ParseNode>* parse_root_ptr;
- g_scheduler->input_file_manager()->AddDynamicInput(SourceFile(), &input_file,
- &tokens, &parse_root_ptr);
-
- input_file->SetContents(input);
- if (origin) {
- // This description will be the blame for any error messages caused by
- // script parsing or if a value is blamed. It will say
- // "Error at <...>:line:char" so here we try to make a string for <...>
- // that reads well in this context.
- input_file->set_friendly_name("dynamically parsed input that " +
- origin->GetRange().begin().Describe(true) +
- " loaded ");
- } else {
- input_file->set_friendly_name("dynamic input");
- }
-
- *tokens = Tokenizer::Tokenize(input_file, err);
- if (err->has_error())
- return Value();
-
- // Parse the file according to what we're looking for.
- if (what == PARSE_VALUE)
- *parse_root_ptr = Parser::ParseValue(*tokens, err);
- else
- *parse_root_ptr = Parser::Parse(*tokens, err); // Will return a Block.
- if (err->has_error())
- return Value();
- ParseNode* parse_root = parse_root_ptr->get(); // For nicer syntax below.
-
- // It's valid for the result to be a null pointer, this just means that the
- // script returned nothing.
- if (!parse_root)
- return Value();
-
- std::unique_ptr<Scope> scope = std::make_unique<Scope>(settings);
- Value result = parse_root->Execute(scope.get(), err);
- if (err->has_error())
- return Value();
-
- // When we want the result as a scope, the result is actually the scope
- // we made, rather than the result of running the block (which will be empty).
- if (what == PARSE_SCOPE) {
- DCHECK(result.type() == Value::NONE);
- result = Value(origin, std::move(scope));
- }
- return result;
-}
-
-Value ParseList(const std::string& input, const ParseNode* origin, Err* err) {
- Value ret(origin, Value::LIST);
- std::vector<std::string> as_lines = base::SplitString(
- input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
- // Trim one empty line from the end since the last line might end in a
- // newline. If the user wants more trimming, they'll specify "trim" in the
- // input conversion options.
- if (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
- as_lines.resize(as_lines.size() - 1);
-
- ret.list_value().reserve(as_lines.size());
- for (const auto& line : as_lines)
- ret.list_value().push_back(Value(origin, line));
- return ret;
-}
-
-bool IsIdentifier(const base::StringPiece& buffer) {
- DCHECK(buffer.size() > 0);
- if (!Tokenizer::IsIdentifierFirstChar(buffer[0]))
- return false;
- for (size_t i = 1; i < buffer.size(); i++)
- if (!Tokenizer::IsIdentifierContinuingChar(buffer[i]))
- return false;
- return true;
-}
-
-Value ParseJSONValue(const Settings* settings,
- const base::Value& value,
- const ParseNode* origin,
- InputFile* input_file,
- Err* err) {
- switch (value.type()) {
- case base::Value::Type::NONE:
- *err = Err(origin, "Null values are not supported.");
- return Value();
- case base::Value::Type::BOOLEAN:
- return Value(origin, value.GetBool());
- case base::Value::Type::INTEGER:
- return Value(origin, static_cast<int64_t>(value.GetInt()));
- case base::Value::Type::STRING:
- return Value(origin, value.GetString());
- case base::Value::Type::BINARY:
- *err = Err(origin, "Binary values are not supported.");
- return Value();
- case base::Value::Type::DICTIONARY: {
- std::unique_ptr<Scope> scope = std::make_unique<Scope>(settings);
- for (const auto& it : value.DictItems()) {
- Value parsed_value =
- ParseJSONValue(settings, it.second, origin, input_file, err);
- if (!IsIdentifier(it.first)) {
- *err = Err(origin, "Invalid identifier \"" + it.first + "\".");
- return Value();
- }
- // Search for the key in the input file. We know it's present because
- // it was parsed by the JSON reader, but we need its location to
- // construct a StringPiece that can be used as key in the Scope.
- size_t off = input_file->contents().find("\"" + it.first + "\"");
- if (off == std::string::npos) {
- *err = Err(origin, "Invalid encoding \"" + it.first + "\".");
- return Value();
- }
- base::StringPiece key(&input_file->contents()[off + 1],
- it.first.size());
- scope->SetValue(key, std::move(parsed_value), origin);
- }
- return Value(origin, std::move(scope));
- }
- case base::Value::Type::LIST: {
- Value result(origin, Value::LIST);
- result.list_value().reserve(value.GetList().size());
- for (const auto& val : value.GetList()) {
- Value parsed_value =
- ParseJSONValue(settings, val, origin, input_file, err);
- result.list_value().push_back(parsed_value);
- }
- return result;
- }
- }
- return Value();
-}
-
-// Parses the JSON string and converts it to GN value.
-Value ParseJSON(const Settings* settings,
- const std::string& input,
- const ParseNode* origin,
- Err* err) {
- InputFile* input_file;
- std::vector<Token>* tokens;
- std::unique_ptr<ParseNode>* parse_root_ptr;
- g_scheduler->input_file_manager()->AddDynamicInput(SourceFile(), &input_file,
- &tokens, &parse_root_ptr);
- input_file->SetContents(input);
-
- int error_code_out;
- std::string error_msg_out;
- std::unique_ptr<base::Value> value = base::JSONReader::ReadAndReturnError(
- input, base::JSONParserOptions::JSON_PARSE_RFC, &error_code_out,
- &error_msg_out);
- if (!value) {
- *err = Err(origin, "Input is not a valid JSON: " + error_msg_out);
- return Value();
- }
-
- return ParseJSONValue(settings, *value, origin, input_file, err);
-}
-
-// Backend for ConvertInputToValue, this takes the extracted string for the
-// input conversion so we can recursively call ourselves to handle the optional
-// "trim" prefix. This original value is also kept for the purposes of throwing
-// errors.
-Value DoConvertInputToValue(const Settings* settings,
- const std::string& input,
- const ParseNode* origin,
- const Value& original_input_conversion,
- const std::string& input_conversion,
- Err* err) {
- if (input_conversion.empty())
- return Value(); // Empty string means discard the result.
-
- const char kTrimPrefix[] = "trim ";
- if (base::StartsWith(input_conversion, kTrimPrefix,
- base::CompareCase::SENSITIVE)) {
- std::string trimmed;
- base::TrimWhitespaceASCII(input, base::TRIM_ALL, &trimmed);
-
- // Remove "trim" prefix from the input conversion and re-run.
- return DoConvertInputToValue(
- settings, trimmed, origin, original_input_conversion,
- input_conversion.substr(arraysize(kTrimPrefix) - 1), err);
- }
-
- if (input_conversion == "value")
- return ParseValueOrScope(settings, input, PARSE_VALUE, origin, err);
- if (input_conversion == "string")
- return Value(origin, input);
- if (input_conversion == "list lines")
- return ParseList(input, origin, err);
- if (input_conversion == "scope")
- return ParseValueOrScope(settings, input, PARSE_SCOPE, origin, err);
- if (input_conversion == "json")
- return ParseJSON(settings, input, origin, err);
-
- *err = Err(original_input_conversion, "Not a valid input_conversion.",
- "Run gn help input_conversion to see your options.");
- return Value();
-}
-
-} // namespace
-
-const char kInputOutputConversion_Help[] =
- R"(Input and output conversion
-
- Input and output conversions are arguments to file and process functions
- that specify how to convert data to or from external formats. The possible
- values for parameters specifying conversions are:
-
- "" (the default)
- input: Discard the result and return None.
-
- output: If value is a list, then "list lines"; otherwise "value".
-
- "list lines"
- input:
- Return the file contents as a list, with a string for each line. The
- newlines will not be present in the result. The last line may or may
- not end in a newline.
-
- After splitting, each individual line will be trimmed of whitespace on
- both ends.
-
- output:
- Renders the value contents as a list, with a string for each line. The
- newlines will not be present in the result. The last line will end in
- with a newline.
-
- "scope"
- input:
- Execute the block as GN code and return a scope with the resulting
- values in it. If the input was:
- a = [ "hello.cc", "world.cc" ]
- b = 26
- and you read the result into a variable named "val", then you could
- access contents the "." operator on "val":
- sources = val.a
- some_count = val.b
-
- output:
- Renders the value contents as a GN code block, reversing the input
- result above.
-
- "string"
- input: Return the file contents into a single string.
-
- output:
- Render the value contents into a single string. The output is:
- a string renders with quotes, e.g. "str"
- an integer renders as a stringified integer, e.g. "6"
- a boolean renders as the associated string, e.g. "true"
- a list renders as a representation of its contents, e.g. "[\"str\", 6]"
- a scope renders as a GN code block of its values. If the Value was:
- Value val;
- val.a = [ "hello.cc", "world.cc" ];
- val.b = 26
- the resulting output would be:
- "{
- a = [ \"hello.cc\", \"world.cc\" ]
- b = 26
- }"
-
- "value"
- input:
- Parse the input as if it was a literal rvalue in a buildfile. Examples of
- typical program output using this mode:
- [ "foo", "bar" ] (result will be a list)
- or
- "foo bar" (result will be a string)
- or
- 5 (result will be an integer)
-
- Note that if the input is empty, the result will be a null value which
- will produce an error if assigned to a variable.
-
- output:
- Render the value contents as a literal rvalue. Strings render with
- escaped quotes.
-
- "json"
- input: Parse the input as a JSON and convert it to equivalent GN rvalue.
-
- output: Convert the Value to equivalent JSON value.
-
- The data type mapping is:
- a string in JSON maps to string in GN
- an integer in JSON maps to integer in GN
- a float in JSON is unsupported and will result in an error
- an object in JSON maps to scope in GN
- an array in JSON maps to list in GN
- a boolean in JSON maps to boolean in GN
- a null in JSON is unsupported and will result in an error
-
- Nota that the input dictionary keys have to be valid GN identifiers
- otherwise they will produce an error.
-
- "trim ..." (input only)
- Prefixing any of the other transformations with the word "trim" will
- result in whitespace being trimmed from the beginning and end of the
- result before processing.
-
- Examples: "trim string" or "trim list lines"
-
- Note that "trim value" is useless because the value parser skips
- whitespace anyway.
-)";
-
-Value ConvertInputToValue(const Settings* settings,
- const std::string& input,
- const ParseNode* origin,
- const Value& input_conversion_value,
- Err* err) {
- if (input_conversion_value.type() == Value::NONE)
- return Value(); // Allow null inputs to mean discard the result.
- if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
- return Value();
- return DoConvertInputToValue(settings, input, origin, input_conversion_value,
- input_conversion_value.string_value(), err);
-}
diff --git a/gn/tools/gn/input_conversion_unittest.cc b/gn/tools/gn/input_conversion_unittest.cc
deleted file mode 100644
index 434d952d064..00000000000
--- a/gn/tools/gn/input_conversion_unittest.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-// 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 "tools/gn/input_conversion.h"
-
-#include "tools/gn/err.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-namespace {
-
-// InputConversion needs a global scheduler object.
-class InputConversionTest : public TestWithScheduler {
- public:
- InputConversionTest() = default;
-
- const Settings* settings() { return setup_.settings(); }
-
- private:
- TestWithScope setup_;
-};
-
-} // namespace
-
-TEST_F(InputConversionTest, String) {
- Err err;
- std::string input("\nfoo bar \n");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "string"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::STRING, result.type());
- EXPECT_EQ(input, result.string_value());
-
- // Test with trimming.
- result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "trim string"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::STRING, result.type());
- EXPECT_EQ("foo bar", result.string_value());
-}
-
-TEST_F(InputConversionTest, ListLines) {
- Err err;
- std::string input("\nfoo\nbar \n\n");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "list lines"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::LIST, result.type());
- ASSERT_EQ(4u, result.list_value().size());
- EXPECT_EQ("", result.list_value()[0].string_value());
- EXPECT_EQ("foo", result.list_value()[1].string_value());
- EXPECT_EQ("bar", result.list_value()[2].string_value());
- EXPECT_EQ("", result.list_value()[3].string_value());
-
- // Test with trimming.
- result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "trim list lines"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::LIST, result.type());
- ASSERT_EQ(2u, result.list_value().size());
- EXPECT_EQ("foo", result.list_value()[0].string_value());
- EXPECT_EQ("bar", result.list_value()[1].string_value());
-}
-
-TEST_F(InputConversionTest, ValueString) {
- Err err;
- std::string input("\"str\"");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::STRING, result.type());
- EXPECT_EQ("str", result.string_value());
-}
-
-TEST_F(InputConversionTest, ValueInt) {
- Err err;
- std::string input("\n\n 6 \n ");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::INTEGER, result.type());
- EXPECT_EQ(6, result.int_value());
-}
-
-TEST_F(InputConversionTest, ValueList) {
- Err err;
- std::string input("\n [ \"a\", 5]");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(Value::LIST, result.type());
- ASSERT_EQ(2u, result.list_value().size());
- EXPECT_EQ("a", result.list_value()[0].string_value());
- EXPECT_EQ(5, result.list_value()[1].int_value());
-}
-
-TEST_F(InputConversionTest, ValueDict) {
- Err err;
- std::string input("\n a = 5 b = \"foo\" c = a + 2");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "scope"), &err);
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(Value::SCOPE, result.type());
-
- const Value* a_value = result.scope_value()->GetValue("a");
- ASSERT_TRUE(a_value);
- EXPECT_EQ(5, a_value->int_value());
-
- const Value* b_value = result.scope_value()->GetValue("b");
- ASSERT_TRUE(b_value);
- EXPECT_EQ("foo", b_value->string_value());
-
- const Value* c_value = result.scope_value()->GetValue("c");
- ASSERT_TRUE(c_value);
- EXPECT_EQ(7, c_value->int_value());
-
- // Tests that when we get Values out of the input conversion, the resulting
- // values have an origin set to something corresponding to the input.
- const ParseNode* a_origin = a_value->origin();
- ASSERT_TRUE(a_origin);
- LocationRange a_range = a_origin->GetRange();
- EXPECT_EQ(2, a_range.begin().line_number());
- EXPECT_EQ(6, a_range.begin().column_number());
-
- const InputFile* a_file = a_range.begin().file();
- EXPECT_EQ(input, a_file->contents());
-}
-
-TEST_F(InputConversionTest, ValueJSON) {
- Err err;
- std::string input(R"*({
- "a": 5,
- "b": "foo",
- "c": {
- "d": true,
- "e": [
- {
- "f": "bar"
- }
- ]
- }
-})*");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "json"), &err);
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(Value::SCOPE, result.type());
-
- const Value* a_value = result.scope_value()->GetValue("a");
- ASSERT_TRUE(a_value);
- EXPECT_EQ(5, a_value->int_value());
-
- const Value* b_value = result.scope_value()->GetValue("b");
- ASSERT_TRUE(b_value);
- EXPECT_EQ("foo", b_value->string_value());
-
- const Value* c_value = result.scope_value()->GetValue("c");
- ASSERT_TRUE(c_value);
- ASSERT_EQ(Value::SCOPE, c_value->type());
-
- const Value* d_value = c_value->scope_value()->GetValue("d");
- ASSERT_TRUE(d_value);
- EXPECT_EQ(true, d_value->boolean_value());
-
- const Value* e_value = c_value->scope_value()->GetValue("e");
- ASSERT_TRUE(e_value);
- ASSERT_EQ(Value::LIST, e_value->type());
-
- EXPECT_EQ(1u, e_value->list_value().size());
- ASSERT_EQ(Value::SCOPE, e_value->list_value()[0].type());
- const Value* f_value = e_value->list_value()[0].scope_value()->GetValue("f");
- ASSERT_TRUE(f_value);
- EXPECT_EQ("bar", f_value->string_value());
-}
-
-TEST_F(InputConversionTest, ValueJSONInvalidInput) {
- Err err;
- std::string input(R"*({
- "a": 5,
- "b":
-})*");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "json"), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Input is not a valid JSON: Line: 4, column: 2, Unexpected token.",
- err.message());
-}
-
-TEST_F(InputConversionTest, ValueJSONUnsupportedValue) {
- Err err;
- std::string input(R"*({
- "a": null
-})*");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "json"), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Null values are not supported.", err.message());
-}
-
-TEST_F(InputConversionTest, ValueJSONInvalidVariable) {
- Err err;
- std::string input(R"*({
- "a\\x0001b": 5
-})*");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "json"), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Invalid identifier \"a\\x0001b\".", err.message());
-}
-
-TEST_F(InputConversionTest, ValueJSONUnsupported) {
- Err err;
- std::string input(R"*({
- "d": 0.0
-})*");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "json"), &err);
- EXPECT_TRUE(err.has_error());
- // Doubles aren't supported.
- EXPECT_EQ("Input is not a valid JSON: ", err.message());
-}
-
-TEST_F(InputConversionTest, ValueEmpty) {
- Err err;
- Value result = ConvertInputToValue(settings(), "", nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::NONE, result.type());
-}
-
-TEST_F(InputConversionTest, ValueError) {
- static const char* const kTests[] = {
- "\n [ \"a\", 5\nfoo bar",
-
- // Blocks not allowed.
- "{ foo = 5 }",
-
- // Function calls not allowed.
- "print(5)",
-
- // Trailing junk not allowed.
- "233105-1",
-
- // Non-literals hidden in arrays are not allowed.
- "[233105 - 1]",
- "[rebase_path(\"//\")]",
- };
-
- for (auto* test : kTests) {
- Err err;
- std::string input(test);
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_TRUE(err.has_error()) << test;
- }
-}
-
-// Passing none or the empty string for input conversion should ignore the
-// result.
-TEST_F(InputConversionTest, Ignore) {
- Err err;
- Value result = ConvertInputToValue(settings(), "foo", nullptr, Value(), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::NONE, result.type());
-
- result =
- ConvertInputToValue(settings(), "foo", nullptr, Value(nullptr, ""), &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(Value::NONE, result.type());
-}
diff --git a/gn/tools/gn/input_file.cc b/gn/tools/gn/input_file.cc
deleted file mode 100644
index dc5ca37fe40..00000000000
--- a/gn/tools/gn/input_file.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 "tools/gn/input_file.h"
-
-#include "base/files/file_util.h"
-
-InputFile::InputFile(const SourceFile& name)
- : name_(name), dir_(name_.GetDir()) {}
-
-InputFile::~InputFile() = default;
-
-void InputFile::SetContents(const std::string& c) {
- contents_loaded_ = true;
- contents_ = c;
-}
-
-bool InputFile::Load(const base::FilePath& system_path) {
- if (base::ReadFileToString(system_path, &contents_)) {
- contents_loaded_ = true;
- physical_name_ = system_path;
- return true;
- }
- return false;
-}
diff --git a/gn/tools/gn/input_file.h b/gn/tools/gn/input_file.h
deleted file mode 100644
index 7a64c658065..00000000000
--- a/gn/tools/gn/input_file.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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 TOOLS_GN_INPUT_FILE_H_
-#define TOOLS_GN_INPUT_FILE_H_
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-
-class InputFile {
- public:
- explicit InputFile(const SourceFile& name);
-
- ~InputFile();
-
- // The virtual name passed into the constructor. This does not take into
- // account whether the file was loaded from the secondary source tree (see
- // BuildSettings secondary_source_path).
- const SourceFile& name() const { return name_; }
-
- // The directory is just a cached version of name()->GetDir() but we get this
- // a lot so computing it once up front saves a bunch of work.
- const SourceDir& dir() const { return dir_; }
-
- // The physical name tells the actual name on disk, if there is one.
- const base::FilePath& physical_name() const { return physical_name_; }
-
- // The friendly name can be set to override the name() in cases where there
- // is no name (like SetContents is used instead) or if the name doesn't
- // make sense. This will be displayed in error messages.
- const std::string& friendly_name() const { return friendly_name_; }
- void set_friendly_name(const std::string& f) { friendly_name_ = f; }
-
- const std::string& contents() const {
- DCHECK(contents_loaded_);
- return contents_;
- }
-
- // For testing and in cases where this input doesn't actually refer to
- // "a file".
- void SetContents(const std::string& c);
-
- // Loads the given file synchronously, returning true on success. This
- bool Load(const base::FilePath& system_path);
-
- private:
- SourceFile name_;
- SourceDir dir_;
-
- base::FilePath physical_name_;
- std::string friendly_name_;
-
- bool contents_loaded_ = false;
- std::string contents_;
-
- DISALLOW_COPY_AND_ASSIGN(InputFile);
-};
-
-#endif // TOOLS_GN_INPUT_FILE_H_
diff --git a/gn/tools/gn/input_file_manager.cc b/gn/tools/gn/input_file_manager.cc
deleted file mode 100644
index aad8cc82294..00000000000
--- a/gn/tools/gn/input_file_manager.cc
+++ /dev/null
@@ -1,332 +0,0 @@
-// 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 "tools/gn/input_file_manager.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/stl_util.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope_per_file_provider.h"
-#include "tools/gn/tokenizer.h"
-#include "tools/gn/trace.h"
-
-namespace {
-
-// The opposite of std::lock_guard.
-struct ScopedUnlock {
- ScopedUnlock(std::unique_lock<std::mutex>& lock) : lock_(lock) {
- lock_.unlock();
- }
- ~ScopedUnlock() { lock_.lock(); }
-
- private:
- std::unique_lock<std::mutex>& lock_;
-};
-
-void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb,
- const ParseNode* node) {
- cb.Run(node);
-}
-
-bool DoLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& name,
- InputFile* file,
- std::vector<Token>* tokens,
- std::unique_ptr<ParseNode>* root,
- Err* err) {
- // Do all of this stuff outside the lock. We should not give out file
- // pointers until the read is complete.
- if (g_scheduler->verbose_logging()) {
- std::string logmsg = name.value();
- if (origin.begin().file())
- logmsg += " (referenced from " + origin.begin().Describe(false) + ")";
- g_scheduler->Log("Loading", logmsg);
- }
-
- // Read.
- base::FilePath primary_path = build_settings->GetFullPath(name);
- ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, name.value());
- if (!file->Load(primary_path)) {
- if (!build_settings->secondary_source_path().empty()) {
- // Fall back to secondary source tree.
- base::FilePath secondary_path =
- build_settings->GetFullPathSecondary(name);
- if (!file->Load(secondary_path)) {
- *err = Err(origin, "Can't load input file.",
- "Unable to load:\n " + FilePathToUTF8(primary_path) +
- "\n"
- "I also checked in the secondary tree for:\n " +
- FilePathToUTF8(secondary_path));
- return false;
- }
- } else {
- *err = Err(origin,
- "Unable to load \"" + FilePathToUTF8(primary_path) + "\".");
- return false;
- }
- }
- load_trace.Done();
-
- ScopedTrace exec_trace(TraceItem::TRACE_FILE_PARSE, name.value());
-
- // Tokenize.
- *tokens = Tokenizer::Tokenize(file, err);
- if (err->has_error())
- return false;
-
- // Parse.
- *root = Parser::Parse(*tokens, err);
- if (err->has_error())
- return false;
-
- exec_trace.Done();
- return true;
-}
-
-} // namespace
-
-InputFileManager::InputFileData::InputFileData(const SourceFile& file_name)
- : file(file_name), loaded(false), sync_invocation(false) {}
-
-InputFileManager::InputFileData::~InputFileData() = default;
-
-InputFileManager::InputFileManager() = default;
-
-InputFileManager::~InputFileManager() {
- // Should be single-threaded by now.
-}
-
-bool InputFileManager::AsyncLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- const FileLoadCallback& callback,
- Err* err) {
- // Try not to schedule callbacks while holding the lock. All cases that don't
- // want to schedule should return early. Otherwise, this will be scheduled
- // after we leave the lock.
- Task schedule_this;
- {
- std::lock_guard<std::mutex> lock(lock_);
-
- InputFileMap::const_iterator found = input_files_.find(file_name);
- if (found == input_files_.end()) {
- // New file, schedule load.
- std::unique_ptr<InputFileData> data =
- std::make_unique<InputFileData>(file_name);
- data->scheduled_callbacks.push_back(callback);
- schedule_this =
- base::BindOnce(&InputFileManager::BackgroundLoadFile, this, origin,
- build_settings, file_name, &data->file);
- input_files_[file_name] = std::move(data);
-
- } else {
- InputFileData* data = found->second.get();
-
- // Prevent mixing async and sync loads. See SyncLoadFile for discussion.
- if (data->sync_invocation) {
- g_scheduler->FailWithError(Err(
- origin, "Load type mismatch.",
- "The file \"" + file_name.value() +
- "\" was previously loaded\n"
- "synchronously (via an import) and now you're trying to load "
- "it "
- "asynchronously\n(via a deps rule). This is a class 2 "
- "misdemeanor: "
- "a single input file must\nbe loaded the same way each time to "
- "avoid blowing my tiny, tiny mind."));
- return false;
- }
-
- if (data->loaded) {
- // Can just directly issue the callback on the background thread.
- schedule_this = base::BindOnce(&InvokeFileLoadCallback, callback,
- data->parsed_root.get());
- } else {
- // Load is pending on this file, schedule the invoke.
- data->scheduled_callbacks.push_back(callback);
- return true;
- }
- }
- }
- g_scheduler->ScheduleWork(std::move(schedule_this));
- return true;
-}
-
-const ParseNode* InputFileManager::SyncLoadFile(
- const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- Err* err) {
- std::unique_lock<std::mutex> lock(lock_);
-
- InputFileData* data = nullptr;
- InputFileMap::iterator found = input_files_.find(file_name);
- if (found == input_files_.end()) {
- // Haven't seen this file yet, start loading right now.
- std::unique_ptr<InputFileData> new_data =
- std::make_unique<InputFileData>(file_name);
- data = new_data.get();
- data->sync_invocation = true;
- input_files_[file_name] = std::move(new_data);
-
- ScopedUnlock unlock(lock);
- if (!LoadFile(origin, build_settings, file_name, &data->file, err))
- return nullptr;
- } else {
- // This file has either been loaded or is pending loading.
- data = found->second.get();
-
- if (!data->sync_invocation) {
- // Don't allow mixing of sync and async loads. If an async load is
- // scheduled and then a bunch of threads need to load it synchronously
- // and block on it loading, it could deadlock or at least cause a lot
- // of wasted CPU while those threads wait for the load to complete (which
- // may be far back in the input queue).
- //
- // We could work around this by promoting the load to a sync load. This
- // requires a bunch of extra code to either check flags and likely do
- // extra locking (bad) or to just do both types of load on the file and
- // deal with the race condition.
- //
- // I have no practical way to test this, and generally we should have
- // all include files processed synchronously and all build files
- // processed asynchronously, so it doesn't happen in practice.
- *err = Err(origin, "Load type mismatch.",
- "The file \"" + file_name.value() +
- "\" was previously loaded\n"
- "asynchronously (via a deps rule) and now you're trying "
- "to load it "
- "synchronously.\nThis is a class 2 misdemeanor: a single "
- "input file "
- "must be loaded the same way\neach time to avoid blowing "
- "my tiny, "
- "tiny mind.");
- return nullptr;
- }
-
- if (!data->loaded) {
- // Wait for the already-pending sync load to complete.
- if (!data->completion_event) {
- data->completion_event = std::make_unique<AutoResetEvent>();
- }
- {
- ScopedUnlock unlock(lock);
- data->completion_event->Wait();
- }
- // If there were multiple waiters on the same event, we now need to wake
- // up the next one.
- data->completion_event->Signal();
- }
- }
-
- // The other load could have failed. It is possible that this thread's error
- // will be reported to the scheduler before the other thread's (and the first
- // error reported "wins"). Forward the parse error from the other load for
- // this thread so that the error message is useful.
- if (!data->parsed_root)
- *err = data->parse_error;
- return data->parsed_root.get();
-}
-
-void InputFileManager::AddDynamicInput(
- const SourceFile& name,
- InputFile** file,
- std::vector<Token>** tokens,
- std::unique_ptr<ParseNode>** parse_root) {
- std::unique_ptr<InputFileData> data = std::make_unique<InputFileData>(name);
- *file = &data->file;
- *tokens = &data->tokens;
- *parse_root = &data->parsed_root;
- {
- std::lock_guard<std::mutex> lock(lock_);
- dynamic_inputs_.push_back(std::move(data));
- }
-}
-
-int InputFileManager::GetInputFileCount() const {
- std::lock_guard<std::mutex> lock(lock_);
- return static_cast<int>(input_files_.size());
-}
-
-void InputFileManager::GetAllPhysicalInputFileNames(
- std::vector<base::FilePath>* result) const {
- std::lock_guard<std::mutex> lock(lock_);
- result->reserve(input_files_.size());
- for (const auto& file : input_files_) {
- if (!file.second->file.physical_name().empty())
- result->push_back(file.second->file.physical_name());
- }
-}
-
-void InputFileManager::BackgroundLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& name,
- InputFile* file) {
- Err err;
- if (!LoadFile(origin, build_settings, name, file, &err))
- g_scheduler->FailWithError(err);
-}
-
-bool InputFileManager::LoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& name,
- InputFile* file,
- Err* err) {
- std::vector<Token> tokens;
- std::unique_ptr<ParseNode> root;
- bool success =
- DoLoadFile(origin, build_settings, name, file, &tokens, &root, err);
- // Can't return early. We have to ensure that the completion event is
- // signaled in all cases bacause another thread could be blocked on this one.
-
- // Save this pointer for running the callbacks below, which happens after the
- // scoped ptr ownership is taken away inside the lock.
- ParseNode* unowned_root = root.get();
-
- std::vector<FileLoadCallback> callbacks;
- {
- std::lock_guard<std::mutex> lock(lock_);
- DCHECK(input_files_.find(name) != input_files_.end());
-
- InputFileData* data = input_files_[name].get();
- data->loaded = true;
- if (success) {
- data->tokens = std::move(tokens);
- data->parsed_root = std::move(root);
- } else {
- data->parse_error = *err;
- }
-
- // Unblock waiters on this event.
- //
- // It's somewhat bad to signal this inside the lock. When it's used, it's
- // lazily created inside the lock. So we need to do the check and signal
- // inside the lock to avoid race conditions on the lazy creation of the
- // lock.
- //
- // We could avoid this by creating the lock every time, but the lock is
- // very seldom used and will generally be NULL, so my current theory is that
- // several signals of a completion event inside a lock is better than
- // creating about 1000 extra locks (one for each file).
- if (data->completion_event)
- data->completion_event->Signal();
-
- callbacks = std::move(data->scheduled_callbacks);
- }
-
- // Run pending invocations. Theoretically we could schedule each of these
- // separately to get some parallelism. But normally there will only be one
- // item in the list, so that's extra overhead and complexity for no gain.
- if (success) {
- for (const auto& cb : callbacks)
- cb.Run(unowned_root);
- }
- return success;
-}
diff --git a/gn/tools/gn/input_file_manager.h b/gn/tools/gn/input_file_manager.h
deleted file mode 100644
index c4f19e97ac2..00000000000
--- a/gn/tools/gn/input_file_manager.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// 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 TOOLS_GN_INPUT_FILE_MANAGER_H_
-#define TOOLS_GN_INPUT_FILE_MANAGER_H_
-
-#include <mutex>
-#include <set>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/settings.h"
-#include "util/auto_reset_event.h"
-
-class Err;
-class LocationRange;
-class ParseNode;
-class Token;
-
-// Manages loading and parsing files from disk. This doesn't actually have
-// any context for executing the results, so potentially multiple configs
-// could use the same input file (saving parsing).
-//
-// This class is threadsafe.
-//
-// InputFile objects must never be deleted while the program is running since
-// various state points into them.
-class InputFileManager : public base::RefCountedThreadSafe<InputFileManager> {
- public:
- // Callback issued when a file is laoded. On auccess, the parse node will
- // refer to the root block of the file. On failure, this will be NULL.
- using FileLoadCallback = base::Callback<void(const ParseNode*)>;
-
- InputFileManager();
-
- // Loads the given file and executes the callback on the worker pool.
- //
- // There are two types of errors. For errors known synchronously, the error
- // will be set, it will return false, and no work will be scheduled.
- //
- // For parse errors and such that happen in the future, the error will be
- // logged to the scheduler and the callback will be invoked with a null
- // ParseNode pointer. The given |origin| will be blamed for the invocation.
- bool AsyncLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- const FileLoadCallback& callback,
- Err* err);
-
- // Loads and parses the given file synchronously, returning the root block
- // corresponding to the parsed result. On error, return NULL and the given
- // Err is set.
- const ParseNode* SyncLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- Err* err);
-
- // Creates an entry to manage the memory associated with keeping a parsed
- // set of code in memory.
- //
- // The values pointed to by the parameters will be filled with pointers to
- // the file, tokens, and parse node that this class created. The calling
- // code is responsible for populating these values and maintaining
- // threadsafety. This class' only job is to hold onto the memory and delete
- // it when the program exits.
- //
- // This solves the problem that sometimes we need to execute something
- // dynamic and save the result, but the values all have references to the
- // nodes and file that created it. Either we need to reset the origin of
- // the values and lose context for error reporting, or somehow keep the
- // associated parse nodes, tokens, and file data in memory. This function
- // allows the latter.
- void AddDynamicInput(const SourceFile& name,
- InputFile** file,
- std::vector<Token>** tokens,
- std::unique_ptr<ParseNode>** parse_root);
-
- // Does not count dynamic input.
- int GetInputFileCount() const;
-
- // Fills the vector with all input files.
- void GetAllPhysicalInputFileNames(std::vector<base::FilePath>* result) const;
-
- private:
- friend class base::RefCountedThreadSafe<InputFileManager>;
-
- struct InputFileData {
- explicit InputFileData(const SourceFile& file_name);
- ~InputFileData();
-
- // Don't touch this outside the lock until it's marked loaded.
- InputFile file;
-
- bool loaded;
-
- bool sync_invocation;
-
- // Lists all invocations that need to be executed when the file completes
- // loading.
- std::vector<FileLoadCallback> scheduled_callbacks;
-
- // Event to signal when the load is complete (or fails). This is lazily
- // created only when a thread is synchronously waiting for this load (which
- // only happens for imports).
- std::unique_ptr<AutoResetEvent> completion_event;
-
- std::vector<Token> tokens;
-
- // Null before the file is loaded or if loading failed.
- std::unique_ptr<ParseNode> parsed_root;
- Err parse_error;
- };
-
- virtual ~InputFileManager();
-
- void BackgroundLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& name,
- InputFile* file);
-
- // Loads the given file. On error, sets the Err and return false.
- bool LoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& name,
- InputFile* file,
- Err* err);
-
- mutable std::mutex lock_;
-
- // Maps repo-relative filenames to the corresponding owned pointer.
- using InputFileMap =
- std::unordered_map<SourceFile, std::unique_ptr<InputFileData>>;
- InputFileMap input_files_;
-
- // Tracks all dynamic inputs. The data are holders for memory management
- // purposes and should not be read or modified by this class. The values
- // will be vended out to the code creating the dynamic input, who is in
- // charge of the threadsafety requirements.
- //
- // See AddDynamicInput().
- std::vector<std::unique_ptr<InputFileData>> dynamic_inputs_;
-
- DISALLOW_COPY_AND_ASSIGN(InputFileManager);
-};
-
-#endif // TOOLS_GN_INPUT_FILE_MANAGER_H_
diff --git a/gn/tools/gn/item.cc b/gn/tools/gn/item.cc
deleted file mode 100644
index f36ff023b36..00000000000
--- a/gn/tools/gn/item.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 "tools/gn/item.h"
-
-#include "base/logging.h"
-#include "tools/gn/settings.h"
-
-Item::Item(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files)
- : settings_(settings),
- label_(label),
- build_dependency_files_(build_dependency_files),
- defined_from_(nullptr) {}
-
-Item::~Item() = default;
-
-Config* Item::AsConfig() {
- return nullptr;
-}
-const Config* Item::AsConfig() const {
- return nullptr;
-}
-Pool* Item::AsPool() {
- return nullptr;
-}
-const Pool* Item::AsPool() const {
- return nullptr;
-}
-Target* Item::AsTarget() {
- return nullptr;
-}
-const Target* Item::AsTarget() const {
- return nullptr;
-}
-Toolchain* Item::AsToolchain() {
- return nullptr;
-}
-const Toolchain* Item::AsToolchain() const {
- return nullptr;
-}
-
-std::string Item::GetItemTypeName() const {
- if (AsConfig())
- return "config";
- if (AsTarget())
- return "target";
- if (AsToolchain())
- return "toolchain";
- if (AsPool())
- return "pool";
- NOTREACHED();
- return "this thing that I have no idea what it is";
-}
-
-bool Item::OnResolved(Err* err) {
- return true;
-}
diff --git a/gn/tools/gn/item.h b/gn/tools/gn/item.h
deleted file mode 100644
index f2e7c0658b8..00000000000
--- a/gn/tools/gn/item.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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 TOOLS_GN_ITEM_H_
-#define TOOLS_GN_ITEM_H_
-
-#include <set>
-#include <string>
-
-#include "tools/gn/label.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/visibility.h"
-
-class Config;
-class ParseNode;
-class Pool;
-class Settings;
-class SourceFile;
-class Target;
-class Toolchain;
-
-// A named item (target, config, etc.) that participates in the dependency
-// graph.
-class Item {
- public:
- Item(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files = {});
- virtual ~Item();
-
- const Settings* settings() const { return settings_; }
-
- // This is guaranteed to never change after construction so this can be
- // accessed from any thread with no locking once the item is constructed.
- const Label& label() const { return label_; }
-
- const ParseNode* defined_from() const { return defined_from_; }
- void set_defined_from(const ParseNode* df) { defined_from_ = df; }
-
- Visibility& visibility() { return visibility_; }
- const Visibility& visibility() const { return visibility_; }
-
- // Manual RTTI.
- virtual Config* AsConfig();
- virtual const Config* AsConfig() const;
- virtual Pool* AsPool();
- virtual const Pool* AsPool() const;
- virtual Target* AsTarget();
- virtual const Target* AsTarget() const;
- virtual Toolchain* AsToolchain();
- virtual const Toolchain* AsToolchain() const;
-
- // Returns a name like "target" or "config" for the type of item this is, to
- // be used in logging and error messages.
- std::string GetItemTypeName() const;
-
- // Returns the set of build files that may affect this item, please refer to
- // Scope for how this is determined.
- const std::set<SourceFile>& build_dependency_files() const {
- return build_dependency_files_;
- }
-
- std::set<SourceFile>& build_dependency_files() {
- return build_dependency_files_;
- }
-
- // Called when this item is resolved, meaning it and all of its dependents
- // have no unresolved deps. Returns true on success. Sets the error and
- // returns false on failure.
- virtual bool OnResolved(Err* err);
-
- private:
- const Settings* settings_;
- Label label_;
- std::set<SourceFile> build_dependency_files_;
- const ParseNode* defined_from_;
-
- Visibility visibility_;
-};
-
-#endif // TOOLS_GN_ITEM_H_
diff --git a/gn/tools/gn/json_project_writer.cc b/gn/tools/gn/json_project_writer.cc
deleted file mode 100644
index 1b000865569..00000000000
--- a/gn/tools/gn/json_project_writer.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright (c) 2016 The Chromium 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 "tools/gn/json_project_writer.h"
-
-#include <iostream>
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/json/json_writer.h"
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/desc_builder.h"
-#include "tools/gn/exec_process.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/settings.h"
-
-// Structure of JSON output file
-// {
-// "build_settings" = {
-// "root_path" : "absolute path of project root",
-// "build_dir" : "build directory (project relative)",
-// "default_toolchain" : "name of default toolchain"
-// }
-// "targets" = {
-// "target x name" : { target x properties },
-// "target y name" : { target y properties },
-// ...
-// }
-// }
-// See desc_builder.cc for overview of target properties
-
-namespace {
-
-void AddTargetDependencies(const Target* target,
- std::set<const Target*>* deps) {
- for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
- if (deps->find(pair.ptr) == deps->end()) {
- deps->insert(pair.ptr);
- AddTargetDependencies(pair.ptr, deps);
- }
- }
-}
-
-// Filters targets according to filter string; Will also recursively
-// add dependent targets.
-bool FilterTargets(const BuildSettings* build_settings,
- std::vector<const Target*>& all_targets,
- std::vector<const Target*>* targets,
- const std::string& dir_filter_string,
- Err* err) {
- if (dir_filter_string.empty()) {
- *targets = all_targets;
- } else {
- targets->reserve(all_targets.size());
- std::vector<LabelPattern> filters;
- if (!commands::FilterPatternsFromString(build_settings, dir_filter_string,
- &filters, err)) {
- return false;
- }
- commands::FilterTargetsByPatterns(all_targets, filters, targets);
-
- std::set<const Target*> target_set(targets->begin(), targets->end());
- for (const auto* target : *targets)
- AddTargetDependencies(target, &target_set);
-
- targets->clear();
- targets->insert(targets->end(), target_set.begin(), target_set.end());
- }
-
- // Sort the list of targets per-label to get a consistent ordering of them
- // in the generated project (and thus stability of the file generated).
- std::sort(targets->begin(), targets->end(),
- [](const Target* a, const Target* b) {
- return a->label().name() < b->label().name();
- });
-
- return true;
-}
-
-bool InvokePython(const BuildSettings* build_settings,
- const base::FilePath& python_script_path,
- const std::string& python_script_extra_args,
- const base::FilePath& output_path,
- bool quiet,
- Err* err) {
- const base::FilePath& python_path = build_settings->python_path();
- base::CommandLine cmdline(python_path);
- cmdline.AppendArg("--");
- cmdline.AppendArgPath(python_script_path);
- cmdline.AppendArgPath(output_path);
- if (!python_script_extra_args.empty()) {
- cmdline.AppendArg(python_script_extra_args);
- }
- base::FilePath startup_dir =
- build_settings->GetFullPath(build_settings->build_dir());
-
- std::string output;
- std::string stderr_output;
-
- int exit_code = 0;
- if (!internal::ExecProcess(cmdline, startup_dir, &output, &stderr_output,
- &exit_code)) {
- *err =
- Err(Location(), "Could not execute python.",
- "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
- return false;
- }
-
- if (!quiet) {
- std::cout << output;
- std::cerr << stderr_output;
- }
-
- if (exit_code != 0) {
- *err = Err(Location(), "Python has quit with exit code " +
- base::IntToString(exit_code) + ".");
- return false;
- }
-
- return true;
-}
-
-} // namespace
-
-bool JSONProjectWriter::RunAndWriteFiles(
- const BuildSettings* build_settings,
- const Builder& builder,
- const std::string& file_name,
- const std::string& exec_script,
- const std::string& exec_script_extra_args,
- const std::string& dir_filter_string,
- bool quiet,
- Err* err) {
- SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
- Value(nullptr, file_name), err);
- if (output_file.is_null()) {
- return false;
- }
-
- base::FilePath output_path = build_settings->GetFullPath(output_file);
-
- std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
- std::vector<const Target*> targets;
- if (!FilterTargets(build_settings, all_targets, &targets, dir_filter_string,
- err)) {
- return false;
- }
-
- std::string json = RenderJSON(build_settings, targets);
- if (!ContentsEqual(output_path, json)) {
- if (!WriteFileIfChanged(output_path, json, err)) {
- return false;
- }
-
- if (!exec_script.empty()) {
- SourceFile script_file;
- if (exec_script[0] != '/') {
- // Relative path, assume the base is in build_dir.
- script_file = build_settings->build_dir().ResolveRelativeFile(
- Value(nullptr, exec_script), err);
- if (script_file.is_null()) {
- return false;
- }
- } else {
- script_file = SourceFile(exec_script);
- }
- base::FilePath script_path = build_settings->GetFullPath(script_file);
- return InvokePython(build_settings, script_path, exec_script_extra_args,
- output_path, quiet, err);
- }
- }
-
- return true;
-}
-
-std::string JSONProjectWriter::RenderJSON(
- const BuildSettings* build_settings,
- std::vector<const Target*>& all_targets) {
- Label default_toolchain_label;
-
- auto targets = std::make_unique<base::DictionaryValue>();
- for (const auto* target : all_targets) {
- if (default_toolchain_label.is_null())
- default_toolchain_label = target->settings()->default_toolchain_label();
- auto description =
- DescBuilder::DescriptionForTarget(target, "", false, false, false);
- // Outputs need to be asked for separately.
- auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs",
- false, false, false);
- base::DictionaryValue* outputs_value = nullptr;
- if (outputs->GetDictionary("source_outputs", &outputs_value) &&
- !outputs_value->empty()) {
- description->MergeDictionary(outputs.get());
- }
- targets->SetWithoutPathExpansion(
- target->label().GetUserVisibleName(default_toolchain_label),
- std::move(description));
- }
-
- auto settings = std::make_unique<base::DictionaryValue>();
- settings->SetKey("root_path", base::Value(build_settings->root_path_utf8()));
- settings->SetKey("build_dir",
- base::Value(build_settings->build_dir().value()));
- settings->SetKey(
- "default_toolchain",
- base::Value(default_toolchain_label.GetUserVisibleName(false)));
-
- auto output = std::make_unique<base::DictionaryValue>();
- output->SetWithoutPathExpansion("targets", std::move(targets));
- output->SetWithoutPathExpansion("build_settings", std::move(settings));
-
- std::string s;
- base::JSONWriter::WriteWithOptions(
- *output.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
- return s;
-}
diff --git a/gn/tools/gn/json_project_writer.h b/gn/tools/gn/json_project_writer.h
deleted file mode 100644
index 2c8a2b7102a..00000000000
--- a/gn/tools/gn/json_project_writer.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2016 The Chromium 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 TOOLS_GN_JSON_WRITER_H_
-#define TOOLS_GN_JSON_WRITER_H_
-
-#include "tools/gn/err.h"
-#include "tools/gn/target.h"
-
-class Builder;
-class BuildSettings;
-
-class JSONProjectWriter {
- public:
- static bool RunAndWriteFiles(const BuildSettings* build_setting,
- const Builder& builder,
- const std::string& file_name,
- const std::string& exec_script,
- const std::string& exec_script_extra_args,
- const std::string& dir_filter_string,
- bool quiet,
- Err* err);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ActionWithResponseFile);
- FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ForEachWithResponseFile);
-
- static std::string RenderJSON(const BuildSettings* build_settings,
- std::vector<const Target*>& all_targets);
-};
-
-#endif
diff --git a/gn/tools/gn/json_project_writer_unittest.cc b/gn/tools/gn/json_project_writer_unittest.cc
deleted file mode 100644
index 9c933dd5ef0..00000000000
--- a/gn/tools/gn/json_project_writer_unittest.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) 2019 The Chromium 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/strings/string_util.h"
-#include "tools/gn/json_project_writer.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-TEST(JSONProjectWriter, ActionWithResponseFile) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION);
-
- target.sources().push_back(SourceFile("//foo/source1.txt"));
- target.config_values().inputs().push_back(SourceFile("//foo/input1.txt"));
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Make sure we get interesting substitutions for both the args and the
- // response file contents.
- target.action_values().args() =
- SubstitutionList::MakeForTest("{{response_file_name}}");
- target.action_values().rsp_file_contents() =
- SubstitutionList::MakeForTest("-j", "3");
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/output1.out");
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
- std::vector<const Target*> targets;
- targets.push_back(&target);
- std::string out =
- JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
-#if defined(OS_WIN)
- base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
-#endif
- const char expected_json[] =
- "{\n"
- " \"build_settings\": {\n"
- " \"build_dir\": \"//out/Debug/\",\n"
- " \"default_toolchain\": \"//toolchain:default\",\n"
- " \"root_path\": \"\"\n"
- " },\n"
- " \"targets\": {\n"
- " \"//foo:bar()\": {\n"
- " \"args\": [ \"{{response_file_name}}\" ],\n"
- " \"deps\": [ ],\n"
- " \"inputs\": [ \"//foo/input1.txt\" ],\n"
- " \"metadata\": {\n"
- "\n"
- " },\n"
- " \"outputs\": [ \"//out/Debug/output1.out\" ],\n"
- " \"public\": \"*\",\n"
- " \"response_file_contents\": [ \"-j\", \"3\" ],\n"
- " \"script\": \"//foo/script.py\",\n"
- " \"sources\": [ \"//foo/source1.txt\" ],\n"
- " \"testonly\": false,\n"
- " \"toolchain\": \"\",\n"
- " \"type\": \"action\",\n"
- " \"visibility\": [ ]\n"
- " }\n"
- " }\n"
- "}\n";
- EXPECT_EQ(expected_json, out);
-}
-
-TEST(JSONProjectWriter, ForEachWithResponseFile) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION_FOREACH);
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Make sure we get interesting substitutions for both the args and the
- // response file contents.
- target.action_values().args() = SubstitutionList::MakeForTest(
- "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
- target.action_values().rsp_file_contents() =
- SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
- std::vector<const Target*> targets;
- targets.push_back(&target);
- std::string out =
- JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
-#if defined(OS_WIN)
- base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
-#endif
- const char expected_json[] =
- "{\n"
- " \"build_settings\": {\n"
- " \"build_dir\": \"//out/Debug/\",\n"
- " \"default_toolchain\": \"//toolchain:default\",\n"
- " \"root_path\": \"\"\n"
- " },\n"
- " \"targets\": {\n"
- " \"//foo:bar()\": {\n"
- " \"args\": [ \"{{source}}\", \"{{source_file_part}}\", "
- "\"{{response_file_name}}\" ],\n"
- " \"deps\": [ ],\n"
- " \"metadata\": {\n"
- "\n"
- " },\n"
- " \"output_patterns\": [ "
- "\"//out/Debug/{{source_name_part}}.out\" ],\n"
- " \"outputs\": [ \"//out/Debug/input1.out\" ],\n"
- " \"public\": \"*\",\n"
- " \"response_file_contents\": [ \"-j\", \"{{source_name_part}}\" "
- "],\n"
- " \"script\": \"//foo/script.py\",\n"
- " \"sources\": [ \"//foo/input1.txt\" ],\n"
- " \"testonly\": false,\n"
- " \"toolchain\": \"\",\n"
- " \"type\": \"action_foreach\",\n"
- " \"visibility\": [ ]\n"
- " }\n"
- " }\n"
- "}\n";
- EXPECT_EQ(expected_json, out);
-}
diff --git a/gn/tools/gn/label.cc b/gn/tools/gn/label.cc
deleted file mode 100644
index 22b6f810a99..00000000000
--- a/gn/tools/gn/label.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-// 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 "tools/gn/label.h"
-
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/value.h"
-#include "util/build_config.h"
-
-namespace {
-
-// We print user visible label names with no trailing slash after the
-// directory name.
-std::string DirWithNoTrailingSlash(const SourceDir& dir) {
- // Be careful not to trim if the input is just "/" or "//".
- if (dir.value().size() > 2)
- return dir.value().substr(0, dir.value().size() - 1);
- return dir.value();
-}
-
-// Given the separate-out input (everything before the colon) in the dep rule,
-// computes the final build rule. Sets err on failure. On success,
-// |*used_implicit| will be set to whether the implicit current directory was
-// used. The value is used only for generating error messages.
-bool ComputeBuildLocationFromDep(const Value& input_value,
- const SourceDir& current_dir,
- const base::StringPiece& input,
- SourceDir* result,
- Err* err) {
- // No rule, use the current location.
- if (input.empty()) {
- *result = current_dir;
- return true;
- }
-
- *result = current_dir.ResolveRelativeDir(input_value, input, err);
- return true;
-}
-
-// Given the separated-out target name (after the colon) computes the final
-// name, using the implicit name from the previously-generated
-// computed_location if necessary. The input_value is used only for generating
-// error messages.
-bool ComputeTargetNameFromDep(const Value& input_value,
- const SourceDir& computed_location,
- const base::StringPiece& input,
- std::string* result,
- Err* err) {
- if (!input.empty()) {
- // Easy case: input is specified, just use it.
- result->assign(input.data(), input.size());
- return true;
- }
-
- const std::string& loc = computed_location.value();
-
- // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc.
- if (loc.size() <= 2) {
- *err = Err(input_value, "This dependency name is empty");
- return false;
- }
-
- size_t next_to_last_slash = loc.rfind('/', loc.size() - 2);
- DCHECK(next_to_last_slash != std::string::npos);
- result->assign(&loc[next_to_last_slash + 1],
- loc.size() - next_to_last_slash - 2);
- return true;
-}
-
-// The original value is used only for error reporting, use the |input| as the
-// input to this function (which may be a substring of the original value when
-// we're parsing toolchains.
-//
-// If the output toolchain vars are NULL, then we'll report an error if we
-// find a toolchain specified (this is used when recursively parsing toolchain
-// labels which themselves can't have toolchain specs).
-//
-// We assume that the output variables are initialized to empty so we don't
-// write them unless we need them to contain something.
-//
-// Returns true on success. On failure, the out* variables might be written to
-// but shouldn't be used.
-bool Resolve(const SourceDir& current_dir,
- const Label& current_toolchain,
- const Value& original_value,
- const base::StringPiece& input,
- SourceDir* out_dir,
- std::string* out_name,
- SourceDir* out_toolchain_dir,
- std::string* out_toolchain_name,
- Err* err) {
- // To workaround the problem that StringPiece operator[] doesn't return a ref.
- const char* input_str = input.data();
- size_t offset = 0;
-#if defined(OS_WIN)
- if (IsPathAbsolute(input)) {
- size_t drive_letter_pos = input[0] == '/' ? 1 : 0;
- if (input.size() > drive_letter_pos + 2 &&
- input[drive_letter_pos + 1] == ':' &&
- IsSlash(input[drive_letter_pos + 2]) &&
- base::IsAsciiAlpha(input[drive_letter_pos])) {
- // Skip over the drive letter colon.
- offset = drive_letter_pos + 2;
- }
- }
-#endif
- size_t path_separator = input.find_first_of(":(", offset);
- base::StringPiece location_piece;
- base::StringPiece name_piece;
- base::StringPiece toolchain_piece;
- if (path_separator == std::string::npos) {
- location_piece = input;
- // Leave name & toolchain piece null.
- } else {
- location_piece = base::StringPiece(&input_str[0], path_separator);
-
- size_t toolchain_separator = input.find('(', path_separator);
- if (toolchain_separator == std::string::npos) {
- name_piece = base::StringPiece(&input_str[path_separator + 1],
- input.size() - path_separator - 1);
- // Leave location piece null.
- } else if (!out_toolchain_dir) {
- // Toolchain specified but not allows in this context.
- *err =
- Err(original_value, "Toolchain has a toolchain.",
- "Your toolchain definition (inside the parens) seems to itself "
- "have a\ntoolchain. Don't do this.");
- return false;
- } else {
- // Name piece is everything between the two separators. Note that the
- // separators may be the same (e.g. "//foo(bar)" which means empty name.
- if (toolchain_separator > path_separator) {
- name_piece =
- base::StringPiece(&input_str[path_separator + 1],
- toolchain_separator - path_separator - 1);
- }
-
- // Toolchain name should end in a ) and this should be the end of the
- // string.
- if (input[input.size() - 1] != ')') {
- *err =
- Err(original_value, "Bad toolchain name.",
- "Toolchain name must end in a \")\" at the end of the label.");
- return false;
- }
-
- // Subtract off the two parens to just get the toolchain name.
- toolchain_piece =
- base::StringPiece(&input_str[toolchain_separator + 1],
- input.size() - toolchain_separator - 2);
- }
- }
-
- // Everything before the separator is the filename.
- // We allow three cases:
- // Absolute: "//foo:bar" -> /foo:bar
- // Target in current file: ":foo" -> <currentdir>:foo
- // Path with implicit name: "/foo" -> /foo:foo
- if (location_piece.empty() && name_piece.empty()) {
- // Can't use both implicit filename and name (":").
- *err = Err(original_value, "This doesn't specify a dependency.");
- return false;
- }
-
- if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece,
- out_dir, err))
- return false;
-
- if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece, out_name,
- err))
- return false;
-
- // Last, do the toolchains.
- if (out_toolchain_dir) {
- // Handle empty toolchain strings. We don't allow normal labels to be
- // empty so we can't allow the recursive call of this function to do this
- // check.
- if (toolchain_piece.empty()) {
- *out_toolchain_dir = current_toolchain.dir();
- *out_toolchain_name = current_toolchain.name();
- return true;
- } else {
- return Resolve(current_dir, current_toolchain, original_value,
- toolchain_piece, out_toolchain_dir, out_toolchain_name,
- nullptr, nullptr, err);
- }
- }
- return true;
-}
-
-} // namespace
-
-const char kLabels_Help[] =
- R"*(About labels
-
- Everything that can participate in the dependency graph (targets, configs,
- and toolchains) are identified by labels. A common label looks like:
-
- //base/test:test_support
-
- This consists of a source-root-absolute path, a colon, and a name. This means
- to look for the thing named "test_support" in "base/test/BUILD.gn".
-
- You can also specify system absolute paths if necessary. Typically such
- paths would be specified via a build arg so the developer can specify where
- the component is on their system.
-
- /usr/local/foo:bar (Posix)
- /C:/Program Files/MyLibs:bar (Windows)
-
-Toolchains
-
- A canonical label includes the label of the toolchain being used. Normally,
- the toolchain label is implicitly inherited from the current execution
- context, but you can override this to specify cross-toolchain dependencies:
-
- //base/test:test_support(//build/toolchain/win:msvc)
-
- Here GN will look for the toolchain definition called "msvc" in the file
- "//build/toolchain/win" to know how to compile this target.
-
-Relative labels
-
- If you want to refer to something in the same buildfile, you can omit
- the path name and just start with a colon. This format is recommended for
- all same-file references.
-
- :base
-
- Labels can be specified as being relative to the current directory.
- Stylistically, we prefer to use absolute paths for all non-file-local
- references unless a build file needs to be run in different contexts (like a
- project needs to be both standalone and pulled into other projects in
- difference places in the directory hierarchy).
-
- source/plugin:myplugin
- ../net:url_request
-
-Implicit names
-
- If a name is unspecified, it will inherit the directory name. Stylistically,
- we prefer to omit the colon and name when possible:
-
- //net -> //net:net
- //tools/gn -> //tools/gn:gn
-)*";
-
-Label::Label(const SourceDir& dir,
- const base::StringPiece& name,
- const SourceDir& toolchain_dir,
- const base::StringPiece& toolchain_name)
- : dir_(dir), toolchain_dir_(toolchain_dir) {
- name_.assign(name.data(), name.size());
- toolchain_name_.assign(toolchain_name.data(), toolchain_name.size());
-}
-
-Label::Label(const SourceDir& dir, const base::StringPiece& name) : dir_(dir) {
- name_.assign(name.data(), name.size());
-}
-
-// static
-Label Label::Resolve(const SourceDir& current_dir,
- const Label& current_toolchain,
- const Value& input,
- Err* err) {
- Label ret;
- if (input.type() != Value::STRING) {
- *err = Err(input, "Dependency is not a string.");
- return ret;
- }
- const std::string& input_string = input.string_value();
- if (input_string.empty()) {
- *err = Err(input, "Dependency string is empty.");
- return ret;
- }
-
- if (!::Resolve(current_dir, current_toolchain, input, input_string, &ret.dir_,
- &ret.name_, &ret.toolchain_dir_, &ret.toolchain_name_, err))
- return Label();
- return ret;
-}
-
-Label Label::GetToolchainLabel() const {
- return Label(toolchain_dir_, toolchain_name_);
-}
-
-Label Label::GetWithNoToolchain() const {
- return Label(dir_, name_);
-}
-
-std::string Label::GetUserVisibleName(bool include_toolchain) const {
- std::string ret;
- ret.reserve(dir_.value().size() + name_.size() + 1);
-
- if (dir_.is_null())
- return ret;
-
- ret = DirWithNoTrailingSlash(dir_);
- ret.push_back(':');
- ret.append(name_);
-
- if (include_toolchain) {
- ret.push_back('(');
- if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) {
- ret.append(DirWithNoTrailingSlash(toolchain_dir_));
- ret.push_back(':');
- ret.append(toolchain_name_);
- }
- ret.push_back(')');
- }
- return ret;
-}
-
-std::string Label::GetUserVisibleName(const Label& default_toolchain) const {
- bool include_toolchain = default_toolchain.dir() != toolchain_dir_ ||
- default_toolchain.name() != toolchain_name_;
- return GetUserVisibleName(include_toolchain);
-}
diff --git a/gn/tools/gn/label.h b/gn/tools/gn/label.h
deleted file mode 100644
index 6606a6b5ad6..00000000000
--- a/gn/tools/gn/label.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// 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 TOOLS_GN_LABEL_H_
-#define TOOLS_GN_LABEL_H_
-
-#include <stddef.h>
-
-#include "tools/gn/source_dir.h"
-
-class Err;
-class Value;
-
-// A label represents the name of a target or some other named thing in
-// the source path. The label is always absolute and always includes a name
-// part, so it starts with a slash, and has one colon.
-class Label {
- public:
- Label() = default;
-
- // Makes a label given an already-separated out path and name.
- // See also Resolve().
- Label(const SourceDir& dir,
- const base::StringPiece& name,
- const SourceDir& toolchain_dir,
- const base::StringPiece& toolchain_name);
-
- // Makes a label with an empty toolchain.
- Label(const SourceDir& dir, const base::StringPiece& name);
-
- // Resolves a string from a build file that may be relative to the
- // current directory into a fully qualified label. On failure returns an
- // is_null() label and sets the error.
- static Label Resolve(const SourceDir& current_dir,
- const Label& current_toolchain,
- const Value& input,
- Err* err);
-
- bool is_null() const { return dir_.is_null(); }
-
- const SourceDir& dir() const { return dir_; }
- const std::string& name() const { return name_; }
-
- const SourceDir& toolchain_dir() const { return toolchain_dir_; }
- const std::string& toolchain_name() const { return toolchain_name_; }
-
- // Returns the current label's toolchain as its own Label.
- Label GetToolchainLabel() const;
-
- // Returns a copy of this label but with an empty toolchain.
- Label GetWithNoToolchain() const;
-
- // Formats this label in a way that we can present to the user or expose to
- // other parts of the system. SourceDirs end in slashes, but the user
- // expects names like "//chrome/renderer:renderer_config" when printed. The
- // toolchain is optionally included.
- std::string GetUserVisibleName(bool include_toolchain) const;
-
- // Like the above version, but automatically includes the toolchain if it's
- // not the default one. Normally the user only cares about the toolchain for
- // non-default ones, so this can make certain output more clear.
- std::string GetUserVisibleName(const Label& default_toolchain) const;
-
- bool operator==(const Label& other) const {
- return name_ == other.name_ && dir_ == other.dir_ &&
- toolchain_dir_ == other.toolchain_dir_ &&
- toolchain_name_ == other.toolchain_name_;
- }
- bool operator!=(const Label& other) const { return !operator==(other); }
- bool operator<(const Label& other) const {
- return std::tie(dir_, name_, toolchain_dir_, toolchain_name_) <
- std::tie(other.dir_, other.name_, other.toolchain_dir_,
- other.toolchain_name_);
- }
-
- // Returns true if the toolchain dir/name of this object matches some
- // other object.
- bool ToolchainsEqual(const Label& other) const {
- return toolchain_dir_ == other.toolchain_dir_ &&
- toolchain_name_ == other.toolchain_name_;
- }
-
- private:
- SourceDir dir_;
- std::string name_;
-
- SourceDir toolchain_dir_;
- std::string toolchain_name_;
-};
-
-namespace std {
-
-template <>
-struct hash<Label> {
- std::size_t operator()(const Label& v) const {
- hash<std::string> stringhash;
- return ((stringhash(v.dir().value()) * 131 + stringhash(v.name())) * 131 +
- stringhash(v.toolchain_dir().value())) *
- 131 +
- stringhash(v.toolchain_name());
- }
-};
-
-} // namespace std
-
-extern const char kLabels_Help[];
-
-#endif // TOOLS_GN_LABEL_H_
diff --git a/gn/tools/gn/label_pattern.cc b/gn/tools/gn/label_pattern.cc
deleted file mode 100644
index f5e6b9c1a08..00000000000
--- a/gn/tools/gn/label_pattern.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-// 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 "tools/gn/label_pattern.h"
-
-#include <stddef.h>
-
-#include "base/strings/string_util.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/value.h"
-#include "util/build_config.h"
-
-const char kLabelPattern_Help[] =
- R"*(Label patterns
-
- A label pattern is a way of expressing one or more labels in a portion of the
- source tree. They are not general regular expressions.
-
- They can take the following forms only:
-
- - Explicit (no wildcard):
- "//foo/bar:baz"
- ":baz"
-
- - Wildcard target names:
- "//foo/bar:*" (all targets in the //foo/bar/BUILD.gn file)
- ":*" (all targets in the current build file)
-
- - Wildcard directory names ("*" is only supported at the end)
- "*" (all targets)
- "//foo/bar/*" (all targets in any subdir of //foo/bar)
- "./*" (all targets in the current build file or sub dirs)
-
- Any of the above forms can additionally take an explicit toolchain
- in parenthesis at the end of the label pattern. In this case, the
- toolchain must be fully qualified (no wildcards are supported in the
- toolchain name).
-
- "//foo:bar(//build/toolchain:mac)"
- An explicit target in an explicit toolchain.
-
- ":*(//build/toolchain/linux:32bit)"
- All targets in the current build file using the 32-bit Linux toolchain.
-
- "//foo/*(//build/toolchain:win)"
- All targets in //foo and any subdirectory using the Windows
- toolchain.
-)*";
-
-LabelPattern::LabelPattern() : type_(MATCH) {}
-
-LabelPattern::LabelPattern(Type type,
- const SourceDir& dir,
- const base::StringPiece& name,
- const Label& toolchain_label)
- : toolchain_(toolchain_label), type_(type), dir_(dir) {
- name.CopyToString(&name_);
-}
-
-LabelPattern::LabelPattern(const LabelPattern& other) = default;
-
-LabelPattern::~LabelPattern() = default;
-
-// static
-LabelPattern LabelPattern::GetPattern(const SourceDir& current_dir,
- const Value& value,
- Err* err) {
- if (!value.VerifyTypeIs(Value::STRING, err))
- return LabelPattern();
-
- base::StringPiece str(value.string_value());
- if (str.empty()) {
- *err = Err(value, "Label pattern must not be empty.");
- return LabelPattern();
- }
-
- // If there's no wildcard, this is specifying an exact label, use the
- // label resolution code to get all the implicit name stuff.
- size_t star = str.find('*');
- if (star == std::string::npos) {
- Label label = Label::Resolve(current_dir, Label(), value, err);
- if (err->has_error())
- return LabelPattern();
-
- // Toolchain.
- Label toolchain_label;
- if (!label.toolchain_dir().is_null() || !label.toolchain_name().empty())
- toolchain_label = label.GetToolchainLabel();
-
- return LabelPattern(MATCH, label.dir(), label.name(), toolchain_label);
- }
-
- // Wildcard case, need to split apart the label to see what it specifies.
- Label toolchain_label;
- size_t open_paren = str.find('(');
- if (open_paren != std::string::npos) {
- // Has a toolchain definition, extract inside the parens.
- size_t close_paren = str.find(')', open_paren);
- if (close_paren == std::string::npos) {
- *err = Err(value, "No close paren when looking for toolchain name.");
- return LabelPattern();
- }
-
- std::string toolchain_string =
- str.substr(open_paren + 1, close_paren - open_paren - 1).as_string();
- if (toolchain_string.find('*') != std::string::npos) {
- *err = Err(value, "Can't have a wildcard in the toolchain.");
- return LabelPattern();
- }
-
- // Parse the inside of the parens as a label for a toolchain.
- Value value_for_toolchain(value.origin(), toolchain_string);
- toolchain_label =
- Label::Resolve(current_dir, Label(), value_for_toolchain, err);
- if (err->has_error())
- return LabelPattern();
-
- // Trim off the toolchain for the processing below.
- str = str.substr(0, open_paren);
- }
-
- // Extract path and name.
- base::StringPiece path;
- base::StringPiece name;
- size_t offset = 0;
-#if defined(OS_WIN)
- if (IsPathAbsolute(str)) {
- size_t drive_letter_pos = str[0] == '/' ? 1 : 0;
- if (str.size() > drive_letter_pos + 2 && str[drive_letter_pos + 1] == ':' &&
- IsSlash(str[drive_letter_pos + 2]) &&
- base::IsAsciiAlpha(str[drive_letter_pos])) {
- // Skip over the drive letter colon.
- offset = drive_letter_pos + 2;
- }
- }
-#endif
- size_t colon = str.find(':', offset);
- if (colon == std::string::npos) {
- path = base::StringPiece(str);
- } else {
- path = str.substr(0, colon);
- name = str.substr(colon + 1);
- }
-
- // The path can have these forms:
- // 1. <empty> (use current dir)
- // 2. <non wildcard stuff> (send through directory resolution)
- // 3. <non wildcard stuff>* (send stuff through dir resolution, note star)
- // 4. * (matches anything)
- SourceDir dir;
- bool has_path_star = false;
- if (path.empty()) {
- // Looks like ":foo".
- dir = current_dir;
- } else if (path[path.size() - 1] == '*') {
- // Case 3 or 4 above.
- has_path_star = true;
-
- // Adjust path to contain everything but the star.
- path = path.substr(0, path.size() - 1);
-
- if (!path.empty() && path[path.size() - 1] != '/') {
- // The input was "foo*" which is invalid.
- *err =
- Err(value, "'*' must match full directories in a label pattern.",
- "You did \"foo*\" but this thing doesn't do general pattern\n"
- "matching. Instead, you have to add a slash: \"foo/*\" to match\n"
- "all targets in a directory hierarchy.");
- return LabelPattern();
- }
- }
-
- // Resolve the part of the path that's not the wildcard.
- if (!path.empty()) {
- // The non-wildcard stuff better not have a wildcard.
- if (path.find('*') != base::StringPiece::npos) {
- *err = Err(value, "Label patterns only support wildcard suffixes.",
- "The pattern contained a '*' that wasn't at the end.");
- return LabelPattern();
- }
-
- // Resolve the non-wildcard stuff.
- dir = current_dir.ResolveRelativeDir(value, path, err);
- if (err->has_error())
- return LabelPattern();
- }
-
- // Resolve the name. At this point, we're doing wildcard matches so the
- // name should either be empty ("foo/*") or a wildcard ("foo:*");
- if (colon != std::string::npos && name != "*") {
- *err = Err(
- value, "Invalid label pattern.",
- "You seem to be using the wildcard more generally that is supported.\n"
- "Did you mean \"foo:*\" to match everything in the file, or\n"
- "\"./*\" to recursively match everything in the currend subtree.");
- return LabelPattern();
- }
-
- Type type;
- if (has_path_star) {
- // We know there's a wildcard, so if the name is empty it looks like
- // "foo/*".
- type = RECURSIVE_DIRECTORY;
- } else {
- // Everything else should be of the form "foo:*".
- type = DIRECTORY;
- }
-
- // When we're doing wildcard matching, the name is always empty.
- return LabelPattern(type, dir, base::StringPiece(), toolchain_label);
-}
-
-bool LabelPattern::HasWildcard(const std::string& str) {
- // Just look for a star. In the future, we may want to handle escaping or
- // other types of patterns.
- return str.find('*') != std::string::npos;
-}
-
-bool LabelPattern::Matches(const Label& label) const {
- if (!toolchain_.is_null()) {
- // Toolchain must match exactly.
- if (toolchain_.dir() != label.toolchain_dir() ||
- toolchain_.name() != label.toolchain_name())
- return false;
- }
-
- switch (type_) {
- case MATCH:
- return label.name() == name_ && label.dir() == dir_;
- case DIRECTORY:
- // The directories must match exactly.
- return label.dir() == dir_;
- case RECURSIVE_DIRECTORY:
- // Our directory must be a prefix of the input label for recursive.
- return label.dir().value().compare(0, dir_.value().size(),
- dir_.value()) == 0;
- default:
- NOTREACHED();
- return false;
- }
-}
-
-// static
-bool LabelPattern::VectorMatches(const std::vector<LabelPattern>& patterns,
- const Label& label) {
- for (const auto& pattern : patterns) {
- if (pattern.Matches(label))
- return true;
- }
- return false;
-}
-
-std::string LabelPattern::Describe() const {
- std::string result;
-
- switch (type()) {
- case MATCH:
- result = DirectoryWithNoLastSlash(dir()) + ":" + name();
- break;
- case DIRECTORY:
- result = DirectoryWithNoLastSlash(dir()) + ":*";
- break;
- case RECURSIVE_DIRECTORY:
- result = dir().value() + "*";
- break;
- }
-
- if (!toolchain_.is_null()) {
- result.push_back('(');
- result.append(toolchain_.GetUserVisibleName(false));
- result.push_back(')');
- }
- return result;
-}
diff --git a/gn/tools/gn/label_pattern.h b/gn/tools/gn/label_pattern.h
deleted file mode 100644
index 231fd6d8957..00000000000
--- a/gn/tools/gn/label_pattern.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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 TOOLS_GN_LABEL_PATTERN_H_
-#define TOOLS_GN_LABEL_PATTERN_H_
-
-#include "base/strings/string_piece.h"
-#include "tools/gn/label.h"
-#include "tools/gn/source_dir.h"
-
-class Err;
-class Value;
-
-extern const char kLabelPattern_Help[];
-
-// A label pattern is a simple pattern that matches labels. It is used for
-// specifying visibility and other times when multiple targets need to be
-// referenced.
-class LabelPattern {
- public:
- enum Type {
- MATCH = 1, // Exact match for a given target.
- DIRECTORY, // Only targets in the file in the given directory.
- RECURSIVE_DIRECTORY // The given directory and any subdir.
- // (also indicates "public" when dir is empty).
- };
-
- LabelPattern();
- LabelPattern(Type type,
- const SourceDir& dir,
- const base::StringPiece& name,
- const Label& toolchain_label);
- LabelPattern(const LabelPattern& other);
- ~LabelPattern();
-
- // Converts the given input string to a pattern. This does special stuff
- // to treat the pattern as a label. Sets the error on failure.
- static LabelPattern GetPattern(const SourceDir& current_dir,
- const Value& value,
- Err* err);
-
- // Returns true if the given input string might match more than one thing.
- static bool HasWildcard(const std::string& str);
-
- // Returns true if this pattern matches the given label.
- bool Matches(const Label& label) const;
-
- // Returns true if any of the patterns in the vector match the label.
- static bool VectorMatches(const std::vector<LabelPattern>& patterns,
- const Label& label);
-
- // Returns a string representation of this pattern.
- std::string Describe() const;
-
- Type type() const { return type_; }
-
- const SourceDir& dir() const { return dir_; }
- const std::string& name() const { return name_; }
-
- const Label& toolchain() const { return toolchain_; }
- void set_toolchain(const Label& tc) { toolchain_ = tc; }
-
- private:
- // If nonempty, specifies the toolchain to use. If empty, this will match
- // all toolchains. This is independent of the match type.
- Label toolchain_;
-
- Type type_;
-
- // Used when type_ == PRIVATE and PRIVATE_RECURSIVE. This specifies the
- // directory that to which the pattern is private to.
- SourceDir dir_;
-
- // Empty name means match everything. Otherwise the name must match
- // exactly.
- std::string name_;
-};
-
-#endif // TOOLS_GN_LABEL_PATTERN_H_
diff --git a/gn/tools/gn/label_pattern_unittest.cc b/gn/tools/gn/label_pattern_unittest.cc
deleted file mode 100644
index 7457a9b55a9..00000000000
--- a/gn/tools/gn/label_pattern_unittest.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/macros.h"
-#include "tools/gn/err.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-namespace {
-
-struct PatternCase {
- const char* input;
- bool success;
-
- LabelPattern::Type type;
- const char* dir;
- const char* name;
- const char* toolchain;
-};
-
-} // namespace
-
-TEST(LabelPattern, PatternParse) {
- SourceDir current_dir("//foo/");
- PatternCase cases[] = {
- // Missing stuff.
- {"", false, LabelPattern::MATCH, "", "", ""},
- {":", false, LabelPattern::MATCH, "", "", ""},
- // Normal things.
- {":bar", true, LabelPattern::MATCH, "//foo/", "bar", ""},
- {"//la:bar", true, LabelPattern::MATCH, "//la/", "bar", ""},
- {"*", true, LabelPattern::RECURSIVE_DIRECTORY, "", "", ""},
- {":*", true, LabelPattern::DIRECTORY, "//foo/", "", ""},
- {"la:*", true, LabelPattern::DIRECTORY, "//foo/la/", "", ""},
- {"la/*:*", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/la/", "", ""},
- {"//la:*", true, LabelPattern::DIRECTORY, "//la/", "", ""},
- {"./*", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/", "", ""},
- {"foo/*", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/foo/", "", ""},
- {"//l/*", true, LabelPattern::RECURSIVE_DIRECTORY, "//l/", "", ""},
- // Toolchains.
- {"//foo()", true, LabelPattern::MATCH, "//foo/", "foo", ""},
- {"//foo(//bar)", true, LabelPattern::MATCH, "//foo/", "foo", "//bar:bar"},
- {"//foo:*(//bar)", true, LabelPattern::DIRECTORY, "//foo/", "",
- "//bar:bar"},
- {"//foo/*(//bar)", true, LabelPattern::RECURSIVE_DIRECTORY, "//foo/", "",
- "//bar:bar"},
- // Wildcards in invalid places.
- {"*foo*:bar", false, LabelPattern::MATCH, "", "", ""},
- {"foo*:*bar", false, LabelPattern::MATCH, "", "", ""},
- {"*foo:bar", false, LabelPattern::MATCH, "", "", ""},
- {"foo:bar*", false, LabelPattern::MATCH, "", "", ""},
- {"*:*", true, LabelPattern::RECURSIVE_DIRECTORY, "", "", ""},
- // Invalid toolchain stuff.
- {"//foo(//foo/bar:*)", false, LabelPattern::MATCH, "", "", ""},
- {"//foo/*(*)", false, LabelPattern::MATCH, "", "", ""},
- {"//foo(//bar", false, LabelPattern::MATCH, "", "", ""},
- // Absolute paths.
- {"/la/*", true, LabelPattern::RECURSIVE_DIRECTORY, "/la/", "", ""},
- {"/la:bar", true, LabelPattern::MATCH, "/la/", "bar", ""},
-#if defined(OS_WIN)
- {"/C:/la/*", true, LabelPattern::RECURSIVE_DIRECTORY, "/C:/la/", "", ""},
- {"C:/la/*", true, LabelPattern::RECURSIVE_DIRECTORY, "/C:/la/", "", ""},
- {"/C:/la:bar", true, LabelPattern::MATCH, "/C:/la/", "bar", ""},
- {"C:/la:bar", true, LabelPattern::MATCH, "/C:/la/", "bar", ""},
- {"C:foo", true, LabelPattern::MATCH, "//foo/C/", "foo", ""},
-#endif
- };
-
- for (size_t i = 0; i < arraysize(cases); i++) {
- const PatternCase& cur = cases[i];
- Err err;
- LabelPattern result =
- LabelPattern::GetPattern(current_dir, Value(nullptr, cur.input), &err);
-
- EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.input;
- EXPECT_EQ(cur.type, result.type()) << i << " " << cur.input;
- EXPECT_EQ(cur.dir, result.dir().value()) << i << " " << cur.input;
- EXPECT_EQ(cur.name, result.name()) << i << " " << cur.input;
- EXPECT_EQ(cur.toolchain, result.toolchain().GetUserVisibleName(false))
- << i << " " << cur.input;
- }
-}
diff --git a/gn/tools/gn/label_ptr.h b/gn/tools/gn/label_ptr.h
deleted file mode 100644
index ac994f51d87..00000000000
--- a/gn/tools/gn/label_ptr.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 TOOLS_GN_LABEL_PTR_H_
-#define TOOLS_GN_LABEL_PTR_H_
-
-#include <stddef.h>
-
-#include <functional>
-
-#include "tools/gn/label.h"
-
-class Config;
-class ParseNode;
-class Target;
-
-// Structure that holds a labeled "thing". This is used for various places
-// where we need to store lists of targets or configs. We sometimes populate
-// the pointers on another thread from where we compute the labels, so this
-// structure lets us save them separately. This also allows us to store the
-// location of the thing that added this dependency.
-template <typename T>
-struct LabelPtrPair {
- typedef T DestType;
-
- LabelPtrPair() = default;
-
- explicit LabelPtrPair(const Label& l) : label(l) {}
-
- // This contructor is typically used in unit tests, it extracts the label
- // automatically from a given pointer.
- explicit LabelPtrPair(const T* p) : label(p->label()), ptr(p) {}
-
- ~LabelPtrPair() = default;
-
- Label label;
- const T* ptr = nullptr;
-
- // The origin of this dependency. This will be null for internally generated
- // dependencies. This happens when a group is automatically expanded and that
- // group's members are added to the target that depends on that group.
- const ParseNode* origin = nullptr;
-};
-
-typedef LabelPtrPair<Config> LabelConfigPair;
-typedef LabelPtrPair<Target> LabelTargetPair;
-
-typedef std::vector<LabelConfigPair> LabelConfigVector;
-typedef std::vector<LabelTargetPair> LabelTargetVector;
-
-// Default comparison operators -----------------------------------------------
-//
-// The default hash and comparison operators operate on the label, which should
-// always be valid, whereas the pointer is sometimes null.
-
-template <typename T>
-inline bool operator==(const LabelPtrPair<T>& a, const LabelPtrPair<T>& b) {
- return a.label == b.label;
-}
-
-template <typename T>
-inline bool operator<(const LabelPtrPair<T>& a, const LabelPtrPair<T>& b) {
- return a.label < b.label;
-}
-
-namespace std {
-
-template <typename T>
-struct hash<LabelPtrPair<T>> {
- std::size_t operator()(const LabelPtrPair<T>& v) const {
- hash<Label> h;
- return h(v.label);
- }
-};
-
-} // namespace std
-
-#endif // TOOLS_GN_LABEL_PTR_H_
diff --git a/gn/tools/gn/label_unittest.cc b/gn/tools/gn/label_unittest.cc
deleted file mode 100644
index 26a276c844f..00000000000
--- a/gn/tools/gn/label_unittest.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/macros.h"
-#include "tools/gn/err.h"
-#include "tools/gn/label.h"
-#include "tools/gn/value.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-namespace {
-
-struct ParseDepStringCase {
- const char* cur_dir;
- const char* str;
- bool success;
- const char* expected_dir;
- const char* expected_name;
- const char* expected_toolchain_dir;
- const char* expected_toolchain_name;
-};
-
-} // namespace
-
-TEST(Label, Resolve) {
- ParseDepStringCase cases[] = {
- {"//chrome/", "", false, "", "", "", ""},
- {"//chrome/", "/", false, "", "", "", ""},
- {"//chrome/", ":", false, "", "", "", ""},
- {"//chrome/", "/:", false, "", "", "", ""},
- {"//chrome/", "blah", true, "//chrome/blah/", "blah", "//t/", "d"},
- {"//chrome/", "blah:bar", true, "//chrome/blah/", "bar", "//t/", "d"},
- // Absolute paths.
- {"//chrome/", "/chrome:bar", true, "/chrome/", "bar", "//t/", "d"},
- {"//chrome/", "/chrome/:bar", true, "/chrome/", "bar", "//t/", "d"},
-#if defined(OS_WIN)
- {"//chrome/", "/C:/chrome:bar", true, "/C:/chrome/", "bar", "//t/", "d"},
- {"//chrome/", "/C:/chrome/:bar", true, "/C:/chrome/", "bar", "//t/", "d"},
- {"//chrome/", "C:/chrome:bar", true, "/C:/chrome/", "bar", "//t/", "d"},
-#endif
- // Refers to root dir.
- {"//chrome/", "//:bar", true, "//", "bar", "//t/", "d"},
- // Implicit directory
- {"//chrome/", ":bar", true, "//chrome/", "bar", "//t/", "d"},
- {"//chrome/renderer/", ":bar", true, "//chrome/renderer/", "bar", "//t/",
- "d"},
- // Implicit names.
- {"//chrome/", "//base", true, "//base/", "base", "//t/", "d"},
- {"//chrome/", "//base/i18n", true, "//base/i18n/", "i18n", "//t/", "d"},
- {"//chrome/", "//base/i18n:foo", true, "//base/i18n/", "foo", "//t/", "d"},
- {"//chrome/", "//", false, "", "", "", ""},
- // Toolchain parsing.
- {"//chrome/", "//chrome:bar(//t:n)", true, "//chrome/", "bar", "//t/", "n"},
- {"//chrome/", "//chrome:bar(//t)", true, "//chrome/", "bar", "//t/", "t"},
- {"//chrome/", "//chrome:bar(//t:)", true, "//chrome/", "bar", "//t/", "t"},
- {"//chrome/", "//chrome:bar()", true, "//chrome/", "bar", "//t/", "d"},
- {"//chrome/", "//chrome:bar(foo)", true, "//chrome/", "bar",
- "//chrome/foo/", "foo"},
- {"//chrome/", "//chrome:bar(:foo)", true, "//chrome/", "bar", "//chrome/",
- "foo"},
- // TODO(brettw) it might be nice to make this an error:
- //{"//chrome/", "//chrome:bar())", false, "", "", "", "" },
- {"//chrome/", "//chrome:bar(//t:bar(tc))", false, "", "", "", ""},
- {"//chrome/", "//chrome:bar(()", false, "", "", "", ""},
- {"//chrome/", "(t:b)", false, "", "", "", ""},
- {"//chrome/", ":bar(//t/b)", true, "//chrome/", "bar", "//t/b/", "b"},
- {"//chrome/", ":bar(/t/b)", true, "//chrome/", "bar", "/t/b/", "b"},
- {"//chrome/", ":bar(t/b)", true, "//chrome/", "bar", "//chrome/t/b/", "b"},
- };
-
- Label default_toolchain(SourceDir("//t/"), "d");
-
- for (size_t i = 0; i < arraysize(cases); i++) {
- const ParseDepStringCase& cur = cases[i];
-
- std::string location, name;
- Err err;
- Value v(nullptr, Value::STRING);
- v.string_value() = cur.str;
- Label result =
- Label::Resolve(SourceDir(cur.cur_dir), default_toolchain, v, &err);
- EXPECT_EQ(cur.success, !err.has_error()) << i << " " << cur.str;
- if (!err.has_error() && cur.success) {
- EXPECT_EQ(cur.expected_dir, result.dir().value()) << i << " " << cur.str;
- EXPECT_EQ(cur.expected_name, result.name()) << i << " " << cur.str;
- EXPECT_EQ(cur.expected_toolchain_dir, result.toolchain_dir().value())
- << i << " " << cur.str;
- EXPECT_EQ(cur.expected_toolchain_name, result.toolchain_name())
- << i << " " << cur.str;
- }
- }
-}
diff --git a/gn/tools/gn/lib_file.cc b/gn/tools/gn/lib_file.cc
deleted file mode 100644
index 81e54fbf9a6..00000000000
--- a/gn/tools/gn/lib_file.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 "tools/gn/lib_file.h"
-
-#include "base/logging.h"
-
-LibFile::LibFile(const SourceFile& source_file) : source_file_(source_file) {}
-
-LibFile::LibFile(const base::StringPiece& lib_name)
- : name_(lib_name.data(), lib_name.size()) {
- DCHECK(!lib_name.empty());
-}
-
-const std::string& LibFile::value() const {
- return is_source_file() ? source_file_.value() : name_;
-}
-
-const SourceFile& LibFile::source_file() const {
- DCHECK(is_source_file());
- return source_file_;
-}
diff --git a/gn/tools/gn/lib_file.h b/gn/tools/gn/lib_file.h
deleted file mode 100644
index 58621ebedf2..00000000000
--- a/gn/tools/gn/lib_file.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 TOOLS_GN_LIB_FILE_H_
-#define TOOLS_GN_LIB_FILE_H_
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <string>
-
-#include "base/strings/string_piece.h"
-#include "tools/gn/source_file.h"
-
-// Represents an entry in "libs" list. Can be either a path (a SourceFile) or
-// a library name (a string).
-class LibFile {
- public:
- LibFile() = default;
-
- explicit LibFile(const base::StringPiece& lib_name);
- explicit LibFile(const SourceFile& source_file);
-
- bool is_source_file() const { return name_.empty(); }
-
- // Returns name, or source_file().value() (whichever is set).
- const std::string& value() const;
- const SourceFile& source_file() const;
-
- bool operator==(const LibFile& other) const {
- return value() == other.value();
- }
- bool operator!=(const LibFile& other) const { return !operator==(other); }
- bool operator<(const LibFile& other) const { return value() < other.value(); }
-
- private:
- std::string name_;
- SourceFile source_file_;
-};
-
-namespace std {
-
-template <>
-struct hash<LibFile> {
- std::size_t operator()(const LibFile& v) const {
- hash<std::string> h;
- return h(v.value());
- }
-};
-
-} // namespace std
-
-#endif // TOOLS_GN_LIB_FILE_H_
diff --git a/gn/tools/gn/loader.cc b/gn/tools/gn/loader.cc
deleted file mode 100644
index d1a2953bb83..00000000000
--- a/gn/tools/gn/loader.cc
+++ /dev/null
@@ -1,437 +0,0 @@
-// 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 "tools/gn/loader.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file_manager.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope_per_file_provider.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/trace.h"
-
-namespace {
-
-struct SourceFileAndOrigin {
- SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
- : file(f), origin(o) {}
-
- SourceFile file;
- LocationRange origin;
-};
-
-} // namespace
-
-// Identifies one time a file is loaded in a given toolchain so we don't load
-// it more than once.
-struct LoaderImpl::LoadID {
- LoadID() = default;
- LoadID(const SourceFile& f, const Label& tc_name)
- : file(f), toolchain_name(tc_name) {}
-
- bool operator<(const LoadID& other) const {
- if (file.value() == other.file.value())
- return toolchain_name < other.toolchain_name;
- return file < other.file;
- }
-
- SourceFile file;
- Label toolchain_name;
-};
-
-// Our tracking information for a toolchain.
-struct LoaderImpl::ToolchainRecord {
- // The default toolchain label can be empty for the first time the default
- // toolchain is loaded, since we don't know it yet. This will be fixed up
- // later. It should be valid in all other cases.
- ToolchainRecord(const BuildSettings* build_settings,
- const Label& toolchain_label,
- const Label& default_toolchain_label)
- : settings(
- build_settings,
- GetOutputSubdirName(toolchain_label,
- toolchain_label == default_toolchain_label)),
- is_toolchain_loaded(false),
- is_config_loaded(false) {
- settings.set_default_toolchain_label(default_toolchain_label);
- settings.set_toolchain_label(toolchain_label);
- }
-
- Settings settings;
-
- bool is_toolchain_loaded;
- bool is_config_loaded;
-
- std::vector<SourceFileAndOrigin> waiting_on_me;
-};
-
-// -----------------------------------------------------------------------------
-
-const void* const Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
-
-Loader::Loader() = default;
-
-Loader::~Loader() = default;
-
-void Loader::Load(const Label& label, const LocationRange& origin) {
- Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
-}
-
-// static
-SourceFile Loader::BuildFileForLabel(const Label& label) {
- return SourceFile(label.dir().value() + "BUILD.gn");
-}
-
-// -----------------------------------------------------------------------------
-
-LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
- : pending_loads_(0), build_settings_(build_settings) {
- // There may not be an active TaskRunner at this point. When that's the case,
- // the calling code is expected to call set_task_runner().
- task_runner_ = MsgLoop::Current();
-}
-
-LoaderImpl::~LoaderImpl() = default;
-
-void LoaderImpl::Load(const SourceFile& file,
- const LocationRange& origin,
- const Label& in_toolchain_name) {
- const Label& toolchain_name = in_toolchain_name.is_null()
- ? default_toolchain_label_
- : in_toolchain_name;
- LoadID load_id(file, toolchain_name);
- if (!invocations_.insert(load_id).second)
- return; // Already in set, so this file was already loaded or schedulerd.
-
- if (toolchain_records_.empty()) {
- // Nothing loaded, need to load the default build config. The initial load
- // should not specify a toolchain.
- DCHECK(toolchain_name.is_null());
-
- std::unique_ptr<ToolchainRecord> new_record =
- std::make_unique<ToolchainRecord>(build_settings_, Label(), Label());
- ToolchainRecord* record = new_record.get();
- Label empty_label; // VS issues spurious warning using ...[Label()].
- toolchain_records_[empty_label] = std::move(new_record);
-
- // The default build config is no dependent on the toolchain definition,
- // since we need to load the build config before we know what the default
- // toolchain name is.
- record->is_toolchain_loaded = true;
-
- record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
- ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
-
- return;
- }
-
- ToolchainRecord* record;
- if (toolchain_name.is_null())
- record = toolchain_records_[default_toolchain_label_].get();
- else
- record = toolchain_records_[toolchain_name].get();
-
- if (!record) {
- DCHECK(!default_toolchain_label_.is_null());
-
- // No reference to this toolchain found yet, make one.
- std::unique_ptr<ToolchainRecord> new_record =
- std::make_unique<ToolchainRecord>(build_settings_, toolchain_name,
- default_toolchain_label_);
- record = new_record.get();
- toolchain_records_[toolchain_name] = std::move(new_record);
-
- // Schedule a load of the toolchain using the default one.
- Load(BuildFileForLabel(toolchain_name), origin, default_toolchain_label_);
- }
-
- if (record->is_config_loaded)
- ScheduleLoadFile(&record->settings, origin, file);
- else
- record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
-}
-
-void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
- ToolchainRecord* record = toolchain_records_[toolchain->label()].get();
- if (!record) {
- DCHECK(!default_toolchain_label_.is_null());
- std::unique_ptr<ToolchainRecord> new_record =
- std::make_unique<ToolchainRecord>(build_settings_, toolchain->label(),
- default_toolchain_label_);
- record = new_record.get();
- toolchain_records_[toolchain->label()] = std::move(new_record);
- }
- record->is_toolchain_loaded = true;
-
- // The default build config is loaded first, then its toolchain. Secondary
- // ones are loaded in the opposite order so we can pass toolchain parameters
- // to the build config. So we may or may not have a config at this point.
- if (!record->is_config_loaded) {
- ScheduleLoadBuildConfig(&record->settings, toolchain->args());
- } else {
- // There should be nobody waiting on this if the build config is already
- // loaded.
- DCHECK(record->waiting_on_me.empty());
- }
-}
-
-Label LoaderImpl::GetDefaultToolchain() const {
- return default_toolchain_label_;
-}
-
-const Settings* LoaderImpl::GetToolchainSettings(const Label& label) const {
- ToolchainRecordMap::const_iterator found_toolchain;
- if (label.is_null()) {
- if (default_toolchain_label_.is_null())
- return nullptr;
- found_toolchain = toolchain_records_.find(default_toolchain_label_);
- } else {
- found_toolchain = toolchain_records_.find(label);
- }
-
- if (found_toolchain == toolchain_records_.end())
- return nullptr;
- return &found_toolchain->second->settings;
-}
-
-void LoaderImpl::ScheduleLoadFile(const Settings* settings,
- const LocationRange& origin,
- const SourceFile& file) {
- Err err;
- pending_loads_++;
- if (!AsyncLoadFile(origin, settings->build_settings(), file,
- base::Bind(&LoaderImpl::BackgroundLoadFile, this, settings,
- file, origin),
- &err)) {
- g_scheduler->FailWithError(err);
- DecrementPendingLoads();
- }
-}
-
-void LoaderImpl::ScheduleLoadBuildConfig(
- Settings* settings,
- const Scope::KeyValueMap& toolchain_overrides) {
- Err err;
- pending_loads_++;
- if (!AsyncLoadFile(LocationRange(), settings->build_settings(),
- settings->build_settings()->build_config_file(),
- base::Bind(&LoaderImpl::BackgroundLoadBuildConfig, this,
- settings, toolchain_overrides),
- &err)) {
- g_scheduler->FailWithError(err);
- DecrementPendingLoads();
- }
-}
-
-void LoaderImpl::BackgroundLoadFile(const Settings* settings,
- const SourceFile& file_name,
- const LocationRange& origin,
- const ParseNode* root) {
- if (!root) {
- task_runner_->PostTask(
- base::BindOnce(&LoaderImpl::DecrementPendingLoads, this));
- return;
- }
-
- if (g_scheduler->verbose_logging()) {
- g_scheduler->Log("Running",
- file_name.value() + " with toolchain " +
- settings->toolchain_label().GetUserVisibleName(false));
- }
-
- Scope our_scope(settings->base_config());
- ScopePerFileProvider per_file_provider(&our_scope, true);
- our_scope.set_source_dir(file_name.GetDir());
- our_scope.AddBuildDependencyFile(file_name);
-
- // Targets, etc. generated as part of running this file will end up here.
- Scope::ItemVector collected_items;
- our_scope.set_item_collector(&collected_items);
-
- ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
- trace.SetToolchain(settings->toolchain_label());
-
- Err err;
- root->Execute(&our_scope, &err);
- if (!err.has_error())
- our_scope.CheckForUnusedVars(&err);
-
- if (err.has_error()) {
- if (!origin.is_null())
- err.AppendSubErr(Err(origin, "which caused the file to be included."));
- g_scheduler->FailWithError(err);
- }
-
- // Pass all of the items that were defined off to the builder.
- for (auto& item : collected_items)
- settings->build_settings()->ItemDefined(std::move(item));
-
- trace.Done();
-
- task_runner_->PostTask(base::BindOnce(&LoaderImpl::DidLoadFile, this));
-}
-
-void LoaderImpl::BackgroundLoadBuildConfig(
- Settings* settings,
- const Scope::KeyValueMap& toolchain_overrides,
- const ParseNode* root) {
- if (!root) {
- task_runner_->PostTask(
- base::BindOnce(&LoaderImpl::DecrementPendingLoads, this));
- return;
- }
-
- Scope* base_config = settings->base_config();
- base_config->set_source_dir(SourceDir("//"));
- base_config->AddBuildDependencyFile(
- settings->build_settings()->build_config_file());
-
- settings->build_settings()->build_args().SetupRootScope(base_config,
- toolchain_overrides);
-
- base_config->SetProcessingBuildConfig();
-
- // See kDefaultToolchainKey in the header.
- Label default_toolchain_label;
- if (settings->is_default())
- base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
-
- ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
- settings->build_settings()->build_config_file().value());
- trace.SetToolchain(settings->toolchain_label());
-
- // Run the BUILDCONFIG with its directory as the current one. We want
- // BUILDCONFIG to modify the base_config so can't make a copy or a nested one.
- base_config->set_source_dir(
- settings->build_settings()->build_config_file().GetDir());
-
- Err err;
- root->Execute(base_config, &err);
-
- // Put back the root as the default source dir. This probably isn't necessary
- // as other scopes will set their directories to their own path, but it's a
- // better default than the build config's directory.
- base_config->set_source_dir(SourceDir("//"));
-
- // Clear all private variables left in the scope. We want the root build
- // config to be like a .gni file in that variables beginning with an
- // underscore aren't exported.
- base_config->RemovePrivateIdentifiers();
-
- trace.Done();
-
- if (err.has_error())
- g_scheduler->FailWithError(err);
-
- base_config->ClearProcessingBuildConfig();
- if (settings->is_default()) {
- // The default toolchain must have been set in the default build config
- // file.
- if (default_toolchain_label.is_null()) {
- g_scheduler->FailWithError(Err(
- Location(),
- "The default build config file did not call set_default_toolchain()",
- "If you don't call this, I can't figure out what toolchain to use\n"
- "for all of this code."));
- } else {
- DCHECK(settings->toolchain_label().is_null());
- settings->set_toolchain_label(default_toolchain_label);
- }
- }
-
- task_runner_->PostTask(base::BindOnce(&LoaderImpl::DidLoadBuildConfig, this,
- settings->toolchain_label()));
-}
-
-void LoaderImpl::DidLoadFile() {
- DecrementPendingLoads();
-}
-
-void LoaderImpl::DidLoadBuildConfig(const Label& label) {
- // Do not return early, we must call DecrementPendingLoads() at the bottom.
-
- ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
- ToolchainRecord* record = nullptr;
- if (found_toolchain == toolchain_records_.end()) {
- // When loading the default build config, we'll insert it into the record
- // map with an empty label since we don't yet know what to call it.
- //
- // In this case, we should have exactly one entry in the map with an empty
- // label. We now need to fix up the naming so it refers to the "real" one.
- CHECK_EQ(1U, toolchain_records_.size());
- ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
- CHECK(empty_label != toolchain_records_.end());
-
- // Fix up the toolchain record.
- std::unique_ptr<ToolchainRecord> moved_record =
- std::move(empty_label->second);
- record = moved_record.get();
- toolchain_records_[label] = std::move(moved_record);
- toolchain_records_.erase(empty_label);
-
- // Save the default toolchain label.
- default_toolchain_label_ = label;
- DCHECK(record->settings.default_toolchain_label().is_null());
- record->settings.set_default_toolchain_label(label);
-
- // The settings object should have the toolchain label already set.
- DCHECK(!record->settings.toolchain_label().is_null());
-
- // Update any stored invocations that refer to the empty toolchain label.
- // This will normally only be one, for the root build file, so brute-force
- // is OK.
- LoadIDSet old_loads;
- invocations_.swap(old_loads);
- for (const auto& load : old_loads) {
- if (load.toolchain_name.is_null()) {
- // Fix up toolchain label
- invocations_.emplace(load.file, label);
- } else {
- // Can keep the old one.
- invocations_.insert(load);
- }
- }
- } else {
- record = found_toolchain->second.get();
- }
-
- DCHECK(!record->is_config_loaded);
- DCHECK(record->is_toolchain_loaded);
- record->is_config_loaded = true;
-
- // Schedule all waiting file loads.
- for (const auto& waiting : record->waiting_on_me)
- ScheduleLoadFile(&record->settings, waiting.origin, waiting.file);
- record->waiting_on_me.clear();
-
- DecrementPendingLoads();
-}
-
-void LoaderImpl::DecrementPendingLoads() {
- DCHECK_GT(pending_loads_, 0);
- pending_loads_--;
- if (pending_loads_ == 0 && !complete_callback_.is_null())
- complete_callback_.Run();
-}
-
-bool LoaderImpl::AsyncLoadFile(
- const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- const base::Callback<void(const ParseNode*)>& callback,
- Err* err) {
- if (async_load_file_.is_null()) {
- return g_scheduler->input_file_manager()->AsyncLoadFile(
- origin, build_settings, file_name, callback, err);
- }
- return async_load_file_.Run(origin, build_settings, file_name, callback, err);
-}
diff --git a/gn/tools/gn/loader.h b/gn/tools/gn/loader.h
deleted file mode 100644
index 71b22b8d889..00000000000
--- a/gn/tools/gn/loader.h
+++ /dev/null
@@ -1,178 +0,0 @@
-// 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 TOOLS_GN_LOADER_H_
-#define TOOLS_GN_LOADER_H_
-
-#include <map>
-#include <memory>
-#include <set>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "tools/gn/label.h"
-#include "tools/gn/scope.h"
-#include "util/msg_loop.h"
-
-class BuildSettings;
-class LocationRange;
-class Settings;
-class SourceFile;
-class Toolchain;
-
-// The loader manages execution of the different build files. It receives
-// requests (normally from the Builder) when new references are found, and also
-// manages loading the build config files.
-//
-// This loader class is abstract so it can be mocked out for testing the
-// Builder.
-class Loader : public base::RefCountedThreadSafe<Loader> {
- public:
- Loader();
-
- // Loads the given file in the conext of the given toolchain. The initial
- // call to this (the one that actually starts the generation) should have an
- // empty toolchain name, which will trigger the load of the default build
- // config.
- virtual void Load(const SourceFile& file,
- const LocationRange& origin,
- const Label& toolchain_name) = 0;
-
- // Notification that the given toolchain has loaded. This will unblock files
- // waiting on this definition.
- virtual void ToolchainLoaded(const Toolchain* toolchain) = 0;
-
- // Returns the label of the default toolchain.
- virtual Label GetDefaultToolchain() const = 0;
-
- // Returns information about the toolchain with the given label. Will return
- // false if we haven't processed this toolchain yet.
- virtual const Settings* GetToolchainSettings(const Label& label) const = 0;
-
- // Helper function that extracts the file and toolchain name from the given
- // label, and calls Load().
- void Load(const Label& label, const LocationRange& origin);
-
- // Returns the build file that the given label references.
- static SourceFile BuildFileForLabel(const Label& label);
-
- // When processing the default build config, we want to capture the argument
- // of set_default_build_config. The implementation of that function uses this
- // constant as a property key to get the Label* out of the scope where the
- // label should be stored.
- static const void* const kDefaultToolchainKey;
-
- protected:
- friend class base::RefCountedThreadSafe<Loader>;
- virtual ~Loader();
-};
-
-class LoaderImpl : public Loader {
- public:
- // Callback to emulate InputFileManager::AsyncLoadFile.
- typedef base::Callback<bool(const LocationRange&,
- const BuildSettings*,
- const SourceFile&,
- const base::Callback<void(const ParseNode*)>&,
- Err*)>
- AsyncLoadFileCallback;
-
- explicit LoaderImpl(const BuildSettings* build_settings);
-
- // Loader implementation.
- void Load(const SourceFile& file,
- const LocationRange& origin,
- const Label& toolchain_name) override;
- void ToolchainLoaded(const Toolchain* toolchain) override;
- Label GetDefaultToolchain() const override;
- const Settings* GetToolchainSettings(const Label& label) const override;
-
- // Sets the task runner corresponding to the main thread. By default this
- // class will use the thread active during construction, but there is not
- // a task runner active during construction all the time.
- void set_task_runner(MsgLoop* task_runner) { task_runner_ = task_runner; }
-
- // The complete callback is called whenever there are no more pending loads.
- // Called on the main thread only. This may be called more than once if the
- // queue is drained, but then more stuff gets added.
- void set_complete_callback(const base::Closure& cb) {
- complete_callback_ = cb;
- }
-
- // This callback is used when the loader finds it wants to load a file.
- void set_async_load_file(const AsyncLoadFileCallback& cb) {
- async_load_file_ = cb;
- }
-
- const Label& default_toolchain_label() const {
- return default_toolchain_label_;
- }
-
- private:
- struct LoadID;
- struct ToolchainRecord;
-
- ~LoaderImpl() override;
-
- // Schedules the input file manager to load the given file.
- void ScheduleLoadFile(const Settings* settings,
- const LocationRange& origin,
- const SourceFile& file);
- void ScheduleLoadBuildConfig(Settings* settings,
- const Scope::KeyValueMap& toolchain_overrides);
-
- // Runs the given file on the background thread. These are called by the
- // input file manager.
- void BackgroundLoadFile(const Settings* settings,
- const SourceFile& file_name,
- const LocationRange& origin,
- const ParseNode* root);
- void BackgroundLoadBuildConfig(Settings* settings,
- const Scope::KeyValueMap& toolchain_overrides,
- const ParseNode* root);
-
- // Posted to the main thread when any file other than a build config file
- // file has completed running.
- void DidLoadFile();
-
- // Posted to the main thread when any build config file has completed
- // running. The label should be the name of the toolchain.
- //
- // If there is no defauled toolchain loaded yet, we'll assume that the first
- // call to this indicates to the default toolchain, and this function will
- // set the default toolchain name to the given label.
- void DidLoadBuildConfig(const Label& label);
-
- // Decrements the pending_loads_ variable and issues the complete callback if
- // necessary.
- void DecrementPendingLoads();
-
- // Forwards to the appropriate location to load the file.
- bool AsyncLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- const base::Callback<void(const ParseNode*)>& callback,
- Err* err);
-
- MsgLoop* task_runner_;
-
- int pending_loads_;
- base::Closure complete_callback_;
-
- // When non-null, use this callback instead of the InputFileManager for
- // mocking purposes.
- AsyncLoadFileCallback async_load_file_;
-
- typedef std::set<LoadID> LoadIDSet;
- LoadIDSet invocations_;
-
- const BuildSettings* build_settings_;
- Label default_toolchain_label_;
-
- // Records for the build config file loads.
- typedef std::map<Label, std::unique_ptr<ToolchainRecord>> ToolchainRecordMap;
- ToolchainRecordMap toolchain_records_;
-};
-
-#endif // TOOLS_GN_LOADER_H_
diff --git a/gn/tools/gn/loader_unittest.cc b/gn/tools/gn/loader_unittest.cc
deleted file mode 100644
index dc8b0df82ad..00000000000
--- a/gn/tools/gn/loader_unittest.cc
+++ /dev/null
@@ -1,262 +0,0 @@
-// 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 <map>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/err.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/tokenizer.h"
-#include "util/msg_loop.h"
-#include "util/test/test.h"
-
-namespace {
-
-bool ItemContainsBuildDependencyFile(const Item* item,
- const SourceFile& source_file) {
- const auto& build_dependency_files = item->build_dependency_files();
- return build_dependency_files.end() !=
- build_dependency_files.find(source_file);
-}
-
-class MockBuilder {
- public:
- void OnItemDefined(std::unique_ptr<Item> item);
- std::vector<const Item*> GetAllItems() const;
-
- private:
- std::vector<std::unique_ptr<Item>> items_;
-};
-
-void MockBuilder::OnItemDefined(std::unique_ptr<Item> item) {
- items_.push_back(std::move(item));
-}
-
-std::vector<const Item*> MockBuilder::GetAllItems() const {
- std::vector<const Item*> result;
- for (const auto& item : items_) {
- result.push_back(item.get());
- }
-
- return result;
-}
-
-class MockInputFileManager {
- public:
- typedef base::Callback<void(const ParseNode*)> Callback;
-
- MockInputFileManager() = default;
-
- LoaderImpl::AsyncLoadFileCallback GetCallback();
-
- // Sets a given response for a given source file.
- void AddCannedResponse(const SourceFile& source_file,
- const std::string& source);
-
- // Returns true if there is/are pending load(s) matching the given file(s).
- bool HasOnePending(const SourceFile& f) const;
- bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const;
-
- void IssueAllPending();
-
- private:
- struct CannedResult {
- std::unique_ptr<InputFile> input_file;
- std::vector<Token> tokens;
- std::unique_ptr<ParseNode> root;
- };
-
- bool AsyncLoadFile(const LocationRange& origin,
- const BuildSettings* build_settings,
- const SourceFile& file_name,
- const Callback& callback,
- Err* err) {
- pending_.push_back(std::make_pair(file_name, callback));
- return true;
- }
-
- typedef std::map<SourceFile, std::unique_ptr<CannedResult>> CannedResponseMap;
- CannedResponseMap canned_responses_;
-
- std::vector<std::pair<SourceFile, Callback>> pending_;
-};
-
-LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetCallback() {
- return base::Bind(&MockInputFileManager::AsyncLoadFile,
- base::Unretained(this));
-}
-
-// Sets a given response for a given source file.
-void MockInputFileManager::AddCannedResponse(const SourceFile& source_file,
- const std::string& source) {
- std::unique_ptr<CannedResult> canned = std::make_unique<CannedResult>();
- canned->input_file = std::make_unique<InputFile>(source_file);
- canned->input_file->SetContents(source);
-
- // Tokenize.
- Err err;
- canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Parse.
- canned->root = Parser::Parse(canned->tokens, &err);
- EXPECT_FALSE(err.has_error());
-
- canned_responses_[source_file] = std::move(canned);
-}
-
-bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
- return pending_.size() == 1u && pending_[0].first == f;
-}
-
-bool MockInputFileManager::HasTwoPending(const SourceFile& f1,
- const SourceFile& f2) const {
- if (pending_.size() != 2u)
- return false;
- return pending_[0].first == f1 && pending_[1].first == f2;
-}
-
-void MockInputFileManager::IssueAllPending() {
- BlockNode block(BlockNode::DISCARDS_RESULT); // Default response.
-
- for (const auto& cur : pending_) {
- CannedResponseMap::const_iterator found = canned_responses_.find(cur.first);
- if (found == canned_responses_.end())
- cur.second.Run(&block);
- else
- cur.second.Run(found->second->root.get());
- }
- pending_.clear();
-}
-
-// LoaderTest ------------------------------------------------------------------
-
-class LoaderTest : public TestWithScheduler {
- public:
- LoaderTest() { build_settings_.SetBuildDir(SourceDir("//out/Debug/")); }
-
- protected:
- BuildSettings build_settings_;
- MockBuilder mock_builder_;
- MockInputFileManager mock_ifm_;
-};
-
-} // namespace
-
-// -----------------------------------------------------------------------------
-
-TEST_F(LoaderTest, Foo) {
- SourceFile build_config("//build/config/BUILDCONFIG.gn");
- build_settings_.set_build_config_file(build_config);
-
- scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
-
- // The default toolchain needs to be set by the build config file.
- mock_ifm_.AddCannedResponse(build_config,
- "set_default_toolchain(\"//tc:tc\")");
-
- loader->set_async_load_file(mock_ifm_.GetCallback());
-
- // Request the root build file be loaded. This should kick off the default
- // build config loading.
- SourceFile root_build("//BUILD.gn");
- loader->Load(root_build, LocationRange(), Label());
- EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
-
- // Completing the build config load should kick off the root build file load.
- mock_ifm_.IssueAllPending();
- MsgLoop::Current()->RunUntilIdleForTesting();
- EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
-
- // Load the root build file.
- mock_ifm_.IssueAllPending();
- MsgLoop::Current()->RunUntilIdleForTesting();
-
- // Schedule some other file to load in another toolchain.
- Label second_tc(SourceDir("//tc2/"), "tc2");
- SourceFile second_file("//foo/BUILD.gn");
- loader->Load(second_file, LocationRange(), second_tc);
- EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn")));
-
- // Running the toolchain file should schedule the build config file to load
- // for that toolchain.
- mock_ifm_.IssueAllPending();
- MsgLoop::Current()->RunUntilIdleForTesting();
-
- // We have to tell it we have a toolchain definition now (normally the
- // builder would do this).
- const Settings* default_settings = loader->GetToolchainSettings(Label());
- Toolchain second_tc_object(default_settings, second_tc);
- loader->ToolchainLoaded(&second_tc_object);
- EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
-
- // Scheduling a second file to load in that toolchain should not make it
- // pending yet (it's waiting for the build config).
- SourceFile third_file("//bar/BUILD.gn");
- loader->Load(third_file, LocationRange(), second_tc);
- EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
-
- // Running the build config file should make our third file pending.
- mock_ifm_.IssueAllPending();
- MsgLoop::Current()->RunUntilIdleForTesting();
- EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file));
-
- EXPECT_FALSE(scheduler().is_failed());
-}
-
-TEST_F(LoaderTest, BuildDependencyFilesAreCollected) {
- SourceFile build_config("//build/config/BUILDCONFIG.gn");
- SourceFile root_build("//BUILD.gn");
- build_settings_.set_build_config_file(build_config);
- build_settings_.set_item_defined_callback(base::Bind(
- &MockBuilder::OnItemDefined, base::Unretained(&mock_builder_)));
-
- scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
- mock_ifm_.AddCannedResponse(build_config,
- "set_default_toolchain(\"//tc:tc\")");
- mock_ifm_.AddCannedResponse(SourceFile("//test.gni"), "concurrent_jobs = 1");
- std::string root_build_content =
- "executable(\"a\") { sources = [ \"a.cc\" ] }\n"
- "config(\"b\") { configs = [\"//t:t\"] }\n"
- "toolchain(\"c\") {}\n"
- "pool(\"d\") { depth = 1 }";
- mock_ifm_.AddCannedResponse(root_build, root_build_content);
-
- loader->set_async_load_file(mock_ifm_.GetCallback());
-
- // Request the root build file be loaded. This should kick off the default
- // build config loading.
- loader->Load(root_build, LocationRange(), Label());
- EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
-
- // Completing the build config load should kick off the root build file load.
- mock_ifm_.IssueAllPending();
- MsgLoop::Current()->RunUntilIdleForTesting();
- EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
-
- // Completing the root build file should define a target which must have
- // set of source files hashes.
- mock_ifm_.IssueAllPending();
- MsgLoop::Current()->RunUntilIdleForTesting();
-
- std::vector<const Item*> items = mock_builder_.GetAllItems();
- EXPECT_TRUE(items[0]->AsTarget());
- EXPECT_TRUE(ItemContainsBuildDependencyFile(items[0], root_build));
- EXPECT_TRUE(items[1]->AsConfig());
- EXPECT_TRUE(ItemContainsBuildDependencyFile(items[1], root_build));
- EXPECT_TRUE(items[2]->AsToolchain());
- EXPECT_TRUE(ItemContainsBuildDependencyFile(items[2], root_build));
- EXPECT_TRUE(items[3]->AsPool());
- EXPECT_TRUE(ItemContainsBuildDependencyFile(items[3], root_build));
-
- EXPECT_FALSE(scheduler().is_failed());
-}
diff --git a/gn/tools/gn/location.cc b/gn/tools/gn/location.cc
deleted file mode 100644
index 2e7b27b3d2e..00000000000
--- a/gn/tools/gn/location.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 "tools/gn/location.h"
-
-#include <tuple>
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/input_file.h"
-
-Location::Location() = default;
-
-Location::Location(const InputFile* file,
- int line_number,
- int column_number,
- int byte)
- : file_(file),
- line_number_(line_number),
- column_number_(column_number),
- byte_(byte) {}
-
-bool Location::operator==(const Location& other) const {
- return other.file_ == file_ && other.line_number_ == line_number_ &&
- other.column_number_ == column_number_;
-}
-
-bool Location::operator!=(const Location& other) const {
- return !operator==(other);
-}
-
-bool Location::operator<(const Location& other) const {
- DCHECK(file_ == other.file_);
- return std::tie(line_number_, column_number_) <
- std::tie(other.line_number_, other.column_number_);
-}
-
-std::string Location::Describe(bool include_column_number) const {
- if (!file_)
- return std::string();
-
- std::string ret;
- if (file_->friendly_name().empty())
- ret = file_->name().value();
- else
- ret = file_->friendly_name();
-
- ret += ":";
- ret += base::IntToString(line_number_);
- if (include_column_number) {
- ret += ":";
- ret += base::IntToString(column_number_);
- }
- return ret;
-}
-
-LocationRange::LocationRange() = default;
-
-LocationRange::LocationRange(const Location& begin, const Location& end)
- : begin_(begin), end_(end) {
- DCHECK(begin_.file() == end_.file());
-}
-
-LocationRange LocationRange::Union(const LocationRange& other) const {
- DCHECK(begin_.file() == other.begin_.file());
- return LocationRange(begin_ < other.begin_ ? begin_ : other.begin_,
- end_ < other.end_ ? other.end_ : end_);
-}
diff --git a/gn/tools/gn/metadata.cc b/gn/tools/gn/metadata.cc
deleted file mode 100644
index bc4c272ea42..00000000000
--- a/gn/tools/gn/metadata.cc
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/metadata.h"
-
-#include "tools/gn/filesystem_utils.h"
-
-const char kMetadata_Help[] =
- R"(Metadata Collection
-
- Metadata is information attached to targets throughout the dependency tree. GN
- allows for the collection of this data into files written during the generation
- step, enabing users to expose and aggregate this data based on the dependency
- tree.
-
-generated_file targets
-
- Similar to the write_file() function, the generated_file target type
- creates a file in the specified location with the specified content. The
- primary difference between the function and the target type is that the
- write_file function does the file write at parse time, while the
- generated_file target type writes at target resolution time. See
- "gn help generated_file" for more detail.
-
- When written at target resolution time, the generated_file enables GN to
- collect and write aggregated metadata from dependents.
-
- A generated_file target can declare either 'contents' (to write statically
- known contents to a file) or 'data_keys'(to aggregate metadata and write the
- result to a file). It can also specify 'walk_keys' (to restrict the metadata
- collection), 'output_conversion', and 'rebase'.
-
-
-Collection and Aggregation
-
- Targets can declare a 'metadata' variable containing a scope, and this
- metadata is collected and written to file by generated_file aggregation
- targets. The 'metadata' scope must contain only list values, since the
- aggregation step collects a list of these values.
-
- During the target resolution, generated_file targets will walk their
- dependencies recursively, collecting metadata based on the specified
- 'data_keys'. 'data_keys' is specified as a list of strings, used by the walk
- to identify which variables in dependencies' 'metadata' scopes to collect.
-
- The walk begins with the listed dependencies of the 'generated_file' target,
- for each checking the "metadata" scope for any of the "data_keys". If
- present, the data in those variables is appended to the aggregate list. Note
- that this means that if more than one walk key is specified, the data in all
- of them will be aggregated into one list. From there, the walk will then
- recurse into the dependencies of each target it encounters, collecting the
- specified metadata for each.
-
- For example:
-
- group("a") {
- metadata = {
- doom_melon = [ "enable" ]
- my_files = [ "foo.cpp" ]
- my_extra_files = [ "bar.cpp" ]
- }
-
- deps = [ ":b" ]
- }
-
- group("b") {
- metadata = {
- my_files = [ "baz.cpp" ]
- }
- }
-
- generated_file("metadata") {
- outputs = [ "$root_build_dir/my_files.json" ]
- data_keys = [ "my_files", "my_extra_files" ]
-
- deps = [ ":a" ]
- }
-
- The above will produce the following file data:
-
- foo.cpp
- bar.cpp
- baz.cpp
-
- The dependency walk can be limited by using the "walk_keys". This is a list of
- labels that should be included in the walk. All labels specified here should
- also be in one of the deps lists. These keys act as barriers, where the walk
- will only recurse into targets listed here. An empty list in all specified
- barriers will end that portion of the walk.
-
- group("a") {
- metadata = {
- my_files = [ "foo.cpp" ]
- my_files_barrier [ ":b" ]
- }
-
- deps = [ ":b", ":c" ]
- }
-
- group("b") {
- metadata = {
- my_files = [ "bar.cpp" ]
- }
- }
-
- group("c") {
- metadata = {
- my_files = [ "doom_melon.cpp" ]
- }
- }
-
- generated_file("metadata") {
- outputs = [ "$root_build_dir/my_files.json" ]
- data_keys = [ "my_files", "my_extra_files" ]
-
- deps = [ ":a" ]
- }
-
- The above will produce the following file data (note that `doom_melon.cpp` is
- not included):
-
- foo.cpp
- bar.cpp
-
- A common example of this sort of barrier is in builds that have host tools
- built as part of the tree, but do not want the metadata from those host tools
- to be collected with the target-side code.
-
-Common Uses
-
- Metadata can be used to collect information about the different targets in the
- build, and so a common use is to provide post-build tooling with a set of data
- necessary to do aggregation tasks. For example, if each test target specifies
- the output location of its binary to run in a metadata field, that can be
- collected into a single file listing the locations of all tests in the
- dependency tree. A local build tool (or continuous integration infrastructure)
- can then use that file to know which tests exist, and where, and run them
- accordingly.
-
- Another use is in image creation, where a post-build image tool needs to know
- various pieces of information about the components it should include in order
- to put together the correct image.
-)";
-
-bool Metadata::WalkStep(const BuildSettings* settings,
- const std::vector<std::string>& keys_to_extract,
- const std::vector<std::string>& keys_to_walk,
- const SourceDir& rebase_dir,
- std::vector<Value>* next_walk_keys,
- std::vector<Value>* result,
- Err* err) const {
- // If there's no metadata, there's nothing to find, so quick exit.
- if (contents_.empty()) {
- next_walk_keys->emplace_back(nullptr, "");
- return true;
- }
-
- // Pull the data from each specified key.
- for (const auto& key : keys_to_extract) {
- auto iter = contents_.find(key);
- if (iter == contents_.end())
- continue;
- assert(iter->second.type() == Value::LIST);
-
- if (!rebase_dir.is_null()) {
- for (const auto& val : iter->second.list_value()) {
- std::pair<Value, bool> pair =
- this->RebaseValue(settings, rebase_dir, val, err);
- if (!pair.second)
- return false;
- result->push_back(pair.first);
- }
- } else {
- result->insert(result->end(), iter->second.list_value().begin(),
- iter->second.list_value().end());
- }
- }
-
- // Get the targets to look at next. If no keys_to_walk are present, we push
- // the empty string to the list so that the target knows to include its deps
- // and data_deps. The values used here must be lists of strings.
- bool found_walk_key = false;
- for (const auto& key : keys_to_walk) {
- auto iter = contents_.find(key);
- if (iter != contents_.end()) {
- found_walk_key = true;
- assert(iter->second.type() == Value::LIST);
- for (const auto& val : iter->second.list_value()) {
- if (!val.VerifyTypeIs(Value::STRING, err))
- return false;
- next_walk_keys->emplace_back(val);
- }
- }
- }
-
- if (!found_walk_key)
- next_walk_keys->emplace_back(nullptr, "");
-
- return true;
-}
-
-std::pair<Value, bool> Metadata::RebaseValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const {
- switch (value.type()) {
- case Value::STRING:
- return this->RebaseStringValue(settings, rebase_dir, value, err);
- case Value::LIST:
- return this->RebaseListValue(settings, rebase_dir, value, err);
- case Value::SCOPE:
- return this->RebaseScopeValue(settings, rebase_dir, value, err);
- default:
- return std::make_pair(value, true);
- }
-}
-
-std::pair<Value, bool> Metadata::RebaseStringValue(
- const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const {
- if (!value.VerifyTypeIs(Value::STRING, err))
- return std::make_pair(value, false);
- std::string filename = source_dir_.ResolveRelativeAs(
- /*as_file = */ true, value, err, settings->root_path_utf8());
- if (err->has_error())
- return std::make_pair(value, false);
- Value rebased_value(value.origin(), RebasePath(filename, rebase_dir,
- settings->root_path_utf8()));
- return std::make_pair(rebased_value, true);
-}
-
-std::pair<Value, bool> Metadata::RebaseListValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const {
- if (!value.VerifyTypeIs(Value::LIST, err))
- return std::make_pair(value, false);
-
- Value rebased_list_value(value.origin(), Value::LIST);
- for (auto& val : value.list_value()) {
- std::pair<Value, bool> pair = RebaseValue(settings, rebase_dir, val, err);
- if (!pair.second)
- return std::make_pair(value, false);
- rebased_list_value.list_value().push_back(pair.first);
- }
- return std::make_pair(rebased_list_value, true);
-}
-
-std::pair<Value, bool> Metadata::RebaseScopeValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const {
- if (!value.VerifyTypeIs(Value::SCOPE, err))
- return std::make_pair(value, false);
- Value rebased_scope_value(value);
- Scope::KeyValueMap scope_values;
- value.scope_value()->GetCurrentScopeValues(&scope_values);
- for (auto& value_pair : scope_values) {
- std::pair<Value, bool> pair =
- RebaseValue(settings, rebase_dir, value_pair.second, err);
- if (!pair.second)
- return std::make_pair(value, false);
-
- rebased_scope_value.scope_value()->SetValue(value_pair.first, pair.first,
- value.origin());
- }
- return std::make_pair(rebased_scope_value, true);
-} \ No newline at end of file
diff --git a/gn/tools/gn/metadata.h b/gn/tools/gn/metadata.h
deleted file mode 100644
index 7b6168a5e1a..00000000000
--- a/gn/tools/gn/metadata.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_METADATA_H_
-#define TOOLS_GN_METADATA_H_
-
-#include <memory>
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/source_dir.h"
-
-extern const char kMetadata_Help[];
-
-// Metadata about a particular target.
-//
-// Metadata is a collection of keys and values relating to a particular target.
-// Generally, these keys will include three categories of strings: ordinary
-// strings, filenames intended to be rebased according to their particular
-// source directory, and target labels intended to be used as barriers to the
-// walk. Verfication of these categories occurs at walk time, not creation
-// time (since it is not clear until the walk which values are intended for
-// which purpose).
-//
-// Represented as a scope in the expression language, here it is reduced to just
-// the KeyValueMap (since it doesn't need the logical overhead of a full scope).
-// Values must be lists of strings, as the walking collection logic contatenates
-// their values across targets.
-class Metadata {
- public:
- using Contents = Scope::KeyValueMap;
-
- Metadata() = default;
-
- const ParseNode* origin() const { return origin_; }
- void set_origin(const ParseNode* origin) { origin_ = origin; }
-
- // The contents of this metadata varaiable.
- const Contents& contents() const { return contents_; }
- Contents& contents() { return contents_; }
- void set_contents(Contents&& contents) { contents_ = std::move(contents); }
-
- // The relative source directory to use when rebasing.
- const SourceDir& source_dir() const { return source_dir_; }
- SourceDir& source_dir() { return source_dir_; }
- void set_source_dir(const SourceDir& d) { source_dir_ = d; }
-
- // Collect the specified metadata from this instance.
- //
- // Calling this will populate `next_walk_keys` with the values of targets to
- // be walked next (with the empty string "" indicating that the target should
- // walk all of its deps and data_deps).
- bool WalkStep(const BuildSettings* settings,
- const std::vector<std::string>& keys_to_extract,
- const std::vector<std::string>& keys_to_walk,
- const SourceDir& rebase_dir,
- std::vector<Value>* next_walk_keys,
- std::vector<Value>* result,
- Err* err) const;
-
- private:
- const ParseNode* origin_ = nullptr;
- Contents contents_;
- SourceDir source_dir_;
-
- std::pair<Value, bool> RebaseValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const;
-
- std::pair<Value, bool> RebaseStringValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const;
-
- std::pair<Value, bool> RebaseListValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const;
-
- std::pair<Value, bool> RebaseScopeValue(const BuildSettings* settings,
- const SourceDir& rebase_dir,
- const Value& value,
- Err* err) const;
-
- DISALLOW_COPY_AND_ASSIGN(Metadata);
-};
-
-#endif // TOOLS_GN_METADATA_H_
diff --git a/gn/tools/gn/metadata_unittest.cc b/gn/tools/gn/metadata_unittest.cc
deleted file mode 100644
index 901e53995bb..00000000000
--- a/gn/tools/gn/metadata_unittest.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/metadata.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(MetadataTest, SetContents) {
- Metadata metadata;
-
- ASSERT_TRUE(metadata.contents().empty());
-
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- Value b_expected(nullptr, Value::LIST);
- b_expected.list_value().push_back(Value(nullptr, true));
-
- Metadata::Contents contents;
- contents.insert(std::pair<base::StringPiece, Value>("a", a_expected));
- contents.insert(std::pair<base::StringPiece, Value>("b", b_expected));
-
- metadata.set_contents(std::move(contents));
-
- ASSERT_EQ(metadata.contents().size(), 2);
- auto a_actual = metadata.contents().find("a");
- auto b_actual = metadata.contents().find("b");
- ASSERT_FALSE(a_actual == metadata.contents().end());
- ASSERT_FALSE(b_actual == metadata.contents().end());
- ASSERT_EQ(a_actual->second, a_expected);
- ASSERT_EQ(b_actual->second, b_expected);
-}
-
-TEST(MetadataTest, Walk) {
- TestWithScope setup;
- Metadata metadata;
- metadata.set_source_dir(SourceDir("/usr/home/files/"));
-
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
- a_expected.list_value().push_back(Value(nullptr, "bar.h"));
-
- metadata.contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- std::vector<std::string> data_keys;
- data_keys.emplace_back("a");
- std::vector<std::string> walk_keys;
- std::vector<Value> next_walk_keys;
- std::vector<Value> results;
-
- std::vector<Value> expected;
- expected.emplace_back(Value(nullptr, "foo.cpp"));
- expected.emplace_back(Value(nullptr, "bar.h"));
-
- std::vector<Value> expected_walk_keys;
- expected_walk_keys.emplace_back(nullptr, "");
-
- Err err;
- EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir(), &next_walk_keys,
- &results, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(next_walk_keys, expected_walk_keys);
- EXPECT_EQ(results, expected);
-}
-
-TEST(MetadataTest, WalkWithRebase) {
- TestWithScope setup;
- Metadata metadata;
- metadata.set_source_dir(SourceDir("/usr/home/files/"));
-
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
- a_expected.list_value().push_back(Value(nullptr, "foo/bar.h"));
-
- metadata.contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- std::vector<std::string> data_keys;
- data_keys.emplace_back("a");
- std::vector<std::string> walk_keys;
- std::vector<Value> next_walk_keys;
- std::vector<Value> results;
-
- std::vector<Value> expected;
- expected.emplace_back(Value(nullptr, "../home/files/foo.cpp"));
- expected.emplace_back(Value(nullptr, "../home/files/foo/bar.h"));
-
- std::vector<Value> expected_walk_keys;
- expected_walk_keys.emplace_back(nullptr, "");
-
- Err err;
- EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir("/usr/foo_dir/"),
- &next_walk_keys, &results, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(next_walk_keys, expected_walk_keys);
- EXPECT_EQ(results, expected);
-}
-
-TEST(MetadataTest, WalkWithRebaseNonString) {
- TestWithScope setup;
- Metadata metadata;
- metadata.set_source_dir(SourceDir("/usr/home/files/"));
-
- Value a(nullptr, Value::LIST);
- Value inner_list(nullptr, Value::LIST);
- Value inner_scope(nullptr, Value::SCOPE);
- inner_list.list_value().push_back(Value(nullptr, "foo.cpp"));
- inner_list.list_value().push_back(Value(nullptr, "foo/bar.h"));
- a.list_value().push_back(inner_list);
-
- std::unique_ptr<Scope> scope(new Scope(setup.settings()));
- scope->SetValue("a1", Value(nullptr, "foo2.cpp"), nullptr);
- scope->SetValue("a2", Value(nullptr, "foo/bar2.h"), nullptr);
- inner_scope.SetScopeValue(std::move(scope));
- a.list_value().push_back(inner_scope);
-
- metadata.contents().insert(std::pair<base::StringPiece, Value>("a", a));
- std::vector<std::string> data_keys;
- data_keys.emplace_back("a");
- std::vector<std::string> walk_keys;
- std::vector<Value> next_walk_keys;
- std::vector<Value> results;
-
- std::vector<Value> expected;
- Value inner_list_expected(nullptr, Value::LIST);
- Value inner_scope_expected(nullptr, Value::SCOPE);
- inner_list_expected.list_value().push_back(
- Value(nullptr, "../home/files/foo.cpp"));
- inner_list_expected.list_value().push_back(
- Value(nullptr, "../home/files/foo/bar.h"));
- expected.push_back(inner_list_expected);
-
- std::unique_ptr<Scope> scope_expected(new Scope(setup.settings()));
- scope_expected->SetValue("a1", Value(nullptr, "../home/files/foo2.cpp"),
- nullptr);
- scope_expected->SetValue("a2", Value(nullptr, "../home/files/foo/bar2.h"),
- nullptr);
- inner_scope_expected.SetScopeValue(std::move(scope_expected));
- expected.push_back(inner_scope_expected);
-
- std::vector<Value> expected_walk_keys;
- expected_walk_keys.emplace_back(nullptr, "");
-
- Err err;
- EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir("/usr/foo_dir/"),
- &next_walk_keys, &results, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(next_walk_keys, expected_walk_keys);
- EXPECT_EQ(results, expected);
-}
-
-TEST(MetadataTest, WalkKeysToWalk) {
- TestWithScope setup;
- Metadata metadata;
- metadata.set_source_dir(SourceDir("/usr/home/files/"));
-
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "//target"));
-
- metadata.contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- std::vector<std::string> data_keys;
- std::vector<std::string> walk_keys;
- walk_keys.emplace_back("a");
- std::vector<Value> next_walk_keys;
- std::vector<Value> results;
-
- std::vector<Value> expected_walk_keys;
- expected_walk_keys.emplace_back(nullptr, "//target");
-
- Err err;
- EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir(), &next_walk_keys,
- &results, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(next_walk_keys, expected_walk_keys);
- EXPECT_TRUE(results.empty());
-}
-
-TEST(MetadataTest, WalkNoContents) {
- TestWithScope setup;
- Metadata metadata;
- metadata.set_source_dir(SourceDir("/usr/home/files/"));
-
- std::vector<std::string> data_keys;
- std::vector<std::string> walk_keys;
- std::vector<Value> next_walk_keys;
- std::vector<Value> results;
-
- std::vector<Value> expected_walk_keys;
- expected_walk_keys.emplace_back(nullptr, "");
-
- Err err;
- EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir(), &next_walk_keys,
- &results, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(next_walk_keys, expected_walk_keys);
- EXPECT_TRUE(results.empty());
-}
-
-TEST(MetadataTest, WalkNoKeysWithContents) {
- TestWithScope setup;
- Metadata metadata;
- metadata.set_source_dir(SourceDir("/usr/home/files/"));
-
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "//target"));
-
- metadata.contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- std::vector<std::string> data_keys;
- std::vector<std::string> walk_keys;
- std::vector<Value> next_walk_keys;
- std::vector<Value> results;
-
- std::vector<Value> expected_walk_keys;
- expected_walk_keys.emplace_back(nullptr, "");
-
- Err err;
- EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir(), &next_walk_keys,
- &results, &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(next_walk_keys, expected_walk_keys);
- EXPECT_TRUE(results.empty());
-}
diff --git a/gn/tools/gn/metadata_walk.cc b/gn/tools/gn/metadata_walk.cc
deleted file mode 100644
index 3022e5e265c..00000000000
--- a/gn/tools/gn/metadata_walk.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/metadata_walk.h"
-
-std::vector<Value> WalkMetadata(
- const UniqueVector<const Target*>& targets_to_walk,
- const std::vector<std::string>& keys_to_extract,
- const std::vector<std::string>& keys_to_walk,
- const SourceDir& rebase_dir,
- std::set<const Target*>* targets_walked,
- Err* err) {
- std::vector<Value> result;
- for (const auto* target : targets_to_walk) {
- auto pair = targets_walked->insert(target);
- if (pair.second) {
- if (!target->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir, false,
- &result, targets_walked, err))
- return std::vector<Value>();
- }
- }
- return result;
-} \ No newline at end of file
diff --git a/gn/tools/gn/metadata_walk.h b/gn/tools/gn/metadata_walk.h
deleted file mode 100644
index 9f4f34d3b29..00000000000
--- a/gn/tools/gn/metadata_walk.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_METADATAWALK_H_
-#define TOOLS_GN_METADATAWALK_H_
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/target.h"
-#include "tools/gn/unique_vector.h"
-#include "tools/gn/value.h"
-
-// Function to collect metadata from resolved targets listed in targets_walked.
-// Intended to be called after all targets are resolved.
-//
-// This populates targets_walked with all targets touched by this walk, and
-// returns the list of metadata values.
-std::vector<Value> WalkMetadata(
- const UniqueVector<const Target*>& targets_to_walk,
- const std::vector<std::string>& keys_to_extract,
- const std::vector<std::string>& keys_to_walk,
- const SourceDir& rebase_dir,
- std::set<const Target*>* targets_walked,
- Err* err);
-
-#endif // TOOLS_GN_METADATAWALK_H_
diff --git a/gn/tools/gn/metadata_walk_unittest.cc b/gn/tools/gn/metadata_walk_unittest.cc
deleted file mode 100644
index a18a07e35d5..00000000000
--- a/gn/tools/gn/metadata_walk_unittest.cc
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/metadata_walk.h"
-
-#include "tools/gn/metadata.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/unique_vector.h"
-#include "util/test/test.h"
-
-TEST(MetadataWalkTest, CollectNoRecurse) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value b_expected(nullptr, Value::LIST);
- b_expected.list_value().push_back(Value(nullptr, true));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("b", b_expected));
-
- one.metadata().set_source_dir(SourceDir("/usr/home/files/"));
-
- TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
- Value a_2_expected(nullptr, Value::LIST);
- a_2_expected.list_value().push_back(Value(nullptr, "bar"));
- two.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_2_expected));
-
- Value b_2_expected(nullptr, Value::LIST);
- b_2_expected.list_value().push_back(Value(nullptr, false));
- two.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("b", b_2_expected));
-
- two.metadata().set_source_dir(SourceDir("/usr/home/files/inner"));
-
- UniqueVector<const Target*> targets;
- targets.push_back(&one);
- targets.push_back(&two);
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
- data_keys.push_back("b");
-
- std::vector<std::string> walk_keys;
-
- Err err;
- std::set<const Target*> targets_walked;
- std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
- SourceDir(), &targets_walked, &err);
- EXPECT_FALSE(err.has_error());
-
- std::vector<Value> expected;
- expected.push_back(Value(nullptr, "foo"));
- expected.push_back(Value(nullptr, true));
- expected.push_back(Value(nullptr, "bar"));
- expected.push_back(Value(nullptr, false));
- EXPECT_EQ(result, expected);
-
- std::set<const Target*> expected_walked_targets;
- expected_walked_targets.insert(&one);
- expected_walked_targets.insert(&two);
- EXPECT_EQ(targets_walked, expected_walked_targets);
-}
-
-TEST(MetadataWalkTest, CollectWithRecurse) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value b_expected(nullptr, Value::LIST);
- b_expected.list_value().push_back(Value(nullptr, true));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("b", b_expected));
-
- TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
- Value a_2_expected(nullptr, Value::LIST);
- a_2_expected.list_value().push_back(Value(nullptr, "bar"));
- two.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_2_expected));
-
- one.public_deps().push_back(LabelTargetPair(&two));
-
- UniqueVector<const Target*> targets;
- targets.push_back(&one);
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
- data_keys.push_back("b");
-
- std::vector<std::string> walk_keys;
-
- Err err;
- std::set<const Target*> targets_walked;
- std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
- SourceDir(), &targets_walked, &err);
- EXPECT_FALSE(err.has_error());
-
- std::vector<Value> expected;
- expected.push_back(Value(nullptr, "bar"));
- expected.push_back(Value(nullptr, "foo"));
- expected.push_back(Value(nullptr, true));
- EXPECT_EQ(result, expected);
-
- std::set<const Target*> expected_walked_targets;
- expected_walked_targets.insert(&one);
- expected_walked_targets.insert(&two);
- EXPECT_EQ(targets_walked, expected_walked_targets);
-}
-
-TEST(MetadataWalkTest, CollectWithBarrier) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value walk_expected(nullptr, Value::LIST);
- walk_expected.list_value().push_back(
- Value(nullptr, "//foo:two(//toolchain:default)"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("walk", walk_expected));
-
- TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
- Value a_2_expected(nullptr, Value::LIST);
- a_2_expected.list_value().push_back(Value(nullptr, "bar"));
- two.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_2_expected));
-
- TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
- Value a_3_expected(nullptr, Value::LIST);
- a_3_expected.list_value().push_back(Value(nullptr, "baz"));
- three.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_3_expected));
-
- one.public_deps().push_back(LabelTargetPair(&two));
- one.public_deps().push_back(LabelTargetPair(&three));
-
- UniqueVector<const Target*> targets;
- targets.push_back(&one);
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
-
- std::vector<std::string> walk_keys;
- walk_keys.push_back("walk");
-
- Err err;
- std::set<const Target*> targets_walked;
- std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
- SourceDir(), &targets_walked, &err);
- EXPECT_FALSE(err.has_error()) << err.message();
-
- std::vector<Value> expected;
- expected.push_back(Value(nullptr, "bar"));
- expected.push_back(Value(nullptr, "foo"));
- EXPECT_EQ(result, expected) << result.size();
-
- std::set<const Target*> expected_walked_targets;
- expected_walked_targets.insert(&one);
- expected_walked_targets.insert(&two);
- EXPECT_EQ(targets_walked, expected_walked_targets);
-}
-
-TEST(MetadataWalkTest, CollectWithError) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value walk_expected(nullptr, Value::LIST);
- walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("walk", walk_expected));
-
- UniqueVector<const Target*> targets;
- targets.push_back(&one);
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
-
- std::vector<std::string> walk_keys;
- walk_keys.push_back("walk");
-
- Err err;
- std::set<const Target*> targets_walked;
- std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
- SourceDir(), &targets_walked, &err);
- EXPECT_TRUE(result.empty());
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ(err.message(),
- "I was expecting //foo:missing(//toolchain:default) to be a "
- "dependency of //foo:one(//toolchain:default). "
- "Make sure it's included in the deps or data_deps, and that you've "
- "specified the appropriate toolchain.")
- << err.message();
-}
diff --git a/gn/tools/gn/misc/emacs/gn-mode.el b/gn/tools/gn/misc/emacs/gn-mode.el
deleted file mode 100644
index 5930b585220..00000000000
--- a/gn/tools/gn/misc/emacs/gn-mode.el
+++ /dev/null
@@ -1,192 +0,0 @@
-;;; gn-mode.el - A major mode for editing gn files.
-
-;; 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.
-
-;; Author: Elliot Glaysher <erg@chromium.org>
-;; Created: April 03, 2015
-;; Keywords: tools, gn, ninja, chromium
-
-;; This file is not part of GNU Emacs.
-
-;;; Commentary:
-
-;; A major mode for editing GN files. GN stands for Generate Ninja. GN is the
-;; meta build system used in Chromium. For more information on GN, see the GN
-;; manual: <https://chromium.googlesource.com/chromium/src/+/master/tools/gn/README.md>
-
-;;; To Do:
-
-;; - We syntax highlight builtin actions, but don't highlight instantiations of
-;; templates. Should we?
-
-
-
-(require 'smie)
-
-(defgroup gn nil
- "Major mode for editing Generate Ninja files."
- :prefix "gn-"
- :group 'languages)
-
-(defcustom gn-indent-basic 2
- "The number of spaces to indent a new scope."
- :group 'gn
- :type 'integer)
-
-(defcustom gn-format-command "gn format --stdin"
- "The command to run to format gn files in place."
- :group 'gn
- :type 'string)
-
-(defgroup gn-faces nil
- "Faces used in Generate Ninja mode."
- :group 'gn
- :group 'faces)
-
-(defface gn-embedded-variable
- '((t :inherit font-lock-variable-name-face))
- "Font lock face used to highlight variable names in strings."
- :group 'gn-faces)
-
-(defface gn-embedded-variable-boundary
- '((t :bold t
- :inherit gn-embedded-variable))
- "Font lock face used to highlight the '$' that starts a
-variable name or the '{{' and '}}' which surround it."
- :group 'gn-faces)
-
-(defvar gn-font-lock-reserved-keywords
- '("true" "false" "if" "else"))
-
-(defvar gn-font-lock-target-declaration-keywords
- '("action" "action_foreach" "bundle_data" "copy" "create_bundle" "executable"
- "group" "loadable_module" "shared_library" "source_set" "static_library"
- "generated_file" "target"))
-
-;; pool() is handled specially since it's also a variable name
-(defvar gn-font-lock-buildfile-fun-keywords
- '("assert" "config" "declare_args" "defined" "exec_script" "foreach"
- "forward_variables_from" "get_label_info" "get_path_info"
- "get_target_outputs" "getenv" "import" "not_needed" "print"
- "process_file_template" "read_file" "rebase_path" "set_default_toolchain"
- "set_defaults" "set_sources_assignment_filter" "split_list" "template"
- "tool" "toolchain" "propagates_configs" "write_file"))
-
-(defvar gn-font-lock-predefined-var-keywords
- '("current_cpu" "current_os" "current_toolchain" "default_toolchain"
- "host_cpu" "host_os" "invoker" "python_path" "root_build_dir" "root_gen_dir"
- "root_out_dir" "target_cpu" "target_gen_dir" "target_name" "target_os"
- "target_out_dir"))
-
-(defvar gn-font-lock-var-keywords
- '("all_dependent_configs" "allow_circular_includes_from" "arflags" "args"
- "asmflags" "assert_no_deps" "bundle_deps_filter" "bundle_executable_dir"
- "bundle_resources_dir" "bundle_root_dir" "cflags" "cflags_c" "cflags_cc"
- "cflags_objc" "cflags_objcc" "check_includes" "code_signing_args"
- "code_signing_outputs" "code_signing_script" "code_signing_sources"
- "complete_static_lib" "configs" "data" "data_deps" "defines" "depfile"
- "deps" "include_dirs" "inputs" "ldflags" "lib_dirs" "libs" "output_dir"
- "output_extension" "output_name" "output_prefix_override" "outputs" "pool"
- "precompiled_header" "precompiled_header_type" "precompiled_source"
- "product_type" "public" "public_configs" "public_deps"
- "response_file_contents" "script" "sources" "testonly" "visibility"
- "write_runtime_deps" "bundle_contents_dir" "contents" "output_conversion"
- "rebase" "data_keys" "walk_keys"))
-
-(defconst gn-font-lock-keywords
- `((,(regexp-opt gn-font-lock-reserved-keywords 'words) .
- font-lock-keyword-face)
- (,(regexp-opt gn-font-lock-target-declaration-keywords 'words) .
- font-lock-type-face)
- (,(regexp-opt gn-font-lock-buildfile-fun-keywords 'words) .
- font-lock-function-name-face)
- ;; pool() as a function
- ("\\<\\(pool\\)\\s-*("
- (1 font-lock-function-name-face))
- (,(regexp-opt gn-font-lock-predefined-var-keywords 'words) .
- font-lock-constant-face)
- (,(regexp-opt gn-font-lock-var-keywords 'words) .
- font-lock-variable-name-face)
- ;; $variables_like_this
- ("\\(\\$\\)\\([a-zA-Z0-9_]+\\)"
- (1 'gn-embedded-variable-boundary t)
- (2 'gn-embedded-variable t))
- ;; ${variables_like_this}
- ("\\(\\${\\)\\([^\n }]+\\)\\(}\\)"
- (1 'gn-embedded-variable-boundary t)
- (2 'gn-embedded-variable t)
- (3 'gn-embedded-variable-boundary t))
- ;; {{placeholders}} (see substitute_type.h)
- ("\\({{\\)\\([^\n }]+\\)\\(}}\\)"
- (1 'gn-embedded-variable-boundary t)
- (2 'gn-embedded-variable t)
- (3 'gn-embedded-variable-boundary t))))
-
-(defun gn-smie-rules (kind token)
- "These are slightly modified indentation rules from the SMIE
- Indentation Example info page. This changes the :before rule
- and adds a :list-intro to handle our x = [ ] syntax."
- (pcase (cons kind token)
- (`(:elem . basic) gn-indent-basic)
- (`(,_ . ",") (smie-rule-separator kind))
- (`(:list-intro . "") gn-indent-basic)
- (`(:before . ,(or `"[" `"(" `"{"))
- (if (smie-rule-hanging-p) (smie-rule-parent)))
- (`(:before . "if")
- (and (not (smie-rule-bolp)) (smie-rule-prev-p "else")
- (smie-rule-parent)))))
-
-(defun gn-fill-paragraph (&optional justify)
- "We only fill inside of comments in GN mode."
- (interactive "P")
- (or (fill-comment-paragraph justify)
- ;; Never return nil; `fill-paragraph' will perform its default behavior
- ;; if we do.
- t))
-
-(defun gn-run-format ()
- "Run 'gn format' on the buffer in place."
- (interactive)
- ;; We can't `save-excursion' here; that will put us at the beginning of the
- ;; shell output, aka the beginning of the document.
- (let ((my-start-line (line-number-at-pos)))
- (shell-command-on-region (point-min) (point-max) gn-format-command nil t)
- (goto-line my-start-line)))
-
-(defvar gn-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map "\C-c\C-f" 'gn-run-format)
- map))
-
-;;;###autoload
-(define-derived-mode gn-mode prog-mode "GN"
- "Major mode for editing gn (Generate Ninja)."
- :group 'gn
-
- (setq-local comment-use-syntax t)
- (setq-local comment-start "#")
- (setq-local comment-end "")
- (setq-local indent-tabs-mode nil)
-
- (setq-local fill-paragraph-function 'gn-fill-paragraph)
-
- (setq-local font-lock-defaults '(gn-font-lock-keywords))
-
- ;; For every 'rule("name") {', adds "name" to the imenu for quick navigation.
- (setq-local imenu-generic-expression
- '((nil "^\s*[a-zA-Z0-9_]+(\"\\([a-zA-Z0-9_]+\\)\")\s*{" 1)))
-
- (smie-setup nil #'gn-smie-rules)
- (setq-local smie-indent-basic gn-indent-basic)
-
- ;; python style comment: “# …”
- (modify-syntax-entry ?# "< b" gn-mode-syntax-table)
- (modify-syntax-entry ?\n "> b" gn-mode-syntax-table)
- (modify-syntax-entry ?_ "w" gn-mode-syntax-table))
-
-;;;###autoload
-(add-to-list 'auto-mode-alist '("\\.gni?\\'" . gn-mode))
-
-(provide 'gn-mode)
diff --git a/gn/tools/gn/misc/tm/GN.tmLanguage b/gn/tools/gn/misc/tm/GN.tmLanguage
deleted file mode 100644
index 5cccf4be399..00000000000
--- a/gn/tools/gn/misc/tm/GN.tmLanguage
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>fileTypes</key>
- <array>
- <string>gn</string>
- <string>gni</string>
- </array>
- <key>name</key>
- <string>GN</string>
- <key>patterns</key>
- <array>
- <dict>
- <key>comment</key>
- <string>keywords</string>
- <key>match</key>
- <string>\b(?:if)\b</string>
- <key>name</key>
- <string>keyword.control.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>constants</string>
- <key>match</key>
- <string>\b(?:true|false)\b</string>
- <key>name</key>
- <string>constant.language.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>numbers</string>
- <key>match</key>
- <string>\b\d+\.?(?:\d+)?\b</string>
- <key>name</key>
- <string>constant.numeric.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>double quoted string</string>
- <key>match</key>
- <string>\"[^\"]*\"</string>
- <key>name</key>
- <string>string.quoted.double.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>comment</string>
- <key>begin</key>
- <string>#</string>
- <key>end</key>
- <string>$</string>
- <key>name</key>
- <string>comment.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>operators</string>
- <key>match</key>
- <string>(?:=|==|\+=|-=|\+|-)</string>
- <key>name</key>
- <string>keyword.operator.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>targets</string>
- <key>match</key>
- <string>\b(?:action|action_foreach|copy|executable|group|loadable_module|shared_library|source_set|static_library|generated_file)\b</string>
- <key>name</key>
- <string>entity.name.tag.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>functions</string>
- <key>match</key>
- <string>\b(?:assert|config|declare_args|defined|exec_script|foreach|get_label_info|get_path_info|get_target_outputs|getenv|import|print|process_file_template|read_file|rebase_path|set_default_toolchain|set_defaults|set_sources_assignment_filter|template|tool|toolchain|toolchain_args|propagates_configs|write_file)\b</string>
- <key>name</key>
- <string>entity.name.function.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>predefined variables</string>
- <key>match</key>
- <string>\b(?:current_cpu|current_os|current_toolchain|default_toolchain|host_cpu|host_os|python_path|root_build_dir|root_gen_dir|root_out_dir|target_cpu|target_gen_dir|target_os|target_out_dir)\b</string>
- <key>name</key>
- <string>variable.parameter.gn</string>
- </dict>
- <dict>
- <key>comment</key>
- <string>target variables</string>
- <key>match</key>
- <string>\b(?:all_dependent_configs|allow_circular_includes_from|args|asmflags|cflags|cflags_c|cflags_cc|cflags_objc|cflags_objcc|check_includes|complete_static_lib|configs|data|data_deps|defines|depfile|deps|include_dirs|inputs|ldflags|lib_dirs|libs|output_extension|output_name|outputs|public|public_configs|public_deps|script|sources|testonly|visibility|contents|output_conversion|rebase|data_keys|walk_keys)\b</string>
- <key>name</key>
- <string>entity.other.attribute-name.gn</string>
- </dict>
- </array>
- <key>scopeName</key>
- <string>source.gn</string>
- <key>uuid</key>
- <string>DE419F8C-EC46-4824-87F3-732BD08694DC</string>
-</dict>
-</plist>
diff --git a/gn/tools/gn/misc/vim/README.md b/gn/tools/gn/misc/vim/README.md
deleted file mode 100644
index f64e22ad0dc..00000000000
--- a/gn/tools/gn/misc/vim/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# GN vim syntax plugin
-
-## Installation with a plugin manager
-
-You can use modern plugin managers to download the GN repo and manage the vim
-plugin:
-
-Example config for [vim-plug](https://github.com/junegunn/vim-plug):
-
-```
-Plug 'https://gn.googlesource.com/gn', { 'rtp': 'tools/gn/misc/vim' }
-```
-
-Or, for [Vundle](https://github.com/VundleVim/Vundle.vim) users:
-
-```
-Plugin 'https://gn.googlesource.com/gn', { 'rtp': 'tools/gn/misc/vim' }
-```
-
-## Manual installation
-
-If you don't use a plugin manager or would prefer to manage the GN repo
-yourself, you can add this explicitly to `rtp` in your `.vimrc`:
-
-```
-set runtimepath+=/path/to/src/tools/gn/misc/vim
-" ...
-filetype plugin indent on " or a similar command to turn on filetypes in vim
-```
diff --git a/gn/tools/gn/misc/vim/gn-format.py b/gn/tools/gn/misc/vim/gn-format.py
deleted file mode 100644
index 7e5d6a44a0f..00000000000
--- a/gn/tools/gn/misc/vim/gn-format.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# 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.
-#
-# Based on clang-format.py.
-#
-# This file is a minimal gn format vim-integration. To install:
-# - Change 'binary' if gn is not on the path (see below).
-# - Add to your .vimrc:
-#
-# map <F1> :pyf <path-to-this-file>/gn-format.py<CR>
-#
-# gn format currently formats only a complete file so visual ranges, etc. won't
-# be used. It operates on the current, potentially unsaved buffer and does not
-# create or save any files. To revert a formatting, just undo.
-
-import difflib
-import subprocess
-import sys
-import vim
-
-# Change this to the full path if gn is not on the path.
-binary = 'gn'
-
-def main():
- # Get the current text.
- buf = vim.current.buffer
- text = '\n'.join(buf)
-
- is_win = sys.platform.startswith('win32')
- # Avoid flashing an ugly cmd prompt on Windows when invoking gn.
- startupinfo = None
- if is_win:
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- startupinfo.wShowWindow = subprocess.SW_HIDE
-
- # Call formatter. Needs shell=True on Windows due to gn.bat in depot_tools.
- p = subprocess.Popen([binary, 'format', '--stdin'],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- stdin=subprocess.PIPE, startupinfo=startupinfo,
- shell=is_win, universal_newlines=True)
- stdout, stderr = p.communicate(input=text)
- if p.returncode != 0:
- print 'Formatting failed, please report to gn-dev@chromium.org.'
- print stdout, stderr
- else:
- # Otherwise, replace current buffer.
- lines = stdout.split('\n')
- # Last line should have trailing \n, but we don't want to insert a blank
- # line at the end of the buffer, so remove that.
- if lines[-1] == '':
- lines = lines[:-1]
- sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
- for op in reversed(sequence.get_opcodes()):
- if op[0] != 'equal':
- vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
-
-main()
diff --git a/gn/tools/gn/misc/vim/syntax/gn.vim b/gn/tools/gn/misc/vim/syntax/gn.vim
deleted file mode 100644
index 9d5b06043c8..00000000000
--- a/gn/tools/gn/misc/vim/syntax/gn.vim
+++ /dev/null
@@ -1,85 +0,0 @@
-" 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.
-"
-" gn.vim: Vim syntax file for GN.
-"
-" Quit when a (custom) syntax file was already loaded
-"if exists("b:current_syntax")
- "finish
-"endif
-
-syn case match
-
-" Keywords within functions
-syn keyword gnConditional if else
-hi def link gnConditional Conditional
-
-" Predefined variables
-syn keyword gnPredefVar current_cpu current_os current_toolchain
-syn keyword gnPredefVar default_toolchain host_cpu host_os
-syn keyword gnPredefVar root_build_dir root_gen_dir root_out_dir
-syn keyword gnPredefVar target_cpu target_gen_dir target_out_dir
-syn keyword gnPredefVar target_os
-syn keyword gnPredefVar true false
-hi def link gnPredefVar Constant
-
-" Target declarations
-syn keyword gnTarget action action_foreach copy executable group
-syn keyword gnTarget shared_library source_set static_library
-syn keyword gnTarget loadable_module generated_file
-hi def link gnTarget Type
-
-" Buildfile functions
-syn keyword gnFunctions assert config declare_args defined exec_script
-syn keyword gnFunctions foreach get_label_info get_path_info
-syn keyword gnFunctions get_target_outputs getenv import print
-syn keyword gnFunctions process_file_template read_file rebase_path
-syn keyword gnFunctions set_default_toolchain set_defaults
-syn keyword gnFunctions set_sources_assignment_filter template tool
-syn keyword gnFunctions toolchain toolchain_args propagates_configs write_file
-hi def link gnFunctions Macro
-
-" Variables
-syn keyword gnVariable all_dependent_configs allow_circular_includes_from
-syn keyword gnVariable args asmflags cflags cflags_c cflags_cc cflags_objc
-syn keyword gnVariable cflags_objcc check_includes complete_static_lib
-syn keyword gnVariable configs data data_deps defines depfile deps
-syn keyword gnVariable include_dirs inputs ldflags lib_dirs libs
-syn keyword gnVariable output_extension output_name outputs public
-syn keyword gnVariable public_configs public_deps scripte sources testonly
-syn keyword gnVariable visibility contents output_conversion rebase
-syn keyword gnVariable data_keys walk_keys
-hi def link gnVariable Keyword
-
-" Strings
-syn region gnString start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=@Spell,gnTargetName
-syn match gnTargetName '\v:[^"]+' contained
-hi def link gnString String
-hi def link gnTargetName Special
-
-" Comments
-syn keyword gnTodo contained TODO FIXME XXX BUG NOTE
-syn cluster gnCommentGroup contains=gnTodo
-syn region gnComment start="#" end="$" contains=@gnCommentGroup,@Spell
-
-hi def link gnComment Comment
-hi def link gnTodo Todo
-
-" Operators; I think this is a bit too colourful.
-"syn match gnOperator /=/
-"syn match gnOperator /!=/
-"syn match gnOperator />=/
-"syn match gnOperator /<=/
-"syn match gnOperator /==/
-"syn match gnOperator /+=/
-"syn match gnOperator /-=/
-"syn match gnOperator /\s>\s/
-"syn match gnOperator /\s<\s/
-"syn match gnOperator /\s+\s/
-"syn match gnOperator /\s-\s/
-"hi def link gnOperator Operator
-
-syn sync minlines=500
-
-let b:current_syntax = "gn"
diff --git a/gn/tools/gn/ninja_action_target_writer.cc b/gn/tools/gn/ninja_action_target_writer.cc
deleted file mode 100644
index 31f5261a35e..00000000000
--- a/gn/tools/gn/ninja_action_target_writer.cc
+++ /dev/null
@@ -1,239 +0,0 @@
-// 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 "tools/gn/ninja_action_target_writer.h"
-
-#include <stddef.h>
-
-#include "base/strings/string_util.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-
-NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out),
- path_output_no_escaping_(
- target->settings()->build_settings()->build_dir(),
- target->settings()->build_settings()->root_path_utf8(),
- ESCAPE_NONE) {}
-
-NinjaActionTargetWriter::~NinjaActionTargetWriter() = default;
-
-void NinjaActionTargetWriter::Run() {
- std::string custom_rule_name = WriteRuleDefinition();
-
- // Collect our deps to pass as "extra hard dependencies" for input deps. This
- // will force all of the action's dependencies to be completed before the
- // action is run. Usually, if an action has a dependency, it will be
- // operating on the result of that previous step, so we need to be sure to
- // serialize these.
- std::vector<const Target*> extra_hard_deps;
- for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED))
- extra_hard_deps.push_back(pair.ptr);
-
- // For ACTIONs, the input deps appear only once in the generated ninja
- // file, so WriteInputDepsStampAndGetDep() won't create a stamp file
- // and the action will just depend on all the input deps directly.
- size_t num_stamp_uses =
- target_->output_type() == Target::ACTION ? 1u : target_->sources().size();
- std::vector<OutputFile> input_deps =
- WriteInputDepsStampAndGetDep(extra_hard_deps, num_stamp_uses);
- out_ << std::endl;
-
- // Collects all output files for writing below.
- std::vector<OutputFile> output_files;
-
- if (target_->output_type() == Target::ACTION_FOREACH) {
- // Write separate build lines for each input source file.
- WriteSourceRules(custom_rule_name, input_deps, &output_files);
- } else {
- DCHECK(target_->output_type() == Target::ACTION);
-
- // Write a rule that invokes the script once with the outputs as outputs,
- // and the data as inputs. It does not depend on the sources.
- out_ << "build";
- SubstitutionWriter::GetListAsOutputFiles(
- settings_, target_->action_values().outputs(), &output_files);
- path_output_.WriteFiles(out_, output_files);
-
- out_ << ": " << custom_rule_name;
- if (!input_deps.empty()) {
- // As in WriteSourceRules, we want to force this target to rebuild any
- // time any of its dependencies change.
- out_ << " |";
- path_output_.WriteFiles(out_, input_deps);
- }
- out_ << std::endl;
- if (target_->action_values().has_depfile()) {
- out_ << " depfile = ";
- WriteDepfile(SourceFile());
- out_ << std::endl;
- }
- if (target_->action_values().pool().ptr) {
- out_ << " pool = ";
- out_ << target_->action_values().pool().ptr->GetNinjaName(
- settings_->default_toolchain_label());
- out_ << std::endl;
- }
- }
- out_ << std::endl;
-
- // Write the stamp, which also depends on all data deps. These are needed at
- // runtime and should be compiled when the action is, but don't need to be
- // done before we run the action.
- // TODO(thakis): If the action has just a single output, make things depend
- // on that output directly without writing a stamp file.
- std::vector<OutputFile> data_outs;
- for (const auto& dep : target_->data_deps())
- data_outs.push_back(dep.ptr->dependency_output_file());
- WriteStampForTarget(output_files, data_outs);
-}
-
-std::string NinjaActionTargetWriter::WriteRuleDefinition() {
- // Make a unique name for this rule.
- //
- // Use a unique name for the response file when there are multiple build
- // steps so that they don't stomp on each other. When there are no sources,
- // there will be only one invocation so we can use a simple name.
- std::string target_label = target_->label().GetUserVisibleName(true);
- std::string custom_rule_name(target_label);
- base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
- custom_rule_name.append("_rule");
-
- const SubstitutionList& args = target_->action_values().args();
- EscapeOptions args_escape_options;
- args_escape_options.mode = ESCAPE_NINJA_COMMAND;
-
- out_ << "rule " << custom_rule_name << std::endl;
-
- if (target_->action_values().uses_rsp_file()) {
- // Needs a response file. The unique_name part is for action_foreach so
- // each invocation of the rule gets a different response file. This isn't
- // strictly necessary for regular one-shot actions, but it's easier to
- // just always define unique_name.
- std::string rspfile = custom_rule_name;
- if (!target_->sources().empty())
- rspfile += ".$unique_name";
- rspfile += ".rsp";
- out_ << " rspfile = " << rspfile << std::endl;
-
- // Response file contents.
- out_ << " rspfile_content =";
- for (const auto& arg :
- target_->action_values().rsp_file_contents().list()) {
- out_ << " ";
- SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options,
- out_);
- }
- out_ << std::endl;
- }
-
- out_ << " command = ";
- path_output_.WriteFile(out_, settings_->build_settings()->python_path());
- out_ << " ";
- path_output_.WriteFile(out_, target_->action_values().script());
- for (const auto& arg : args.list()) {
- out_ << " ";
- SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
- }
- out_ << std::endl;
- out_ << " description = ACTION " << target_label << std::endl;
- out_ << " restat = 1" << std::endl;
- const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
- if (tool && tool->pool().ptr) {
- out_ << " pool = ";
- out_ << tool->pool().ptr->GetNinjaName(
- settings_->default_toolchain_label());
- out_ << std::endl;
- }
-
- return custom_rule_name;
-}
-
-void NinjaActionTargetWriter::WriteSourceRules(
- const std::string& custom_rule_name,
- const std::vector<OutputFile>& input_deps,
- std::vector<OutputFile>* output_files) {
- EscapeOptions args_escape_options;
- args_escape_options.mode = ESCAPE_NINJA_COMMAND;
- // We're writing the substitution values, these should not be quoted since
- // they will get pasted into the real command line.
- args_escape_options.inhibit_quoting = true;
-
- const Target::FileList& sources = target_->sources();
- for (size_t i = 0; i < sources.size(); i++) {
- out_ << "build";
- WriteOutputFilesForBuildLine(sources[i], output_files);
-
- out_ << ": " << custom_rule_name << " ";
- path_output_.WriteFile(out_, sources[i]);
- if (!input_deps.empty()) {
- // Using "|" for the dependencies forces all implicit dependencies to be
- // fully up to date before running the action, and will re-run this
- // action if any input dependencies change. This is important because
- // this action may consume the outputs of previous steps.
- out_ << " |";
- path_output_.WriteFiles(out_, input_deps);
- }
- out_ << std::endl;
-
- // Response files require a unique name be defined.
- if (target_->action_values().uses_rsp_file())
- out_ << " unique_name = " << i << std::endl;
-
- // The required types is the union of the args and response file. This
- // might theoretically duplicate a definition if the same substitution is
- // used in both the args and the response file. However, this should be
- // very unusual (normally the substitutions will go in one place or the
- // other) and the redundant assignment won't bother Ninja.
- SubstitutionWriter::WriteNinjaVariablesForSource(
- target_, settings_, sources[i],
- target_->action_values().args().required_types(), args_escape_options,
- out_);
- SubstitutionWriter::WriteNinjaVariablesForSource(
- target_, settings_, sources[i],
- target_->action_values().rsp_file_contents().required_types(),
- args_escape_options, out_);
-
- if (target_->action_values().has_depfile()) {
- out_ << " depfile = ";
- WriteDepfile(sources[i]);
- out_ << std::endl;
- }
- if (target_->action_values().pool().ptr) {
- out_ << " pool = ";
- out_ << target_->action_values().pool().ptr->GetNinjaName(
- settings_->default_toolchain_label());
- out_ << std::endl;
- }
- }
-}
-
-void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
- const SourceFile& source,
- std::vector<OutputFile>* output_files) {
- size_t first_output_index = output_files->size();
-
- SubstitutionWriter::ApplyListToSourceAsOutputFile(
- target_, settings_, target_->action_values().outputs(), source,
- output_files);
-
- for (size_t i = first_output_index; i < output_files->size(); i++) {
- out_ << " ";
- path_output_.WriteFile(out_, (*output_files)[i]);
- }
-}
-
-void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
- path_output_.WriteFile(
- out_,
- SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
- target_, settings_, target_->action_values().depfile(), source));
-}
diff --git a/gn/tools/gn/ninja_action_target_writer.h b/gn/tools/gn/ninja_action_target_writer.h
deleted file mode 100644
index 36d7cd96d9f..00000000000
--- a/gn/tools/gn/ninja_action_target_writer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_
-
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "tools/gn/ninja_target_writer.h"
-
-class OutputFile;
-
-// Writes a .ninja file for a action target type.
-class NinjaActionTargetWriter : public NinjaTargetWriter {
- public:
- NinjaActionTargetWriter(const Target* target, std::ostream& out);
- ~NinjaActionTargetWriter() override;
-
- void Run() override;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter,
- WriteOutputFilesForBuildLine);
- FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter,
- WriteOutputFilesForBuildLineWithDepfile);
- FRIEND_TEST_ALL_PREFIXES(NinjaActionTargetWriter, WriteArgsSubstitutions);
-
- // Writes the Ninja rule for invoking the script.
- //
- // Returns the name of the custom rule generated. This will be based on the
- // target name, and will include the string "$unique_name" if there are
- // multiple inputs.
- std::string WriteRuleDefinition();
-
- // Writes the rules for compiling each source, writing all output files
- // to the given vector.
- //
- // input_deps are the dependencies common to all build steps.
- void WriteSourceRules(const std::string& custom_rule_name,
- const std::vector<OutputFile>& input_deps,
- std::vector<OutputFile>* output_files);
-
- // Writes the output files generated by the output template for the given
- // source file. This will start with a space and will not include a newline.
- // Appends the output files to the given vector.
- void WriteOutputFilesForBuildLine(const SourceFile& source,
- std::vector<OutputFile>* output_files);
-
- void WriteDepfile(const SourceFile& source);
-
- // Path output writer that doesn't do any escaping or quoting. It does,
- // however, convert slashes. Used for
- // computing intermediate strings.
- PathOutput path_output_no_escaping_;
-
- DISALLOW_COPY_AND_ASSIGN(NinjaActionTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_ACTION_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_action_target_writer_unittest.cc b/gn/tools/gn/ninja_action_target_writer_unittest.cc
deleted file mode 100644
index 90173b3c0fd..00000000000
--- a/gn/tools/gn/ninja_action_target_writer_unittest.cc
+++ /dev/null
@@ -1,475 +0,0 @@
-// 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 <algorithm>
-#include <sstream>
-
-#include "tools/gn/ninja_action_target_writer.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION_FOREACH);
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/gen/a b{{source_name_part}}.h",
- "//out/Debug/gen/{{source_name_part}}.cc");
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
-
- SourceFile source("//foo/bar.in");
- std::vector<OutputFile> output_files;
- writer.WriteOutputFilesForBuildLine(source, &output_files);
-
- EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out.str());
-}
-
-// Tests an action with no sources.
-TEST(NinjaActionTargetWriter, ActionNoSources) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION);
-
- target.action_values().set_script(SourceFile("//foo/script.py"));
- target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
-
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/foo.out");
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- const char* expected = R"(rule __foo_bar___rule
- command = /usr/bin/python ../../foo/script.py
- description = ACTION //foo:bar()
- restat = 1
-
-build foo.out: __foo_bar___rule | ../../foo/script.py ../../foo/included.txt
-
-build obj/foo/bar.stamp: stamp foo.out
-)";
- EXPECT_EQ(expected, out.str()) << expected << "--" << out.str();
-}
-
-// Tests an action with no sources and pool
-TEST(NinjaActionTargetWriter, ActionNoSourcesConsole) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION);
-
- target.action_values().set_script(SourceFile("//foo/script.py"));
- target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
-
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/foo.out");
-
- Pool pool(setup.settings(),
- Label(SourceDir("//"), "console", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- pool.set_depth(1);
- target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- // The console pool's name must be mapped exactly to the string "console"
- // which is a special pre-defined pool name in ninja.
- const char* expected = R"(rule __foo_bar___rule
- command = /usr/bin/python ../../foo/script.py
- description = ACTION //foo:bar()
- restat = 1
-
-build foo.out: __foo_bar___rule | ../../foo/script.py ../../foo/included.txt
- pool = console
-
-build obj/foo/bar.stamp: stamp foo.out
-)";
- EXPECT_EQ(expected, out.str());
-}
-
-// Makes sure that we write sources as input dependencies for actions with
-// both sources and inputs (ACTION_FOREACH treats the sources differently).
-TEST(NinjaActionTargetWriter, ActionWithSources) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION);
-
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- target.sources().push_back(SourceFile("//foo/source.txt"));
- target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
-
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/foo.out");
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __foo_bar___rule\n"
- " command = /usr/bin/python ../../foo/script.py\n"
- " description = ACTION //foo:bar()\n"
- " restat = 1\n"
- "\n"
- "build foo.out: __foo_bar___rule | ../../foo/script.py "
- "../../foo/included.txt ../../foo/source.txt\n"
- "\n"
- "build obj/foo/bar.stamp: stamp foo.out\n";
- EXPECT_EQ(expected_linux, out.str());
-}
-
-TEST(NinjaActionTargetWriter, ForEach) {
- Err err;
- TestWithScope setup;
-
- // Some dependencies that the action can depend on. Use actions for these
- // so they have a nice platform-independent stamp file that can appear in the
- // output (rather than having to worry about how the current platform names
- // binaries).
- Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
- dep.set_output_type(Target::ACTION);
- dep.visibility().SetPublic();
- dep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
- datadep.set_output_type(Target::ACTION);
- datadep.visibility().SetPublic();
- datadep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(datadep.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION_FOREACH);
- target.private_deps().push_back(LabelTargetPair(&dep));
- target.data_deps().push_back(LabelTargetPair(&datadep));
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.sources().push_back(SourceFile("//foo/input2.txt"));
-
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- target.action_values().args() = SubstitutionList::MakeForTest(
- "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
-
- target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __foo_bar___rule\n"
- " command = /usr/bin/python ../../foo/script.py -i ${in} "
-// Escaping is different between Windows and Posix.
-#if defined(OS_WIN)
- "\"--out=foo$ bar${source_name_part}.o\"\n"
-#else
- "--out=foo\\$ bar${source_name_part}.o\n"
-#endif
- " description = ACTION //foo:bar()\n"
- " restat = 1\n"
- "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
- "../../foo/included.txt obj/foo/dep.stamp\n"
- "\n"
- "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
- "obj/foo/bar.inputdeps.stamp\n"
- " source_name_part = input1\n"
- "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
- "obj/foo/bar.inputdeps.stamp\n"
- " source_name_part = input2\n"
- "\n"
- "build obj/foo/bar.stamp: "
- "stamp input1.out input2.out || obj/foo/datadep.stamp\n";
-
- std::string out_str = out.str();
-#if defined(OS_WIN)
- std::replace(out_str.begin(), out_str.end(), '\\', '/');
-#endif
- EXPECT_EQ(expected_linux, out_str);
-}
-
-TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION_FOREACH);
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.sources().push_back(SourceFile("//foo/input2.txt"));
-
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- SubstitutionPattern depfile;
- ASSERT_TRUE(
- depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", nullptr, &err));
- target.action_values().set_depfile(depfile);
-
- target.action_values().args() = SubstitutionList::MakeForTest(
- "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
-
- target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __foo_bar___rule\n"
- " command = /usr/bin/python ../../foo/script.py -i ${in} "
-#if defined(OS_WIN)
- "\"--out=foo$ bar${source_name_part}.o\"\n"
-#else
- "--out=foo\\$ bar${source_name_part}.o\n"
-#endif
- " description = ACTION //foo:bar()\n"
- " restat = 1\n"
- "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
- "../../foo/included.txt\n"
- "\n"
- "build input1.out: __foo_bar___rule ../../foo/input1.txt"
- " | obj/foo/bar.inputdeps.stamp\n"
- " source_name_part = input1\n"
- " depfile = gen/input1.d\n"
- "build input2.out: __foo_bar___rule ../../foo/input2.txt"
- " | obj/foo/bar.inputdeps.stamp\n"
- " source_name_part = input2\n"
- " depfile = gen/input2.d\n"
- "\n"
- "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
- EXPECT_EQ(expected_linux, out.str());
-}
-
-TEST(NinjaActionTargetWriter, ForEachWithResponseFile) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION_FOREACH);
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Make sure we get interesting substitutions for both the args and the
- // response file contents.
- target.action_values().args() = SubstitutionList::MakeForTest(
- "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
- target.action_values().rsp_file_contents() =
- SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __foo_bar___rule\n"
- // This name is autogenerated from the target rule name.
- " rspfile = __foo_bar___rule.$unique_name.rsp\n"
- // These come from rsp_file_contents above.
- " rspfile_content = -j ${source_name_part}\n"
- // These come from the args.
- " command = /usr/bin/python ../../foo/script.py ${in} "
- "${source_file_part} ${rspfile}\n"
- " description = ACTION //foo:bar()\n"
- " restat = 1\n"
- "\n"
- "build input1.out: __foo_bar___rule ../../foo/input1.txt"
- " | ../../foo/script.py\n"
- // Necessary for the rspfile defined in the rule.
- " unique_name = 0\n"
- // Substitution for the args.
- " source_file_part = input1.txt\n"
- // Substitution for the rspfile contents.
- " source_name_part = input1\n"
- "\n"
- "build obj/foo/bar.stamp: stamp input1.out\n";
- EXPECT_EQ(expected_linux, out.str());
-}
-
-TEST(NinjaActionTargetWriter, ForEachWithPool) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::ACTION_FOREACH);
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.action_values().set_script(SourceFile("//foo/script.py"));
-
- Pool pool(setup.settings(),
- Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- pool.set_depth(5);
- target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Make sure we get interesting substitutions for both the args and the
- // response file contents.
- target.action_values().args() =
- SubstitutionList::MakeForTest("{{source}}", "{{source_file_part}}");
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- std::ostringstream out;
- NinjaActionTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __foo_bar___rule\n"
- // These come from the args.
- " command = /usr/bin/python ../../foo/script.py ${in} "
- "${source_file_part}\n"
- " description = ACTION //foo:bar()\n"
- " restat = 1\n"
- "\n"
- "build input1.out: __foo_bar___rule ../../foo/input1.txt"
- " | ../../foo/script.py\n"
- // Substitution for the args.
- " source_file_part = input1.txt\n"
- " pool = foo_pool\n"
- "\n"
- "build obj/foo/bar.stamp: stamp input1.out\n";
- EXPECT_EQ(expected_linux, out.str());
-}
-
-TEST(NinjaActionTargetWriter, NoTransitiveHardDeps) {
- Err err;
- TestWithScope setup;
-
- setup.build_settings()->set_python_path(
- base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
-
- Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
- dep.set_output_type(Target::ACTION);
- dep.visibility().SetPublic();
- dep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
- foo.set_output_type(Target::ACTION);
- foo.visibility().SetPublic();
- foo.sources().push_back(SourceFile("//foo/input1.txt"));
- foo.action_values().set_script(SourceFile("//foo/script.py"));
- foo.private_deps().push_back(LabelTargetPair(&dep));
- foo.SetToolchain(setup.toolchain());
- foo.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/foo.out");
- ASSERT_TRUE(foo.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaActionTargetWriter writer(&foo, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __foo_foo___rule\n"
- // These come from the args.
- " command = /usr/bin/python ../../foo/script.py\n"
- " description = ACTION //foo:foo()\n"
- " restat = 1\n"
- "\n"
- "build foo.out: __foo_foo___rule | ../../foo/script.py"
- " ../../foo/input1.txt obj/foo/dep.stamp\n"
- "\n"
- "build obj/foo/foo.stamp: stamp foo.out\n";
- EXPECT_EQ(expected_linux, out.str());
- }
-
- Target bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
- bar.set_output_type(Target::ACTION);
- bar.sources().push_back(SourceFile("//bar/input1.txt"));
- bar.action_values().set_script(SourceFile("//bar/script.py"));
- bar.private_deps().push_back(LabelTargetPair(&foo));
- bar.SetToolchain(setup.toolchain());
- bar.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/bar.out");
- ASSERT_TRUE(bar.OnResolved(&err)) << err.message();
-
- {
- std::ostringstream out;
- NinjaActionTargetWriter writer(&bar, out);
- writer.Run();
-
- const char expected_linux[] =
- "rule __bar_bar___rule\n"
- // These come from the args.
- " command = /usr/bin/python ../../bar/script.py\n"
- " description = ACTION //bar:bar()\n"
- " restat = 1\n"
- "\n"
- // Do not have obj/foo/dep.stamp as dependency.
- "build bar.out: __bar_bar___rule | ../../bar/script.py"
- " ../../bar/input1.txt obj/foo/foo.stamp\n"
- "\n"
- "build obj/bar/bar.stamp: stamp bar.out\n";
- EXPECT_EQ(expected_linux, out.str());
- }
-}
diff --git a/gn/tools/gn/ninja_binary_target_writer.cc b/gn/tools/gn/ninja_binary_target_writer.cc
deleted file mode 100644
index 3cf7c48c6cc..00000000000
--- a/gn/tools/gn/ninja_binary_target_writer.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-// 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 "tools/gn/ninja_binary_target_writer.h"
-
-#include <sstream>
-
-#include "base/strings/string_util.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_c_binary_target_writer.h"
-#include "tools/gn/ninja_rust_binary_target_writer.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-
-NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out),
- rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
-
-NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default;
-
-void NinjaBinaryTargetWriter::Run() {
- if (target_->source_types_used().RustSourceUsed()) {
- NinjaRustBinaryTargetWriter writer(target_, out_);
- writer.Run();
- return;
- }
-
- NinjaCBinaryTargetWriter writer(target_, out_);
- writer.Run();
-}
-
-OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const {
- CHECK(target_->toolchain()) << "Toolchain not set on target "
- << target_->label().GetUserVisibleName(true);
-
- UniqueVector<const SourceFile*> inputs;
- for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
- for (const auto& input : iter.cur().inputs()) {
- inputs.push_back(&input);
- }
- }
-
- if (inputs.size() == 0)
- return OutputFile(); // No inputs
-
- // If we only have one input, return it directly instead of writing a stamp
- // file for it.
- if (inputs.size() == 1)
- return OutputFile(settings_->build_settings(), *inputs[0]);
-
- // Make a stamp file.
- OutputFile stamp_file =
- GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
- stamp_file.value().append(target_->label().name());
- stamp_file.value().append(".inputs.stamp");
-
- out_ << "build ";
- path_output_.WriteFile(out_, stamp_file);
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolStamp;
-
- // File inputs.
- for (const auto* input : inputs) {
- out_ << " ";
- path_output_.WriteFile(out_, *input);
- }
-
- out_ << std::endl;
- return stamp_file;
-}
-
-void NinjaBinaryTargetWriter::WriteSourceSetStamp(
- const std::vector<OutputFile>& object_files) {
- // The stamp rule for source sets is generally not used, since targets that
- // depend on this will reference the object files directly. However, writing
- // this rule allows the user to type the name of the target and get a build
- // which can be convenient for development.
- UniqueVector<OutputFile> extra_object_files;
- UniqueVector<const Target*> linkable_deps;
- UniqueVector<const Target*> non_linkable_deps;
- GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
-
- // The classifier should never put extra object files in a source sets: any
- // source sets that we depend on should appear in our non-linkable deps
- // instead.
- DCHECK(extra_object_files.empty());
-
- std::vector<OutputFile> order_only_deps;
- for (auto* dep : non_linkable_deps)
- order_only_deps.push_back(dep->dependency_output_file());
-
- WriteStampForTarget(object_files, order_only_deps);
-}
-
-void NinjaBinaryTargetWriter::GetDeps(
- UniqueVector<OutputFile>* extra_object_files,
- UniqueVector<const Target*>* linkable_deps,
- UniqueVector<const Target*>* non_linkable_deps) const {
- // Normal public/private deps.
- for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
- ClassifyDependency(pair.ptr, extra_object_files, linkable_deps,
- non_linkable_deps);
- }
-
- // Inherited libraries.
- for (auto* inherited_target : target_->inherited_libraries().GetOrdered()) {
- ClassifyDependency(inherited_target, extra_object_files, linkable_deps,
- non_linkable_deps);
- }
-
- // Data deps.
- for (const auto& data_dep_pair : target_->data_deps())
- non_linkable_deps->push_back(data_dep_pair.ptr);
-}
-
-void NinjaBinaryTargetWriter::ClassifyDependency(
- const Target* dep,
- UniqueVector<OutputFile>* extra_object_files,
- UniqueVector<const Target*>* linkable_deps,
- UniqueVector<const Target*>* non_linkable_deps) const {
- // Only the following types of outputs have libraries linked into them:
- // EXECUTABLE
- // SHARED_LIBRARY
- // _complete_ STATIC_LIBRARY
- //
- // Child deps of intermediate static libraries get pushed up the
- // dependency tree until one of these is reached, and source sets
- // don't link at all.
- bool can_link_libs = target_->IsFinal();
-
- if (dep->output_type() == Target::SOURCE_SET ||
- // If a complete static library depends on an incomplete static library,
- // manually link in the object files of the dependent library as if it
- // were a source set. This avoids problems with braindead tools such as
- // ar which don't properly link dependent static libraries.
- (target_->complete_static_lib() &&
- (dep->output_type() == Target::STATIC_LIBRARY &&
- !dep->complete_static_lib()))) {
- // Source sets have their object files linked into final targets
- // (shared libraries, executables, loadable modules, and complete static
- // libraries). Intermediate static libraries and other source sets
- // just forward the dependency, otherwise the files in the source
- // set can easily get linked more than once which will cause
- // multiple definition errors.
- if (can_link_libs)
- AddSourceSetFiles(dep, extra_object_files);
-
- // Add the source set itself as a non-linkable dependency on the current
- // target. This will make sure that anything the source set's stamp file
- // depends on (like data deps) are also built before the current target
- // can be complete. Otherwise, these will be skipped since this target
- // will depend only on the source set's object files.
- non_linkable_deps->push_back(dep);
- } else if (target_->output_type() == Target::RUST_LIBRARY &&
- dep->IsLinkable()) {
- // Rust libraries aren't final, but need to have the link lines of all
- // transitive deps specified.
- linkable_deps->push_back(dep);
- } else if (target_->complete_static_lib() && dep->IsFinal()) {
- non_linkable_deps->push_back(dep);
- } else if (can_link_libs && dep->IsLinkable()) {
- linkable_deps->push_back(dep);
- } else {
- non_linkable_deps->push_back(dep);
- }
-}
-
-void NinjaBinaryTargetWriter::AddSourceSetFiles(
- const Target* source_set,
- UniqueVector<OutputFile>* obj_files) const {
- // Just add all sources to the list.
- for (const auto& source : source_set->sources()) {
- obj_files->push_back(OutputFile(settings_->build_settings(), source));
- }
-}
-
-void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
- const SourceFile& source,
- const std::vector<OutputFile>& extra_deps,
- const std::vector<OutputFile>& order_only_deps,
- const char* tool_name,
- const std::vector<OutputFile>& outputs) {
- out_ << "build";
- path_output_.WriteFiles(out_, outputs);
-
- out_ << ": " << rule_prefix_ << tool_name;
- out_ << " ";
- path_output_.WriteFile(out_, source);
-
- if (!extra_deps.empty()) {
- out_ << " |";
- path_output_.WriteFiles(out_, extra_deps);
- }
-
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
- out_ << std::endl;
-} \ No newline at end of file
diff --git a/gn/tools/gn/ninja_binary_target_writer.h b/gn/tools/gn/ninja_binary_target_writer.h
deleted file mode 100644
index 3274867cad7..00000000000
--- a/gn/tools/gn/ninja_binary_target_writer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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 TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/c_tool.h"
-#include "tools/gn/config_values.h"
-#include "tools/gn/ninja_target_writer.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/unique_vector.h"
-
-struct EscapeOptions;
-
-// Writes a .ninja file for a binary target type (an executable, a shared
-// library, or a static library).
-class NinjaBinaryTargetWriter : public NinjaTargetWriter {
- public:
- NinjaBinaryTargetWriter(const Target* target, std::ostream& out);
- ~NinjaBinaryTargetWriter() override;
-
- void Run() override;
-
- protected:
- // Writes to the output stream a stamp rule for inputs, and
- // returns the file to be appended to source rules that encodes the
- // implicit dependencies for the current target. The returned OutputFile
- // will be empty if there are no inputs.
- OutputFile WriteInputsStampAndGetDep() const;
-
- // Writes the stamp line for a source set. These are not linked.
- void WriteSourceSetStamp(const std::vector<OutputFile>& object_files);
-
- // Gets all target dependencies and classifies them, as well as accumulates
- // object files from source sets we need to link.
- void GetDeps(UniqueVector<OutputFile>* extra_object_files,
- UniqueVector<const Target*>* linkable_deps,
- UniqueVector<const Target*>* non_linkable_deps) const;
-
- // Classifies the dependency as linkable or nonlinkable with the current
- // target, adding it to the appropriate vector. If the dependency is a source
- // set we should link in, the source set's object files will be appended to
- // |extra_object_files|.
- void ClassifyDependency(const Target* dep,
- UniqueVector<OutputFile>* extra_object_files,
- UniqueVector<const Target*>* linkable_deps,
- UniqueVector<const Target*>* non_linkable_deps) const;
-
- OutputFile WriteStampAndGetDep(const UniqueVector<const SourceFile*>& files,
- const std::string& stamp_ext) const;
-
- void WriteCompilerBuildLine(const SourceFile& source,
- const std::vector<OutputFile>& extra_deps,
- const std::vector<OutputFile>& order_only_deps,
- const char* tool_name,
- const std::vector<OutputFile>& outputs);
-
- virtual void AddSourceSetFiles(const Target* source_set,
- UniqueVector<OutputFile>* obj_files) const;
-
- // Cached version of the prefix used for rule types for this toolchain.
- std::string rule_prefix_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NinjaBinaryTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_binary_target_writer_unittest.cc b/gn/tools/gn/ninja_binary_target_writer_unittest.cc
deleted file mode 100644
index 7e92ad40518..00000000000
--- a/gn/tools/gn/ninja_binary_target_writer_unittest.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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 "tools/gn/ninja_binary_target_writer.h"
-
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using NinjaBinaryTargetWriterTest = TestWithScheduler;
-
-TEST_F(NinjaBinaryTargetWriterTest, CSources) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- // Also test object files, which should be just passed through to the
- // dependents to link.
- target.sources().push_back(SourceFile("//foo/input3.o"));
- target.sources().push_back(SourceFile("//foo/input4.obj"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.source_types_used().Set(SourceFile::SOURCE_O);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, NoSourcesSourceSet) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "\n"
- "build obj/foo/bar.stamp: stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, NoSourcesStaticLib) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::STATIC_LIBRARY);
- target.visibility().SetPublic();
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "\n"
- "build obj/foo/libbar.a: alink\n"
- " arflags =\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
diff --git a/gn/tools/gn/ninja_build_writer.cc b/gn/tools/gn/ninja_build_writer.cc
deleted file mode 100644
index 3127c9a31cd..00000000000
--- a/gn/tools/gn/ninja_build_writer.cc
+++ /dev/null
@@ -1,626 +0,0 @@
-// 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 "tools/gn/ninja_build_writer.h"
-
-#include <stddef.h>
-
-#include <fstream>
-#include <map>
-#include <sstream>
-#include <unordered_set>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/err.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file_manager.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-#include "util/build_config.h"
-#include "util/exe_path.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-namespace {
-
-struct Counts {
- Counts() : count(0), last_seen(nullptr) {}
-
- // Number of targets of this type.
- int count;
-
- // The last one we encountered.
- const Target* last_seen;
-};
-
-} // namespace
-
-base::CommandLine GetSelfInvocationCommandLine(
- const BuildSettings* build_settings) {
- const base::FilePath build_path =
- build_settings->build_dir().Resolve(build_settings->root_path());
-
- base::FilePath exe_path = GetExePath();
- if (build_path.IsAbsolute())
- exe_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, exe_path);
-
- base::CommandLine cmdline(exe_path.NormalizePathSeparatorsTo('/'));
-
- // Use "." for the directory to generate. When Ninja runs the command it
- // will have the build directory as the current one. Coding it explicitly
- // will cause everything to get confused if the user renames the directory.
- cmdline.AppendArg("gen");
- cmdline.AppendArg(".");
-
- base::FilePath root_path = build_settings->root_path();
- if (build_path.IsAbsolute())
- root_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, root_path);
-
- cmdline.AppendSwitchPath(std::string("--") + switches::kRoot,
- root_path.NormalizePathSeparatorsTo('/'));
- // Successful automatic invocations shouldn't print output.
- cmdline.AppendSwitch(std::string("-") + switches::kQuiet);
-
- EscapeOptions escape_shell;
- escape_shell.mode = ESCAPE_NINJA_COMMAND;
-#if defined(OS_WIN)
- // The command line code quoting varies by platform. We have one string,
- // possibly with spaces, that we want to quote. The Windows command line
- // quotes again, so we don't want quoting. The Posix one doesn't.
- escape_shell.inhibit_quoting = true;
-#endif
-
- // If both --root and --dotfile are passed, make sure the --dotfile is
- // made relative to the build dir here.
- base::FilePath dotfile_path = build_settings->dotfile_name();
- if (!dotfile_path.empty()) {
- if (build_path.IsAbsolute()) {
- dotfile_path =
- MakeAbsoluteFilePathRelativeIfPossible(build_path, dotfile_path);
- }
- cmdline.AppendSwitchPath(std::string("--") + switches::kDotfile,
- dotfile_path.NormalizePathSeparatorsTo('/'));
- }
-
- const base::CommandLine& our_cmdline =
- *base::CommandLine::ForCurrentProcess();
- const base::CommandLine::SwitchMap& switches = our_cmdline.GetSwitches();
- for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
- i != switches.end(); ++i) {
- // Only write arguments we haven't already written. Always skip "args"
- // since those will have been written to the file and will be used
- // implicitly in the future. Keeping --args would mean changes to the file
- // would be ignored.
- if (i->first != switches::kQuiet && i->first != switches::kRoot &&
- i->first != switches::kDotfile && i->first != switches::kArgs) {
- std::string escaped_value =
- EscapeString(FilePathToUTF8(i->second), escape_shell, nullptr);
- cmdline.AppendSwitchASCII(i->first, escaped_value);
- }
- }
-
- return cmdline;
-}
-
-namespace {
-
-std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
- base::CommandLine cmdline = GetSelfInvocationCommandLine(
- build_settings);
-#if defined(OS_WIN)
- return base::WideToUTF8(cmdline.GetCommandLineString());
-#else
- return cmdline.GetCommandLineString();
-#endif
-}
-
-// Given an output that appears more than once, generates an error message
-// that describes the problem and which targets generate it.
-Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets,
- const OutputFile& bad_output) {
- std::vector<const Target*> matches;
- for (const Target* target : all_targets) {
- for (const auto& output : target->computed_outputs()) {
- if (output == bad_output) {
- matches.push_back(target);
- break;
- }
- }
- }
-
- // There should always be at least two targets generating this file for this
- // function to be called in the first place.
- DCHECK(matches.size() >= 2);
- std::string matches_string;
- for (const Target* target : matches)
- matches_string += " " + target->label().GetUserVisibleName(false) + "\n";
-
- Err result(matches[0]->defined_from(), "Duplicate output file.",
- "Two or more targets generate the same output:\n " +
- bad_output.value() +
- "\n\n"
- "This is can often be fixed by changing one of the target "
- "names, or by \n"
- "setting an output_name on one of them.\n"
- "\nCollisions:\n" +
- matches_string);
- for (size_t i = 1; i < matches.size(); i++)
- result.AppendSubErr(Err(matches[i]->defined_from(), "Collision."));
- return result;
-}
-
-// Given two toolchains with the same name, generates an error message
-// that describes the problem.
-Err GetDuplicateToolchainError(const SourceFile& source_file,
- const Toolchain* previous_toolchain,
- const Toolchain* toolchain) {
- Err result(
- toolchain->defined_from(), "Duplicate toolchain.",
- "Two or more toolchains write to the same directory:\n " +
- source_file.GetDir().value() +
- "\n\n"
- "This can be fixed by making sure that distinct toolchains have\n"
- "distinct names.\n");
- result.AppendSubErr(
- Err(previous_toolchain->defined_from(), "Previous toolchain."));
- return result;
-}
-
-} // namespace
-
-NinjaBuildWriter::NinjaBuildWriter(
- const BuildSettings* build_settings,
- const std::unordered_map<const Settings*, const Toolchain*>&
- used_toolchains,
- const std::vector<const Target*>& all_targets,
- const Toolchain* default_toolchain,
- const std::vector<const Target*>& default_toolchain_targets,
- std::ostream& out,
- std::ostream& dep_out)
- : build_settings_(build_settings),
- used_toolchains_(used_toolchains),
- all_targets_(all_targets),
- default_toolchain_(default_toolchain),
- default_toolchain_targets_(default_toolchain_targets),
- out_(out),
- dep_out_(dep_out),
- path_output_(build_settings->build_dir(),
- build_settings->root_path_utf8(),
- ESCAPE_NINJA) {}
-
-NinjaBuildWriter::~NinjaBuildWriter() = default;
-
-bool NinjaBuildWriter::Run(Err* err) {
- WriteNinjaRules();
- WriteAllPools();
- return WriteSubninjas(err) && WritePhonyAndAllRules(err);
-}
-
-// static
-bool NinjaBuildWriter::RunAndWriteFile(const BuildSettings* build_settings,
- const Builder& builder,
- Err* err) {
- ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja");
-
- std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
- std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
-
- // Find the default toolchain info.
- Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
- const Settings* default_toolchain_settings =
- builder.loader()->GetToolchainSettings(default_toolchain_label);
- const Toolchain* default_toolchain =
- builder.GetToolchain(default_toolchain_label);
-
- // Most targets will be in the default toolchain. Add it at the beginning and
- // skip adding it to the list every time in the loop.
- used_toolchains[default_toolchain_settings] = default_toolchain;
-
- std::vector<const Target*> default_toolchain_targets;
- default_toolchain_targets.reserve(all_targets.size());
- for (const Target* target : all_targets) {
- if (target->settings() == default_toolchain_settings) {
- default_toolchain_targets.push_back(target);
- // The default toolchain will already have been added to the used
- // settings array.
- } else if (used_toolchains.find(target->settings()) ==
- used_toolchains.end()) {
- used_toolchains[target->settings()] =
- builder.GetToolchain(target->settings()->toolchain_label());
- }
- }
-
- std::stringstream file;
- std::stringstream depfile;
- NinjaBuildWriter gen(build_settings, used_toolchains, all_targets,
- default_toolchain, default_toolchain_targets,
- file, depfile);
- if (!gen.Run(err))
- return false;
-
- // Unconditionally write the build.ninja. Ninja's build-out-of-date checking
- // will re-run GN when any build input is newer than build.ninja, so any time
- // the build is updated, build.ninja's timestamp needs to updated also, even
- // if the contents haven't been changed.
- base::FilePath ninja_file_name(build_settings->GetFullPath(
- SourceFile(build_settings->build_dir().value() + "build.ninja")));
- base::CreateDirectory(ninja_file_name.DirName());
- std::string ninja_contents = file.str();
- if (base::WriteFile(ninja_file_name, ninja_contents.data(),
- static_cast<int>(ninja_contents.size())) !=
- static_cast<int>(ninja_contents.size()))
- return false;
-
- // Dep file listing build dependencies.
- base::FilePath dep_file_name(build_settings->GetFullPath(
- SourceFile(build_settings->build_dir().value() + "build.ninja.d")));
- std::string dep_contents = depfile.str();
- if (base::WriteFile(dep_file_name, dep_contents.data(),
- static_cast<int>(dep_contents.size())) !=
- static_cast<int>(dep_contents.size()))
- return false;
-
- return true;
-}
-
-void NinjaBuildWriter::WriteNinjaRules() {
- out_ << "ninja_required_version = 1.7.2\n\n";
- out_ << "rule gn\n";
- out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n";
- out_ << " description = Regenerating ninja files\n\n";
-
- // This rule will regenerate the ninja files when any input file has changed.
- out_ << "build build.ninja: gn\n"
- << " generator = 1\n"
- << " depfile = build.ninja.d\n";
-
- // Input build files. These go in the ".d" file. If we write them as
- // dependencies in the .ninja file itself, ninja will expect the files to
- // exist and will error if they don't. When files are listed in a depfile,
- // missing files are ignored.
- dep_out_ << "build.ninja:";
- std::vector<base::FilePath> input_files;
- g_scheduler->input_file_manager()->GetAllPhysicalInputFileNames(&input_files);
-
- // Other files read by the build.
- std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
-
- // Sort the input files to order them deterministically.
- // Additionally, remove duplicate filepaths that seem to creep in.
- std::set<base::FilePath> fileset(input_files.begin(), input_files.end());
- fileset.insert(other_files.begin(), other_files.end());
-
- const base::FilePath build_path =
- build_settings_->build_dir().Resolve(build_settings_->root_path());
-
- EscapeOptions depfile_escape;
- depfile_escape.mode = ESCAPE_DEPFILE;
- for (const auto& other_file : fileset) {
- const base::FilePath file =
- MakeAbsoluteFilePathRelativeIfPossible(build_path, other_file);
- dep_out_ << " ";
- EscapeStringToStream(dep_out_,
- FilePathToUTF8(file.NormalizePathSeparatorsTo('/')),
- depfile_escape);
- }
-
- out_ << std::endl;
-}
-
-void NinjaBuildWriter::WriteAllPools() {
- // Compute the pools referenced by all tools of all used toolchains.
- std::unordered_set<const Pool*> used_pools;
- for (const auto& pair : used_toolchains_) {
- for (const auto& tool : pair.second->tools()) {
- if (tool.second->pool().ptr)
- used_pools.insert(tool.second->pool().ptr);
- }
- }
-
- for (const Target* target : all_targets_) {
- if (target->output_type() == Target::ACTION) {
- const LabelPtrPair<Pool>& pool = target->action_values().pool();
- if (pool.ptr)
- used_pools.insert(pool.ptr);
- }
- }
-
- // Write pools sorted by their name, to make output deterministic.
- std::vector<const Pool*> sorted_pools(used_pools.begin(), used_pools.end());
- auto pool_name = [this](const Pool* pool) {
- return pool->GetNinjaName(default_toolchain_->label());
- };
- std::sort(sorted_pools.begin(), sorted_pools.end(),
- [&pool_name](const Pool* a, const Pool* b) {
- return pool_name(a) < pool_name(b);
- });
- for (const Pool* pool : sorted_pools) {
- std::string name = pool_name(pool);
- if (name == "console")
- continue;
- out_ << "pool " << name << std::endl
- << " depth = " << pool->depth() << std::endl
- << std::endl;
- }
-}
-
-bool NinjaBuildWriter::WriteSubninjas(Err* err) {
- // Write toolchains sorted by their name, to make output deterministic.
- std::vector<std::pair<const Settings*, const Toolchain*>> sorted_settings(
- used_toolchains_.begin(), used_toolchains_.end());
- std::sort(sorted_settings.begin(), sorted_settings.end(),
- [this](const std::pair<const Settings*, const Toolchain*>& a,
- const std::pair<const Settings*, const Toolchain*>& b) {
- // Always put the default toolchain first.
- if (b.second == default_toolchain_)
- return false;
- if (a.second == default_toolchain_)
- return true;
- return GetNinjaFileForToolchain(a.first) <
- GetNinjaFileForToolchain(b.first);
- });
-
- SourceFile previous_subninja;
- const Toolchain* previous_toolchain = nullptr;
-
- for (const auto& pair : sorted_settings) {
- SourceFile subninja = GetNinjaFileForToolchain(pair.first);
-
- // Since the toolchains are sorted, comparing to the previous subninja is
- // enough to find duplicates.
- if (subninja == previous_subninja) {
- *err =
- GetDuplicateToolchainError(subninja, previous_toolchain, pair.second);
- return false;
- }
-
- out_ << "subninja ";
- path_output_.WriteFile(out_, subninja);
- out_ << std::endl;
- previous_subninja = subninja;
- previous_toolchain = pair.second;
- }
- out_ << std::endl;
- return true;
-}
-
-const char kNinjaRules_Help[] =
- R"(Ninja build rules
-
-The "all" and "default" rules
-
- All generated targets (see "gn help execution") will be added to an implicit
- build rule called "all" so "ninja all" will always compile everything. The
- default rule will be used by Ninja if no specific target is specified (just
- typing "ninja"). If there is a target named "default" in the root build file,
- it will be the default build rule, otherwise the implicit "all" rule will be
- used.
-
-Phony rules
-
- GN generates Ninja "phony" rules for targets in the default toolchain. The
- phony rules can collide with each other and with the names of generated files
- so are generated with the following priority:
-
- 1. Actual files generated by the build always take precedence.
-
- 2. Targets in the toplevel //BUILD.gn file.
-
- 3. Targets in toplevel directories matching the names of the directories.
- So "ninja foo" can be used to compile "//foo:foo". This only applies to
- the first level of directories since usually these are the most
- important (so this won't apply to "//foo/bar:bar").
-
- 4. The short names of executables if there is only one executable with that
- short name. Use "ninja doom_melon" to compile the
- "//tools/fruit:doom_melon" executable.
-
- 5. The short names of all targets if there is only one target with that
- short name.
-
- 6. Full label name with no leading slashes. So you can use
- "ninja tools/fruit:doom_melon" to build "//tools/fruit:doom_melon".
-
- 7. Labels with an implicit name part (when the short names match the
- directory). So you can use "ninja foo/bar" to compile "//foo/bar:bar".
-
- These "phony" rules are provided only for running Ninja since this matches
- people's historical expectations for building. For consistency with the rest
- of the program, GN introspection commands accept explicit labels.
-
- To explicitly compile a target in a non-default toolchain, you must give
- Ninja the exact name of the output file relative to the build directory.
-)";
-
-bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) {
- // Track rules as we generate them so we don't accidentally write a phony
- // rule that collides with something else.
- // GN internally generates an "all" target, so don't duplicate it.
- std::unordered_set<std::string> written_rules;
- written_rules.insert("all");
-
- // Set if we encounter a target named "//:default".
- const Target* default_target = nullptr;
-
- // Targets in the root build file.
- std::vector<const Target*> toplevel_targets;
-
- // Targets with names matching their toplevel directories. For example
- // "//foo:foo". Expect this is the naming scheme for "big components."
- std::vector<const Target*> toplevel_dir_targets;
-
- // Tracks the number of each target with the given short name, as well
- // as the short names of executables (which will be a subset of short_names).
- std::map<std::string, Counts> short_names;
- std::map<std::string, Counts> exes;
-
- // ----------------------------------------------------
- // If you change this algorithm, update the help above!
- // ----------------------------------------------------
-
- for (const Target* target : default_toolchain_targets_) {
- const Label& label = target->label();
- const std::string& short_name = label.name();
-
- if (label.dir() == build_settings_->root_target_label().dir() &&
- short_name == "default")
- default_target = target;
-
- // Count the number of targets with the given short name.
- Counts& short_names_counts = short_names[short_name];
- short_names_counts.count++;
- short_names_counts.last_seen = target;
-
- // Count executables with the given short name.
- if (target->output_type() == Target::EXECUTABLE) {
- Counts& exes_counts = exes[short_name];
- exes_counts.count++;
- exes_counts.last_seen = target;
- }
-
- // Find targets in "important" directories.
- const std::string& dir_string = label.dir().value();
- if (dir_string.size() == 2 && dir_string[0] == '/' &&
- dir_string[1] == '/') {
- toplevel_targets.push_back(target);
- } else if (dir_string.size() == label.name().size() + 3 && // Size matches.
- dir_string[0] == '/' &&
- dir_string[1] == '/' && // "//" at beginning.
- dir_string[dir_string.size() - 1] == '/' && // "/" at end.
- dir_string.compare(2, label.name().size(), label.name()) == 0) {
- toplevel_dir_targets.push_back(target);
- }
-
- // Add the output files from each target to the written rules so that
- // we don't write phony rules that collide with anything generated by the
- // build.
- //
- // If at this point there is a collision (no phony rules have been
- // generated yet), two targets make the same output so throw an error.
- for (const auto& output : target->computed_outputs()) {
- // Need to normalize because many toolchain outputs will be preceeded
- // with "./".
- std::string output_string(output.value());
- NormalizePath(&output_string);
- if (!written_rules.insert(output_string).second) {
- *err = GetDuplicateOutputError(default_toolchain_targets_, output);
- return false;
- }
- }
- }
-
- // First prefer the short names of toplevel targets.
- for (const Target* target : toplevel_targets) {
- if (written_rules.insert(target->label().name()).second)
- WritePhonyRule(target, target->label().name());
- }
-
- // Next prefer short names of toplevel dir targets.
- for (const Target* target : toplevel_dir_targets) {
- if (written_rules.insert(target->label().name()).second)
- WritePhonyRule(target, target->label().name());
- }
-
- // Write out the names labels of executables. Many toolchains will produce
- // executables in the root build directory with no extensions, so the names
- // will already exist and this will be a no-op. But on Windows such programs
- // will have extensions, and executables may override the output directory to
- // go into some other place.
- //
- // Putting this after the "toplevel" rules above also means that you can
- // steal the short name from an executable by outputting the executable to
- // a different directory or using a different output name, and writing a
- // toplevel build rule.
- for (const auto& pair : exes) {
- const Counts& counts = pair.second;
- const std::string& short_name = counts.last_seen->label().name();
- if (counts.count == 1 && written_rules.insert(short_name).second)
- WritePhonyRule(counts.last_seen, short_name);
- }
-
- // Write short names when those names are unique and not already taken.
- for (const auto& pair : short_names) {
- const Counts& counts = pair.second;
- const std::string& short_name = counts.last_seen->label().name();
- if (counts.count == 1 && written_rules.insert(short_name).second)
- WritePhonyRule(counts.last_seen, short_name);
- }
-
- // Write the label variants of the target name.
- for (const Target* target : default_toolchain_targets_) {
- const Label& label = target->label();
-
- // Write the long name "foo/bar:baz" for the target "//foo/bar:baz".
- std::string long_name = label.GetUserVisibleName(false);
- base::TrimString(long_name, "/", &long_name);
- if (written_rules.insert(long_name).second)
- WritePhonyRule(target, long_name);
-
- // Write the directory name with no target name if they match
- // (e.g. "//foo/bar:bar" -> "foo/bar").
- if (FindLastDirComponent(label.dir()) == label.name()) {
- std::string medium_name = DirectoryWithNoLastSlash(label.dir());
- base::TrimString(medium_name, "/", &medium_name);
- // That may have generated a name the same as the short name of the
- // target which we already wrote.
- if (medium_name != label.name() &&
- written_rules.insert(medium_name).second)
- WritePhonyRule(target, medium_name);
- }
- }
-
- // Write the autogenerated "all" rule.
- if (!default_toolchain_targets_.empty()) {
- out_ << "\nbuild all: phony";
-
- EscapeOptions ninja_escape;
- ninja_escape.mode = ESCAPE_NINJA;
- for (const Target* target : default_toolchain_targets_) {
- out_ << " $\n ";
- path_output_.WriteFile(out_, target->dependency_output_file());
- }
- }
- out_ << std::endl;
-
- if (default_target) {
- // Use the short name when available
- if (written_rules.find("default") != written_rules.end()) {
- out_ << "\ndefault default" << std::endl;
- } else {
- out_ << "\ndefault ";
- path_output_.WriteFile(out_, default_target->dependency_output_file());
- out_ << std::endl;
- }
- } else if (!default_toolchain_targets_.empty()) {
- out_ << "\ndefault all" << std::endl;
- }
-
- return true;
-}
-
-void NinjaBuildWriter::WritePhonyRule(const Target* target,
- const std::string& phony_name) {
- EscapeOptions ninja_escape;
- ninja_escape.mode = ESCAPE_NINJA;
-
- // Escape for special chars Ninja will handle.
- std::string escaped = EscapeString(phony_name, ninja_escape, nullptr);
-
- out_ << "build " << escaped << ": phony ";
- path_output_.WriteFile(out_, target->dependency_output_file());
- out_ << std::endl;
-}
diff --git a/gn/tools/gn/ninja_build_writer.h b/gn/tools/gn/ninja_build_writer.h
deleted file mode 100644
index f4351e4bc86..00000000000
--- a/gn/tools/gn/ninja_build_writer.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 TOOLS_GN_NINJA_BUILD_WRITER_H_
-#define TOOLS_GN_NINJA_BUILD_WRITER_H_
-
-#include <iosfwd>
-#include <map>
-#include <unordered_map>
-#include <vector>
-
-#include "base/macros.h"
-#include "tools/gn/path_output.h"
-
-class Builder;
-class BuildSettings;
-class Err;
-class Settings;
-class Target;
-class Toolchain;
-
-namespace base {
-class CommandLine;
-} // base
-
-// Generates the toplevel "build.ninja" file. This references the individual
-// toolchain files and lists all input .gn files as dependencies of the
-// build itself.
-class NinjaBuildWriter {
- public:
- NinjaBuildWriter(const BuildSettings* settings,
- const std::unordered_map<const Settings*, const Toolchain*>&
- used_toolchains,
- const std::vector<const Target*>& all_targets,
- const Toolchain* default_toolchain,
- const std::vector<const Target*>& default_toolchain_targets,
- std::ostream& out,
- std::ostream& dep_out);
- ~NinjaBuildWriter();
-
- // The design of this class is that this static factory function takes the
- // Builder, extracts the relevant information, and passes it to the class
- // constructor. The class itself doesn't depend on the Builder at all which
- // makes testing much easier (tests integrating various functions along with
- // the Builder get very complicated).
- static bool RunAndWriteFile(const BuildSettings* settings,
- const Builder& builder,
- Err* err);
-
- bool Run(Err* err);
-
- private:
- void WriteNinjaRules();
- void WriteAllPools();
- bool WriteSubninjas(Err* err);
- bool WritePhonyAndAllRules(Err* err);
-
- void WritePhonyRule(const Target* target, const std::string& phony_name);
-
- const BuildSettings* build_settings_;
-
- const std::unordered_map<const Settings*, const Toolchain*>& used_toolchains_;
- const std::vector<const Target*>& all_targets_;
- const Toolchain* default_toolchain_;
- const std::vector<const Target*>& default_toolchain_targets_;
-
- std::ostream& out_;
- std::ostream& dep_out_;
- PathOutput path_output_;
-
- DISALLOW_COPY_AND_ASSIGN(NinjaBuildWriter);
-};
-
-extern const char kNinjaRules_Help[];
-
-// Exposed for testing.
-base::CommandLine GetSelfInvocationCommandLine(
- const BuildSettings* build_settings);
-
-#endif // TOOLS_GN_NINJA_BUILD_WRITER_H_
diff --git a/gn/tools/gn/ninja_build_writer_unittest.cc b/gn/tools/gn/ninja_build_writer_unittest.cc
deleted file mode 100644
index 7b5e82a1c18..00000000000
--- a/gn/tools/gn/ninja_build_writer_unittest.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright 2016 The Chromium 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 <sstream>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "tools/gn/ninja_build_writer.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using NinjaBuildWriterTest = TestWithScheduler;
-
-class ScopedDotGNFile {
- public:
- ScopedDotGNFile(const base::FilePath& path)
- : path_(path),
- file_(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE) {
- EXPECT_TRUE(file_.IsValid());
- }
- ~ScopedDotGNFile() {
- file_.Close();
- base::DeleteFile(path_, false);
- }
-
- private:
- base::FilePath path_;
- base::File file_;
-};
-
-TEST_F(NinjaBuildWriterTest, GetSelfInvocationCommandLine) {
- // TestWithScope sets up a config with a build dir of //out/Debug.
- TestWithScope setup;
- base::CommandLine cmd_out(base::CommandLine::NO_PROGRAM);
-
- // Setup sets the default root dir to ".".
- base::FilePath root(FILE_PATH_LITERAL("."));
- base::FilePath root_realpath = base::MakeAbsoluteFilePath(root);
-
- base::FilePath gn(FILE_PATH_LITERAL("testdot.gn"));
-
- // The file must exist on disk for MakeAbsoluteFilePath() to work.
- ScopedDotGNFile dot_gn(gn);
- base::FilePath gn_realpath = base::MakeAbsoluteFilePath(gn);
-
- // Without any parameters the self invocation should pass --root=../..
- // (from //out/Debug to //).
- setup.build_settings()->SetRootPath(root_realpath);
- cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
- EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
- EXPECT_FALSE(cmd_out.HasSwitch(switches::kDotfile));
-
- // If --root is . and --dotfile is foo/.gn, then --dotfile also needs
- // to to become ../../foo/.gn.
- setup.build_settings()->SetRootPath(root_realpath);
- setup.build_settings()->set_dotfile_name(gn_realpath);
- cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
- EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
- EXPECT_EQ("../../testdot.gn",
- cmd_out.GetSwitchValueASCII(switches::kDotfile));
-}
-
-TEST_F(NinjaBuildWriterTest, TwoTargets) {
- TestWithScope setup;
- Err err;
-
- Target target_foo(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target_foo.set_output_type(Target::ACTION);
- target_foo.action_values().set_script(SourceFile("//foo/script.py"));
- target_foo.action_values().outputs() = SubstitutionList::MakeForTest(
- "//out/Debug/out1.out", "//out/Debug/out2.out");
- target_foo.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target_foo.OnResolved(&err));
-
- Target target_bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
- target_bar.set_output_type(Target::ACTION);
- target_bar.action_values().set_script(SourceFile("//bar/script.py"));
- target_bar.action_values().outputs() = SubstitutionList::MakeForTest(
- "//out/Debug/out3.out", "//out/Debug/out4.out");
- target_bar.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target_bar.OnResolved(&err));
-
- // Make a secondary toolchain that references two pools.
- Label other_toolchain_label(SourceDir("//other/"), "toolchain");
- Toolchain other_toolchain(setup.settings(), other_toolchain_label);
- TestWithScope::SetupToolchain(&other_toolchain);
-
- Pool other_regular_pool(
- setup.settings(),
- Label(SourceDir("//other/"), "depth_pool", other_toolchain_label.dir(),
- other_toolchain_label.name()));
- other_regular_pool.set_depth(42);
- other_toolchain.GetTool(CTool::kCToolLink)
- ->set_pool(LabelPtrPair<Pool>(&other_regular_pool));
-
- // Make another target that uses its own pool
-
- Pool another_regular_pool(
- setup.settings(),
- Label(SourceDir("//another/"), "depth_pool", other_toolchain_label.dir(),
- other_toolchain_label.name()));
- another_regular_pool.set_depth(7);
-
- Target target_baz(setup.settings(), Label(SourceDir("//baz/"), "baz"));
- target_baz.set_output_type(Target::ACTION);
- target_baz.action_values().set_script(SourceFile("//baz/script.py"));
- target_baz.action_values().outputs() = SubstitutionList::MakeForTest(
- "//out/Debug/out5.out", "//out/Debug/out6.out");
- target_baz.SetToolchain(&other_toolchain);
- target_baz.action_values().set_pool(
- LabelPtrPair<Pool>(&another_regular_pool));
- ASSERT_TRUE(target_baz.OnResolved(&err));
-
- // The console pool must be in the default toolchain.
- Pool console_pool(setup.settings(), Label(SourceDir("//"), "console",
- setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- console_pool.set_depth(1);
- other_toolchain.GetTool(GeneralTool::kGeneralToolStamp)
- ->set_pool(LabelPtrPair<Pool>(&console_pool));
-
- // Settings to go with the other toolchain.
- Settings other_settings(setup.build_settings(), "toolchain/");
- other_settings.set_toolchain_label(other_toolchain_label);
-
- std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
- used_toolchains[setup.settings()] = setup.toolchain();
- used_toolchains[&other_settings] = &other_toolchain;
-
- std::vector<const Target*> targets = {&target_foo, &target_bar, &target_baz};
-
- std::ostringstream ninja_out;
- std::ostringstream depfile_out;
-
- NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
- setup.toolchain(), targets, ninja_out, depfile_out);
- ASSERT_TRUE(writer.Run(&err));
-
- const char expected_rule_gn[] = "rule gn\n";
- const char expected_build_ninja[] =
- "build build.ninja: gn\n"
- " generator = 1\n"
- " depfile = build.ninja.d\n";
- const char expected_other_pool[] =
- "pool other_toolchain_another_depth_pool\n"
- " depth = 7\n"
- "\n"
- "pool other_toolchain_other_depth_pool\n"
- " depth = 42\n";
- const char expected_toolchain[] = "subninja toolchain.ninja\n";
- const char expected_targets[] =
- "build bar: phony obj/bar/bar.stamp\n"
- "build baz: phony obj/baz/baz.stamp\n"
- "build foo$:bar: phony obj/foo/bar.stamp\n"
- "build bar$:bar: phony obj/bar/bar.stamp\n"
- "build baz$:baz: phony obj/baz/baz.stamp\n";
- const char expected_root_target[] =
- "build all: phony $\n"
- " obj/foo/bar.stamp $\n"
- " obj/bar/bar.stamp $\n"
- " obj/baz/baz.stamp\n";
- const char expected_default[] = "default all\n";
- std::string out_str = ninja_out.str();
-#define EXPECT_SNIPPET(expected) \
- EXPECT_NE(std::string::npos, out_str.find(expected)) \
- << "Expected to find: " << expected << "\n" \
- << "Within: " << out_str
- EXPECT_SNIPPET(expected_rule_gn);
- EXPECT_SNIPPET(expected_build_ninja);
- EXPECT_SNIPPET(expected_other_pool);
- EXPECT_SNIPPET(expected_toolchain);
- EXPECT_SNIPPET(expected_targets);
- EXPECT_SNIPPET(expected_root_target);
- EXPECT_SNIPPET(expected_default);
-#undef EXPECT_SNIPPET
-
- // A pool definition for ninja's built-in console pool must not be written.
- EXPECT_EQ(std::string::npos, out_str.find("pool console"));
-}
-
-TEST_F(NinjaBuildWriterTest, SpaceInDepfile) {
- TestWithScope setup;
- Err err;
-
- // Setup sets the default root dir to ".".
- base::FilePath root(FILE_PATH_LITERAL("."));
- base::FilePath root_realpath = base::MakeAbsoluteFilePath(root);
- setup.build_settings()->SetRootPath(root_realpath);
-
- // Cannot use MakeAbsoluteFilePath for non-existed paths
- base::FilePath dependency =
- root_realpath.Append(FILE_PATH_LITERAL("path with space/BUILD.gn"));
- g_scheduler->AddGenDependency(dependency);
-
- std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
- used_toolchains[setup.settings()] = setup.toolchain();
- std::vector<const Target*> targets;
- std::ostringstream ninja_out;
- std::ostringstream depfile_out;
- NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
- setup.toolchain(), targets, ninja_out, depfile_out);
- ASSERT_TRUE(writer.Run(&err));
-
- EXPECT_EQ(depfile_out.str(),
- "build.ninja: ../../path\\ with\\ space/BUILD.gn");
-}
-
-TEST_F(NinjaBuildWriterTest, DuplicateOutputs) {
- TestWithScope setup;
- Err err;
-
- Target target_foo(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target_foo.set_output_type(Target::ACTION);
- target_foo.action_values().set_script(SourceFile("//foo/script.py"));
- target_foo.action_values().outputs() = SubstitutionList::MakeForTest(
- "//out/Debug/out1.out", "//out/Debug/out2.out");
- target_foo.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target_foo.OnResolved(&err));
-
- Target target_bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
- target_bar.set_output_type(Target::ACTION);
- target_bar.action_values().set_script(SourceFile("//bar/script.py"));
- target_bar.action_values().outputs() = SubstitutionList::MakeForTest(
- "//out/Debug/out3.out", "//out/Debug/out2.out");
- target_bar.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target_bar.OnResolved(&err));
-
- std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
- used_toolchains[setup.settings()] = setup.toolchain();
- std::vector<const Target*> targets = {&target_foo, &target_bar};
- std::ostringstream ninja_out;
- std::ostringstream depfile_out;
- NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
- setup.toolchain(), targets, ninja_out, depfile_out);
- ASSERT_FALSE(writer.Run(&err));
-
- const char expected_help_test[] =
- "Two or more targets generate the same output:\n"
- " out2.out\n"
- "\n"
- "This is can often be fixed by changing one of the target names, or by \n"
- "setting an output_name on one of them.\n"
- "\n"
- "Collisions:\n"
- " //foo:bar\n"
- " //bar:bar\n";
-
- EXPECT_EQ(expected_help_test, err.help_text());
-}
diff --git a/gn/tools/gn/ninja_bundle_data_target_writer.cc b/gn/tools/gn/ninja_bundle_data_target_writer.cc
deleted file mode 100644
index ec3cf74c207..00000000000
--- a/gn/tools/gn/ninja_bundle_data_target_writer.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/ninja_bundle_data_target_writer.h"
-
-#include "tools/gn/output_file.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target.h"
-
-NinjaBundleDataTargetWriter::NinjaBundleDataTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out) {}
-
-NinjaBundleDataTargetWriter::~NinjaBundleDataTargetWriter() = default;
-
-void NinjaBundleDataTargetWriter::Run() {
- std::vector<OutputFile> output_files;
- for (const SourceFile& source_file : target_->sources()) {
- output_files.push_back(
- OutputFile(settings_->build_settings(), source_file));
- }
-
- std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
- std::vector<const Target*>(), /*num_stamp_uses=*/1);
- output_files.insert(output_files.end(), input_deps.begin(), input_deps.end());
-
- std::vector<OutputFile> order_only_deps;
- for (const auto& pair : target_->data_deps())
- order_only_deps.push_back(pair.ptr->dependency_output_file());
-
- WriteStampForTarget(output_files, order_only_deps);
-}
diff --git a/gn/tools/gn/ninja_bundle_data_target_writer.h b/gn/tools/gn/ninja_bundle_data_target_writer.h
deleted file mode 100644
index c097f67b591..00000000000
--- a/gn/tools/gn/ninja_bundle_data_target_writer.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_NINJA_BUNDLE_DATA_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_BUNDLE_DATA_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/ninja_target_writer.h"
-
-// Writes a .ninja file for a bundle_data target type.
-class NinjaBundleDataTargetWriter : public NinjaTargetWriter {
- public:
- NinjaBundleDataTargetWriter(const Target* target, std::ostream& out);
- ~NinjaBundleDataTargetWriter() override;
-
- void Run() override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NinjaBundleDataTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_BUNDLE_DATA_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc b/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc
deleted file mode 100644
index f2ec6c548d6..00000000000
--- a/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/ninja_bundle_data_target_writer.h"
-
-#include <algorithm>
-#include <sstream>
-
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(NinjaBundleDataTargetWriter, Run) {
- Err err;
- TestWithScope setup;
-
- Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
- bundle_data.set_output_type(Target::BUNDLE_DATA);
- bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
- bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/Contents.json"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
- bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data.SetToolchain(setup.toolchain());
- bundle_data.visibility().SetPublic();
- ASSERT_TRUE(bundle_data.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBundleDataTargetWriter writer(&bundle_data, out);
- writer.Run();
-
- const char expected[] =
- "build obj/foo/data.stamp: stamp "
- "../../foo/input1.txt "
- "../../foo/input2.txt "
- "../../foo/Foo.xcassets/Contents.json "
- "../../foo/Foo.xcassets/foo.colorset/Contents.json "
- "../../foo/Foo.xcassets/foo.imageset/Contents.json "
- "../../foo/Foo.xcassets/foo.imageset/FooIcon-29.png "
- "../../foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png "
- "../../foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
diff --git a/gn/tools/gn/ninja_c_binary_target_writer.cc b/gn/tools/gn/ninja_c_binary_target_writer.cc
deleted file mode 100644
index 04698b58b93..00000000000
--- a/gn/tools/gn/ninja_c_binary_target_writer.cc
+++ /dev/null
@@ -1,714 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/ninja_c_binary_target_writer.h"
-
-#include <stddef.h>
-#include <string.h>
-
-#include <cstring>
-#include <set>
-#include <sstream>
-#include <unordered_set>
-
-#include "base/strings/string_util.h"
-#include "tools/gn/c_substitution_type.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-
-namespace {
-
-// Returns the proper escape options for writing compiler and linker flags.
-EscapeOptions GetFlagOptions() {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_COMMAND;
- return opts;
-}
-
-// Returns the language-specific lang recognized by gcc’s -x flag for
-// precompiled header files.
-const char* GetPCHLangForToolType(const char* name) {
- if (name == CTool::kCToolCc)
- return "c-header";
- if (name == CTool::kCToolCxx)
- return "c++-header";
- if (name == CTool::kCToolObjC)
- return "objective-c-header";
- if (name == CTool::kCToolObjCxx)
- return "objective-c++-header";
- NOTREACHED() << "Not a valid PCH tool type: " << name;
- return "";
-}
-
-} // namespace
-
-NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaBinaryTargetWriter(target, out),
- tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {}
-
-NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default;
-
-void NinjaCBinaryTargetWriter::Run() {
- WriteCompilerVars();
-
- OutputFile input_dep = WriteInputsStampAndGetDep();
-
- // The input dependencies will be an order-only dependency. This will cause
- // Ninja to make sure the inputs are up to date before compiling this source,
- // but changes in the inputs deps won't cause the file to be recompiled.
- //
- // This is important to prevent changes in unrelated actions that are
- // upstream of this target from causing everything to be recompiled.
- //
- // Why can we get away with this rather than using implicit deps ("|", which
- // will force rebuilds when the inputs change)? For source code, the
- // computed dependencies of all headers will be computed by the compiler,
- // which will cause source rebuilds if any "real" upstream dependencies
- // change.
- //
- // If a .cc file is generated by an input dependency, Ninja will see the
- // input to the build rule doesn't exist, and that it is an output from a
- // previous step, and build the previous step first. This is a "real"
- // dependency and doesn't need | or || to express.
- //
- // The only case where this rule matters is for the first build where no .d
- // files exist, and Ninja doesn't know what that source file depends on. In
- // this case it's sufficient to ensure that the upstream dependencies are
- // built first. This is exactly what Ninja's order-only dependencies
- // expresses.
- //
- // The order only deps are referenced by each source file compile,
- // but also by PCH compiles. The latter are annoying to count, so omit
- // them here. This means that binary targets with a single source file
- // that also use PCH files won't have a stamp file even though having
- // one would make output ninja file size a bit lower. That's ok, binary
- // targets with a single source are rare.
- size_t num_stamp_uses = target_->sources().size();
- std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
- std::vector<const Target*>(), num_stamp_uses);
-
- // For GCC builds, the .gch files are not object files, but still need to be
- // added as explicit dependencies below. The .gch output files are placed in
- // |pch_other_files|. This is to prevent linking against them.
- std::vector<OutputFile> pch_obj_files;
- std::vector<OutputFile> pch_other_files;
- WritePCHCommands(input_dep, order_only_deps, &pch_obj_files,
- &pch_other_files);
- std::vector<OutputFile>* pch_files =
- !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files;
-
- // Treat all pch output files as explicit dependencies of all
- // compiles that support them. Some notes:
- //
- // - On Windows, the .pch file is the input to the compile, not the
- // precompiled header's corresponding object file that we're using here.
- // But Ninja's depslog doesn't support multiple outputs from the
- // precompiled header compile step (it outputs both the .pch file and a
- // corresponding .obj file). So we consistently list the .obj file and the
- // .pch file we really need comes along with it.
- //
- // - GCC .gch files are not object files, therefore they are not added to the
- // object file list.
- std::vector<OutputFile> obj_files;
- std::vector<SourceFile> other_files;
- WriteSources(*pch_files, input_dep, order_only_deps, &obj_files,
- &other_files);
-
- // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
- obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
- if (!CheckForDuplicateObjectFiles(obj_files))
- return;
-
- if (target_->output_type() == Target::SOURCE_SET) {
- WriteSourceSetStamp(obj_files);
-#ifndef NDEBUG
- // Verify that the function that separately computes a source set's object
- // files match the object files just computed.
- UniqueVector<OutputFile> computed_obj;
- AddSourceSetFiles(target_, &computed_obj);
- DCHECK_EQ(obj_files.size(), computed_obj.size());
- for (const auto& obj : obj_files)
- DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj));
-#endif
- } else {
- WriteLinkerStuff(obj_files, other_files, input_dep);
- }
-}
-
-void NinjaCBinaryTargetWriter::WriteCompilerVars() {
- const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
-
- // Defines.
- if (subst.used.count(&CSubstitutionDefines)) {
- out_ << CSubstitutionDefines.ninja_name << " =";
- RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
- DefineWriter(), out_);
- out_ << std::endl;
- }
-
- // Include directories.
- if (subst.used.count(&CSubstitutionIncludeDirs)) {
- out_ << CSubstitutionIncludeDirs.ninja_name << " =";
- PathOutput include_path_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- RecursiveTargetConfigToStream<SourceDir>(
- target_, &ConfigValues::include_dirs,
- IncludeWriter(include_path_output), out_);
- out_ << std::endl;
- }
-
- bool has_precompiled_headers =
- target_->config_values().has_precompiled_headers();
-
- EscapeOptions opts = GetFlagOptions();
- if (target_->source_types_used().Get(SourceFile::SOURCE_S) ||
- target_->source_types_used().Get(SourceFile::SOURCE_ASM)) {
- WriteOneFlag(target_, &CSubstitutionAsmFlags, false, Tool::kToolNone,
- &ConfigValues::asmflags, opts, path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
- target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
- target_->source_types_used().Get(SourceFile::SOURCE_M) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
- WriteOneFlag(target_, &CSubstitutionCFlags, false, Tool::kToolNone,
- &ConfigValues::cflags, opts, path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_C)) {
- WriteOneFlag(target_, &CSubstitutionCFlagsC, has_precompiled_headers,
- CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output_,
- out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
- WriteOneFlag(target_, &CSubstitutionCFlagsCc, has_precompiled_headers,
- CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
- out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_M)) {
- WriteOneFlag(target_, &CSubstitutionCFlagsObjC, has_precompiled_headers,
- CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
- WriteOneFlag(target_, &CSubstitutionCFlagsObjCc, has_precompiled_headers,
- CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
- path_output_, out_);
- }
-
- WriteSharedVars(subst);
-}
-
-void NinjaCBinaryTargetWriter::WritePCHCommands(
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files) {
- if (!target_->config_values().has_precompiled_headers())
- return;
-
- const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc);
- if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE &&
- target_->source_types_used().Get(SourceFile::SOURCE_C)) {
- WritePCHCommand(&CSubstitutionCFlagsC, CTool::kCToolCc,
- tool_c->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
- const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx);
- if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE &&
- target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
- WritePCHCommand(&CSubstitutionCFlagsCc, CTool::kCToolCxx,
- tool_cxx->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
-
- const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC);
- if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC &&
- target_->source_types_used().Get(SourceFile::SOURCE_M)) {
- WritePCHCommand(&CSubstitutionCFlagsObjC, CTool::kCToolObjC,
- tool_objc->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
-
- const CTool* tool_objcxx =
- target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
- if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC &&
- target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
- WritePCHCommand(&CSubstitutionCFlagsObjCc, CTool::kCToolObjCxx,
- tool_objcxx->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
-}
-
-void NinjaCBinaryTargetWriter::WritePCHCommand(
- const Substitution* flag_type,
- const char* tool_name,
- CTool::PrecompiledHeaderType header_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files) {
- switch (header_type) {
- case CTool::PCH_MSVC:
- WriteWindowsPCHCommand(flag_type, tool_name, input_dep, order_only_deps,
- object_files);
- break;
- case CTool::PCH_GCC:
- WriteGCCPCHCommand(flag_type, tool_name, input_dep, order_only_deps,
- other_files);
- break;
- case CTool::PCH_NONE:
- NOTREACHED() << "Cannot write a PCH command with no PCH header type";
- break;
- }
-}
-
-void NinjaCBinaryTargetWriter::WriteGCCPCHCommand(
- const Substitution* flag_type,
- const char* tool_name,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* gch_files) {
- // Compute the pch output file (it will be language-specific).
- std::vector<OutputFile> outputs;
- GetPCHOutputFiles(target_, tool_name, &outputs);
- if (outputs.empty())
- return;
-
- gch_files->insert(gch_files->end(), outputs.begin(), outputs.end());
-
- std::vector<OutputFile> extra_deps;
- if (!input_dep.value().empty())
- extra_deps.push_back(input_dep);
-
- // Build line to compile the file.
- WriteCompilerBuildLine(target_->config_values().precompiled_source(),
- extra_deps, order_only_deps, tool_name, outputs);
-
- // This build line needs a custom language-specific flags value. Rule-specific
- // variables are just indented underneath the rule line.
- out_ << " " << flag_type->ninja_name << " =";
-
- // Each substitution flag is overwritten in the target rule to replace the
- // implicitly generated -include flag with the -x <header lang> flag required
- // for .gch targets.
- EscapeOptions opts = GetFlagOptions();
- if (tool_name == CTool::kCToolCc) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts,
- out_);
- } else if (tool_name == CTool::kCToolCxx) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc,
- opts, out_);
- } else if (tool_name == CTool::kCToolObjC) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc,
- opts, out_);
- } else if (tool_name == CTool::kCToolObjCxx) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc,
- opts, out_);
- }
-
- // Append the command to specify the language of the .gch file.
- out_ << " -x " << GetPCHLangForToolType(tool_name);
-
- // Write two blank lines to help separate the PCH build lines from the
- // regular source build lines.
- out_ << std::endl << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand(
- const Substitution* flag_type,
- const char* tool_name,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files) {
- // Compute the pch output file (it will be language-specific).
- std::vector<OutputFile> outputs;
- GetPCHOutputFiles(target_, tool_name, &outputs);
- if (outputs.empty())
- return;
-
- object_files->insert(object_files->end(), outputs.begin(), outputs.end());
-
- std::vector<OutputFile> extra_deps;
- if (!input_dep.value().empty())
- extra_deps.push_back(input_dep);
-
- // Build line to compile the file.
- WriteCompilerBuildLine(target_->config_values().precompiled_source(),
- extra_deps, order_only_deps, tool_name, outputs);
-
- // This build line needs a custom language-specific flags value. Rule-specific
- // variables are just indented underneath the rule line.
- out_ << " " << flag_type->ninja_name << " =";
-
- // Append the command to generate the .pch file.
- // This adds the value to the existing flag instead of overwriting it.
- out_ << " ${" << flag_type->ninja_name << "}";
- out_ << " /Yc" << target_->config_values().precompiled_header();
-
- // Write two blank lines to help separate the PCH build lines from the
- // regular source build lines.
- out_ << std::endl << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteSources(
- const std::vector<OutputFile>& pch_deps,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<SourceFile>* other_files) {
- object_files->reserve(object_files->size() + target_->sources().size());
-
- std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
- std::vector<OutputFile> deps;
- for (const auto& source : target_->sources()) {
- // Clear the vector but maintain the max capacity to prevent reallocations.
- deps.resize(0);
- const char* tool_name = Tool::kToolNone;
- if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) {
- if (source.type() == SourceFile::SOURCE_DEF)
- other_files->push_back(source);
- continue; // No output for this source.
- }
-
- if (!input_dep.value().empty())
- deps.push_back(input_dep);
-
- if (tool_name != Tool::kToolNone) {
- // Only include PCH deps that correspond to the tool type, for instance,
- // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
- // for the output of a C tool type.
- //
- // This makes the assumption that pch_deps only contains pch output files
- // with the naming scheme specified in GetWindowsPCHObjectExtension or
- // GetGCCPCHOutputExtension.
- const CTool* tool = target_->toolchain()->GetToolAsC(tool_name);
- if (tool->precompiled_header_type() != CTool::PCH_NONE) {
- for (const auto& dep : pch_deps) {
- const std::string& output_value = dep.value();
- size_t extension_offset = FindExtensionOffset(output_value);
- if (extension_offset == std::string::npos)
- continue;
- std::string output_extension;
- if (tool->precompiled_header_type() == CTool::PCH_MSVC) {
- output_extension = GetWindowsPCHObjectExtension(
- tool_name, output_value.substr(extension_offset - 1));
- } else if (tool->precompiled_header_type() == CTool::PCH_GCC) {
- output_extension = GetGCCPCHOutputExtension(tool_name);
- }
- if (output_value.compare(
- output_value.size() - output_extension.size(),
- output_extension.size(), output_extension) == 0) {
- deps.push_back(dep);
- }
- }
- }
- WriteCompilerBuildLine(source, deps, order_only_deps, tool_name,
- tool_outputs);
- }
-
- // It's theoretically possible for a compiler to produce more than one
- // output, but we'll only link to the first output.
- object_files->push_back(tool_outputs[0]);
- }
- out_ << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteLinkerStuff(
- const std::vector<OutputFile>& object_files,
- const std::vector<SourceFile>& other_files,
- const OutputFile& input_dep) {
- std::vector<OutputFile> output_files;
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- target_, tool_, tool_->outputs(), &output_files);
-
- out_ << "build";
- path_output_.WriteFiles(out_, output_files);
-
- out_ << ": " << rule_prefix_
- << Tool::GetToolTypeForTargetFinalOutput(target_);
-
- UniqueVector<OutputFile> extra_object_files;
- UniqueVector<const Target*> linkable_deps;
- UniqueVector<const Target*> non_linkable_deps;
- GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
-
- // Object files.
- path_output_.WriteFiles(out_, object_files);
- path_output_.WriteFiles(out_, extra_object_files);
-
- // Dependencies.
- std::vector<OutputFile> implicit_deps;
- std::vector<OutputFile> solibs;
- for (const Target* cur : linkable_deps) {
- // All linkable deps should have a link output file.
- DCHECK(!cur->link_output_file().value().empty())
- << "No link output file for "
- << target_->label().GetUserVisibleName(false);
-
- if (cur->dependency_output_file().value() !=
- cur->link_output_file().value()) {
- // This is a shared library with separate link and deps files. Save for
- // later.
- implicit_deps.push_back(cur->dependency_output_file());
- solibs.push_back(cur->link_output_file());
- } else {
- // Normal case, just link to this target.
- out_ << " ";
- path_output_.WriteFile(out_, cur->link_output_file());
- }
- }
-
- const SourceFile* optional_def_file = nullptr;
- if (!other_files.empty()) {
- for (const SourceFile& src_file : other_files) {
- if (src_file.type() == SourceFile::SOURCE_DEF) {
- optional_def_file = &src_file;
- implicit_deps.push_back(
- OutputFile(settings_->build_settings(), src_file));
- break; // Only one def file is allowed.
- }
- }
- }
-
- // Libraries specified by paths.
- const OrderedSet<LibFile>& libs = target_->all_libs();
- for (size_t i = 0; i < libs.size(); i++) {
- if (libs[i].is_source_file()) {
- implicit_deps.push_back(
- OutputFile(settings_->build_settings(), libs[i].source_file()));
- }
- }
-
- // The input dependency is only needed if there are no object files, as the
- // dependency is normally provided transitively by the source files.
- if (!input_dep.value().empty() && object_files.empty())
- implicit_deps.push_back(input_dep);
-
- // Append implicit dependencies collected above.
- if (!implicit_deps.empty()) {
- out_ << " |";
- path_output_.WriteFiles(out_, implicit_deps);
- }
-
- // Append data dependencies as order-only dependencies.
- //
- // This will include data dependencies and input dependencies (like when
- // this target depends on an action). Having the data dependencies in this
- // list ensures that the data is available at runtime when the user builds
- // this target.
- //
- // The action dependencies are not strictly necessary in this case. They
- // should also have been collected via the input deps stamp that each source
- // file has for an order-only dependency, and since this target depends on
- // the sources, there is already an implicit order-only dependency. However,
- // it's extra work to separate these out and there's no disadvantage to
- // listing them again.
- WriteOrderOnlyDependencies(non_linkable_deps);
-
- // End of the link "build" line.
- out_ << std::endl;
-
- // The remaining things go in the inner scope of the link line.
- if (target_->output_type() == Target::EXECUTABLE ||
- target_->output_type() == Target::SHARED_LIBRARY ||
- target_->output_type() == Target::LOADABLE_MODULE) {
- WriteLinkerFlags(optional_def_file);
- WriteLibs();
- } else if (target_->output_type() == Target::STATIC_LIBRARY) {
- out_ << " arflags =";
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags,
- GetFlagOptions(), out_);
- out_ << std::endl;
- }
- WriteOutputSubstitutions();
- WriteSolibs(solibs);
-}
-
-void NinjaCBinaryTargetWriter::WriteLinkerFlags(
- const SourceFile* optional_def_file) {
- out_ << " ldflags =";
-
- // First the ldflags from the target and its config.
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
- GetFlagOptions(), out_);
-
- // Followed by library search paths that have been recursively pushed
- // through the dependency tree.
- const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
- if (!all_lib_dirs.empty()) {
- // Since we're passing these on the command line to the linker and not
- // to Ninja, we need to do shell escaping.
- PathOutput lib_path_output(path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(),
- ESCAPE_NINJA_COMMAND);
- for (size_t i = 0; i < all_lib_dirs.size(); i++) {
- out_ << " " << tool_->lib_dir_switch();
- lib_path_output.WriteDir(out_, all_lib_dirs[i],
- PathOutput::DIR_NO_LAST_SLASH);
- }
- }
-
- if (optional_def_file) {
- out_ << " /DEF:";
- path_output_.WriteFile(out_, *optional_def_file);
- }
-
- out_ << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteLibs() {
- out_ << " libs =";
-
- // Libraries that have been recursively pushed through the dependency tree.
- EscapeOptions lib_escape_opts;
- lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
- const OrderedSet<LibFile> all_libs = target_->all_libs();
- const std::string framework_ending(".framework");
- for (size_t i = 0; i < all_libs.size(); i++) {
- const LibFile& lib_file = all_libs[i];
- const std::string& lib_value = lib_file.value();
- if (lib_file.is_source_file()) {
- out_ << " ";
- path_output_.WriteFile(out_, lib_file.source_file());
- } else if (base::EndsWith(lib_value, framework_ending,
- base::CompareCase::INSENSITIVE_ASCII)) {
- // Special-case libraries ending in ".framework" to support Mac: Add the
- // -framework switch and don't add the extension to the output.
- out_ << " -framework ";
- EscapeStringToStream(
- out_, lib_value.substr(0, lib_value.size() - framework_ending.size()),
- lib_escape_opts);
- } else {
- out_ << " " << tool_->lib_switch();
- EscapeStringToStream(out_, lib_value, lib_escape_opts);
- }
- }
- out_ << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
- out_ << " output_extension = "
- << SubstitutionWriter::GetLinkerSubstitution(
- target_, tool_, &CSubstitutionOutputExtension);
- out_ << std::endl;
- out_ << " output_dir = "
- << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
- &SubstitutionOutputDir);
- out_ << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteSolibs(
- const std::vector<OutputFile>& solibs) {
- if (solibs.empty())
- return;
-
- out_ << " solibs =";
- path_output_.WriteFiles(out_, solibs);
- out_ << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies(
- const UniqueVector<const Target*>& non_linkable_deps) {
- if (!non_linkable_deps.empty()) {
- out_ << " ||";
-
- // Non-linkable targets.
- for (auto* non_linkable_dep : non_linkable_deps) {
- out_ << " ";
- path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
- }
- }
-}
-
-bool NinjaCBinaryTargetWriter::CheckForDuplicateObjectFiles(
- const std::vector<OutputFile>& files) const {
- std::unordered_set<std::string> set;
- for (const auto& file : files) {
- if (!set.insert(file.value()).second) {
- Err err(
- target_->defined_from(), "Duplicate object file",
- "The target " + target_->label().GetUserVisibleName(false) +
- "\ngenerates two object files with the same name:\n " +
- file.value() +
- "\n"
- "\n"
- "It could be you accidentally have a file listed twice in the\n"
- "sources. Or, depending on how your toolchain maps sources to\n"
- "object files, two source files with the same name in different\n"
- "directories could map to the same object file.\n"
- "\n"
- "In the latter case, either rename one of the files or move one "
- "of\n"
- "the sources to a separate source_set to avoid them both being "
- "in\n"
- "the same target.");
- g_scheduler->FailWithError(err);
- return false;
- }
- }
- return true;
-}
-
-// Appends the object files generated by the given source set to the given
-// output vector.
-void NinjaCBinaryTargetWriter::AddSourceSetFiles(
- const Target* source_set,
- UniqueVector<OutputFile>* obj_files) const {
- std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
-
- // Compute object files for all sources. Only link the first output from
- // the tool if there are more than one.
- for (const auto& source : source_set->sources()) {
- const char* tool_name = Tool::kToolNone;
- if (source_set->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
- obj_files->push_back(tool_outputs[0]);
- }
-
- // Add MSVC precompiled header object files. GCC .gch files are not object
- // files so they are omitted.
- if (source_set->config_values().has_precompiled_headers()) {
- if (source_set->source_types_used().Get(SourceFile::SOURCE_C)) {
- const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCc);
- if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, CTool::kCToolCc, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- if (source_set->source_types_used().Get(SourceFile::SOURCE_CPP)) {
- const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCxx);
- if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, CTool::kCToolCxx, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- if (source_set->source_types_used().Get(SourceFile::SOURCE_M)) {
- const CTool* tool =
- source_set->toolchain()->GetToolAsC(CTool::kCToolObjC);
- if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, CTool::kCToolObjC, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- if (source_set->source_types_used().Get(SourceFile::SOURCE_MM)) {
- const CTool* tool =
- source_set->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
- if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, CTool::kCToolObjCxx, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- }
-}
diff --git a/gn/tools/gn/ninja_c_binary_target_writer.h b/gn/tools/gn/ninja_c_binary_target_writer.h
deleted file mode 100644
index 8dacf8eea27..00000000000
--- a/gn/tools/gn/ninja_c_binary_target_writer.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/config_values.h"
-#include "tools/gn/ninja_binary_target_writer.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/unique_vector.h"
-
-struct EscapeOptions;
-
-// Writes a .ninja file for a binary target type (an executable, a shared
-// library, or a static library).
-class NinjaCBinaryTargetWriter : public NinjaBinaryTargetWriter {
- public:
- NinjaCBinaryTargetWriter(const Target* target, std::ostream& out);
- ~NinjaCBinaryTargetWriter() override;
-
- void Run() override;
-
- protected:
- // Adds source_set files to the list of object files.
- void AddSourceSetFiles(const Target* source_set,
- UniqueVector<OutputFile>* obj_files) const override;
-
- private:
- typedef std::set<OutputFile> OutputFileSet;
-
- // Writes all flags for the compiler: includes, defines, cflags, etc.
- void WriteCompilerVars();
-
- // Writes build lines required for precompiled headers. Any generated
- // object files will be appended to the |object_files|. Any generated
- // non-object files (for instance, .gch files from a GCC toolchain, are
- // appended to |other_files|).
- //
- // input_dep is the stamp file collecting the dependencies required before
- // compiling this target. It will be empty if there are no input deps.
- void WritePCHCommands(const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files);
-
- // Writes a .pch compile build line for a language type.
- void WritePCHCommand(const Substitution* flag_type,
- const char* tool_name,
- CTool::PrecompiledHeaderType header_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files);
-
- void WriteGCCPCHCommand(const Substitution* flag_type,
- const char* tool_name,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* gch_files);
-
- void WriteWindowsPCHCommand(const Substitution* flag_type,
- const char* tool_name,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files);
-
- // pch_deps are additional dependencies to run before the rule. They are
- // expected to abide by the naming conventions specified by GetPCHOutputFiles.
- //
- // order_only_dep are the dependencies that must be run before doing any
- // compiles.
- //
- // The files produced by the compiler will be added to two output vectors.
- void WriteSources(const std::vector<OutputFile>& pch_deps,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<SourceFile>* other_files);
-
- void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
- const std::vector<SourceFile>& other_files,
- const OutputFile& input_dep);
- void WriteLinkerFlags(const SourceFile* optional_def_file);
- void WriteLibs();
- void WriteOutputSubstitutions();
- void WriteSolibs(const std::vector<OutputFile>& solibs);
-
- // Writes the implicit dependencies for the link or stamp line. This is
- // the "||" and everything following it on the ninja line.
- //
- // The order-only dependencies are the non-linkable deps passed in as an
- // argument, plus the data file depdencies in the target.
- void WriteOrderOnlyDependencies(
- const UniqueVector<const Target*>& non_linkable_deps);
-
- // Checks for duplicates in the given list of output files. If any duplicates
- // are found, throws an error and return false.
- bool CheckForDuplicateObjectFiles(const std::vector<OutputFile>& files) const;
-
- const CTool* tool_;
-
- DISALLOW_COPY_AND_ASSIGN(NinjaCBinaryTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc b/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc
deleted file mode 100644
index 3281176efe8..00000000000
--- a/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc
+++ /dev/null
@@ -1,1187 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/ninja_c_binary_target_writer.h"
-
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "tools/gn/config.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-using NinjaCBinaryTargetWriterTest = TestWithScheduler;
-
-TEST_F(NinjaCBinaryTargetWriterTest, SourceSet) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- // Also test object files, which should be just passed through to the
- // dependents to link.
- target.sources().push_back(SourceFile("//foo/input3.o"));
- target.sources().push_back(SourceFile("//foo/input4.obj"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.source_types_used().Set(SourceFile::SOURCE_O);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Source set itself.
- {
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // A shared library that depends on the source set.
- Target shlib_target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- shlib_target.set_output_type(Target::SHARED_LIBRARY);
- shlib_target.public_deps().push_back(LabelTargetPair(&target));
- shlib_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(shlib_target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&shlib_target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libshlib\n"
- "\n"
- "\n"
- // Ordering of the obj files here should come out in the order
- // specified, with the target's first, followed by the source set's, in
- // order.
- "build ./libshlib.so: solink obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
- "|| obj/foo/bar.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // A static library that depends on the source set (should not link it).
- Target stlib_target(setup.settings(), Label(SourceDir("//foo/"), "stlib"));
- stlib_target.set_output_type(Target::STATIC_LIBRARY);
- stlib_target.public_deps().push_back(LabelTargetPair(&target));
- stlib_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(stlib_target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&stlib_target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libstlib\n"
- "\n"
- "\n"
- // There are no sources so there are no params to alink. (In practice
- // this will probably fail in the archive tool.)
- "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
- " arflags =\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // Make the static library 'complete', which means it should be linked.
- stlib_target.set_complete_static_lib(true);
- {
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&stlib_target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libstlib\n"
- "\n"
- "\n"
- // Ordering of the obj files here should come out in the order
- // specified, with the target's first, followed by the source set's, in
- // order.
- "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
- "|| obj/foo/bar.stamp\n"
- " arflags =\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, EscapeDefines) {
- TestWithScope setup;
- Err err;
-
- TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
- target.config_values().defines().push_back("BOOL_DEF");
- target.config_values().defines().push_back("INT_DEF=123");
- target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expectedSubstr[] =
-#if defined(OS_WIN)
- "defines = -DBOOL_DEF -DINT_DEF=123 \"-DSTR_DEF=\\\"ABCD-1\\\"\"";
-#else
- "defines = -DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\"ABCD-1\\\"";
-#endif
- std::string out_str = out.str();
- EXPECT_TRUE(out_str.find(out_str) != std::string::npos);
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, StaticLibrary) {
- TestWithScope setup;
- Err err;
-
- TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.config_values().arflags().push_back("--asdf");
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
- "\n"
- "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
- " arflags = --asdf\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibrary) {
- TestWithScope setup;
- Err err;
-
- TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.config_values().arflags().push_back("--asdf");
- target.set_complete_static_lib(true);
-
- TestTarget baz(setup, "//foo:baz", Target::STATIC_LIBRARY);
- baz.sources().push_back(SourceFile("//foo/input2.cc"));
- baz.source_types_used().Set(SourceFile::SOURCE_CPP);
-
- target.public_deps().push_back(LabelTargetPair(&baz));
-
- ASSERT_TRUE(target.OnResolved(&err));
- ASSERT_TRUE(baz.OnResolved(&err));
-
- // A complete static library that depends on an incomplete static library
- // should link in the dependent object files as if the dependent target
- // were a source set.
- {
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
- "\n"
- "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
- "obj/foo/libbaz.input2.o || obj/foo/libbaz.a\n"
- " arflags = --asdf\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // Make the dependent static library complete.
- baz.set_complete_static_lib(true);
-
- // Dependent complete static libraries should not be linked directly.
- {
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
- "\n"
- "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
- "|| obj/foo/libbaz.a\n"
- " arflags = --asdf\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-}
-
-// This tests that output extension and output dir overrides apply, and input
-// dependencies are applied.
-TEST_F(NinjaCBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
- Err err;
- TestWithScope setup;
-
- // An action for our library to depend on.
- Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
- action.set_output_type(Target::ACTION_FOREACH);
- action.visibility().SetPublic();
- action.SetToolchain(setup.toolchain());
- ASSERT_TRUE(action.OnResolved(&err));
-
- // A shared library w/ the output_extension set to a custom value.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.set_output_extension(std::string("so.6"));
- target.set_output_dir(SourceDir("//out/Debug/foo/"));
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.public_deps().push_back(LabelTargetPair(&action));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libshlib\n"
- "\n"
- "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
- " || obj/foo/action.stamp\n"
- "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
- " || obj/foo/action.stamp\n"
- "\n"
- "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
- // The order-only dependency here is stricly unnecessary since the
- // sources list this as an order-only dep. See discussion in the code
- // that writes this.
- "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so.6\n"
- " output_dir = foo\n";
-
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
- Err err;
- TestWithScope setup;
-
- SourceFile generated_file("//out/Debug/generated.cc");
-
- // An action does code generation.
- Target action(setup.settings(), Label(SourceDir("//foo/"), "generate"));
- action.set_output_type(Target::ACTION);
- action.visibility().SetPublic();
- action.SetToolchain(setup.toolchain());
- action.set_output_dir(SourceDir("//out/Debug/foo/"));
- action.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/generated.cc");
- ASSERT_TRUE(action.OnResolved(&err));
-
- // A source set compiling geneated code, this target does not publicize any
- // headers.
- Target gen_obj(setup.settings(), Label(SourceDir("//foo/"), "gen_obj"));
- gen_obj.set_output_type(Target::SOURCE_SET);
- gen_obj.set_output_dir(SourceDir("//out/Debug/foo/"));
- gen_obj.sources().push_back(generated_file);
- gen_obj.source_types_used().Set(SourceFile::SOURCE_CPP);
- gen_obj.visibility().SetPublic();
- gen_obj.private_deps().push_back(LabelTargetPair(&action));
- gen_obj.set_all_headers_public(false);
- gen_obj.SetToolchain(setup.toolchain());
- ASSERT_TRUE(gen_obj.OnResolved(&err));
-
- std::ostringstream obj_out;
- NinjaCBinaryTargetWriter obj_writer(&gen_obj, obj_out);
- obj_writer.Run();
-
- const char obj_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = gen_obj\n"
- "\n"
- "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
- " || obj/foo/generate.stamp\n"
- "\n"
- "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/gen_obj.generated.o"
- // The order-only dependency here is strictly unnecessary since the
- // sources list this as an order-only dep.
- " || obj/foo/generate.stamp\n";
-
- std::string obj_str = obj_out.str();
- EXPECT_EQ(obj_expected, obj_str);
-
- // A shared library depends on gen_obj, having corresponding header for
- // generated obj.
- Target gen_lib(setup.settings(), Label(SourceDir("//foo/"), "gen_lib"));
- gen_lib.set_output_type(Target::SHARED_LIBRARY);
- gen_lib.set_output_dir(SourceDir("//out/Debug/foo/"));
- gen_lib.sources().push_back(SourceFile("//foor/generated.h"));
- gen_lib.source_types_used().Set(SourceFile::SOURCE_H);
- gen_lib.visibility().SetPublic();
- gen_lib.private_deps().push_back(LabelTargetPair(&gen_obj));
- gen_lib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(gen_lib.OnResolved(&err));
-
- std::ostringstream lib_out;
- NinjaCBinaryTargetWriter lib_writer(&gen_lib, lib_out);
- lib_writer.Run();
-
- const char lib_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libgen_lib\n"
- "\n"
- "\n"
- "build ./libgen_lib.so: solink obj/out/Debug/gen_obj.generated.o"
- // The order-only dependency here is strictly unnecessary since
- // obj/out/Debug/gen_obj.generated.o has dependency to
- // obj/foo/gen_obj.stamp
- " || obj/foo/gen_obj.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = foo\n";
-
- std::string lib_str = lib_out.str();
- EXPECT_EQ(lib_expected, lib_str);
-
- // An executable depends on gen_lib.
- Target executable(setup.settings(),
- Label(SourceDir("//foo/"), "final_target"));
- executable.set_output_type(Target::EXECUTABLE);
- executable.set_output_dir(SourceDir("//out/Debug/foo/"));
- executable.sources().push_back(SourceFile("//foo/main.cc"));
- executable.source_types_used().Set(SourceFile::SOURCE_CPP);
- executable.private_deps().push_back(LabelTargetPair(&gen_lib));
- executable.SetToolchain(setup.toolchain());
- ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
-
- std::ostringstream final_out;
- NinjaCBinaryTargetWriter final_writer(&executable, final_out);
- final_writer.Run();
-
- // There is no order only dependency to action target.
- const char final_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = final_target\n"
- "\n"
- "build obj/foo/final_target.main.o: cxx ../../foo/main.cc\n"
- "\n"
- "build ./final_target: link obj/foo/final_target.main.o"
- " ./libgen_lib.so\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = foo\n";
-
- std::string final_str = final_out.str();
- EXPECT_EQ(final_expected, final_str);
-}
-
-// Tests libs are applied.
-TEST_F(NinjaCBinaryTargetWriterTest, LibsAndLibDirs) {
- Err err;
- TestWithScope setup;
-
- // A shared library w/ libs and lib_dirs.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.config_values().libs().push_back(LibFile(SourceFile("//foo/lib1.a")));
- target.config_values().libs().push_back(LibFile("foo"));
- target.config_values().lib_dirs().push_back(SourceDir("//foo/bar/"));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libshlib\n"
- "\n"
- "\n"
- "build ./libshlib.so: solink | ../../foo/lib1.a\n"
- " ldflags = -L../../foo/bar\n"
- " libs = ../../foo/lib1.a -lfoo\n"
- " output_extension = .so\n"
- " output_dir = \n";
-
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, EmptyOutputExtension) {
- Err err;
- TestWithScope setup;
-
- // This test is the same as OutputExtensionAndInputDeps, except that we call
- // set_output_extension("") and ensure that we get an empty one and override
- // the output prefix so that the name matches the target exactly.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.set_output_prefix_override(true);
- target.set_output_extension(std::string());
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = shlib\n"
- "\n"
- "build obj/foo/shlib.input1.o: cxx ../../foo/input1.cc\n"
- "build obj/foo/shlib.input2.o: cxx ../../foo/input2.cc\n"
- "\n"
- "build ./shlib: solink obj/foo/shlib.input1.o "
- "obj/foo/shlib.input2.o\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = \n";
-
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, SourceSetDataDeps) {
- Err err;
- TestWithScope setup;
-
- // This target is a data (runtime) dependency of the intermediate target.
- Target data(setup.settings(), Label(SourceDir("//foo/"), "data_target"));
- data.set_output_type(Target::EXECUTABLE);
- data.visibility().SetPublic();
- data.SetToolchain(setup.toolchain());
- ASSERT_TRUE(data.OnResolved(&err));
-
- // Intermediate source set target.
- Target inter(setup.settings(), Label(SourceDir("//foo/"), "inter"));
- inter.set_output_type(Target::SOURCE_SET);
- inter.visibility().SetPublic();
- inter.data_deps().push_back(LabelTargetPair(&data));
- inter.SetToolchain(setup.toolchain());
- inter.sources().push_back(SourceFile("//foo/inter.cc"));
- inter.source_types_used().Set(SourceFile::SOURCE_CPP);
- ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
-
- // Write out the intermediate target.
- std::ostringstream inter_out;
- NinjaCBinaryTargetWriter inter_writer(&inter, inter_out);
- inter_writer.Run();
-
- // The intermediate source set will be a stamp file that depends on the
- // object files, and will have an order-only dependency on its data dep and
- // data file.
- const char inter_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = inter\n"
- "\n"
- "build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
- "\n"
- "build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
- "./data_target\n";
- EXPECT_EQ(inter_expected, inter_out.str());
-
- // Final target.
- Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
- exe.set_output_type(Target::EXECUTABLE);
- exe.public_deps().push_back(LabelTargetPair(&inter));
- exe.SetToolchain(setup.toolchain());
- exe.sources().push_back(SourceFile("//foo/final.cc"));
- exe.source_types_used().Set(SourceFile::SOURCE_CPP);
- ASSERT_TRUE(exe.OnResolved(&err));
-
- std::ostringstream final_out;
- NinjaCBinaryTargetWriter final_writer(&exe, final_out);
- final_writer.Run();
-
- // The final output depends on both object files (one from the final target,
- // one from the source set) and has an order-only dependency on the source
- // set's stamp file and the final target's data file. The source set stamp
- // dependency will create an implicit order-only dependency on the data
- // target.
- const char final_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = exe\n"
- "\n"
- "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
- "\n"
- "build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
- "obj/foo/inter.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = \n";
- EXPECT_EQ(final_expected, final_out.str());
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, SharedLibraryModuleDefinitionFile) {
- Err err;
- TestWithScope setup;
-
- Target shared_lib(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- shared_lib.set_output_type(Target::SHARED_LIBRARY);
- shared_lib.SetToolchain(setup.toolchain());
- shared_lib.sources().push_back(SourceFile("//foo/sources.cc"));
- shared_lib.sources().push_back(SourceFile("//foo/bar.def"));
- shared_lib.source_types_used().Set(SourceFile::SOURCE_CPP);
- shared_lib.source_types_used().Set(SourceFile::SOURCE_DEF);
- ASSERT_TRUE(shared_lib.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&shared_lib, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
- "\n"
- "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
- " ldflags = /DEF:../../foo/bar.def\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
- EXPECT_EQ(expected, out.str());
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, LoadableModule) {
- Err err;
- TestWithScope setup;
-
- Target loadable_module(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- loadable_module.set_output_type(Target::LOADABLE_MODULE);
- loadable_module.visibility().SetPublic();
- loadable_module.SetToolchain(setup.toolchain());
- loadable_module.sources().push_back(SourceFile("//foo/sources.cc"));
- loadable_module.source_types_used().Set(SourceFile::SOURCE_CPP);
- ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&loadable_module, out);
- writer.Run();
-
- const char loadable_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
- "\n"
- "build ./libbar.so: solink_module obj/foo/libbar.sources.o\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
- EXPECT_EQ(loadable_expected, out.str());
-
- // Final target.
- Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
- exe.set_output_type(Target::EXECUTABLE);
- exe.public_deps().push_back(LabelTargetPair(&loadable_module));
- exe.SetToolchain(setup.toolchain());
- exe.sources().push_back(SourceFile("//foo/final.cc"));
- exe.source_types_used().Set(SourceFile::SOURCE_CPP);
- ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
-
- std::ostringstream final_out;
- NinjaCBinaryTargetWriter final_writer(&exe, final_out);
- final_writer.Run();
-
- // The final output depends on the loadable module so should have an
- // order-only dependency on the loadable modules's output file.
- const char final_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = exe\n"
- "\n"
- "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
- "\n"
- "build ./exe: link obj/foo/exe.final.o || ./libbar.so\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = \n";
- EXPECT_EQ(final_expected, final_out.str());
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, WinPrecompiledHeaders) {
- Err err;
-
- // This setup's toolchain does not have precompiled headers defined.
- TestWithScope setup;
-
- // A precompiled header toolchain.
- Settings pch_settings(setup.build_settings(), "withpch/");
- Toolchain pch_toolchain(&pch_settings,
- Label(SourceDir("//toolchain/"), "withpch"));
- pch_settings.set_toolchain_label(pch_toolchain.label());
- pch_settings.set_default_toolchain_label(setup.toolchain()->label());
-
- // Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
- CTool* cxx_tool = cxx->AsC();
- TestWithScope::SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool);
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
- pch_toolchain.SetTool(std::move(cxx));
-
- // Add a C compiler as well.
- std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
- CTool* cc_tool = cc->AsC();
- TestWithScope::SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool);
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
- pch_toolchain.SetTool(std::move(cc));
- pch_toolchain.ToolchainSetupComplete();
-
- // This target doesn't specify precompiled headers.
- {
- Target no_pch_target(&pch_settings,
- Label(SourceDir("//foo/"), "no_pch_target"));
- no_pch_target.set_output_type(Target::SOURCE_SET);
- no_pch_target.visibility().SetPublic();
- no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
- no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
- no_pch_target.config_values().cflags_c().push_back("-std=c99");
- no_pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(no_pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&no_pch_target, out);
- writer.Run();
-
- const char no_pch_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_c = -std=c99\n"
- "cflags_cc =\n"
- "target_output_name = no_pch_target\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.input1.o: "
- "withpch_cxx ../../foo/input1.cc\n"
- "build withpch/obj/foo/no_pch_target.input2.o: "
- "withpch_cc ../../foo/input2.c\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.stamp: "
- "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
- "withpch/obj/foo/no_pch_target.input2.o\n";
- EXPECT_EQ(no_pch_expected, out.str());
- }
-
- // This target specifies PCH.
- {
- Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
- pch_target.config_values().set_precompiled_header("build/precompile.h");
- pch_target.config_values().set_precompiled_source(
- SourceFile("//build/precompile.cc"));
- pch_target.set_output_type(Target::SOURCE_SET);
- pch_target.visibility().SetPublic();
- pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
- pch_target.source_types_used().Set(SourceFile::SOURCE_C);
- pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&pch_target, out);
- writer.Run();
-
- const char pch_win_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- // It should output language-specific pch files.
- "cflags_c = /Fpwithpch/obj/foo/pch_target_c.pch "
- "/Yubuild/precompile.h\n"
- "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
- "/Yubuild/precompile.h\n"
- "target_output_name = pch_target\n"
- "\n"
- // Compile the precompiled source files with /Yc.
- "build withpch/obj/build/pch_target.precompile.c.o: "
- "withpch_cc ../../build/precompile.cc\n"
- " cflags_c = ${cflags_c} /Ycbuild/precompile.h\n"
- "\n"
- "build withpch/obj/build/pch_target.precompile.cc.o: "
- "withpch_cxx ../../build/precompile.cc\n"
- " cflags_cc = ${cflags_cc} /Ycbuild/precompile.h\n"
- "\n"
- "build withpch/obj/foo/pch_target.input1.o: "
- "withpch_cxx ../../foo/input1.cc | "
- // Explicit dependency on the PCH build step.
- "withpch/obj/build/pch_target.precompile.cc.o\n"
- "build withpch/obj/foo/pch_target.input2.o: "
- "withpch_cc ../../foo/input2.c | "
- // Explicit dependency on the PCH build step.
- "withpch/obj/build/pch_target.precompile.c.o\n"
- "\n"
- "build withpch/obj/foo/pch_target.stamp: withpch_stamp "
- "withpch/obj/foo/pch_target.input1.o "
- "withpch/obj/foo/pch_target.input2.o "
- // The precompiled object files were added to the outputs.
- "withpch/obj/build/pch_target.precompile.c.o "
- "withpch/obj/build/pch_target.precompile.cc.o\n";
- EXPECT_EQ(pch_win_expected, out.str());
- }
-}
-
-TEST_F(NinjaCBinaryTargetWriterTest, GCCPrecompiledHeaders) {
- Err err;
-
- // This setup's toolchain does not have precompiled headers defined.
- TestWithScope setup;
-
- // A precompiled header toolchain.
- Settings pch_settings(setup.build_settings(), "withpch/");
- Toolchain pch_toolchain(&pch_settings,
- Label(SourceDir("//toolchain/"), "withpch"));
- pch_settings.set_toolchain_label(pch_toolchain.label());
- pch_settings.set_default_toolchain_label(setup.toolchain()->label());
-
- // Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
- CTool* cxx_tool = cxx->AsC();
- TestWithScope::SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool);
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
- pch_toolchain.SetTool(std::move(cxx));
- pch_toolchain.ToolchainSetupComplete();
-
- // Add a C compiler as well.
- std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
- CTool* cc_tool = cc->AsC();
- TestWithScope::SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool);
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
- pch_toolchain.SetTool(std::move(cc));
- pch_toolchain.ToolchainSetupComplete();
-
- // This target doesn't specify precompiled headers.
- {
- Target no_pch_target(&pch_settings,
- Label(SourceDir("//foo/"), "no_pch_target"));
- no_pch_target.set_output_type(Target::SOURCE_SET);
- no_pch_target.visibility().SetPublic();
- no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
- no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
- no_pch_target.config_values().cflags_c().push_back("-std=c99");
- no_pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(no_pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&no_pch_target, out);
- writer.Run();
-
- const char no_pch_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_c = -std=c99\n"
- "cflags_cc =\n"
- "target_output_name = no_pch_target\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.input1.o: "
- "withpch_cxx ../../foo/input1.cc\n"
- "build withpch/obj/foo/no_pch_target.input2.o: "
- "withpch_cc ../../foo/input2.c\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.stamp: "
- "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
- "withpch/obj/foo/no_pch_target.input2.o\n";
- EXPECT_EQ(no_pch_expected, out.str());
- }
-
- // This target specifies PCH.
- {
- Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
- pch_target.config_values().set_precompiled_source(
- SourceFile("//build/precompile.h"));
- pch_target.config_values().cflags_c().push_back("-std=c99");
- pch_target.set_output_type(Target::SOURCE_SET);
- pch_target.visibility().SetPublic();
- pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
- pch_target.source_types_used().Set(SourceFile::SOURCE_C);
- pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&pch_target, out);
- writer.Run();
-
- const char pch_gcc_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_c = -std=c99 "
- "-include withpch/obj/build/pch_target.precompile.h-c\n"
- "cflags_cc = -include withpch/obj/build/pch_target.precompile.h-cc\n"
- "target_output_name = pch_target\n"
- "\n"
- // Compile the precompiled sources with -x <lang>.
- "build withpch/obj/build/pch_target.precompile.h-c.gch: "
- "withpch_cc ../../build/precompile.h\n"
- " cflags_c = -std=c99 -x c-header\n"
- "\n"
- "build withpch/obj/build/pch_target.precompile.h-cc.gch: "
- "withpch_cxx ../../build/precompile.h\n"
- " cflags_cc = -x c++-header\n"
- "\n"
- "build withpch/obj/foo/pch_target.input1.o: "
- "withpch_cxx ../../foo/input1.cc | "
- // Explicit dependency on the PCH build step.
- "withpch/obj/build/pch_target.precompile.h-cc.gch\n"
- "build withpch/obj/foo/pch_target.input2.o: "
- "withpch_cc ../../foo/input2.c | "
- // Explicit dependency on the PCH build step.
- "withpch/obj/build/pch_target.precompile.h-c.gch\n"
- "\n"
- "build withpch/obj/foo/pch_target.stamp: "
- "withpch_stamp withpch/obj/foo/pch_target.input1.o "
- "withpch/obj/foo/pch_target.input2.o\n";
- EXPECT_EQ(pch_gcc_expected, out.str());
- }
-}
-
-// Should throw an error with the scheduler if a duplicate object file exists.
-// This is dependent on the toolchain's object file mapping.
-TEST_F(NinjaCBinaryTargetWriterTest, DupeObjFileError) {
- TestWithScope setup;
- TestTarget target(setup, "//foo:bar", Target::EXECUTABLE);
- target.sources().push_back(SourceFile("//a.cc"));
- target.sources().push_back(SourceFile("//a.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
-
- EXPECT_FALSE(scheduler().is_failed());
-
- scheduler().SuppressOutputForTesting(true);
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- scheduler().SuppressOutputForTesting(false);
-
- // Should have issued an error.
- EXPECT_TRUE(scheduler().is_failed());
-}
-
-// This tests that output extension and output dir overrides apply, and input
-// dependencies are applied.
-TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
- Err err;
- TestWithScope setup;
-
- // This target has one input.
- {
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
- " | ../../foo/input.data\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
- " | ../../foo/input.data\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o\n";
-
- EXPECT_EQ(expected, out.str());
- }
-
- // This target has one input but no source files.
- {
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.visibility().SetPublic();
- target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "\n"
- "build ./libbar.so: solink | ../../foo/input.data\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
-
- EXPECT_EQ(expected, out.str());
- }
-
- // This target has multiple inputs.
- {
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
- target.config_values().inputs().push_back(SourceFile("//foo/input2.data"));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.inputs.stamp: stamp"
- " ../../foo/input1.data ../../foo/input2.data\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o\n";
-
- EXPECT_EQ(expected, out.str());
- }
-
- // This target has one input itself, one from an immediate config, and one
- // from a config tacked on to said config.
- {
- Config far_config(setup.settings(), Label(SourceDir("//foo/"), "qux"));
- far_config.own_values().inputs().push_back(SourceFile("//foo/input3.data"));
- ASSERT_TRUE(far_config.OnResolved(&err));
-
- Config config(setup.settings(), Label(SourceDir("//foo/"), "baz"));
- config.own_values().inputs().push_back(SourceFile("//foo/input2.data"));
- config.configs().push_back(LabelConfigPair(&far_config));
- ASSERT_TRUE(config.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
- target.configs().push_back(LabelConfigPair(&config));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.inputs.stamp: stamp"
- " ../../foo/input1.data ../../foo/input2.data ../../foo/input3.data\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o\n";
-
- EXPECT_EQ(expected, out.str());
- }
-}
diff --git a/gn/tools/gn/ninja_copy_target_writer.cc b/gn/tools/gn/ninja_copy_target_writer.cc
deleted file mode 100644
index ee5cf5273dc..00000000000
--- a/gn/tools/gn/ninja_copy_target_writer.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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 "tools/gn/ninja_copy_target_writer.h"
-
-#include "base/strings/string_util.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/toolchain.h"
-
-NinjaCopyTargetWriter::NinjaCopyTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out) {}
-
-NinjaCopyTargetWriter::~NinjaCopyTargetWriter() = default;
-
-void NinjaCopyTargetWriter::Run() {
- const Tool* copy_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolCopy);
- if (!copy_tool) {
- g_scheduler->FailWithError(Err(
- nullptr, "Copy tool not defined",
- "The toolchain " +
- target_->toolchain()->label().GetUserVisibleName(false) +
- "\n used by target " + target_->label().GetUserVisibleName(false) +
- "\n doesn't define a \"copy\" tool."));
- return;
- }
-
- const Tool* stamp_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolStamp);
- if (!stamp_tool) {
- g_scheduler->FailWithError(Err(
- nullptr, "Copy tool not defined",
- "The toolchain " +
- target_->toolchain()->label().GetUserVisibleName(false) +
- "\n used by target " + target_->label().GetUserVisibleName(false) +
- "\n doesn't define a \"stamp\" tool."));
- return;
- }
-
- // Figure out the substitutions used by the copy and stamp tools.
- SubstitutionBits required_bits = copy_tool->substitution_bits();
- required_bits.MergeFrom(stamp_tool->substitution_bits());
-
- // General target-related substitutions needed by both tools.
- WriteSharedVars(required_bits);
-
- std::vector<OutputFile> output_files;
- WriteCopyRules(&output_files);
- out_ << std::endl;
- WriteStampForTarget(output_files, std::vector<OutputFile>());
-}
-
-void NinjaCopyTargetWriter::WriteCopyRules(
- std::vector<OutputFile>* output_files) {
- CHECK(target_->action_values().outputs().list().size() == 1);
- const SubstitutionList& output_subst_list =
- target_->action_values().outputs();
- CHECK_EQ(1u, output_subst_list.list().size())
- << "Should have one entry exactly.";
- const SubstitutionPattern& output_subst = output_subst_list.list()[0];
-
- std::string tool_name = GetNinjaRulePrefixForToolchain(settings_) +
- GeneralTool::kGeneralToolCopy;
-
- size_t num_stamp_uses = target_->sources().size();
- std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
- std::vector<const Target*>(), num_stamp_uses);
-
- // Note that we don't write implicit deps for copy steps. "copy" only
- // depends on the output files themselves, rather than having includes
- // (the possibility of generated #includes is the main reason for implicit
- // dependencies).
- //
- // It would seem that specifying implicit dependencies on the deps of the
- // copy command would still be harmeless. But Chrome implements copy tools
- // as hard links (much faster) which don't change the timestamp. If the
- // ninja rule looks like this:
- // output: copy input | foo.stamp
- // The copy will not make a new timestamp on the output file, but the
- // foo.stamp file generated from a previous step will have a new timestamp.
- // The copy rule will therefore look out-of-date to Ninja and the rule will
- // get rebuilt.
- //
- // If this copy is copying a generated file, not listing the implicit
- // dependency will be fine as long as the input to the copy is properly
- // listed as the output from the step that generated it.
- //
- // Moreover, doing this assumes that the copy step is always a simple
- // locally run command, so there is no need for a toolchain dependency.
- //
- // Note that there is the need in some cases for order-only dependencies
- // where a command might need to make sure something else runs before it runs
- // to avoid conflicts. Such cases should be avoided where possible, but
- // sometimes that's not possible.
- for (const auto& input_file : target_->sources()) {
- OutputFile output_file =
- SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
- target_, target_->settings(), output_subst, input_file);
- output_files->push_back(output_file);
-
- out_ << "build ";
- path_output_.WriteFile(out_, output_file);
- out_ << ": " << tool_name << " ";
- path_output_.WriteFile(out_, input_file);
- if (!input_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, input_deps);
- }
- out_ << std::endl;
- }
-}
diff --git a/gn/tools/gn/ninja_copy_target_writer.h b/gn/tools/gn/ninja_copy_target_writer.h
deleted file mode 100644
index a45a470a3d4..00000000000
--- a/gn/tools/gn/ninja_copy_target_writer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 TOOLS_GN_NINJA_COPY_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_COPY_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/ninja_target_writer.h"
-
-// Writes a .ninja file for a copy target type.
-class NinjaCopyTargetWriter : public NinjaTargetWriter {
- public:
- NinjaCopyTargetWriter(const Target* target, std::ostream& out);
- ~NinjaCopyTargetWriter() override;
-
- void Run() override;
-
- private:
- // Writes the rules top copy the file(s), putting the computed output file
- // name(s) into the given vector.
- void WriteCopyRules(std::vector<OutputFile>* output_files);
-
- DISALLOW_COPY_AND_ASSIGN(NinjaCopyTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_COPY_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_copy_target_writer_unittest.cc b/gn/tools/gn/ninja_copy_target_writer_unittest.cc
deleted file mode 100644
index be17a11556b..00000000000
--- a/gn/tools/gn/ninja_copy_target_writer_unittest.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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 <algorithm>
-#include <sstream>
-
-#include "tools/gn/ninja_copy_target_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-// Tests multiple files with an output pattern and no toolchain dependency.
-TEST(NinjaCopyTargetWriter, Run) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::COPY_FILES);
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.sources().push_back(SourceFile("//foo/input2.txt"));
-
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCopyTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "build input1.out: copy ../../foo/input1.txt\n"
- "build input2.out: copy ../../foo/input2.txt\n"
- "\n"
- "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected_linux, out_str);
-}
-
-// Tests a single file with no output pattern.
-TEST(NinjaCopyTargetWriter, ToolchainDeps) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::COPY_FILES);
-
- target.sources().push_back(SourceFile("//foo/input1.txt"));
-
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/output.out");
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCopyTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "build output.out: copy ../../foo/input1.txt\n"
- "\n"
- "build obj/foo/bar.stamp: stamp output.out\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected_linux, out_str);
-}
-
-TEST(NinjaCopyTargetWriter, OrderOnlyDeps) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::COPY_FILES);
- target.sources().push_back(SourceFile("//foo/input1.txt"));
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
- target.config_values().inputs().push_back(SourceFile("//foo/script.py"));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCopyTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected_linux[] =
- "build input1.out: copy ../../foo/input1.txt || ../../foo/script.py\n"
- "\n"
- "build obj/foo/bar.stamp: stamp input1.out\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected_linux, out_str);
-}
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer.cc b/gn/tools/gn/ninja_create_bundle_target_writer.cc
deleted file mode 100644
index dab4485cbf5..00000000000
--- a/gn/tools/gn/ninja_create_bundle_target_writer.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/ninja_create_bundle_target_writer.h"
-
-#include "base/macros.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/toolchain.h"
-
-namespace {
-
-bool TargetRequireAssetCatalogCompilation(const Target* target) {
- return !target->bundle_data().assets_catalog_sources().empty() ||
- !target->bundle_data().partial_info_plist().is_null();
-}
-
-void FailWithMissingToolError(const char* tool_name, const Target* target) {
- g_scheduler->FailWithError(
- Err(nullptr, std::string(tool_name) + " tool not defined",
- "The toolchain " +
- target->toolchain()->label().GetUserVisibleName(false) +
- "\n"
- "used by target " +
- target->label().GetUserVisibleName(false) +
- "\n"
- "doesn't define a \"" +
- tool_name + "\" tool."));
-}
-
-bool EnsureAllToolsAvailable(const Target* target) {
- const char* kRequiredTools[] = {
- GeneralTool::kGeneralToolCopyBundleData,
- GeneralTool::kGeneralToolStamp,
- };
-
- for (size_t i = 0; i < arraysize(kRequiredTools); ++i) {
- if (!target->toolchain()->GetTool(kRequiredTools[i])) {
- FailWithMissingToolError(kRequiredTools[i], target);
- return false;
- }
- }
-
- // The compile_xcassets tool is only required if the target has asset
- // catalog resources to compile.
- if (TargetRequireAssetCatalogCompilation(target)) {
- if (!target->toolchain()->GetTool(
- GeneralTool::kGeneralToolCompileXCAssets)) {
- FailWithMissingToolError(GeneralTool::kGeneralToolCompileXCAssets,
- target);
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace
-
-NinjaCreateBundleTargetWriter::NinjaCreateBundleTargetWriter(
- const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out) {}
-
-NinjaCreateBundleTargetWriter::~NinjaCreateBundleTargetWriter() = default;
-
-void NinjaCreateBundleTargetWriter::Run() {
- if (!EnsureAllToolsAvailable(target_))
- return;
-
- // Stamp users are CopyBundleData, CompileAssetsCatalog, CodeSigning and
- // StampForTarget.
- size_t num_stamp_uses = 4;
- std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
- std::vector<const Target*>(), num_stamp_uses);
-
- std::string code_signing_rule_name = WriteCodeSigningRuleDefinition();
-
- std::vector<OutputFile> output_files;
- WriteCopyBundleDataSteps(order_only_deps, &output_files);
- WriteCompileAssetsCatalogStep(order_only_deps, &output_files);
- WriteCodeSigningStep(code_signing_rule_name, order_only_deps, &output_files);
-
- for (const auto& pair : target_->data_deps())
- order_only_deps.push_back(pair.ptr->dependency_output_file());
- WriteStampForTarget(output_files, order_only_deps);
-
- // Write a phony target for the outer bundle directory. This allows other
- // targets to treat the entire bundle as a single unit, even though it is
- // a directory, so that it can be depended upon as a discrete build edge.
- out_ << "build ";
- path_output_.WriteFile(
- out_,
- OutputFile(settings_->build_settings(),
- target_->bundle_data().GetBundleRootDirOutput(settings_)));
- out_ << ": phony " << target_->dependency_output_file().value();
- out_ << std::endl;
-}
-
-std::string NinjaCreateBundleTargetWriter::WriteCodeSigningRuleDefinition() {
- if (target_->bundle_data().code_signing_script().is_null())
- return std::string();
-
- std::string target_label = target_->label().GetUserVisibleName(true);
- std::string custom_rule_name(target_label);
- base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
- custom_rule_name.append("_code_signing_rule");
-
- out_ << "rule " << custom_rule_name << std::endl;
- out_ << " command = ";
- path_output_.WriteFile(out_, settings_->build_settings()->python_path());
- out_ << " ";
- path_output_.WriteFile(out_, target_->bundle_data().code_signing_script());
-
- const SubstitutionList& args = target_->bundle_data().code_signing_args();
- EscapeOptions args_escape_options;
- args_escape_options.mode = ESCAPE_NINJA_COMMAND;
-
- for (const auto& arg : args.list()) {
- out_ << " ";
- SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
- }
- out_ << std::endl;
- out_ << " description = CODE SIGNING " << target_label << std::endl;
- out_ << " restat = 1" << std::endl;
- out_ << std::endl;
-
- return custom_rule_name;
-}
-
-void NinjaCreateBundleTargetWriter::WriteCopyBundleDataSteps(
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files) {
- for (const BundleFileRule& file_rule : target_->bundle_data().file_rules())
- WriteCopyBundleFileRuleSteps(file_rule, order_only_deps, output_files);
-}
-
-void NinjaCreateBundleTargetWriter::WriteCopyBundleFileRuleSteps(
- const BundleFileRule& file_rule,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files) {
- // Note that we don't write implicit deps for copy steps. "copy_bundle_data"
- // steps as this is most likely implemented using hardlink in the common case.
- // See NinjaCopyTargetWriter::WriteCopyRules() for a detailed explanation.
- for (const SourceFile& source_file : file_rule.sources()) {
- // There is no need to check for errors here as the substitution will have
- // been performed when computing the list of output of the target during
- // the Target::OnResolved phase earlier.
- OutputFile expanded_output_file;
- file_rule.ApplyPatternToSourceAsOutputFile(
- settings_, target_, target_->bundle_data(), source_file,
- &expanded_output_file,
- /*err=*/nullptr);
- output_files->push_back(expanded_output_file);
-
- out_ << "build ";
- path_output_.WriteFile(out_, expanded_output_file);
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolCopyBundleData << " ";
- path_output_.WriteFile(out_, source_file);
-
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
-
- out_ << std::endl;
- }
-}
-
-void NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogStep(
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files) {
- if (!TargetRequireAssetCatalogCompilation(target_))
- return;
-
- OutputFile compiled_catalog;
- if (!target_->bundle_data().assets_catalog_sources().empty()) {
- compiled_catalog =
- OutputFile(settings_->build_settings(),
- target_->bundle_data().GetCompiledAssetCatalogPath());
- output_files->push_back(compiled_catalog);
- }
-
- OutputFile partial_info_plist;
- if (!target_->bundle_data().partial_info_plist().is_null()) {
- partial_info_plist =
- OutputFile(settings_->build_settings(),
- target_->bundle_data().partial_info_plist());
-
- output_files->push_back(partial_info_plist);
- }
-
- // If there are no asset catalog to compile but the "partial_info_plist" is
- // non-empty, then add a target to generate an empty file (to avoid breaking
- // code that depends on this file existence).
- if (target_->bundle_data().assets_catalog_sources().empty()) {
- DCHECK(!target_->bundle_data().partial_info_plist().is_null());
-
- out_ << "build ";
- path_output_.WriteFile(out_, partial_info_plist);
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolStamp;
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
- out_ << std::endl;
- return;
- }
-
- OutputFile input_dep = WriteCompileAssetsCatalogInputDepsStamp(
- target_->bundle_data().assets_catalog_deps());
- DCHECK(!input_dep.value().empty());
-
- out_ << "build ";
- path_output_.WriteFile(out_, compiled_catalog);
- if (partial_info_plist != OutputFile()) {
- // If "partial_info_plist" is non-empty, then add it to list of implicit
- // outputs of the asset catalog compilation, so that target can use it
- // without getting the ninja error "'foo', needed by 'bar', missing and
- // no known rule to make it".
- out_ << " | ";
- path_output_.WriteFile(out_, partial_info_plist);
- }
-
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolCompileXCAssets;
-
- std::set<SourceFile> asset_catalog_bundles;
- for (const auto& source : target_->bundle_data().assets_catalog_sources()) {
- out_ << " ";
- path_output_.WriteFile(out_, source);
- asset_catalog_bundles.insert(source);
- }
-
- out_ << " | ";
- path_output_.WriteFile(out_, input_dep);
-
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
-
- out_ << std::endl;
-
- out_ << " product_type = " << target_->bundle_data().product_type()
- << std::endl;
-
- if (partial_info_plist != OutputFile()) {
- out_ << " partial_info_plist = ";
- path_output_.WriteFile(out_, partial_info_plist);
- out_ << std::endl;
- }
-}
-
-OutputFile
-NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogInputDepsStamp(
- const std::vector<const Target*>& dependencies) {
- DCHECK(!dependencies.empty());
- if (dependencies.size() == 1)
- return dependencies[0]->dependency_output_file();
-
- OutputFile xcassets_input_stamp_file =
- GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
- xcassets_input_stamp_file.value().append(target_->label().name());
- xcassets_input_stamp_file.value().append(".xcassets.inputdeps.stamp");
-
- out_ << "build ";
- path_output_.WriteFile(out_, xcassets_input_stamp_file);
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolStamp;
-
- for (const Target* target : dependencies) {
- out_ << " ";
- path_output_.WriteFile(out_, target->dependency_output_file());
- }
- out_ << std::endl;
- return xcassets_input_stamp_file;
-}
-
-void NinjaCreateBundleTargetWriter::WriteCodeSigningStep(
- const std::string& code_signing_rule_name,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files) {
- if (code_signing_rule_name.empty())
- return;
-
- OutputFile code_signing_input_stamp_file =
- WriteCodeSigningInputDepsStamp(order_only_deps, output_files);
- DCHECK(!code_signing_input_stamp_file.value().empty());
-
- out_ << "build";
- std::vector<OutputFile> code_signing_output_files;
- SubstitutionWriter::GetListAsOutputFiles(
- settings_, target_->bundle_data().code_signing_outputs(),
- &code_signing_output_files);
- path_output_.WriteFiles(out_, code_signing_output_files);
-
- // Since the code signature step depends on all the files from the bundle,
- // the create_bundle stamp can just depends on the output of the signature
- // script (dependencies are transitive).
- *output_files = std::move(code_signing_output_files);
-
- out_ << ": " << code_signing_rule_name;
- out_ << " | ";
- path_output_.WriteFile(out_, code_signing_input_stamp_file);
- out_ << std::endl;
-}
-
-OutputFile NinjaCreateBundleTargetWriter::WriteCodeSigningInputDepsStamp(
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files) {
- std::vector<SourceFile> code_signing_input_files;
- code_signing_input_files.push_back(
- target_->bundle_data().code_signing_script());
- code_signing_input_files.insert(
- code_signing_input_files.end(),
- target_->bundle_data().code_signing_sources().begin(),
- target_->bundle_data().code_signing_sources().end());
- for (const OutputFile& output_file : *output_files) {
- code_signing_input_files.push_back(
- output_file.AsSourceFile(settings_->build_settings()));
- }
-
- DCHECK(!code_signing_input_files.empty());
- if (code_signing_input_files.size() == 1 && order_only_deps.empty())
- return OutputFile(settings_->build_settings(), code_signing_input_files[0]);
-
- OutputFile code_signing_input_stamp_file =
- GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
- code_signing_input_stamp_file.value().append(target_->label().name());
- code_signing_input_stamp_file.value().append(".codesigning.inputdeps.stamp");
-
- out_ << "build ";
- path_output_.WriteFile(out_, code_signing_input_stamp_file);
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolStamp;
-
- for (const SourceFile& source : code_signing_input_files) {
- out_ << " ";
- path_output_.WriteFile(out_, source);
- }
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
- out_ << std::endl;
- return code_signing_input_stamp_file;
-}
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer.h b/gn/tools/gn/ninja_create_bundle_target_writer.h
deleted file mode 100644
index 072e02c2a73..00000000000
--- a/gn/tools/gn/ninja_create_bundle_target_writer.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_NINJA_CREATE_BUNDLE_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_CREATE_BUNDLE_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/ninja_target_writer.h"
-
-class BundleFileRule;
-
-// Writes a .ninja file for a bundle_data target type.
-class NinjaCreateBundleTargetWriter : public NinjaTargetWriter {
- public:
- NinjaCreateBundleTargetWriter(const Target* target, std::ostream& out);
- ~NinjaCreateBundleTargetWriter() override;
-
- void Run() override;
-
- private:
- // Writes the Ninja rule for invoking the code signing script.
- //
- // Returns the name of the custom rule generated for the code signing step if
- // defined, otherwise returns an empty string.
- std::string WriteCodeSigningRuleDefinition();
-
- // Writes the steps to copy files into the bundle.
- //
- // The list of newly created files will be added to |output_files|.
- void WriteCopyBundleDataSteps(const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files);
-
- // Writes the step to copy files BundleFileRule into the bundle.
- //
- // The list of newly created files will be added to |output_files|.
- void WriteCopyBundleFileRuleSteps(
- const BundleFileRule& file_rule,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files);
-
- // Writes the step to compile assets catalogs.
- //
- // The list of newly created files will be added to |output_files|.
- void WriteCompileAssetsCatalogStep(
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files);
-
- // Writes the stamp file for the assets catalog compilation input
- // dependencies.
- OutputFile WriteCompileAssetsCatalogInputDepsStamp(
- const std::vector<const Target*>& dependencies);
-
- // Writes the code signing step (if a script is defined).
- //
- // The list of newly created files will be added to |output_files|. As the
- // code signing may depends on the full bundle structure, this step will
- // depends on all files generated via other rules.
- void WriteCodeSigningStep(const std::string& code_signing_rule_name,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files);
-
- // Writes the stamp file for the code signing input dependencies.
- OutputFile WriteCodeSigningInputDepsStamp(
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* output_files);
-
- DISALLOW_COPY_AND_ASSIGN(NinjaCreateBundleTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_CREATE_BUNDLE_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc b/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
deleted file mode 100644
index 51c982c828a..00000000000
--- a/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/ninja_create_bundle_target_writer.h"
-
-#include <algorithm>
-#include <memory>
-#include <sstream>
-
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-void SetupBundleDataDir(BundleData* bundle_data, const std::string& root_dir) {
- std::string bundle_root_dir = root_dir + "/bar.bundle";
- bundle_data->root_dir() = SourceDir(bundle_root_dir);
- bundle_data->contents_dir() = SourceDir(bundle_root_dir + "/Contents");
- bundle_data->resources_dir() =
- SourceDir(bundle_data->contents_dir().value() + "/Resources");
- bundle_data->executable_dir() =
- SourceDir(bundle_data->contents_dir().value() + "/MacOS");
-}
-
-std::unique_ptr<Target> NewAction(const TestWithScope& setup) {
- Err err;
- auto action = std::make_unique<Target>(setup.settings(),
- Label(SourceDir("//foo/"), "bar"));
- action->set_output_type(Target::ACTION);
- action->visibility().SetPublic();
- action->action_values().set_script(SourceFile("//foo/script.py"));
-
- action->action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/foo.out");
-
- action->SetToolchain(setup.toolchain());
- return action;
-}
-
-} // namespace
-
-// Tests multiple files with an output pattern.
-TEST(NinjaCreateBundleTargetWriter, Run) {
- Err err;
- TestWithScope setup;
-
- std::unique_ptr<Target> action = NewAction(setup);
- ASSERT_TRUE(action->OnResolved(&err)) << err.message();
-
- Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
- bundle_data.set_output_type(Target::BUNDLE_DATA);
- bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
- bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
- bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data.SetToolchain(setup.toolchain());
- bundle_data.visibility().SetPublic();
- ASSERT_TRUE(bundle_data.OnResolved(&err));
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
- create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
- "obj/foo/data.stamp\n"
- "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
- "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
- "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
- "build obj/baz/bar.stamp: stamp "
- "bar.bundle/Contents/Resources/input1.txt "
- "bar.bundle/Contents/Resources/input2.txt"
- " || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-// Tests creating a bundle in a sub-directory of $root_out_dir.
-TEST(NinjaCreateBundleTargetWriter, InSubDirectory) {
- Err err;
- TestWithScope setup;
-
- std::unique_ptr<Target> action = NewAction(setup);
- ASSERT_TRUE(action->OnResolved(&err)) << err.message();
-
- Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
- bundle_data.set_output_type(Target::BUNDLE_DATA);
- bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
- bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
- bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data.SetToolchain(setup.toolchain());
- bundle_data.visibility().SetPublic();
- ASSERT_TRUE(bundle_data.OnResolved(&err));
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug/gen");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
- create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
- "obj/foo/data.stamp\n"
- "build gen/bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
- "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
- "build gen/bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
- "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
- "build obj/baz/bar.stamp: stamp "
- "gen/bar.bundle/Contents/Resources/input1.txt "
- "gen/bar.bundle/Contents/Resources/input2.txt || "
- "obj/baz/bar.inputdeps.stamp\n"
- "build gen/bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-// Tests empty asset catalog with partial_info_plist property defined.
-TEST(NinjaCreateBundleTargetWriter, JustPartialInfoPlist) {
- Err err;
- TestWithScope setup;
-
- std::unique_ptr<Target> action = NewAction(setup);
- ASSERT_TRUE(action->OnResolved(&err)) << err.message();
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
- create_bundle.bundle_data().product_type().assign("com.apple.product-type");
- create_bundle.bundle_data().set_partial_info_plist(
- SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build baz/bar/bar_partial_info.plist: stamp || obj/foo/bar.stamp\n"
- "build obj/baz/bar.stamp: stamp "
- "baz/bar/bar_partial_info.plist || obj/foo/bar.stamp\n"
- "build bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-// Tests multiple files from asset catalog.
-TEST(NinjaCreateBundleTargetWriter, AssetCatalog) {
- Err err;
- TestWithScope setup;
-
- std::unique_ptr<Target> action = NewAction(setup);
- ASSERT_TRUE(action->OnResolved(&err)) << err.message();
-
- Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
- bundle_data.set_output_type(Target::BUNDLE_DATA);
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/Contents.json"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
- bundle_data.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
- bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data.SetToolchain(setup.toolchain());
- bundle_data.visibility().SetPublic();
- ASSERT_TRUE(bundle_data.OnResolved(&err));
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
- create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
- create_bundle.bundle_data().product_type().assign("com.apple.product-type");
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
- "obj/foo/data.stamp\n"
- "build bar.bundle/Contents/Resources/Assets.car: compile_xcassets "
- "../../foo/Foo.xcassets | obj/foo/data.stamp || "
- "obj/baz/bar.inputdeps.stamp\n"
- " product_type = com.apple.product-type\n"
- "build obj/baz/bar.stamp: stamp "
- "bar.bundle/Contents/Resources/Assets.car || "
- "obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-// Tests that the phony target for the top-level bundle directory is generated
-// correctly.
-TEST(NinjaCreateBundleTargetWriter, PhonyTarget) {
- Err err;
- TestWithScope setup;
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build obj/baz/bar.stamp: stamp\n"
- "build bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-// Tests complex target with multiple bundle_data sources, including
-// some asset catalog.
-TEST(NinjaCreateBundleTargetWriter, Complex) {
- Err err;
- TestWithScope setup;
-
- std::unique_ptr<Target> action = NewAction(setup);
- ASSERT_TRUE(action->OnResolved(&err)) << err.message();
-
- Target bundle_data0(setup.settings(),
- Label(SourceDir("//qux/"), "info_plist"));
- bundle_data0.set_output_type(Target::BUNDLE_DATA);
- bundle_data0.sources().push_back(SourceFile("//qux/qux-Info.plist"));
- bundle_data0.action_values().outputs() =
- SubstitutionList::MakeForTest("{{bundle_contents_dir}}/Info.plist");
- bundle_data0.SetToolchain(setup.toolchain());
- bundle_data0.visibility().SetPublic();
- ASSERT_TRUE(bundle_data0.OnResolved(&err));
-
- Target bundle_data1(setup.settings(), Label(SourceDir("//foo/"), "data"));
- bundle_data1.set_output_type(Target::BUNDLE_DATA);
- bundle_data1.sources().push_back(SourceFile("//foo/input1.txt"));
- bundle_data1.sources().push_back(SourceFile("//foo/input2.txt"));
- bundle_data1.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data1.SetToolchain(setup.toolchain());
- bundle_data1.visibility().SetPublic();
- ASSERT_TRUE(bundle_data1.OnResolved(&err));
-
- Target bundle_data2(setup.settings(), Label(SourceDir("//foo/"), "assets"));
- bundle_data2.set_output_type(Target::BUNDLE_DATA);
- bundle_data2.sources().push_back(
- SourceFile("//foo/Foo.xcassets/Contents.json"));
- bundle_data2.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
- bundle_data2.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
- bundle_data2.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
- bundle_data2.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
- bundle_data2.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
- bundle_data2.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data2.SetToolchain(setup.toolchain());
- bundle_data2.visibility().SetPublic();
- ASSERT_TRUE(bundle_data2.OnResolved(&err));
-
- Target bundle_data3(setup.settings(), Label(SourceDir("//quz/"), "assets"));
- bundle_data3.set_output_type(Target::BUNDLE_DATA);
- bundle_data3.sources().push_back(
- SourceFile("//quz/Quz.xcassets/Contents.json"));
- bundle_data3.sources().push_back(
- SourceFile("//quz/Quz.xcassets/quz.imageset/Contents.json"));
- bundle_data3.sources().push_back(
- SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29.png"));
- bundle_data3.sources().push_back(
- SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@2x.png"));
- bundle_data3.sources().push_back(
- SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@3x.png"));
- bundle_data3.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data3.SetToolchain(setup.toolchain());
- bundle_data3.visibility().SetPublic();
- ASSERT_TRUE(bundle_data3.OnResolved(&err));
-
- Target bundle_data4(setup.settings(), Label(SourceDir("//biz/"), "assets"));
- bundle_data4.set_output_type(Target::BUNDLE_DATA);
- bundle_data4.sources().push_back(
- SourceFile("//biz/Biz.xcassets/Contents.json"));
- bundle_data4.sources().push_back(
- SourceFile("//biz/Biz.xcassets/biz.colorset/Contents.json"));
- bundle_data4.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data4.SetToolchain(setup.toolchain());
- bundle_data4.visibility().SetPublic();
- ASSERT_TRUE(bundle_data4.OnResolved(&err));
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data0));
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data1));
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data2));
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data3));
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data4));
- create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
- create_bundle.bundle_data().product_type().assign("com.apple.product-type");
- create_bundle.bundle_data().set_partial_info_plist(
- SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build obj/baz/bar.inputdeps.stamp: stamp obj/biz/assets.stamp "
- "obj/foo/assets.stamp obj/foo/bar.stamp obj/foo/data.stamp "
- "obj/qux/info_plist.stamp obj/quz/assets.stamp\n"
- "build bar.bundle/Contents/Info.plist: copy_bundle_data "
- "../../qux/qux-Info.plist || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
- "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
- "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
- "build obj/baz/bar.xcassets.inputdeps.stamp: stamp "
- "obj/foo/assets.stamp "
- "obj/quz/assets.stamp obj/biz/assets.stamp\n"
- "build bar.bundle/Contents/Resources/Assets.car | "
- "baz/bar/bar_partial_info.plist: compile_xcassets "
- "../../foo/Foo.xcassets ../../quz/Quz.xcassets "
- "../../biz/Biz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || "
- "obj/baz/bar.inputdeps.stamp\n"
- " product_type = com.apple.product-type\n"
- " partial_info_plist = baz/bar/bar_partial_info.plist\n"
- "build obj/baz/bar.stamp: stamp "
- "bar.bundle/Contents/Info.plist "
- "bar.bundle/Contents/Resources/input1.txt "
- "bar.bundle/Contents/Resources/input2.txt "
- "bar.bundle/Contents/Resources/Assets.car "
- "baz/bar/bar_partial_info.plist || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-// Tests code signing steps.
-TEST(NinjaCreateBundleTargetWriter, CodeSigning) {
- Err err;
- TestWithScope setup;
-
- std::unique_ptr<Target> action = NewAction(setup);
- ASSERT_TRUE(action->OnResolved(&err)) << err.message();
-
- Target executable(setup.settings(), Label(SourceDir("//baz/"), "quz"));
- executable.set_output_type(Target::EXECUTABLE);
- executable.sources().push_back(SourceFile("//baz/quz.c"));
- executable.SetToolchain(setup.toolchain());
- executable.visibility().SetPublic();
- ASSERT_TRUE(executable.OnResolved(&err));
-
- Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
- bundle_data.set_output_type(Target::BUNDLE_DATA);
- bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
- bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
- bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- bundle_data.SetToolchain(setup.toolchain());
- bundle_data.visibility().SetPublic();
- ASSERT_TRUE(bundle_data.OnResolved(&err));
-
- Target create_bundle(
- setup.settings(),
- Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
- setup.toolchain()->label().name()));
- SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
- create_bundle.set_output_type(Target::CREATE_BUNDLE);
- create_bundle.bundle_data().set_code_signing_script(
- SourceFile("//build/codesign.py"));
- create_bundle.bundle_data().code_signing_sources().push_back(
- SourceFile("//out/Debug/quz"));
- create_bundle.bundle_data().code_signing_outputs() =
- SubstitutionList::MakeForTest(
- "//out/Debug/bar.bundle/Contents/quz",
- "//out/Debug/bar.bundle/_CodeSignature/CodeResources");
- create_bundle.bundle_data().code_signing_args() =
- SubstitutionList::MakeForTest("-b=quz", "bar.bundle");
- create_bundle.public_deps().push_back(LabelTargetPair(&executable));
- create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
- create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
- create_bundle.SetToolchain(setup.toolchain());
- ASSERT_TRUE(create_bundle.OnResolved(&err));
-
- std::ostringstream out;
- NinjaCreateBundleTargetWriter writer(&create_bundle, out);
- writer.Run();
-
- const char expected[] =
- "build obj/baz/bar.inputdeps.stamp: stamp ./quz obj/foo/bar.stamp "
- "obj/foo/data.stamp\n"
- "rule __baz_bar___toolchain_default__code_signing_rule\n"
- " command = ../../build/codesign.py -b=quz bar.bundle\n"
- " description = CODE SIGNING //baz:bar(//toolchain:default)\n"
- " restat = 1\n"
- "\n"
- "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
- "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
- "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
- "build obj/baz/bar.codesigning.inputdeps.stamp: stamp "
- "../../build/codesign.py "
- "quz "
- "bar.bundle/Contents/Resources/input1.txt "
- "bar.bundle/Contents/Resources/input2.txt || "
- "obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: "
- "__baz_bar___toolchain_default__code_signing_rule "
- "| obj/baz/bar.codesigning.inputdeps.stamp\n"
- "build obj/baz/bar.stamp: stamp "
- "bar.bundle/Contents/quz "
- "bar.bundle/_CodeSignature/CodeResources || obj/baz/bar.inputdeps.stamp\n"
- "build bar.bundle: phony obj/baz/bar.stamp\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
diff --git a/gn/tools/gn/ninja_generated_file_target_writer.cc b/gn/tools/gn/ninja_generated_file_target_writer.cc
deleted file mode 100644
index 7dc60b02c57..00000000000
--- a/gn/tools/gn/ninja_generated_file_target_writer.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/ninja_generated_file_target_writer.h"
-
-#include "base/strings/string_util.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/output_conversion.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-
-NinjaGeneratedFileTargetWriter::NinjaGeneratedFileTargetWriter(
- const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out) {}
-
-NinjaGeneratedFileTargetWriter::~NinjaGeneratedFileTargetWriter() = default;
-
-void NinjaGeneratedFileTargetWriter::Run() {
- // Write the file.
- GenerateFile();
-
- // A generated_file target should generate a stamp file with dependencies
- // on each of the deps and data_deps in the target. The actual collection is
- // done at gen time, and so ninja doesn't need to know about it.
- std::vector<OutputFile> output_files;
- for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED))
- output_files.push_back(pair.ptr->dependency_output_file());
-
- std::vector<OutputFile> data_output_files;
- const LabelTargetVector& data_deps = target_->data_deps();
- for (const auto& pair : data_deps)
- data_output_files.push_back(pair.ptr->dependency_output_file());
-
- WriteStampForTarget(output_files, data_output_files);
-}
-
-void NinjaGeneratedFileTargetWriter::GenerateFile() {
- Err err;
-
- // If this is a metadata target, populate the write value with the appropriate
- // data.
- Value contents;
- if (target_->contents().type() == Value::NONE) {
- // Origin is set to the outputs location, so that errors with this value
- // get flagged on the right target.
- CHECK(target_->action_values().outputs().list().size() == 1U);
- contents = Value(target_->action_values().outputs().list()[0].origin(),
- Value::LIST);
- std::set<const Target*> targets_walked;
- if (!target_->GetMetadata(target_->data_keys(), target_->walk_keys(),
- target_->rebase(), /*deps_only = */ true,
- &contents.list_value(), &targets_walked, &err)) {
- g_scheduler->FailWithError(err);
- return;
- }
- } else {
- contents = target_->contents();
- }
-
- std::vector<SourceFile> outputs_as_sources;
- target_->action_values().GetOutputsAsSourceFiles(target_,
- &outputs_as_sources);
- CHECK(outputs_as_sources.size() == 1);
-
- base::FilePath output =
- settings_->build_settings()->GetFullPath(outputs_as_sources[0]);
- ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, outputs_as_sources[0].value());
-
- // Compute output.
- std::ostringstream out;
- ConvertValueToOutput(settings_, contents, target_->output_conversion(), out,
- &err);
-
- if (err.has_error()) {
- g_scheduler->FailWithError(err);
- return;
- }
-
- WriteFileIfChanged(output, out.str(), &err);
-
- if (err.has_error()) {
- g_scheduler->FailWithError(err);
- return;
- }
-}
diff --git a/gn/tools/gn/ninja_generated_file_target_writer.h b/gn/tools/gn/ninja_generated_file_target_writer.h
deleted file mode 100644
index fa2e6c2ee58..00000000000
--- a/gn/tools/gn/ninja_generated_file_target_writer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/ninja_target_writer.h"
-
-// Writes a .ninja file for a group target type.
-class NinjaGeneratedFileTargetWriter : public NinjaTargetWriter {
- public:
- NinjaGeneratedFileTargetWriter(const Target* target, std::ostream& out);
- ~NinjaGeneratedFileTargetWriter() override;
-
- void Run() override;
-
- private:
- void GenerateFile();
-
- DISALLOW_COPY_AND_ASSIGN(NinjaGeneratedFileTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc b/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc
deleted file mode 100644
index a01ef7b0c5f..00000000000
--- a/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 "tools/gn/ninja_generated_file_target_writer.h"
-
-#include "tools/gn/source_file.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-using NinjaGeneratedFileTargetWriterTest = TestWithScheduler;
-
-TEST_F(NinjaGeneratedFileTargetWriterTest, Run) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::GENERATED_FILE);
- target.visibility().SetPublic();
- target.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/foo.json");
- target.set_contents(Value(nullptr, true));
- target.set_output_conversion(Value(nullptr, "json"));
-
- Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
- dep.set_output_type(Target::ACTION);
- dep.visibility().SetPublic();
- dep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target dep2(setup.settings(), Label(SourceDir("//foo/"), "dep2"));
- dep2.set_output_type(Target::ACTION);
- dep2.visibility().SetPublic();
- dep2.SetToolchain(setup.toolchain());
- ASSERT_TRUE(dep2.OnResolved(&err));
-
- Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
- datadep.set_output_type(Target::ACTION);
- datadep.visibility().SetPublic();
- datadep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(datadep.OnResolved(&err));
-
- target.public_deps().push_back(LabelTargetPair(&dep));
- target.public_deps().push_back(LabelTargetPair(&dep2));
- target.data_deps().push_back(LabelTargetPair(&datadep));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err)) << err.message();
-
- std::ostringstream out;
- NinjaGeneratedFileTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "build obj/foo/bar.stamp: stamp obj/foo/dep.stamp obj/foo/dep2.stamp || "
- "obj/foo/datadep.stamp\n";
- EXPECT_EQ(expected, out.str());
-}
diff --git a/gn/tools/gn/ninja_group_target_writer.cc b/gn/tools/gn/ninja_group_target_writer.cc
deleted file mode 100644
index 85906bb1514..00000000000
--- a/gn/tools/gn/ninja_group_target_writer.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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 "tools/gn/ninja_group_target_writer.h"
-
-#include "base/strings/string_util.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/target.h"
-
-NinjaGroupTargetWriter::NinjaGroupTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaTargetWriter(target, out) {}
-
-NinjaGroupTargetWriter::~NinjaGroupTargetWriter() = default;
-
-void NinjaGroupTargetWriter::Run() {
- // A group rule just generates a stamp file with dependencies on each of
- // the deps and data_deps in the group.
- std::vector<OutputFile> output_files;
- for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED))
- output_files.push_back(pair.ptr->dependency_output_file());
-
- std::vector<OutputFile> data_output_files;
- const LabelTargetVector& data_deps = target_->data_deps();
- for (const auto& pair : data_deps)
- data_output_files.push_back(pair.ptr->dependency_output_file());
-
- WriteStampForTarget(output_files, data_output_files);
-}
diff --git a/gn/tools/gn/ninja_group_target_writer.h b/gn/tools/gn/ninja_group_target_writer.h
deleted file mode 100644
index 66e5f043155..00000000000
--- a/gn/tools/gn/ninja_group_target_writer.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 TOOLS_GN_NINJA_GROUP_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_GROUP_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/ninja_target_writer.h"
-
-// Writes a .ninja file for a group target type.
-class NinjaGroupTargetWriter : public NinjaTargetWriter {
- public:
- NinjaGroupTargetWriter(const Target* target, std::ostream& out);
- ~NinjaGroupTargetWriter() override;
-
- void Run() override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NinjaGroupTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_GROUP_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_group_target_writer_unittest.cc b/gn/tools/gn/ninja_group_target_writer_unittest.cc
deleted file mode 100644
index ab5f5978710..00000000000
--- a/gn/tools/gn/ninja_group_target_writer_unittest.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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 "tools/gn/ninja_group_target_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(NinjaGroupTargetWriter, Run) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::GROUP);
- target.visibility().SetPublic();
-
- Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
- dep.set_output_type(Target::ACTION);
- dep.visibility().SetPublic();
- dep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target dep2(setup.settings(), Label(SourceDir("//foo/"), "dep2"));
- dep2.set_output_type(Target::ACTION);
- dep2.visibility().SetPublic();
- dep2.SetToolchain(setup.toolchain());
- ASSERT_TRUE(dep2.OnResolved(&err));
-
- Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
- datadep.set_output_type(Target::ACTION);
- datadep.visibility().SetPublic();
- datadep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(datadep.OnResolved(&err));
-
- target.public_deps().push_back(LabelTargetPair(&dep));
- target.public_deps().push_back(LabelTargetPair(&dep2));
- target.data_deps().push_back(LabelTargetPair(&datadep));
-
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaGroupTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "build obj/foo/bar.stamp: stamp obj/foo/dep.stamp obj/foo/dep2.stamp || "
- "obj/foo/datadep.stamp\n";
- EXPECT_EQ(expected, out.str());
-}
diff --git a/gn/tools/gn/ninja_rust_binary_target_writer.cc b/gn/tools/gn/ninja_rust_binary_target_writer.cc
deleted file mode 100644
index 5372fec834b..00000000000
--- a/gn/tools/gn/ninja_rust_binary_target_writer.cc
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/ninja_rust_binary_target_writer.h"
-
-#include <sstream>
-
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/rust_substitution_type.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-
-namespace {
-
-// Returns the proper escape options for writing compiler and linker flags.
-EscapeOptions GetFlagOptions() {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_COMMAND;
- return opts;
-}
-
-void WriteVar(const char* name,
- const std::string& value,
- EscapeOptions opts,
- std::ostream& out) {
- out << name << " = ";
- EscapeStringToStream(out, value, opts);
- out << std::endl;
-}
-
-void WriteCrateVars(const Target* target,
- const Tool* tool,
- EscapeOptions opts,
- std::ostream& out) {
- WriteVar(kRustSubstitutionCrateName.ninja_name,
- target->rust_values().crate_name(), opts, out);
-
- std::string crate_type;
- switch (target->rust_values().crate_type()) {
- // Auto-select the crate type for executables, static libraries, and rlibs.
- case RustValues::CRATE_AUTO: {
- switch (target->output_type()) {
- case Target::EXECUTABLE:
- crate_type = "bin";
- break;
- case Target::STATIC_LIBRARY:
- crate_type = "staticlib";
- break;
- case Target::RUST_LIBRARY:
- crate_type = "rlib";
- break;
- default:
- NOTREACHED();
- }
- break;
- }
- case RustValues::CRATE_BIN:
- crate_type = "bin";
- break;
- case RustValues::CRATE_CDYLIB:
- crate_type = "cdylib";
- break;
- case RustValues::CRATE_DYLIB:
- crate_type = "dylib";
- break;
- case RustValues::CRATE_PROC_MACRO:
- crate_type = "proc-macro";
- break;
- case RustValues::CRATE_RLIB:
- crate_type = "rlib";
- break;
- case RustValues::CRATE_STATICLIB:
- crate_type = "staticlib";
- break;
- default:
- NOTREACHED();
- }
- WriteVar(kRustSubstitutionCrateType.ninja_name, crate_type, opts, out);
-
- WriteVar(SubstitutionOutputDir.ninja_name,
- SubstitutionWriter::GetLinkerSubstitution(target, tool,
- &SubstitutionOutputDir),
- opts, out);
- if (!target->output_extension_set()) {
- DCHECK(tool->AsRust());
- WriteVar(kRustSubstitutionOutputExtension.ninja_name,
- tool->AsRust()->rustc_output_extension(
- target->output_type(), target->rust_values().crate_type()),
- opts, out);
- } else if (target->output_extension().empty()) {
- WriteVar(kRustSubstitutionOutputExtension.ninja_name, "", opts, out);
- } else {
- WriteVar(kRustSubstitutionOutputExtension.ninja_name,
- std::string(".") + target->output_extension(), opts, out);
- }
-
- if (target->output_type() == Target::RUST_LIBRARY ||
- target->output_type() == Target::SHARED_LIBRARY)
- WriteVar(kRustSubstitutionOutputPrefix.ninja_name, "lib", opts, out);
-}
-
-} // namespace
-
-NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target,
- std::ostream& out)
- : NinjaBinaryTargetWriter(target, out),
- tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {}
-
-NinjaRustBinaryTargetWriter::~NinjaRustBinaryTargetWriter() = default;
-
-// TODO(juliehockett): add inherited library support? and IsLinkable support?
-// for c-cross-compat
-void NinjaRustBinaryTargetWriter::Run() {
- OutputFile input_dep = WriteInputsStampAndGetDep();
-
- // The input dependencies will be an order-only dependency. This will cause
- // Ninja to make sure the inputs are up to date before compiling this source,
- // but changes in the inputs deps won't cause the file to be recompiled. See
- // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation.
- size_t num_stamp_uses = target_->sources().size();
- std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
- std::vector<const Target*>(), num_stamp_uses);
-
- // Public rust_library deps go in a --extern rlibs, public non-rust deps go in
- // -Ldependency rustdeps, and non-public source_sets get passed in as normal
- // source files
- UniqueVector<OutputFile> deps;
- AddSourceSetFiles(target_, &deps);
- if (target_->output_type() == Target::SOURCE_SET) {
- WriteSharedVars(target_->toolchain()->substitution_bits());
- WriteSourceSetStamp(deps.vector());
- } else {
- WriteCompilerVars();
- UniqueVector<const Target*> linkable_deps;
- UniqueVector<const Target*> non_linkable_deps;
- GetDeps(&deps, &linkable_deps, &non_linkable_deps);
-
- if (!input_dep.value().empty())
- order_only_deps.push_back(input_dep);
-
- std::vector<OutputFile> rustdeps;
- std::vector<OutputFile> nonrustdeps;
- for (const auto* non_linkable_dep : non_linkable_deps) {
- order_only_deps.push_back(non_linkable_dep->dependency_output_file());
- }
-
- for (const auto* linkable_dep : linkable_deps) {
- if (linkable_dep->source_types_used().RustSourceUsed()) {
- rustdeps.push_back(linkable_dep->dependency_output_file());
- } else {
- nonrustdeps.push_back(linkable_dep->dependency_output_file());
- }
- deps.push_back(linkable_dep->dependency_output_file());
- }
-
- std::vector<OutputFile> tool_outputs;
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- target_, tool_, tool_->outputs(), &tool_outputs);
- WriteCompilerBuildLine(target_->rust_values().crate_root(), deps.vector(),
- order_only_deps, tool_->name(), tool_outputs);
-
- std::vector<const Target*> extern_deps(linkable_deps.vector());
- std::copy(non_linkable_deps.begin(), non_linkable_deps.end(),
- std::back_inserter(extern_deps));
- WriteExterns(extern_deps);
-
- WriteRustdeps(rustdeps, nonrustdeps);
- WriteEdition();
- }
-}
-
-void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
- const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
-
- EscapeOptions opts = GetFlagOptions();
- WriteCrateVars(target_, tool_, opts, out_);
-
- WriteOneFlag(target_, &kRustSubstitutionRustFlags, false,
- RustTool::kRsToolRustc, &ConfigValues::rustflags, opts,
- path_output_, out_);
-
- WriteOneFlag(target_, &kRustSubstitutionRustEnv, false,
- RustTool::kRsToolRustc, &ConfigValues::rustenv, opts,
- path_output_, out_);
-
- WriteSharedVars(subst);
-}
-
-void NinjaRustBinaryTargetWriter::WriteExterns(
- const std::vector<const Target*>& deps) {
- std::vector<const Target*> externs;
- for (const Target* target : deps) {
- if (target->output_type() == Target::RUST_LIBRARY ||
- target->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO) {
- externs.push_back(target);
- }
- }
- if (externs.empty())
- return;
- out_ << " externs =";
- for (const Target* ex : externs) {
- out_ << " --extern ";
-
- const auto& renamed_dep =
- target_->rust_values().aliased_deps().find(ex->label());
- if (renamed_dep != target_->rust_values().aliased_deps().end()) {
- out_ << renamed_dep->second << "=";
- } else {
- out_ << std::string(ex->rust_values().crate_name()) << "=";
- }
-
- path_output_.WriteFile(out_, ex->dependency_output_file());
- }
- out_ << std::endl;
-}
-
-void NinjaRustBinaryTargetWriter::WriteRustdeps(
- const std::vector<OutputFile>& rustdeps,
- const std::vector<OutputFile>& nonrustdeps) {
- if (rustdeps.empty() && nonrustdeps.empty())
- return;
-
- out_ << " rustdeps =";
- for (const auto& rustdep : rustdeps) {
- out_ << " -Ldependency=";
- path_output_.WriteDir(
- out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(),
- PathOutput::DIR_NO_LAST_SLASH);
- }
-
- for (const auto& rustdep : nonrustdeps) {
- out_ << " -Lnative=";
- path_output_.WriteDir(
- out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(),
- PathOutput::DIR_NO_LAST_SLASH);
- }
- out_ << std::endl;
-}
-
-void NinjaRustBinaryTargetWriter::WriteEdition() {
- DCHECK(!target_->rust_values().edition().empty());
- out_ << " edition = " << target_->rust_values().edition() << std::endl;
-}
diff --git a/gn/tools/gn/ninja_rust_binary_target_writer.h b/gn/tools/gn/ninja_rust_binary_target_writer.h
deleted file mode 100644
index 31d636b01aa..00000000000
--- a/gn/tools/gn/ninja_rust_binary_target_writer.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
-
-#include "base/macros.h"
-#include "tools/gn/ninja_binary_target_writer.h"
-#include "tools/gn/rust_tool.h"
-
-struct EscapeOptions;
-
-// Writes a .ninja file for a binary target type (an executable, a shared
-// library, or a static library).
-class NinjaRustBinaryTargetWriter : public NinjaBinaryTargetWriter {
- public:
- NinjaRustBinaryTargetWriter(const Target* target, std::ostream& out);
- ~NinjaRustBinaryTargetWriter() override;
-
- void Run() override;
-
- private:
- void WriteCompilerVars();
- void WriteSources(const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps);
- void WriteExterns(const std::vector<const Target*>& deps);
- void WriteRustdeps(const std::vector<OutputFile>& rustdeps,
- const std::vector<OutputFile>& nonrustdeps);
- void WriteEdition();
-
- const RustTool* tool_;
-
- DISALLOW_COPY_AND_ASSIGN(NinjaRustBinaryTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc b/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc
deleted file mode 100644
index 90d592bf5db..00000000000
--- a/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc
+++ /dev/null
@@ -1,578 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/ninja_rust_binary_target_writer.h"
-
-#include "tools/gn/config.h"
-#include "tools/gn/rust_values.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-using NinjaRustBinaryTargetWriterTest = TestWithScheduler;
-
-TEST_F(NinjaRustBinaryTargetWriterTest, RustSourceSet) {
- Err err;
- TestWithScope setup;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.rs"));
- target.sources().push_back(SourceFile("//foo/main.rs"));
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Source set itself.
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.stamp: stamp ../../foo/input1.rs "
- "../../foo/main.rs\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, RustExecutable) {
- Err err;
- TestWithScope setup;
-
- Target source_set(setup.settings(), Label(SourceDir("//foo/"), "sources"));
- source_set.set_output_type(Target::SOURCE_SET);
- source_set.visibility().SetPublic();
- source_set.sources().push_back(SourceFile("//foo/input1.rs"));
- source_set.sources().push_back(SourceFile("//foo/input2.rs"));
- source_set.source_types_used().Set(SourceFile::SOURCE_RS);
- source_set.SetToolchain(setup.toolchain());
- ASSERT_TRUE(source_set.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/input3.rs"));
- target.sources().push_back(main);
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.rust_values().edition() = "2018";
- target.private_deps().push_back(LabelTargetPair(&source_set));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/input3.rs "
- "../../foo/main.rs ../../foo/input1.rs ../../foo/input2.rs || "
- "obj/foo/sources.stamp\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, RlibDeps) {
- Err err;
- TestWithScope setup;
-
- Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
- rlib.set_output_type(Target::RUST_LIBRARY);
- rlib.visibility().SetPublic();
- SourceFile barlib("//bar/lib.rs");
- rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
- rlib.sources().push_back(barlib);
- rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib.rust_values().set_crate_root(barlib);
- rlib.rust_values().crate_name() = "mylib";
- rlib.rust_values().edition() = "2018";
- rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&rlib, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = mylib\n"
- "crate_type = rlib\n"
- "output_dir = \n"
- "rustc_output_extension = .rlib\n"
- "rustc_output_prefix = lib\n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = mylib\n"
- "\n"
- "build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
- }
-
- Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- another_rlib.set_output_type(Target::RUST_LIBRARY);
- another_rlib.visibility().SetPublic();
- SourceFile lib("//foo/main.rs");
- another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
- another_rlib.sources().push_back(lib);
- another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- another_rlib.rust_values().set_crate_root(lib);
- another_rlib.rust_values().crate_name() = "direct";
- another_rlib.rust_values().edition() = "2018";
- another_rlib.SetToolchain(setup.toolchain());
- another_rlib.public_deps().push_back(LabelTargetPair(&rlib));
- ASSERT_TRUE(another_rlib.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/source.rs"));
- target.sources().push_back(main);
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.rust_values().edition() = "2018";
- target.private_deps().push_back(LabelTargetPair(&another_rlib));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.rlib obj/bar/libmylib.rlib\n"
- " externs = --extern direct=obj/foo/libdirect.rlib --extern "
- "mylib=obj/bar/libmylib.rlib\n"
- " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, RenamedDeps) {
- Err err;
- TestWithScope setup;
-
- Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- another_rlib.set_output_type(Target::RUST_LIBRARY);
- another_rlib.visibility().SetPublic();
- SourceFile lib("//foo/lib.rs");
- another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
- another_rlib.sources().push_back(lib);
- another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- another_rlib.rust_values().set_crate_root(lib);
- another_rlib.rust_values().crate_name() = "direct";
- another_rlib.rust_values().edition() = "2018";
- another_rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(another_rlib.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/source.rs"));
- target.sources().push_back(main);
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.rust_values().aliased_deps()[another_rlib.label()] = "direct_renamed";
- target.rust_values().edition() = "2018";
- target.private_deps().push_back(LabelTargetPair(&another_rlib));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.rlib\n"
- " externs = --extern direct_renamed=obj/foo/libdirect.rlib\n"
- " rustdeps = -Ldependency=obj/foo\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
- Err err;
- TestWithScope setup;
-
- Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
- rlib.set_output_type(Target::RUST_LIBRARY);
- rlib.visibility().SetPublic();
- SourceFile barlib("//bar/lib.rs");
- rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
- rlib.sources().push_back(barlib);
- rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib.rust_values().set_crate_root(barlib);
- rlib.rust_values().crate_name() = "mylib";
- rlib.rust_values().edition() = "2018";
- rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib.OnResolved(&err));
-
- Target staticlib(setup.settings(), Label(SourceDir("//foo/"), "static"));
- staticlib.set_output_type(Target::STATIC_LIBRARY);
- staticlib.visibility().SetPublic();
- staticlib.sources().push_back(SourceFile("//foo/static.cpp"));
- staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
- staticlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(staticlib.OnResolved(&err));
-
- Target nonrust(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- nonrust.set_output_type(Target::EXECUTABLE);
- nonrust.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- nonrust.sources().push_back(SourceFile("//foo/source.rs"));
- nonrust.sources().push_back(main);
- nonrust.source_types_used().Set(SourceFile::SOURCE_RS);
- nonrust.rust_values().set_crate_root(main);
- nonrust.rust_values().crate_name() = "foo_bar";
- nonrust.rust_values().edition() = "2018";
- nonrust.private_deps().push_back(LabelTargetPair(&rlib));
- nonrust.private_deps().push_back(LabelTargetPair(&staticlib));
- nonrust.SetToolchain(setup.toolchain());
- ASSERT_TRUE(nonrust.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&nonrust, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/bar/libmylib.rlib obj/foo/libstatic.a\n"
- " externs = --extern mylib=obj/bar/libmylib.rlib\n"
- " rustdeps = -Ldependency=obj/bar -Lnative=obj/foo\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
-
- Target nonrust_only(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- nonrust_only.set_output_type(Target::EXECUTABLE);
- nonrust_only.visibility().SetPublic();
- nonrust_only.sources().push_back(SourceFile("//foo/source.rs"));
- nonrust_only.sources().push_back(main);
- nonrust_only.source_types_used().Set(SourceFile::SOURCE_RS);
- nonrust_only.rust_values().set_crate_root(main);
- nonrust_only.rust_values().crate_name() = "foo_bar";
- nonrust_only.rust_values().edition() = "2018";
- nonrust_only.private_deps().push_back(LabelTargetPair(&staticlib));
- nonrust_only.SetToolchain(setup.toolchain());
- ASSERT_TRUE(nonrust_only.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&nonrust_only, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libstatic.a\n"
- " rustdeps = -Lnative=obj/foo\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, RustOutputExtensionAndDir) {
- Err err;
- TestWithScope setup;
-
- Target source_set(setup.settings(), Label(SourceDir("//foo/"), "sources"));
- source_set.set_output_type(Target::SOURCE_SET);
- source_set.visibility().SetPublic();
- source_set.sources().push_back(SourceFile("//foo/input1.rs"));
- source_set.sources().push_back(SourceFile("//foo/input2.rs"));
- source_set.source_types_used().Set(SourceFile::SOURCE_RS);
- source_set.SetToolchain(setup.toolchain());
- ASSERT_TRUE(source_set.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/input3.rs"));
- target.sources().push_back(main);
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.set_output_extension(std::string("exe"));
- target.set_output_dir(SourceDir("//out/Debug/foo/"));
- target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.rust_values().edition() = "2018";
- target.private_deps().push_back(LabelTargetPair(&source_set));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = foo\n"
- "rustc_output_extension = .exe\n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar.exe: rustc ../../foo/main.rs | ../../foo/input3.rs "
- "../../foo/main.rs ../../foo/input1.rs ../../foo/input2.rs || "
- "obj/foo/sources.stamp\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, ProcMacro) {
- Err err;
- TestWithScope setup;
-
- Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
- procmacro.set_output_type(Target::LOADABLE_MODULE);
- procmacro.visibility().SetPublic();
- SourceFile barlib("//bar/lib.rs");
- procmacro.sources().push_back(SourceFile("//bar/mylib.rs"));
- procmacro.sources().push_back(barlib);
- procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
- procmacro.rust_values().set_crate_root(barlib);
- procmacro.rust_values().crate_name() = "mymacro";
- procmacro.rust_values().edition() = "2018";
- procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
- procmacro.SetToolchain(setup.toolchain());
- ASSERT_TRUE(procmacro.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&procmacro, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = mymacro\n"
- "crate_type = proc-macro\n"
- "output_dir = \n"
- "rustc_output_extension = .so\n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = mymacro\n"
- "\n"
- "build obj/bar/libmymacro.so: rustc ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
- }
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/source.rs"));
- target.sources().push_back(main);
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.rust_values().edition() = "2018";
- target.private_deps().push_back(LabelTargetPair(&procmacro));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs || obj/bar/libmymacro.so\n"
- " externs = --extern mymacro=obj/bar/libmymacro.so\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
-}
-
-TEST_F(NinjaRustBinaryTargetWriterTest, GroupDeps) {
- Err err;
- TestWithScope setup;
-
- Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
- rlib.set_output_type(Target::RUST_LIBRARY);
- rlib.visibility().SetPublic();
- SourceFile barlib("//bar/lib.rs");
- rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
- rlib.sources().push_back(barlib);
- rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib.rust_values().set_crate_root(barlib);
- rlib.rust_values().crate_name() = "mylib";
- rlib.rust_values().edition() = "2018";
- rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&rlib, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = mylib\n"
- "crate_type = rlib\n"
- "output_dir = \n"
- "rustc_output_extension = .rlib\n"
- "rustc_output_prefix = lib\n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = mylib\n"
- "\n"
- "build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
- }
-
- Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
- group.set_output_type(Target::GROUP);
- group.visibility().SetPublic();
- group.public_deps().push_back(LabelTargetPair(&rlib));
- group.SetToolchain(setup.toolchain());
- ASSERT_TRUE(group.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/source.rs"));
- target.sources().push_back(main);
- target.source_types_used().Set(SourceFile::SOURCE_RS);
- target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.rust_values().edition() = "2018";
- target.private_deps().push_back(LabelTargetPair(&group));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expected[] =
- "crate_name = foo_bar\n"
- "crate_type = bin\n"
- "output_dir = \n"
- "rustc_output_extension = \n"
- "rustflags =\n"
- "rustenv =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/bar/libmylib.rlib || obj/baz/group.stamp\n"
- " externs = --extern mylib=obj/bar/libmylib.rlib\n"
- " rustdeps = -Ldependency=obj/bar\n"
- " edition = 2018\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
-}
diff --git a/gn/tools/gn/ninja_target_command_util.cc b/gn/tools/gn/ninja_target_command_util.cc
deleted file mode 100644
index ef9957e8c25..00000000000
--- a/gn/tools/gn/ninja_target_command_util.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/ninja_target_command_util.h"
-
-#include <string.h>
-
-#include "tools/gn/c_tool.h"
-#include "tools/gn/substitution_writer.h"
-
-namespace {
-
-// Returns the language-specific suffix for precompiled header files.
-const char* GetPCHLangSuffixForToolType(const char* name) {
- if (name == CTool::kCToolCc)
- return "c";
- if (name == CTool::kCToolCxx)
- return "cc";
- if (name == CTool::kCToolObjC)
- return "m";
- if (name == CTool::kCToolObjCxx)
- return "mm";
- NOTREACHED() << "Not a valid PCH tool type: " << name;
- return "";
-}
-
-} // namespace
-
-// Returns the computed name of the Windows .pch file for the given
-// tool type. The tool must support precompiled headers.
-OutputFile GetWindowsPCHFile(const Target* target, const char* tool_name) {
- // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
- // looking like "obj/chrome/browser/browser_cc.pch"
- OutputFile ret = GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ);
- ret.value().append(target->label().name());
- ret.value().push_back('_');
- ret.value().append(GetPCHLangSuffixForToolType(tool_name));
- ret.value().append(".pch");
-
- return ret;
-}
-
-void WriteOneFlag(const Target* target,
- const Substitution* subst_enum,
- bool has_precompiled_headers,
- const char* tool_name,
- const std::vector<std::string>& (ConfigValues::*getter)()
- const,
- EscapeOptions flag_escape_options,
- PathOutput& path_output,
- std::ostream& out,
- bool write_substitution) {
- if (!target->toolchain()->substitution_bits().used.count(subst_enum))
- return;
-
- if (write_substitution)
- out << subst_enum->ninja_name << " =";
-
- if (has_precompiled_headers) {
- const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
- if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
- // Name the .pch file.
- out << " /Fp";
- path_output.WriteFile(out, GetWindowsPCHFile(target, tool_name));
-
- // Enables precompiled headers and names the .h file. It's a string
- // rather than a file name (so no need to rebase or use path_output).
- out << " /Yu" << target->config_values().precompiled_header();
- RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
- out);
- } else if (tool && tool->precompiled_header_type() == CTool::PCH_GCC) {
- // The targets to build the .gch files should omit the -include flag
- // below. To accomplish this, each substitution flag is overwritten in
- // the target rule and these values are repeated. The -include flag is
- // omitted in place of the required -x <header lang> flag for .gch
- // targets.
- RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
- out);
-
- // Compute the gch file (it will be language-specific).
- std::vector<OutputFile> outputs;
- GetPCHOutputFiles(target, tool_name, &outputs);
- if (!outputs.empty()) {
- // Trim the .gch suffix for the -include flag.
- // e.g. for gch file foo/bar/target.precompiled.h.gch:
- // -include foo/bar/target.precompiled.h
- std::string pch_file = outputs[0].value();
- pch_file.erase(pch_file.length() - 4);
- out << " -include " << pch_file;
- }
- } else {
- RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
- out);
- }
- } else {
- RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
- out);
- }
-
- if (write_substitution)
- out << std::endl;
-}
-
-void GetPCHOutputFiles(const Target* target,
- const char* tool_name,
- std::vector<OutputFile>* outputs) {
- outputs->clear();
-
- // Compute the tool. This must use the tool type passed in rather than the
- // detected file type of the precompiled source file since the same
- // precompiled source file will be used for separate C/C++ compiles.
- const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
- if (!tool)
- return;
- SubstitutionWriter::ApplyListToCompilerAsOutputFile(
- target, target->config_values().precompiled_source(), tool->outputs(),
- outputs);
-
- if (outputs->empty())
- return;
- if (outputs->size() > 1)
- outputs->resize(1); // Only link the first output from the compiler tool.
-
- std::string& output_value = (*outputs)[0].value();
- size_t extension_offset = FindExtensionOffset(output_value);
- if (extension_offset == std::string::npos) {
- // No extension found.
- return;
- }
- DCHECK(extension_offset >= 1);
- DCHECK(output_value[extension_offset - 1] == '.');
-
- std::string output_extension;
- CTool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
- switch (header_type) {
- case CTool::PCH_MSVC:
- output_extension = GetWindowsPCHObjectExtension(
- tool_name, output_value.substr(extension_offset - 1));
- break;
- case CTool::PCH_GCC:
- output_extension = GetGCCPCHOutputExtension(tool_name);
- break;
- case CTool::PCH_NONE:
- NOTREACHED() << "No outputs for no PCH type.";
- break;
- }
- output_value.replace(extension_offset - 1, std::string::npos,
- output_extension);
-}
-
-std::string GetGCCPCHOutputExtension(const char* tool_name) {
- const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
- std::string result = ".";
- // For GCC, the output name must have a .gch suffix and be annotated with
- // the language type. For example:
- // obj/foo/target_name.header.h ->
- // obj/foo/target_name.header.h-cc.gch
- // In order for the compiler to pick it up, the output name (minus the .gch
- // suffix MUST match whatever is passed to the -include flag).
- result += "h-";
- result += lang_suffix;
- result += ".gch";
- return result;
-}
-
-std::string GetWindowsPCHObjectExtension(const char* tool_name,
- const std::string& obj_extension) {
- const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
- std::string result = ".";
- // For MSVC, annotate the obj files with the language type. For example:
- // obj/foo/target_name.precompile.obj ->
- // obj/foo/target_name.precompile.cc.obj
- result += lang_suffix;
- result += obj_extension;
- return result;
-}
diff --git a/gn/tools/gn/ninja_target_command_util.h b/gn/tools/gn/ninja_target_command_util.h
deleted file mode 100644
index 327e120279c..00000000000
--- a/gn/tools/gn/ninja_target_command_util.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
-#define TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
-
-#include "base/json/string_escape.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/path_output.h"
-#include "tools/gn/target.h"
-#include "tools/gn/toolchain.h"
-
-struct DefineWriter {
- DefineWriter() { options.mode = ESCAPE_NINJA_COMMAND; }
- DefineWriter(EscapingMode mode, bool escape_strings)
- : escape_strings(escape_strings) {
- options.mode = mode;
- }
-
- void operator()(const std::string& s, std::ostream& out) const {
- out << " ";
- if (escape_strings) {
- std::string dest;
- base::EscapeJSONString(s, false, &dest);
- EscapeStringToStream(out, "-D" + dest, options);
- return;
- }
- EscapeStringToStream(out, "-D" + s, options);
- }
-
- EscapeOptions options;
- bool escape_strings = false;
-};
-
-struct IncludeWriter {
- explicit IncludeWriter(PathOutput& path_output) : path_output_(path_output) {}
- ~IncludeWriter() = default;
-
- void operator()(const SourceDir& d, std::ostream& out) const {
- std::ostringstream path_out;
- path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
- const std::string& path = path_out.str();
- if (path[0] == '"')
- out << " \"-I" << path.substr(1);
- else
- out << " -I" << path;
- }
-
- PathOutput& path_output_;
-};
-
-// has_precompiled_headers is set when this substitution matches a tool type
-// that supports precompiled headers, and this target supports precompiled
-// headers. It doesn't indicate if the tool has precompiled headers (this
-// will be looked up by this function).
-//
-// The tool_type indicates the corresponding tool for flags that are
-// tool-specific (e.g. "cflags_c"). For non-tool-specific flags (e.g.
-// "defines") tool_type should be TYPE_NONE.
-void WriteOneFlag(const Target* target,
- const Substitution* subst_enum,
- bool has_precompiled_headers,
- const char* tool_name,
- const std::vector<std::string>& (ConfigValues::*getter)()
- const,
- EscapeOptions flag_escape_options,
- PathOutput& path_output,
- std::ostream& out,
- bool write_substitution = true);
-
-// Fills |outputs| with the object or gch file for the precompiled header of the
-// given type (flag type and tool type must match).
-void GetPCHOutputFiles(const Target* target,
- const char* tool_name,
- std::vector<OutputFile>* outputs);
-
-std::string GetGCCPCHOutputExtension(const char* tool_name);
-std::string GetWindowsPCHObjectExtension(const char* tool_name,
- const std::string& obj_extension);
-
-#endif // TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
diff --git a/gn/tools/gn/ninja_target_writer.cc b/gn/tools/gn/ninja_target_writer.cc
deleted file mode 100644
index 239e50fb4cb..00000000000
--- a/gn/tools/gn/ninja_target_writer.cc
+++ /dev/null
@@ -1,329 +0,0 @@
-// 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 "tools/gn/ninja_target_writer.h"
-
-#include <sstream>
-
-#include "base/files/file_util.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/err.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_action_target_writer.h"
-#include "tools/gn/ninja_binary_target_writer.h"
-#include "tools/gn/ninja_bundle_data_target_writer.h"
-#include "tools/gn/ninja_copy_target_writer.h"
-#include "tools/gn/ninja_create_bundle_target_writer.h"
-#include "tools/gn/ninja_generated_file_target_writer.h"
-#include "tools/gn/ninja_group_target_writer.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-
-NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
- : settings_(target->settings()),
- target_(target),
- out_(out),
- path_output_(settings_->build_settings()->build_dir(),
- settings_->build_settings()->root_path_utf8(),
- ESCAPE_NINJA) {}
-
-NinjaTargetWriter::~NinjaTargetWriter() = default;
-
-// static
-std::string NinjaTargetWriter::RunAndWriteFile(const Target* target) {
- const Settings* settings = target->settings();
-
- ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
- target->label().GetUserVisibleName(false));
- trace.SetToolchain(settings->toolchain_label());
-
- if (g_scheduler->verbose_logging())
- g_scheduler->Log("Computing", target->label().GetUserVisibleName(true));
-
- // It's ridiculously faster to write to a string and then write that to
- // disk in one operation than to use an fstream here.
- std::stringstream rules;
-
- // Call out to the correct sub-type of writer. Binary targets need to be
- // written to separate files for compiler flag scoping, but other target
- // types can have their rules coalesced.
- //
- // In ninja, if a rule uses a variable (like $include_dirs) it will use
- // the value set by indenting it under the build line or it takes the value
- // from the end of the invoking scope (otherwise the current file). It does
- // not copy the value from what it was when the build line was encountered.
- // To avoid writing lots of duplicate rules for defines and cflags, etc. on
- // each source file build line, we use separate .ninja files with the shared
- // variables set at the top.
- //
- // Groups and actions don't use this type of flag, they make unique rules
- // or write variables scoped under each build line. As a result, they don't
- // need the separate files.
- bool needs_file_write = false;
- if (target->output_type() == Target::BUNDLE_DATA) {
- NinjaBundleDataTargetWriter writer(target, rules);
- writer.Run();
- } else if (target->output_type() == Target::CREATE_BUNDLE) {
- NinjaCreateBundleTargetWriter writer(target, rules);
- writer.Run();
- } else if (target->output_type() == Target::COPY_FILES) {
- NinjaCopyTargetWriter writer(target, rules);
- writer.Run();
- } else if (target->output_type() == Target::ACTION ||
- target->output_type() == Target::ACTION_FOREACH) {
- NinjaActionTargetWriter writer(target, rules);
- writer.Run();
- } else if (target->output_type() == Target::GROUP) {
- NinjaGroupTargetWriter writer(target, rules);
- writer.Run();
- } else if (target->output_type() == Target::GENERATED_FILE) {
- NinjaGeneratedFileTargetWriter writer(target, rules);
- writer.Run();
- } else if (target->IsBinary()) {
- needs_file_write = true;
- NinjaBinaryTargetWriter writer(target, rules);
- writer.Run();
- } else {
- CHECK(0) << "Output type of target not handled.";
- }
-
- if (needs_file_write) {
- // Write the ninja file.
- SourceFile ninja_file = GetNinjaFileForTarget(target);
- base::FilePath full_ninja_file =
- settings->build_settings()->GetFullPath(ninja_file);
- base::CreateDirectory(full_ninja_file.DirName());
- WriteFileIfChanged(full_ninja_file, rules.str(), nullptr);
-
- EscapeOptions options;
- options.mode = ESCAPE_NINJA;
-
- // Return the subninja command to load the rules file.
- std::string result = "subninja ";
- result.append(EscapeString(
- OutputFile(target->settings()->build_settings(), ninja_file).value(),
- options, nullptr));
- result.push_back('\n');
- return result;
- }
-
- // No separate file required, just return the rules.
- return rules.str();
-}
-
-void NinjaTargetWriter::WriteEscapedSubstitution(const Substitution* type) {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA;
-
- out_ << type->ninja_name << " = ";
- EscapeStringToStream(
- out_, SubstitutionWriter::GetTargetSubstitution(target_, type), opts);
- out_ << std::endl;
-}
-
-void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
- bool written_anything = false;
-
- // Target label.
- if (bits.used.count(&SubstitutionLabel)) {
- WriteEscapedSubstitution(&SubstitutionLabel);
- written_anything = true;
- }
-
- // Target label name
- if (bits.used.count(&SubstitutionLabelName)) {
- WriteEscapedSubstitution(&SubstitutionLabelName);
- written_anything = true;
- }
-
- // Root gen dir.
- if (bits.used.count(&SubstitutionRootGenDir)) {
- WriteEscapedSubstitution(&SubstitutionRootGenDir);
- written_anything = true;
- }
-
- // Root out dir.
- if (bits.used.count(&SubstitutionRootOutDir)) {
- WriteEscapedSubstitution(&SubstitutionRootOutDir);
- written_anything = true;
- }
-
- // Target gen dir.
- if (bits.used.count(&SubstitutionTargetGenDir)) {
- WriteEscapedSubstitution(&SubstitutionTargetGenDir);
- written_anything = true;
- }
-
- // Target out dir.
- if (bits.used.count(&SubstitutionTargetOutDir)) {
- WriteEscapedSubstitution(&SubstitutionTargetOutDir);
- written_anything = true;
- }
-
- // Target output name.
- if (bits.used.count(&SubstitutionTargetOutputName)) {
- WriteEscapedSubstitution(&SubstitutionTargetOutputName);
- written_anything = true;
- }
-
- // If we wrote any vars, separate them from the rest of the file that follows
- // with a blank line.
- if (written_anything)
- out_ << std::endl;
-}
-
-std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
- const std::vector<const Target*>& extra_hard_deps,
- size_t num_stamp_uses) const {
- CHECK(target_->toolchain()) << "Toolchain not set on target "
- << target_->label().GetUserVisibleName(true);
-
- // ----------
- // Collect all input files that are input deps of this target. Knowing the
- // number before writing allows us to either skip writing the input deps
- // stamp or optimize it. Use pointers to avoid copies here.
- std::vector<const SourceFile*> input_deps_sources;
- input_deps_sources.reserve(32);
-
- // Actions get implicit dependencies on the script itself.
- if (target_->output_type() == Target::ACTION ||
- target_->output_type() == Target::ACTION_FOREACH)
- input_deps_sources.push_back(&target_->action_values().script());
-
- // Input files are only considered for non-binary targets which use an
- // implicit dependency instead. The implicit depedency in this case is
- // handled separately by the binary target writer.
- if (!target_->IsBinary()) {
- for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
- for (const auto& input : iter.cur().inputs())
- input_deps_sources.push_back(&input);
- }
- }
-
- // For an action (where we run a script only once) the sources are the same
- // as the inputs. For action_foreach, the sources will be operated on
- // separately so don't handle them here.
- if (target_->output_type() == Target::ACTION) {
- for (const auto& source : target_->sources())
- input_deps_sources.push_back(&source);
- }
-
- // ----------
- // Collect all target input dependencies of this target as was done for the
- // files above.
- std::vector<const Target*> input_deps_targets;
- input_deps_targets.reserve(32);
-
- // Hard dependencies that are direct or indirect dependencies.
- // These are large (up to 100s), hence why we check other
- const std::set<const Target*>& hard_deps(target_->recursive_hard_deps());
- for (const Target* target : hard_deps)
- input_deps_targets.push_back(target);
-
- // Extra hard dependencies passed in. These are usually empty or small, and
- // we don't want to duplicate the explicit hard deps of the target.
- for (const Target* target : extra_hard_deps) {
- if (!hard_deps.count(target))
- input_deps_targets.push_back(target);
- }
-
- // Toolchain dependencies. These must be resolved before doing anything.
- // This just writes all toolchain deps for simplicity. If we find that
- // toolchains often have more than one dependency, we could consider writing
- // a toolchain-specific stamp file and only include the stamp here.
- // Note that these are usually empty/small.
- const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
- for (const auto& toolchain_dep : toolchain_deps) {
- // This could theoretically duplicate dependencies already in the list,
- // but it shouldn't happen in practice, is inconvenient to check for,
- // and only results in harmless redundant dependencies listed.
- input_deps_targets.push_back(toolchain_dep.ptr);
- }
-
- // ---------
- // Write the outputs.
-
- if (input_deps_sources.size() + input_deps_targets.size() == 0)
- return std::vector<OutputFile>(); // No input dependencies.
-
- // If we're only generating one input dependency, return it directly instead
- // of writing a stamp file for it.
- if (input_deps_sources.size() == 1 && input_deps_targets.size() == 0)
- return std::vector<OutputFile>{
- OutputFile(settings_->build_settings(), *input_deps_sources[0])};
- if (input_deps_sources.size() == 0 && input_deps_targets.size() == 1) {
- const OutputFile& dep = input_deps_targets[0]->dependency_output_file();
- DCHECK(!dep.value().empty());
- return std::vector<OutputFile>{dep};
- }
-
- std::vector<OutputFile> outs;
- // File input deps.
- for (const SourceFile* source : input_deps_sources)
- outs.push_back(OutputFile(settings_->build_settings(), *source));
- // Target input deps. Sort by label so the output is deterministic (otherwise
- // some of the targets will have gone through std::sets which will have
- // sorted them by pointer).
- std::sort(
- input_deps_targets.begin(), input_deps_targets.end(),
- [](const Target* a, const Target* b) { return a->label() < b->label(); });
- for (auto* dep : input_deps_targets) {
- DCHECK(!dep->dependency_output_file().value().empty());
- outs.push_back(dep->dependency_output_file());
- }
-
- // If there are multiple inputs, but the stamp file would be referenced only
- // once, don't write it but depend on the inputs directly.
- if (num_stamp_uses == 1u)
- return outs;
-
- // Make a stamp file.
- OutputFile input_stamp_file =
- GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
- input_stamp_file.value().append(target_->label().name());
- input_stamp_file.value().append(".inputdeps.stamp");
-
- out_ << "build ";
- path_output_.WriteFile(out_, input_stamp_file);
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolStamp;
- path_output_.WriteFiles(out_, outs);
-
- out_ << "\n";
- return std::vector<OutputFile>{input_stamp_file};
-}
-
-void NinjaTargetWriter::WriteStampForTarget(
- const std::vector<OutputFile>& files,
- const std::vector<OutputFile>& order_only_deps) {
- const OutputFile& stamp_file = target_->dependency_output_file();
-
- // First validate that the target's dependency is a stamp file. Otherwise,
- // we shouldn't have gotten here!
- CHECK(base::EndsWith(stamp_file.value(), ".stamp",
- base::CompareCase::INSENSITIVE_ASCII))
- << "Output should end in \".stamp\" for stamp file output. Instead got: "
- << "\"" << stamp_file.value() << "\"";
-
- out_ << "build ";
- path_output_.WriteFile(out_, stamp_file);
-
- out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << GeneralTool::kGeneralToolStamp;
- path_output_.WriteFiles(out_, files);
-
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
- out_ << std::endl;
-}
diff --git a/gn/tools/gn/ninja_target_writer.h b/gn/tools/gn/ninja_target_writer.h
deleted file mode 100644
index b49f33e3a11..00000000000
--- a/gn/tools/gn/ninja_target_writer.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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 TOOLS_GN_NINJA_TARGET_WRITER_H_
-#define TOOLS_GN_NINJA_TARGET_WRITER_H_
-
-#include <iosfwd>
-
-#include "base/macros.h"
-#include "tools/gn/path_output.h"
-#include "tools/gn/substitution_type.h"
-
-class OutputFile;
-class Settings;
-class Target;
-struct SubstitutionBits;
-
-// Generates one target's ".ninja" file. The toplevel "build.ninja" file is
-// generated by the NinjaBuildWriter.
-class NinjaTargetWriter {
- public:
- NinjaTargetWriter(const Target* target, std::ostream& out);
- virtual ~NinjaTargetWriter();
-
- // Returns the build line to be written to the toolchain build file.
- //
- // Some targets have their rules written to separate files, and some can have
- // their rules coalesced in the main build file. For the coalesced case, this
- // function will return the rules as a string. For the separate file case,
- // the separate ninja file will be written and the return string will be the
- // subninja command to load that file.
- static std::string RunAndWriteFile(const Target* target);
-
- virtual void Run() = 0;
-
- protected:
- // Writes out the substitution values that are shared between the different
- // types of tools (target gen dir, target label, etc.). Only the substitutions
- // identified by the given bits will be written.
- void WriteSharedVars(const SubstitutionBits& bits);
-
- // Writes to the output stream a stamp rule for input dependencies, and
- // returns the file to be appended to source rules that encodes the
- // order-only dependencies for the current target.
- // If num_stamp_uses is small, this might return all input dependencies
- // directly, without writing a stamp file.
- // If there are no implicit dependencies and no extra target dependencies
- // are passed in, this returns an empty vector.
- std::vector<OutputFile> WriteInputDepsStampAndGetDep(
- const std::vector<const Target*>& extra_hard_deps,
- size_t num_stamp_uses) const;
-
- // Writes to the output file a final stamp rule for the target that stamps
- // the given list of files. This function assumes the stamp is for the target
- // as a whole so the stamp file is set as the target's dependency output.
- void WriteStampForTarget(const std::vector<OutputFile>& deps,
- const std::vector<OutputFile>& order_only_deps);
-
- const Settings* settings_; // Non-owning.
- const Target* target_; // Non-owning.
- std::ostream& out_;
- PathOutput path_output_;
-
- private:
- void WriteCopyRules();
- void WriteEscapedSubstitution(const Substitution* type);
-
- DISALLOW_COPY_AND_ASSIGN(NinjaTargetWriter);
-};
-
-#endif // TOOLS_GN_NINJA_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_target_writer_unittest.cc b/gn/tools/gn/ninja_target_writer_unittest.cc
deleted file mode 100644
index 3a33192363a..00000000000
--- a/gn/tools/gn/ninja_target_writer_unittest.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// 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 <sstream>
-
-#include "tools/gn/ninja_action_target_writer.h"
-#include "tools/gn/ninja_target_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-class TestingNinjaTargetWriter : public NinjaTargetWriter {
- public:
- TestingNinjaTargetWriter(const Target* target,
- const Toolchain* toolchain,
- std::ostream& out)
- : NinjaTargetWriter(target, out) {}
-
- void Run() override {}
-
- // Make this public so the test can call it.
- std::vector<OutputFile> WriteInputDepsStampAndGetDep(
- const std::vector<const Target*>& extra_hard_deps,
- size_t num_stamp_uses) {
- return NinjaTargetWriter::WriteInputDepsStampAndGetDep(extra_hard_deps,
- num_stamp_uses);
- }
-};
-
-} // namespace
-
-TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDep) {
- TestWithScope setup;
- Err err;
-
- // Make a base target that's a hard dep (action).
- Target base_target(setup.settings(), Label(SourceDir("//foo/"), "base"));
- base_target.set_output_type(Target::ACTION);
- base_target.visibility().SetPublic();
- base_target.SetToolchain(setup.toolchain());
- base_target.action_values().set_script(SourceFile("//foo/script.py"));
-
- // Dependent target that also includes a source prerequisite (should get
- // included) and a source (should not be included).
- Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- target.SetToolchain(setup.toolchain());
- target.config_values().inputs().push_back(SourceFile("//foo/input.txt"));
- target.sources().push_back(SourceFile("//foo/source.txt"));
- target.public_deps().push_back(LabelTargetPair(&base_target));
-
- // Dependent action to test that action sources will be treated the same as
- // inputs.
- Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
- action.set_output_type(Target::ACTION);
- action.visibility().SetPublic();
- action.SetToolchain(setup.toolchain());
- action.action_values().set_script(SourceFile("//foo/script.py"));
- action.sources().push_back(SourceFile("//foo/action_source.txt"));
- action.public_deps().push_back(LabelTargetPair(&target));
-
- ASSERT_TRUE(base_target.OnResolved(&err));
- ASSERT_TRUE(target.OnResolved(&err));
- ASSERT_TRUE(action.OnResolved(&err));
-
- // Input deps for the base (should be only the script itself).
- {
- std::ostringstream stream;
- TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream);
- std::vector<OutputFile> dep =
- writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
-
- // Since there is only one dependency, it should just be returned and
- // nothing written to the stream.
- ASSERT_EQ(1u, dep.size());
- EXPECT_EQ("../../foo/script.py", dep[0].value());
- EXPECT_EQ("", stream.str());
- }
-
- // Input deps for the target (should depend on the base).
- {
- std::ostringstream stream;
- TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
- std::vector<OutputFile> dep =
- writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
-
- // Since there is only one dependency, a stamp file will be returned
- // directly without writing any additional rules.
- ASSERT_EQ(1u, dep.size());
- EXPECT_EQ("obj/foo/base.stamp", dep[0].value());
- }
-
- {
- std::ostringstream stream;
- NinjaActionTargetWriter writer(&action, stream);
- writer.Run();
- EXPECT_EQ(
- "rule __foo_action___rule\n"
- " command = ../../foo/script.py\n"
- " description = ACTION //foo:action()\n"
- " restat = 1\n"
- "\n"
- "build: __foo_action___rule | ../../foo/script.py"
- " ../../foo/action_source.txt ./target\n"
- "\n"
- "build obj/foo/action.stamp: stamp\n",
- stream.str());
- }
-
- // Input deps for action which should depend on the base since its a hard dep
- // that is a (indirect) dependency, as well as the the action source.
- {
- std::ostringstream stream;
- TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream);
- std::vector<OutputFile> dep =
- writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
-
- ASSERT_EQ(1u, dep.size());
- EXPECT_EQ("obj/foo/action.inputdeps.stamp", dep[0].value());
- EXPECT_EQ(
- "build obj/foo/action.inputdeps.stamp: stamp ../../foo/script.py "
- "../../foo/action_source.txt ./target\n",
- stream.str());
- }
-}
-
-// Tests WriteInputDepsStampAndGetDep when toolchain deps are present.
-TEST(NinjaTargetWriter, WriteInputDepsStampAndGetDepWithToolchainDeps) {
- TestWithScope setup;
- Err err;
-
- // Toolchain dependency. Here we make a target in the same toolchain for
- // simplicity, but in real life (using the Builder) this would be rejected
- // because it would be a circular dependency (the target depends on its
- // toolchain, and the toolchain depends on this target).
- Target toolchain_dep_target(setup.settings(),
- Label(SourceDir("//foo/"), "setup"));
- toolchain_dep_target.set_output_type(Target::ACTION);
- toolchain_dep_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(toolchain_dep_target.OnResolved(&err));
- setup.toolchain()->deps().push_back(LabelTargetPair(&toolchain_dep_target));
-
- // Make a binary target
- Target target(setup.settings(), Label(SourceDir("//foo/"), "target"));
- target.set_output_type(Target::EXECUTABLE);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream stream;
- TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
- std::vector<OutputFile> dep =
- writer.WriteInputDepsStampAndGetDep(std::vector<const Target*>(), 10u);
-
- // Since there is more than one dependency, a stamp file will be returned
- // and the rule for the stamp file will be written to the stream.
- ASSERT_EQ(1u, dep.size());
- EXPECT_EQ("obj/foo/setup.stamp", dep[0].value());
- EXPECT_EQ("", stream.str());
-}
diff --git a/gn/tools/gn/ninja_toolchain_writer.cc b/gn/tools/gn/ninja_toolchain_writer.cc
deleted file mode 100644
index 89562773826..00000000000
--- a/gn/tools/gn/ninja_toolchain_writer.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// 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 "tools/gn/ninja_toolchain_writer.h"
-
-#include <fstream>
-
-#include "base/files/file_util.h"
-#include "base/strings/stringize_macros.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/c_tool.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/ninja_utils.h"
-#include "tools/gn/pool.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/trace.h"
-
-namespace {
-
-const char kIndent[] = " ";
-
-} // namespace
-
-NinjaToolchainWriter::NinjaToolchainWriter(const Settings* settings,
- const Toolchain* toolchain,
- std::ostream& out)
- : settings_(settings),
- toolchain_(toolchain),
- out_(out),
- path_output_(settings_->build_settings()->build_dir(),
- settings_->build_settings()->root_path_utf8(),
- ESCAPE_NINJA) {}
-
-NinjaToolchainWriter::~NinjaToolchainWriter() = default;
-
-void NinjaToolchainWriter::Run(
- const std::vector<NinjaWriter::TargetRulePair>& rules) {
- std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
-
- for (const auto& tool : toolchain_->tools()) {
- if (tool.second->name() == GeneralTool::kGeneralToolAction)
- continue;
- WriteToolRule(tool.second.get(), rule_prefix);
- }
- out_ << std::endl;
-
- for (const auto& pair : rules)
- out_ << pair.second;
-}
-
-// static
-bool NinjaToolchainWriter::RunAndWriteFile(
- const Settings* settings,
- const Toolchain* toolchain,
- const std::vector<NinjaWriter::TargetRulePair>& rules) {
- base::FilePath ninja_file(settings->build_settings()->GetFullPath(
- GetNinjaFileForToolchain(settings)));
- ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, FilePathToUTF8(ninja_file));
-
- base::CreateDirectory(ninja_file.DirName());
-
- std::ofstream file;
- file.open(FilePathToUTF8(ninja_file).c_str(),
- std::ios_base::out | std::ios_base::binary);
- if (file.fail())
- return false;
-
- NinjaToolchainWriter gen(settings, toolchain, file);
- gen.Run(rules);
- return true;
-}
-
-void NinjaToolchainWriter::WriteToolRule(Tool* tool,
- const std::string& rule_prefix) {
- out_ << "rule " << rule_prefix << tool->name() << std::endl;
-
- // Rules explicitly include shell commands, so don't try to escape.
- EscapeOptions options;
- options.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
-
- WriteCommandRulePattern("command", tool->command_launcher(), tool->command(), options);
-
- WriteRulePattern("description", tool->description(), options);
- WriteRulePattern("rspfile", tool->rspfile(), options);
- WriteRulePattern("rspfile_content", tool->rspfile_content(), options);
-
- if (CTool* c_tool = tool->AsC()) {
- if (c_tool->depsformat() == CTool::DEPS_GCC) {
- // GCC-style deps require a depfile.
- if (!tool->depfile().empty()) {
- WriteRulePattern("depfile", tool->depfile(), options);
- out_ << kIndent << "deps = gcc" << std::endl;
- }
- } else if (c_tool->depsformat() == CTool::DEPS_MSVC) {
- // MSVC deps don't have a depfile.
- out_ << kIndent << "deps = msvc" << std::endl;
- }
- } else if (!tool->depfile().empty()) {
- WriteRulePattern("depfile", tool->depfile(), options);
- out_ << kIndent << "deps = gcc" << std::endl;
- }
-
- // Use pool is specified.
- if (tool->pool().ptr) {
- std::string pool_name =
- tool->pool().ptr->GetNinjaName(settings_->default_toolchain_label());
- out_ << kIndent << "pool = " << pool_name << std::endl;
- }
-
- if (tool->restat())
- out_ << kIndent << "restat = 1" << std::endl;
-}
-
-void NinjaToolchainWriter::WriteRulePattern(const char* name,
- const SubstitutionPattern& pattern,
- const EscapeOptions& options) {
- if (pattern.empty())
- return;
- out_ << kIndent << name << " = ";
- SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out_);
- out_ << std::endl;
-}
-
-void NinjaToolchainWriter::WriteCommandRulePattern(
- const char* name,
- const std::string& launcher,
- const SubstitutionPattern& command,
- const EscapeOptions& options) {
- CHECK(!command.empty()) << "Command should not be empty";
- if (command.empty())
- return;
- out_ << kIndent << name << " = " ;
- if (!launcher.empty())
- out_ << launcher << " ";
- SubstitutionWriter::WriteWithNinjaVariables(command, options, out_);
- out_ << std::endl;
-}
diff --git a/gn/tools/gn/ninja_toolchain_writer.h b/gn/tools/gn/ninja_toolchain_writer.h
deleted file mode 100644
index b9b9906c48d..00000000000
--- a/gn/tools/gn/ninja_toolchain_writer.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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 TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
-#define TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
-
-#include <iosfwd>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "tools/gn/ninja_writer.h"
-#include "tools/gn/path_output.h"
-#include "tools/gn/toolchain.h"
-
-struct EscapeOptions;
-class Settings;
-class Tool;
-
-class NinjaToolchainWriter {
- public:
- // Takes the settings for the toolchain, as well as the list of all targets
- // associated with the toolchain.
- static bool RunAndWriteFile(
- const Settings* settings,
- const Toolchain* toolchain,
- const std::vector<NinjaWriter::TargetRulePair>& rules);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRule);
- FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRuleWithLauncher);
-
- NinjaToolchainWriter(const Settings* settings,
- const Toolchain* toolchain,
- std::ostream& out);
- ~NinjaToolchainWriter();
-
- void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
-
- void WriteRules();
- void WriteToolRule(Tool* tool, const std::string& rule_prefix);
- void WriteRulePattern(const char* name,
- const SubstitutionPattern& pattern,
- const EscapeOptions& options);
- void WriteCommandRulePattern(const char* name,
- const std::string& launcher,
- const SubstitutionPattern& command,
- const EscapeOptions& options);
-
- const Settings* settings_;
- const Toolchain* toolchain_;
- std::ostream& out_;
- PathOutput path_output_;
-
- DISALLOW_COPY_AND_ASSIGN(NinjaToolchainWriter);
-};
-
-#endif // TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
diff --git a/gn/tools/gn/ninja_toolchain_writer_unittest.cc b/gn/tools/gn/ninja_toolchain_writer_unittest.cc
deleted file mode 100644
index ce1c533016e..00000000000
--- a/gn/tools/gn/ninja_toolchain_writer_unittest.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 <sstream>
-
-#include "tools/gn/ninja_toolchain_writer.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(NinjaToolchainWriter, WriteToolRule) {
- TestWithScope setup;
-
- std::ostringstream stream;
- NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
- writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCc),
- std::string("prefix_"));
-
- EXPECT_EQ(
- "rule prefix_cc\n"
- " command = cc ${in} ${cflags} ${cflags_c} ${defines} ${include_dirs} "
- "-o ${out}\n",
- stream.str());
-}
-
-TEST(NinjaToolchainWriter, WriteToolRuleWithLauncher) {
- TestWithScope setup;
-
- std::ostringstream stream;
- NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
- writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCxx),
- std::string("prefix_"));
-
- EXPECT_EQ(
- "rule prefix_cxx\n"
- " command = launcher c++ ${in} ${cflags} ${cflags_cc} ${defines} ${include_dirs} "
- "-o ${out}\n",
- stream.str());
-}
diff --git a/gn/tools/gn/ninja_utils.cc b/gn/tools/gn/ninja_utils.cc
deleted file mode 100644
index 98f320517f9..00000000000
--- a/gn/tools/gn/ninja_utils.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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 "tools/gn/ninja_utils.h"
-
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target.h"
-
-SourceFile GetNinjaFileForTarget(const Target* target) {
- return SourceFile(
- GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ).value() +
- target->label().name() + ".ninja");
-}
-
-SourceFile GetNinjaFileForToolchain(const Settings* settings) {
- return SourceFile(GetBuildDirAsSourceDir(BuildDirContext(settings),
- BuildDirType::TOOLCHAIN_ROOT)
- .value() +
- "toolchain.ninja");
-}
-
-std::string GetNinjaRulePrefixForToolchain(const Settings* settings) {
- // Don't prefix the default toolchain so it looks prettier, prefix everything
- // else.
- if (settings->is_default())
- return std::string(); // Default toolchain has no prefix.
- return settings->toolchain_label().name() + "_";
-}
diff --git a/gn/tools/gn/ninja_writer.cc b/gn/tools/gn/ninja_writer.cc
deleted file mode 100644
index c6d65ac2945..00000000000
--- a/gn/tools/gn/ninja_writer.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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 "tools/gn/ninja_writer.h"
-
-#include "tools/gn/builder.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/location.h"
-#include "tools/gn/ninja_build_writer.h"
-#include "tools/gn/ninja_toolchain_writer.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target.h"
-
-NinjaWriter::NinjaWriter(const Builder& builder) : builder_(builder) {}
-
-NinjaWriter::~NinjaWriter() = default;
-
-// static
-bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings,
- const Builder& builder,
- const PerToolchainRules& per_toolchain_rules,
- Err* err) {
- NinjaWriter writer(builder);
-
- if (!writer.WriteToolchains(per_toolchain_rules, err))
- return false;
- return NinjaBuildWriter::RunAndWriteFile(build_settings, builder, err);
-}
-
-bool NinjaWriter::WriteToolchains(const PerToolchainRules& per_toolchain_rules,
- Err* err) {
- if (per_toolchain_rules.empty()) {
- Err(Location(), "No targets.",
- "I could not find any targets to write, so I'm doing nothing.")
- .PrintToStdout();
- return false;
- }
-
- for (const auto& i : per_toolchain_rules) {
- const Toolchain* toolchain = i.first;
- const Settings* settings =
- builder_.loader()->GetToolchainSettings(toolchain->label());
- if (!NinjaToolchainWriter::RunAndWriteFile(settings, toolchain, i.second)) {
- Err(Location(), "Couldn't open toolchain buildfile(s) for writing")
- .PrintToStdout();
- return false;
- }
- }
-
- return true;
-}
diff --git a/gn/tools/gn/operators.cc b/gn/tools/gn/operators.cc
deleted file mode 100644
index 40ea900978a..00000000000
--- a/gn/tools/gn/operators.cc
+++ /dev/null
@@ -1,785 +0,0 @@
-// 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 "tools/gn/operators.h"
-
-#include <stddef.h>
-#include <algorithm>
-
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/err.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/token.h"
-#include "tools/gn/value.h"
-
-namespace {
-
-const char kSourcesName[] = "sources";
-
-// Helper class used for assignment operations: =, +=, and -= to generalize
-// writing to various types of destinations.
-class ValueDestination {
- public:
- ValueDestination();
-
- bool Init(Scope* exec_scope,
- const ParseNode* dest,
- const BinaryOpNode* op_node,
- Err* err);
-
- // Returns the value in the destination scope if it already exists, or null
- // if it doesn't. This is for validation and does not count as a "use".
- // Other nested scopes will be searched.
- const Value* GetExistingValue() const;
-
- // Returns an existing version of the output if it can be modified. This will
- // not search nested scopes since writes only go into the current scope.
- // Returns null if the value does not exist, or is not in the current scope
- // (meaning assignments won't go to this value and it's not mutable). This
- // is for implementing += and -=.
- //
- // If it exists, this will mark the origin of the value to be the passed-in
- // node, and the value will be also marked unused (if possible) under the
- // assumption that it will be modified in-place.
- Value* GetExistingMutableValueIfExists(const ParseNode* origin);
-
- // Returns the sources assignment filter if it exists for the current
- // scope and it should be applied to this assignment. Otherwise returns null.
- const PatternList* GetAssignmentFilter(const Scope* exec_scope) const;
-
- // Returns a pointer to the value that was set.
- Value* SetValue(Value value, const ParseNode* set_node);
-
- // Fills the Err with an undefined value error appropriate for modification
- // operators: += and -= (where the source is also the dest).
- void MakeUndefinedIdentifierForModifyError(Err* err);
-
- private:
- enum Type { UNINITIALIZED, SCOPE, LIST };
-
- Type type_;
-
- // Valid when type_ == SCOPE.
- Scope* scope_;
- const Token* name_token_;
-
- // Valid when type_ == LIST.
- Value* list_;
- size_t index_; // Guaranteed in-range when Init() succeeds.
-};
-
-ValueDestination::ValueDestination()
- : type_(UNINITIALIZED),
- scope_(nullptr),
- name_token_(nullptr),
- list_(nullptr),
- index_(0) {}
-
-bool ValueDestination::Init(Scope* exec_scope,
- const ParseNode* dest,
- const BinaryOpNode* op_node,
- Err* err) {
- // Check for standard variable set.
- const IdentifierNode* dest_identifier = dest->AsIdentifier();
- if (dest_identifier) {
- type_ = SCOPE;
- scope_ = exec_scope;
- name_token_ = &dest_identifier->value();
- return true;
- }
-
- // Check for array and scope accesses. The base (array or scope variable
- // name) must always be defined ahead of time.
- const AccessorNode* dest_accessor = dest->AsAccessor();
- if (!dest_accessor) {
- *err = Err(op_node, "Assignment requires a lvalue.",
- "This thing on the left is not an identifier or accessor.");
- err->AppendRange(dest->GetRange());
- return false;
- }
-
- // Known to be an accessor.
- base::StringPiece base_str = dest_accessor->base().value();
- Value* base =
- exec_scope->GetMutableValue(base_str, Scope::SEARCH_CURRENT, false);
- if (!base) {
- // Base is either undefined or it's defined but not in the current scope.
- // Make a good error message.
- if (exec_scope->GetValue(base_str, false)) {
- *err = Err(
- dest_accessor->base(), "Suspicious in-place modification.",
- "This variable exists in a containing scope. Normally, writing to it "
- "would\nmake a copy of it into the current scope with the modified "
- "version. But\nhere you're modifying only an element of a scope or "
- "list object. It's unlikely\nyou meant to copy the entire thing just "
- "to modify this part of it.\n"
- "\n"
- "If you really wanted to do this, do:\n"
- " " +
- base_str.as_string() + " = " + base_str.as_string() +
- "\n"
- "to copy it into the current scope before doing this operation.");
- } else {
- *err = Err(dest_accessor->base(), "Undefined identifier.");
- }
- return false;
- }
-
- if (dest_accessor->index()) {
- // List access with an index.
- if (!base->VerifyTypeIs(Value::LIST, err)) {
- // Errors here will confusingly refer to the variable declaration (since
- // that's all Value knows) rather than the list access. So rewrite the
- // error location to refer to the base value's location.
- *err = Err(dest_accessor->base(), err->message(), err->help_text());
- return false;
- }
-
- type_ = LIST;
- list_ = base;
- return dest_accessor->ComputeAndValidateListIndex(
- exec_scope, base->list_value().size(), &index_, err);
- }
-
- // Scope access with a dot.
- if (!base->VerifyTypeIs(Value::SCOPE, err)) {
- // As the for the list index case above, rewrite the error location.
- *err = Err(dest_accessor->base(), err->message(), err->help_text());
- return false;
- }
- type_ = SCOPE;
- scope_ = base->scope_value();
- name_token_ = &dest_accessor->member()->value();
- return true;
-}
-
-const Value* ValueDestination::GetExistingValue() const {
- if (type_ == SCOPE)
- return scope_->GetValue(name_token_->value(), true);
- else if (type_ == LIST)
- return &list_->list_value()[index_];
- return nullptr;
-}
-
-Value* ValueDestination::GetExistingMutableValueIfExists(
- const ParseNode* origin) {
- if (type_ == SCOPE) {
- Value* value = scope_->GetMutableValue(name_token_->value(),
- Scope::SEARCH_CURRENT, false);
- if (value) {
- // The value will be written to, reset its tracking information.
- value->set_origin(origin);
- scope_->MarkUnused(name_token_->value());
- }
- }
- if (type_ == LIST)
- return &list_->list_value()[index_];
- return nullptr;
-}
-
-const PatternList* ValueDestination::GetAssignmentFilter(
- const Scope* exec_scope) const {
- if (type_ != SCOPE)
- return nullptr; // Destination can't be named, so no sources filtering.
- if (name_token_->value() != kSourcesName)
- return nullptr; // Destination not named "sources".
-
- const PatternList* filter = exec_scope->GetSourcesAssignmentFilter();
- if (!filter || filter->is_empty())
- return nullptr; // No filter or empty filter, don't need to do anything.
- return filter;
-}
-
-Value* ValueDestination::SetValue(Value value, const ParseNode* set_node) {
- if (type_ == SCOPE) {
- return scope_->SetValue(name_token_->value(), std::move(value), set_node);
- } else if (type_ == LIST) {
- Value* dest = &list_->list_value()[index_];
- *dest = std::move(value);
- return dest;
- }
- return nullptr;
-}
-
-void ValueDestination::MakeUndefinedIdentifierForModifyError(Err* err) {
- // When Init() succeeds, the base of any accessor has already been resolved
- // and that list indices are in-range. This means any undefined identifiers
- // are for scope accesses.
- DCHECK(type_ == SCOPE);
- *err = Err(*name_token_, "Undefined identifier.");
-}
-
-// Computes an error message for overwriting a nonempty list/scope with another.
-Err MakeOverwriteError(const BinaryOpNode* op_node, const Value& old_value) {
- std::string type_name;
- std::string empty_def;
-
- if (old_value.type() == Value::LIST) {
- type_name = "list";
- empty_def = "[]";
- } else if (old_value.type() == Value::SCOPE) {
- type_name = "scope";
- empty_def = "{}";
- } else {
- NOTREACHED();
- }
-
- Err result(op_node->left()->GetRange(),
- "Replacing nonempty " + type_name + ".",
- "This overwrites a previously-defined nonempty " + type_name +
- " with another nonempty " + type_name + ".");
- result.AppendSubErr(Err(
- old_value, "for previous definition",
- "Did you mean to append/modify instead? If you really want to overwrite, "
- "do:\n"
- " foo = " +
- empty_def + "\nbefore reassigning."));
- return result;
-}
-
-// -----------------------------------------------------------------------------
-
-Err MakeIncompatibleTypeError(const BinaryOpNode* op_node,
- const Value& left,
- const Value& right) {
- std::string msg = std::string("You can't do <") +
- Value::DescribeType(left.type()) + "> " +
- op_node->op().value().as_string() + " <" +
- Value::DescribeType(right.type()) + ">.";
- if (left.type() == Value::LIST) {
- // Append extra hint for list stuff.
- msg +=
- "\n\nHint: If you're attempting to add or remove a single item from "
- " a list, use \"foo + [ bar ]\".";
- }
- return Err(op_node, "Incompatible types for binary operator.", msg);
-}
-
-Value GetValueOrFillError(const BinaryOpNode* op_node,
- const ParseNode* node,
- const char* name,
- Scope* scope,
- Err* err) {
- Value value = node->Execute(scope, err);
- if (err->has_error())
- return Value();
- if (value.type() == Value::NONE) {
- *err = Err(op_node->op(), "Operator requires a value.",
- "This thing on the " + std::string(name) +
- " does not evaluate to a value.");
- err->AppendRange(node->GetRange());
- return Value();
- }
- return value;
-}
-
-void RemoveMatchesFromList(const BinaryOpNode* op_node,
- Value* list,
- const Value& to_remove,
- Err* err) {
- std::vector<Value>& v = list->list_value();
- switch (to_remove.type()) {
- case Value::BOOLEAN:
- case Value::INTEGER: // Filter out the individual int/string.
- case Value::STRING:
- case Value::SCOPE: {
- bool found_match = false;
- for (size_t i = 0; i < v.size(); /* nothing */) {
- if (v[i] == to_remove) {
- found_match = true;
- v.erase(v.begin() + i);
- } else {
- i++;
- }
- }
- if (!found_match) {
- *err = Err(to_remove.origin()->GetRange(), "Item not found",
- "You were trying to remove " + to_remove.ToString(true) +
- "\nfrom the list but it wasn't there.");
- }
- break;
- }
-
- case Value::LIST: // Filter out each individual thing.
- for (const auto& elem : to_remove.list_value()) {
- // TODO(brettw) if the nested item is a list, we may want to search
- // for the literal list rather than remote the items in it.
- RemoveMatchesFromList(op_node, list, elem, err);
- if (err->has_error())
- return;
- }
- break;
-
- case Value::NONE:
- break;
- }
-}
-
-// Assignment -----------------------------------------------------------------
-
-// We return a null value from this rather than the result of doing the append.
-// See ValuePlusEquals for rationale.
-Value ExecuteEquals(Scope* exec_scope,
- const BinaryOpNode* op_node,
- ValueDestination* dest,
- Value right,
- Err* err) {
- const Value* old_value = dest->GetExistingValue();
- if (old_value) {
- // Check for overwriting nonempty scopes or lists with other nonempty
- // scopes or lists. This prevents mistakes that clobber a value rather than
- // appending to it. For cases where a user meant to clear a value, allow
- // overwriting a nonempty list/scope with an empty one, which can then be
- // modified.
- if (old_value->type() == Value::LIST && right.type() == Value::LIST &&
- !old_value->list_value().empty() && !right.list_value().empty()) {
- *err = MakeOverwriteError(op_node, *old_value);
- return Value();
- } else if (old_value->type() == Value::SCOPE &&
- right.type() == Value::SCOPE &&
- old_value->scope_value()->HasValues(Scope::SEARCH_CURRENT) &&
- right.scope_value()->HasValues(Scope::SEARCH_CURRENT)) {
- *err = MakeOverwriteError(op_node, *old_value);
- return Value();
- }
- }
-
- Value* written_value = dest->SetValue(std::move(right), op_node->right());
-
- // Optionally apply the assignment filter in-place.
- const PatternList* filter = dest->GetAssignmentFilter(exec_scope);
- if (filter && written_value->type() == Value::LIST) {
- std::vector<Value>& list_value = written_value->list_value();
- auto first_deleted = std::remove_if(
- list_value.begin(), list_value.end(),
- [filter](const Value& v) { return filter->MatchesValue(v); });
- list_value.erase(first_deleted, list_value.end());
- }
- return Value();
-}
-
-// Plus/minus ------------------------------------------------------------------
-
-// allow_left_type_conversion indicates if we're allowed to change the type of
-// the left value. This is set to true when doing +, and false when doing +=.
-Value ExecutePlus(const BinaryOpNode* op_node,
- Value left,
- Value right,
- bool allow_left_type_conversion,
- Err* err) {
- // Left-hand-side integer.
- if (left.type() == Value::INTEGER) {
- if (right.type() == Value::INTEGER) {
- // Int + int -> addition.
- return Value(op_node, left.int_value() + right.int_value());
- } else if (right.type() == Value::STRING && allow_left_type_conversion) {
- // Int + string -> string concat.
- return Value(op_node, base::Int64ToString(left.int_value()) +
- right.string_value());
- }
- *err = MakeIncompatibleTypeError(op_node, left, right);
- return Value();
- }
-
- // Left-hand-side string.
- if (left.type() == Value::STRING) {
- if (right.type() == Value::INTEGER) {
- // String + int -> string concat.
- return Value(op_node, left.string_value() +
- base::Int64ToString(right.int_value()));
- } else if (right.type() == Value::STRING) {
- // String + string -> string concat. Since the left is passed by copy
- // we can avoid realloc if there is enough buffer by appending to left
- // and assigning.
- left.string_value().append(right.string_value());
- return left; // FIXME(brettw) des this copy?
- }
- *err = MakeIncompatibleTypeError(op_node, left, right);
- return Value();
- }
-
- // Left-hand-side list. The only valid thing is to add another list.
- if (left.type() == Value::LIST && right.type() == Value::LIST) {
- // Since left was passed by copy, avoid realloc by destructively appending
- // to it and using that as the result.
- for (Value& value : right.list_value())
- left.list_value().push_back(std::move(value));
- return left; // FIXME(brettw) does this copy?
- }
-
- *err = MakeIncompatibleTypeError(op_node, left, right);
- return Value();
-}
-
-// Left is passed by value because it will be modified in-place and returned
-// for the list case.
-Value ExecuteMinus(const BinaryOpNode* op_node,
- Value left,
- const Value& right,
- Err* err) {
- // Left-hand-side int. The only thing to do is subtract another int.
- if (left.type() == Value::INTEGER && right.type() == Value::INTEGER) {
- // Int - int -> subtraction.
- return Value(op_node, left.int_value() - right.int_value());
- }
-
- // Left-hand-side list. The only thing to do is subtract another list.
- if (left.type() == Value::LIST && right.type() == Value::LIST) {
- // In-place modify left and return it.
- RemoveMatchesFromList(op_node, &left, right, err);
- return left;
- }
-
- *err = MakeIncompatibleTypeError(op_node, left, right);
- return Value();
-}
-
-// In-place plus/minus ---------------------------------------------------------
-
-void ExecutePlusEquals(Scope* exec_scope,
- const BinaryOpNode* op_node,
- ValueDestination* dest,
- Value right,
- Err* err) {
- // There are several cases. Some things we can convert "foo += bar" to
- // "foo = foo + bar". Some cases we can't (the 'sources' variable won't
- // get the right filtering on the list). Some cases we don't want to (lists
- // and strings will get unnecessary copying so we can to optimize these).
- //
- // - Value is already mutable in the current scope:
- // 1. List/string append: use it.
- // 2. Other types: fall back to "foo = foo + bar"
- //
- // - Value is not mutable in the current scope:
- // 3. List/string append: copy into current scope and append to that.
- // 4. Other types: fall back to "foo = foo + bar"
- //
- // The common case is to use += for list and string appends in the local
- // scope, so this is written to avoid multiple variable lookups in that case.
- Value* mutable_dest = dest->GetExistingMutableValueIfExists(op_node);
- if (!mutable_dest) {
- const Value* existing_value = dest->GetExistingValue();
- if (!existing_value) {
- // Undefined left-hand-size for +=.
- dest->MakeUndefinedIdentifierForModifyError(err);
- return;
- }
-
- if (existing_value->type() != Value::STRING &&
- existing_value->type() != Value::LIST) {
- // Case #4 above.
- dest->SetValue(
- ExecutePlus(op_node, *existing_value, std::move(right), false, err),
- op_node);
- return;
- }
-
- // Case #3 above, copy to current scope and fall-through to appending.
- mutable_dest = dest->SetValue(*existing_value, op_node);
- } else if (mutable_dest->type() != Value::STRING &&
- mutable_dest->type() != Value::LIST) {
- // Case #2 above.
- dest->SetValue(
- ExecutePlus(op_node, *mutable_dest, std::move(right), false, err),
- op_node);
- return;
- } // "else" is case #1 above.
-
- if (mutable_dest->type() == Value::STRING) {
- if (right.type() == Value::INTEGER) {
- // String + int -> string concat.
- mutable_dest->string_value().append(
- base::Int64ToString(right.int_value()));
- } else if (right.type() == Value::STRING) {
- // String + string -> string concat.
- mutable_dest->string_value().append(right.string_value());
- } else {
- *err = MakeIncompatibleTypeError(op_node, *mutable_dest, right);
- }
- } else if (mutable_dest->type() == Value::LIST) {
- // List concat.
- if (right.type() == Value::LIST) {
- // Note: don't reserve() the dest vector here since that actually hurts
- // the allocation pattern when the build script is doing multiple small
- // additions.
- const PatternList* filter = dest->GetAssignmentFilter(exec_scope);
- if (filter) {
- // Filtered list concat.
- for (Value& value : right.list_value()) {
- if (!filter->MatchesValue(value))
- mutable_dest->list_value().push_back(std::move(value));
- }
- } else {
- // Normal list concat. This is a destructive move.
- for (Value& value : right.list_value())
- mutable_dest->list_value().push_back(std::move(value));
- }
- } else {
- *err = Err(op_node->op(), "Incompatible types to add.",
- "To append a single item to a list do \"foo += [ bar ]\".");
- }
- }
-}
-
-void ExecuteMinusEquals(const BinaryOpNode* op_node,
- ValueDestination* dest,
- const Value& right,
- Err* err) {
- // Like the += case, we can convert "foo -= bar" to "foo = foo - bar". Since
- // there is no sources filtering, this is always semantically valid. The
- // only case we don't do it is for lists in the current scope which is the
- // most common case, and also the one that can be optimized the most by
- // doing it in-place.
- Value* mutable_dest = dest->GetExistingMutableValueIfExists(op_node);
- if (!mutable_dest ||
- (mutable_dest->type() != Value::LIST || right.type() != Value::LIST)) {
- const Value* existing_value = dest->GetExistingValue();
- if (!existing_value) {
- // Undefined left-hand-size for -=.
- dest->MakeUndefinedIdentifierForModifyError(err);
- return;
- }
- dest->SetValue(ExecuteMinus(op_node, *existing_value, right, err), op_node);
- return;
- }
-
- // In-place removal of items from "right".
- RemoveMatchesFromList(op_node, mutable_dest, right, err);
-}
-
-// Comparison -----------------------------------------------------------------
-
-Value ExecuteEqualsEquals(Scope* scope,
- const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- if (left == right)
- return Value(op_node, true);
- return Value(op_node, false);
-}
-
-Value ExecuteNotEquals(Scope* scope,
- const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- // Evaluate in terms of ==.
- Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
- result.boolean_value() = !result.boolean_value();
- return result;
-}
-
-Value FillNeedsTwoIntegersError(const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- *err = Err(op_node, "Comparison requires two integers.",
- "This operator can only compare two integers.");
- err->AppendRange(left.origin()->GetRange());
- err->AppendRange(right.origin()->GetRange());
- return Value();
-}
-
-Value ExecuteLessEquals(Scope* scope,
- const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
- return FillNeedsTwoIntegersError(op_node, left, right, err);
- return Value(op_node, left.int_value() <= right.int_value());
-}
-
-Value ExecuteGreaterEquals(Scope* scope,
- const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
- return FillNeedsTwoIntegersError(op_node, left, right, err);
- return Value(op_node, left.int_value() >= right.int_value());
-}
-
-Value ExecuteGreater(Scope* scope,
- const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
- return FillNeedsTwoIntegersError(op_node, left, right, err);
- return Value(op_node, left.int_value() > right.int_value());
-}
-
-Value ExecuteLess(Scope* scope,
- const BinaryOpNode* op_node,
- const Value& left,
- const Value& right,
- Err* err) {
- if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
- return FillNeedsTwoIntegersError(op_node, left, right, err);
- return Value(op_node, left.int_value() < right.int_value());
-}
-
-// Binary ----------------------------------------------------------------------
-
-Value ExecuteOr(Scope* scope,
- const BinaryOpNode* op_node,
- const ParseNode* left_node,
- const ParseNode* right_node,
- Err* err) {
- Value left = GetValueOrFillError(op_node, left_node, "left", scope, err);
- if (err->has_error())
- return Value();
- if (left.type() != Value::BOOLEAN) {
- *err = Err(op_node->left(), "Left side of || operator is not a boolean.",
- "Type is \"" + std::string(Value::DescribeType(left.type())) +
- "\" instead.");
- return Value();
- }
- if (left.boolean_value())
- return Value(op_node, left.boolean_value());
-
- Value right = GetValueOrFillError(op_node, right_node, "right", scope, err);
- if (err->has_error())
- return Value();
- if (right.type() != Value::BOOLEAN) {
- *err = Err(op_node->right(), "Right side of || operator is not a boolean.",
- "Type is \"" + std::string(Value::DescribeType(right.type())) +
- "\" instead.");
- return Value();
- }
-
- return Value(op_node, left.boolean_value() || right.boolean_value());
-}
-
-Value ExecuteAnd(Scope* scope,
- const BinaryOpNode* op_node,
- const ParseNode* left_node,
- const ParseNode* right_node,
- Err* err) {
- Value left = GetValueOrFillError(op_node, left_node, "left", scope, err);
- if (err->has_error())
- return Value();
- if (left.type() != Value::BOOLEAN) {
- *err = Err(op_node->left(), "Left side of && operator is not a boolean.",
- "Type is \"" + std::string(Value::DescribeType(left.type())) +
- "\" instead.");
- return Value();
- }
- if (!left.boolean_value())
- return Value(op_node, left.boolean_value());
-
- Value right = GetValueOrFillError(op_node, right_node, "right", scope, err);
- if (err->has_error())
- return Value();
- if (right.type() != Value::BOOLEAN) {
- *err = Err(op_node->right(), "Right side of && operator is not a boolean.",
- "Type is \"" + std::string(Value::DescribeType(right.type())) +
- "\" instead.");
- return Value();
- }
- return Value(op_node, left.boolean_value() && right.boolean_value());
-}
-
-} // namespace
-
-// ----------------------------------------------------------------------------
-
-Value ExecuteUnaryOperator(Scope* scope,
- const UnaryOpNode* op_node,
- const Value& expr,
- Err* err) {
- DCHECK(op_node->op().type() == Token::BANG);
-
- if (expr.type() != Value::BOOLEAN) {
- *err = Err(op_node, "Operand of ! operator is not a boolean.",
- "Type is \"" + std::string(Value::DescribeType(expr.type())) +
- "\" instead.");
- return Value();
- }
- // TODO(scottmg): Why no unary minus?
- return Value(op_node, !expr.boolean_value());
-}
-
-Value ExecuteBinaryOperator(Scope* scope,
- const BinaryOpNode* op_node,
- const ParseNode* left,
- const ParseNode* right,
- Err* err) {
- const Token& op = op_node->op();
-
- // First handle the ones that take an lvalue.
- if (op.type() == Token::EQUAL || op.type() == Token::PLUS_EQUALS ||
- op.type() == Token::MINUS_EQUALS) {
- // Compute the left side.
- ValueDestination dest;
- if (!dest.Init(scope, left, op_node, err))
- return Value();
-
- // Compute the right side.
- Value right_value = right->Execute(scope, err);
- if (err->has_error())
- return Value();
- if (right_value.type() == Value::NONE) {
- *err = Err(op, "Operator requires a rvalue.",
- "This thing on the right does not evaluate to a value.");
- err->AppendRange(right->GetRange());
- return Value();
- }
-
- // "foo += bar" (same for "-=") is converted to "foo = foo + bar" here, but
- // we pass the original value of "foo" by pointer to avoid a copy.
- if (op.type() == Token::EQUAL) {
- ExecuteEquals(scope, op_node, &dest, std::move(right_value), err);
- } else if (op.type() == Token::PLUS_EQUALS) {
- ExecutePlusEquals(scope, op_node, &dest, std::move(right_value), err);
- } else if (op.type() == Token::MINUS_EQUALS) {
- ExecuteMinusEquals(op_node, &dest, right_value, err);
- } else {
- NOTREACHED();
- }
- return Value();
- }
-
- // ||, &&. Passed the node instead of the value so that they can avoid
- // evaluating the RHS on early-out.
- if (op.type() == Token::BOOLEAN_OR)
- return ExecuteOr(scope, op_node, left, right, err);
- if (op.type() == Token::BOOLEAN_AND)
- return ExecuteAnd(scope, op_node, left, right, err);
-
- // Everything else works on the evaluated left and right values.
- Value left_value = GetValueOrFillError(op_node, left, "left", scope, err);
- if (err->has_error())
- return Value();
- Value right_value = GetValueOrFillError(op_node, right, "right", scope, err);
- if (err->has_error())
- return Value();
-
- // +, -.
- if (op.type() == Token::MINUS)
- return ExecuteMinus(op_node, std::move(left_value), right_value, err);
- if (op.type() == Token::PLUS) {
- return ExecutePlus(op_node, std::move(left_value), std::move(right_value),
- true, err);
- }
-
- // Comparisons.
- if (op.type() == Token::EQUAL_EQUAL)
- return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
- if (op.type() == Token::NOT_EQUAL)
- return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
- if (op.type() == Token::GREATER_EQUAL)
- return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
- if (op.type() == Token::LESS_EQUAL)
- return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
- if (op.type() == Token::GREATER_THAN)
- return ExecuteGreater(scope, op_node, left_value, right_value, err);
- if (op.type() == Token::LESS_THAN)
- return ExecuteLess(scope, op_node, left_value, right_value, err);
-
- return Value();
-}
diff --git a/gn/tools/gn/operators_unittest.cc b/gn/tools/gn/operators_unittest.cc
deleted file mode 100644
index 2569bc56601..00000000000
--- a/gn/tools/gn/operators_unittest.cc
+++ /dev/null
@@ -1,424 +0,0 @@
-// 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 "tools/gn/operators.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/pattern.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-bool IsValueIntegerEqualing(const Value& v, int64_t i) {
- if (v.type() != Value::INTEGER)
- return false;
- return v.int_value() == i;
-}
-
-bool IsValueStringEqualing(const Value& v, const char* s) {
- if (v.type() != Value::STRING)
- return false;
- return v.string_value() == s;
-}
-
-// This parse node is for passing to tests. It returns a canned value for
-// Execute().
-class TestParseNode : public ParseNode {
- public:
- TestParseNode(const Value& v) : value_(v) {}
-
- Value Execute(Scope* scope, Err* err) const override { return value_; }
- LocationRange GetRange() const override { return LocationRange(); }
- Err MakeErrorDescribing(const std::string& msg,
- const std::string& help) const override {
- return Err(this, msg);
- }
- base::Value GetJSONNode() const override {
- return base::Value();
- }
-
- private:
- Value value_;
-};
-
-// Sets up a BinaryOpNode for testing.
-class TestBinaryOpNode : public BinaryOpNode {
- public:
- // Input token value string must outlive class.
- TestBinaryOpNode(Token::Type op_token_type, const char* op_token_value)
- : BinaryOpNode(),
- op_token_ownership_(Location(), op_token_type, op_token_value) {
- set_op(op_token_ownership_);
- }
-
- void SetLeftToValue(const Value& value) {
- set_left(std::make_unique<TestParseNode>(value));
- }
-
- // Sets the left-hand side of the operator to an identifier node, this is
- // used for testing assignments. Input string must outlive class.
- void SetLeftToIdentifier(const char* identifier) {
- left_identifier_token_ownership_ =
- Token(Location(), Token::IDENTIFIER, identifier);
- set_left(
- std::make_unique<IdentifierNode>(left_identifier_token_ownership_));
- }
-
- void SetRightToValue(const Value& value) {
- set_right(std::make_unique<TestParseNode>(value));
- }
- void SetRightToListOfValue(const Value& value) {
- Value list(nullptr, Value::LIST);
- list.list_value().push_back(value);
- set_right(std::make_unique<TestParseNode>(list));
- }
- void SetRightToListOfValue(const Value& value1, const Value& value2) {
- Value list(nullptr, Value::LIST);
- list.list_value().push_back(value1);
- list.list_value().push_back(value2);
- set_right(std::make_unique<TestParseNode>(list));
- }
-
- private:
- // The base class takes the Token by reference, this manages the lifetime.
- Token op_token_ownership_;
-
- // When setting the left to an identifier, this manages the lifetime of
- // the identifier token.
- Token left_identifier_token_ownership_;
-};
-
-} // namespace
-
-TEST(Operators, SourcesAppend) {
- Err err;
- TestWithScope setup;
-
- // Set up "sources" with an empty list.
- const char sources[] = "sources";
- setup.scope()->SetValue(sources, Value(nullptr, Value::LIST), nullptr);
-
- // Set up the operator.
- TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
- node.SetLeftToIdentifier(sources);
-
- // Set up the filter on the scope to remove everything ending with "rm"
- std::unique_ptr<PatternList> pattern_list = std::make_unique<PatternList>();
- pattern_list->Append(Pattern("*rm"));
- setup.scope()->set_sources_assignment_filter(std::move(pattern_list));
-
- // Append an integer.
- node.SetRightToListOfValue(Value(nullptr, static_cast<int64_t>(5)));
- node.Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Append a string that doesn't match the pattern, it should get appended.
- const char string1[] = "good";
- node.SetRightToListOfValue(Value(nullptr, string1));
- node.Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Append a string that does match the pattern, it should be a no-op.
- const char string2[] = "foo-rm";
- node.SetRightToListOfValue(Value(nullptr, string2));
- node.Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Append a list with the two strings from above.
- node.SetRightToListOfValue(Value(nullptr, string1), Value(nullptr, string2));
- node.Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // The sources variable in the scope should now have: [ 5, "good", "good" ]
- const Value* value = setup.scope()->GetValue(sources);
- ASSERT_TRUE(value);
- ASSERT_EQ(Value::LIST, value->type());
- ASSERT_EQ(3u, value->list_value().size());
- EXPECT_TRUE(IsValueIntegerEqualing(value->list_value()[0], 5));
- EXPECT_TRUE(IsValueStringEqualing(value->list_value()[1], "good"));
- EXPECT_TRUE(IsValueStringEqualing(value->list_value()[2], "good"));
-}
-
-// Note that the SourcesAppend test above tests the basic list + list features,
-// this test handles the other cases.
-TEST(Operators, ListAppend) {
- Err err;
- TestWithScope setup;
-
- // Set up "foo" with an empty list.
- const char foo[] = "foo";
- setup.scope()->SetValue(foo, Value(nullptr, Value::LIST), nullptr);
-
- // Set up the operator to append to "foo".
- TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
- node.SetLeftToIdentifier(foo);
-
- // Append a list with a list, the result should be a nested list.
- Value inner_list(nullptr, Value::LIST);
- inner_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(12)));
- node.SetRightToListOfValue(inner_list);
-
- Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Return from the operator should always be "none", it should update the
- // value only.
- EXPECT_EQ(Value::NONE, ret.type());
-
- // The value should be updated with "[ [ 12 ] ]"
- Value result = *setup.scope()->GetValue(foo);
- ASSERT_EQ(Value::LIST, result.type());
- ASSERT_EQ(1u, result.list_value().size());
- ASSERT_EQ(Value::LIST, result.list_value()[0].type());
- ASSERT_EQ(1u, result.list_value()[0].list_value().size());
- ASSERT_EQ(Value::INTEGER, result.list_value()[0].list_value()[0].type());
- ASSERT_EQ(12, result.list_value()[0].list_value()[0].int_value());
-
- // Try to append an integer and a string directly (e.g. foo += "hi").
- // This should fail.
- const char str_str[] = "\"hi\"";
- Token str(Location(), Token::STRING, str_str);
- node.set_right(std::make_unique<LiteralNode>(str));
- ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
- EXPECT_TRUE(err.has_error());
- err = Err();
-
- node.SetRightToValue(Value(nullptr, static_cast<int64_t>(12)));
- ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
- EXPECT_TRUE(err.has_error());
-}
-
-TEST(Operators, ListRemove) {
- Err err;
- TestWithScope setup;
-
- const char foo_str[] = "foo";
- const char bar_str[] = "bar";
- Value test_list(nullptr, Value::LIST);
- test_list.list_value().push_back(Value(nullptr, foo_str));
- test_list.list_value().push_back(Value(nullptr, bar_str));
- test_list.list_value().push_back(Value(nullptr, foo_str));
-
- // Set up "var" with an the test list.
- const char var[] = "var";
- setup.scope()->SetValue(var, test_list, nullptr);
-
- TestBinaryOpNode node(Token::MINUS_EQUALS, "-=");
- node.SetLeftToIdentifier(var);
-
- // Subtract a list consisting of "foo".
- node.SetRightToListOfValue(Value(nullptr, foo_str));
- Value result = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- EXPECT_FALSE(err.has_error());
-
- // -= returns an empty value to reduce the possibility of writing confusing
- // cases like foo = bar += 1.
- EXPECT_EQ(Value::NONE, result.type());
-
- // The "var" variable should have been updated. Both instances of "foo" are
- // deleted.
- const Value* new_value = setup.scope()->GetValue(var);
- ASSERT_TRUE(new_value);
- ASSERT_EQ(Value::LIST, new_value->type());
- ASSERT_EQ(1u, new_value->list_value().size());
- ASSERT_EQ(Value::STRING, new_value->list_value()[0].type());
- EXPECT_EQ("bar", new_value->list_value()[0].string_value());
-}
-
-TEST(Operators, ListSubtractWithScope) {
- Err err;
- TestWithScope setup;
-
- Scope* scope_a = new Scope(setup.settings());
- Value scopeval_a(nullptr, std::unique_ptr<Scope>(scope_a));
- scope_a->SetValue("a", Value(nullptr, "foo"), nullptr);
-
- Scope* scope_b = new Scope(setup.settings());
- Value scopeval_b(nullptr, std::unique_ptr<Scope>(scope_b));
- scope_b->SetValue("b", Value(nullptr, "bar"), nullptr);
-
- Value lval(nullptr, Value::LIST);
- lval.list_value().push_back(scopeval_a);
- lval.list_value().push_back(scopeval_b);
-
- Scope* scope_a_other = new Scope(setup.settings());
- Value scopeval_a_other(nullptr, std::unique_ptr<Scope>(scope_a_other));
- scope_a_other->SetValue("a", Value(nullptr, "foo"), nullptr);
-
- Value rval(nullptr, Value::LIST);
- rval.list_value().push_back(scopeval_a_other);
-
- TestBinaryOpNode node(Token::MINUS, "-");
- node.SetLeftToValue(lval);
- node.SetRightToValue(rval);
- Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- ASSERT_FALSE(err.has_error());
- ASSERT_EQ(Value::LIST, ret.type());
-
- std::vector<Value> expected;
- Scope* scope_expected = new Scope(setup.settings());
- Value scopeval_expected(nullptr, std::unique_ptr<Scope>(scope_expected));
- scope_expected->SetValue("b", Value(nullptr, "bar"), nullptr);
- expected.push_back(scopeval_expected);
- EXPECT_EQ(expected, ret.list_value());
-}
-
-TEST(Operators, IntegerAdd) {
- Err err;
- TestWithScope setup;
-
- TestBinaryOpNode node(Token::PLUS, "+");
- node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
- node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
- Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- ASSERT_FALSE(err.has_error());
- ASSERT_EQ(Value::INTEGER, ret.type());
- EXPECT_EQ(579, ret.int_value());
-}
-
-TEST(Operators, IntegerSubtract) {
- Err err;
- TestWithScope setup;
-
- TestBinaryOpNode node(Token::MINUS, "-");
- node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
- node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
- Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- ASSERT_FALSE(err.has_error());
- ASSERT_EQ(Value::INTEGER, ret.type());
- EXPECT_EQ(-333, ret.int_value());
-}
-
-TEST(Operators, ShortCircuitAnd) {
- Err err;
- TestWithScope setup;
-
- // Set a && operator with the left to false.
- TestBinaryOpNode node(Token::BOOLEAN_AND, "&&");
- node.SetLeftToValue(Value(nullptr, false));
-
- // Set right as foo, but don't define a value for it.
- const char foo[] = "foo";
- Token identifier_token(Location(), Token::IDENTIFIER, foo);
- node.set_right(std::make_unique<IdentifierNode>(identifier_token));
-
- Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- EXPECT_FALSE(err.has_error());
-}
-
-TEST(Operators, ShortCircuitOr) {
- Err err;
- TestWithScope setup;
-
- // Set a || operator with the left to true.
- TestBinaryOpNode node(Token::BOOLEAN_OR, "||");
- node.SetLeftToValue(Value(nullptr, true));
-
- // Set right as foo, but don't define a value for it.
- const char foo[] = "foo";
- Token identifier_token(Location(), Token::IDENTIFIER, foo);
- node.set_right(std::make_unique<IdentifierNode>(identifier_token));
-
- Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
- node.right(), &err);
- EXPECT_FALSE(err.has_error());
-}
-
-// Overwriting nonempty lists and scopes with other nonempty lists and scopes
-// should be disallowed.
-TEST(Operators, NonemptyOverwriting) {
- Err err;
- TestWithScope setup;
-
- // Set up "foo" with a nonempty list.
- const char foo[] = "foo";
- Value old_value(nullptr, Value::LIST);
- old_value.list_value().push_back(Value(nullptr, "string"));
- setup.scope()->SetValue(foo, old_value, nullptr);
-
- TestBinaryOpNode node(Token::EQUAL, "=");
- node.SetLeftToIdentifier(foo);
-
- // Assigning a nonempty list should fail.
- node.SetRightToListOfValue(Value(nullptr, "string"));
- node.Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Replacing nonempty list.", err.message());
- err = Err();
-
- // Assigning an empty list should succeed.
- node.SetRightToValue(Value(nullptr, Value::LIST));
- node.Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error());
- const Value* new_value = setup.scope()->GetValue(foo);
- ASSERT_TRUE(new_value);
- ASSERT_EQ(Value::LIST, new_value->type());
- ASSERT_TRUE(new_value->list_value().empty());
-
- // Set up "foo" with a nonempty scope.
- const char bar[] = "bar";
- old_value = Value(nullptr, std::make_unique<Scope>(setup.settings()));
- old_value.scope_value()->SetValue(bar, Value(nullptr, "bar"), nullptr);
- setup.scope()->SetValue(foo, old_value, nullptr);
-
- // Assigning a nonempty scope should fail (re-use old_value copy).
- node.SetRightToValue(old_value);
- node.Execute(setup.scope(), &err);
- ASSERT_TRUE(err.has_error());
- EXPECT_EQ("Replacing nonempty scope.", err.message());
- err = Err();
-
- // Assigning an empty list should succeed.
- node.SetRightToValue(
- Value(nullptr, std::make_unique<Scope>(setup.settings())));
- node.Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error());
- new_value = setup.scope()->GetValue(foo);
- ASSERT_TRUE(new_value);
- ASSERT_EQ(Value::SCOPE, new_value->type());
- ASSERT_FALSE(new_value->scope_value()->HasValues(Scope::SEARCH_CURRENT));
-}
-
-// Tests this case:
-// foo = 1
-// target(...) {
-// foo += 1
-//
-// This should mark the outer "foo" as used, and the inner "foo" as unused.
-TEST(Operators, PlusEqualsUsed) {
- Err err;
- TestWithScope setup;
-
- // Outer "foo" definition, it should be unused.
- const char foo[] = "foo";
- Value old_value(nullptr, static_cast<int64_t>(1));
- setup.scope()->SetValue(foo, old_value, nullptr);
- EXPECT_TRUE(setup.scope()->IsSetButUnused(foo));
-
- // Nested scope.
- Scope nested(setup.scope());
-
- // Run "foo += 1".
- TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
- node.SetLeftToIdentifier(foo);
- node.SetRightToValue(Value(nullptr, static_cast<int64_t>(1)));
- node.Execute(&nested, &err);
- ASSERT_FALSE(err.has_error());
-
- // Outer foo should be used, inner foo should not be.
- EXPECT_FALSE(setup.scope()->IsSetButUnused(foo));
- EXPECT_TRUE(nested.IsSetButUnused(foo));
-}
diff --git a/gn/tools/gn/output_conversion.cc b/gn/tools/gn/output_conversion.cc
deleted file mode 100644
index 7f62896a032..00000000000
--- a/gn/tools/gn/output_conversion.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/output_conversion.h"
-
-#include "tools/gn/settings.h"
-#include "tools/gn/value.h"
-
-namespace {
-
-void ToString(const Value& output, std::ostream& out) {
- out << output.ToString(false);
-}
-
-void ToStringQuoted(const Value& output, std::ostream& out) {
- out << "\"" << output.ToString(false) << "\"";
-}
-
-void Indent(int indent, std::ostream& out) {
- for (int i = 0; i < indent; ++i)
- out << " ";
-}
-
-// Forward declare so it can be used recursively.
-void RenderScopeToJSON(const Value& output, std::ostream& out, int indent);
-
-void RenderListToJSON(const Value& output, std::ostream& out, int indent) {
- assert(indent > 0);
- bool first = true;
- out << "[\n";
- for (const auto& value : output.list_value()) {
- if (!first)
- out << ",\n";
- Indent(indent, out);
- if (value.type() == Value::SCOPE)
- RenderScopeToJSON(value, out, indent + 1);
- else if (value.type() == Value::LIST)
- RenderListToJSON(value, out, indent + 1);
- else
- out << value.ToString(true);
- first = false;
- }
- out << "\n";
- Indent(indent - 1, out);
- out << "]";
-}
-
-void RenderScopeToJSON(const Value& output, std::ostream& out, int indent) {
- assert(indent > 0);
- Scope::KeyValueMap scope_values;
- output.scope_value()->GetCurrentScopeValues(&scope_values);
- bool first = true;
- out << "{\n";
- for (const auto& pair : scope_values) {
- if (!first)
- out << ",\n";
- Indent(indent, out);
- out << "\"" << pair.first.as_string() << "\": ";
- if (pair.second.type() == Value::SCOPE)
- RenderScopeToJSON(pair.second, out, indent + 1);
- else if (pair.second.type() == Value::LIST)
- RenderListToJSON(pair.second, out, indent + 1);
- else
- out << pair.second.ToString(true);
- first = false;
- }
- out << "\n";
- Indent(indent - 1, out);
- out << "}";
-}
-
-void OutputListLines(const Value& output, std::ostream& out) {
- assert(output.type() == Value::LIST);
- const std::vector<Value>& list = output.list_value();
- for (const auto& cur : list)
- out << cur.ToString(false) << "\n";
-}
-
-void OutputString(const Value& output, std::ostream& out) {
- if (output.type() == Value::NONE)
- return;
- if (output.type() == Value::STRING) {
- ToString(output, out);
- return;
- }
- ToStringQuoted(output, out);
-}
-
-void OutputValue(const Value& output, std::ostream& out) {
- if (output.type() == Value::NONE)
- return;
- if (output.type() == Value::STRING) {
- ToStringQuoted(output, out);
- return;
- }
- ToString(output, out);
-}
-
-// The direct Value::ToString call wraps the scope in '{}', which we don't want
-// here for the top-level scope being output.
-void OutputScope(const Value& output, std::ostream& out) {
- Scope::KeyValueMap scope_values;
- output.scope_value()->GetCurrentScopeValues(&scope_values);
- for (const auto& pair : scope_values) {
- out << " " << pair.first.as_string() << " = " << pair.second.ToString(true)
- << "\n";
- }
-}
-
-void OutputDefault(const Value& output, std::ostream& out) {
- if (output.type() == Value::LIST)
- OutputListLines(output, out);
- else
- ToString(output, out);
-}
-
-void OutputJSON(const Value& output, std::ostream& out) {
- if (output.type() == Value::SCOPE) {
- RenderScopeToJSON(output, out, /*indent=*/1);
- return;
- }
- if (output.type() == Value::LIST) {
- RenderListToJSON(output, out, /*indent=*/1);
- return;
- }
- ToStringQuoted(output, out);
-}
-
-void DoConvertValueToOutput(const Value& output,
- const std::string& output_conversion,
- const Value& original_output_conversion,
- std::ostream& out,
- Err* err) {
- if (output_conversion == "") {
- OutputDefault(output, out);
- } else if (output_conversion == "list lines") {
- if (output.type() != Value::LIST) {
- *err = Err(original_output_conversion, "Not a valid list.");
- return;
- }
- OutputListLines(output, out);
- } else if (output_conversion == "string") {
- OutputString(output, out);
- } else if (output_conversion == "value") {
- OutputValue(output, out);
- } else if (output_conversion == "json") {
- OutputJSON(output, out);
- } else if (output_conversion == "scope") {
- if (output.type() != Value::SCOPE) {
- *err = Err(original_output_conversion, "Not a valid scope.");
- return;
- }
- OutputScope(output, out);
- } else {
- // If we make it here, we didn't match any of the valid options.
- *err = Err(original_output_conversion, "Not a valid output_conversion.",
- "Run gn help output_conversion to see your options.");
- }
-}
-
-} // namespace
-
-void ConvertValueToOutput(const Settings* settings,
- const Value& output,
- const Value& output_conversion,
- std::ostream& out,
- Err* err) {
- if (output_conversion.type() == Value::NONE) {
- OutputDefault(output, out);
- return;
- }
- if (!output_conversion.VerifyTypeIs(Value::STRING, err))
- return;
-
- DoConvertValueToOutput(output, output_conversion.string_value(),
- output_conversion, out, err);
-}
diff --git a/gn/tools/gn/output_conversion.h b/gn/tools/gn/output_conversion.h
deleted file mode 100644
index 127a6ca7ffb..00000000000
--- a/gn/tools/gn/output_conversion.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_OUTPUT_CONVERSION_H_
-#define TOOLS_GN_OUTPUT_CONVERSION_H_
-
-#include <iostream>
-#include <string>
-
-class Err;
-class Settings;
-class Value;
-
-// Converts the given input Value to an output string (to be written to a file).
-// Conversions as specified in the output_conversion string will be performed.
-// The given ostream will be used for writing the resulting string.
-//
-// If the conversion string is invalid, the error will be set.
-void ConvertValueToOutput(const Settings* settings,
- const Value& output,
- const Value& output_conversion_value,
- std::ostream& out,
- Err* err);
-
-#endif // TOOLS_GN_OUTPUT_CONVERSION_H_
diff --git a/gn/tools/gn/output_conversion_unittest.cc b/gn/tools/gn/output_conversion_unittest.cc
deleted file mode 100644
index 2e4146d9764..00000000000
--- a/gn/tools/gn/output_conversion_unittest.cc
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/output_conversion.h"
-
-#include <sstream>
-
-#include "tools/gn/err.h"
-#include "tools/gn/input_conversion.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/template.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-namespace {
-
-// InputConversion needs a global scheduler object.
-class OutputConversionTest : public TestWithScheduler {
- public:
- OutputConversionTest() = default;
-
- const Settings* settings() { return setup_.settings(); }
- Scope* scope() { return setup_.scope(); }
-
- private:
- TestWithScope setup_;
-};
-
-} // namespace
-
-TEST_F(OutputConversionTest, ListLines) {
- Err err;
- Value output(nullptr, Value::LIST);
- output.list_value().push_back(Value(nullptr, ""));
- output.list_value().push_back(Value(nullptr, "foo"));
- output.list_value().push_back(Value(nullptr, ""));
- output.list_value().push_back(Value(nullptr, "bar"));
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "list lines"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ("\nfoo\n\nbar\n", result.str());
-}
-
-TEST_F(OutputConversionTest, String) {
- Err err;
- Value output(nullptr, "foo bar");
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "foo bar");
-}
-
-TEST_F(OutputConversionTest, StringInt) {
- Err err;
- Value output(nullptr, int64_t(6));
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "\"6\"");
-}
-
-TEST_F(OutputConversionTest, StringBool) {
- Err err;
- Value output(nullptr, true);
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "\"true\"");
-}
-
-TEST_F(OutputConversionTest, StringList) {
- Err err;
- Value output(nullptr, Value::LIST);
- output.list_value().push_back(Value(nullptr, "foo"));
- output.list_value().push_back(Value(nullptr, "bar"));
- output.list_value().push_back(Value(nullptr, int64_t(6)));
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "\"[\"foo\", \"bar\", 6]\"");
-}
-
-TEST_F(OutputConversionTest, StringScope) {
- Err err;
-
- auto new_scope = std::make_unique<Scope>(settings());
- // Add some values to the scope.
- Value value(nullptr, "hello");
- new_scope->SetValue("v", value, nullptr);
- base::StringPiece private_var_name("_private");
- new_scope->SetValue(private_var_name, value, nullptr);
-
- std::ostringstream result;
- ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
- Value(nullptr, "string"), result, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "\"{\n _private = \"hello\"\n v = \"hello\"\n}\"");
-}
-
-TEST_F(OutputConversionTest, ValueString) {
- Err err;
- Value output(nullptr, "foo bar");
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "\"foo bar\"");
-}
-
-TEST_F(OutputConversionTest, ValueInt) {
- Err err;
- Value output(nullptr, int64_t(6));
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "6");
-}
-
-TEST_F(OutputConversionTest, ValueBool) {
- Err err;
- Value output(nullptr, true);
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "true");
-}
-
-TEST_F(OutputConversionTest, ValueList) {
- Err err;
- Value output(nullptr, Value::LIST);
- output.list_value().push_back(Value(nullptr, "foo"));
- output.list_value().push_back(Value(nullptr, "bar"));
- output.list_value().push_back(Value(nullptr, int64_t(6)));
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
- &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "[\"foo\", \"bar\", 6]");
-}
-
-TEST_F(OutputConversionTest, ValueScope) {
- Err err;
-
- auto new_scope = std::make_unique<Scope>(settings());
- // Add some values to the scope.
- Value value(nullptr, "hello");
- new_scope->SetValue("v", value, nullptr);
- base::StringPiece private_var_name("_private");
- new_scope->SetValue(private_var_name, value, nullptr);
-
- std::ostringstream result;
- ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
- Value(nullptr, "value"), result, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "{\n _private = \"hello\"\n v = \"hello\"\n}");
-}
-
-TEST_F(OutputConversionTest, JSON) {
- Err err;
- auto new_scope = std::make_unique<Scope>(settings());
- // Add some values to the scope.
- Value a_value(nullptr, "foo");
- new_scope->SetValue("a", a_value, nullptr);
- Value b_value(nullptr, int64_t(6));
- new_scope->SetValue("b", b_value, nullptr);
-
- auto c_scope = std::make_unique<Scope>(settings());
- Value e_value(nullptr, Value::LIST);
- e_value.list_value().push_back(Value(nullptr, "bar"));
-
- auto e_value_scope = std::make_unique<Scope>(settings());
- Value f_value(nullptr, "baz");
- e_value_scope->SetValue("f", f_value, nullptr);
- e_value.list_value().push_back(Value(nullptr, std::move(e_value_scope)));
-
- c_scope->SetValue("e", e_value, nullptr);
-
- new_scope->SetValue("c", Value(nullptr, std::move(c_scope)), nullptr);
-
- std::string expected(R"*({
- "a": "foo",
- "b": 6,
- "c": {
- "e": [
- "bar",
- {
- "f": "baz"
- }
- ]
- }
-})*");
- std::ostringstream result;
- ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
- Value(nullptr, "json"), result, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), expected);
-}
-
-TEST_F(OutputConversionTest, ValueEmpty) {
- Err err;
- std::ostringstream result;
- ConvertValueToOutput(settings(), Value(), Value(nullptr, ""), result, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "<void>");
-}
-
-TEST_F(OutputConversionTest, DefaultValue) {
- Err err;
- Value output(nullptr, "foo bar");
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, ""), result, &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(result.str(), "foo bar");
-}
-
-TEST_F(OutputConversionTest, DefaultListLines) {
- Err err;
- Value output(nullptr, Value::LIST);
- output.list_value().push_back(Value(nullptr, ""));
- output.list_value().push_back(Value(nullptr, "foo"));
- output.list_value().push_back(Value(nullptr, ""));
- output.list_value().push_back(Value(nullptr, "bar"));
- std::ostringstream result;
- ConvertValueToOutput(settings(), output, Value(nullptr, ""), result, &err);
-
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ("\nfoo\n\nbar\n", result.str());
-}
-
-TEST_F(OutputConversionTest, ReverseString) {
- Err err;
- std::string input("foo bar");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "string"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "string"), reverse,
- &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), input)
- << "actual: " << reverse.str() << "expected:" << input;
-}
-
-TEST_F(OutputConversionTest, ReverseListLines) {
- Err err;
- std::string input("\nfoo\nbar\n\n");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "list lines"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "list lines"),
- reverse, &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), input)
- << "actual: " << reverse.str() << "expected:" << input;
-}
-
-TEST_F(OutputConversionTest, ReverseValueString) {
- Err err;
- std::string input("\"str\"");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
- &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), input)
- << "actual: " << reverse.str() << "expected:" << input;
-}
-
-TEST_F(OutputConversionTest, ReverseValueInt) {
- Err err;
- std::string input("6");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
- &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), input)
- << "actual: " << reverse.str() << "expected:" << input;
-}
-
-TEST_F(OutputConversionTest, ReverseValueList) {
- Err err;
- std::string input("[\"a\", 5]");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
- &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), input)
- << "actual: " << reverse.str() << "expected:" << input;
-}
-
-TEST_F(OutputConversionTest, ReverseValueDict) {
- Err err;
- std::string input(" a = 5\n b = \"foo\"\n c = 7\n");
- Value result = ConvertInputToValue(settings(), input, nullptr,
- Value(nullptr, "scope"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "scope"), reverse,
- &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), input)
- << "actual: " << reverse.str() << "expected:" << input;
-}
-
-TEST_F(OutputConversionTest, ReverseValueEmpty) {
- Err err;
- Value result = ConvertInputToValue(settings(), "", nullptr,
- Value(nullptr, "value"), &err);
- EXPECT_FALSE(err.has_error());
-
- std::ostringstream reverse;
- ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
- &err);
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(reverse.str(), "");
-}
diff --git a/gn/tools/gn/output_file.cc b/gn/tools/gn/output_file.cc
deleted file mode 100644
index e0c82fc23ec..00000000000
--- a/gn/tools/gn/output_file.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 "tools/gn/output_file.h"
-
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/source_file.h"
-
-OutputFile::OutputFile(std::string&& v) : value_(std::move(v)) {}
-
-OutputFile::OutputFile(const std::string& v) : value_(v) {}
-
-OutputFile::OutputFile(const BuildSettings* build_settings,
- const SourceFile& source_file)
- : value_(RebasePath(source_file.value(),
- build_settings->build_dir(),
- build_settings->root_path_utf8())) {}
-
-SourceFile OutputFile::AsSourceFile(const BuildSettings* build_settings) const {
- DCHECK(!value_.empty());
- DCHECK(value_[value_.size() - 1] != '/');
-
- std::string path = build_settings->build_dir().value();
- path.append(value_);
- return SourceFile(std::move(path));
-}
-
-SourceDir OutputFile::AsSourceDir(const BuildSettings* build_settings) const {
- if (!value_.empty()) {
- // Empty means the root build dir. Otherwise, we expect it to end in a
- // slash.
- DCHECK(value_[value_.size() - 1] == '/');
- }
- std::string path = build_settings->build_dir().value();
- path.append(value_);
- NormalizePath(&path);
- return SourceDir(std::move(path));
-}
diff --git a/gn/tools/gn/output_file.h b/gn/tools/gn/output_file.h
deleted file mode 100644
index cd3ee33f798..00000000000
--- a/gn/tools/gn/output_file.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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 TOOLS_GN_OUTPUT_FILE_H_
-#define TOOLS_GN_OUTPUT_FILE_H_
-
-#include <stddef.h>
-
-#include <string>
-
-#include "tools/gn/build_settings.h"
-
-class SourceFile;
-
-// A simple wrapper around a string that indicates the string is a path
-// relative to the output directory.
-class OutputFile {
- public:
- OutputFile() = default;
-
- explicit OutputFile(std::string&& v);
- explicit OutputFile(const std::string& v);
-
- OutputFile(const BuildSettings* build_settings,
- const SourceFile& source_file);
-
- std::string& value() { return value_; }
- const std::string& value() const { return value_; }
-
- // Converts to a SourceFile by prepending the build directory to the file.
- // The *Dir version requires that the current OutputFile ends in a slash, and
- // the *File version is the opposite.
- SourceFile AsSourceFile(const BuildSettings* build_settings) const;
- SourceDir AsSourceDir(const BuildSettings* build_settings) const;
-
- bool operator==(const OutputFile& other) const {
- return value_ == other.value_;
- }
- bool operator!=(const OutputFile& other) const {
- return value_ != other.value_;
- }
- bool operator<(const OutputFile& other) const {
- return value_ < other.value_;
- }
-
- private:
- std::string value_;
-};
-
-namespace std {
-
-template <>
-struct hash<OutputFile> {
- std::size_t operator()(const OutputFile& v) const {
- hash<std::string> h;
- return h(v.value());
- }
-};
-
-} // namespace std
-
-#endif // TOOLS_GN_OUTPUT_FILE_H_
diff --git a/gn/tools/gn/parse_node_value_adapter.cc b/gn/tools/gn/parse_node_value_adapter.cc
deleted file mode 100644
index bf3ad66ca63..00000000000
--- a/gn/tools/gn/parse_node_value_adapter.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/parse_node_value_adapter.h"
-
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-
-ParseNodeValueAdapter::ParseNodeValueAdapter() : ref_(nullptr) {}
-
-ParseNodeValueAdapter::~ParseNodeValueAdapter() = default;
-
-bool ParseNodeValueAdapter::Init(Scope* scope,
- const ParseNode* node,
- Err* err) {
- const IdentifierNode* identifier = node->AsIdentifier();
- if (identifier) {
- ref_ = scope->GetValue(identifier->value().value(), true);
- if (!ref_) {
- identifier->MakeErrorDescribing("Undefined identifier");
- return false;
- }
- return true;
- }
-
- temporary_ = node->Execute(scope, err);
- return !err->has_error();
-}
-
-bool ParseNodeValueAdapter::InitForType(Scope* scope,
- const ParseNode* node,
- Value::Type type,
- Err* err) {
- if (!Init(scope, node, err))
- return false;
- if (get().VerifyTypeIs(type, err))
- return true;
-
- // Fix up the error range (see class comment in the header file) to be the
- // identifier node rather than the original value.
- *err = Err(node, err->message(), err->help_text());
- return false;
-}
diff --git a/gn/tools/gn/parse_node_value_adapter.h b/gn/tools/gn/parse_node_value_adapter.h
deleted file mode 100644
index db34e24b12d..00000000000
--- a/gn/tools/gn/parse_node_value_adapter.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_PARSE_NODE_VALUE_ADAPTER_H_
-#define TOOLS_GN_PARSE_NODE_VALUE_ADAPTER_H_
-
-#include "base/macros.h"
-#include "tools/gn/value.h"
-
-class ParseNode;
-
-// Provides a means to convert a parse node to a value without causing a copy
-// in the common case of an "Identifier" node. Normally to get a value from a
-// parse node you have to call Execute(), and when an identifier is executed
-// it just returns the current value of itself as a copy. But some variables
-// are very large (lists of many strings for example).
-//
-// The reason you might not want to do this is that in the case of an
-// identifier where the copy is optimized away, the origin will still be the
-// original value. The result can be confusing because it will reference the
-// original value rather than the place where the value was dereferenced, e.g.
-// for a function call. The InitForType() function will verify type information
-// and will fix up the origin so it's not confusing.
-class ParseNodeValueAdapter {
- public:
- ParseNodeValueAdapter();
- ~ParseNodeValueAdapter();
-
- const Value& get() {
- if (ref_)
- return *ref_;
- return temporary_;
- }
-
- // Initializes the adapter for the result of the given expression. Returns
- // truen on success.
- bool Init(Scope* scope, const ParseNode* node, Err* err);
-
- // Like Init() but additionally verifies that the type of the result matches.
- bool InitForType(Scope* scope,
- const ParseNode* node,
- Value::Type type,
- Err* err);
-
- private:
- // Holds either a reference to an existing item, or a temporary as a copy.
- // If ref is non-null, it's valid, otherwise the temporary is used.
- const Value* ref_;
- Value temporary_;
-
- DISALLOW_COPY_AND_ASSIGN(ParseNodeValueAdapter);
-};
-
-#endif // TOOLS_GN_PARSE_NODE_VALUE_ADAPTER_H_
diff --git a/gn/tools/gn/parse_tree.cc b/gn/tools/gn/parse_tree.cc
deleted file mode 100644
index 821fb7eeba4..00000000000
--- a/gn/tools/gn/parse_tree.cc
+++ /dev/null
@@ -1,941 +0,0 @@
-// 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 "tools/gn/parse_tree.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <tuple>
-
-#include "base/json/string_escape.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/operators.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/string_utils.h"
-
-// Dictionary keys used for JSON-formatted tree dump.
-const char kJsonNodeChild[] = "child";
-const char kJsonNodeType[] = "type";
-const char kJsonNodeValue[] = "value";
-const char kJsonBeforeComment[] = "before_comment";
-const char kJsonSuffixComment[] = "suffix_comment";
-const char kJsonAfterComment[] = "after_comment";
-
-namespace {
-
-enum DepsCategory {
- DEPS_CATEGORY_LOCAL,
- DEPS_CATEGORY_RELATIVE,
- DEPS_CATEGORY_ABSOLUTE,
- DEPS_CATEGORY_OTHER,
-};
-
-DepsCategory GetDepsCategory(base::StringPiece deps) {
- if (deps.length() < 2 || deps[0] != '"' || deps[deps.size() - 1] != '"')
- return DEPS_CATEGORY_OTHER;
-
- if (deps[1] == ':')
- return DEPS_CATEGORY_LOCAL;
-
- if (deps[1] == '/')
- return DEPS_CATEGORY_ABSOLUTE;
-
- return DEPS_CATEGORY_RELATIVE;
-}
-
-std::tuple<base::StringPiece, base::StringPiece> SplitAtFirst(
- base::StringPiece str,
- char c) {
- if (!str.starts_with("\"") || !str.ends_with("\""))
- return std::make_tuple(str, base::StringPiece());
-
- str = str.substr(1, str.length() - 2);
- size_t index_of_first = str.find(c);
- return std::make_tuple(str.substr(0, index_of_first),
- index_of_first != base::StringPiece::npos
- ? str.substr(index_of_first + 1)
- : base::StringPiece());
-}
-
-bool IsSortRangeSeparator(const ParseNode* node, const ParseNode* prev) {
- // If it's a block comment, or has an attached comment with a blank line
- // before it, then we break the range at this point.
- return node->AsBlockComment() != nullptr ||
- (prev && node->comments() && !node->comments()->before().empty() &&
- (node->GetRange().begin().line_number() >
- prev->GetRange().end().line_number() +
- static_cast<int>(node->comments()->before().size() + 1)));
-}
-
-base::StringPiece GetStringRepresentation(const ParseNode* node) {
- DCHECK(node->AsLiteral() || node->AsIdentifier() || node->AsAccessor());
- if (node->AsLiteral())
- return node->AsLiteral()->value().value();
- else if (node->AsIdentifier())
- return node->AsIdentifier()->value().value();
- else if (node->AsAccessor())
- return node->AsAccessor()->base().value();
- return base::StringPiece();
-}
-
-} // namespace
-
-Comments::Comments() = default;
-
-Comments::~Comments() = default;
-
-void Comments::ReverseSuffix() {
- for (int i = 0, j = static_cast<int>(suffix_.size() - 1); i < j; ++i, --j)
- std::swap(suffix_[i], suffix_[j]);
-}
-
-ParseNode::ParseNode() = default;
-
-ParseNode::~ParseNode() = default;
-
-const AccessorNode* ParseNode::AsAccessor() const {
- return nullptr;
-}
-const BinaryOpNode* ParseNode::AsBinaryOp() const {
- return nullptr;
-}
-const BlockCommentNode* ParseNode::AsBlockComment() const {
- return nullptr;
-}
-const BlockNode* ParseNode::AsBlock() const {
- return nullptr;
-}
-const ConditionNode* ParseNode::AsConditionNode() const {
- return nullptr;
-}
-const EndNode* ParseNode::AsEnd() const {
- return nullptr;
-}
-const FunctionCallNode* ParseNode::AsFunctionCall() const {
- return nullptr;
-}
-const IdentifierNode* ParseNode::AsIdentifier() const {
- return nullptr;
-}
-const ListNode* ParseNode::AsList() const {
- return nullptr;
-}
-const LiteralNode* ParseNode::AsLiteral() const {
- return nullptr;
-}
-const UnaryOpNode* ParseNode::AsUnaryOp() const {
- return nullptr;
-}
-
-Comments* ParseNode::comments_mutable() {
- if (!comments_)
- comments_ = std::make_unique<Comments>();
- return comments_.get();
-}
-
-base::Value ParseNode::CreateJSONNode(const char* type) const {
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetKey(kJsonNodeType, base::Value(type));
- AddCommentsJSONNodes(&dict);
- return dict;
-}
-
-base::Value ParseNode::CreateJSONNode(const char* type,
- const base::StringPiece& value) const {
- base::Value dict(base::Value::Type::DICTIONARY);
- dict.SetKey(kJsonNodeType, base::Value(type));
- dict.SetKey(kJsonNodeValue, base::Value(value));
- AddCommentsJSONNodes(&dict);
- return dict;
-}
-
-void ParseNode::AddCommentsJSONNodes(base::Value* out_value) const {
- if (comments_) {
- if (comments_->before().size()) {
- base::Value comment_values(base::Value::Type::LIST);
- for (const auto& token : comments_->before())
- comment_values.GetList().push_back(base::Value(token.value()));
- out_value->SetKey(kJsonBeforeComment, std::move(comment_values));
- }
- if (comments_->suffix().size()) {
- base::Value comment_values(base::Value::Type::LIST);
- for (const auto& token : comments_->suffix())
- comment_values.GetList().push_back(base::Value(token.value()));
- out_value->SetKey(kJsonSuffixComment, std::move(comment_values));
- }
- if (comments_->after().size()) {
- base::Value comment_values(base::Value::Type::LIST);
- for (const auto& token : comments_->after())
- comment_values.GetList().push_back(base::Value(token.value()));
- out_value->SetKey(kJsonAfterComment, std::move(comment_values));
- }
- }
-}
-
-// AccessorNode ---------------------------------------------------------------
-
-AccessorNode::AccessorNode() = default;
-
-AccessorNode::~AccessorNode() = default;
-
-const AccessorNode* AccessorNode::AsAccessor() const {
- return this;
-}
-
-Value AccessorNode::Execute(Scope* scope, Err* err) const {
- if (index_)
- return ExecuteArrayAccess(scope, err);
- else if (member_)
- return ExecuteScopeAccess(scope, err);
- NOTREACHED();
- return Value();
-}
-
-LocationRange AccessorNode::GetRange() const {
- if (index_)
- return LocationRange(base_.location(), index_->GetRange().end());
- else if (member_)
- return LocationRange(base_.location(), member_->GetRange().end());
- NOTREACHED();
- return LocationRange();
-}
-
-Err AccessorNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(GetRange(), msg, help);
-}
-
-base::Value AccessorNode::GetJSONNode() const {
- base::Value dict(CreateJSONNode("ACCESSOR", base_.value()));
- base::Value child(base::Value::Type::LIST);
- if (index_)
- child.GetList().push_back(index_->GetJSONNode());
- else if (member_)
- child.GetList().push_back(member_->GetJSONNode());
- dict.SetKey(kJsonNodeChild, std::move(child));
- return dict;
-}
-
-Value AccessorNode::ExecuteArrayAccess(Scope* scope, Err* err) const {
- const Value* base_value = scope->GetValue(base_.value(), true);
- if (!base_value) {
- *err = MakeErrorDescribing("Undefined identifier.");
- return Value();
- }
- if (!base_value->VerifyTypeIs(Value::LIST, err))
- return Value();
-
- size_t index = 0;
- if (!ComputeAndValidateListIndex(scope, base_value->list_value().size(),
- &index, err))
- return Value();
- return base_value->list_value()[index];
-}
-
-Value AccessorNode::ExecuteScopeAccess(Scope* scope, Err* err) const {
- // We jump through some hoops here since ideally a.b will count "b" as
- // accessed in the given scope. The value "a" might be in some normal nested
- // scope and we can modify it, but it might also be inherited from the
- // readonly root scope and we can't do used variable tracking on it. (It's
- // not legal to const cast it away since the root scope will be in readonly
- // mode and being accessed from multiple threads without locking.) So this
- // code handles both cases.
- const Value* result = nullptr;
-
- // Look up the value in the scope named by "base_".
- Value* mutable_base_value =
- scope->GetMutableValue(base_.value(), Scope::SEARCH_NESTED, true);
- if (mutable_base_value) {
- // Common case: base value is mutable so we can track variable accesses
- // for unused value warnings.
- if (!mutable_base_value->VerifyTypeIs(Value::SCOPE, err))
- return Value();
- result = mutable_base_value->scope_value()->GetValue(
- member_->value().value(), true);
- } else {
- // Fall back to see if the value is on a read-only scope.
- const Value* const_base_value = scope->GetValue(base_.value(), true);
- if (const_base_value) {
- // Read only value, don't try to mark the value access as a "used" one.
- if (!const_base_value->VerifyTypeIs(Value::SCOPE, err))
- return Value();
- result =
- const_base_value->scope_value()->GetValue(member_->value().value());
- } else {
- *err = Err(base_, "Undefined identifier.");
- return Value();
- }
- }
-
- if (!result) {
- *err = Err(member_.get(), "No value named \"" + member_->value().value() +
- "\" in scope \"" + base_.value() + "\"");
- return Value();
- }
- return *result;
-}
-
-void AccessorNode::SetNewLocation(int line_number) {
- Location old = base_.location();
- base_.set_location(
- Location(old.file(), line_number, old.column_number(), old.byte()));
-}
-
-bool AccessorNode::ComputeAndValidateListIndex(Scope* scope,
- size_t max_len,
- size_t* computed_index,
- Err* err) const {
- Value index_value = index_->Execute(scope, err);
- if (err->has_error())
- return false;
- if (!index_value.VerifyTypeIs(Value::INTEGER, err))
- return false;
-
- int64_t index_int = index_value.int_value();
- if (index_int < 0) {
- *err = Err(index_->GetRange(), "Negative array subscript.",
- "You gave me " + base::Int64ToString(index_int) + ".");
- return false;
- }
- if (max_len == 0) {
- *err = Err(index_->GetRange(), "Array subscript out of range.",
- "You gave me " + base::Int64ToString(index_int) + " but the " +
- "array has no elements.");
- return false;
- }
- size_t index_sizet = static_cast<size_t>(index_int);
- if (index_sizet >= max_len) {
- *err = Err(index_->GetRange(), "Array subscript out of range.",
- "You gave me " + base::Int64ToString(index_int) +
- " but I was expecting something from 0 to " +
- base::NumberToString(max_len - 1) + ", inclusive.");
- return false;
- }
-
- *computed_index = index_sizet;
- return true;
-}
-
-// BinaryOpNode ---------------------------------------------------------------
-
-BinaryOpNode::BinaryOpNode() = default;
-
-BinaryOpNode::~BinaryOpNode() = default;
-
-const BinaryOpNode* BinaryOpNode::AsBinaryOp() const {
- return this;
-}
-
-Value BinaryOpNode::Execute(Scope* scope, Err* err) const {
- return ExecuteBinaryOperator(scope, this, left_.get(), right_.get(), err);
-}
-
-LocationRange BinaryOpNode::GetRange() const {
- return left_->GetRange().Union(right_->GetRange());
-}
-
-Err BinaryOpNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(op_, msg, help);
-}
-
-base::Value BinaryOpNode::GetJSONNode() const {
- base::Value dict(CreateJSONNode("BINARY", op_.value()));
- base::Value child(base::Value::Type::LIST);
- child.GetList().push_back(left_->GetJSONNode());
- child.GetList().push_back(right_->GetJSONNode());
- dict.SetKey(kJsonNodeChild, std::move(child));
- return dict;
-}
-
-// BlockNode ------------------------------------------------------------------
-
-BlockNode::BlockNode(ResultMode result_mode) : result_mode_(result_mode) {}
-
-BlockNode::~BlockNode() = default;
-
-const BlockNode* BlockNode::AsBlock() const {
- return this;
-}
-
-Value BlockNode::Execute(Scope* enclosing_scope, Err* err) const {
- std::unique_ptr<Scope> nested_scope; // May be null.
-
- Scope* execution_scope; // Either the enclosing_scope or nested_scope.
- if (result_mode_ == RETURNS_SCOPE) {
- // Create a nested scope to save the values for returning.
- nested_scope = std::make_unique<Scope>(enclosing_scope);
- execution_scope = nested_scope.get();
- } else {
- // Use the enclosing scope. Modifications will go into this also (for
- // example, if conditions and loops).
- execution_scope = enclosing_scope;
- }
-
- for (size_t i = 0; i < statements_.size() && !err->has_error(); i++) {
- // Check for trying to execute things with no side effects in a block.
- //
- // A BlockNode here means that somebody has a free-floating { }.
- // Technically this can have side effects since it could generated targets,
- // but we don't want to allow this since it creates ambiguity when
- // immediately following a function call that takes no block. By not
- // allowing free-floating blocks that aren't passed anywhere or assigned to
- // anything, this ambiguity is resolved.
- const ParseNode* cur = statements_[i].get();
- if (cur->AsList() || cur->AsLiteral() || cur->AsUnaryOp() ||
- cur->AsIdentifier() || cur->AsBlock()) {
- *err = cur->MakeErrorDescribing(
- "This statement has no effect.",
- "Either delete it or do something with the result.");
- return Value();
- }
- cur->Execute(execution_scope, err);
- }
-
- if (result_mode_ == RETURNS_SCOPE) {
- // Clear the reference to the containing scope. This will be passed in
- // a value whose lifetime will not be related to the enclosing_scope passed
- // to this function.
- nested_scope->DetachFromContaining();
- return Value(this, std::move(nested_scope));
- }
- return Value();
-}
-
-LocationRange BlockNode::GetRange() const {
- if (begin_token_.type() != Token::INVALID &&
- end_->value().type() != Token::INVALID) {
- return begin_token_.range().Union(end_->value().range());
- } else if (!statements_.empty()) {
- return statements_[0]->GetRange().Union(
- statements_[statements_.size() - 1]->GetRange());
- }
- return LocationRange();
-}
-
-Err BlockNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(GetRange(), msg, help);
-}
-
-base::Value BlockNode::GetJSONNode() const {
- base::Value dict(CreateJSONNode("BLOCK"));
- base::Value statements(base::Value::Type::LIST);
- for (const auto& statement : statements_)
- statements.GetList().push_back(statement->GetJSONNode());
- if (end_ && end_->comments())
- statements.GetList().push_back(end_->GetJSONNode());
-
- dict.SetKey("child", std::move(statements));
- return dict;
-}
-
-// ConditionNode --------------------------------------------------------------
-
-ConditionNode::ConditionNode() = default;
-
-ConditionNode::~ConditionNode() = default;
-
-const ConditionNode* ConditionNode::AsConditionNode() const {
- return this;
-}
-
-Value ConditionNode::Execute(Scope* scope, Err* err) const {
- Value condition_result = condition_->Execute(scope, err);
- if (err->has_error())
- return Value();
- if (condition_result.type() != Value::BOOLEAN) {
- *err = condition_->MakeErrorDescribing(
- "Condition does not evaluate to a boolean value.",
- std::string("This is a value of type \"") +
- Value::DescribeType(condition_result.type()) + "\" instead.");
- err->AppendRange(if_token_.range());
- return Value();
- }
-
- if (condition_result.boolean_value()) {
- if_true_->Execute(scope, err);
- } else if (if_false_) {
- // The else block is optional.
- if_false_->Execute(scope, err);
- }
-
- return Value();
-}
-
-LocationRange ConditionNode::GetRange() const {
- if (if_false_)
- return if_token_.range().Union(if_false_->GetRange());
- return if_token_.range().Union(if_true_->GetRange());
-}
-
-Err ConditionNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(if_token_, msg, help);
-}
-
-base::Value ConditionNode::GetJSONNode() const {
- base::Value dict = CreateJSONNode("CONDITION");
- base::Value child(base::Value::Type::LIST);
- child.GetList().push_back(condition_->GetJSONNode());
- child.GetList().push_back(if_true_->GetJSONNode());
- if (if_false_) {
- child.GetList().push_back(if_false_->GetJSONNode());
- }
- dict.SetKey(kJsonNodeChild, std::move(child));
- return std::move(dict);
-}
-
-// FunctionCallNode -----------------------------------------------------------
-
-FunctionCallNode::FunctionCallNode() = default;
-
-FunctionCallNode::~FunctionCallNode() = default;
-
-const FunctionCallNode* FunctionCallNode::AsFunctionCall() const {
- return this;
-}
-
-Value FunctionCallNode::Execute(Scope* scope, Err* err) const {
- return functions::RunFunction(scope, this, args_.get(), block_.get(), err);
-}
-
-LocationRange FunctionCallNode::GetRange() const {
- if (function_.type() == Token::INVALID)
- return LocationRange(); // This will be null in some tests.
- if (block_)
- return function_.range().Union(block_->GetRange());
- return function_.range().Union(args_->GetRange());
-}
-
-Err FunctionCallNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(function_, msg, help);
-}
-
-base::Value FunctionCallNode::GetJSONNode() const {
- base::Value dict = CreateJSONNode("FUNCTION", function_.value());
- base::Value child(base::Value::Type::LIST);
- child.GetList().push_back(args_->GetJSONNode());
- if (block_) {
- child.GetList().push_back(block_->GetJSONNode());
- }
- dict.SetKey(kJsonNodeChild, std::move(child));
- return dict;
-}
-
-void FunctionCallNode::SetNewLocation(int line_number) {
- Location func_old_loc = function_.location();
- Location func_new_loc =
- Location(func_old_loc.file(), line_number, func_old_loc.column_number(),
- func_old_loc.byte());
- function_.set_location(func_new_loc);
-
- Location args_old_loc = args_->Begin().location();
- Location args_new_loc =
- Location(args_old_loc.file(), line_number, args_old_loc.column_number(),
- args_old_loc.byte());
- const_cast<Token&>(args_->Begin()).set_location(args_new_loc);
- const_cast<Token&>(args_->End()->value()).set_location(args_new_loc);
-}
-
-// IdentifierNode --------------------------------------------------------------
-
-IdentifierNode::IdentifierNode() = default;
-
-IdentifierNode::IdentifierNode(const Token& token) : value_(token) {}
-
-IdentifierNode::~IdentifierNode() = default;
-
-const IdentifierNode* IdentifierNode::AsIdentifier() const {
- return this;
-}
-
-Value IdentifierNode::Execute(Scope* scope, Err* err) const {
- const Scope* found_in_scope = nullptr;
- const Value* value =
- scope->GetValueWithScope(value_.value(), true, &found_in_scope);
- Value result;
- if (!value) {
- *err = MakeErrorDescribing("Undefined identifier");
- return result;
- }
-
- if (!EnsureNotReadingFromSameDeclareArgs(this, scope, found_in_scope, err))
- return result;
-
- result = *value;
- result.set_origin(this);
- return result;
-}
-
-LocationRange IdentifierNode::GetRange() const {
- return value_.range();
-}
-
-Err IdentifierNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(value_, msg, help);
-}
-
-base::Value IdentifierNode::GetJSONNode() const {
- return CreateJSONNode("IDENTIFIER", value_.value());
-}
-
-void IdentifierNode::SetNewLocation(int line_number) {
- Location old = value_.location();
- value_.set_location(
- Location(old.file(), line_number, old.column_number(), old.byte()));
-}
-
-// ListNode -------------------------------------------------------------------
-
-ListNode::ListNode() : prefer_multiline_(false) {}
-
-ListNode::~ListNode() = default;
-
-const ListNode* ListNode::AsList() const {
- return this;
-}
-
-Value ListNode::Execute(Scope* scope, Err* err) const {
- Value result_value(this, Value::LIST);
- std::vector<Value>& results = result_value.list_value();
- results.reserve(contents_.size());
-
- for (const auto& cur : contents_) {
- if (cur->AsBlockComment())
- continue;
- results.push_back(cur->Execute(scope, err));
- if (err->has_error())
- return Value();
- if (results.back().type() == Value::NONE) {
- *err = cur->MakeErrorDescribing("This does not evaluate to a value.",
- "I can't do something with nothing.");
- return Value();
- }
- }
- return result_value;
-}
-
-LocationRange ListNode::GetRange() const {
- return LocationRange(begin_token_.location(), end_->value().location());
-}
-
-Err ListNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(begin_token_, msg, help);
-}
-
-base::Value ListNode::GetJSONNode() const {
- base::Value dict(CreateJSONNode("LIST"));
- base::Value child(base::Value::Type::LIST);
- for (const auto& cur : contents_) {
- child.GetList().push_back(cur->GetJSONNode());
- }
- if (end_ && end_->comments()) {
- child.GetList().push_back(end_->GetJSONNode());
- }
- dict.SetKey(kJsonNodeChild, std::move(child));
- return dict;
-}
-
-template <typename Comparator>
-void ListNode::SortList(Comparator comparator) {
- // Partitions first on BlockCommentNodes and sorts each partition separately.
- for (auto sr : GetSortRanges()) {
- bool skip = false;
- for (size_t i = sr.begin; i != sr.end; ++i) {
- // Bails out if any of the nodes are unsupported.
- const ParseNode* node = contents_[i].get();
- if (!node->AsLiteral() && !node->AsIdentifier() && !node->AsAccessor()) {
- skip = true;
- continue;
- }
- }
- if (skip)
- continue;
- // Save the original line number so that we can re-assign ranges. We assume
- // they're contiguous lines because GetSortRanges() does so above. We need
- // to re-assign these line numbers primiarily because `gn format` uses them
- // to determine whether two nodes were initially separated by a blank line
- // or not.
- int start_line = contents_[sr.begin]->GetRange().begin().line_number();
- const ParseNode* original_first = contents_[sr.begin].get();
- std::sort(contents_.begin() + sr.begin, contents_.begin() + sr.end,
- [&comparator](const std::unique_ptr<const ParseNode>& a,
- const std::unique_ptr<const ParseNode>& b) {
- return comparator(a.get(), b.get());
- });
- // If the beginning of the range had before comments, and the first node
- // moved during the sort, then move its comments to the new head of the
- // range.
- if (original_first->comments() &&
- contents_[sr.begin].get() != original_first) {
- for (const auto& hc : original_first->comments()->before()) {
- const_cast<ParseNode*>(contents_[sr.begin].get())
- ->comments_mutable()
- ->append_before(hc);
- }
- const_cast<ParseNode*>(original_first)
- ->comments_mutable()
- ->clear_before();
- }
- const ParseNode* prev = nullptr;
- for (size_t i = sr.begin; i != sr.end; ++i) {
- const ParseNode* node = contents_[i].get();
- DCHECK(node->AsLiteral() || node->AsIdentifier() || node->AsAccessor());
- int line_number =
- prev ? prev->GetRange().end().line_number() + 1 : start_line;
- if (node->AsLiteral()) {
- const_cast<LiteralNode*>(node->AsLiteral())
- ->SetNewLocation(line_number);
- } else if (node->AsIdentifier()) {
- const_cast<IdentifierNode*>(node->AsIdentifier())
- ->SetNewLocation(line_number);
- } else if (node->AsAccessor()) {
- const_cast<AccessorNode*>(node->AsAccessor())
- ->SetNewLocation(line_number);
- }
- prev = node;
- }
- }
-}
-
-void ListNode::SortAsStringsList() {
- // Sorts alphabetically.
- SortList([](const ParseNode* a, const ParseNode* b) {
- base::StringPiece astr = GetStringRepresentation(a);
- base::StringPiece bstr = GetStringRepresentation(b);
- return astr < bstr;
- });
-}
-
-void ListNode::SortAsDepsList() {
- // Sorts first relative targets, then absolute, each group is sorted
- // alphabetically.
- SortList([](const ParseNode* a, const ParseNode* b) {
- base::StringPiece astr = GetStringRepresentation(a);
- base::StringPiece bstr = GetStringRepresentation(b);
- return std::make_pair(GetDepsCategory(astr), SplitAtFirst(astr, ':')) <
- std::make_pair(GetDepsCategory(bstr), SplitAtFirst(bstr, ':'));
- });
-}
-
-// Breaks the ParseNodes of |contents| up by ranges that should be separately
-// sorted. In particular, we break at a block comment, or an item that has an
-// attached "before" comment and is separated by a blank line from the item
-// before it. The assumption is that both of these indicate a separate 'section'
-// of a sources block across which items should not be inter-sorted.
-std::vector<ListNode::SortRange> ListNode::GetSortRanges() const {
- std::vector<SortRange> ranges;
- const ParseNode* prev = nullptr;
- size_t begin = 0;
- for (size_t i = begin; i < contents_.size(); prev = contents_[i++].get()) {
- if (IsSortRangeSeparator(contents_[i].get(), prev)) {
- if (i > begin) {
- ranges.push_back(SortRange(begin, i));
- // If |i| is an item with an attached comment, then we start the next
- // range at that point, because we want to include it in the sort.
- // Otherwise, it's a block comment which we skip over entirely because
- // we don't want to move or include it in the sort. The two cases are:
- //
- // sources = [
- // "a",
- // "b",
- //
- // #
- // # This is a block comment.
- // #
- //
- // "c",
- // "d",
- // ]
- //
- // which contains 5 elements, and for which the ranges would be { [0,
- // 2), [3, 5) } (notably excluding 2, the block comment), and:
- //
- // sources = [
- // "a",
- // "b",
- //
- // # This is a header comment.
- // "c",
- // "d",
- // ]
- //
- // which contains 4 elements, index 2 containing an attached 'before'
- // comments, and the ranges should be { [0, 2), [2, 4) }.
- if (!contents_[i]->AsBlockComment())
- begin = i;
- else
- begin = i + 1;
- } else {
- // If it was a one item range, just skip over it.
- begin = i + 1;
- }
- }
- }
- if (begin != contents_.size())
- ranges.push_back(SortRange(begin, contents_.size()));
- return ranges;
-}
-
-// LiteralNode -----------------------------------------------------------------
-
-LiteralNode::LiteralNode() = default;
-
-LiteralNode::LiteralNode(const Token& token) : value_(token) {}
-
-LiteralNode::~LiteralNode() = default;
-
-const LiteralNode* LiteralNode::AsLiteral() const {
- return this;
-}
-
-Value LiteralNode::Execute(Scope* scope, Err* err) const {
- switch (value_.type()) {
- case Token::TRUE_TOKEN:
- return Value(this, true);
- case Token::FALSE_TOKEN:
- return Value(this, false);
- case Token::INTEGER: {
- base::StringPiece s = value_.value();
- if ((s.starts_with("0") && s.size() > 1) || s.starts_with("-0")) {
- if (s == "-0")
- *err = MakeErrorDescribing("Negative zero doesn't make sense");
- else
- *err = MakeErrorDescribing("Leading zeros not allowed");
- return Value();
- }
- int64_t result_int;
- if (!base::StringToInt64(s, &result_int)) {
- *err = MakeErrorDescribing("This does not look like an integer");
- return Value();
- }
- return Value(this, result_int);
- }
- case Token::STRING: {
- Value v(this, Value::STRING);
- ExpandStringLiteral(scope, value_, &v, err);
- return v;
- }
- default:
- NOTREACHED();
- return Value();
- }
-}
-
-LocationRange LiteralNode::GetRange() const {
- return value_.range();
-}
-
-Err LiteralNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(value_, msg, help);
-}
-
-base::Value LiteralNode::GetJSONNode() const {
- return CreateJSONNode("LITERAL", value_.value());
-}
-
-void LiteralNode::SetNewLocation(int line_number) {
- Location old = value_.location();
- value_.set_location(
- Location(old.file(), line_number, old.column_number(), old.byte()));
-}
-
-// UnaryOpNode ----------------------------------------------------------------
-
-UnaryOpNode::UnaryOpNode() = default;
-
-UnaryOpNode::~UnaryOpNode() = default;
-
-const UnaryOpNode* UnaryOpNode::AsUnaryOp() const {
- return this;
-}
-
-Value UnaryOpNode::Execute(Scope* scope, Err* err) const {
- Value operand_value = operand_->Execute(scope, err);
- if (err->has_error())
- return Value();
- return ExecuteUnaryOperator(scope, this, operand_value, err);
-}
-
-LocationRange UnaryOpNode::GetRange() const {
- return op_.range().Union(operand_->GetRange());
-}
-
-Err UnaryOpNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(op_, msg, help);
-}
-
-base::Value UnaryOpNode::GetJSONNode() const {
- base::Value dict = CreateJSONNode("UNARY", op_.value());
- base::Value child(base::Value::Type::LIST);
- child.GetList().push_back(operand_->GetJSONNode());
- dict.SetKey(kJsonNodeChild, std::move(child));
- return dict;
-}
-
-// BlockCommentNode ------------------------------------------------------------
-
-BlockCommentNode::BlockCommentNode() = default;
-
-BlockCommentNode::~BlockCommentNode() = default;
-
-const BlockCommentNode* BlockCommentNode::AsBlockComment() const {
- return this;
-}
-
-Value BlockCommentNode::Execute(Scope* scope, Err* err) const {
- return Value();
-}
-
-LocationRange BlockCommentNode::GetRange() const {
- return comment_.range();
-}
-
-Err BlockCommentNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(comment_, msg, help);
-}
-
-base::Value BlockCommentNode::GetJSONNode() const {
- std::string escaped;
- base::EscapeJSONString(comment_.value().as_string(), false, &escaped);
- return CreateJSONNode("BLOCK_COMMENT", escaped);
-}
-
-// EndNode ---------------------------------------------------------------------
-
-EndNode::EndNode(const Token& token) : value_(token) {}
-
-EndNode::~EndNode() = default;
-
-const EndNode* EndNode::AsEnd() const {
- return this;
-}
-
-Value EndNode::Execute(Scope* scope, Err* err) const {
- return Value();
-}
-
-LocationRange EndNode::GetRange() const {
- return value_.range();
-}
-
-Err EndNode::MakeErrorDescribing(const std::string& msg,
- const std::string& help) const {
- return Err(value_, msg, help);
-}
-
-base::Value EndNode::GetJSONNode() const {
- return CreateJSONNode("END", value_.value());
-}
diff --git a/gn/tools/gn/parse_tree.h b/gn/tools/gn/parse_tree.h
deleted file mode 100644
index 6c61b724559..00000000000
--- a/gn/tools/gn/parse_tree.h
+++ /dev/null
@@ -1,567 +0,0 @@
-// 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 TOOLS_GN_PARSE_TREE_H_
-#define TOOLS_GN_PARSE_TREE_H_
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/values.h"
-#include "tools/gn/err.h"
-#include "tools/gn/token.h"
-#include "tools/gn/value.h"
-
-class AccessorNode;
-class BinaryOpNode;
-class BlockCommentNode;
-class BlockNode;
-class ConditionNode;
-class EndNode;
-class FunctionCallNode;
-class IdentifierNode;
-class ListNode;
-class LiteralNode;
-class Scope;
-class UnaryOpNode;
-
-// Dictionary keys used for JSON-formatted tree dump.
-extern const char kJsonNodeChild[];
-extern const char kJsonNodeType[];
-extern const char kJsonNodeValue[];
-extern const char kJsonBeforeComment[];
-extern const char kJsonSuffixComment[];
-extern const char kJsonAfterComment[];
-
-class Comments {
- public:
- Comments();
- virtual ~Comments();
-
- const std::vector<Token>& before() const { return before_; }
- void append_before(Token c) { before_.push_back(c); }
- void clear_before() { before_.clear(); }
-
- const std::vector<Token>& suffix() const { return suffix_; }
- void append_suffix(Token c) { suffix_.push_back(c); }
- // Reverse the order of the suffix comments. When walking the tree in
- // post-order we append suffix comments in reverse order, so this fixes them
- // up.
- void ReverseSuffix();
-
- const std::vector<Token>& after() const { return after_; }
- void append_after(Token c) { after_.push_back(c); }
-
- private:
- // Whole line comments before the expression.
- std::vector<Token> before_;
-
- // End-of-line comments after this expression.
- std::vector<Token> suffix_;
-
- // For top-level expressions only, after_ lists whole-line comments
- // following the expression.
- std::vector<Token> after_;
-
- DISALLOW_COPY_AND_ASSIGN(Comments);
-};
-
-// ParseNode -------------------------------------------------------------------
-
-// A node in the AST.
-class ParseNode {
- public:
- ParseNode();
- virtual ~ParseNode();
-
- virtual const AccessorNode* AsAccessor() const;
- virtual const BinaryOpNode* AsBinaryOp() const;
- virtual const BlockCommentNode* AsBlockComment() const;
- virtual const BlockNode* AsBlock() const;
- virtual const ConditionNode* AsConditionNode() const;
- virtual const EndNode* AsEnd() const;
- virtual const FunctionCallNode* AsFunctionCall() const;
- virtual const IdentifierNode* AsIdentifier() const;
- virtual const ListNode* AsList() const;
- virtual const LiteralNode* AsLiteral() const;
- virtual const UnaryOpNode* AsUnaryOp() const;
-
- virtual Value Execute(Scope* scope, Err* err) const = 0;
-
- virtual LocationRange GetRange() const = 0;
-
- // Returns an error with the given messages and the range set to something
- // that indicates this node.
- virtual Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const = 0;
-
- // Generates a representation of this node in base::Value, to be used for
- // exporting the tree as a JSON or formatted text with indents.
- virtual base::Value GetJSONNode() const = 0;
-
- const Comments* comments() const { return comments_.get(); }
- Comments* comments_mutable();
-
- protected:
- // Helper functions for GetJSONNode. Creates and fills a Value object with
- // given type (and value).
- base::Value CreateJSONNode(const char* type) const;
- base::Value CreateJSONNode(const char* type, const base::StringPiece& value)
- const;
-
- private:
- // Helper function for CreateJSONNode.
- void AddCommentsJSONNodes(base::Value* out_value) const;
-
- std::unique_ptr<Comments> comments_;
-
- DISALLOW_COPY_AND_ASSIGN(ParseNode);
-};
-
-// AccessorNode ----------------------------------------------------------------
-
-// Access an array or scope element.
-//
-// Currently, such values are only read-only. In that you can do:
-// a = obj1.a
-// b = obj2[0]
-// But not
-// obj1.a = 5
-// obj2[0] = 6
-//
-// In the current design where the dot operator is used only for templates, we
-// explicitly don't want to allow you to do "invoker.foo = 5", so if we added
-// support for accessors to be lvalues, we would also need to add some concept
-// of a constant scope. Supporting this would also add a lot of complications
-// to the operator= implementation, since some accessors might return values
-// in the const root scope that shouldn't be modified. Without a strong
-// use-case for this, it seems simpler to just disallow it.
-//
-// Additionally, the left-hand-side of the accessor must currently be an
-// identifier. So you can't do things like:
-// function_call()[1]
-// a = b.c.d
-// These are easier to implement if we needed them but given the very limited
-// use cases for this, it hasn't seemed worth the bother.
-class AccessorNode : public ParseNode {
- public:
- AccessorNode();
- ~AccessorNode() override;
-
- const AccessorNode* AsAccessor() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- // Base is the thing on the left of the [] or dot, currently always required
- // to be an identifier token.
- const Token& base() const { return base_; }
- void set_base(const Token& b) { base_ = b; }
-
- // Index is the expression inside the []. Will be null if member is set.
- const ParseNode* index() const { return index_.get(); }
- void set_index(std::unique_ptr<ParseNode> i) { index_ = std::move(i); }
-
- // The member is the identifier on the right hand side of the dot. Will be
- // null if the index is set.
- const IdentifierNode* member() const { return member_.get(); }
- void set_member(std::unique_ptr<IdentifierNode> i) { member_ = std::move(i); }
-
- void SetNewLocation(int line_number);
-
- // Evaluates the index for list accessor operations and range checks it
- // against the max length of the list. If the index is OK, sets
- // |*computed_index| and returns true. Otherwise sets the |*err| and returns
- // false.
- bool ComputeAndValidateListIndex(Scope* scope,
- size_t max_len,
- size_t* computed_index,
- Err* err) const;
-
- private:
- Value ExecuteArrayAccess(Scope* scope, Err* err) const;
- Value ExecuteScopeAccess(Scope* scope, Err* err) const;
-
- Token base_;
-
- // Either index or member will be set according to what type of access this
- // is.
- std::unique_ptr<ParseNode> index_;
- std::unique_ptr<IdentifierNode> member_;
-
- DISALLOW_COPY_AND_ASSIGN(AccessorNode);
-};
-
-// BinaryOpNode ----------------------------------------------------------------
-
-class BinaryOpNode : public ParseNode {
- public:
- BinaryOpNode();
- ~BinaryOpNode() override;
-
- const BinaryOpNode* AsBinaryOp() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& op() const { return op_; }
- void set_op(const Token& t) { op_ = t; }
-
- const ParseNode* left() const { return left_.get(); }
- void set_left(std::unique_ptr<ParseNode> left) { left_ = std::move(left); }
-
- const ParseNode* right() const { return right_.get(); }
- void set_right(std::unique_ptr<ParseNode> right) {
- right_ = std::move(right);
- }
-
- private:
- std::unique_ptr<ParseNode> left_;
- Token op_;
- std::unique_ptr<ParseNode> right_;
-
- DISALLOW_COPY_AND_ASSIGN(BinaryOpNode);
-};
-
-// BlockNode -------------------------------------------------------------------
-
-class BlockNode : public ParseNode {
- public:
- // How Execute manages the scopes and results.
- enum ResultMode {
- // Creates a new scope for the execution of this block and returns it as
- // a Value from Execute().
- RETURNS_SCOPE,
-
- // Executes in the context of the calling scope (variables set will go
- // into the invoking scope) and Execute will return an empty Value.
- DISCARDS_RESULT
- };
-
- BlockNode(ResultMode result_mode);
- ~BlockNode() override;
-
- const BlockNode* AsBlock() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- void set_begin_token(const Token& t) { begin_token_ = t; }
- void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
- const EndNode* End() const { return end_.get(); }
-
- ResultMode result_mode() const { return result_mode_; }
-
- const std::vector<std::unique_ptr<ParseNode>>& statements() const {
- return statements_;
- }
- void append_statement(std::unique_ptr<ParseNode> s) {
- statements_.push_back(std::move(s));
- }
-
- private:
- const ResultMode result_mode_;
-
- // Tokens corresponding to { and }, if any (may be NULL). The end is stored
- // in a custom parse node so that it can have comments hung off of it.
- Token begin_token_;
- std::unique_ptr<EndNode> end_;
-
- std::vector<std::unique_ptr<ParseNode>> statements_;
-
- DISALLOW_COPY_AND_ASSIGN(BlockNode);
-};
-
-// ConditionNode ---------------------------------------------------------------
-
-class ConditionNode : public ParseNode {
- public:
- ConditionNode();
- ~ConditionNode() override;
-
- const ConditionNode* AsConditionNode() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- void set_if_token(const Token& token) { if_token_ = token; }
-
- const ParseNode* condition() const { return condition_.get(); }
- void set_condition(std::unique_ptr<ParseNode> c) {
- condition_ = std::move(c);
- }
-
- const BlockNode* if_true() const { return if_true_.get(); }
- void set_if_true(std::unique_ptr<BlockNode> t) { if_true_ = std::move(t); }
-
- // This is either empty, a block (for the else clause), or another
- // condition.
- const ParseNode* if_false() const { return if_false_.get(); }
- void set_if_false(std::unique_ptr<ParseNode> f) { if_false_ = std::move(f); }
-
- private:
- // Token corresponding to the "if" string.
- Token if_token_;
-
- std::unique_ptr<ParseNode> condition_; // Always non-null.
- std::unique_ptr<BlockNode> if_true_; // Always non-null.
- std::unique_ptr<ParseNode> if_false_; // May be null.
-
- DISALLOW_COPY_AND_ASSIGN(ConditionNode);
-};
-
-// FunctionCallNode ------------------------------------------------------------
-
-class FunctionCallNode : public ParseNode {
- public:
- FunctionCallNode();
- ~FunctionCallNode() override;
-
- const FunctionCallNode* AsFunctionCall() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& function() const { return function_; }
- void set_function(Token t) { function_ = t; }
-
- const ListNode* args() const { return args_.get(); }
- void set_args(std::unique_ptr<ListNode> a) { args_ = std::move(a); }
-
- const BlockNode* block() const { return block_.get(); }
- void set_block(std::unique_ptr<BlockNode> b) { block_ = std::move(b); }
-
- void SetNewLocation(int line_number);
-
- private:
- Token function_;
- std::unique_ptr<ListNode> args_;
- std::unique_ptr<BlockNode> block_; // May be null.
-
- DISALLOW_COPY_AND_ASSIGN(FunctionCallNode);
-};
-
-// IdentifierNode --------------------------------------------------------------
-
-class IdentifierNode : public ParseNode {
- public:
- IdentifierNode();
- explicit IdentifierNode(const Token& token);
- ~IdentifierNode() override;
-
- const IdentifierNode* AsIdentifier() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& value() const { return value_; }
- void set_value(const Token& t) { value_ = t; }
-
- void SetNewLocation(int line_number);
-
- private:
- Token value_;
-
- DISALLOW_COPY_AND_ASSIGN(IdentifierNode);
-};
-
-// ListNode --------------------------------------------------------------------
-
-class ListNode : public ParseNode {
- public:
- ListNode();
- ~ListNode() override;
-
- const ListNode* AsList() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- void set_begin_token(const Token& t) { begin_token_ = t; }
- const Token& Begin() const { return begin_token_; }
- void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
- const EndNode* End() const { return end_.get(); }
-
- void append_item(std::unique_ptr<ParseNode> s) {
- contents_.push_back(std::move(s));
- }
- const std::vector<std::unique_ptr<const ParseNode>>& contents() const {
- return contents_;
- }
-
- void SortAsStringsList();
- void SortAsDepsList();
-
- // During formatting, do we want this list to always be multliline? This is
- // used to make assignments to deps, sources, etc. always be multiline lists,
- // rather than collapsed to a single line when they're one element.
- bool prefer_multiline() const { return prefer_multiline_; }
- void set_prefer_multiline(bool prefer_multiline) {
- prefer_multiline_ = prefer_multiline;
- }
-
- struct SortRange {
- size_t begin;
- size_t end;
- SortRange(size_t begin, size_t end) : begin(begin), end(end) {}
- };
- // Only public for testing.
- std::vector<SortRange> GetSortRanges() const;
-
- private:
- template <typename Comparator>
- void SortList(Comparator comparator);
-
- // Tokens corresponding to the [ and ]. The end token is stored in inside an
- // custom parse node so that it can have comments hung off of it.
- Token begin_token_;
- std::unique_ptr<EndNode> end_;
- bool prefer_multiline_;
-
- std::vector<std::unique_ptr<const ParseNode>> contents_;
-
- DISALLOW_COPY_AND_ASSIGN(ListNode);
-};
-
-// LiteralNode -----------------------------------------------------------------
-
-class LiteralNode : public ParseNode {
- public:
- LiteralNode();
- explicit LiteralNode(const Token& token);
- ~LiteralNode() override;
-
- const LiteralNode* AsLiteral() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& value() const { return value_; }
- void set_value(const Token& t) { value_ = t; }
-
- void SetNewLocation(int line_number);
-
- private:
- Token value_;
-
- DISALLOW_COPY_AND_ASSIGN(LiteralNode);
-};
-
-// UnaryOpNode -----------------------------------------------------------------
-
-class UnaryOpNode : public ParseNode {
- public:
- UnaryOpNode();
- ~UnaryOpNode() override;
-
- const UnaryOpNode* AsUnaryOp() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& op() const { return op_; }
- void set_op(const Token& t) { op_ = t; }
-
- const ParseNode* operand() const { return operand_.get(); }
- void set_operand(std::unique_ptr<ParseNode> operand) {
- operand_ = std::move(operand);
- }
-
- private:
- Token op_;
- std::unique_ptr<ParseNode> operand_;
-
- DISALLOW_COPY_AND_ASSIGN(UnaryOpNode);
-};
-
-// BlockCommentNode ------------------------------------------------------------
-
-// This node type is only used for standalone comments (that is, those not
-// specifically attached to another syntax element. The most common of these
-// is a standard header block. This node contains only the last line of such
-// a comment block as the anchor, and other lines of the block comment are
-// hung off of it as Before comments, similar to other syntax elements.
-class BlockCommentNode : public ParseNode {
- public:
- BlockCommentNode();
- ~BlockCommentNode() override;
-
- const BlockCommentNode* AsBlockComment() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& comment() const { return comment_; }
- void set_comment(const Token& t) { comment_ = t; }
-
- private:
- Token comment_;
-
- DISALLOW_COPY_AND_ASSIGN(BlockCommentNode);
-};
-
-// EndNode ---------------------------------------------------------------------
-
-// This node type is used as the end_ object for lists and blocks (rather than
-// just the end ']', '}', or ')' token). This is so that during formatting
-// traversal there is a node that appears at the end of the block to which
-// comments can be attached.
-class EndNode : public ParseNode {
- public:
- explicit EndNode(const Token& token);
- ~EndNode() override;
-
- const EndNode* AsEnd() const override;
- Value Execute(Scope* scope, Err* err) const override;
- LocationRange GetRange() const override;
- Err MakeErrorDescribing(
- const std::string& msg,
- const std::string& help = std::string()) const override;
- base::Value GetJSONNode() const override;
-
- const Token& value() const { return value_; }
- void set_value(const Token& t) { value_ = t; }
-
- private:
- Token value_;
-
- DISALLOW_COPY_AND_ASSIGN(EndNode);
-};
-
-#endif // TOOLS_GN_PARSE_TREE_H_
diff --git a/gn/tools/gn/parse_tree_unittest.cc b/gn/tools/gn/parse_tree_unittest.cc
deleted file mode 100644
index e9f03dd3793..00000000000
--- a/gn/tools/gn/parse_tree_unittest.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// 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 "tools/gn/parse_tree.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/input_file.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(ParseTree, Accessor) {
- TestWithScope setup;
-
- // Make a pretend parse node with proper tracking that we can blame for the
- // given value.
- InputFile input_file(SourceFile("//foo"));
- Token base_token(Location(&input_file, 1, 1, 1), Token::IDENTIFIER, "a");
- Token member_token(Location(&input_file, 1, 1, 1), Token::IDENTIFIER, "b");
-
- AccessorNode accessor;
- accessor.set_base(base_token);
-
- std::unique_ptr<IdentifierNode> member_identifier =
- std::make_unique<IdentifierNode>(member_token);
- accessor.set_member(std::move(member_identifier));
-
- // The access should fail because a is not defined.
- Err err;
- Value result = accessor.Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ(Value::NONE, result.type());
-
- // Define a as a Scope. It should still fail because b isn't defined.
- err = Err();
- setup.scope()->SetValue(
- "a", Value(nullptr, std::make_unique<Scope>(setup.scope())), nullptr);
- result = accessor.Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ(Value::NONE, result.type());
-
- // Define b, accessor should succeed now.
- const int64_t kBValue = 42;
- err = Err();
- setup.scope()
- ->GetMutableValue("a", Scope::SEARCH_NESTED, false)
- ->scope_value()
- ->SetValue("b", Value(nullptr, kBValue), nullptr);
- result = accessor.Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(Value::INTEGER, result.type());
- EXPECT_EQ(kBValue, result.int_value());
-}
-
-TEST(ParseTree, BlockUnusedVars) {
- TestWithScope setup;
-
- // Printing both values should be OK.
- //
- // The crazy template definition here is a way to execute a block without
- // defining a target. Templates require that both the target_name and the
- // invoker be used, which is what the assertion statement inside the template
- // does.
- TestParseInput input_all_used(
- "template(\"foo\") { assert(target_name != 0 && invoker != 0) }\n"
- "foo(\"a\") {\n"
- " a = 12\n"
- " b = 13\n"
- " print(\"$a $b\")\n"
- "}");
- EXPECT_FALSE(input_all_used.has_error());
-
- Err err;
- input_all_used.parsed()->Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
-
- // Skipping one should throw an unused var error.
- TestParseInput input_unused(
- "foo(\"a\") {\n"
- " a = 12\n"
- " b = 13\n"
- " print(\"$a\")\n"
- "}");
- EXPECT_FALSE(input_unused.has_error());
-
- input_unused.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
-
- // Also verify that the unused variable has the correct origin set. The
- // origin will point to the value assigned to the variable (in this case, the
- // "13" assigned to "b".
- EXPECT_EQ(3, err.location().line_number());
- EXPECT_EQ(7, err.location().column_number());
-}
-
-TEST(ParseTree, OriginForDereference) {
- TestWithScope setup;
- TestParseInput input(
- "a = 6\n"
- "get_target_outputs(a)");
- EXPECT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
-
- // The origin for the "not a string" error message should be where the value
- // was dereferenced (the "a" on the second line).
- EXPECT_EQ(2, err.location().line_number());
- EXPECT_EQ(20, err.location().column_number());
-}
-
-TEST(ParseTree, SortRangeExtraction) {
- TestWithScope setup;
-
- // Ranges are [begin, end).
-
- {
- TestParseInput input(
- "sources = [\n"
- " \"a\",\n"
- " \"b\",\n"
- " \n"
- " #\n"
- " # Block\n"
- " #\n"
- " \n"
- " \"c\","
- " \"d\","
- "]\n");
- EXPECT_FALSE(input.has_error());
- ASSERT_TRUE(input.parsed()->AsBlock());
- ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
- const BinaryOpNode* binop =
- input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
- ASSERT_TRUE(binop->right()->AsList());
- const ListNode* list = binop->right()->AsList();
- EXPECT_EQ(5u, list->contents().size());
- auto ranges = list->GetSortRanges();
- ASSERT_EQ(2u, ranges.size());
- EXPECT_EQ(0u, ranges[0].begin);
- EXPECT_EQ(2u, ranges[0].end);
- EXPECT_EQ(3u, ranges[1].begin);
- EXPECT_EQ(5u, ranges[1].end);
- }
-
- {
- TestParseInput input(
- "sources = [\n"
- " \"a\",\n"
- " \"b\",\n"
- " \n"
- " # Attached comment.\n"
- " \"c\","
- " \"d\","
- "]\n");
- EXPECT_FALSE(input.has_error());
- ASSERT_TRUE(input.parsed()->AsBlock());
- ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
- const BinaryOpNode* binop =
- input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
- ASSERT_TRUE(binop->right()->AsList());
- const ListNode* list = binop->right()->AsList();
- EXPECT_EQ(4u, list->contents().size());
- auto ranges = list->GetSortRanges();
- ASSERT_EQ(2u, ranges.size());
- EXPECT_EQ(0u, ranges[0].begin);
- EXPECT_EQ(2u, ranges[0].end);
- EXPECT_EQ(2u, ranges[1].begin);
- EXPECT_EQ(4u, ranges[1].end);
- }
-
- {
- TestParseInput input(
- "sources = [\n"
- " # At end of list.\n"
- " \"zzzzzzzzzzz.cc\","
- "]\n");
- EXPECT_FALSE(input.has_error());
- ASSERT_TRUE(input.parsed()->AsBlock());
- ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
- const BinaryOpNode* binop =
- input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
- ASSERT_TRUE(binop->right()->AsList());
- const ListNode* list = binop->right()->AsList();
- EXPECT_EQ(1u, list->contents().size());
- auto ranges = list->GetSortRanges();
- ASSERT_EQ(1u, ranges.size());
- EXPECT_EQ(0u, ranges[0].begin);
- EXPECT_EQ(1u, ranges[0].end);
- }
-
- {
- TestParseInput input(
- "sources = [\n"
- " # Block at start.\n"
- " \n"
- " \"z.cc\","
- " \"y.cc\","
- "]\n");
- EXPECT_FALSE(input.has_error());
- ASSERT_TRUE(input.parsed()->AsBlock());
- ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp());
- const BinaryOpNode* binop =
- input.parsed()->AsBlock()->statements()[0]->AsBinaryOp();
- ASSERT_TRUE(binop->right()->AsList());
- const ListNode* list = binop->right()->AsList();
- EXPECT_EQ(3u, list->contents().size());
- auto ranges = list->GetSortRanges();
- ASSERT_EQ(1u, ranges.size());
- EXPECT_EQ(1u, ranges[0].begin);
- EXPECT_EQ(3u, ranges[0].end);
- }
-}
-
-TEST(ParseTree, Integers) {
- static const char* const kGood[] = {
- "0",
- "10",
- "-54321",
- "9223372036854775807", // INT64_MAX
- "-9223372036854775808", // INT64_MIN
- };
- for (auto* s : kGood) {
- TestParseInput input(std::string("x = ") + s);
- EXPECT_FALSE(input.has_error());
-
- TestWithScope setup;
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- EXPECT_FALSE(err.has_error());
- }
-
- static const char* const kBad[] = {
- "-0",
- "010",
- "-010",
- "9223372036854775808", // INT64_MAX + 1
- "-9223372036854775809", // INT64_MIN - 1
- };
- for (auto* s : kBad) {
- TestParseInput input(std::string("x = ") + s);
- EXPECT_FALSE(input.has_error());
-
- TestWithScope setup;
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
- }
-}
diff --git a/gn/tools/gn/parser.cc b/gn/tools/gn/parser.cc
deleted file mode 100644
index 0065afd7de7..00000000000
--- a/gn/tools/gn/parser.cc
+++ /dev/null
@@ -1,942 +0,0 @@
-// 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 "tools/gn/parser.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/logging.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/operators.h"
-#include "tools/gn/token.h"
-
-const char kGrammar_Help[] =
- R"*(Language and grammar for GN build files
-
-Tokens
-
- GN build files are read as sequences of tokens. While splitting the file
- into tokens, the next token is the longest sequence of characters that form a
- valid token.
-
-White space and comments
-
- White space is comprised of spaces (U+0020), horizontal tabs (U+0009),
- carriage returns (U+000D), and newlines (U+000A).
-
- Comments start at the character "#" and stop at the next newline.
-
- White space and comments are ignored except that they may separate tokens
- that would otherwise combine into a single token.
-
-Identifiers
-
- Identifiers name variables and functions.
-
- identifier = letter { letter | digit } .
- letter = "A" ... "Z" | "a" ... "z" | "_" .
- digit = "0" ... "9" .
-
-Keywords
-
- The following keywords are reserved and may not be used as identifiers:
-
- else false if true
-
-Integer literals
-
- An integer literal represents a decimal integer value.
-
- integer = [ "-" ] digit { digit } .
-
- Leading zeros and negative zero are disallowed.
-
-String literals
-
- A string literal represents a string value consisting of the quoted
- characters with possible escape sequences and variable expansions.
-
- string = `"` { char | escape | expansion } `"` .
- escape = `\` ( "$" | `"` | char ) .
- BracketExpansion = "{" ( identifier | ArrayAccess | ScopeAccess "
- ") "}" .
- Hex = "0x" [0-9A-Fa-f][0-9A-Fa-f]
- expansion = "$" ( identifier | BracketExpansion | Hex ) .
- char = /* any character except "$", `"`, or newline "
- "*/ .
-
- After a backslash, certain sequences represent special characters:
-
- \" U+0022 quotation mark
- \$ U+0024 dollar sign
- \\ U+005C backslash
-
- All other backslashes represent themselves.
-
- To insert an arbitrary byte value, use $0xFF. For example, to insert a
- newline character: "Line one$0x0ALine two".
-
- An expansion will evaluate the variable following the '$' and insert a
- stringified version of it into the result. For example, to concat two path
- components with a slash separating them:
- "$var_one/$var_two"
- Use the "${var_one}" format to be explicitly deliniate the variable for
- otherwise-ambiguous cases.
-
-Punctuation
-
- The following character sequences represent punctuation:
-
- + += == != ( )
- - -= < <= [ ]
- ! = > >= { }
- && || . ,
-
-Grammar
-
- The input tokens form a syntax tree following a context-free grammar:
-
- File = StatementList .
-
- Statement = Assignment | Call | Condition .
- LValue = identifier | ArrayAccess | ScopeAccess .
- Assignment = LValue AssignOp Expr .
- Call = identifier "(" [ ExprList ] ")" [ Block ] .
- Condition = "if" "(" Expr ")" Block
- [ "else" ( Condition | Block ) ] .
- Block = "{" StatementList "}" .
- StatementList = { Statement } .
-
- ArrayAccess = identifier "[" Expr "]" .
- ScopeAccess = identifier "." identifier .
- Expr = UnaryExpr | Expr BinaryOp Expr .
- UnaryExpr = PrimaryExpr | UnaryOp UnaryExpr .
- PrimaryExpr = identifier | integer | string | Call
- | ArrayAccess | ScopeAccess | Block
- | "(" Expr ")"
- | "[" [ ExprList [ "," ] ] "]" .
- ExprList = Expr { "," Expr } .
-
- AssignOp = "=" | "+=" | "-=" .
- UnaryOp = "!" .
- BinaryOp = "+" | "-" // highest priority
- | "<" | "<=" | ">" | ">="
- | "==" | "!="
- | "&&"
- | "||" . // lowest priority
-
- All binary operators are left-associative.
-
-Types
-
- The GN language is dynamically typed. The following types are used:
-
- - Boolean: Uses the keywords "true" and "false". There is no implicit
- conversion between booleans and integers.
-
- - Integers: All numbers in GN are signed 64-bit integers.
-
- - Strings: Strings are 8-bit with no enforced encoding. When a string is
- used to interact with other systems with particular encodings (like the
- Windows and Mac filesystems) it is assumed to be UTF-8. See "String
- literals" above for more.
-
- - Lists: Lists are arbitrary-length ordered lists of values. See "Lists"
- below for more.
-
- - Scopes: Scopes are like dictionaries that use variable names for keys. See
- "Scopes" below for more.
-
-Lists
-
- Lists are created with [] and using commas to separate items:
-
- mylist = [ 0, 1, 2, "some string" ]
-
- A comma after the last item is optional. Lists are dereferenced using 0-based
- indexing:
-
- mylist[0] += 1
- var = mylist[2]
-
- Lists can be concatenated using the '+' and '+=' operators. Bare values can
- not be concatenated with lists, to add a single item, it must be put into a
- list of length one.
-
- Items can be removed from lists using the '-' and '-=' operators. This will
- remove all occurrences of every item in the right-hand list from the
- left-hand list. It is an error to remove an item not in the list. This is to
- prevent common typos and to detect dead code that is removing things that no
- longer apply.
-
- It is an error to use '=' to replace a nonempty list with another nonempty
- list. This is to prevent accidentally overwriting data when in most cases
- '+=' was intended. To overwrite a list on purpose, first assign it to the
- empty list:
-
- mylist = []
- mylist = otherlist
-
- When assigning to a list named 'sources' using '=' or '+=', list items may be
- automatically filtered out. See "gn help set_sources_assignment_filter" for
- more.
-
-Scopes
-
- All execution happens in the context of a scope which holds the current state
- (like variables). With the exception of loops and conditions, '{' introduces
- a new scope that has a parent reference to the old scope.
-
- Variable reads recursively search all nested scopes until the variable is
- found or there are no more scopes. Variable writes always go into the current
- scope. This means that after the closing '}' (again excepting loops and
- conditions), all local variables will be restored to the previous values.
- This also means that "foo = foo" can do useful work by copying a variable
- into the current scope that was defined in a containing scope.
-
- Scopes can also be assigned to variables. Such scopes can be created by
- functions like exec_script, when invoking a template (the template code
- refers to the variables set by the invoking code by the implicitly-created
- "invoker" scope), or explicitly like:
-
- empty_scope = {}
- myvalues = {
- foo = 21
- bar = "something"
- }
-
- Inside such a scope definition can be any GN code including conditionals and
- function calls. After the close of the scope, it will contain all variables
- explicitly set by the code contained inside it. After this, the values can be
- read, modified, or added to:
-
- myvalues.foo += 2
- empty_scope.new_thing = [ 1, 2, 3 ]
-
- Scope equality is defined as single-level scopes identical within the current
- scope. That is, all values in the first scope must be present and identical
- within the second, and vice versa. Note that this means inherited scopes are
- always unequal by definition.
-)*";
-
-// Precedence constants.
-//
-// Currently all operators are left-associative so this list is sequential. To
-// implement a right-assocative operators in a Pratt parser we would leave gaps
-// in between the constants, and right-associative operators get a precedence
-// of "<left-associated-precedence> - 1".
-enum Precedence {
- PRECEDENCE_ASSIGNMENT = 1, // Lowest precedence.
- PRECEDENCE_OR = 2,
- PRECEDENCE_AND = 3,
- PRECEDENCE_EQUALITY = 4,
- PRECEDENCE_RELATION = 5,
- PRECEDENCE_SUM = 6,
- PRECEDENCE_PREFIX = 7,
- PRECEDENCE_CALL = 8,
- PRECEDENCE_DOT = 9, // Highest precedence.
-};
-
-// The top-level for blocks/ifs is recursive descent, the expression parser is
-// a Pratt parser. The basic idea there is to have the precedences (and
-// associativities) encoded relative to each other and only parse up until you
-// hit something of that precedence. There's a dispatch table in expressions_
-// at the top of parser.cc that describes how each token dispatches if it's
-// seen as either a prefix or infix operator, and if it's infix, what its
-// precedence is.
-//
-// References:
-// http://javascript.crockford.com/tdop/tdop.html
-// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
-
-// Indexed by Token::Type.
-ParserHelper Parser::expressions_[] = {
- {nullptr, nullptr, -1}, // INVALID
- {&Parser::Literal, nullptr, -1}, // INTEGER
- {&Parser::Literal, nullptr, -1}, // STRING
- {&Parser::Literal, nullptr, -1}, // TRUE_TOKEN
- {&Parser::Literal, nullptr, -1}, // FALSE_TOKEN
- {nullptr, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // EQUAL
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_SUM}, // PLUS
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_SUM}, // MINUS
- {nullptr, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // PLUS_EQUALS
- {nullptr, &Parser::Assignment, PRECEDENCE_ASSIGNMENT}, // MINUS_EQUALS
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // EQUAL_EQUAL
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_EQUALITY}, // NOT_EQUAL
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_EQUAL
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_EQUAL
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // LESS_THAN
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_RELATION}, // GREATER_THAN
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_AND}, // BOOLEAN_AND
- {nullptr, &Parser::BinaryOperator, PRECEDENCE_OR}, // BOOLEAN_OR
- {&Parser::Not, nullptr, -1}, // BANG
- {nullptr, &Parser::DotOperator, PRECEDENCE_DOT}, // DOT
- {&Parser::Group, nullptr, -1}, // LEFT_PAREN
- {nullptr, nullptr, -1}, // RIGHT_PAREN
- {&Parser::List, &Parser::Subscript, PRECEDENCE_CALL}, // LEFT_BRACKET
- {nullptr, nullptr, -1}, // RIGHT_BRACKET
- {&Parser::Block, nullptr, -1}, // LEFT_BRACE
- {nullptr, nullptr, -1}, // RIGHT_BRACE
- {nullptr, nullptr, -1}, // IF
- {nullptr, nullptr, -1}, // ELSE
- {&Parser::Name, &Parser::IdentifierOrCall, PRECEDENCE_CALL}, // IDENTIFIER
- {nullptr, nullptr, -1}, // COMMA
- {nullptr, nullptr, -1}, // UNCLASSIFIED_COMMENT
- {nullptr, nullptr, -1}, // LINE_COMMENT
- {nullptr, nullptr, -1}, // SUFFIX_COMMENT
- {&Parser::BlockComment, nullptr, -1}, // BLOCK_COMMENT
-};
-
-Parser::Parser(const std::vector<Token>& tokens, Err* err)
- : invalid_token_(Location(), Token::INVALID, base::StringPiece()),
- err_(err),
- cur_(0) {
- for (const auto& token : tokens) {
- switch (token.type()) {
- case Token::LINE_COMMENT:
- line_comment_tokens_.push_back(token);
- break;
- case Token::SUFFIX_COMMENT:
- suffix_comment_tokens_.push_back(token);
- break;
- default:
- // Note that BLOCK_COMMENTs (top-level standalone comments) are passed
- // through the real parser.
- tokens_.push_back(token);
- break;
- }
- }
-}
-
-Parser::~Parser() = default;
-
-// static
-std::unique_ptr<ParseNode> Parser::Parse(const std::vector<Token>& tokens,
- Err* err) {
- Parser p(tokens, err);
- return p.ParseFile();
-}
-
-// static
-std::unique_ptr<ParseNode> Parser::ParseExpression(
- const std::vector<Token>& tokens,
- Err* err) {
- Parser p(tokens, err);
- std::unique_ptr<ParseNode> expr = p.ParseExpression();
- if (!p.at_end() && !err->has_error()) {
- *err = Err(p.cur_token(), "Trailing garbage");
- return nullptr;
- }
- return expr;
-}
-
-// static
-std::unique_ptr<ParseNode> Parser::ParseValue(const std::vector<Token>& tokens,
- Err* err) {
- for (const Token& token : tokens) {
- switch (token.type()) {
- case Token::INTEGER:
- case Token::STRING:
- case Token::TRUE_TOKEN:
- case Token::FALSE_TOKEN:
- case Token::LEFT_BRACKET:
- case Token::RIGHT_BRACKET:
- case Token::COMMA:
- continue;
- default:
- *err = Err(token, "Invalid token in literal value");
- return nullptr;
- }
- }
-
- return ParseExpression(tokens, err);
-}
-
-bool Parser::IsAssignment(const ParseNode* node) const {
- return node && node->AsBinaryOp() &&
- (node->AsBinaryOp()->op().type() == Token::EQUAL ||
- node->AsBinaryOp()->op().type() == Token::PLUS_EQUALS ||
- node->AsBinaryOp()->op().type() == Token::MINUS_EQUALS);
-}
-
-bool Parser::IsStatementBreak(Token::Type token_type) const {
- switch (token_type) {
- case Token::IDENTIFIER:
- case Token::LEFT_BRACE:
- case Token::RIGHT_BRACE:
- case Token::IF:
- case Token::ELSE:
- return true;
- default:
- return false;
- }
-}
-
-bool Parser::LookAhead(Token::Type type) {
- if (at_end())
- return false;
- return cur_token().type() == type;
-}
-
-bool Parser::Match(Token::Type type) {
- if (!LookAhead(type))
- return false;
- Consume();
- return true;
-}
-
-const Token& Parser::Consume(Token::Type type, const char* error_message) {
- Token::Type types[1] = {type};
- return Consume(types, 1, error_message);
-}
-
-const Token& Parser::Consume(Token::Type* types,
- size_t num_types,
- const char* error_message) {
- if (has_error()) {
- // Don't overwrite current error, but make progress through tokens so that
- // a loop that's expecting a particular token will still terminate.
- if (!at_end())
- cur_++;
- return invalid_token_;
- }
- if (at_end()) {
- const char kEOFMsg[] = "I hit EOF instead.";
- if (tokens_.empty())
- *err_ = Err(Location(), error_message, kEOFMsg);
- else
- *err_ = Err(tokens_[tokens_.size() - 1], error_message, kEOFMsg);
- return invalid_token_;
- }
-
- for (size_t i = 0; i < num_types; ++i) {
- if (cur_token().type() == types[i])
- return Consume();
- }
- *err_ = Err(cur_token(), error_message);
- return invalid_token_;
-}
-
-const Token& Parser::Consume() {
- return tokens_[cur_++];
-}
-
-std::unique_ptr<ParseNode> Parser::ParseExpression() {
- return ParseExpression(0);
-}
-
-std::unique_ptr<ParseNode> Parser::ParseExpression(int precedence) {
- if (at_end())
- return std::unique_ptr<ParseNode>();
-
- const Token& token = Consume();
- PrefixFunc prefix = expressions_[token.type()].prefix;
-
- if (prefix == nullptr) {
- *err_ = Err(token, std::string("Unexpected token '") +
- token.value().as_string() + std::string("'"));
- return std::unique_ptr<ParseNode>();
- }
-
- std::unique_ptr<ParseNode> left = (this->*prefix)(token);
- if (has_error())
- return left;
-
- while (!at_end() && !IsStatementBreak(cur_token().type()) &&
- precedence <= expressions_[cur_token().type()].precedence) {
- const Token& next_token = Consume();
- InfixFunc infix = expressions_[next_token.type()].infix;
- if (infix == nullptr) {
- *err_ = Err(next_token, std::string("Unexpected token '") +
- next_token.value().as_string() +
- std::string("'"));
- return std::unique_ptr<ParseNode>();
- }
- left = (this->*infix)(std::move(left), next_token);
- if (has_error())
- return std::unique_ptr<ParseNode>();
- }
-
- return left;
-}
-
-std::unique_ptr<ParseNode> Parser::Block(const Token& token) {
- // This entrypoint into ParseBlock means it's part of an expression and we
- // always want the result.
- return ParseBlock(token, BlockNode::RETURNS_SCOPE);
-}
-
-std::unique_ptr<ParseNode> Parser::Literal(const Token& token) {
- return std::make_unique<LiteralNode>(token);
-}
-
-std::unique_ptr<ParseNode> Parser::Name(const Token& token) {
- return IdentifierOrCall(std::unique_ptr<ParseNode>(), token);
-}
-
-std::unique_ptr<ParseNode> Parser::BlockComment(const Token& token) {
- std::unique_ptr<BlockCommentNode> comment =
- std::make_unique<BlockCommentNode>();
- comment->set_comment(token);
- return std::move(comment);
-}
-
-std::unique_ptr<ParseNode> Parser::Group(const Token& token) {
- std::unique_ptr<ParseNode> expr = ParseExpression();
- if (has_error())
- return std::unique_ptr<ParseNode>();
- Consume(Token::RIGHT_PAREN, "Expected ')'");
- return expr;
-}
-
-std::unique_ptr<ParseNode> Parser::Not(const Token& token) {
- std::unique_ptr<ParseNode> expr = ParseExpression(PRECEDENCE_PREFIX + 1);
- if (has_error())
- return std::unique_ptr<ParseNode>();
- if (!expr) {
- if (!has_error())
- *err_ = Err(token, "Expected right-hand side for '!'.");
- return std::unique_ptr<ParseNode>();
- }
- std::unique_ptr<UnaryOpNode> unary_op = std::make_unique<UnaryOpNode>();
- unary_op->set_op(token);
- unary_op->set_operand(std::move(expr));
- return std::move(unary_op);
-}
-
-std::unique_ptr<ParseNode> Parser::List(const Token& node) {
- std::unique_ptr<ParseNode> list(ParseList(node, Token::RIGHT_BRACKET, true));
- if (!has_error() && !at_end())
- Consume(Token::RIGHT_BRACKET, "Expected ']'");
- return list;
-}
-
-std::unique_ptr<ParseNode> Parser::BinaryOperator(
- std::unique_ptr<ParseNode> left,
- const Token& token) {
- std::unique_ptr<ParseNode> right =
- ParseExpression(expressions_[token.type()].precedence + 1);
- if (!right) {
- if (!has_error()) {
- *err_ = Err(token, "Expected right-hand side for '" +
- token.value().as_string() + "'");
- }
- return std::unique_ptr<ParseNode>();
- }
- std::unique_ptr<BinaryOpNode> binary_op = std::make_unique<BinaryOpNode>();
- binary_op->set_op(token);
- binary_op->set_left(std::move(left));
- binary_op->set_right(std::move(right));
- return std::move(binary_op);
-}
-
-std::unique_ptr<ParseNode> Parser::IdentifierOrCall(
- std::unique_ptr<ParseNode> left,
- const Token& token) {
- std::unique_ptr<ListNode> list = std::make_unique<ListNode>();
- list->set_begin_token(token);
- list->set_end(std::make_unique<EndNode>(token));
- std::unique_ptr<BlockNode> block;
- bool has_arg = false;
- if (LookAhead(Token::LEFT_PAREN)) {
- const Token& start_token = Consume();
- // Parsing a function call.
- has_arg = true;
- if (Match(Token::RIGHT_PAREN)) {
- // Nothing, just an empty call.
- } else {
- list = ParseList(start_token, Token::RIGHT_PAREN, false);
- if (has_error())
- return std::unique_ptr<ParseNode>();
- Consume(Token::RIGHT_PAREN, "Expected ')' after call");
- }
- // Optionally with a scope.
- if (LookAhead(Token::LEFT_BRACE)) {
- block = ParseBlock(Consume(), BlockNode::DISCARDS_RESULT);
- if (has_error())
- return std::unique_ptr<ParseNode>();
- }
- }
-
- if (!left && !has_arg) {
- // Not a function call, just a standalone identifier.
- return std::make_unique<IdentifierNode>(token);
- }
- std::unique_ptr<FunctionCallNode> func_call =
- std::make_unique<FunctionCallNode>();
- func_call->set_function(token);
- func_call->set_args(std::move(list));
- if (block)
- func_call->set_block(std::move(block));
- return std::move(func_call);
-}
-
-std::unique_ptr<ParseNode> Parser::Assignment(std::unique_ptr<ParseNode> left,
- const Token& token) {
- if (left->AsIdentifier() == nullptr && left->AsAccessor() == nullptr) {
- *err_ = Err(left.get(),
- "The left-hand side of an assignment must be an identifier, "
- "scope access, or array access.");
- return std::unique_ptr<ParseNode>();
- }
- std::unique_ptr<ParseNode> value = ParseExpression(PRECEDENCE_ASSIGNMENT);
- if (!value) {
- if (!has_error())
- *err_ = Err(token, "Expected right-hand side for assignment.");
- return std::unique_ptr<ParseNode>();
- }
- std::unique_ptr<BinaryOpNode> assign = std::make_unique<BinaryOpNode>();
- assign->set_op(token);
- assign->set_left(std::move(left));
- assign->set_right(std::move(value));
- return std::move(assign);
-}
-
-std::unique_ptr<ParseNode> Parser::Subscript(std::unique_ptr<ParseNode> left,
- const Token& token) {
- // TODO: Maybe support more complex expressions like a[0][0]. This would
- // require work on the evaluator too.
- if (left->AsIdentifier() == nullptr) {
- *err_ = Err(
- left.get(), "May only subscript identifiers.",
- "The thing on the left hand side of the [] must be an identifier\n"
- "and not an expression. If you need this, you'll have to assign the\n"
- "value to a temporary before subscripting. Sorry.");
- return std::unique_ptr<ParseNode>();
- }
- std::unique_ptr<ParseNode> value = ParseExpression();
- Consume(Token::RIGHT_BRACKET, "Expecting ']' after subscript.");
- std::unique_ptr<AccessorNode> accessor = std::make_unique<AccessorNode>();
- accessor->set_base(left->AsIdentifier()->value());
- accessor->set_index(std::move(value));
- return std::move(accessor);
-}
-
-std::unique_ptr<ParseNode> Parser::DotOperator(std::unique_ptr<ParseNode> left,
- const Token& token) {
- if (left->AsIdentifier() == nullptr) {
- *err_ = Err(
- left.get(), "May only use \".\" for identifiers.",
- "The thing on the left hand side of the dot must be an identifier\n"
- "and not an expression. If you need this, you'll have to assign the\n"
- "value to a temporary first. Sorry.");
- return std::unique_ptr<ParseNode>();
- }
-
- std::unique_ptr<ParseNode> right = ParseExpression(PRECEDENCE_DOT);
- if (!right || !right->AsIdentifier()) {
- *err_ = Err(
- token, "Expected identifier for right-hand-side of \".\"",
- "Good: a.cookies\nBad: a.42\nLooks good but still bad: a.cookies()");
- return std::unique_ptr<ParseNode>();
- }
-
- std::unique_ptr<AccessorNode> accessor = std::make_unique<AccessorNode>();
- accessor->set_base(left->AsIdentifier()->value());
- accessor->set_member(std::unique_ptr<IdentifierNode>(
- static_cast<IdentifierNode*>(right.release())));
- return std::move(accessor);
-}
-
-// Does not Consume the start or end token.
-std::unique_ptr<ListNode> Parser::ParseList(const Token& start_token,
- Token::Type stop_before,
- bool allow_trailing_comma) {
- std::unique_ptr<ListNode> list = std::make_unique<ListNode>();
- list->set_begin_token(start_token);
- bool just_got_comma = false;
- bool first_time = true;
- while (!LookAhead(stop_before)) {
- if (!first_time) {
- if (!just_got_comma) {
- // Require commas separate things in lists.
- *err_ = Err(cur_token(), "Expected comma between items.");
- return std::unique_ptr<ListNode>();
- }
- }
- first_time = false;
-
- // Why _OR? We're parsing things that are higher precedence than the ,
- // that separates the items of the list. , should appear lower than
- // boolean expressions (the lowest of which is OR), but above assignments.
- list->append_item(ParseExpression(PRECEDENCE_OR));
- if (has_error())
- return std::unique_ptr<ListNode>();
- if (at_end()) {
- *err_ =
- Err(tokens_[tokens_.size() - 1], "Unexpected end of file in list.");
- return std::unique_ptr<ListNode>();
- }
- if (list->contents().back()->AsBlockComment()) {
- // If there was a comment inside the list, we don't need a comma to the
- // next item, so pretend we got one, if we're expecting one.
- just_got_comma = allow_trailing_comma;
- } else {
- just_got_comma = Match(Token::COMMA);
- }
- }
- if (just_got_comma && !allow_trailing_comma) {
- *err_ = Err(cur_token(), "Trailing comma");
- return std::unique_ptr<ListNode>();
- }
- list->set_end(std::make_unique<EndNode>(cur_token()));
- return list;
-}
-
-std::unique_ptr<ParseNode> Parser::ParseFile() {
- std::unique_ptr<BlockNode> file =
- std::make_unique<BlockNode>(BlockNode::DISCARDS_RESULT);
- for (;;) {
- if (at_end())
- break;
- std::unique_ptr<ParseNode> statement = ParseStatement();
- if (!statement)
- break;
- file->append_statement(std::move(statement));
- }
- if (!at_end() && !has_error())
- *err_ = Err(cur_token(), "Unexpected here, should be newline.");
- if (has_error())
- return std::unique_ptr<ParseNode>();
-
- // TODO(scottmg): If this is measurably expensive, it could be done only
- // when necessary (when reformatting, or during tests). Comments are
- // separate from the parse tree at this point, so downstream code can remain
- // ignorant of them.
- AssignComments(file.get());
-
- return std::move(file);
-}
-
-std::unique_ptr<ParseNode> Parser::ParseStatement() {
- if (LookAhead(Token::IF)) {
- return ParseCondition();
- } else if (LookAhead(Token::BLOCK_COMMENT)) {
- return BlockComment(Consume());
- } else {
- // TODO(scottmg): Is this too strict? Just drop all the testing if we want
- // to allow "pointless" expressions and return ParseExpression() directly.
- std::unique_ptr<ParseNode> stmt = ParseExpression();
- if (stmt) {
- if (stmt->AsFunctionCall() || IsAssignment(stmt.get()))
- return stmt;
- }
- if (!has_error()) {
- const Token& token = cur_or_last_token();
- *err_ = Err(token, "Expecting assignment or function call.");
- }
- return std::unique_ptr<ParseNode>();
- }
-}
-
-std::unique_ptr<BlockNode> Parser::ParseBlock(
- const Token& begin_brace,
- BlockNode::ResultMode result_mode) {
- if (has_error())
- return std::unique_ptr<BlockNode>();
- std::unique_ptr<BlockNode> block = std::make_unique<BlockNode>(result_mode);
- block->set_begin_token(begin_brace);
-
- for (;;) {
- if (LookAhead(Token::RIGHT_BRACE)) {
- block->set_end(std::make_unique<EndNode>(Consume()));
- break;
- }
-
- std::unique_ptr<ParseNode> statement = ParseStatement();
- if (!statement)
- return std::unique_ptr<BlockNode>();
- block->append_statement(std::move(statement));
- }
- return block;
-}
-
-std::unique_ptr<ParseNode> Parser::ParseCondition() {
- std::unique_ptr<ConditionNode> condition = std::make_unique<ConditionNode>();
- condition->set_if_token(Consume(Token::IF, "Expected 'if'"));
- Consume(Token::LEFT_PAREN, "Expected '(' after 'if'.");
- condition->set_condition(ParseExpression());
- if (IsAssignment(condition->condition()))
- *err_ = Err(condition->condition(), "Assignment not allowed in 'if'.");
- Consume(Token::RIGHT_PAREN, "Expected ')' after condition of 'if'.");
- condition->set_if_true(ParseBlock(
- Consume(Token::LEFT_BRACE, "Expected '{' to start 'if' block."),
- BlockNode::DISCARDS_RESULT));
- if (Match(Token::ELSE)) {
- if (LookAhead(Token::LEFT_BRACE)) {
- condition->set_if_false(
- ParseBlock(Consume(), BlockNode::DISCARDS_RESULT));
- } else if (LookAhead(Token::IF)) {
- condition->set_if_false(ParseStatement());
- } else {
- *err_ = Err(cur_or_last_token(), "Expected '{' or 'if' after 'else'.");
- return std::unique_ptr<ParseNode>();
- }
- }
- if (has_error())
- return std::unique_ptr<ParseNode>();
- return std::move(condition);
-}
-
-void Parser::TraverseOrder(const ParseNode* root,
- std::vector<const ParseNode*>* pre,
- std::vector<const ParseNode*>* post) {
- if (root) {
- pre->push_back(root);
-
- if (const AccessorNode* accessor = root->AsAccessor()) {
- TraverseOrder(accessor->index(), pre, post);
- TraverseOrder(accessor->member(), pre, post);
- } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
- TraverseOrder(binop->left(), pre, post);
- TraverseOrder(binop->right(), pre, post);
- } else if (const BlockNode* block = root->AsBlock()) {
- for (const auto& statement : block->statements())
- TraverseOrder(statement.get(), pre, post);
- TraverseOrder(block->End(), pre, post);
- } else if (const ConditionNode* condition = root->AsConditionNode()) {
- TraverseOrder(condition->condition(), pre, post);
- TraverseOrder(condition->if_true(), pre, post);
- TraverseOrder(condition->if_false(), pre, post);
- } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
- TraverseOrder(func_call->args(), pre, post);
- TraverseOrder(func_call->block(), pre, post);
- } else if (root->AsIdentifier()) {
- // Nothing.
- } else if (const ListNode* list = root->AsList()) {
- for (const auto& node : list->contents())
- TraverseOrder(node.get(), pre, post);
- TraverseOrder(list->End(), pre, post);
- } else if (root->AsLiteral()) {
- // Nothing.
- } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
- TraverseOrder(unaryop->operand(), pre, post);
- } else if (root->AsBlockComment()) {
- // Nothing.
- } else if (root->AsEnd()) {
- // Nothing.
- } else {
- CHECK(false) << "Unhandled case in TraverseOrder.";
- }
-
- post->push_back(root);
- }
-}
-
-void Parser::AssignComments(ParseNode* file) {
- // Start by generating a pre- and post- order traversal of the tree so we
- // can determine what's before and after comments.
- std::vector<const ParseNode*> pre;
- std::vector<const ParseNode*> post;
- TraverseOrder(file, &pre, &post);
-
- // Assign line comments to syntax immediately following.
- int cur_comment = 0;
- for (auto* node : pre) {
- if (node->GetRange().is_null()) {
- CHECK_EQ(node, file) << "Only expected on top file node";
- continue;
- }
- const Location start = node->GetRange().begin();
- while (cur_comment < static_cast<int>(line_comment_tokens_.size())) {
- if (start.byte() >= line_comment_tokens_[cur_comment].location().byte()) {
- const_cast<ParseNode*>(node)->comments_mutable()->append_before(
- line_comment_tokens_[cur_comment]);
- ++cur_comment;
- } else {
- break;
- }
- }
- }
-
- // Remaining line comments go at end of file.
- for (; cur_comment < static_cast<int>(line_comment_tokens_.size());
- ++cur_comment)
- file->comments_mutable()->append_after(line_comment_tokens_[cur_comment]);
-
- // Assign suffix to syntax immediately before.
- cur_comment = static_cast<int>(suffix_comment_tokens_.size() - 1);
- for (std::vector<const ParseNode*>::const_reverse_iterator i = post.rbegin();
- i != post.rend(); ++i) {
- // Don't assign suffix comments to the function, list, or block, but instead
- // to the last thing inside.
- if ((*i)->AsFunctionCall() || (*i)->AsList() || (*i)->AsBlock())
- continue;
-
- Location start = (*i)->GetRange().begin();
- Location end = (*i)->GetRange().end();
-
- // Don't assign suffix comments to something that starts on an earlier
- // line, so that in:
- //
- // sources = [ "a",
- // "b" ] # comment
- //
- // it's attached to "b", not sources = [ ... ].
- if (start.line_number() != end.line_number())
- continue;
-
- while (cur_comment >= 0) {
- if (end.byte() <= suffix_comment_tokens_[cur_comment].location().byte()) {
- const_cast<ParseNode*>(*i)->comments_mutable()->append_suffix(
- suffix_comment_tokens_[cur_comment]);
- --cur_comment;
- } else {
- break;
- }
- }
-
- // Suffix comments were assigned in reverse, so if there were multiple on
- // the same node, they need to be reversed.
- if ((*i)->comments() && !(*i)->comments()->suffix().empty())
- const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix();
- }
-}
-
-std::string IndentFor(int value) {
- return std::string(value, ' ');
-}
-
-void RenderToText(const base::Value& node, int indent_level,
- std::ostringstream& os) {
- const base::Value* child = node.FindKey(std::string("child"));
- std::string node_type(node.FindKey("type")->GetString());
- if (node_type == "ACCESSOR") {
- // AccessorNode is a bit special, in that it holds a Token, not a ParseNode
- // for the base.
- os << IndentFor(indent_level) << node_type << std::endl;
- os << IndentFor(indent_level + 1) << node.FindKey("value")->GetString()
- << std::endl;
- } else {
- os << IndentFor(indent_level) << node_type;
- if (node.FindKey("value")) {
- os << "(" << node.FindKey("value")->GetString() << ")";
- }
- os << std::endl;
- }
- if (node.FindKey(kJsonBeforeComment)) {
- for (auto& v : node.FindKey(kJsonBeforeComment)->GetList()) {
- os << IndentFor(indent_level + 1) <<
- "+BEFORE_COMMENT(\"" << v.GetString() << "\")\n";
- }
- }
- if (node.FindKey(kJsonSuffixComment)) {
- for (auto& v : node.FindKey(kJsonSuffixComment)->GetList()) {
- os << IndentFor(indent_level + 1) <<
- "+SUFFIX_COMMENT(\"" << v.GetString() << "\")\n";
- }
- }
- if (node.FindKey(kJsonAfterComment)) {
- for (auto& v : node.FindKey(kJsonAfterComment)->GetList()) {
- os << IndentFor(indent_level + 1) <<
- "+AFTER_COMMENT(\"" << v.GetString() << "\")\n";
- }
- }
- if (child) {
- for (const base::Value& n : child->GetList()) {
- RenderToText(n, indent_level + 1, os);
- }
- }
-}
diff --git a/gn/tools/gn/parser.h b/gn/tools/gn/parser.h
deleted file mode 100644
index a53717061b3..00000000000
--- a/gn/tools/gn/parser.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// 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 TOOLS_GN_PARSER_H_
-#define TOOLS_GN_PARSER_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "tools/gn/err.h"
-#include "tools/gn/parse_tree.h"
-
-extern const char kGrammar_Help[];
-
-struct ParserHelper;
-
-// Parses a series of tokens. The resulting AST will refer to the tokens passed
-// to the input, so the tokens an the file data they refer to must outlive your
-// use of the ParseNode.
-class Parser {
- public:
- // Will return a null pointer and set the err on error.
- static std::unique_ptr<ParseNode> Parse(const std::vector<Token>& tokens,
- Err* err);
-
- // Alternative to parsing that assumes the input is an expression.
- static std::unique_ptr<ParseNode> ParseExpression(
- const std::vector<Token>& tokens,
- Err* err);
-
- // Alternative to parsing that assumes the input is a literal value.
- static std::unique_ptr<ParseNode> ParseValue(const std::vector<Token>& tokens,
- Err* err);
-
- private:
- // Vector must be valid for lifetime of call.
- Parser(const std::vector<Token>& tokens, Err* err);
- ~Parser();
-
- std::unique_ptr<ParseNode> ParseExpression();
-
- // Parses an expression with the given precedence or higher.
- std::unique_ptr<ParseNode> ParseExpression(int precedence);
-
- // |PrefixFunc|s used in parsing expressions.
- std::unique_ptr<ParseNode> Block(const Token& token);
- std::unique_ptr<ParseNode> Literal(const Token& token);
- std::unique_ptr<ParseNode> Name(const Token& token);
- std::unique_ptr<ParseNode> Group(const Token& token);
- std::unique_ptr<ParseNode> Not(const Token& token);
- std::unique_ptr<ParseNode> List(const Token& token);
- std::unique_ptr<ParseNode> BlockComment(const Token& token);
-
- // |InfixFunc|s used in parsing expressions.
- std::unique_ptr<ParseNode> BinaryOperator(std::unique_ptr<ParseNode> left,
- const Token& token);
- std::unique_ptr<ParseNode> IdentifierOrCall(std::unique_ptr<ParseNode> left,
- const Token& token);
- std::unique_ptr<ParseNode> Assignment(std::unique_ptr<ParseNode> left,
- const Token& token);
- std::unique_ptr<ParseNode> Subscript(std::unique_ptr<ParseNode> left,
- const Token& token);
- std::unique_ptr<ParseNode> DotOperator(std::unique_ptr<ParseNode> left,
- const Token& token);
-
- // Helper to parse a comma separated list, optionally allowing trailing
- // commas (allowed in [] lists, not in function calls).
- std::unique_ptr<ListNode> ParseList(const Token& start_token,
- Token::Type stop_before,
- bool allow_trailing_comma);
-
- std::unique_ptr<ParseNode> ParseFile();
- std::unique_ptr<ParseNode> ParseStatement();
- // Expects to be passed the token corresponding to the '{' and that the
- // current token is the one following the '{'.
- std::unique_ptr<BlockNode> ParseBlock(const Token& begin_brace,
- BlockNode::ResultMode result_mode);
- std::unique_ptr<ParseNode> ParseCondition();
-
- // Generates a pre- and post-order traversal of the tree.
- void TraverseOrder(const ParseNode* root,
- std::vector<const ParseNode*>* pre,
- std::vector<const ParseNode*>* post);
-
- // Attach comments to nearby syntax.
- void AssignComments(ParseNode* file);
-
- bool IsAssignment(const ParseNode* node) const;
- bool IsStatementBreak(Token::Type token_type) const;
-
- bool LookAhead(Token::Type type);
- bool Match(Token::Type type);
- const Token& Consume(Token::Type type, const char* error_message);
- const Token& Consume(Token::Type* types,
- size_t num_types,
- const char* error_message);
- const Token& Consume();
-
- // Call this only if !at_end().
- const Token& cur_token() const { return tokens_[cur_]; }
-
- const Token& cur_or_last_token() const {
- return at_end() ? tokens_[tokens_.size() - 1] : cur_token();
- }
-
- bool done() const { return at_end() || has_error(); }
- bool at_end() const { return cur_ >= tokens_.size(); }
- bool has_error() const { return err_->has_error(); }
-
- std::vector<Token> tokens_;
- std::vector<Token> line_comment_tokens_;
- std::vector<Token> suffix_comment_tokens_;
-
- static ParserHelper expressions_[Token::NUM_TYPES];
-
- Token invalid_token_;
- Err* err_;
-
- // Current index into the tokens.
- size_t cur_;
-
- FRIEND_TEST_ALL_PREFIXES(Parser, BinaryOp);
- FRIEND_TEST_ALL_PREFIXES(Parser, Block);
- FRIEND_TEST_ALL_PREFIXES(Parser, Condition);
- FRIEND_TEST_ALL_PREFIXES(Parser, Expression);
- FRIEND_TEST_ALL_PREFIXES(Parser, FunctionCall);
- FRIEND_TEST_ALL_PREFIXES(Parser, List);
- FRIEND_TEST_ALL_PREFIXES(Parser, ParenExpression);
- FRIEND_TEST_ALL_PREFIXES(Parser, UnaryOp);
-
- DISALLOW_COPY_AND_ASSIGN(Parser);
-};
-
-typedef std::unique_ptr<ParseNode> (Parser::*PrefixFunc)(const Token& token);
-typedef std::unique_ptr<ParseNode> (
- Parser::*InfixFunc)(std::unique_ptr<ParseNode> left, const Token& token);
-
-struct ParserHelper {
- PrefixFunc prefix;
- InfixFunc infix;
-
- // Used only for infix operators.
- int precedence;
-};
-
-// Renders parse subtree as a formatted text, indenting by the given number of
-// spaces.
-void RenderToText(const base::Value& node, int indent_level,
- std::ostringstream& os);
-
-#endif // TOOLS_GN_PARSER_H_
diff --git a/gn/tools/gn/parser_unittest.cc b/gn/tools/gn/parser_unittest.cc
deleted file mode 100644
index d24067c352d..00000000000
--- a/gn/tools/gn/parser_unittest.cc
+++ /dev/null
@@ -1,735 +0,0 @@
-// 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 <iostream>
-#include <sstream>
-
-#include "tools/gn/input_file.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/tokenizer.h"
-#include "util/test/test.h"
-
-namespace {
-
-bool GetTokens(const InputFile* input, std::vector<Token>* result) {
- result->clear();
- Err err;
- *result = Tokenizer::Tokenize(input, &err);
- return !err.has_error();
-}
-
-void DoParserPrintTest(const char* input, const char* expected) {
- std::vector<Token> tokens;
- InputFile input_file(SourceFile("/test"));
- input_file.SetContents(input);
- ASSERT_TRUE(GetTokens(&input_file, &tokens));
-
- Err err;
- std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
- if (!result)
- err.PrintToStdout();
- ASSERT_TRUE(result);
-
- std::ostringstream collector;
- RenderToText(result->GetJSONNode(), 0, collector);
-
- EXPECT_EQ(expected, collector.str());
-}
-
-void DoExpressionPrintTest(const char* input, const char* expected) {
- std::vector<Token> tokens;
- InputFile input_file(SourceFile("/test"));
- input_file.SetContents(input);
- ASSERT_TRUE(GetTokens(&input_file, &tokens));
-
- Err err;
- std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
- ASSERT_TRUE(result);
-
- std::ostringstream collector;
- RenderToText(result->GetJSONNode(), 0, collector);
-
- EXPECT_EQ(expected, collector.str());
-}
-
-// Expects the tokenizer or parser to identify an error at the given line and
-// character.
-void DoParserErrorTest(const char* input, int err_line, int err_char) {
- InputFile input_file(SourceFile("/test"));
- input_file.SetContents(input);
-
- Err err;
- std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
- if (!err.has_error()) {
- std::unique_ptr<ParseNode> result = Parser::Parse(tokens, &err);
- ASSERT_FALSE(result);
- ASSERT_TRUE(err.has_error());
- }
-
- EXPECT_EQ(err_line, err.location().line_number());
- EXPECT_EQ(err_char, err.location().column_number());
-}
-
-// Expects the tokenizer or parser to identify an error at the given line and
-// character.
-void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
- InputFile input_file(SourceFile("/test"));
- input_file.SetContents(input);
-
- Err err;
- std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
- if (!err.has_error()) {
- std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
- ASSERT_FALSE(result);
- ASSERT_TRUE(err.has_error());
- }
-
- EXPECT_EQ(err_line, err.location().line_number());
- EXPECT_EQ(err_char, err.location().column_number());
-}
-
-} // namespace
-
-TEST(Parser, Literal) {
- DoExpressionPrintTest("5", "LITERAL(5)\n");
- DoExpressionPrintTest("\"stuff\"", "LITERAL(\"stuff\")\n");
-}
-
-TEST(Parser, BinaryOp) {
- // TODO(scottmg): The tokenizer is dumb, and treats "5-1" as two integers,
- // not a binary operator between two positive integers.
- DoExpressionPrintTest("5 - 1",
- "BINARY(-)\n"
- " LITERAL(5)\n"
- " LITERAL(1)\n");
- DoExpressionPrintTest("5+1",
- "BINARY(+)\n"
- " LITERAL(5)\n"
- " LITERAL(1)\n");
- DoExpressionPrintTest("5 - 1 - 2",
- "BINARY(-)\n"
- " BINARY(-)\n"
- " LITERAL(5)\n"
- " LITERAL(1)\n"
- " LITERAL(2)\n");
-}
-
-TEST(Parser, FunctionCall) {
- DoExpressionPrintTest("foo()",
- "FUNCTION(foo)\n"
- " LIST\n");
- DoExpressionPrintTest("blah(1, 2)",
- "FUNCTION(blah)\n"
- " LIST\n"
- " LITERAL(1)\n"
- " LITERAL(2)\n");
- DoExpressionErrorTest("foo(1, 2,)", 1, 10);
- DoExpressionErrorTest("foo(1 2)", 1, 7);
-}
-
-TEST(Parser, ParenExpression) {
- const char* input = "(foo(1)) + (a + (b - c) + d)";
- const char* expected =
- "BINARY(+)\n"
- " FUNCTION(foo)\n"
- " LIST\n"
- " LITERAL(1)\n"
- " BINARY(+)\n"
- " BINARY(+)\n"
- " IDENTIFIER(a)\n"
- " BINARY(-)\n"
- " IDENTIFIER(b)\n"
- " IDENTIFIER(c)\n"
- " IDENTIFIER(d)\n";
- DoExpressionPrintTest(input, expected);
- DoExpressionErrorTest("(a +", 1, 4);
-}
-
-TEST(Parser, OrderOfOperationsLeftAssociative) {
- const char* input = "5 - 1 - 2\n";
- const char* expected =
- "BINARY(-)\n"
- " BINARY(-)\n"
- " LITERAL(5)\n"
- " LITERAL(1)\n"
- " LITERAL(2)\n";
- DoExpressionPrintTest(input, expected);
-}
-
-TEST(Parser, OrderOfOperationsEqualityBoolean) {
- const char* input =
- "if (a == \"b\" && is_stuff) {\n"
- " print(\"hai\")\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " CONDITION\n"
- " BINARY(&&)\n"
- " BINARY(==)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(\"b\")\n"
- " IDENTIFIER(is_stuff)\n"
- " BLOCK\n"
- " FUNCTION(print)\n"
- " LIST\n"
- " LITERAL(\"hai\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, UnaryOp) {
- DoExpressionPrintTest("!foo",
- "UNARY(!)\n"
- " IDENTIFIER(foo)\n");
-
- // No contents for binary operator.
- DoExpressionErrorTest("a = !", 1, 5);
-}
-
-TEST(Parser, List) {
- DoExpressionPrintTest("[]", "LIST\n");
- DoExpressionPrintTest("[1,asd,]",
- "LIST\n"
- " LITERAL(1)\n"
- " IDENTIFIER(asd)\n");
- DoExpressionPrintTest("[1, 2+3 - foo]",
- "LIST\n"
- " LITERAL(1)\n"
- " BINARY(-)\n"
- " BINARY(+)\n"
- " LITERAL(2)\n"
- " LITERAL(3)\n"
- " IDENTIFIER(foo)\n");
- DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
- "LIST\n"
- " LITERAL(1)\n"
- " LITERAL(2)\n"
- " LITERAL(3)\n"
- " LITERAL(4)\n");
-
- DoExpressionErrorTest("[a, 2+,]", 1, 7);
- DoExpressionErrorTest("[,]", 1, 2);
- DoExpressionErrorTest("[a,,]", 1, 4);
-}
-
-TEST(Parser, Assignment) {
- DoParserPrintTest("a=2",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(2)\n");
-
- DoExpressionErrorTest("a = ", 1, 3);
-}
-
-TEST(Parser, Accessor) {
- // Accessor indexing.
- DoParserPrintTest("a=b[c+2]",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " ACCESSOR\n"
- " b\n" // AccessorNode is a bit weird in that it holds
- // a Token, not a ParseNode for the base.
- " BINARY(+)\n"
- " IDENTIFIER(c)\n"
- " LITERAL(2)\n");
- DoParserErrorTest("a = b[1][0]", 1, 5);
-
- // Member accessors.
- DoParserPrintTest("a=b.c+2",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " BINARY(+)\n"
- " ACCESSOR\n"
- " b\n"
- " IDENTIFIER(c)\n"
- " LITERAL(2)\n");
- DoParserPrintTest("a.b = 5",
- "BLOCK\n"
- " BINARY(=)\n"
- " ACCESSOR\n"
- " a\n"
- " IDENTIFIER(b)\n"
- " LITERAL(5)\n");
- DoParserErrorTest("a = b.c.d", 1, 6); // Can't nest accessors (currently).
-
- // Error at the bad dot in the RHS, not the + operator (crbug.com/472038).
- DoParserErrorTest("foo(a + b.c.d)", 1, 10);
-}
-
-TEST(Parser, Condition) {
- DoParserPrintTest("if(1) { a = 2 }",
- "BLOCK\n"
- " CONDITION\n"
- " LITERAL(1)\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(2)\n");
-
- DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
- "BLOCK\n"
- " CONDITION\n"
- " LITERAL(1)\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(2)\n"
- " CONDITION\n"
- " LITERAL(0)\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(3)\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(4)\n");
-}
-
-TEST(Parser, OnlyCallAndAssignInBody) {
- DoParserErrorTest("[]", 1, 2);
- DoParserErrorTest("3 + 4", 1, 5);
- DoParserErrorTest("6 - 7", 1, 5);
- DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
-}
-
-TEST(Parser, NoAssignmentInCondition) {
- DoParserErrorTest("if (a=2) {}", 1, 5);
-}
-
-TEST(Parser, CompleteFunction) {
- const char* input =
- "cc_test(\"foo\") {\n"
- " sources = [\n"
- " \"foo.cc\",\n"
- " \"foo.h\"\n"
- " ]\n"
- " dependencies = [\n"
- " \"base\"\n"
- " ]\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(cc_test)\n"
- " LIST\n"
- " LITERAL(\"foo\")\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"foo.cc\")\n"
- " LITERAL(\"foo.h\")\n"
- " BINARY(=)\n"
- " IDENTIFIER(dependencies)\n"
- " LIST\n"
- " LITERAL(\"base\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, FunctionWithConditional) {
- const char* input =
- "cc_test(\"foo\") {\n"
- " sources = [\"foo.cc\"]\n"
- " if (OS == \"mac\") {\n"
- " sources += \"bar.cc\"\n"
- " } else if (OS == \"win\") {\n"
- " sources -= [\"asd.cc\", \"foo.cc\"]\n"
- " } else {\n"
- " dependencies += [\"bar.cc\"]\n"
- " }\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(cc_test)\n"
- " LIST\n"
- " LITERAL(\"foo\")\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"foo.cc\")\n"
- " CONDITION\n"
- " BINARY(==)\n"
- " IDENTIFIER(OS)\n"
- " LITERAL(\"mac\")\n"
- " BLOCK\n"
- " BINARY(+=)\n"
- " IDENTIFIER(sources)\n"
- " LITERAL(\"bar.cc\")\n"
- " CONDITION\n"
- " BINARY(==)\n"
- " IDENTIFIER(OS)\n"
- " LITERAL(\"win\")\n"
- " BLOCK\n"
- " BINARY(-=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"asd.cc\")\n"
- " LITERAL(\"foo.cc\")\n"
- " BLOCK\n"
- " BINARY(+=)\n"
- " IDENTIFIER(dependencies)\n"
- " LIST\n"
- " LITERAL(\"bar.cc\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, UnterminatedBlock) {
- DoParserErrorTest("stuff() {", 1, 9);
-}
-
-TEST(Parser, BadlyTerminatedNumber) {
- DoParserErrorTest("1234z", 1, 5);
-}
-
-TEST(Parser, NewlinesInUnusualPlaces) {
- DoParserPrintTest(
- "if\n"
- "(\n"
- "a\n"
- ")\n"
- "{\n"
- "}\n",
- "BLOCK\n"
- " CONDITION\n"
- " IDENTIFIER(a)\n"
- " BLOCK\n");
-}
-
-TEST(Parser, NewlinesInUnusualPlaces2) {
- DoParserPrintTest("a\n=\n2\n",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(2)\n");
- DoParserPrintTest("x =\ny if\n(1\n) {}",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(x)\n"
- " IDENTIFIER(y)\n"
- " CONDITION\n"
- " LITERAL(1)\n"
- " BLOCK\n");
- DoParserPrintTest("x = 3\n+2",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(x)\n"
- " BINARY(+)\n"
- " LITERAL(3)\n"
- " LITERAL(2)\n");
-}
-
-TEST(Parser, NewlineBeforeSubscript) {
- const char* input = "a = b[1]";
- const char* input_with_newline = "a = b\n[1]";
- const char* expected =
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " ACCESSOR\n"
- " b\n"
- " LITERAL(1)\n";
- DoParserPrintTest(input, expected);
- DoParserPrintTest(input_with_newline, expected);
-}
-
-TEST(Parser, SequenceOfExpressions) {
- DoParserPrintTest("a = 1 b = 2",
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(1)\n"
- " BINARY(=)\n"
- " IDENTIFIER(b)\n"
- " LITERAL(2)\n");
-}
-
-TEST(Parser, BlockAfterFunction) {
- const char* input = "func(\"stuff\") {\n}";
- // TODO(scottmg): Do we really want these to mean different things?
- const char* input_with_newline = "func(\"stuff\")\n{\n}";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(func)\n"
- " LIST\n"
- " LITERAL(\"stuff\")\n"
- " BLOCK\n";
- DoParserPrintTest(input, expected);
- DoParserPrintTest(input_with_newline, expected);
-}
-
-TEST(Parser, LongExpression) {
- const char* input = "a = b + c && d || e";
- const char* expected =
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " BINARY(||)\n"
- " BINARY(&&)\n"
- " BINARY(+)\n"
- " IDENTIFIER(b)\n"
- " IDENTIFIER(c)\n"
- " IDENTIFIER(d)\n"
- " IDENTIFIER(e)\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsStandalone) {
- const char* input =
- "# Toplevel comment.\n"
- "\n"
- "executable(\"wee\") {}\n";
- const char* expected =
- "BLOCK\n"
- " BLOCK_COMMENT(# Toplevel comment.)\n"
- " FUNCTION(executable)\n"
- " LIST\n"
- " LITERAL(\"wee\")\n"
- " BLOCK\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsStandaloneEof) {
- const char* input =
- "executable(\"wee\") {}\n"
- "# EOF comment.\n";
- const char* expected =
- "BLOCK\n"
- " +AFTER_COMMENT(\"# EOF comment.\")\n"
- " FUNCTION(executable)\n"
- " LIST\n"
- " LITERAL(\"wee\")\n"
- " BLOCK\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsLineAttached) {
- const char* input =
- "executable(\"wee\") {\n"
- " # Some sources.\n"
- " sources = [\n"
- " \"stuff.cc\",\n"
- " \"things.cc\",\n"
- " # This file is special or something.\n"
- " \"another.cc\",\n"
- " ]\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(executable)\n"
- " LIST\n"
- " LITERAL(\"wee\")\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " +BEFORE_COMMENT(\"# Some sources.\")\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"stuff.cc\")\n"
- " LITERAL(\"things.cc\")\n"
- " LITERAL(\"another.cc\")\n"
- " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsSuffix) {
- const char* input =
- "executable(\"wee\") { # This is some stuff.\n"
- "sources = [ \"a.cc\" # And another comment here.\n"
- "] }";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(executable)\n"
- " LIST\n"
- " LITERAL(\"wee\")\n"
- " END())\n"
- " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"a.cc\")\n"
- " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsSuffixDifferentLine) {
- const char* input =
- "executable(\"wee\") {\n"
- " sources = [ \"a\",\n"
- " \"b\" ] # Comment\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(executable)\n"
- " LIST\n"
- " LITERAL(\"wee\")\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"a\")\n"
- " LITERAL(\"b\")\n"
- " END(])\n"
- " +SUFFIX_COMMENT(\"# Comment\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsSuffixMultiple) {
- const char* input =
- "executable(\"wee\") {\n"
- " sources = [\n"
- " \"a\", # This is a comment,\n"
- " # and some more,\n" // Note that this is aligned with above.
- " # then the end.\n"
- " ]\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(executable)\n"
- " LIST\n"
- " LITERAL(\"wee\")\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"a\")\n"
- " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
- " +SUFFIX_COMMENT(\"# and some more,\")\n"
- " +SUFFIX_COMMENT(\"# then the end.\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsConnectedInList) {
- const char* input =
- "defines = [\n"
- "\n"
- " # Connected comment.\n"
- " \"WEE\",\n"
- " \"BLORPY\",\n"
- "]\n";
- const char* expected =
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(defines)\n"
- " LIST\n"
- " LITERAL(\"WEE\")\n"
- " +BEFORE_COMMENT(\"# Connected comment.\")\n"
- " LITERAL(\"BLORPY\")\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, CommentsAtEndOfBlock) {
- const char* input =
- "if (is_win) {\n"
- " sources = [\"a.cc\"]\n"
- " # Some comment at end.\n"
- "}\n";
- const char* expected =
- "BLOCK\n"
- " CONDITION\n"
- " IDENTIFIER(is_win)\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(sources)\n"
- " LIST\n"
- " LITERAL(\"a.cc\")\n"
- " END(})\n"
- " +BEFORE_COMMENT(\"# Some comment at end.\")\n";
- DoParserPrintTest(input, expected);
-}
-
-// TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
-// which thing this comment is intended to be attached to.
-TEST(Parser, CommentsEndOfBlockSingleLine) {
- const char* input =
- "defines = [ # EOL defines.\n"
- "]\n";
- const char* expected =
- "BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(defines)\n"
- " +SUFFIX_COMMENT(\"# EOL defines.\")\n"
- " LIST\n";
- DoParserPrintTest(input, expected);
-}
-
-TEST(Parser, HangingIf) {
- DoParserErrorTest("if", 1, 1);
-}
-
-TEST(Parser, NegatingList) {
- DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);
-}
-
-TEST(Parser, ConditionNoBracesIf) {
- DoParserErrorTest(
- "if (true)\n"
- " foreach(foo, []) {}\n"
- "else {\n"
- " foreach(bar, []) {}\n"
- "}\n",
- 2, 3);
-}
-
-TEST(Parser, ConditionNoBracesElse) {
- DoParserErrorTest(
- "if (true) {\n"
- " foreach(foo, []) {}\n"
- "} else\n"
- " foreach(bar, []) {}\n",
- 4, 3);
-}
-
-TEST(Parser, ConditionNoBracesElseIf) {
- DoParserErrorTest(
- "if (true) {\n"
- " foreach(foo, []) {}\n"
- "} else if (true)\n"
- " foreach(bar, []) {}\n",
- 4, 3);
-}
-
-// Disallow standalone {} for introducing new scopes. These are ambiguous with
-// target declarations (e.g. is:
-// foo("bar") {}
-// a function with an associated block, or a standalone function with a
-// freestanding block.
-TEST(Parser, StandaloneBlock) {
- // The error is reported at the end of the block when nothing is done
- // with it. If we had said "a = { ..." then it would have been OK.
- DoParserErrorTest(
- "if (true) {\n"
- "}\n"
- "{\n"
- " assert(false)\n"
- "}\n",
- 5, 1);
-}
-
-TEST(Parser, BlockValues) {
- const char* input =
- "print({a = 1 b = 2}, 3)\n"
- "a = { b = \"asd\" }";
- const char* expected =
- "BLOCK\n"
- " FUNCTION(print)\n"
- " LIST\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " LITERAL(1)\n"
- " BINARY(=)\n"
- " IDENTIFIER(b)\n"
- " LITERAL(2)\n"
- " LITERAL(3)\n"
- " BINARY(=)\n"
- " IDENTIFIER(a)\n"
- " BLOCK\n"
- " BINARY(=)\n"
- " IDENTIFIER(b)\n"
- " LITERAL(\"asd\")\n";
- DoParserPrintTest(input, expected);
-}
diff --git a/gn/tools/gn/path_output.cc b/gn/tools/gn/path_output.cc
deleted file mode 100644
index 0f5799774c8..00000000000
--- a/gn/tools/gn/path_output.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-// 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 "tools/gn/path_output.h"
-
-#include "base/strings/string_util.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/string_utils.h"
-#include "util/build_config.h"
-
-PathOutput::PathOutput(const SourceDir& current_dir,
- const base::StringPiece& source_root,
- EscapingMode escaping)
- : current_dir_(current_dir) {
- inverse_current_dir_ = RebasePath("//", current_dir, source_root);
- if (!EndsWithSlash(inverse_current_dir_))
- inverse_current_dir_.push_back('/');
- options_.mode = escaping;
-}
-
-PathOutput::~PathOutput() = default;
-
-void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const {
- WritePathStr(out, file.value());
-}
-
-void PathOutput::WriteDir(std::ostream& out,
- const SourceDir& dir,
- DirSlashEnding slash_ending) const {
- if (dir.value() == "/") {
- // Writing system root is always a slash (this will normally only come up
- // on Posix systems).
- if (slash_ending == DIR_NO_LAST_SLASH)
- out << "/.";
- else
- out << "/";
- } else if (dir.value() == "//") {
- // Writing out the source root.
- if (slash_ending == DIR_NO_LAST_SLASH) {
- // The inverse_current_dir_ will contain a [back]slash at the end, so we
- // can't just write it out.
- if (inverse_current_dir_.empty()) {
- out << ".";
- } else {
- out.write(inverse_current_dir_.c_str(),
- inverse_current_dir_.size() - 1);
- }
- } else {
- if (inverse_current_dir_.empty())
- out << "./";
- else
- out << inverse_current_dir_;
- }
- } else if (dir == current_dir_) {
- // Writing the same directory. This needs special handling here since
- // we need to output something else other than the input.
- if (slash_ending == DIR_INCLUDE_LAST_SLASH)
- out << "./";
- else
- out << ".";
- } else if (slash_ending == DIR_INCLUDE_LAST_SLASH) {
- WritePathStr(out, dir.value());
- } else {
- // DIR_NO_LAST_SLASH mode, just trim the last char.
- WritePathStr(out,
- base::StringPiece(dir.value().data(), dir.value().size() - 1));
- }
-}
-
-void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const {
- // Here we assume that the path is already preprocessed.
- EscapeStringToStream(out, file.value(), options_);
-}
-
-void PathOutput::WriteFiles(std::ostream& out,
- const std::vector<OutputFile>& files) const {
- for (const auto& file : files) {
- out << " ";
- WriteFile(out, file);
- }
-}
-
-void PathOutput::WriteFiles(std::ostream& out,
- const UniqueVector<OutputFile>& files) const {
- for (const auto& file : files) {
- out << " ";
- WriteFile(out, file);
- }
-}
-
-void PathOutput::WriteDir(std::ostream& out,
- const OutputFile& file,
- DirSlashEnding slash_ending) const {
- DCHECK(file.value().empty() || file.value()[file.value().size() - 1] == '/');
-
- switch (slash_ending) {
- case DIR_INCLUDE_LAST_SLASH:
- EscapeStringToStream(out, file.value(), options_);
- break;
- case DIR_NO_LAST_SLASH:
- if (!file.value().empty() &&
- file.value()[file.value().size() - 1] == '/') {
- // Trim trailing slash.
- EscapeStringToStream(
- out,
- base::StringPiece(file.value().data(), file.value().size() - 1),
- options_);
- } else {
- // Doesn't end with a slash, write the whole thing.
- EscapeStringToStream(out, file.value(), options_);
- }
- break;
- }
-}
-
-void PathOutput::WriteFile(std::ostream& out,
- const base::FilePath& file) const {
- // Assume native file paths are always absolute.
- EscapeStringToStream(out, FilePathToUTF8(file), options_);
-}
-
-void PathOutput::WriteSourceRelativeString(std::ostream& out,
- const base::StringPiece& str) const {
- if (options_.mode == ESCAPE_NINJA_COMMAND) {
- // Shell escaping needs an intermediate string since it may end up
- // quoting the whole thing.
- std::string intermediate;
- intermediate.reserve(inverse_current_dir_.size() + str.size());
- intermediate.assign(inverse_current_dir_.c_str(),
- inverse_current_dir_.size());
- intermediate.append(str.data(), str.size());
-
- EscapeStringToStream(
- out, base::StringPiece(intermediate.c_str(), intermediate.size()),
- options_);
- } else {
- // Ninja (and none) escaping can avoid the intermediate string and
- // reprocessing of the inverse_current_dir_.
- out << inverse_current_dir_;
- EscapeStringToStream(out, str, options_);
- }
-}
-
-void PathOutput::WritePathStr(std::ostream& out,
- const base::StringPiece& str) const {
- DCHECK(str.size() > 0 && str[0] == '/');
-
- if (str.substr(0, current_dir_.value().size()) ==
- base::StringPiece(current_dir_.value())) {
- // The current dir is a prefix of the output file, so we can strip the
- // prefix and write out the result.
- EscapeStringToStream(out, str.substr(current_dir_.value().size()),
- options_);
- } else if (str.size() >= 2 && str[1] == '/') {
- WriteSourceRelativeString(out, str.substr(2));
- } else {
-// Input begins with one slash, don't write the current directory since
-// it's system-absolute.
-#if defined(OS_WIN)
- // On Windows, trim the leading slash, since the input for absolute
- // paths will look like "/C:/foo/bar.txt".
- EscapeStringToStream(out, str.substr(1), options_);
-#else
- EscapeStringToStream(out, str, options_);
-#endif
- }
-}
diff --git a/gn/tools/gn/path_output.h b/gn/tools/gn/path_output.h
deleted file mode 100644
index 1d98a2c78e5..00000000000
--- a/gn/tools/gn/path_output.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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 TOOLS_GN_PATH_OUTPUT_H_
-#define TOOLS_GN_PATH_OUTPUT_H_
-
-#include <iosfwd>
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/unique_vector.h"
-
-class OutputFile;
-class SourceFile;
-
-namespace base {
-class FilePath;
-}
-
-// Writes file names to streams assuming a certain input directory and
-// escaping rules. This gives us a central place for managing this state.
-class PathOutput {
- public:
- // Controls whether writing directory names include the trailing slash.
- // Often we don't want the trailing slash when writing out to a command line,
- // especially on Windows where it's a backslash and might be interpreted as
- // escaping the thing following it.
- enum DirSlashEnding {
- DIR_INCLUDE_LAST_SLASH,
- DIR_NO_LAST_SLASH,
- };
-
- PathOutput(const SourceDir& current_dir,
- const base::StringPiece& source_root,
- EscapingMode escaping);
- ~PathOutput();
-
- // Read-only since inverse_current_dir_ is computed depending on this.
- EscapingMode escaping_mode() const { return options_.mode; }
-
- const SourceDir& current_dir() const { return current_dir_; }
-
- // Getter/setters for flags inside the escape options.
- bool inhibit_quoting() const { return options_.inhibit_quoting; }
- void set_inhibit_quoting(bool iq) { options_.inhibit_quoting = iq; }
- void set_escape_platform(EscapingPlatform p) { options_.platform = p; }
-
- void WriteFile(std::ostream& out, const SourceFile& file) const;
- void WriteFile(std::ostream& out, const OutputFile& file) const;
- void WriteFile(std::ostream& out, const base::FilePath& file) const;
-
- // Writes the given OutputFiles with spaces separating them. This will also
- // write an initial space before the first item.
- void WriteFiles(std::ostream& out,
- const std::vector<OutputFile>& files) const;
- void WriteFiles(std::ostream& out,
- const UniqueVector<OutputFile>& files) const;
-
- // This variant assumes the dir ends in a trailing slash or is empty.
- void WriteDir(std::ostream& out,
- const SourceDir& dir,
- DirSlashEnding slash_ending) const;
-
- void WriteDir(std::ostream& out,
- const OutputFile& file,
- DirSlashEnding slash_ending) const;
-
- // Backend for WriteFile and WriteDir. This appends the given file or
- // directory string to the file.
- void WritePathStr(std::ostream& out, const base::StringPiece& str) const;
-
- private:
- // Takes the given string and writes it out, appending to the inverse
- // current dir. This assumes leading slashes have been trimmed.
- void WriteSourceRelativeString(std::ostream& out,
- const base::StringPiece& str) const;
-
- SourceDir current_dir_;
-
- // Uses system slashes if convert_slashes_to_system_.
- std::string inverse_current_dir_;
-
- // Since the inverse_current_dir_ depends on some of these, we don't expose
- // this directly to modification.
- EscapeOptions options_;
-};
-
-#endif // TOOLS_GN_PATH_OUTPUT_H_
diff --git a/gn/tools/gn/path_output_unittest.cc b/gn/tools/gn/path_output_unittest.cc
deleted file mode 100644
index 8857fd9f9e8..00000000000
--- a/gn/tools/gn/path_output_unittest.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-// 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 <sstream>
-
-#include "base/files/file_path.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/path_output.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-TEST(PathOutput, Basic) {
- SourceDir build_dir("//out/Debug/");
- base::StringPiece source_root("/source/root");
- PathOutput writer(build_dir, source_root, ESCAPE_NONE);
- {
- // Normal source-root path.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/bar.cc"));
- EXPECT_EQ("../../foo/bar.cc", out.str());
- }
- {
- // File in the root dir.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo.cc"));
- EXPECT_EQ("../../foo.cc", out.str());
- }
- {
- // Files in the output dir.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//out/Debug/foo.cc"));
- out << " ";
- writer.WriteFile(out, SourceFile("//out/Debug/bar/baz.cc"));
- EXPECT_EQ("foo.cc bar/baz.cc", out.str());
- }
-#if defined(OS_WIN)
- {
- // System-absolute path.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("/C:/foo/bar.cc"));
- EXPECT_EQ("C:/foo/bar.cc", out.str());
- }
-#else
- {
- // System-absolute path.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("/foo/bar.cc"));
- EXPECT_EQ("/foo/bar.cc", out.str());
- }
-#endif
-}
-
-// Same as basic but the output dir is the root.
-TEST(PathOutput, BasicInRoot) {
- SourceDir build_dir("//");
- base::StringPiece source_root("/source/root");
- PathOutput writer(build_dir, source_root, ESCAPE_NONE);
- {
- // Normal source-root path.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/bar.cc"));
- EXPECT_EQ("foo/bar.cc", out.str());
- }
- {
- // File in the root dir.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo.cc"));
- EXPECT_EQ("foo.cc", out.str());
- }
-}
-
-TEST(PathOutput, NinjaEscaping) {
- SourceDir build_dir("//out/Debug/");
- base::StringPiece source_root("/source/root");
- PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
- {
- // Spaces and $ in filenames.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/foo bar$.cc"));
- EXPECT_EQ("../../foo/foo$ bar$$.cc", out.str());
- }
- {
- // Not other weird stuff
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/\"foo\".cc"));
- EXPECT_EQ("../../foo/\"foo\".cc", out.str());
- }
-}
-
-TEST(PathOutput, NinjaForkEscaping) {
- SourceDir build_dir("//out/Debug/");
- base::StringPiece source_root("/source/root");
- PathOutput writer(build_dir, source_root, ESCAPE_NINJA_COMMAND);
-
- // Spaces in filenames should get quoted on Windows.
- writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
- {
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
- EXPECT_EQ("\"../../foo/foo$ bar.cc\"", out.str());
- }
-
- // Spaces in filenames should get escaped on Posix.
- writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
- {
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
- EXPECT_EQ("../../foo/foo\\$ bar.cc", out.str());
- }
-
- // Quotes should get blackslash-escaped on Windows and Posix.
- writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
- {
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
- // Our Windows code currently quotes the whole thing in this case for
- // code simplicity, even though it's strictly unnecessary. This might
- // change in the future.
- EXPECT_EQ("\"../../foo/\\\"foobar\\\".cc\"", out.str());
- }
- writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
- {
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
- EXPECT_EQ("../../foo/\\\"foobar\\\".cc", out.str());
- }
-
- // Backslashes should get escaped on non-Windows and preserved on Windows.
- writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
- {
- std::ostringstream out;
- writer.WriteFile(out, OutputFile("foo\\bar.cc"));
- EXPECT_EQ("foo\\bar.cc", out.str());
- }
- writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
- {
- std::ostringstream out;
- writer.WriteFile(out, OutputFile("foo\\bar.cc"));
- EXPECT_EQ("foo\\\\bar.cc", out.str());
- }
-}
-
-TEST(PathOutput, InhibitQuoting) {
- SourceDir build_dir("//out/Debug/");
- base::StringPiece source_root("/source/root");
- PathOutput writer(build_dir, source_root, ESCAPE_NINJA_COMMAND);
- writer.set_inhibit_quoting(true);
-
- writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
- {
- // We should get unescaped spaces in the output with no quotes.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
- EXPECT_EQ("../../foo/foo$ bar.cc", out.str());
- }
-
- writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
- {
- // Escapes the space.
- std::ostringstream out;
- writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
- EXPECT_EQ("../../foo/foo\\$ bar.cc", out.str());
- }
-}
-
-TEST(PathOutput, WriteDir) {
- {
- SourceDir build_dir("//out/Debug/");
- base::StringPiece source_root("/source/root");
- PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//foo/bar/"),
- PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("../../foo/bar/", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//foo/bar/"),
- PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ("../../foo/bar", out.str());
- }
-
- // Output source root dir.
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("../../", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ("../..", out.str());
- }
-
- // Output system root dir.
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("/", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("/", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ("/.", out.str());
- }
-
- // Output inside current dir.
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//out/Debug/"),
- PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("./", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//out/Debug/"),
- PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ(".", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//out/Debug/foo/"),
- PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("foo/", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, SourceDir("//out/Debug/foo/"),
- PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ("foo", out.str());
- }
-
- // WriteDir using an OutputFile.
- {
- std::ostringstream out;
- writer.WriteDir(out, OutputFile("foo/"),
- PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("foo/", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, OutputFile("foo/"), PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ("foo", out.str());
- }
- {
- std::ostringstream out;
- writer.WriteDir(out, OutputFile(), PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("", out.str());
- }
- }
- {
- // Empty build dir writer.
- base::StringPiece source_root("/source/root");
- PathOutput root_writer(SourceDir("//"), source_root, ESCAPE_NINJA);
- {
- std::ostringstream out;
- root_writer.WriteDir(out, SourceDir("//"),
- PathOutput::DIR_INCLUDE_LAST_SLASH);
- EXPECT_EQ("./", out.str());
- }
- {
- std::ostringstream out;
- root_writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_NO_LAST_SLASH);
- EXPECT_EQ(".", out.str());
- }
- }
-}
diff --git a/gn/tools/gn/pattern.cc b/gn/tools/gn/pattern.cc
deleted file mode 100644
index aff2ad952e6..00000000000
--- a/gn/tools/gn/pattern.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-// 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 "tools/gn/pattern.h"
-
-#include "tools/gn/value.h"
-
-namespace {
-
-void ParsePattern(const std::string& s, std::vector<Pattern::Subrange>* out) {
- // Set when the last subrange is a literal so we can just append when we
- // find another literal.
- Pattern::Subrange* last_literal = nullptr;
-
- for (size_t i = 0; i < s.size(); i++) {
- if (s[i] == '*') {
- // Don't allow two **.
- if (out->size() == 0 ||
- (*out)[out->size() - 1].type != Pattern::Subrange::ANYTHING)
- out->push_back(Pattern::Subrange(Pattern::Subrange::ANYTHING));
- last_literal = nullptr;
- } else if (s[i] == '\\') {
- if (i < s.size() - 1 && s[i + 1] == 'b') {
- // "\b" means path boundary.
- i++;
- out->push_back(Pattern::Subrange(Pattern::Subrange::PATH_BOUNDARY));
- last_literal = nullptr;
- } else {
- // Backslash + anything else means that literal char.
- if (!last_literal) {
- out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
- last_literal = &(*out)[out->size() - 1];
- }
- if (i < s.size() - 1) {
- i++;
- last_literal->literal.push_back(s[i]);
- } else {
- // Single backslash at end, use literal backslash.
- last_literal->literal.push_back('\\');
- }
- }
- } else {
- if (!last_literal) {
- out->push_back(Pattern::Subrange(Pattern::Subrange::LITERAL));
- last_literal = &(*out)[out->size() - 1];
- }
- last_literal->literal.push_back(s[i]);
- }
- }
-}
-
-} // namespace
-
-Pattern::Pattern(const std::string& s) {
- ParsePattern(s, &subranges_);
- is_suffix_ =
- (subranges_.size() == 2 && subranges_[0].type == Subrange::ANYTHING &&
- subranges_[1].type == Subrange::LITERAL);
-}
-
-Pattern::Pattern(const Pattern& other) = default;
-
-Pattern::~Pattern() = default;
-
-bool Pattern::MatchesString(const std::string& s) const {
- // Empty pattern matches only empty string.
- if (subranges_.empty())
- return s.empty();
-
- if (is_suffix_) {
- const std::string& suffix = subranges_[1].literal;
- if (suffix.size() > s.size())
- return false; // Too short.
- return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
- }
-
- return RecursiveMatch(s, 0, 0, true);
-}
-
-// We assume the number of ranges is small so recursive is always reasonable.
-// Could be optimized to only be recursive for *.
-bool Pattern::RecursiveMatch(const std::string& s,
- size_t begin_char,
- size_t subrange_index,
- bool allow_implicit_path_boundary) const {
- if (subrange_index >= subranges_.size()) {
- // Hit the end of our subranges, the text should also be at the end for a
- // match.
- return begin_char == s.size();
- }
-
- const Subrange& sr = subranges_[subrange_index];
- switch (sr.type) {
- case Subrange::LITERAL: {
- if (s.size() - begin_char < sr.literal.size())
- return false; // Not enough room.
- if (s.compare(begin_char, sr.literal.size(), sr.literal) != 0)
- return false; // Literal doesn't match.
-
- // Recursively check the next one.
- return RecursiveMatch(s, begin_char + sr.literal.size(),
- subrange_index + 1, true);
- }
-
- case Subrange::PATH_BOUNDARY: {
- // When we can accept an implicit path boundary, we have to check both
- // a match of the literal and the implicit one.
- if (allow_implicit_path_boundary &&
- (begin_char == 0 || begin_char == s.size())) {
- // At implicit path boundary, see if the rest of the pattern matches.
- if (RecursiveMatch(s, begin_char, subrange_index + 1, false))
- return true;
- }
-
- // Check for a literal "/".
- if (begin_char < s.size() && s[begin_char] == '/') {
- // At explicit boundary, see if the rest of the pattern matches.
- if (RecursiveMatch(s, begin_char + 1, subrange_index + 1, true))
- return true;
- }
- return false;
- }
-
- case Subrange::ANYTHING: {
- if (subrange_index == subranges_.size() - 1)
- return true; // * at the end, consider it matching.
-
- size_t min_next_size = sr.MinSize();
-
- // We don't care about exactly what matched as long as there was a match,
- // so we can do this front-to-back. If we needed the match, we would
- // normally want "*" to be greedy so would work backwards.
- for (size_t i = begin_char; i < s.size() - min_next_size; i++) {
- // Note: this could probably be faster by detecting the type of the
- // next match in advance and checking for a match in this loop rather
- // than doing a full recursive call for each character.
- if (RecursiveMatch(s, i, subrange_index + 1, true))
- return true;
- }
- return false;
- }
-
- default:
- NOTREACHED();
- }
-
- return false;
-}
-
-PatternList::PatternList() = default;
-
-PatternList::PatternList(const PatternList& other) = default;
-
-PatternList::~PatternList() = default;
-
-void PatternList::Append(const Pattern& pattern) {
- patterns_.push_back(pattern);
-}
-
-void PatternList::SetFromValue(const Value& v, Err* err) {
- patterns_.clear();
-
- if (v.type() != Value::LIST) {
- *err = Err(v.origin(), "This value must be a list.");
- return;
- }
-
- const std::vector<Value>& list = v.list_value();
- for (const auto& elem : list) {
- if (!elem.VerifyTypeIs(Value::STRING, err))
- return;
- patterns_.push_back(Pattern(elem.string_value()));
- }
-}
-
-bool PatternList::MatchesString(const std::string& s) const {
- for (const auto& pattern : patterns_) {
- if (pattern.MatchesString(s))
- return true;
- }
- return false;
-}
-
-bool PatternList::MatchesValue(const Value& v) const {
- if (v.type() == Value::STRING)
- return MatchesString(v.string_value());
- return false;
-}
diff --git a/gn/tools/gn/pattern.h b/gn/tools/gn/pattern.h
deleted file mode 100644
index ef29c629668..00000000000
--- a/gn/tools/gn/pattern.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 TOOLS_GN_PATTERN_H_
-#define TOOLS_GN_PATTERN_H_
-
-#include <stddef.h>
-
-#include <string>
-#include <vector>
-
-#include "tools/gn/value.h"
-
-class Pattern {
- public:
- struct Subrange {
- enum Type {
- LITERAL, // Matches exactly the contents of the string.
- ANYTHING, // * (zero or more chars).
- PATH_BOUNDARY // '/' or beginning of string.
- };
-
- explicit Subrange(Type t, const std::string& l = std::string())
- : type(t), literal(l) {}
-
- // Returns the minimum number of chars that this subrange requires.
- size_t MinSize() const {
- switch (type) {
- case LITERAL:
- return literal.size();
- case ANYTHING:
- return 0;
- case PATH_BOUNDARY:
- return 0; // Can match beginning or end of string, which is 0 len.
- default:
- return 0;
- }
- }
-
- Type type;
-
- // When type == LITERAL this is the text to match.
- std::string literal;
- };
-
- explicit Pattern(const std::string& s);
- Pattern(const Pattern& other);
- ~Pattern();
-
- // Returns true if the current pattern matches the given string.
- bool MatchesString(const std::string& s) const;
-
- private:
- // allow_implicit_path_boundary determines if a path boundary should accept
- // matches at the beginning or end of the string.
- bool RecursiveMatch(const std::string& s,
- size_t begin_char,
- size_t subrange_index,
- bool allow_implicit_path_boundary) const;
-
- std::vector<Subrange> subranges_;
-
- // Set to true when the subranges are "*foo" ("ANYTHING" followed by a
- // literal). This covers most patterns so we optimize for this.
- bool is_suffix_;
-};
-
-class PatternList {
- public:
- PatternList();
- PatternList(const PatternList& other);
- ~PatternList();
-
- bool is_empty() const { return patterns_.empty(); }
-
- void Append(const Pattern& pattern);
-
- // Initializes the pattern list from a give list of pattern strings. Sets
- // |*err| on failure.
- void SetFromValue(const Value& v, Err* err);
-
- bool MatchesString(const std::string& s) const;
- bool MatchesValue(const Value& v) const;
-
- private:
- std::vector<Pattern> patterns_;
-};
-
-#endif // TOOLS_GN_PATTERN_H_
diff --git a/gn/tools/gn/pattern_unittest.cc b/gn/tools/gn/pattern_unittest.cc
deleted file mode 100644
index 9e14ca2a28e..00000000000
--- a/gn/tools/gn/pattern_unittest.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/macros.h"
-#include "tools/gn/pattern.h"
-#include "util/test/test.h"
-
-namespace {
-
-struct Case {
- const char* pattern;
- const char* candidate;
- bool expected_match;
-};
-
-} // namespace
-
-TEST(Pattern, Matches) {
- Case pattern_cases[] = {
- // Empty pattern matches only empty string.
- {"", "", true},
- {"", "foo", false},
- // Exact matches.
- {"foo", "foo", true},
- {"foo", "bar", false},
- // Path boundaries.
- {"\\b", "", true},
- {"\\b", "/", true},
- {"\\b\\b", "/", true},
- {"\\b\\b\\b", "", false},
- {"\\b\\b\\b", "/", true},
- {"\\b", "//", false},
- {"\\bfoo\\b", "foo", true},
- {"\\bfoo\\b", "/foo/", true},
- {"\\b\\bfoo", "/foo", true},
- // *
- {"*", "", true},
- {"*", "foo", true},
- {"*foo", "foo", true},
- {"*foo", "gagafoo", true},
- {"*foo", "gagafoob", false},
- {"foo*bar", "foobar", true},
- {"foo*bar", "foo-bar", true},
- {"foo*bar", "foolalalalabar", true},
- {"foo*bar", "foolalalalabaz", false},
- {"*a*b*c*d*", "abcd", true},
- {"*a*b*c*d*", "1a2b3c4d5", true},
- {"*a*b*c*d*", "1a2b3c45", false},
- {"*\\bfoo\\b*", "foo", true},
- {"*\\bfoo\\b*", "/foo/", true},
- {"*\\bfoo\\b*", "foob", false},
- {"*\\bfoo\\b*", "lala/foo/bar/baz", true},
- };
- for (size_t i = 0; i < arraysize(pattern_cases); i++) {
- const Case& c = pattern_cases[i];
- Pattern pattern(c.pattern);
- bool result = pattern.MatchesString(c.candidate);
- EXPECT_EQ(c.expected_match, result)
- << i << ": \"" << c.pattern << "\", \"" << c.candidate << "\"";
- }
-}
diff --git a/gn/tools/gn/pool.cc b/gn/tools/gn/pool.cc
deleted file mode 100644
index 4b9629bbe7f..00000000000
--- a/gn/tools/gn/pool.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/pool.h"
-
-#include <sstream>
-
-#include "base/logging.h"
-
-Pool::~Pool() = default;
-
-Pool* Pool::AsPool() {
- return this;
-}
-
-const Pool* Pool::AsPool() const {
- return this;
-}
-
-std::string Pool::GetNinjaName(const Label& default_toolchain) const {
- bool include_toolchain = label().toolchain_dir() != default_toolchain.dir() ||
- label().toolchain_name() != default_toolchain.name();
- return GetNinjaName(include_toolchain);
-}
-
-std::string Pool::GetNinjaName(bool include_toolchain) const {
- std::ostringstream buffer;
- if (include_toolchain) {
- DCHECK(label().toolchain_dir().is_source_absolute());
- std::string toolchain_dir = label().toolchain_dir().value();
- for (std::string::size_type i = 2; i < toolchain_dir.size(); ++i) {
- buffer << (toolchain_dir[i] == '/' ? '_' : toolchain_dir[i]);
- }
- buffer << label().toolchain_name() << "_";
- }
-
- DCHECK(label().dir().is_source_absolute());
- std::string label_dir = label().dir().value();
- for (std::string::size_type i = 2; i < label_dir.size(); ++i) {
- buffer << (label_dir[i] == '/' ? '_' : label_dir[i]);
- }
- buffer << label().name();
- return buffer.str();
-}
diff --git a/gn/tools/gn/pool.h b/gn/tools/gn/pool.h
deleted file mode 100644
index 42a80789ec8..00000000000
--- a/gn/tools/gn/pool.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_POOL_H_
-#define TOOLS_GN_POOL_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "tools/gn/item.h"
-
-// Represents a named pool in the dependency graph.
-//
-// A pool is used to limit the parallelism of task invocation in the
-// generated ninja build. Pools are referenced by toolchains.
-class Pool : public Item {
- public:
- using Item::Item;
- ~Pool() override;
-
- // Item implementation.
- Pool* AsPool() override;
- const Pool* AsPool() const override;
-
- // The pool depth (number of task to run simultaneously).
- int64_t depth() const { return depth_; }
- void set_depth(int64_t depth) { depth_ = depth; }
-
- // The pool name in generated ninja files.
- std::string GetNinjaName(const Label& default_toolchain) const;
-
- private:
- std::string GetNinjaName(bool include_toolchain) const;
-
- int64_t depth_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(Pool);
-};
-
-#endif // TOOLS_GN_POOL_H_
diff --git a/gn/tools/gn/qt_creator_writer.cc b/gn/tools/gn/qt_creator_writer.cc
deleted file mode 100644
index c24502430dc..00000000000
--- a/gn/tools/gn/qt_creator_writer.cc
+++ /dev/null
@@ -1,302 +0,0 @@
-// Copyright (c) 2016 The Chromium 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 "tools/gn/qt_creator_writer.h"
-
-#include <set>
-#include <sstream>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/optional.h"
-
-#include "tools/gn/builder.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/label.h"
-#include "tools/gn/loader.h"
-
-namespace {
-base::FilePath::CharType kProjectDirName[] =
- FILE_PATH_LITERAL("qtcreator_project");
-base::FilePath::CharType kProjectName[] = FILE_PATH_LITERAL("all");
-base::FilePath::CharType kMainProjectFileSuffix[] =
- FILE_PATH_LITERAL(".creator");
-base::FilePath::CharType kSourcesFileSuffix[] = FILE_PATH_LITERAL(".files");
-base::FilePath::CharType kIncludesFileSuffix[] = FILE_PATH_LITERAL(".includes");
-base::FilePath::CharType kDefinesFileSuffix[] = FILE_PATH_LITERAL(".config");
-} // namespace
-
-// static
-bool QtCreatorWriter::RunAndWriteFile(const BuildSettings* build_settings,
- const Builder& builder,
- Err* err,
- const std::string& root_target) {
- base::FilePath project_dir =
- build_settings->GetFullPath(build_settings->build_dir())
- .Append(kProjectDirName);
- if (!base::DirectoryExists(project_dir)) {
- base::File::Error error;
- if (!base::CreateDirectoryAndGetError(project_dir, &error)) {
- *err =
- Err(Location(), "Could not create the QtCreator project directory '" +
- FilePathToUTF8(project_dir) +
- "': " + base::File::ErrorToString(error));
- return false;
- }
- }
-
- base::FilePath project_prefix = project_dir.Append(kProjectName);
- QtCreatorWriter gen(build_settings, builder, project_prefix, root_target);
- gen.Run();
- if (gen.err_.has_error()) {
- *err = gen.err_;
- return false;
- }
- return true;
-}
-
-QtCreatorWriter::QtCreatorWriter(const BuildSettings* build_settings,
- const Builder& builder,
- const base::FilePath& project_prefix,
- const std::string& root_target_name)
- : build_settings_(build_settings),
- builder_(builder),
- project_prefix_(project_prefix),
- root_target_name_(root_target_name) {}
-
-QtCreatorWriter::~QtCreatorWriter() = default;
-
-void QtCreatorWriter::CollectDeps(const Target* target) {
- for (const auto& dep : target->GetDeps(Target::DEPS_ALL)) {
- const Target* dep_target = dep.ptr;
- if (targets_.count(dep_target))
- continue;
- targets_.insert(dep_target);
- CollectDeps(dep_target);
- }
-}
-
-bool QtCreatorWriter::DiscoverTargets() {
- auto all_targets = builder_.GetAllResolvedTargets();
-
- if (root_target_name_.empty()) {
- targets_ = std::set<const Target*>(all_targets.begin(), all_targets.end());
- return true;
- }
-
- const Target* root_target = nullptr;
- for (const Target* target : all_targets) {
- if (target->label().name() == root_target_name_) {
- root_target = target;
- break;
- }
- }
-
- if (!root_target) {
- err_ = Err(Location(), "Target '" + root_target_name_ + "' not found.");
- return false;
- }
-
- targets_.insert(root_target);
- CollectDeps(root_target);
- return true;
-}
-
-void QtCreatorWriter::AddToSources(const Target::FileList& files) {
- for (const SourceFile& file : files) {
- const std::string& file_path =
- FilePathToUTF8(build_settings_->GetFullPath(file));
- sources_.insert(file_path);
- }
-}
-
-namespace QtCreatorWriterUtils {
-
-enum class CVersion {
- C99,
- C11,
-};
-
-enum class CxxVersion {
- CXX98,
- CXX03,
- CXX11,
- CXX14,
- CXX17,
-};
-
-std::string ToMacro(CVersion version) {
- const std::string s = "__STDC_VERSION__";
-
- switch(version) {
- case CVersion::C99:
- return s + " 199901L";
- case CVersion::C11:
- return s + " 201112L";
- }
-
- return std::string();
-}
-
-std::string ToMacro(CxxVersion version) {
- const std::string name = "__cplusplus";
-
- switch(version) {
- case CxxVersion::CXX98:
- case CxxVersion::CXX03:
- return name + " 199711L";
- case CxxVersion::CXX11:
- return name + " 201103L";
- case CxxVersion::CXX14:
- return name + " 201402L";
- case CxxVersion::CXX17:
- return name + " 201703L";
- }
-
- return std::string();
-}
-
-const std::map<std::string, CVersion> kFlagToCVersion{
- {"-std=gnu99" , CVersion::C99},
- {"-std=c99" , CVersion::C99},
- {"-std=gnu11" , CVersion::C11},
- {"-std=c11" , CVersion::C11}
-};
-
-const std::map<std::string, CxxVersion> kFlagToCxxVersion{
- {"-std=gnu++11", CxxVersion::CXX11}, {"-std=c++11", CxxVersion::CXX11},
- {"-std=gnu++98", CxxVersion::CXX98}, {"-std=c++98", CxxVersion::CXX98},
- {"-std=gnu++03", CxxVersion::CXX03}, {"-std=c++03", CxxVersion::CXX03},
- {"-std=gnu++14", CxxVersion::CXX14}, {"-std=c++14", CxxVersion::CXX14},
- {"-std=c++1y" , CxxVersion::CXX14},
- {"-std=gnu++17", CxxVersion::CXX17}, {"-std=c++17", CxxVersion::CXX17},
- {"-std=c++1z" , CxxVersion::CXX17},
-};
-
-template<typename Enum>
-struct CompVersion {
- bool operator()(Enum a, Enum b) {
- return static_cast<int>(a) < static_cast<int>(b);
- }
-};
-
-struct CompilerOptions {
- base::Optional<CVersion> c_version_;
- base::Optional<CxxVersion> cxx_version_;
-
- void SetCVersion(CVersion ver) {
- SetVersionImpl(c_version_, ver);
- }
-
- void SetCxxVersion(CxxVersion ver) {
- SetVersionImpl(cxx_version_, ver);
- }
-
-private:
- template<typename Version>
- void SetVersionImpl(base::Optional<Version> &cur_ver, Version ver) {
- if (cur_ver)
- cur_ver = std::max(*cur_ver, ver, CompVersion<Version> {});
- else
- cur_ver = ver;
- }
-};
-
-void ParseCompilerOption(const std::string& flag, CompilerOptions* options) {
- auto c_ver = kFlagToCVersion.find(flag);
- if (c_ver != kFlagToCVersion.end())
- options->SetCVersion(c_ver->second);
-
- auto cxx_ver = kFlagToCxxVersion.find(flag);
- if (cxx_ver != kFlagToCxxVersion.end())
- options->SetCxxVersion(cxx_ver->second);
-}
-
-void ParseCompilerOptions(const std::vector<std::string>& cflags,
- CompilerOptions* options) {
- for (const std::string& flag : cflags)
- ParseCompilerOption(flag, options);
-}
-
-} // QtCreatorWriterUtils
-
-void QtCreatorWriter::HandleTarget(const Target* target) {
- using namespace QtCreatorWriterUtils;
-
- SourceFile build_file = Loader::BuildFileForLabel(target->label());
- sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(build_file)));
- AddToSources(target->settings()->import_manager().GetImportedFiles());
-
- AddToSources(target->sources());
- AddToSources(target->public_headers());
-
- for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
- for (const auto& input : it.cur().inputs())
- sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(input)));
-
- SourceFile precompiled_source = it.cur().precompiled_source();
- if (!precompiled_source.is_null()) {
- sources_.insert(
- FilePathToUTF8(build_settings_->GetFullPath(precompiled_source)));
- }
-
- for (const SourceDir& include_dir : it.cur().include_dirs()) {
- includes_.insert(
- FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
- }
-
- static constexpr const char *define_str = "#define ";
- for (std::string define : it.cur().defines()) {
- size_t equal_pos = define.find('=');
- if (equal_pos != std::string::npos)
- define[equal_pos] = ' ';
- define.insert(0, define_str);
- defines_.insert(define);
- }
-
- CompilerOptions options;
- ParseCompilerOptions(it.cur().cflags(), &options);
- ParseCompilerOptions(it.cur().cflags_c(), &options);
- ParseCompilerOptions(it.cur().cflags_cc(), &options);
-
- auto add_define_version = [this] (auto &ver) {
- if (ver)
- defines_.insert(define_str + ToMacro(*ver));
- };
- add_define_version(options.c_version_);
- add_define_version(options.cxx_version_);
- }
-}
-
-void QtCreatorWriter::GenerateFile(const base::FilePath::CharType* suffix,
- const std::set<std::string>& items) {
- const base::FilePath file_path = project_prefix_.AddExtension(suffix);
- std::ostringstream output;
- for (const std::string& item : items)
- output << item << std::endl;
- WriteFileIfChanged(file_path, output.str(), &err_);
-}
-
-void QtCreatorWriter::Run() {
- if (!DiscoverTargets())
- return;
-
- for (const Target* target : targets_) {
- if (target->toolchain()->label() !=
- builder_.loader()->GetDefaultToolchain())
- continue;
- HandleTarget(target);
- }
-
- std::set<std::string> empty_list;
-
- GenerateFile(kMainProjectFileSuffix, empty_list);
- GenerateFile(kSourcesFileSuffix, sources_);
- GenerateFile(kIncludesFileSuffix, includes_);
- GenerateFile(kDefinesFileSuffix, defines_);
-}
diff --git a/gn/tools/gn/qt_creator_writer.h b/gn/tools/gn/qt_creator_writer.h
deleted file mode 100644
index cb374049f40..00000000000
--- a/gn/tools/gn/qt_creator_writer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2016 The Chromium 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 TOOLS_GN_QT_CREATOR_WRITER_H_
-#define TOOLS_GN_QT_CREATOR_WRITER_H_
-
-#include <set>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "tools/gn/err.h"
-#include "tools/gn/target.h"
-
-class Builder;
-class BuildSettings;
-
-class QtCreatorWriter {
- public:
- static bool RunAndWriteFile(const BuildSettings* build_settings,
- const Builder& builder,
- Err* err,
- const std::string& root_target);
-
- private:
- QtCreatorWriter(const BuildSettings* build_settings,
- const Builder& builder,
- const base::FilePath& project_prefix,
- const std::string& root_target_name);
- ~QtCreatorWriter();
-
- void Run();
-
- bool DiscoverTargets();
- void HandleTarget(const Target* target);
-
- void CollectDeps(const Target* target);
- void AddToSources(const Target::FileList& files);
- void GenerateFile(const base::FilePath::CharType* suffix,
- const std::set<std::string>& items);
-
- const BuildSettings* build_settings_;
- const Builder& builder_;
- base::FilePath project_prefix_;
- std::string root_target_name_;
- std::set<const Target*> targets_;
- std::set<std::string> sources_;
- std::set<std::string> includes_;
- std::set<std::string> defines_;
- Err err_;
-
- DISALLOW_COPY_AND_ASSIGN(QtCreatorWriter);
-};
-
-#endif // TOOLS_GN_QT_CREATOR_WRITER_H_
diff --git a/gn/tools/gn/runtime_deps.cc b/gn/tools/gn/runtime_deps.cc
deleted file mode 100644
index 3477ce4d2ed..00000000000
--- a/gn/tools/gn/runtime_deps.cc
+++ /dev/null
@@ -1,314 +0,0 @@
-// 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 "tools/gn/runtime_deps.h"
-
-#include <map>
-#include <set>
-#include <sstream>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/strings/string_split.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/target.h"
-#include "tools/gn/trace.h"
-
-namespace {
-
-using RuntimeDepsVector = std::vector<std::pair<OutputFile, const Target*>>;
-
-// Adds the given file to the deps list if it hasn't already been listed in
-// the found_files list. Updates the list.
-void AddIfNew(const OutputFile& output_file,
- const Target* source,
- RuntimeDepsVector* deps,
- std::set<OutputFile>* found_file) {
- if (found_file->find(output_file) != found_file->end())
- return; // Already there.
- deps->push_back(std::make_pair(output_file, source));
-}
-
-// Automatically converts a string that looks like a source to an OutputFile.
-void AddIfNew(const std::string& str,
- const Target* source,
- RuntimeDepsVector* deps,
- std::set<OutputFile>* found_file) {
- OutputFile output_file(
- RebasePath(str, source->settings()->build_settings()->build_dir(),
- source->settings()->build_settings()->root_path_utf8()));
- AddIfNew(output_file, source, deps, found_file);
-}
-
-// To avoid duplicate traversals of targets, or duplicating output files that
-// might be listed by more than one target, the set of targets and output files
-// that have been found so far is passed. The "value" of the seen_targets map
-// is a boolean indicating if the seen dep was a data dep (true = data_dep).
-// data deps add more stuff, so we will want to revisit a target if it's a
-// data dependency and we've previously only seen it as a regular dep.
-void RecursiveCollectRuntimeDeps(const Target* target,
- bool is_target_data_dep,
- RuntimeDepsVector* deps,
- std::map<const Target*, bool>* seen_targets,
- std::set<OutputFile>* found_files) {
- const auto& found_seen_target = seen_targets->find(target);
- if (found_seen_target != seen_targets->end()) {
- // Already visited.
- if (found_seen_target->second || !is_target_data_dep) {
- // Already visited as a data dep, or the current dep is not a data
- // dep so visiting again will be a no-op.
- return;
- }
- // In the else case, the previously seen target was a regular dependency
- // and we'll now process it as a data dependency.
- }
- (*seen_targets)[target] = is_target_data_dep;
-
- // Add the main output file for executables, shared libraries, and
- // loadable modules.
- if (target->output_type() == Target::EXECUTABLE ||
- target->output_type() == Target::LOADABLE_MODULE ||
- target->output_type() == Target::SHARED_LIBRARY) {
- for (const auto& runtime_output : target->runtime_outputs())
- AddIfNew(runtime_output, target, deps, found_files);
- }
-
- // Add all data files.
- for (const auto& file : target->data())
- AddIfNew(file, target, deps, found_files);
-
- // Actions/copy have all outputs considered when the're a data dep.
- if (is_target_data_dep && (target->output_type() == Target::ACTION ||
- target->output_type() == Target::ACTION_FOREACH ||
- target->output_type() == Target::COPY_FILES)) {
- std::vector<SourceFile> outputs;
- target->action_values().GetOutputsAsSourceFiles(target, &outputs);
- for (const auto& output_file : outputs)
- AddIfNew(output_file.value(), target, deps, found_files);
- }
-
- // Data dependencies.
- for (const auto& dep_pair : target->data_deps()) {
- RecursiveCollectRuntimeDeps(dep_pair.ptr, true, deps, seen_targets,
- found_files);
- }
-
- // Do not recurse into bundle targets. A bundle's dependencies should be
- // copied into the bundle itself for run-time access.
- if (target->output_type() == Target::CREATE_BUNDLE) {
- SourceDir bundle_root_dir =
- target->bundle_data().GetBundleRootDirOutputAsDir(target->settings());
- AddIfNew(bundle_root_dir.value(), target, deps, found_files);
- return;
- }
-
- // Non-data dependencies (both public and private).
- for (const auto& dep_pair : target->GetDeps(Target::DEPS_LINKED)) {
- if (dep_pair.ptr->output_type() == Target::EXECUTABLE)
- continue; // Skip executables that aren't data deps.
- if (dep_pair.ptr->output_type() == Target::SHARED_LIBRARY &&
- (target->output_type() == Target::ACTION ||
- target->output_type() == Target::ACTION_FOREACH)) {
- // Skip shared libraries that action depends on,
- // unless it were listed in data deps.
- continue;
- }
- RecursiveCollectRuntimeDeps(dep_pair.ptr, false, deps, seen_targets,
- found_files);
- }
-}
-
-bool CollectRuntimeDepsFromFlag(const Builder& builder,
- RuntimeDepsVector* files_to_write,
- Err* err) {
- std::string deps_target_list_file =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kRuntimeDepsListFile);
-
- if (deps_target_list_file.empty())
- return true;
-
- std::string list_contents;
- ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, deps_target_list_file);
- if (!base::ReadFileToString(UTF8ToFilePath(deps_target_list_file),
- &list_contents)) {
- *err = Err(Location(),
- std::string("File for --") + switches::kRuntimeDepsListFile +
- " doesn't exist.",
- "The file given was \"" + deps_target_list_file + "\"");
- return false;
- }
- load_trace.Done();
-
- SourceDir root_dir("//");
- Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
- for (const auto& line : base::SplitString(
- list_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
- if (line.empty())
- continue;
- Label label = Label::Resolve(root_dir, default_toolchain_label,
- Value(nullptr, line), err);
- if (err->has_error())
- return false;
-
- const Item* item = builder.GetItem(label);
- const Target* target = item ? item->AsTarget() : nullptr;
- if (!target) {
- *err =
- Err(Location(),
- "The label \"" + label.GetUserVisibleName(true) +
- "\" isn't a target.",
- "When reading the line:\n " + line +
- "\n"
- "from the --" +
- switches::kRuntimeDepsListFile + "=" + deps_target_list_file);
- return false;
- }
-
- OutputFile output_file;
- const char extension[] = ".runtime_deps";
- if (target->output_type() == Target::SHARED_LIBRARY ||
- target->output_type() == Target::LOADABLE_MODULE) {
- // Force the first output for shared-library-type linker outputs since
- // the dependency output files might not be the main output.
- CHECK(!target->computed_outputs().empty());
- output_file =
- OutputFile(target->computed_outputs()[0].value() + extension);
- } else {
- output_file =
- OutputFile(target->dependency_output_file().value() + extension);
- }
- files_to_write->push_back(std::make_pair(output_file, target));
- }
- return true;
-}
-
-bool WriteRuntimeDepsFile(const OutputFile& output_file,
- const Target* target,
- Err* err) {
- SourceFile output_as_source =
- output_file.AsSourceFile(target->settings()->build_settings());
- base::FilePath data_deps_file =
- target->settings()->build_settings()->GetFullPath(output_as_source);
-
- std::stringstream contents;
- for (const auto& pair : ComputeRuntimeDeps(target))
- contents << pair.first.value() << std::endl;
-
- ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, output_as_source.value());
- return WriteFileIfChanged(data_deps_file, contents.str(), err);
-}
-
-} // namespace
-
-const char kRuntimeDeps_Help[] =
- R"(Runtime dependencies
-
- Runtime dependencies of a target are exposed via the "runtime_deps" category
- of "gn desc" (see "gn help desc") or they can be written at build generation
- time via write_runtime_deps(), or --runtime-deps-list-file (see "gn help
- --runtime-deps-list-file").
-
- To a first approximation, the runtime dependencies of a target are the set of
- "data" files, data directories, and the shared libraries from all transitive
- dependencies. Executables, shared libraries, and loadable modules are
- considered runtime dependencies of themselves.
-
-Executables
-
- Executable targets and those executable targets' transitive dependencies are
- not considered unless that executable is listed in "data_deps". Otherwise, GN
- assumes that the executable (and everything it requires) is a build-time
- dependency only.
-
-Actions and copies
-
- Action and copy targets that are listed as "data_deps" will have all of their
- outputs and data files considered as runtime dependencies. Action and copy
- targets that are "deps" or "public_deps" will have only their data files
- considered as runtime dependencies. These targets can list an output file in
- both the "outputs" and "data" lists to force an output file as a runtime
- dependency in all cases.
-
- The different rules for deps and data_deps are to express build-time (deps)
- vs. run-time (data_deps) outputs. If GN counted all build-time copy steps as
- data dependencies, there would be a lot of extra stuff, and if GN counted all
- run-time dependencies as regular deps, the build's parallelism would be
- unnecessarily constrained.
-
- This rule can sometimes lead to unintuitive results. For example, given the
- three targets:
- A --[data_deps]--> B --[deps]--> ACTION
- GN would say that A does not have runtime deps on the result of the ACTION,
- which is often correct. But the purpose of the B target might be to collect
- many actions into one logic unit, and the "data"-ness of A's dependency is
- lost. Solutions:
-
- - List the outputs of the action in its data section (if the results of
- that action are always runtime files).
- - Have B list the action in data_deps (if the outputs of the actions are
- always runtime files).
- - Have B list the action in both deps and data deps (if the outputs might be
- used in both contexts and you don't care about unnecessary entries in the
- list of files required at runtime).
- - Split B into run-time and build-time versions with the appropriate "deps"
- for each.
-
-Static libraries and source sets
-
- The results of static_library or source_set targets are not considered
- runtime dependencies since these are assumed to be intermediate targets only.
- If you need to list a static library as a runtime dependency, you can
- manually compute the .a/.lib file name for the current platform and list it
- in the "data" list of a target (possibly on the static library target
- itself).
-
-Multiple outputs
-
- Linker tools can specify which of their outputs should be considered when
- computing the runtime deps by setting runtime_outputs. If this is unset on
- the tool, the default will be the first output only.
-)";
-
-RuntimeDepsVector ComputeRuntimeDeps(const Target* target) {
- RuntimeDepsVector result;
- std::map<const Target*, bool> seen_targets;
- std::set<OutputFile> found_files;
-
- // The initial target is not considered a data dependency so that actions's
- // outputs (if the current target is an action) are not automatically
- // considered data deps.
- RecursiveCollectRuntimeDeps(target, false, &result, &seen_targets,
- &found_files);
- return result;
-}
-
-bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err) {
- RuntimeDepsVector files_to_write;
- if (!CollectRuntimeDepsFromFlag(builder, &files_to_write, err))
- return false;
-
- // Files scheduled by write_runtime_deps.
- for (const Target* target : g_scheduler->GetWriteRuntimeDepsTargets()) {
- files_to_write.push_back(
- std::make_pair(target->write_runtime_deps_output(), target));
- }
-
- for (const auto& entry : files_to_write) {
- // Currently this writes all runtime deps files sequentially. We generally
- // expect few of these. We can run this on the worker pool if it looks
- // like it's talking a long time.
- if (!WriteRuntimeDepsFile(entry.first, entry.second, err))
- return false;
- }
- return true;
-}
diff --git a/gn/tools/gn/runtime_deps.h b/gn/tools/gn/runtime_deps.h
deleted file mode 100644
index 8592677b6dd..00000000000
--- a/gn/tools/gn/runtime_deps.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 TOOLS_GN_RUNTIME_DEPS_H
-#define TOOLS_GN_RUNTIME_DEPS_H
-
-#include <utility>
-#include <vector>
-
-class Builder;
-class Err;
-class OutputFile;
-class Target;
-
-extern const char kRuntimeDeps_Help[];
-
-// Computes the runtime dependencies of the given target. The result is a list
-// of pairs listing the runtime dependency and the target that the runtime
-// dependency is from (for blaming).
-std::vector<std::pair<OutputFile, const Target*>> ComputeRuntimeDeps(
- const Target* target);
-
-// Writes all runtime deps files requested on the command line, or does nothing
-// if no files were specified.
-bool WriteRuntimeDepsFilesIfNecessary(const Builder& builder, Err* err);
-
-#endif // TOOLS_GN_RUNTIME_DEPS_H
diff --git a/gn/tools/gn/runtime_deps_unittest.cc b/gn/tools/gn/runtime_deps_unittest.cc
deleted file mode 100644
index 1adfdd41744..00000000000
--- a/gn/tools/gn/runtime_deps_unittest.cc
+++ /dev/null
@@ -1,448 +0,0 @@
-// 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 <stddef.h>
-
-#include "base/stl_util.h"
-#include "tools/gn/runtime_deps.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-void InitTargetWithType(TestWithScope& setup,
- Target* target,
- Target::OutputType type) {
- target->set_output_type(type);
- target->visibility().SetPublic();
- target->SetToolchain(setup.toolchain());
-}
-
-// Convenience function to make the correct kind of pair.
-std::pair<OutputFile, const Target*> MakePair(const char* str,
- const Target* t) {
- return std::pair<OutputFile, const Target*>(OutputFile(str), t);
-}
-
-std::string GetVectorDescription(
- const std::vector<std::pair<OutputFile, const Target*>>& v) {
- std::string result;
- for (size_t i = 0; i < v.size(); i++) {
- if (i != 0)
- result.append(", ");
- result.append("\"" + v[i].first.value() + "\"");
- }
- return result;
-}
-
-} // namespace
-
-using RuntimeDeps = TestWithScheduler;
-
-// Tests an exe depending on different types of libraries.
-TEST_F(RuntimeDeps, Libs) {
- TestWithScope setup;
- Err err;
-
- // Dependency hierarchy: main(exe) -> static library
- // -> shared library
- // -> loadable module
- // -> source set
-
- Target stat(setup.settings(), Label(SourceDir("//"), "stat"));
- InitTargetWithType(setup, &stat, Target::STATIC_LIBRARY);
- stat.data().push_back("//stat.dat");
- ASSERT_TRUE(stat.OnResolved(&err));
-
- Target shared(setup.settings(), Label(SourceDir("//"), "shared"));
- InitTargetWithType(setup, &shared, Target::SHARED_LIBRARY);
- shared.data().push_back("//shared.dat");
- ASSERT_TRUE(shared.OnResolved(&err));
-
- Target loadable(setup.settings(), Label(SourceDir("//"), "loadable"));
- InitTargetWithType(setup, &loadable, Target::LOADABLE_MODULE);
- loadable.data().push_back("//loadable.dat");
- ASSERT_TRUE(loadable.OnResolved(&err));
-
- Target set(setup.settings(), Label(SourceDir("//"), "set"));
- InitTargetWithType(setup, &set, Target::SOURCE_SET);
- set.data().push_back("//set.dat");
- ASSERT_TRUE(set.OnResolved(&err));
-
- Target main(setup.settings(), Label(SourceDir("//"), "main"));
- InitTargetWithType(setup, &main, Target::EXECUTABLE);
- main.private_deps().push_back(LabelTargetPair(&stat));
- main.private_deps().push_back(LabelTargetPair(&shared));
- main.private_deps().push_back(LabelTargetPair(&loadable));
- main.private_deps().push_back(LabelTargetPair(&set));
- main.data().push_back("//main.dat");
- ASSERT_TRUE(main.OnResolved(&err));
-
- std::vector<std::pair<OutputFile, const Target*>> result =
- ComputeRuntimeDeps(&main);
-
- // The result should have deps of main, all 5 dat files, libshared.so, and
- // libloadable.so.
- ASSERT_EQ(8u, result.size()) << GetVectorDescription(result);
-
- // The first one should always be the main exe.
- EXPECT_TRUE(MakePair("./main", &main) == result[0]);
-
- // The rest of the ordering is undefined. First the data files.
- EXPECT_TRUE(base::ContainsValue(result, MakePair("../../stat.dat", &stat)))
- << GetVectorDescription(result);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../shared.dat", &shared)))
- << GetVectorDescription(result);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../loadable.dat", &loadable)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(result, MakePair("../../set.dat", &set)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(result, MakePair("../../main.dat", &main)))
- << GetVectorDescription(result);
-
- // Check the static library and loadable module.
- EXPECT_TRUE(base::ContainsValue(result, MakePair("./libshared.so", &shared)))
- << GetVectorDescription(result);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("./libloadable.so", &loadable)))
- << GetVectorDescription(result);
-}
-
-// Tests that executables that aren't listed as data deps aren't included in
-// the output, but executables that are data deps are included.
-TEST_F(RuntimeDeps, ExeDataDep) {
- TestWithScope setup;
- Err err;
-
- // Dependency hierarchy: main(exe) -> datadep(exe) -> final_in(source set)
- // -> dep(exe) -> final_out(source set)
- // The final_in/out targets each have data files. final_in's should be
- // included, final_out's should not be.
-
- Target final_in(setup.settings(), Label(SourceDir("//"), "final_in"));
- InitTargetWithType(setup, &final_in, Target::SOURCE_SET);
- final_in.data().push_back("//final_in.dat");
- ASSERT_TRUE(final_in.OnResolved(&err));
-
- Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
- InitTargetWithType(setup, &datadep, Target::EXECUTABLE);
- datadep.private_deps().push_back(LabelTargetPair(&final_in));
- ASSERT_TRUE(datadep.OnResolved(&err));
-
- Target final_out(setup.settings(), Label(SourceDir("//"), "final_out"));
- InitTargetWithType(setup, &final_out, Target::SOURCE_SET);
- final_out.data().push_back("//final_out.dat");
- ASSERT_TRUE(final_out.OnResolved(&err));
-
- Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
- InitTargetWithType(setup, &dep, Target::EXECUTABLE);
- dep.private_deps().push_back(LabelTargetPair(&final_out));
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target main(setup.settings(), Label(SourceDir("//"), "main"));
- InitTargetWithType(setup, &main, Target::EXECUTABLE);
- main.private_deps().push_back(LabelTargetPair(&dep));
- main.data_deps().push_back(LabelTargetPair(&datadep));
- ASSERT_TRUE(main.OnResolved(&err));
-
- std::vector<std::pair<OutputFile, const Target*>> result =
- ComputeRuntimeDeps(&main);
-
- // The result should have deps of main, datadep, final_in.dat
- ASSERT_EQ(3u, result.size()) << GetVectorDescription(result);
-
- // The first one should always be the main exe.
- EXPECT_TRUE(MakePair("./main", &main) == result[0]);
-
- // The rest of the ordering is undefined.
- EXPECT_TRUE(base::ContainsValue(result, MakePair("./datadep", &datadep)))
- << GetVectorDescription(result);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../final_in.dat", &final_in)))
- << GetVectorDescription(result);
-}
-
-TEST_F(RuntimeDeps, ActionSharedLib) {
- TestWithScope setup;
- Err err;
-
- // Dependency hierarchy: main(exe) -> action -> datadep(shared library)
- // -> dep(shared library)
- // Datadep should be included, dep should not be.
-
- Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
- InitTargetWithType(setup, &dep, Target::SHARED_LIBRARY);
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
- InitTargetWithType(setup, &datadep, Target::SHARED_LIBRARY);
- ASSERT_TRUE(datadep.OnResolved(&err));
-
- Target action(setup.settings(), Label(SourceDir("//"), "action"));
- InitTargetWithType(setup, &action, Target::ACTION);
- action.private_deps().push_back(LabelTargetPair(&dep));
- action.data_deps().push_back(LabelTargetPair(&datadep));
- action.action_values().outputs() =
- SubstitutionList::MakeForTest("//action.output");
- ASSERT_TRUE(action.OnResolved(&err));
-
- Target main(setup.settings(), Label(SourceDir("//"), "main"));
- InitTargetWithType(setup, &main, Target::EXECUTABLE);
- main.private_deps().push_back(LabelTargetPair(&action));
- ASSERT_TRUE(main.OnResolved(&err));
-
- std::vector<std::pair<OutputFile, const Target*>> result =
- ComputeRuntimeDeps(&main);
-
- // The result should have deps of main and data_dep.
- ASSERT_EQ(2u, result.size()) << GetVectorDescription(result);
-
- // The first one should always be the main exe.
- EXPECT_TRUE(MakePair("./main", &main) == result[0]);
- EXPECT_TRUE(MakePair("./libdatadep.so", &datadep) == result[1]);
-}
-
-// Tests that action and copy outputs are considered if they're data deps, but
-// not if they're regular deps. Action and copy "data" files are always
-// included.
-TEST_F(RuntimeDeps, ActionOutputs) {
- TestWithScope setup;
- Err err;
-
- // Dependency hierarchy: main(exe) -> datadep (action)
- // -> datadep_copy (copy)
- // -> dep (action)
- // -> dep_copy (copy)
-
- Target datadep(setup.settings(), Label(SourceDir("//"), "datadep"));
- InitTargetWithType(setup, &datadep, Target::ACTION);
- datadep.data().push_back("//datadep.data");
- datadep.action_values().outputs() =
- SubstitutionList::MakeForTest("//datadep.output");
- ASSERT_TRUE(datadep.OnResolved(&err));
-
- Target datadep_copy(setup.settings(), Label(SourceDir("//"), "datadep_copy"));
- InitTargetWithType(setup, &datadep_copy, Target::COPY_FILES);
- datadep_copy.sources().push_back(SourceFile("//input"));
- datadep_copy.data().push_back("//datadep_copy.data");
- datadep_copy.action_values().outputs() =
- SubstitutionList::MakeForTest("//datadep_copy.output");
- ASSERT_TRUE(datadep_copy.OnResolved(&err));
-
- Target dep(setup.settings(), Label(SourceDir("//"), "dep"));
- InitTargetWithType(setup, &dep, Target::ACTION);
- dep.data().push_back("//dep.data");
- dep.action_values().outputs() = SubstitutionList::MakeForTest("//dep.output");
- ASSERT_TRUE(dep.OnResolved(&err));
-
- Target dep_copy(setup.settings(), Label(SourceDir("//"), "dep_copy"));
- InitTargetWithType(setup, &dep_copy, Target::COPY_FILES);
- dep_copy.sources().push_back(SourceFile("//input"));
- dep_copy.data().push_back("//dep_copy/data/"); // Tests a directory.
- dep_copy.action_values().outputs() =
- SubstitutionList::MakeForTest("//dep_copy.output");
- ASSERT_TRUE(dep_copy.OnResolved(&err));
-
- Target main(setup.settings(), Label(SourceDir("//"), "main"));
- InitTargetWithType(setup, &main, Target::EXECUTABLE);
- main.private_deps().push_back(LabelTargetPair(&dep));
- main.private_deps().push_back(LabelTargetPair(&dep_copy));
- main.data_deps().push_back(LabelTargetPair(&datadep));
- main.data_deps().push_back(LabelTargetPair(&datadep_copy));
- ASSERT_TRUE(main.OnResolved(&err));
-
- std::vector<std::pair<OutputFile, const Target*>> result =
- ComputeRuntimeDeps(&main);
-
- // The result should have deps of main, both datadeps files, but only
- // the data file from dep.
- ASSERT_EQ(7u, result.size()) << GetVectorDescription(result);
-
- // The first one should always be the main exe.
- EXPECT_TRUE(MakePair("./main", &main) == result[0]);
-
- // The rest of the ordering is undefined.
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../datadep.data", &datadep)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(
- result, MakePair("../../datadep_copy.data", &datadep_copy)))
- << GetVectorDescription(result);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../datadep.output", &datadep)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(
- result, MakePair("../../datadep_copy.output", &datadep_copy)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(result, MakePair("../../dep.data", &dep)))
- << GetVectorDescription(result);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../dep_copy/data/", &dep_copy)))
- << GetVectorDescription(result);
-
- // Explicitly asking for the runtime deps of an action target only includes
- // the data and not all outputs.
- result = ComputeRuntimeDeps(&dep);
- ASSERT_EQ(1u, result.size());
- EXPECT_TRUE(MakePair("../../dep.data", &dep) == result[0]);
-}
-
-// Tests that the search for dependencies terminates at a bundle target,
-// ignoring any shared libraries or loadable modules that get copied into the
-// bundle.
-TEST_F(RuntimeDeps, CreateBundle) {
- TestWithScope setup;
- Err err;
-
- // Dependency hierarchy:
- // main(exe) -> dep(bundle) -> dep(shared_library) -> dep(source set)
- // -> dep(bundle_data) -> dep(loadable_module)
- // -> data(lm.data)
- // -> datadep(datadep) -> data(dd.data)
-
- const SourceDir source_dir("//");
- const std::string& build_dir = setup.build_settings()->build_dir().value();
-
- Target loadable_module(setup.settings(),
- Label(source_dir, "loadable_module"));
- InitTargetWithType(setup, &loadable_module, Target::LOADABLE_MODULE);
- loadable_module.data().push_back("//lm.data");
- ASSERT_TRUE(loadable_module.OnResolved(&err));
-
- Target module_data(setup.settings(), Label(source_dir, "module_data"));
- InitTargetWithType(setup, &module_data, Target::BUNDLE_DATA);
- module_data.private_deps().push_back(LabelTargetPair(&loadable_module));
- module_data.bundle_data().file_rules().push_back(BundleFileRule(
- nullptr,
- std::vector<SourceFile>{SourceFile(build_dir + "loadable_module.so")},
- SubstitutionPattern::MakeForTest("{{bundle_resources_dir}}")));
- ASSERT_TRUE(module_data.OnResolved(&err));
-
- Target source_set(setup.settings(), Label(source_dir, "sources"));
- InitTargetWithType(setup, &source_set, Target::SOURCE_SET);
- source_set.sources().push_back(SourceFile(source_dir.value() + "foo.cc"));
- ASSERT_TRUE(source_set.OnResolved(&err));
-
- Target dylib(setup.settings(), Label(source_dir, "dylib"));
- dylib.set_output_prefix_override(true);
- dylib.set_output_extension("");
- dylib.set_output_name("Bundle");
- InitTargetWithType(setup, &dylib, Target::SHARED_LIBRARY);
- dylib.private_deps().push_back(LabelTargetPair(&source_set));
- ASSERT_TRUE(dylib.OnResolved(&err));
-
- Target dylib_data(setup.settings(), Label(source_dir, "dylib_data"));
- InitTargetWithType(setup, &dylib_data, Target::BUNDLE_DATA);
- dylib_data.private_deps().push_back(LabelTargetPair(&dylib));
- dylib_data.bundle_data().file_rules().push_back(BundleFileRule(
- nullptr, std::vector<SourceFile>{SourceFile(build_dir + "dylib")},
- SubstitutionPattern::MakeForTest("{{bundle_executable_dir}}")));
- ASSERT_TRUE(dylib_data.OnResolved(&err));
-
- Target data_dep(setup.settings(), Label(source_dir, "datadep"));
- InitTargetWithType(setup, &data_dep, Target::EXECUTABLE);
- data_dep.data().push_back("//dd.data");
- ASSERT_TRUE(data_dep.OnResolved(&err));
-
- Target bundle(setup.settings(), Label(source_dir, "bundle"));
- InitTargetWithType(setup, &bundle, Target::CREATE_BUNDLE);
- const std::string root_dir(build_dir + "Bundle.framework/");
- const std::string contents_dir(root_dir + "Versions/A/");
- bundle.bundle_data().root_dir() = SourceDir(root_dir);
- bundle.bundle_data().contents_dir() = SourceDir(contents_dir);
- bundle.bundle_data().resources_dir() = SourceDir(contents_dir + "Resources");
- bundle.bundle_data().executable_dir() = SourceDir(contents_dir + "MacOS");
- bundle.private_deps().push_back(LabelTargetPair(&dylib_data));
- bundle.private_deps().push_back(LabelTargetPair(&module_data));
- bundle.data_deps().push_back(LabelTargetPair(&data_dep));
- bundle.data().push_back("//b.data");
- ASSERT_TRUE(bundle.OnResolved(&err));
-
- Target main(setup.settings(), Label(source_dir, "main"));
- InitTargetWithType(setup, &main, Target::EXECUTABLE);
- main.data_deps().push_back(LabelTargetPair(&bundle));
- ASSERT_TRUE(main.OnResolved(&err));
-
- std::vector<std::pair<OutputFile, const Target*>> result =
- ComputeRuntimeDeps(&main);
-
- // The result should have deps of main, datadep, final_in.dat
- ASSERT_EQ(5u, result.size()) << GetVectorDescription(result);
-
- // The first one should always be the main exe.
- EXPECT_EQ(MakePair("./main", &main), result[0]);
-
- // The rest of the ordering is undefined.
-
- // The framework bundle's internal dependencies should not be incldued.
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("Bundle.framework/", &bundle)))
- << GetVectorDescription(result);
- // But direct data and data dependencies should be.
- EXPECT_TRUE(base::ContainsValue(result, MakePair("./datadep", &data_dep)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(result, MakePair("../../dd.data", &data_dep)))
- << GetVectorDescription(result);
- EXPECT_TRUE(base::ContainsValue(result, MakePair("../../b.data", &bundle)))
- << GetVectorDescription(result);
-}
-
-// Tests that a dependency duplicated in regular and data deps is processed
-// as a data dep.
-TEST_F(RuntimeDeps, Dupe) {
- TestWithScope setup;
- Err err;
-
- Target action(setup.settings(), Label(SourceDir("//"), "action"));
- InitTargetWithType(setup, &action, Target::ACTION);
- action.action_values().outputs() =
- SubstitutionList::MakeForTest("//action.output");
- ASSERT_TRUE(action.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//"), "foo"));
- InitTargetWithType(setup, &target, Target::EXECUTABLE);
- target.private_deps().push_back(LabelTargetPair(&action));
- target.data_deps().push_back(LabelTargetPair(&action));
- ASSERT_TRUE(target.OnResolved(&err));
-
- // The results should be the executable and the copy output.
- std::vector<std::pair<OutputFile, const Target*>> result =
- ComputeRuntimeDeps(&target);
- EXPECT_TRUE(
- base::ContainsValue(result, MakePair("../../action.output", &action)))
- << GetVectorDescription(result);
-}
-
-// Tests that actions can't have output substitutions.
-TEST_F(RuntimeDeps, WriteRuntimeDepsVariable) {
- TestWithScope setup;
- Err err;
-
- // Should refuse to write files outside of the output dir.
- EXPECT_FALSE(setup.ExecuteSnippet(
- "group(\"foo\") { write_runtime_deps = \"//foo.txt\" }", &err));
-
- // Should fail for garbage inputs.
- err = Err();
- EXPECT_FALSE(
- setup.ExecuteSnippet("group(\"foo\") { write_runtime_deps = 0 }", &err));
-
- // Should be able to write inside the out dir, and shouldn't write the one
- // in the else clause.
- err = Err();
- EXPECT_TRUE(setup.ExecuteSnippet(
- "if (true) {\n"
- " group(\"foo\") { write_runtime_deps = \"//out/Debug/foo.txt\" }\n"
- "} else {\n"
- " group(\"bar\") { write_runtime_deps = \"//out/Debug/bar.txt\" }\n"
- "}",
- &err));
- EXPECT_EQ(1U, setup.items().size());
- EXPECT_EQ(1U, scheduler().GetWriteRuntimeDepsTargets().size());
-}
diff --git a/gn/tools/gn/rust_substitution_type.cc b/gn/tools/gn/rust_substitution_type.cc
deleted file mode 100644
index c6b8e9e74f7..00000000000
--- a/gn/tools/gn/rust_substitution_type.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/rust_substitution_type.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "tools/gn/err.h"
-#include "tools/gn/substitution_type.h"
-
-const SubstitutionTypes RustSubstitutions = {
- &kRustSubstitutionCrateName, &kRustSubstitutionCrateType,
- &kRustSubstitutionEdition, &kRustSubstitutionExterns,
- &kRustSubstitutionOutputExtension, &kRustSubstitutionOutputPrefix,
- &kRustSubstitutionRustDeps, &kRustSubstitutionRustFlags,
- &kRustSubstitutionRustEnv,
-};
-
-// Valid for Rust tools.
-const Substitution kRustSubstitutionCrateName = {"{{crate_name}}",
- "crate_name"};
-const Substitution kRustSubstitutionCrateType = {"{{crate_type}}",
- "crate_type"};
-const Substitution kRustSubstitutionEdition = {"{{edition}}", "edition"};
-const Substitution kRustSubstitutionExterns = {"{{externs}}", "externs"};
-const Substitution kRustSubstitutionOutputExtension = {
- "{{rustc_output_extension}}", "rustc_output_extension"};
-const Substitution kRustSubstitutionOutputPrefix = {"{{rustc_output_prefix}}",
- "rustc_output_prefix"};
-const Substitution kRustSubstitutionRustDeps = {"{{rustdeps}}", "rustdeps"};
-const Substitution kRustSubstitutionRustEnv = {"{{rustenv}}", "rustenv"};
-const Substitution kRustSubstitutionRustFlags = {"{{rustflags}}", "rustflags"};
-
-bool IsValidRustSubstitution(const Substitution* type) {
- return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
- type == &SubstitutionOutputDir ||
- type == &kRustSubstitutionCrateName ||
- type == &kRustSubstitutionCrateType ||
- type == &kRustSubstitutionEdition ||
- type == &kRustSubstitutionExterns ||
- type == &kRustSubstitutionOutputExtension ||
- type == &kRustSubstitutionOutputPrefix ||
- type == &kRustSubstitutionRustDeps ||
- type == &kRustSubstitutionRustEnv ||
- type == &kRustSubstitutionRustFlags;
-}
diff --git a/gn/tools/gn/rust_substitution_type.h b/gn/tools/gn/rust_substitution_type.h
deleted file mode 100644
index 827f5d6d4a8..00000000000
--- a/gn/tools/gn/rust_substitution_type.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
-#define TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
-
-#include <set>
-#include <vector>
-
-#include "tools/gn/substitution_type.h"
-
-// The set of substitutions available to Rust tools.
-extern const SubstitutionTypes RustSubstitutions;
-
-// Valid for Rust tools.
-extern const Substitution kRustSubstitutionCrateName;
-extern const Substitution kRustSubstitutionCrateType;
-extern const Substitution kRustSubstitutionEdition;
-extern const Substitution kRustSubstitutionExterns;
-extern const Substitution kRustSubstitutionOutputExtension;
-extern const Substitution kRustSubstitutionOutputPrefix;
-extern const Substitution kRustSubstitutionRustDeps;
-extern const Substitution kRustSubstitutionRustEnv;
-extern const Substitution kRustSubstitutionRustFlags;
-
-bool IsValidRustSubstitution(const Substitution* type);
-
-#endif // TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
diff --git a/gn/tools/gn/rust_tool.cc b/gn/tools/gn/rust_tool.cc
deleted file mode 100644
index e4e127d2ddc..00000000000
--- a/gn/tools/gn/rust_tool.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/rust_tool.h"
-
-#include "tools/gn/rust_substitution_type.h"
-#include "tools/gn/target.h"
-
-const char* RustTool::kRsToolRustc = "rustc";
-
-RustTool::RustTool(const char* n) : Tool(n), rlib_output_extension_(".rlib") {
- CHECK(ValidateName(n));
-}
-
-RustTool::~RustTool() = default;
-
-RustTool* RustTool::AsRust() {
- return this;
-}
-const RustTool* RustTool::AsRust() const {
- return this;
-}
-
-bool RustTool::ValidateName(const char* name) const {
- return name_ == kRsToolRustc;
-}
-
-void RustTool::SetComplete() {
- SetToolComplete();
-}
-
-bool RustTool::SetOutputExtension(const Value* value,
- std::string* var,
- Err* err) {
- DCHECK(!complete_);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
- if (value->string_value().empty())
- return true;
-
- *var = std::move(value->string_value());
- return true;
-}
-
-bool RustTool::ReadOutputExtensions(Scope* scope, Err* err) {
- if (!SetOutputExtension(scope->GetValue("exe_output_extension", true),
- &exe_output_extension_, err))
- return false;
- if (!SetOutputExtension(scope->GetValue("rlib_output_extension", true),
- &rlib_output_extension_, err))
- return false;
- if (!SetOutputExtension(scope->GetValue("dylib_output_extension", true),
- &dylib_output_extension_, err))
- return false;
- if (!SetOutputExtension(scope->GetValue("cdylib_output_extension", true),
- &cdylib_output_extension_, err))
- return false;
- if (!SetOutputExtension(scope->GetValue("staticlib_output_extension", true),
- &staticlib_output_extension_, err))
- return false;
- if (!SetOutputExtension(scope->GetValue("proc_macro_output_extension", true),
- &proc_macro_output_extension_, err))
- return false;
- return true;
-}
-
-bool RustTool::ReadOutputsPatternList(Scope* scope,
- const char* var,
- SubstitutionList* field,
- Err* err) {
- DCHECK(!complete_);
- const Value* value = scope->GetValue(var, true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::LIST, err))
- return false;
-
- SubstitutionList list;
- if (!list.Parse(*value, err))
- return false;
-
- // Validate the right kinds of patterns are used.
- if (list.list().empty()) {
- *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
- return false;
- }
-
- for (const auto& cur_type : list.required_types()) {
- if (!IsValidRustSubstitution(cur_type)) {
- *err = Err(*value, "Pattern not valid here.",
- "You used the pattern " + std::string(cur_type->name) +
- " which is not valid\nfor this variable.");
- return false;
- }
- }
-
- *field = std::move(list);
- return true;
-}
-
-bool RustTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
- // Initialize default vars.
- if (!Tool::InitTool(scope, toolchain, err)) {
- return false;
- }
-
- if (!ReadOutputExtensions(scope, err)) {
- return false;
- }
-
- // All Rust tools should have outputs.
- if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
- return false;
- }
-
- return true;
-}
-
-bool RustTool::ValidateSubstitution(const Substitution* sub_type) const {
- if (name_ == kRsToolRustc)
- return IsValidRustSubstitution(sub_type);
- NOTREACHED();
- return false;
-}
-
-const std::string& RustTool::rustc_output_extension(
- Target::OutputType type,
- const RustValues::CrateType crate_type) const {
- switch (crate_type) {
- case RustValues::CRATE_AUTO: {
- switch (type) {
- case Target::EXECUTABLE:
- return exe_output_extension_;
- case Target::STATIC_LIBRARY:
- return staticlib_output_extension_;
- case Target::RUST_LIBRARY:
- return rlib_output_extension_;
- default:
- NOTREACHED();
- return exe_output_extension_;
- }
- }
- case RustValues::CRATE_DYLIB:
- return dylib_output_extension_;
- case RustValues::CRATE_CDYLIB:
- return cdylib_output_extension_;
- case RustValues::CRATE_PROC_MACRO:
- return proc_macro_output_extension_;
- default:
- NOTREACHED();
- return exe_output_extension_;
- }
-}
diff --git a/gn/tools/gn/rust_tool.h b/gn/tools/gn/rust_tool.h
deleted file mode 100644
index 682f74ad275..00000000000
--- a/gn/tools/gn/rust_tool.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_TOOL_H_
-#define TOOLS_GN_RUST_TOOL_H_
-
-#include <string>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/rust_values.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_pattern.h"
-#include "tools/gn/target.h"
-#include "tools/gn/tool.h"
-
-class RustTool : public Tool {
- public:
- // Rust tools
- static const char* kRsToolRustc;
-
- explicit RustTool(const char* n);
- ~RustTool();
-
- // Manual RTTI and required functions ---------------------------------------
-
- bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
- bool ValidateName(const char* name) const override;
- void SetComplete() override;
- bool ValidateSubstitution(const Substitution* sub_type) const override;
-
- RustTool* AsRust() override;
- const RustTool* AsRust() const override;
-
- void set_exe_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- exe_output_extension_ = std::move(ext);
- }
-
- void set_rlib_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- rlib_output_extension_ = std::move(ext);
- }
-
- void set_dylib_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- dylib_output_extension_ = std::move(ext);
- }
-
- void set_cdylib_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- cdylib_output_extension_ = std::move(ext);
- }
-
- void set_staticlib_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- staticlib_output_extension_ = std::move(ext);
- }
-
- void set_proc_macro_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- proc_macro_output_extension_ = std::move(ext);
- }
-
- // Will include a leading "." if nonempty.
- const std::string& rustc_output_extension(
- Target::OutputType type,
- const RustValues::CrateType crate_type) const;
-
- private:
- bool SetOutputExtension(const Value* value, std::string* var, Err* err);
- bool ReadOutputExtensions(Scope* scope, Err* err);
- bool ReadOutputsPatternList(Scope* scope,
- const char* var,
- SubstitutionList* field,
- Err* err);
-
- std::string exe_output_extension_;
- std::string rlib_output_extension_;
- std::string dylib_output_extension_;
- std::string cdylib_output_extension_;
- std::string staticlib_output_extension_;
- std::string proc_macro_output_extension_;
-
- DISALLOW_COPY_AND_ASSIGN(RustTool);
-};
-
-#endif // TOOLS_GN_RUST_TOOL_H_
diff --git a/gn/tools/gn/rust_values.cc b/gn/tools/gn/rust_values.cc
deleted file mode 100644
index 08f3e5ee9a2..00000000000
--- a/gn/tools/gn/rust_values.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/rust_values.h"
-
-RustValues::RustValues() : crate_type_(RustValues::CRATE_AUTO) {}
-
-RustValues::~RustValues() = default; \ No newline at end of file
diff --git a/gn/tools/gn/rust_values.h b/gn/tools/gn/rust_values.h
deleted file mode 100644
index ad9c5c6c88d..00000000000
--- a/gn/tools/gn/rust_values.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_TARGET_VALUES_H_
-#define TOOLS_GN_RUST_TARGET_VALUES_H_
-
-#include <map>
-
-#include "base/containers/flat_map.h"
-#include "tools/gn/label.h"
-#include "tools/gn/source_file.h"
-
-// Holds the values (outputs, args, script name, etc.) for either an action or
-// an action_foreach target.
-class RustValues {
- public:
- RustValues();
- ~RustValues();
-
- // Library crate types are specified here. Shared library crate types must be
- // specified, all other crate types can be automatically deduced from the
- // target type (e.g. executables use crate_type = "bin", static_libraries use
- // crate_type = "staticlib") unless explicitly set.
- enum CrateType {
- CRATE_AUTO = 0,
- CRATE_BIN,
- CRATE_CDYLIB,
- CRATE_DYLIB,
- CRATE_PROC_MACRO,
- CRATE_RLIB,
- CRATE_STATICLIB,
- };
-
- // Name of this crate.
- std::string& crate_name() { return crate_name_; }
- const std::string& crate_name() const { return crate_name_; }
-
- // Main source file for this crate.
- const SourceFile& crate_root() const { return crate_root_; }
- void set_crate_root(SourceFile& s) { crate_root_ = s; }
-
- // Crate type for compilation.
- CrateType crate_type() { return crate_type_; }
- const CrateType crate_type() const { return crate_type_; }
- void set_crate_type(CrateType s) { crate_type_ = s; }
-
- std::string& edition() { return edition_; }
- const std::string& edition() const { return edition_; }
-
- // Any renamed dependencies for the `extern` flags.
- const std::map<Label, std::string>& aliased_deps() const {
- return aliased_deps_;
- }
- std::map<Label, std::string>& aliased_deps() { return aliased_deps_; }
-
- private:
- std::string crate_name_;
- SourceFile crate_root_;
- CrateType crate_type_ = CRATE_AUTO;
- std::string edition_;
- std::map<Label, std::string> aliased_deps_;
-
- DISALLOW_COPY_AND_ASSIGN(RustValues);
-};
-
-#endif // TOOLS_GN_RUST_TARGET_VALUES_H_
diff --git a/gn/tools/gn/rust_values_generator.cc b/gn/tools/gn/rust_values_generator.cc
deleted file mode 100644
index 910d5af7f88..00000000000
--- a/gn/tools/gn/rust_values_generator.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/rust_values_generator.h"
-
-#include "tools/gn/config_values_generator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/rust_variables.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value_extractors.h"
-
-static const char* kRustSupportedCrateTypesError =
- "\"crate_type\" must be one of \"bin\", \"cdylib\", \"dylib\", or "
- "\"proc-macro\", \"rlib\", \"staticlib\".";
-
-RustTargetGenerator::RustTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err)
- : target_(target),
- scope_(scope),
- function_call_(function_call),
- err_(err) {}
-
-RustTargetGenerator::~RustTargetGenerator() = default;
-
-void RustTargetGenerator::Run() {
- // source_set targets don't need any special Rust handling.
- if (target_->output_type() == Target::SOURCE_SET)
- return;
-
- // Check that this type of target is Rust-supported.
- if (target_->output_type() != Target::EXECUTABLE &&
- target_->output_type() != Target::SHARED_LIBRARY &&
- target_->output_type() != Target::RUST_LIBRARY &&
- target_->output_type() != Target::STATIC_LIBRARY &&
- target_->output_type() != Target::LOADABLE_MODULE) {
- // Only valid rust output types.
- *err_ = Err(function_call_,
- "Target type \"" +
- std::string(Target::GetStringForOutputType(
- target_->output_type())) +
- "\" is not supported for Rust compilation.",
- "Supported target types are \"executable\", \"loadable_module\""
- "\"shared_library\", \"static_library\", or \"source_set\".");
- return;
- }
-
- if (!FillCrateName())
- return;
-
- if (!FillCrateType())
- return;
-
- if (!FillCrateRoot())
- return;
-
- if (!FillEdition())
- return;
-
- if (!FillAliasedDeps())
- return;
-}
-
-bool RustTargetGenerator::FillCrateName() {
- const Value* value = scope_->GetValue(variables::kRustCrateName, true);
- if (!value) {
- // The target name will be used.
- target_->rust_values().crate_name() = target_->label().name();
- return true;
- }
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- target_->rust_values().crate_name() = std::move(value->string_value());
- return true;
-}
-
-bool RustTargetGenerator::FillCrateType() {
- const Value* value = scope_->GetValue(variables::kRustCrateType, true);
- if (!value) {
- // Require shared_library and loadable_module targets to tell us what
- // they want.
- if (target_->output_type() == Target::SHARED_LIBRARY ||
- target_->output_type() == Target::LOADABLE_MODULE) {
- *err_ = Err(function_call_,
- "Must set \"crate_type\" on a Rust \"shared_library\".",
- kRustSupportedCrateTypesError);
- return false;
- }
-
- return true;
- }
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- if (value->string_value() == "bin") {
- target_->rust_values().set_crate_type(RustValues::CRATE_BIN);
- return true;
- }
- if (value->string_value() == "cdylib") {
- target_->rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
- return true;
- }
- if (value->string_value() == "dylib") {
- target_->rust_values().set_crate_type(RustValues::CRATE_DYLIB);
- return true;
- }
- if (value->string_value() == "proc-macro") {
- target_->rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
- return true;
- }
- if (value->string_value() == "rlib") {
- target_->rust_values().set_crate_type(RustValues::CRATE_RLIB);
- return true;
- }
- if (value->string_value() == "staticlib") {
- target_->rust_values().set_crate_type(RustValues::CRATE_STATICLIB);
- return true;
- }
-
- *err_ = Err(value->origin(),
- "Inadmissible crate type \"" + value->string_value() + "\".",
- kRustSupportedCrateTypesError);
- return false;
-}
-
-bool RustTargetGenerator::FillCrateRoot() {
- const Value* value = scope_->GetValue(variables::kRustCrateRoot, true);
- if (!value) {
- // If there's only one source, use that.
- if (target_->sources().size() == 1) {
- target_->rust_values().set_crate_root(target_->sources()[0]);
- return true;
- }
- // Otherwise, see if "lib.rs" or "main.rs" (as relevant) are in sources.
- std::string to_find =
- target_->output_type() == Target::EXECUTABLE ? "main.rs" : "lib.rs";
- for (auto& source : target_->sources()) {
- if (source.GetName() == to_find) {
- target_->rust_values().set_crate_root(source);
- return true;
- }
- }
- *err_ = Err(function_call_, "Missing \"crate_root\" and missing \"" +
- to_find + "\" in sources.");
- return false;
- }
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- SourceFile dest;
- if (!ExtractRelativeFile(scope_->settings()->build_settings(), *value,
- scope_->GetSourceDir(), &dest, err_))
- return false;
-
- target_->rust_values().set_crate_root(dest);
- return true;
-}
-
-bool RustTargetGenerator::FillEdition() {
- const Value* value = scope_->GetValue(variables::kRustEdition, true);
- if (!value) {
- *err_ = Err(function_call_, "Missing \"edition\" in Rust target.");
- return false;
- }
-
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
-
- target_->rust_values().edition() = std::move(value->string_value());
- return true;
-}
-
-bool RustTargetGenerator::FillAliasedDeps() {
- const Value* value = scope_->GetValue(variables::kRustAliasedDeps, true);
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::SCOPE, err_))
- return false;
-
- Scope::KeyValueMap aliased_deps;
- value->scope_value()->GetCurrentScopeValues(&aliased_deps);
- for (const auto& pair : aliased_deps) {
- Label dep_label =
- Label::Resolve(scope_->GetSourceDir(), ToolchainLabelForScope(scope_),
- pair.second, err_);
-
- if (err_->has_error())
- return false;
-
- // Insert into the aliased_deps map.
- target_->rust_values().aliased_deps().emplace(std::move(dep_label),
- pair.first.as_string());
- }
-
- return true;
-}
diff --git a/gn/tools/gn/rust_values_generator.h b/gn/tools/gn/rust_values_generator.h
deleted file mode 100644
index fb939dbf2c9..00000000000
--- a/gn/tools/gn/rust_values_generator.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_VALUES_GENERATOR_H_
-#define TOOLS_GN_RUST_VALUES_GENERATOR_H_
-
-#include "base/macros.h"
-#include "tools/gn/target.h"
-
-class FunctionCallNode;
-
-// Collects and writes specified data.
-class RustTargetGenerator {
- public:
- RustTargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err);
- ~RustTargetGenerator();
-
- void Run();
-
- private:
- bool FillCrateName();
- bool FillCrateRoot();
- bool FillCrateType();
- bool FillEdition();
- bool FillAliasedDeps();
-
- Target* target_;
- Scope* scope_;
- const FunctionCallNode* function_call_;
- Err* err_;
-
- DISALLOW_COPY_AND_ASSIGN(RustTargetGenerator);
-};
-
-#endif // TOOLS_GN_RUST_VALUES_GENERATOR_H_
diff --git a/gn/tools/gn/rust_variables.cc b/gn/tools/gn/rust_variables.cc
deleted file mode 100644
index 67018c7e34f..00000000000
--- a/gn/tools/gn/rust_variables.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2019 The Chromium 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 "tools/gn/rust_variables.h"
-
-namespace variables {
-
-// Rust target variables ------------------------------------------------------
-
-const char kRustAliasedDeps[] = "aliased_deps";
-const char kRustAliasedDeps_HelpShort[] =
- "aliased_deps: [scope] Set of crate-dependency pairs.";
-const char kRustAliasedDeps_Help[] =
- R"(aliased_deps: [scope] Set of crate-dependency pairs.
-
- Valid for `rust_library` targets and `executable`, `static_library`, and
- `shared_library` targets that contain Rust sources.
-
- A scope, each key indicating the renamed crate and the corresponding value
- specifying the label of the dependency producing the relevant binary.
-
- All dependencies listed in this field *must* be listed as deps of the target.
-
- executable("foo") {
- sources = [ "main.rs" ]
- deps = [ "//bar" ]
- }
-
- This target would compile the `foo` crate with the following `extern` flag:
- `rustc ...command... --extern bar=<build_out_dir>/obj/bar`
-
- executable("foo") {
- sources = [ "main.rs" ]
- deps = [ ":bar" ]
- aliased_deps = {
- bar_renamed = ":bar"
- }
- }
-
- With the addition of `aliased_deps`, above target would instead compile with:
- `rustc ...command... --extern bar_renamed=<build_out_dir>/obj/bar`
-)";
-
-const char kRustCrateName[] = "crate_name";
-const char kRustCrateName_HelpShort[] =
- "crate_name: [string] The name for the compiled crate.";
-const char kRustCrateName_Help[] =
- R"(crate_name: [string] The name for the compiled crate.
-
- Valid for `rust_library` targets and `executable`, `static_library`,
- `shared_library`, and `source_set` targets that contain Rust sources.
-
- If crate_name is not set, then this rule will use the target name.
-)";
-
-const char kRustCrateType[] = "crate_type";
-const char kRustCrateType_HelpShort[] =
- "crate_type: [string] The type of linkage to use on a shared_library.";
-const char kRustCrateType_Help[] =
- R"(crate_type: [string] The type of linkage to use on a shared_library.
-
- Valid for `rust_library` targets and `executable`, `static_library`,
- `shared_library`, and `source_set` targets that contain Rust sources.
-
- Options for this field are "cdylib", "staticlib", "proc-macro", and "dylib".
- This field sets the `crate-type` attribute for the `rustc` tool on static
- libraries, as well as the appropiate output extension in the
- `rust_output_extension` attribute. Since outputs must be explicit, the `lib`
- crate type (where the Rust compiler produces what it thinks is the
- appropriate library type) is not supported.
-
- It should be noted that the "dylib" crate type in Rust is unstable in the set
- of symbols it exposes, and most usages today are potentially wrong and will
- be broken in the future.
-
- Static libraries, rust libraries, and executables have this field set
- automatically.
-)";
-
-const char kRustCrateRoot[] = "crate_root";
-const char kRustCrateRoot_HelpShort[] =
- "crate_root: [string] The root source file for a binary or library.";
-const char kRustCrateRoot_Help[] =
- R"(crate_root: [string] The root source file for a binary or library.
-
- Valid for `rust_library` targets and `executable`, `static_library`,
- `shared_library`, and `source_set` targets that contain Rust sources.
-
- This file is usually the `main.rs` or `lib.rs` for binaries and libraries,
- respectively.
-
- If crate_root is not set, then this rule will look for a lib.rs file (or
- main.rs for executable) or a single file in sources, if sources contains
- only one file.
-)";
-
-const char kRustEdition[] = "edition";
-const char kRustEdition_HelpShort[] =
- "edition: [string] The rustc edition to use in compiliation.";
-const char kRustEdition_Help[] =
- R"(edition: [string] The rustc edition to use in compiliation.
-
- Valid for `rust_library` targets and `executable`, `static_library`,
- `shared_library`, and `source_set` targets that contain Rust sources.
-
- This indicates the compiler edition to use in compilition. Should be a value
- like "2015" or "2018", indiicating the appropriate value to pass to the
- `--edition=<>` flag in rustc.
-)";
-
-void InsertRustVariables(VariableInfoMap* info_map) {
- info_map->insert(std::make_pair(
- kRustAliasedDeps,
- VariableInfo(kRustAliasedDeps_HelpShort, kRustAliasedDeps_Help)));
- info_map->insert(std::make_pair(
- kRustCrateName,
- VariableInfo(kRustCrateName_HelpShort, kRustCrateName_Help)));
- info_map->insert(std::make_pair(
- kRustCrateType,
- VariableInfo(kRustCrateType_HelpShort, kRustCrateType_Help)));
- info_map->insert(std::make_pair(
- kRustCrateRoot,
- VariableInfo(kRustCrateRoot_HelpShort, kRustCrateRoot_Help)));
- info_map->insert(std::make_pair(
- kRustEdition, VariableInfo(kRustEdition_HelpShort, kRustEdition_Help)));
-}
-
-} // namespace variables
diff --git a/gn/tools/gn/rust_variables.h b/gn/tools/gn/rust_variables.h
deleted file mode 100644
index c9ae4a0870c..00000000000
--- a/gn/tools/gn/rust_variables.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium 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 TOOLS_GN_RUST_VARIABLES_H_
-#define TOOLS_GN_RUST_VARIABLES_H_
-
-#include "tools/gn/variables.h"
-
-namespace variables {
-
-// Rust target vars ------------------------------------------------------
-
-extern const char kRustAliasedDeps[];
-extern const char kRustAliasedDeps_HelpShort[];
-extern const char kRustAliasedDeps_Help[];
-
-extern const char kRustCrateName[];
-extern const char kRustCrateName_HelpShort[];
-extern const char kRustCrateName_Help[];
-
-extern const char kRustCrateType[];
-extern const char kRustCrateType_HelpShort[];
-extern const char kRustCrateType_Help[];
-
-extern const char kRustCrateRoot[];
-extern const char kRustCrateRoot_HelpShort[];
-extern const char kRustCrateRoot_Help[];
-
-extern const char kRustEdition[];
-extern const char kRustEdition_HelpShort[];
-extern const char kRustEdition_Help[];
-
-void InsertRustVariables(VariableInfoMap* info_map);
-
-} // namespace variables
-
-#endif // TOOLS_GN_RUST_VARIABLES_H_ \ No newline at end of file
diff --git a/gn/tools/gn/scheduler.cc b/gn/tools/gn/scheduler.cc
deleted file mode 100644
index f4960b9bf63..00000000000
--- a/gn/tools/gn/scheduler.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-// 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 "tools/gn/scheduler.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/target.h"
-
-namespace {} // namespace
-
-Scheduler* g_scheduler = nullptr;
-
-Scheduler::Scheduler()
- : main_thread_run_loop_(MsgLoop::Current()),
- input_file_manager_(new InputFileManager) {
- g_scheduler = this;
-}
-
-Scheduler::~Scheduler() {
- WaitForPoolTasks();
- g_scheduler = nullptr;
-}
-
-bool Scheduler::Run() {
- main_thread_run_loop_->Run();
- bool local_is_failed;
- {
- std::lock_guard<std::mutex> lock(lock_);
- local_is_failed = is_failed();
- has_been_shutdown_ = true;
- }
- // Don't do this while holding |lock_|, since it will block on the workers,
- // which may be in turn waiting on the lock.
- WaitForPoolTasks();
- return !local_is_failed;
-}
-
-void Scheduler::Log(const std::string& verb, const std::string& msg) {
- task_runner()->PostTask(base::BindOnce(&Scheduler::LogOnMainThread,
- base::Unretained(this), verb, msg));
-}
-
-void Scheduler::FailWithError(const Err& err) {
- DCHECK(err.has_error());
- {
- std::lock_guard<std::mutex> lock(lock_);
-
- if (is_failed_ || has_been_shutdown_)
- return; // Ignore errors once we see one.
- is_failed_ = true;
- }
-
- task_runner()->PostTask(base::BindOnce(&Scheduler::FailWithErrorOnMainThread,
- base::Unretained(this), err));
-}
-
-void Scheduler::ScheduleWork(Task work) {
- IncrementWorkCount();
- pool_work_count_.Increment();
- worker_pool_.PostTask(base::BindOnce(
- [](Scheduler* self, Task work) {
- std::move(work).Run();
- self->DecrementWorkCount();
- if (!self->pool_work_count_.Decrement()) {
- std::unique_lock<std::mutex> auto_lock(self->pool_work_count_lock_);
- self->pool_work_count_cv_.notify_one();
- }
- },
- this, std::move(work)));
-}
-
-void Scheduler::AddGenDependency(const base::FilePath& file) {
- std::lock_guard<std::mutex> lock(lock_);
- gen_dependencies_.push_back(file);
-}
-
-std::vector<base::FilePath> Scheduler::GetGenDependencies() const {
- std::lock_guard<std::mutex> lock(lock_);
- return gen_dependencies_;
-}
-
-void Scheduler::AddWrittenFile(const SourceFile& file) {
- std::lock_guard<std::mutex> lock(lock_);
- written_files_.push_back(file);
-}
-
-void Scheduler::AddUnknownGeneratedInput(const Target* target,
- const SourceFile& file) {
- std::lock_guard<std::mutex> lock(lock_);
- unknown_generated_inputs_.insert(std::make_pair(file, target));
-}
-
-void Scheduler::AddWriteRuntimeDepsTarget(const Target* target) {
- std::lock_guard<std::mutex> lock(lock_);
- write_runtime_deps_targets_.push_back(target);
-}
-
-std::vector<const Target*> Scheduler::GetWriteRuntimeDepsTargets() const {
- std::lock_guard<std::mutex> lock(lock_);
- return write_runtime_deps_targets_;
-}
-
-bool Scheduler::IsFileGeneratedByWriteRuntimeDeps(
- const OutputFile& file) const {
- std::lock_guard<std::mutex> lock(lock_);
- // Number of targets should be quite small, so brute-force search is fine.
- for (const Target* target : write_runtime_deps_targets_) {
- if (file == target->write_runtime_deps_output()) {
- return true;
- }
- }
- return false;
-}
-
-void Scheduler::AddGeneratedFile(const SourceFile& entry) {
- std::lock_guard<std::mutex> lock(lock_);
- generated_files_.insert(std::make_pair(entry, true));
-}
-
-bool Scheduler::IsFileGeneratedByTarget(const SourceFile& file) const {
- std::lock_guard<std::mutex> lock(lock_);
- return generated_files_.find(file) != generated_files_.end();
-}
-
-std::multimap<SourceFile, const Target*> Scheduler::GetUnknownGeneratedInputs()
- const {
- std::lock_guard<std::mutex> lock(lock_);
-
- // Remove all unknown inputs that were written files. These are OK as inputs
- // to build steps since they were written as a side-effect of running GN.
- //
- // It's assumed that this function is called once during cleanup to check for
- // errors, so performing this work in the lock doesn't matter.
- std::multimap<SourceFile, const Target*> filtered = unknown_generated_inputs_;
- for (const SourceFile& file : written_files_)
- filtered.erase(file);
-
- return filtered;
-}
-
-void Scheduler::ClearUnknownGeneratedInputsAndWrittenFiles() {
- std::lock_guard<std::mutex> lock(lock_);
- unknown_generated_inputs_.clear();
- written_files_.clear();
-}
-
-void Scheduler::IncrementWorkCount() {
- work_count_.Increment();
-}
-
-void Scheduler::DecrementWorkCount() {
- if (!work_count_.Decrement()) {
- task_runner()->PostTask(
- base::BindOnce(&Scheduler::OnComplete, base::Unretained(this)));
- }
-}
-
-void Scheduler::SuppressOutputForTesting(bool suppress) {
- std::lock_guard<std::mutex> lock(lock_);
- suppress_output_for_testing_ = suppress;
-}
-
-void Scheduler::LogOnMainThread(const std::string& verb,
- const std::string& msg) {
- OutputString(verb, DECORATION_YELLOW);
- OutputString(" " + msg + "\n");
-}
-
-void Scheduler::FailWithErrorOnMainThread(const Err& err) {
- if (!suppress_output_for_testing_)
- err.PrintToStdout();
- task_runner()->PostQuit();
-}
-
-void Scheduler::OnComplete() {
- // Should be called on the main thread.
- DCHECK(task_runner() == MsgLoop::Current());
- task_runner()->PostQuit();
-}
-
-void Scheduler::WaitForPoolTasks() {
- std::unique_lock<std::mutex> lock(pool_work_count_lock_);
- while (!pool_work_count_.IsZero())
- pool_work_count_cv_.wait(lock);
-}
diff --git a/gn/tools/gn/scheduler.h b/gn/tools/gn/scheduler.h
deleted file mode 100644
index d005df559c8..00000000000
--- a/gn/tools/gn/scheduler.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// 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 TOOLS_GN_SCHEDULER_H_
-#define TOOLS_GN_SCHEDULER_H_
-
-#include <condition_variable>
-#include <map>
-#include <mutex>
-
-#include "base/atomic_ref_count.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "tools/gn/input_file_manager.h"
-#include "tools/gn/label.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/token.h"
-#include "util/msg_loop.h"
-#include "util/task.h"
-#include "util/worker_pool.h"
-
-class Target;
-
-// Maintains the thread pool and error state.
-class Scheduler {
- public:
- Scheduler();
- ~Scheduler();
-
- bool Run();
-
- MsgLoop* task_runner() {
- DCHECK(main_thread_run_loop_);
- return main_thread_run_loop_;
- }
-
- InputFileManager* input_file_manager() { return input_file_manager_.get(); }
-
- bool verbose_logging() const { return verbose_logging_; }
- void set_verbose_logging(bool v) { verbose_logging_ = v; }
-
- // TODO(brettw) data race on this access (benign?).
- bool is_failed() const { return is_failed_; }
-
- void Log(const std::string& verb, const std::string& msg);
- void FailWithError(const Err& err);
-
- void ScheduleWork(Task work);
-
- void Shutdown();
-
- // Declares that the given file was read and affected the build output.
- //
- // Some consumers expect provided path to be absolute.kk
- //
- // TODO(brettw) this is global rather than per-BuildSettings. If we
- // start using >1 build settings, then we probably want this to take a
- // BuildSettings object so we know the depdency on a per-build basis.
- // If moved, most of the Add/Get functions below should move as well.
- void AddGenDependency(const base::FilePath& file);
- std::vector<base::FilePath> GetGenDependencies() const;
-
- // Tracks calls to write_file for resolving with the unknown generated
- // inputs (see AddUnknownGeneratedInput below).
- void AddWrittenFile(const SourceFile& file);
-
- // Schedules a file to be written due to a target setting write_runtime_deps.
- void AddWriteRuntimeDepsTarget(const Target* entry);
- std::vector<const Target*> GetWriteRuntimeDepsTargets() const;
- bool IsFileGeneratedByWriteRuntimeDeps(const OutputFile& file) const;
-
- // Tracks generated_file calls.
- void AddGeneratedFile(const SourceFile& entry);
- bool IsFileGeneratedByTarget(const SourceFile& file) const;
-
- // Unknown generated inputs are files that a target declares as an input
- // in the output directory, but which aren't generated by any dependency.
- //
- // Some of these files will be files written by write_file and will be
- // GenDependencies (see AddWrittenFile above). There are OK and include
- // things like response files for scripts. Others cases will be ones where
- // the file is generated by a target that's not a dependency.
- //
- // In order to distinguish these two cases, the checking for these input
- // files needs to be done after all targets are complete. This also has the
- // nice side effect that if a target generates the file we can find it and
- // tell the user which dependency is missing.
- //
- // The result returned by GetUnknownGeneratedInputs will not count any files
- // that were written by write_file during execution.
- void AddUnknownGeneratedInput(const Target* target, const SourceFile& file);
- std::multimap<SourceFile, const Target*> GetUnknownGeneratedInputs() const;
- void ClearUnknownGeneratedInputsAndWrittenFiles(); // For testing.
-
- // We maintain a count of the things we need to do that works like a
- // refcount. When this reaches 0, the program exits.
- void IncrementWorkCount();
- void DecrementWorkCount();
-
- void SuppressOutputForTesting(bool suppress);
-
- private:
- void LogOnMainThread(const std::string& verb, const std::string& msg);
- void FailWithErrorOnMainThread(const Err& err);
-
- void DoTargetFileWrite(const Target* target);
-
- void OnComplete();
-
- // Waits for tasks scheduled via ScheduleWork() to complete their execution.
- void WaitForPoolTasks();
-
- MsgLoop* main_thread_run_loop_;
-
- scoped_refptr<InputFileManager> input_file_manager_;
-
- bool verbose_logging_ = false;
-
- base::AtomicRefCount work_count_;
-
- // Number of tasks scheduled by ScheduleWork() that haven't completed their
- // execution.
- base::AtomicRefCount pool_work_count_;
-
- // Lock for |pool_work_count_cv_|.
- std::mutex pool_work_count_lock_;
-
- // Condition variable signaled when |pool_work_count_| reaches zero.
- std::condition_variable pool_work_count_cv_;
-
- WorkerPool worker_pool_;
-
- mutable std::mutex lock_;
- bool is_failed_ = false;
-
- bool suppress_output_for_testing_ = false;
-
- // Used to track whether the worker pool has been shutdown. This is necessary
- // to clean up after tests that make a scheduler but don't run the message
- // loop.
- bool has_been_shutdown_ = false;
-
- // Protected by the lock. See the corresponding Add/Get functions above.
- std::vector<base::FilePath> gen_dependencies_;
- std::vector<SourceFile> written_files_;
- std::vector<const Target*> write_runtime_deps_targets_;
- std::multimap<SourceFile, const Target*> unknown_generated_inputs_;
- std::map<SourceFile, bool> generated_files_;
-
- DISALLOW_COPY_AND_ASSIGN(Scheduler);
-};
-
-extern Scheduler* g_scheduler;
-
-#endif // TOOLS_GN_SCHEDULER_H_
diff --git a/gn/tools/gn/scope.cc b/gn/tools/gn/scope.cc
deleted file mode 100644
index dd1a576840a..00000000000
--- a/gn/tools/gn/scope.cc
+++ /dev/null
@@ -1,593 +0,0 @@
-// 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 "tools/gn/scope.h"
-
-#include <memory>
-
-#include "base/logging.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/template.h"
-
-namespace {
-
-// FLags set in the mode_flags_ of a scope. If a bit is set, it applies
-// recursively to all dependent scopes.
-const unsigned kProcessingBuildConfigFlag = 1;
-const unsigned kProcessingImportFlag = 2;
-
-// Returns true if this variable name should be considered private. Private
-// values start with an underscore, and are not imported from "gni" files
-// when processing an import.
-bool IsPrivateVar(const base::StringPiece& name) {
- return name.empty() || name[0] == '_';
-}
-
-} // namespace
-
-// Defaults to all false, which are the things least likely to cause errors.
-Scope::MergeOptions::MergeOptions()
- : clobber_existing(false),
- skip_private_vars(false),
- mark_dest_used(false) {}
-
-Scope::MergeOptions::~MergeOptions() = default;
-
-Scope::ProgrammaticProvider::~ProgrammaticProvider() {
- scope_->RemoveProvider(this);
-}
-
-Scope::Scope(const Settings* settings)
- : const_containing_(nullptr),
- mutable_containing_(nullptr),
- settings_(settings),
- mode_flags_(0),
- item_collector_(nullptr) {}
-
-Scope::Scope(Scope* parent)
- : const_containing_(nullptr),
- mutable_containing_(parent),
- settings_(parent->settings()),
- mode_flags_(0),
- item_collector_(nullptr),
- build_dependency_files_(parent->build_dependency_files_) {}
-
-Scope::Scope(const Scope* parent)
- : const_containing_(parent),
- mutable_containing_(nullptr),
- settings_(parent->settings()),
- mode_flags_(0),
- item_collector_(nullptr),
- build_dependency_files_(parent->build_dependency_files_) {}
-
-Scope::~Scope() = default;
-
-void Scope::DetachFromContaining() {
- const_containing_ = nullptr;
- mutable_containing_ = nullptr;
-}
-
-bool Scope::HasValues(SearchNested search_nested) const {
- DCHECK(search_nested == SEARCH_CURRENT);
- return !values_.empty();
-}
-
-const Value* Scope::GetValue(const base::StringPiece& ident,
- bool counts_as_used) {
- const Scope* found_in_scope = nullptr;
- return GetValueWithScope(ident, counts_as_used, &found_in_scope);
-}
-
-const Value* Scope::GetValueWithScope(const base::StringPiece& ident,
- bool counts_as_used,
- const Scope** found_in_scope) {
- // First check for programmatically-provided values.
- for (auto* provider : programmatic_providers_) {
- const Value* v = provider->GetProgrammaticValue(ident);
- if (v) {
- *found_in_scope = nullptr;
- return v;
- }
- }
-
- RecordMap::iterator found = values_.find(ident);
- if (found != values_.end()) {
- if (counts_as_used)
- found->second.used = true;
- *found_in_scope = this;
- return &found->second.value;
- }
-
- // Search in the parent scope.
- if (const_containing_)
- return const_containing_->GetValueWithScope(ident, found_in_scope);
- if (mutable_containing_) {
- return mutable_containing_->GetValueWithScope(ident, counts_as_used,
- found_in_scope);
- }
- return nullptr;
-}
-
-Value* Scope::GetMutableValue(const base::StringPiece& ident,
- SearchNested search_mode,
- bool counts_as_used) {
- // Don't do programmatic values, which are not mutable.
- RecordMap::iterator found = values_.find(ident);
- if (found != values_.end()) {
- if (counts_as_used)
- found->second.used = true;
- return &found->second.value;
- }
-
- // Search in the parent mutable scope if requested, but not const one.
- if (search_mode == SEARCH_NESTED && mutable_containing_) {
- return mutable_containing_->GetMutableValue(ident, Scope::SEARCH_NESTED,
- counts_as_used);
- }
- return nullptr;
-}
-
-base::StringPiece Scope::GetStorageKey(const base::StringPiece& ident) const {
- RecordMap::const_iterator found = values_.find(ident);
- if (found != values_.end())
- return found->first;
-
- // Search in parent scope.
- if (containing())
- return containing()->GetStorageKey(ident);
- return base::StringPiece();
-}
-
-const Value* Scope::GetValue(const base::StringPiece& ident) const {
- const Scope* found_in_scope = nullptr;
- return GetValueWithScope(ident, &found_in_scope);
-}
-
-const Value* Scope::GetValueWithScope(const base::StringPiece& ident,
- const Scope** found_in_scope) const {
- RecordMap::const_iterator found = values_.find(ident);
- if (found != values_.end()) {
- *found_in_scope = this;
- return &found->second.value;
- }
- if (containing())
- return containing()->GetValueWithScope(ident, found_in_scope);
- return nullptr;
-}
-
-Value* Scope::SetValue(const base::StringPiece& ident,
- Value v,
- const ParseNode* set_node) {
- Record& r = values_[ident]; // Clears any existing value.
- r.value = std::move(v);
- r.value.set_origin(set_node);
- return &r.value;
-}
-
-void Scope::RemoveIdentifier(const base::StringPiece& ident) {
- RecordMap::iterator found = values_.find(ident);
- if (found != values_.end())
- values_.erase(found);
-}
-
-void Scope::RemovePrivateIdentifiers() {
- // Do it in two phases to avoid mutating while iterating. Our hash map is
- // currently backed by several different vendor-specific implementations and
- // I'm not sure if all of them support mutating while iterating. Since this
- // is not perf-critical, do the safe thing.
- std::vector<base::StringPiece> to_remove;
- for (const auto& cur : values_) {
- if (IsPrivateVar(cur.first))
- to_remove.push_back(cur.first);
- }
-
- for (const auto& cur : to_remove)
- values_.erase(cur);
-}
-
-bool Scope::AddTemplate(const std::string& name, const Template* templ) {
- if (GetTemplate(name))
- return false;
- templates_[name] = templ;
- return true;
-}
-
-const Template* Scope::GetTemplate(const std::string& name) const {
- TemplateMap::const_iterator found = templates_.find(name);
- if (found != templates_.end())
- return found->second.get();
- if (containing())
- return containing()->GetTemplate(name);
- return nullptr;
-}
-
-void Scope::MarkUsed(const base::StringPiece& ident) {
- RecordMap::iterator found = values_.find(ident);
- if (found == values_.end()) {
- NOTREACHED();
- return;
- }
- found->second.used = true;
-}
-
-void Scope::MarkAllUsed() {
- for (auto& cur : values_)
- cur.second.used = true;
-}
-
-void Scope::MarkAllUsed(const std::set<std::string>& excluded_values) {
- for (auto& cur : values_) {
- if (!excluded_values.empty() &&
- excluded_values.find(cur.first.as_string()) != excluded_values.end()) {
- continue; // Skip this excluded value.
- }
- cur.second.used = true;
- }
-}
-
-void Scope::MarkUnused(const base::StringPiece& ident) {
- RecordMap::iterator found = values_.find(ident);
- if (found == values_.end()) {
- NOTREACHED();
- return;
- }
- found->second.used = false;
-}
-
-bool Scope::IsSetButUnused(const base::StringPiece& ident) const {
- RecordMap::const_iterator found = values_.find(ident);
- if (found != values_.end()) {
- if (!found->second.used) {
- return true;
- }
- }
- return false;
-}
-
-bool Scope::CheckForUnusedVars(Err* err) const {
- for (const auto& pair : values_) {
- if (!pair.second.used) {
- std::string help =
- "You set the variable \"" + pair.first.as_string() +
- "\" here and it was unused before it went\nout of scope.";
-
- const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
- if (binary && binary->op().type() == Token::EQUAL) {
- // Make a nicer error message for normal var sets.
- *err =
- Err(binary->left()->GetRange(), "Assignment had no effect.", help);
- } else {
- // This will happen for internally-generated variables.
- *err =
- Err(pair.second.value.origin(), "Assignment had no effect.", help);
- }
- return false;
- }
- }
- return true;
-}
-
-void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
- for (const auto& pair : values_)
- (*output)[pair.first] = pair.second.value;
-}
-
-bool Scope::CheckCurrentScopeValuesEqual(const Scope* other) const {
- // If there are containing scopes, equality shouldn't work.
- if (containing()) {
- return false;
- }
- if (values_.size() != other->values_.size()) {
- return false;
- }
- for (const auto& pair : values_) {
- const Value* v = other->GetValue(pair.first);
- if (!v || *v != pair.second.value) {
- return false;
- }
- }
- return true;
-}
-
-bool Scope::NonRecursiveMergeTo(Scope* dest,
- const MergeOptions& options,
- const ParseNode* node_for_err,
- const char* desc_for_err,
- Err* err) const {
- // Values.
- for (const auto& pair : values_) {
- const base::StringPiece& current_name = pair.first;
- if (options.skip_private_vars && IsPrivateVar(current_name))
- continue; // Skip this private var.
- if (!options.excluded_values.empty() &&
- options.excluded_values.find(current_name.as_string()) !=
- options.excluded_values.end()) {
- continue; // Skip this excluded value.
- }
-
- const Value& new_value = pair.second.value;
- if (!options.clobber_existing) {
- const Value* existing_value = dest->GetValue(current_name);
- if (existing_value && new_value != *existing_value) {
- // Value present in both the source and the dest.
- std::string desc_string(desc_for_err);
- *err = Err(node_for_err, "Value collision.",
- "This " + desc_string + " contains \"" +
- current_name.as_string() + "\"");
- err->AppendSubErr(
- Err(pair.second.value, "defined here.",
- "Which would clobber the one in your current scope"));
- err->AppendSubErr(
- Err(*existing_value, "defined here.",
- "Executing " + desc_string +
- " should not conflict with anything "
- "in the current\nscope unless the values are identical."));
- return false;
- }
- }
- dest->values_[current_name] = pair.second;
-
- if (options.mark_dest_used)
- dest->MarkUsed(current_name);
- }
-
- // Target defaults are owning pointers.
- for (const auto& pair : target_defaults_) {
- const std::string& current_name = pair.first;
- if (!options.excluded_values.empty() &&
- options.excluded_values.find(current_name) !=
- options.excluded_values.end()) {
- continue; // Skip the excluded value.
- }
-
- if (!options.clobber_existing) {
- const Scope* dest_defaults = dest->GetTargetDefaults(current_name);
- if (dest_defaults) {
- if (RecordMapValuesEqual(pair.second->values_,
- dest_defaults->values_)) {
- // Values of the two defaults are equivalent, just ignore the
- // collision.
- continue;
- } else {
- // TODO(brettw) it would be nice to know the origin of a
- // set_target_defaults so we can give locations for the colliding
- // target defaults.
- std::string desc_string(desc_for_err);
- *err = Err(node_for_err, "Target defaults collision.",
- "This " + desc_string +
- " contains target defaults for\n"
- "\"" +
- current_name +
- "\" which would clobber one for the\n"
- "same target type in your current scope. It's "
- "unfortunate that "
- "I'm too stupid\nto tell you the location of where "
- "the target "
- "defaults were set. Usually\nthis happens in the "
- "BUILDCONFIG.gn "
- "file or in a related .gni file.\n");
- return false;
- }
- }
- }
-
- std::unique_ptr<Scope>& dest_scope = dest->target_defaults_[current_name];
- dest_scope = std::make_unique<Scope>(settings_);
- pair.second->NonRecursiveMergeTo(dest_scope.get(), options, node_for_err,
- "<SHOULDN'T HAPPEN>", err);
- }
-
- // Sources assignment filter.
- if (sources_assignment_filter_) {
- if (!options.clobber_existing) {
- if (dest->GetSourcesAssignmentFilter()) {
- // Sources assignment filter present in both the source and the dest.
- std::string desc_string(desc_for_err);
- *err = Err(node_for_err, "Assignment filter collision.",
- "The " + desc_string +
- " contains a sources_assignment_filter "
- "which\nwould clobber the one in your current scope.");
- return false;
- }
- }
- dest->sources_assignment_filter_ =
- std::make_unique<PatternList>(*sources_assignment_filter_);
- }
-
- // Templates.
- for (const auto& pair : templates_) {
- const std::string& current_name = pair.first;
- if (options.skip_private_vars && IsPrivateVar(current_name))
- continue; // Skip this private template.
- if (!options.excluded_values.empty() &&
- options.excluded_values.find(current_name) !=
- options.excluded_values.end()) {
- continue; // Skip the excluded value.
- }
-
- if (!options.clobber_existing) {
- const Template* existing_template = dest->GetTemplate(current_name);
- // Since templates are refcounted, we can check if it's the same one by
- // comparing pointers.
- if (existing_template && pair.second.get() != existing_template) {
- // Rule present in both the source and the dest, and they're not the
- // same one.
- std::string desc_string(desc_for_err);
- *err = Err(node_for_err, "Template collision.",
- "This " + desc_string + " contains a template \"" +
- current_name + "\"");
- err->AppendSubErr(
- Err(pair.second->GetDefinitionRange(), "defined here.",
- "Which would clobber the one in your current scope"));
- err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
- "defined here.",
- "Executing " + desc_string +
- " should not conflict with anything "
- "in the current\nscope."));
- return false;
- }
- }
-
- // Be careful to delete any pointer we're about to clobber.
- dest->templates_[current_name] = pair.second;
- }
-
- // Propogate build dependency files,
- dest->build_dependency_files_.insert(build_dependency_files_.begin(),
- build_dependency_files_.end());
-
- return true;
-}
-
-std::unique_ptr<Scope> Scope::MakeClosure() const {
- std::unique_ptr<Scope> result;
- if (const_containing_) {
- // We reached the top of the mutable scope stack. The result scope just
- // references the const scope (which will never change).
- result = std::make_unique<Scope>(const_containing_);
- } else if (mutable_containing_) {
- // There are more nested mutable scopes. Recursively go up the stack to
- // get the closure.
- result = mutable_containing_->MakeClosure();
- } else {
- // This is a standalone scope, just copy it.
- result = std::make_unique<Scope>(settings_);
- }
-
- // Want to clobber since we've flattened some nested scopes, and our parent
- // scope may have a duplicate value set.
- MergeOptions options;
- options.clobber_existing = true;
-
- // Add in our variables and we're done.
- Err err;
- NonRecursiveMergeTo(result.get(), options, nullptr, "<SHOULDN'T HAPPEN>",
- &err);
- DCHECK(!err.has_error());
- return result;
-}
-
-Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
- std::unique_ptr<Scope>& dest = target_defaults_[target_type];
- dest = std::make_unique<Scope>(settings_);
- return dest.get();
-}
-
-const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
- NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
- if (found != target_defaults_.end())
- return found->second.get();
- if (containing())
- return containing()->GetTargetDefaults(target_type);
- return nullptr;
-}
-
-const PatternList* Scope::GetSourcesAssignmentFilter() const {
- if (sources_assignment_filter_)
- return sources_assignment_filter_.get();
- if (containing())
- return containing()->GetSourcesAssignmentFilter();
- return nullptr;
-}
-
-void Scope::SetProcessingBuildConfig() {
- DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
- mode_flags_ |= kProcessingBuildConfigFlag;
-}
-
-void Scope::ClearProcessingBuildConfig() {
- DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
- mode_flags_ &= ~(kProcessingBuildConfigFlag);
-}
-
-bool Scope::IsProcessingBuildConfig() const {
- if (mode_flags_ & kProcessingBuildConfigFlag)
- return true;
- if (containing())
- return containing()->IsProcessingBuildConfig();
- return false;
-}
-
-void Scope::SetProcessingImport() {
- DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
- mode_flags_ |= kProcessingImportFlag;
-}
-
-void Scope::ClearProcessingImport() {
- DCHECK(mode_flags_ & kProcessingImportFlag);
- mode_flags_ &= ~(kProcessingImportFlag);
-}
-
-bool Scope::IsProcessingImport() const {
- if (mode_flags_ & kProcessingImportFlag)
- return true;
- if (containing())
- return containing()->IsProcessingImport();
- return false;
-}
-
-const SourceDir& Scope::GetSourceDir() const {
- if (!source_dir_.is_null())
- return source_dir_;
- if (containing())
- return containing()->GetSourceDir();
- return source_dir_;
-}
-
-void Scope::AddBuildDependencyFile(const SourceFile& build_dependency_file) {
- build_dependency_files_.insert(build_dependency_file);
-}
-
-Scope::ItemVector* Scope::GetItemCollector() {
- if (item_collector_)
- return item_collector_;
- if (mutable_containing())
- return mutable_containing()->GetItemCollector();
- return nullptr;
-}
-
-void Scope::SetProperty(const void* key, void* value) {
- if (!value) {
- DCHECK(properties_.find(key) != properties_.end());
- properties_.erase(key);
- } else {
- properties_[key] = value;
- }
-}
-
-void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
- PropertyMap::const_iterator found = properties_.find(key);
- if (found != properties_.end()) {
- if (found_on_scope)
- *found_on_scope = this;
- return found->second;
- }
- if (containing())
- return containing()->GetProperty(key, found_on_scope);
- return nullptr;
-}
-
-void Scope::AddProvider(ProgrammaticProvider* p) {
- programmatic_providers_.insert(p);
-}
-
-void Scope::RemoveProvider(ProgrammaticProvider* p) {
- DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
- programmatic_providers_.erase(p);
-}
-
-// static
-bool Scope::RecordMapValuesEqual(const RecordMap& a, const RecordMap& b) {
- if (a.size() != b.size())
- return false;
- for (const auto& pair : a) {
- const auto& found_b = b.find(pair.first);
- if (found_b == b.end())
- return false; // Item in 'a' but not 'b'.
- if (pair.second.value != found_b->second.value)
- return false; // Values for variable in 'a' and 'b' are different.
- }
- return true;
-}
diff --git a/gn/tools/gn/scope.h b/gn/tools/gn/scope.h
deleted file mode 100644
index 6c26cdbece2..00000000000
--- a/gn/tools/gn/scope.h
+++ /dev/null
@@ -1,397 +0,0 @@
-// 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 TOOLS_GN_SCOPE_H_
-#define TOOLS_GN_SCOPE_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "tools/gn/err.h"
-#include "tools/gn/pattern.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/value.h"
-
-class Item;
-class ParseNode;
-class Settings;
-class SourceFile;
-class Template;
-
-// Scope for the script execution.
-//
-// Scopes are nested. Writing goes into the toplevel scope, reading checks
-// values resursively down the stack until a match is found or there are no
-// more containing scopes.
-//
-// A containing scope can be const or non-const. The const containing scope is
-// used primarily to refer to the master build config which is shared across
-// many invocations. A const containing scope, however, prevents us from
-// marking variables "used" which prevents us from issuing errors on unused
-// variables. So you should use a non-const containing scope whenever possible.
-class Scope {
- public:
- using KeyValueMap = std::map<base::StringPiece, Value>;
- // Holds an owning list of Items.
- using ItemVector = std::vector<std::unique_ptr<Item>>;
-
- // A flag to indicate whether a function should recurse into nested scopes,
- // or only operate on the current scope.
- enum SearchNested { SEARCH_NESTED, SEARCH_CURRENT };
-
- // Allows code to provide values for built-in variables. This class will
- // automatically register itself on construction and deregister itself on
- // destruction.
- class ProgrammaticProvider {
- public:
- explicit ProgrammaticProvider(Scope* scope) : scope_(scope) {
- scope_->AddProvider(this);
- }
- virtual ~ProgrammaticProvider();
-
- // Returns a non-null value if the given value can be programmatically
- // generated, or NULL if there is none.
- virtual const Value* GetProgrammaticValue(
- const base::StringPiece& ident) = 0;
-
- protected:
- Scope* scope_;
- };
-
- // Options for configuring scope merges.
- struct MergeOptions {
- MergeOptions();
- ~MergeOptions();
-
- // When set, all existing avlues in the destination scope will be
- // overwritten.
- //
- // When false, it will be an error to merge a variable into another scope
- // where a variable with the same name is already set. The exception is
- // if both of the variables have the same value (which happens if you
- // somehow multiply import the same file, for example). This case will be
- // ignored since there is nothing getting lost.
- bool clobber_existing;
-
- // When true, private variables (names beginning with an underscore) will
- // be copied to the destination scope. When false, private values will be
- // skipped.
- bool skip_private_vars;
-
- // When set, values copied to the destination scope will be marked as used
- // so won't trigger an unused variable warning. You want this when doing an
- // import, for example, or files that don't need a variable from the .gni
- // file will throw an error.
- bool mark_dest_used;
-
- // When set, those variables are not merged.
- std::set<std::string> excluded_values;
- };
-
- // Creates an empty toplevel scope.
- explicit Scope(const Settings* settings);
-
- // Creates a dependent scope.
- explicit Scope(Scope* parent);
- explicit Scope(const Scope* parent);
-
- ~Scope();
-
- const Settings* settings() const { return settings_; }
-
- // See the const_/mutable_containing_ var declarations below. Yes, it's a
- // bit weird that we can have a const pointer to the "mutable" one.
- Scope* mutable_containing() { return mutable_containing_; }
- const Scope* mutable_containing() const { return mutable_containing_; }
- const Scope* const_containing() const { return const_containing_; }
- const Scope* containing() const {
- return mutable_containing_ ? mutable_containing_ : const_containing_;
- }
-
- // Clears any references to containing scopes. This scope will now be
- // self-sufficient.
- void DetachFromContaining();
-
- // Returns true if the scope has any values set. This does not check other
- // things that may be set like templates or defaults.
- //
- // Currently this does not search nested scopes and this will assert if you
- // want to search nested scopes. The enum is passed so the callers are
- // unambiguous about nested scope handling. This can be added if needed.
- bool HasValues(SearchNested search_nested) const;
-
- // Returns NULL if there's no such value.
- //
- // counts_as_used should be set if the variable is being read in a way that
- // should count for unused variable checking.
- //
- // found_in_scope is set to the scope that contains the definition of the
- // ident. If the value was provided programmatically (like host_cpu),
- // found_in_scope will be set to null.
- const Value* GetValue(const base::StringPiece& ident, bool counts_as_used);
- const Value* GetValue(const base::StringPiece& ident) const;
- const Value* GetValueWithScope(const base::StringPiece& ident,
- const Scope** found_in_scope) const;
- const Value* GetValueWithScope(const base::StringPiece& ident,
- bool counts_as_used,
- const Scope** found_in_scope);
-
- // Returns the requested value as a mutable one if possible. If the value
- // is not found in a mutable scope, then returns null. Note that the value
- // could still exist in a const scope, so GetValue() could still return
- // non-null in this case.
- //
- // Say you have a local scope that then refers to the const root scope from
- // the master build config. You can't change the values from the master
- // build config (it's read-only so it can be read from multiple threads
- // without locking). Read-only operations would work on values from the root
- // scope, but write operations would only work on values in the derived
- // scope(s).
- //
- // Be careful when calling this. It's not normally correct to modify values,
- // but you should instead do a new Set each time.
- //
- // Consider this code:
- // a = 5
- // {
- // a = 6
- // }
- // The 6 should get set on the nested scope rather than modify the value
- // in the outer one.
- Value* GetMutableValue(const base::StringPiece& ident,
- SearchNested search_mode,
- bool counts_as_used);
-
- // Returns the StringPiece used to identify the value. This string piece
- // will have the same contents as "ident" passed in, but may point to a
- // different underlying buffer. This is useful because this StringPiece is
- // static and won't be deleted for the life of the program, so it can be used
- // as keys in places that may outlive a temporary. It will return an empty
- // string for programmatic and nonexistant values.
- base::StringPiece GetStorageKey(const base::StringPiece& ident) const;
-
- // The set_node indicates the statement that caused the set, for displaying
- // errors later. Returns a pointer to the value in the current scope (a copy
- // is made for storage).
- Value* SetValue(const base::StringPiece& ident,
- Value v,
- const ParseNode* set_node);
-
- // Removes the value with the given identifier if it exists on the current
- // scope. This does not search recursive scopes. Does nothing if not found.
- void RemoveIdentifier(const base::StringPiece& ident);
-
- // Removes from this scope all identifiers and templates that are considered
- // private.
- void RemovePrivateIdentifiers();
-
- // Templates associated with this scope. A template can only be set once, so
- // AddTemplate will fail and return false if a rule with that name already
- // exists. GetTemplate returns NULL if the rule doesn't exist, and it will
- // check all containing scoped rescursively.
- bool AddTemplate(const std::string& name, const Template* templ);
- const Template* GetTemplate(const std::string& name) const;
-
- // Marks the given identifier as (un)used in the current scope.
- void MarkUsed(const base::StringPiece& ident);
- void MarkAllUsed();
- void MarkAllUsed(const std::set<std::string>& excluded_values);
- void MarkUnused(const base::StringPiece& ident);
-
- // Checks to see if the scope has a var set that hasn't been used. This is
- // called before replacing the var with a different one. It does not check
- // containing scopes.
- //
- // If the identifier is present but hasnn't been used, return true.
- bool IsSetButUnused(const base::StringPiece& ident) const;
-
- // Checks the scope to see if any values were set but not used, and fills in
- // the error and returns false if they were.
- bool CheckForUnusedVars(Err* err) const;
-
- // Returns all values set in the current scope, without going to the parent
- // scopes.
- void GetCurrentScopeValues(KeyValueMap* output) const;
-
- // Returns true if the values in the current scope are the same as all
- // values in the given scope, without going to the parent scopes. Returns
- // false if not.
- bool CheckCurrentScopeValuesEqual(const Scope* other) const;
-
- // Copies this scope's values into the destination. Values from the
- // containing scope(s) (normally shadowed into the current one) will not be
- // copied, neither will the reference to the containing scope (this is why
- // it's "non-recursive").
- //
- // This is used in different contexts. When generating the error, the given
- // parse node will be blamed, and the given desc will be used to describe
- // the operation that doesn't support doing this. For example, desc_for_err
- // would be "import" when doing an import, and the error string would say
- // something like "The import contains...".
- bool NonRecursiveMergeTo(Scope* dest,
- const MergeOptions& options,
- const ParseNode* node_for_err,
- const char* desc_for_err,
- Err* err) const;
-
- // Constructs a scope that is a copy of the current one. Nested scopes will
- // be collapsed until we reach a const containing scope. Private values will
- // be included. The resulting closure will reference the const containing
- // scope as its containing scope (since we assume the const scope won't
- // change, we don't have to copy its values).
- std::unique_ptr<Scope> MakeClosure() const;
-
- // Makes an empty scope with the given name. Overwrites any existing one.
- Scope* MakeTargetDefaults(const std::string& target_type);
-
- // Gets the scope associated with the given target name, or null if it hasn't
- // been set.
- const Scope* GetTargetDefaults(const std::string& target_type) const;
-
- // Filter to apply when the sources variable is assigned. May return NULL.
- const PatternList* GetSourcesAssignmentFilter() const;
- void set_sources_assignment_filter(std::unique_ptr<PatternList> f) {
- sources_assignment_filter_ = std::move(f);
- }
-
- // Indicates if we're currently processing the build configuration file.
- // This is true when processing the config file for any toolchain.
- //
- // To set or clear the flag, it must currently be in the opposite state in
- // the current scope. Note that querying the state of the flag recursively
- // checks all containing scopes until it reaches the top or finds the flag
- // set.
- void SetProcessingBuildConfig();
- void ClearProcessingBuildConfig();
- bool IsProcessingBuildConfig() const;
-
- // Indicates if we're currently processing an import file.
- //
- // See SetProcessingBaseConfig for how flags work.
- void SetProcessingImport();
- void ClearProcessingImport();
- bool IsProcessingImport() const;
-
- // The source directory associated with this scope. This will check embedded
- // scopes until it finds a nonempty source directory. This will default to
- // an empty dir if no containing scope has a source dir set.
- const SourceDir& GetSourceDir() const;
- void set_source_dir(const SourceDir& d) { source_dir_ = d; }
-
- // Set of files that may affect the execution of this scope. Note that this
- // set is constructed conservatively, meanining that every file that can
- // potentially affect this scope is included, but not necessarily every change
- // to these files will affect this scope.
- const std::set<SourceFile>& build_dependency_files() const {
- return build_dependency_files_;
- }
- void AddBuildDependencyFile(const SourceFile& build_dependency_file);
-
- // The item collector is where Items (Targets, Configs, etc.) go that have
- // been defined. If a scope can generate items, this non-owning pointer will
- // point to the storage for such items. The creator of this scope will be
- // responsible for setting up the collector and then dealing with the
- // collected items once execution of the context is complete.
- //
- // The items in a scope are collected as we go and then dispatched at the end
- // of execution of a scope so that we can query the previously-generated
- // targets (like getting the outputs).
- //
- // This can be null if the current scope can not generate items (like for
- // imports and such).
- //
- // When retrieving the collector, the non-const scopes are recursively
- // queried. The collector is not copied for closures, etc.
- void set_item_collector(ItemVector* collector) {
- item_collector_ = collector;
- }
- ItemVector* GetItemCollector();
-
- // Properties are opaque pointers that code can use to set state on a Scope
- // that it can retrieve later.
- //
- // The key should be a pointer to some use-case-specific object (to avoid
- // collisions, otherwise it doesn't matter). Memory management is up to the
- // setter. Setting the value to NULL will delete the property.
- //
- // Getting a property recursively searches all scopes, and the optional
- // |found_on_scope| variable will be filled with the actual scope containing
- // the key (if the pointer is non-NULL).
- void SetProperty(const void* key, void* value);
- void* GetProperty(const void* key, const Scope** found_on_scope) const;
-
- private:
- friend class ProgrammaticProvider;
-
- struct Record {
- Record() : used(false) {}
- explicit Record(const Value& v) : used(false), value(v) {}
-
- bool used; // Set to true when the variable is used.
- Value value;
- };
-
- typedef std::unordered_map<base::StringPiece, Record, base::StringPieceHash>
- RecordMap;
-
- void AddProvider(ProgrammaticProvider* p);
- void RemoveProvider(ProgrammaticProvider* p);
-
- // Returns true if the two RecordMaps contain the same values (the origins
- // of the values may be different).
- static bool RecordMapValuesEqual(const RecordMap& a, const RecordMap& b);
-
- // Scopes can have no containing scope (both null), a mutable containing
- // scope, or a const containing scope. The reason is that when we're doing
- // a new target, we want to refer to the base_config scope which will be read
- // by multiple threads at the same time, so we REALLY want it to be const.
- // When you just do a nested {}, however, we sometimes want to be able to
- // change things (especially marking unused vars).
- const Scope* const_containing_;
- Scope* mutable_containing_;
-
- const Settings* settings_;
-
- // Bits set for different modes. See the flag definitions in the .cc file
- // for more.
- unsigned mode_flags_;
-
- RecordMap values_;
-
- // Note that this can't use string pieces since the names are constructed from
- // Values which might be deallocated before this goes out of scope.
- typedef std::unordered_map<std::string, std::unique_ptr<Scope>> NamedScopeMap;
- NamedScopeMap target_defaults_;
-
- // Null indicates not set and that we should fallback to the containing
- // scope's filter.
- std::unique_ptr<PatternList> sources_assignment_filter_;
-
- // Owning pointers, must be deleted.
- typedef std::map<std::string, scoped_refptr<const Template>> TemplateMap;
- TemplateMap templates_;
-
- ItemVector* item_collector_;
-
- // Opaque pointers. See SetProperty() above.
- typedef std::map<const void*, void*> PropertyMap;
- PropertyMap properties_;
-
- typedef std::set<ProgrammaticProvider*> ProviderSet;
- ProviderSet programmatic_providers_;
-
- SourceDir source_dir_;
-
- std::set<SourceFile> build_dependency_files_;
-
- DISALLOW_COPY_AND_ASSIGN(Scope);
-};
-
-#endif // TOOLS_GN_SCOPE_H_
diff --git a/gn/tools/gn/scope_per_file_provider.cc b/gn/tools/gn/scope_per_file_provider.cc
deleted file mode 100644
index d7d11afc707..00000000000
--- a/gn/tools/gn/scope_per_file_provider.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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 "tools/gn/scope_per_file_provider.h"
-
-#include <memory>
-
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/value.h"
-#include "tools/gn/variables.h"
-
-ScopePerFileProvider::ScopePerFileProvider(Scope* scope, bool allow_target_vars)
- : ProgrammaticProvider(scope), allow_target_vars_(allow_target_vars) {}
-
-ScopePerFileProvider::~ScopePerFileProvider() = default;
-
-const Value* ScopePerFileProvider::GetProgrammaticValue(
- const base::StringPiece& ident) {
- if (ident == variables::kCurrentToolchain)
- return GetCurrentToolchain();
- if (ident == variables::kDefaultToolchain)
- return GetDefaultToolchain();
- if (ident == variables::kPythonPath)
- return GetPythonPath();
-
- if (ident == variables::kRootBuildDir)
- return GetRootBuildDir();
- if (ident == variables::kRootGenDir)
- return GetRootGenDir();
- if (ident == variables::kRootOutDir)
- return GetRootOutDir();
-
- if (allow_target_vars_) {
- if (ident == variables::kTargetGenDir)
- return GetTargetGenDir();
- if (ident == variables::kTargetOutDir)
- return GetTargetOutDir();
- }
- return nullptr;
-}
-
-const Value* ScopePerFileProvider::GetCurrentToolchain() {
- if (!current_toolchain_) {
- current_toolchain_ = std::make_unique<Value>(
- nullptr,
- scope_->settings()->toolchain_label().GetUserVisibleName(false));
- }
- return current_toolchain_.get();
-}
-
-const Value* ScopePerFileProvider::GetDefaultToolchain() {
- if (!default_toolchain_) {
- default_toolchain_ = std::make_unique<Value>(
- nullptr,
- scope_->settings()->default_toolchain_label().GetUserVisibleName(
- false));
- }
- return default_toolchain_.get();
-}
-
-const Value* ScopePerFileProvider::GetPythonPath() {
- if (!python_path_) {
- python_path_ = std::make_unique<Value>(
- nullptr,
- FilePathToUTF8(scope_->settings()->build_settings()->python_path()));
- }
- return python_path_.get();
-}
-
-const Value* ScopePerFileProvider::GetRootBuildDir() {
- if (!root_build_dir_) {
- root_build_dir_ = std::make_unique<Value>(
- nullptr, DirectoryWithNoLastSlash(
- scope_->settings()->build_settings()->build_dir()));
- }
- return root_build_dir_.get();
-}
-
-const Value* ScopePerFileProvider::GetRootGenDir() {
- if (!root_gen_dir_) {
- root_gen_dir_ = std::make_unique<Value>(
- nullptr, DirectoryWithNoLastSlash(GetBuildDirAsSourceDir(
- BuildDirContext(scope_), BuildDirType::GEN)));
- }
- return root_gen_dir_.get();
-}
-
-const Value* ScopePerFileProvider::GetRootOutDir() {
- if (!root_out_dir_) {
- root_out_dir_ = std::make_unique<Value>(
- nullptr, DirectoryWithNoLastSlash(GetScopeCurrentBuildDirAsSourceDir(
- scope_, BuildDirType::TOOLCHAIN_ROOT)));
- }
- return root_out_dir_.get();
-}
-
-const Value* ScopePerFileProvider::GetTargetGenDir() {
- if (!target_gen_dir_) {
- target_gen_dir_ = std::make_unique<Value>(
- nullptr, DirectoryWithNoLastSlash(GetScopeCurrentBuildDirAsSourceDir(
- scope_, BuildDirType::GEN)));
- }
- return target_gen_dir_.get();
-}
-
-const Value* ScopePerFileProvider::GetTargetOutDir() {
- if (!target_out_dir_) {
- target_out_dir_ = std::make_unique<Value>(
- nullptr, DirectoryWithNoLastSlash(GetScopeCurrentBuildDirAsSourceDir(
- scope_, BuildDirType::OBJ)));
- }
- return target_out_dir_.get();
-}
diff --git a/gn/tools/gn/scope_per_file_provider.h b/gn/tools/gn/scope_per_file_provider.h
deleted file mode 100644
index ac0d8720615..00000000000
--- a/gn/tools/gn/scope_per_file_provider.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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 TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
-#define TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "tools/gn/scope.h"
-
-// ProgrammaticProvider for a scope to provide it with per-file built-in
-// variable support.
-class ScopePerFileProvider : public Scope::ProgrammaticProvider {
- public:
- // allow_target_vars allows the target-related variables to get resolved.
- // When allow_target_vars is unset, the target-related values will be
- // undefined to GN script.
- ScopePerFileProvider(Scope* scope, bool allow_target_vars);
- ~ScopePerFileProvider() override;
-
- // ProgrammaticProvider implementation.
- const Value* GetProgrammaticValue(const base::StringPiece& ident) override;
-
- private:
- const Value* GetCurrentToolchain();
- const Value* GetDefaultToolchain();
- const Value* GetPythonPath();
- const Value* GetRootBuildDir();
- const Value* GetRootGenDir();
- const Value* GetRootOutDir();
- const Value* GetTargetGenDir();
- const Value* GetTargetOutDir();
-
- bool allow_target_vars_;
-
- // All values are lazily created.
- std::unique_ptr<Value> current_toolchain_;
- std::unique_ptr<Value> default_toolchain_;
- std::unique_ptr<Value> python_path_;
- std::unique_ptr<Value> root_build_dir_;
- std::unique_ptr<Value> root_gen_dir_;
- std::unique_ptr<Value> root_out_dir_;
- std::unique_ptr<Value> target_gen_dir_;
- std::unique_ptr<Value> target_out_dir_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopePerFileProvider);
-};
-
-#endif // TOOLS_GN_SCOPE_PER_FILE_PROVIDER_H_
diff --git a/gn/tools/gn/scope_per_file_provider_unittest.cc b/gn/tools/gn/scope_per_file_provider_unittest.cc
deleted file mode 100644
index 1a6ef1a3e56..00000000000
--- a/gn/tools/gn/scope_per_file_provider_unittest.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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 "tools/gn/scope_per_file_provider.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/variables.h"
-#include "util/test/test.h"
-
-TEST(ScopePerFileProvider, Expected) {
- TestWithScope test;
-
-// Prevent horrible wrapping of calls below.
-#define GPV(val) provider.GetProgrammaticValue(val)->string_value()
-
- // Test the default toolchain.
- {
- Scope scope(test.settings());
- scope.set_source_dir(SourceDir("//source/"));
- ScopePerFileProvider provider(&scope, true);
-
- EXPECT_EQ("//toolchain:default", GPV(variables::kCurrentToolchain));
- // TODO(brettw) this test harness does not set up the Toolchain manager
- // which is the source of this value, so we can't test this yet.
- // EXPECT_EQ("//toolchain:default", GPV(variables::kDefaultToolchain));
- EXPECT_EQ("//out/Debug", GPV(variables::kRootBuildDir));
- EXPECT_EQ("//out/Debug/gen", GPV(variables::kRootGenDir));
- EXPECT_EQ("//out/Debug", GPV(variables::kRootOutDir));
- EXPECT_EQ("//out/Debug/gen/source", GPV(variables::kTargetGenDir));
- EXPECT_EQ("//out/Debug/obj/source", GPV(variables::kTargetOutDir));
- }
-
- // Test some with an alternate toolchain.
- {
- Settings settings(test.build_settings(), "tc/");
- Toolchain toolchain(&settings, Label(SourceDir("//toolchain/"), "tc"));
- settings.set_toolchain_label(toolchain.label());
-
- Scope scope(&settings);
- scope.set_source_dir(SourceDir("//source/"));
- ScopePerFileProvider provider(&scope, true);
-
- EXPECT_EQ("//toolchain:tc", GPV(variables::kCurrentToolchain));
- // See above.
- // EXPECT_EQ("//toolchain:default", GPV(variables::kDefaultToolchain));
- EXPECT_EQ("//out/Debug", GPV(variables::kRootBuildDir));
- EXPECT_EQ("//out/Debug/tc/gen", GPV(variables::kRootGenDir));
- EXPECT_EQ("//out/Debug/tc", GPV(variables::kRootOutDir));
- EXPECT_EQ("//out/Debug/tc/gen/source", GPV(variables::kTargetGenDir));
- EXPECT_EQ("//out/Debug/tc/obj/source", GPV(variables::kTargetOutDir));
- }
-}
diff --git a/gn/tools/gn/scope_unittest.cc b/gn/tools/gn/scope_unittest.cc
deleted file mode 100644
index 681d6c88ca5..00000000000
--- a/gn/tools/gn/scope_unittest.cc
+++ /dev/null
@@ -1,335 +0,0 @@
-// 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 "tools/gn/scope.h"
-
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/template.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-namespace {
-
-bool HasStringValueEqualTo(const Scope* scope,
- const char* name,
- const char* expected_value) {
- const Value* value = scope->GetValue(name);
- if (!value)
- return false;
- if (value->type() != Value::STRING)
- return false;
- return value->string_value() == expected_value;
-}
-
-bool ContainsBuildDependencyFile(const Scope* scope,
- const SourceFile& source_file) {
- const auto& build_dependency_files = scope->build_dependency_files();
- return build_dependency_files.end() !=
- build_dependency_files.find(source_file);
-}
-
-} // namespace
-
-TEST(Scope, InheritBuildDependencyFilesFromParent) {
- TestWithScope setup;
- SourceFile source_file = SourceFile("//a/BUILD.gn");
- setup.scope()->AddBuildDependencyFile(source_file);
-
- Scope new_scope(setup.scope());
- EXPECT_EQ(1U, new_scope.build_dependency_files().size());
- EXPECT_TRUE(ContainsBuildDependencyFile(&new_scope, source_file));
-}
-
-TEST(Scope, NonRecursiveMergeTo) {
- TestWithScope setup;
-
- // Make a pretend parse node with proper tracking that we can blame for the
- // given value.
- InputFile input_file(SourceFile("//foo"));
- Token assignment_token(Location(&input_file, 1, 1, 1), Token::STRING,
- "\"hello\"");
- LiteralNode assignment;
- assignment.set_value(assignment_token);
-
- // Add some values to the scope.
- Value old_value(&assignment, "hello");
- setup.scope()->SetValue("v", old_value, &assignment);
- base::StringPiece private_var_name("_private");
- setup.scope()->SetValue(private_var_name, old_value, &assignment);
-
- // Add some templates to the scope.
- FunctionCallNode templ_definition;
- scoped_refptr<Template> templ(new Template(setup.scope(), &templ_definition));
- setup.scope()->AddTemplate("templ", templ.get());
- scoped_refptr<Template> private_templ(
- new Template(setup.scope(), &templ_definition));
- setup.scope()->AddTemplate("_templ", private_templ.get());
-
- // Detect collisions of values' values.
- {
- Scope new_scope(setup.settings());
- Value new_value(&assignment, "goodbye");
- new_scope.SetValue("v", new_value, &assignment);
-
- Err err;
- EXPECT_FALSE(setup.scope()->NonRecursiveMergeTo(
- &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
- EXPECT_TRUE(err.has_error());
- }
-
- // Template name collisions.
- {
- Scope new_scope(setup.settings());
-
- scoped_refptr<Template> new_templ(
- new Template(&new_scope, &templ_definition));
- new_scope.AddTemplate("templ", new_templ.get());
-
- Err err;
- EXPECT_FALSE(setup.scope()->NonRecursiveMergeTo(
- &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
- EXPECT_TRUE(err.has_error());
- }
-
- // The clobber flag should just overwrite colliding values.
- {
- Scope new_scope(setup.settings());
- Value new_value(&assignment, "goodbye");
- new_scope.SetValue("v", new_value, &assignment);
-
- Err err;
- Scope::MergeOptions options;
- options.clobber_existing = true;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
- &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
-
- const Value* found_value = new_scope.GetValue("v");
- ASSERT_TRUE(found_value);
- EXPECT_TRUE(old_value == *found_value);
- }
-
- // Clobber flag for templates.
- {
- Scope new_scope(setup.settings());
-
- scoped_refptr<Template> new_templ(
- new Template(&new_scope, &templ_definition));
- new_scope.AddTemplate("templ", new_templ.get());
- Scope::MergeOptions options;
- options.clobber_existing = true;
-
- Err err;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
- &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
-
- const Template* found_value = new_scope.GetTemplate("templ");
- ASSERT_TRUE(found_value);
- EXPECT_TRUE(templ.get() == found_value);
- }
-
- // Don't flag values that technically collide but have the same value.
- {
- Scope new_scope(setup.settings());
- Value new_value(&assignment, "hello");
- new_scope.SetValue("v", new_value, &assignment);
-
- Err err;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
- &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
- }
-
- // Templates that technically collide but are the same.
- {
- Scope new_scope(setup.settings());
-
- scoped_refptr<Template> new_templ(
- new Template(&new_scope, &templ_definition));
- new_scope.AddTemplate("templ", templ.get());
-
- Err err;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
- &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
- }
-
- // Copy private values and templates.
- {
- Scope new_scope(setup.settings());
-
- Err err;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
- &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(new_scope.GetValue(private_var_name));
- EXPECT_TRUE(new_scope.GetTemplate("_templ"));
- }
-
- // Skip private values and templates.
- {
- Scope new_scope(setup.settings());
-
- Err err;
- Scope::MergeOptions options;
- options.skip_private_vars = true;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
- &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_FALSE(new_scope.GetValue(private_var_name));
- EXPECT_FALSE(new_scope.GetTemplate("_templ"));
- }
-
- // Don't mark used.
- {
- Scope new_scope(setup.settings());
-
- Err err;
- Scope::MergeOptions options;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
- &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_FALSE(new_scope.CheckForUnusedVars(&err));
- EXPECT_TRUE(err.has_error());
- }
-
- // Mark dest used.
- {
- Scope new_scope(setup.settings());
-
- Err err;
- Scope::MergeOptions options;
- options.mark_dest_used = true;
- EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
- &assignment, "error", &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(new_scope.CheckForUnusedVars(&err));
- EXPECT_FALSE(err.has_error());
- }
-
- // Build dependency files are merged.
- {
- Scope from_scope(setup.settings());
- SourceFile source_file = SourceFile("//a/BUILD.gn");
- from_scope.AddBuildDependencyFile(source_file);
-
- Scope to_scope(setup.settings());
- EXPECT_FALSE(ContainsBuildDependencyFile(&to_scope, source_file));
-
- Scope::MergeOptions options;
- Err err;
- EXPECT_TRUE(from_scope.NonRecursiveMergeTo(&to_scope, options, &assignment,
- "error", &err));
- EXPECT_FALSE(err.has_error());
- EXPECT_EQ(1U, to_scope.build_dependency_files().size());
- EXPECT_TRUE(ContainsBuildDependencyFile(&to_scope, source_file));
- }
-}
-
-TEST(Scope, MakeClosure) {
- // Create 3 nested scopes [const root from setup] <- nested1 <- nested2.
- TestWithScope setup;
-
- // Make a pretend parse node with proper tracking that we can blame for the
- // given value.
- InputFile input_file(SourceFile("//foo"));
- Token assignment_token(Location(&input_file, 1, 1, 1), Token::STRING,
- "\"hello\"");
- LiteralNode assignment;
- assignment.set_value(assignment_token);
- setup.scope()->SetValue("on_root", Value(&assignment, "on_root"),
- &assignment);
-
- // Root scope should be const from the nested caller's perspective.
- Scope nested1(static_cast<const Scope*>(setup.scope()));
- nested1.SetValue("on_one", Value(&assignment, "on_one"), &assignment);
-
- Scope nested2(&nested1);
- nested2.SetValue("on_one", Value(&assignment, "on_two"), &assignment);
- nested2.SetValue("on_two", Value(&assignment, "on_two2"), &assignment);
-
- // Making a closure from the root scope.
- std::unique_ptr<Scope> result = setup.scope()->MakeClosure();
- EXPECT_FALSE(result->containing()); // Should have no containing scope.
- EXPECT_TRUE(result->GetValue("on_root")); // Value should be copied.
-
- // Making a closure from the second nested scope.
- result = nested2.MakeClosure();
- EXPECT_EQ(setup.scope(),
- result->containing()); // Containing scope should be the root.
- EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_root", "on_root"));
- EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_one", "on_two"));
- EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_two", "on_two2"));
-}
-
-TEST(Scope, GetMutableValue) {
- TestWithScope setup;
-
- // Make a pretend parse node with proper tracking that we can blame for the
- // given value.
- InputFile input_file(SourceFile("//foo"));
- Token assignment_token(Location(&input_file, 1, 1, 1), Token::STRING,
- "\"hello\"");
- LiteralNode assignment;
- assignment.set_value(assignment_token);
-
- const char kOnConst[] = "on_const";
- const char kOnMutable1[] = "on_mutable1";
- const char kOnMutable2[] = "on_mutable2";
-
- Value value(&assignment, "hello");
-
- // Create a root scope with one value.
- Scope root_scope(setup.settings());
- root_scope.SetValue(kOnConst, value, &assignment);
-
- // Create a first nested scope with a different value.
- const Scope* const_root_scope = &root_scope;
- Scope mutable_scope1(const_root_scope);
- mutable_scope1.SetValue(kOnMutable1, value, &assignment);
-
- // Create a second nested scope with a different value.
- Scope mutable_scope2(&mutable_scope1);
- mutable_scope2.SetValue(kOnMutable2, value, &assignment);
-
- // Check getting root scope values.
- EXPECT_TRUE(mutable_scope2.GetValue(kOnConst, true));
- EXPECT_FALSE(
- mutable_scope2.GetMutableValue(kOnConst, Scope::SEARCH_NESTED, true));
-
- // Test reading a value from scope 1.
- Value* mutable1_result =
- mutable_scope2.GetMutableValue(kOnMutable1, Scope::SEARCH_NESTED, false);
- ASSERT_TRUE(mutable1_result);
- EXPECT_TRUE(*mutable1_result == value);
-
- // Make sure CheckForUnusedVars works on scope1 (we didn't mark the value as
- // used in the previous step).
- Err err;
- EXPECT_FALSE(mutable_scope1.CheckForUnusedVars(&err));
- mutable1_result =
- mutable_scope2.GetMutableValue(kOnMutable1, Scope::SEARCH_NESTED, true);
- EXPECT_TRUE(mutable1_result);
- err = Err();
- EXPECT_TRUE(mutable_scope1.CheckForUnusedVars(&err));
-
- // Test reading a value from scope 2.
- Value* mutable2_result =
- mutable_scope2.GetMutableValue(kOnMutable2, Scope::SEARCH_NESTED, true);
- ASSERT_TRUE(mutable2_result);
- EXPECT_TRUE(*mutable2_result == value);
-}
-
-TEST(Scope, RemovePrivateIdentifiers) {
- TestWithScope setup;
- setup.scope()->SetValue("a", Value(nullptr, true), nullptr);
- setup.scope()->SetValue("_b", Value(nullptr, true), nullptr);
-
- setup.scope()->RemovePrivateIdentifiers();
- EXPECT_TRUE(setup.scope()->GetValue("a"));
- EXPECT_FALSE(setup.scope()->GetValue("_b"));
-}
diff --git a/gn/tools/gn/settings.cc b/gn/tools/gn/settings.cc
deleted file mode 100644
index c630fa72e0e..00000000000
--- a/gn/tools/gn/settings.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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 "tools/gn/settings.h"
-
-#include "base/logging.h"
-#include "tools/gn/filesystem_utils.h"
-#include "util/build_config.h"
-
-Settings::Settings(const BuildSettings* build_settings,
- const std::string& output_subdir_name)
- : build_settings_(build_settings), base_config_(this) {
- if (output_subdir_name.empty()) {
- toolchain_output_dir_ = build_settings->build_dir();
- } else {
- // We guarantee this ends in a slash.
- DCHECK(output_subdir_name[output_subdir_name.size() - 1] == '/');
- toolchain_output_subdir_.value().append(output_subdir_name);
-
- DCHECK(!build_settings->build_dir().is_null());
- toolchain_output_dir_ = SourceDir(build_settings->build_dir().value() +
- toolchain_output_subdir_.value());
- }
- // The output dir will be null in some tests and when invoked to parsed
- // one-off data without doing generation.
- if (!toolchain_output_dir_.is_null())
- toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/");
-}
diff --git a/gn/tools/gn/settings.h b/gn/tools/gn/settings.h
deleted file mode 100644
index eb9952f68f6..00000000000
--- a/gn/tools/gn/settings.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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 TOOLS_GN_SETTINGS_H_
-#define TOOLS_GN_SETTINGS_H_
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/import_manager.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/toolchain.h"
-
-// Holds the settings for one toolchain invocation. There will be one
-// Settings object for each toolchain type, each referring to the same
-// BuildSettings object for shared stuff.
-//
-// The Settings object is const once it is constructed, which allows us to
-// use it from multiple threads during target generation without locking (which
-// is important, because it gets used a lot).
-//
-// The Toolchain object holds the set of stuff that is set by the toolchain
-// declaration, which obviously needs to be set later when we actually parse
-// the file with the toolchain declaration in it.
-class Settings {
- public:
- // Constructs a toolchain settings.
- //
- // The output_subdir_name is the name we should use for the subdirectory in
- // the build output directory for this toolchain's outputs. The default
- // toolchain would use an empty string (it goes in the root build dir).
- // Otherwise, it must end in a slash.
- Settings(const BuildSettings* build_settings,
- const std::string& output_subdir_name);
-
- const BuildSettings* build_settings() const { return build_settings_; }
-
- // The actual Toolchain object pointer is not available on the settings
- // object because it might not be resolved yet. Code running after the
- // load is complete can ask the Builder for the Toolchain corresponding to
- // this label.
- const Label& toolchain_label() const { return toolchain_label_; }
- void set_toolchain_label(const Label& l) { toolchain_label_ = l; }
-
- const Label& default_toolchain_label() const {
- return default_toolchain_label_;
- }
- void set_default_toolchain_label(const Label& default_label) {
- default_toolchain_label_ = default_label;
- }
-
- // Indicates if this corresponds to the default toolchain.
- bool is_default() const {
- return toolchain_label_ == default_toolchain_label_;
- }
-
- const OutputFile& toolchain_output_subdir() const {
- return toolchain_output_subdir_;
- }
- const SourceDir& toolchain_output_dir() const {
- return toolchain_output_dir_;
- }
-
- // Directory for generated files.
- const SourceDir& toolchain_gen_dir() const { return toolchain_gen_dir_; }
-
- // The import manager caches the result of executing imported files in the
- // context of a given settings object.
- //
- // See the ItemTree getter in GlobalSettings for why this doesn't return a
- // const pointer.
- ImportManager& import_manager() const { return import_manager_; }
-
- const Scope* base_config() const { return &base_config_; }
- Scope* base_config() { return &base_config_; }
-
- // Set to true when every target we encounter should be generated. False
- // means that only targets that have a dependency from (directly or
- // indirectly) some magic root node are actually generated. See the comments
- // on ItemTree for more.
- bool greedy_target_generation() const { return greedy_target_generation_; }
- void set_greedy_target_generation(bool gtg) {
- greedy_target_generation_ = gtg;
- }
-
- private:
- const BuildSettings* build_settings_;
-
- Label toolchain_label_;
- Label default_toolchain_label_;
-
- mutable ImportManager import_manager_;
-
- // The subdirectory inside the build output for this toolchain. For the
- // default toolchain, this will be empty (since the deafult toolchain's
- // output directory is the same as the build directory). When nonempty, this
- // is guaranteed to end in a slash.
- OutputFile toolchain_output_subdir_;
-
- // Full source file path to the toolchain output directory.
- SourceDir toolchain_output_dir_;
-
- SourceDir toolchain_gen_dir_;
-
- Scope base_config_;
-
- bool greedy_target_generation_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(Settings);
-};
-
-#endif // TOOLS_GN_SETTINGS_H_
diff --git a/gn/tools/gn/setup.cc b/gn/tools/gn/setup.cc
deleted file mode 100644
index ef264317426..00000000000
--- a/gn/tools/gn/setup.cc
+++ /dev/null
@@ -1,862 +0,0 @@
-// 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 "tools/gn/setup.h"
-
-#include <stdlib.h>
-
-#include <algorithm>
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/command_format.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/exec_process.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/tokenizer.h"
-#include "tools/gn/trace.h"
-#include "tools/gn/value.h"
-#include "tools/gn/value_extractors.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-
-#include "base/win/scoped_process_information.h"
-#endif
-
-const char kDotfile_Help[] =
- R"(.gn file
-
- When gn starts, it will search the current directory and parent directories
- for a file called ".gn". This indicates the source root. You can override
- this detection by using the --root command-line argument
-
- The .gn file in the source root will be executed. The syntax is the same as a
- buildfile, but with very limited build setup-specific meaning.
-
- If you specify --root, by default GN will look for the file .gn in that
- directory. If you want to specify a different file, you can additionally pass
- --dotfile:
-
- gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn
-
-Variables
-
- arg_file_template [optional]
- Path to a file containing the text that should be used as the default
- args.gn content when you run `gn args`.
-
- buildconfig [required]
- Path to the build config file. This file will be used to set up the
- build file execution environment for each toolchain.
-
- check_targets [optional]
- A list of labels and label patterns that should be checked when running
- "gn check" or "gn gen --check". If unspecified, all targets will be
- checked. If it is the empty list, no targets will be checked. To
- bypass this list, request an explicit check of targets, like "//*".
-
- The format of this list is identical to that of "visibility" so see "gn
- help visibility" for examples.
-
- exec_script_whitelist [optional]
- A list of .gn/.gni files (not labels) that have permission to call the
- exec_script function. If this list is defined, calls to exec_script will
- be checked against this list and GN will fail if the current file isn't
- in the list.
-
- This is to allow the use of exec_script to be restricted since is easy to
- use inappropriately. Wildcards are not supported. Files in the
- secondary_source tree (if defined) should be referenced by ignoring the
- secondary tree and naming them as if they are in the main tree.
-
- If unspecified, the ability to call exec_script is unrestricted.
-
- Example:
- exec_script_whitelist = [
- "//base/BUILD.gn",
- "//build/my_config.gni",
- ]
-
- root [optional]
- Label of the root build target. The GN build will start by loading the
- build file containing this target name. This defaults to "//:" which will
- cause the file //BUILD.gn to be loaded.
-
- script_executable [optional]
- Path to specific Python executable or potentially a different language
- interpreter that is used to execute scripts in action targets and
- exec_script calls.
-
- secondary_source [optional]
- Label of an alternate directory tree to find input files. When searching
- for a BUILD.gn file (or the build config file discussed above), the file
- will first be looked for in the source root. If it's not found, the
- secondary source root will be checked (which would contain a parallel
- directory hierarchy).
-
- This behavior is intended to be used when BUILD.gn files can't be checked
- in to certain source directories for whatever reason.
-
- The secondary source root must be inside the main source tree.
-
- default_args [optional]
- Scope containing the default overrides for declared arguments. These
- overrides take precedence over the default values specified in the
- declare_args() block, but can be overriden using --args or the
- args.gn file.
-
- This is intended to be used when subprojects declare arguments with
- default values that need to be changed for whatever reason.
-
-Example .gn file contents
-
- buildconfig = "//build/config/BUILDCONFIG.gn"
-
- check_targets = [
- "//doom_melon/*", # Check everything in this subtree.
- "//tools:mind_controlling_ant", # Check this specific target.
- ]
-
- root = "//:root"
-
- secondary_source = "//build/config/temporary_buildfiles/"
-
- default_args = {
- # Default to release builds for this project.
- is_debug = false
- is_component_build = false
- }
-)";
-
-namespace {
-
-const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
-const char kDefaultArgsGn[] = "# Set build arguments here. See `gn buildargs`.";
-
-base::FilePath FindDotFile(const base::FilePath& current_dir) {
- base::FilePath try_this_file = current_dir.Append(kGnFile);
- if (base::PathExists(try_this_file))
- return try_this_file;
-
- base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
- base::FilePath up_one_dir = with_no_slash.DirName();
- if (up_one_dir == current_dir)
- return base::FilePath(); // Got to the top.
-
- return FindDotFile(up_one_dir);
-}
-
-// Called on any thread. Post the item to the builder on the main thread.
-void ItemDefinedCallback(MsgLoop* task_runner,
- Builder* builder_call_on_main_thread_only,
- std::unique_ptr<Item> item) {
- DCHECK(item);
-
- // Increment the work count for the duration of defining the item with the
- // builder. Otherwise finishing this callback will race finishing loading
- // files. If there is no other pending work at any point in the middle of
- // this call completing on the main thread, the 'Complete' function will
- // be signaled and we'll stop running with an incomplete build.
- g_scheduler->IncrementWorkCount();
- task_runner->PostTask(base::BindOnce(
- [](Builder* builder_call_on_main_thread_only,
- std::unique_ptr<Item> item) {
- builder_call_on_main_thread_only->ItemDefined(std::move(item));
- g_scheduler->DecrementWorkCount();
- },
- builder_call_on_main_thread_only, base::Passed(&item)));
-}
-
-void DecrementWorkCount() {
- g_scheduler->DecrementWorkCount();
-}
-
-#if defined(OS_WIN)
-
-std::wstring SysMultiByteToWide(base::StringPiece mb) {
- if (mb.empty())
- return std::wstring();
-
- int mb_length = static_cast<int>(mb.length());
- // Compute the length of the buffer.
- int charcount = MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
- if (charcount == 0)
- return std::wstring();
-
- std::wstring wide;
- wide.resize(charcount);
- MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, &wide[0], charcount);
-
- return wide;
-}
-
-// Given the path to a batch file that runs Python, extracts the name of the
-// executable actually implementing Python. Generally people write a batch file
-// to put something named "python" on the path, which then just redirects to
-// a python.exe somewhere else. This step decodes that setup. On failure,
-// returns empty path.
-base::FilePath PythonBatToExe(const base::FilePath& bat_path) {
- // Note exciting double-quoting to allow spaces. The /c switch seems to check
- // for quotes around the whole thing and then deletes them. If you want to
- // quote the first argument in addition (to allow for spaces in the Python
- // path, you need *another* set of quotes around that, likewise, we need
- // two quotes at the end.
- base::string16 command = L"cmd.exe /c \"\"";
- command.append(bat_path.value());
- command.append(L"\" -c \"import sys; print sys.executable\"\"");
-
- std::string python_path;
- std::string std_err;
- int exit_code;
- base::FilePath cwd;
- GetCurrentDirectory(&cwd);
- if (internal::ExecProcess(command, cwd, &python_path, &std_err, &exit_code) &&
- exit_code == 0 && std_err.empty()) {
- base::TrimWhitespaceASCII(python_path, base::TRIM_ALL, &python_path);
-
- // Python uses the system multibyte code page for sys.executable.
- base::FilePath exe_path(SysMultiByteToWide(python_path));
-
- // Check for reasonable output, cmd may have output an error message.
- if (base::PathExists(exe_path))
- return exe_path;
- }
- return base::FilePath();
-}
-
-const base::char16 kPythonExeName[] = L"python.exe";
-const base::char16 kPythonBatName[] = L"python.bat";
-
-base::FilePath FindWindowsPython() {
- base::char16 current_directory[MAX_PATH];
- ::GetCurrentDirectory(MAX_PATH, current_directory);
-
- // First search for python.exe in the current directory.
- base::FilePath cur_dir_candidate_exe =
- base::FilePath(current_directory).Append(kPythonExeName);
- if (base::PathExists(cur_dir_candidate_exe))
- return cur_dir_candidate_exe;
-
- // Get the path.
- const base::char16 kPathEnvVarName[] = L"Path";
- DWORD path_length = ::GetEnvironmentVariable(kPathEnvVarName, nullptr, 0);
- if (path_length == 0)
- return base::FilePath();
- std::unique_ptr<base::char16[]> full_path(new base::char16[path_length]);
- DWORD actual_path_length =
- ::GetEnvironmentVariable(kPathEnvVarName, full_path.get(), path_length);
- CHECK_EQ(path_length, actual_path_length + 1);
-
- // Search for python.exe in the path.
- for (const auto& component : base::SplitStringPiece(
- base::StringPiece16(full_path.get(), path_length), L";",
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
- base::FilePath candidate_exe =
- base::FilePath(component).Append(kPythonExeName);
- if (base::PathExists(candidate_exe))
- return candidate_exe;
-
- // Also allow python.bat, but convert into the .exe.
- base::FilePath candidate_bat =
- base::FilePath(component).Append(kPythonBatName);
- if (base::PathExists(candidate_bat)) {
- base::FilePath python_exe = PythonBatToExe(candidate_bat);
- if (!python_exe.empty())
- return python_exe;
- }
- }
- return base::FilePath();
-}
-#endif
-
-} // namespace
-
-const char Setup::kBuildArgFileName[] = "args.gn";
-
-Setup::Setup()
- : build_settings_(),
- loader_(new LoaderImpl(&build_settings_)),
- builder_(loader_.get()),
- root_build_file_("//BUILD.gn"),
- dotfile_settings_(&build_settings_, std::string()),
- dotfile_scope_(&dotfile_settings_) {
- dotfile_settings_.set_toolchain_label(Label());
-
- build_settings_.set_item_defined_callback(
- base::Bind(&ItemDefinedCallback, scheduler_.task_runner(), &builder_));
-
- loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
- // The scheduler's task runner wasn't created when the Loader was created, so
- // we need to set it now.
- loader_->set_task_runner(scheduler_.task_runner());
-}
-
-bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
- return DoSetup(build_dir, force_create,
- *base::CommandLine::ForCurrentProcess());
-}
-
-bool Setup::DoSetup(const std::string& build_dir,
- bool force_create,
- const base::CommandLine& cmdline) {
- scheduler_.set_verbose_logging(cmdline.HasSwitch(switches::kVerbose));
- if (cmdline.HasSwitch(switches::kTime) ||
- cmdline.HasSwitch(switches::kTracelog))
- EnableTracing();
-
- ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
-
- if (!FillSourceDir(cmdline))
- return false;
- if (!RunConfigFile())
- return false;
- if (!FillOtherConfig(cmdline))
- return false;
-
- // Must be after FillSourceDir to resolve.
- if (!FillBuildDir(build_dir, !force_create))
- return false;
-
- // Apply project-specific default (if specified).
- // Must happen before FillArguments().
- if (default_args_) {
- Scope::KeyValueMap overrides;
- default_args_->GetCurrentScopeValues(&overrides);
- build_settings_.build_args().AddDefaultArgOverrides(overrides);
- }
-
- if (fill_arguments_) {
- if (!FillArguments(cmdline))
- return false;
- }
- if (!FillPythonPath(cmdline))
- return false;
-
- // Check for unused variables in the .gn file.
- Err err;
- if (!dotfile_scope_.CheckForUnusedVars(&err)) {
- err.PrintToStdout();
- return false;
- }
-
- return true;
-}
-
-bool Setup::Run() {
- return Run(*base::CommandLine::ForCurrentProcess());
-}
-
-bool Setup::Run(const base::CommandLine& cmdline) {
- RunPreMessageLoop();
- if (!scheduler_.Run())
- return false;
- return RunPostMessageLoop(cmdline);
-}
-
-SourceFile Setup::GetBuildArgFile() const {
- return SourceFile(build_settings_.build_dir().value() + kBuildArgFileName);
-}
-
-void Setup::RunPreMessageLoop() {
- // Will be decremented with the loader is drained.
- g_scheduler->IncrementWorkCount();
-
- // Load the root build file.
- loader_->Load(root_build_file_, LocationRange(), Label());
-}
-
-bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
- Err err;
- if (!builder_.CheckForBadItems(&err)) {
- err.PrintToStdout();
- return false;
- }
-
- if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
- if (cmdline.HasSwitch(switches::kFailOnUnusedArgs)) {
- err.PrintToStdout();
- return false;
- }
- err.PrintNonfatalToStdout();
- OutputString(
- "\nThe build continued as if that argument was "
- "unspecified.\n\n");
- // Nonfatal error.
- }
-
- if (check_public_headers_) {
- std::vector<const Target*> all_targets = builder_.GetAllResolvedTargets();
- std::vector<const Target*> to_check;
- if (check_patterns()) {
- commands::FilterTargetsByPatterns(all_targets, *check_patterns(),
- &to_check);
- } else {
- to_check = all_targets;
- }
-
- if (!commands::CheckPublicHeaders(&build_settings_, all_targets, to_check,
- false, false)) {
- return false;
- }
- }
-
- // Write out tracing and timing if requested.
- if (cmdline.HasSwitch(switches::kTime))
- PrintLongHelp(SummarizeTraces());
- if (cmdline.HasSwitch(switches::kTracelog))
- SaveTraces(cmdline.GetSwitchValuePath(switches::kTracelog));
-
- return true;
-}
-
-bool Setup::FillArguments(const base::CommandLine& cmdline) {
- // Use the args on the command line if specified, and save them. Do this even
- // if the list is empty (this means clear any defaults).
- // If --args is not set, args.gn file does not exist and gen_empty_args
- // is set, generate an empty args.gn file with default comments.
-
- base::FilePath build_arg_file =
- build_settings_.GetFullPath(GetBuildArgFile());
- auto switch_value = cmdline.GetSwitchValueASCII(switches::kArgs);
- if (cmdline.HasSwitch(switches::kArgs) ||
- (gen_empty_args_ && !PathExists(build_arg_file))) {
- if (!FillArgsFromCommandLine(switch_value.empty() ? kDefaultArgsGn
- : switch_value)) {
- return false;
- }
- SaveArgsToFile();
- return true;
- }
-
- // No command line args given, use the arguments from the build dir (if any).
- return FillArgsFromFile();
-}
-
-bool Setup::FillArgsFromCommandLine(const std::string& args) {
- args_input_file_ = std::make_unique<InputFile>(SourceFile());
- args_input_file_->SetContents(args);
- args_input_file_->set_friendly_name("the command-line \"--args\"");
- return FillArgsFromArgsInputFile();
-}
-
-bool Setup::FillArgsFromFile() {
- ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Load args file");
-
- SourceFile build_arg_source_file = GetBuildArgFile();
- base::FilePath build_arg_file =
- build_settings_.GetFullPath(build_arg_source_file);
-
- std::string contents;
- if (!base::ReadFileToString(build_arg_file, &contents))
- return true; // File doesn't exist, continue with default args.
-
- // Add a dependency on the build arguments file. If this changes, we want
- // to re-generate the build.
- g_scheduler->AddGenDependency(build_arg_file);
-
- if (contents.empty())
- return true; // Empty file, do nothing.
-
- args_input_file_ = std::make_unique<InputFile>(build_arg_source_file);
- args_input_file_->SetContents(contents);
- args_input_file_->set_friendly_name(
- "build arg file (use \"gn args <out_dir>\" to edit)");
-
- setup_trace.Done(); // Only want to count the load as part of the trace.
- return FillArgsFromArgsInputFile();
-}
-
-bool Setup::FillArgsFromArgsInputFile() {
- ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Parse args");
-
- Err err;
- args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- args_root_ = Parser::Parse(args_tokens_, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- Scope arg_scope(&dotfile_settings_);
- // Set soure dir so relative imports in args work.
- SourceDir root_source_dir =
- SourceDirForCurrentDirectory(build_settings_.root_path());
- arg_scope.set_source_dir(root_source_dir);
- args_root_->Execute(&arg_scope, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- // Save the result of the command args.
- Scope::KeyValueMap overrides;
- arg_scope.GetCurrentScopeValues(&overrides);
- build_settings_.build_args().AddArgOverrides(overrides);
- build_settings_.build_args().set_build_args_dependency_files(
- arg_scope.build_dependency_files());
- return true;
-}
-
-bool Setup::SaveArgsToFile() {
- ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Save args file");
-
- // For the first run, the build output dir might not be created yet, so do
- // that so we can write a file into it. Ignore errors, we'll catch the error
- // when we try to write a file to it below.
- base::FilePath build_arg_file =
- build_settings_.GetFullPath(GetBuildArgFile());
- base::CreateDirectory(build_arg_file.DirName());
-
- std::string contents = args_input_file_->contents();
- commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive,
- &contents);
-#if defined(OS_WIN)
- // Use Windows lineendings for this file since it will often open in
- // Notepad which can't handle Unix ones.
- base::ReplaceSubstringsAfterOffset(&contents, 0, "\n", "\r\n");
-#endif
- if (base::WriteFile(build_arg_file, contents.c_str(),
- static_cast<int>(contents.size())) == -1) {
- Err(Location(), "Args file could not be written.",
- "The file is \"" + FilePathToUTF8(build_arg_file) + "\"")
- .PrintToStdout();
- return false;
- }
-
- // Add a dependency on the build arguments file. If this changes, we want
- // to re-generate the build.
- g_scheduler->AddGenDependency(build_arg_file);
-
- return true;
-}
-
-bool Setup::FillSourceDir(const base::CommandLine& cmdline) {
- // Find the .gn file.
- base::FilePath root_path;
-
- // Prefer the command line args to the config file.
- base::FilePath relative_root_path =
- cmdline.GetSwitchValuePath(switches::kRoot);
- if (!relative_root_path.empty()) {
- root_path = base::MakeAbsoluteFilePath(relative_root_path);
- if (root_path.empty()) {
- Err(Location(), "Root source path not found.",
- "The path \"" + FilePathToUTF8(relative_root_path) +
- "\" doesn't exist.")
- .PrintToStdout();
- return false;
- }
-
- // When --root is specified, an alternate --dotfile can also be set.
- // --dotfile should be a real file path and not a "//foo" source-relative
- // path.
- base::FilePath dotfile_path =
- cmdline.GetSwitchValuePath(switches::kDotfile);
- if (dotfile_path.empty()) {
- dotfile_name_ = root_path.Append(kGnFile);
- } else {
- dotfile_name_ = base::MakeAbsoluteFilePath(dotfile_path);
- if (dotfile_name_.empty()) {
- Err(Location(), "Could not load dotfile.",
- "The file \"" + FilePathToUTF8(dotfile_path) +
- "\" couldn't be loaded.")
- .PrintToStdout();
- return false;
- }
- // Only set dotfile_name if it was passed explicitly.
- build_settings_.set_dotfile_name(dotfile_name_);
- }
- } else {
- // In the default case, look for a dotfile and that also tells us where the
- // source root is.
- base::FilePath cur_dir;
- base::GetCurrentDirectory(&cur_dir);
- dotfile_name_ = FindDotFile(cur_dir);
- if (dotfile_name_.empty()) {
- Err(Location(), "Can't find source root.",
- "I could not find a \".gn\" file in the current directory or any "
- "parent,\nand the --root command-line argument was not specified.")
- .PrintToStdout();
- return false;
- }
- root_path = dotfile_name_.DirName();
- }
-
- base::FilePath root_realpath = base::MakeAbsoluteFilePath(root_path);
- if (root_realpath.empty()) {
- Err(Location(), "Can't get the real root path.",
- "I could not get the real path of \"" + FilePathToUTF8(root_path) +
- "\".")
- .PrintToStdout();
- return false;
- }
- if (scheduler_.verbose_logging())
- scheduler_.Log("Using source root", FilePathToUTF8(root_realpath));
- build_settings_.SetRootPath(root_realpath);
-
- return true;
-}
-
-bool Setup::FillBuildDir(const std::string& build_dir, bool require_exists) {
- Err err;
- SourceDir resolved =
- SourceDirForCurrentDirectory(build_settings_.root_path())
- .ResolveRelativeDir(Value(nullptr, build_dir), &err,
- build_settings_.root_path_utf8());
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- base::FilePath build_dir_path = build_settings_.GetFullPath(resolved);
- if (!base::CreateDirectory(build_dir_path)) {
- Err(Location(), "Can't create the build dir.",
- "I could not create the build dir \"" + FilePathToUTF8(build_dir_path) +
- "\".")
- .PrintToStdout();
- return false;
- }
- base::FilePath build_dir_realpath =
- base::MakeAbsoluteFilePath(build_dir_path);
- if (build_dir_realpath.empty()) {
- Err(Location(), "Can't get the real build dir path.",
- "I could not get the real path of \"" + FilePathToUTF8(build_dir_path) +
- "\".")
- .PrintToStdout();
- return false;
- }
- resolved = SourceDirForPath(build_settings_.root_path(), build_dir_realpath);
-
- if (scheduler_.verbose_logging())
- scheduler_.Log("Using build dir", resolved.value());
-
- if (require_exists) {
- if (!base::PathExists(
- build_dir_path.Append(FILE_PATH_LITERAL("build.ninja")))) {
- Err(Location(), "Not a build directory.",
- "This command requires an existing build directory. I interpreted "
- "your input\n\"" +
- build_dir + "\" as:\n " + FilePathToUTF8(build_dir_path) +
- "\nwhich doesn't seem to contain a previously-generated build.")
- .PrintToStdout();
- return false;
- }
- }
-
- build_settings_.SetBuildDir(resolved);
- return true;
-}
-
-bool Setup::FillPythonPath(const base::CommandLine& cmdline) {
- // Trace this since it tends to be a bit slow on Windows.
- ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path");
- const Value* value = dotfile_scope_.GetValue("script_executable", true);
- if (cmdline.HasSwitch(switches::kScriptExecutable)) {
- build_settings_.set_python_path(
- cmdline.GetSwitchValuePath(switches::kScriptExecutable));
- } else if (value) {
- Err err;
- if (!value->VerifyTypeIs(Value::STRING, &err)) {
- err.PrintToStdout();
- return false;
- }
- build_settings_.set_python_path(
- base::FilePath(UTF8ToFilePath(value->string_value())));
- } else {
-#if defined(OS_WIN)
- base::FilePath python_path = FindWindowsPython();
- if (python_path.empty()) {
- scheduler_.Log("WARNING",
- "Could not find python on path, using "
- "just \"python.exe\"");
- python_path = base::FilePath(kPythonExeName);
- }
- build_settings_.set_python_path(python_path.NormalizePathSeparatorsTo('/'));
-#else
- build_settings_.set_python_path(base::FilePath("python"));
-#endif
- }
- return true;
-}
-
-bool Setup::RunConfigFile() {
- if (scheduler_.verbose_logging())
- scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
-
- dotfile_input_file_ = std::make_unique<InputFile>(SourceFile("//.gn"));
- if (!dotfile_input_file_->Load(dotfile_name_)) {
- Err(Location(), "Could not load dotfile.",
- "The file \"" + FilePathToUTF8(dotfile_name_) + "\" couldn't be loaded")
- .PrintToStdout();
- return false;
- }
-
- Err err;
- dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- // Add a dependency on the build arguments file. If this changes, we want
- // to re-generate the build. This causes the dotfile to make it into
- // build.ninja.d.
- g_scheduler->AddGenDependency(dotfile_name_);
-
- // Also add a build dependency to the scope, which is used by `gn analyze`.
- dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
- dotfile_root_->Execute(&dotfile_scope_, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- return true;
-}
-
-bool Setup::FillOtherConfig(const base::CommandLine& cmdline) {
- Err err;
- SourceDir current_dir("//");
- Label root_target_label(current_dir, "");
-
- // Secondary source path, read from the config file if present.
- // Read from the config file if present.
- const Value* secondary_value =
- dotfile_scope_.GetValue("secondary_source", true);
- if (secondary_value) {
- if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
- err.PrintToStdout();
- return false;
- }
- build_settings_.SetSecondarySourcePath(
- SourceDir(secondary_value->string_value()));
- }
-
- // Root build file.
- const Value* root_value = dotfile_scope_.GetValue("root", true);
- if (root_value) {
- if (!root_value->VerifyTypeIs(Value::STRING, &err)) {
- err.PrintToStdout();
- return false;
- }
-
- root_target_label = Label::Resolve(current_dir, Label(), *root_value, &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
-
- root_build_file_ = Loader::BuildFileForLabel(root_target_label);
- }
- build_settings_.SetRootTargetLabel(root_target_label);
-
- // Build config file.
- const Value* build_config_value =
- dotfile_scope_.GetValue("buildconfig", true);
- if (!build_config_value) {
- Err(Location(), "No build config file.",
- "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) +
- "\")\n"
- "didn't specify a \"buildconfig\" value.")
- .PrintToStdout();
- return false;
- } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
- err.PrintToStdout();
- return false;
- }
- build_settings_.set_build_config_file(
- SourceFile(build_config_value->string_value()));
-
- // Targets to check.
- const Value* check_targets_value =
- dotfile_scope_.GetValue("check_targets", true);
- if (check_targets_value) {
- check_patterns_.reset(new std::vector<LabelPattern>);
- ExtractListOfLabelPatterns(*check_targets_value, current_dir,
- check_patterns_.get(), &err);
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
- }
-
- // Fill exec_script_whitelist.
- const Value* exec_script_whitelist_value =
- dotfile_scope_.GetValue("exec_script_whitelist", true);
- if (exec_script_whitelist_value) {
- // Fill the list of targets to check.
- if (!exec_script_whitelist_value->VerifyTypeIs(Value::LIST, &err)) {
- err.PrintToStdout();
- return false;
- }
- std::unique_ptr<std::set<SourceFile>> whitelist =
- std::make_unique<std::set<SourceFile>>();
- for (const auto& item : exec_script_whitelist_value->list_value()) {
- if (!item.VerifyTypeIs(Value::STRING, &err)) {
- err.PrintToStdout();
- return false;
- }
- whitelist->insert(current_dir.ResolveRelativeFile(item, &err));
- if (err.has_error()) {
- err.PrintToStdout();
- return false;
- }
- }
- build_settings_.set_exec_script_whitelist(std::move(whitelist));
- }
-
- // Fill optional default_args.
- const Value* default_args_value =
- dotfile_scope_.GetValue("default_args", true);
- if (default_args_value) {
- if (!default_args_value->VerifyTypeIs(Value::SCOPE, &err)) {
- err.PrintToStdout();
- return false;
- }
-
- default_args_ = default_args_value->scope_value();
- }
-
- const Value* arg_file_template_value =
- dotfile_scope_.GetValue("arg_file_template", true);
- if (arg_file_template_value) {
- if (!arg_file_template_value->VerifyTypeIs(Value::STRING, &err)) {
- err.PrintToStdout();
- return false;
- }
- SourceFile path(arg_file_template_value->string_value());
- build_settings_.set_arg_file_template_path(path);
- }
-
- return true;
-}
diff --git a/gn/tools/gn/setup.h b/gn/tools/gn/setup.h
deleted file mode 100644
index 9aef0735ce1..00000000000
--- a/gn/tools/gn/setup.h
+++ /dev/null
@@ -1,189 +0,0 @@
-// 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 TOOLS_GN_SETUP_H_
-#define TOOLS_GN_SETUP_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/loader.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/token.h"
-#include "tools/gn/toolchain.h"
-
-class InputFile;
-class ParseNode;
-
-namespace base {
-class CommandLine;
-}
-
-extern const char kDotfile_Help[];
-
-// Helper class to set up the build settings and environment for the various
-// commands to run.
-class Setup {
- public:
- Setup();
-
- // Configures the build for the current command line. On success returns
- // true. On failure, prints the error and returns false.
- //
- // The parameter is the string the user specified for the build directory. We
- // will try to interpret this as a SourceDir if possible, and will fail if is
- // is malformed.
- //
- // With force_create = false, setup will fail if the build directory doesn't
- // already exist with an args file in it. With force_create set to true, the
- // directory will be created if necessary. Commands explicitly doing
- // generation should set this to true to create it, but querying commands
- // should set it to false to prevent creating oddly-named directories in case
- // the user omits the build directory argument (which is easy to do).
- //
- // cmdline is the gn invocation command, with flags like --root and --dotfile.
- // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
- // is used.
- bool DoSetup(const std::string& build_dir, bool force_create);
- bool DoSetup(const std::string& build_dir,
- bool force_create,
- const base::CommandLine& cmdline);
-
- // Runs the load, returning true on success. On failure, prints the error
- // and returns false. This includes both RunPreMessageLoop() and
- // RunPostMessageLoop().
- //
- // cmdline is the gn invocation command, with flags like --root and --dotfile.
- // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
- // is used.
- bool Run();
- bool Run(const base::CommandLine& cmdline);
-
- Scheduler& scheduler() { return scheduler_; }
-
- // Returns the file used to store the build arguments. Note that the path
- // might not exist.
- SourceFile GetBuildArgFile() const;
-
- // Sets whether the build arguments should be filled during setup from the
- // command line/build argument file. This will be true by default. The use
- // case for setting it to false is when editing build arguments, we don't
- // want to rely on them being valid.
- void set_fill_arguments(bool fa) { fill_arguments_ = fa; }
-
- // After a successful run, setting this will additionally cause the public
- // headers to be checked. Defaults to false.
- void set_check_public_headers(bool s) { check_public_headers_ = s; }
-
- // Before DoSetup, setting this will generate an empty args.gn if
- // it does not exist and set up correct dependencies for it.
- void set_gen_empty_args(bool ge) { gen_empty_args_ = ge; }
-
- // Read from the .gn file, these are the targets to check. If the .gn file
- // does not specify anything, this will be null. If the .gn file specifies
- // the empty list, this will be non-null but empty.
- const std::vector<LabelPattern>* check_patterns() const {
- return check_patterns_.get();
- }
-
- BuildSettings& build_settings() { return build_settings_; }
- Builder& builder() { return builder_; }
- LoaderImpl* loader() { return loader_.get(); }
-
- const SourceFile& GetDotFile() const { return dotfile_input_file_->name(); }
-
- // Name of the file in the root build directory that contains the build
- // arguments.
- static const char kBuildArgFileName[];
-
- private:
- // Performs the two sets of operations to run the generation before and after
- // the message loop is run.
- void RunPreMessageLoop();
- bool RunPostMessageLoop(const base::CommandLine& cmdline);
-
- // Fills build arguments. Returns true on success.
- bool FillArguments(const base::CommandLine& cmdline);
-
- // Fills the build arguments from the command line or from the build arg file.
- bool FillArgsFromCommandLine(const std::string& args);
- bool FillArgsFromFile();
-
- // Given an already-loaded args_input_file_, parses and saves the resulting
- // arguments. Backend for the different FillArgs variants.
- bool FillArgsFromArgsInputFile();
-
- // Writes the build arguments to the build arg file.
- bool SaveArgsToFile();
-
- // Fills the root directory into the settings. Returns true on success.
- bool FillSourceDir(const base::CommandLine& cmdline);
-
- // Fills the build directory given the value the user has specified.
- // Must happen after FillSourceDir so we can resolve source-relative
- // paths. If require_exists is false, it will fail if the dir doesn't exist.
- bool FillBuildDir(const std::string& build_dir, bool require_exists);
-
- // Fills the python path portion of the command line. On failure, sets
- // it to just "python".
- bool FillPythonPath(const base::CommandLine& cmdline);
-
- // Run config file.
- bool RunConfigFile();
-
- bool FillOtherConfig(const base::CommandLine& cmdline);
-
- BuildSettings build_settings_;
- scoped_refptr<LoaderImpl> loader_;
- Builder builder_;
-
- SourceFile root_build_file_;
-
- bool check_public_headers_ = false;
-
- // See getter for info.
- std::unique_ptr<std::vector<LabelPattern>> check_patterns_;
-
- Scheduler scheduler_;
-
- // These settings and toolchain are used to interpret the command line and
- // dot file.
- Settings dotfile_settings_;
- Scope dotfile_scope_;
-
- // State for invoking the dotfile.
- base::FilePath dotfile_name_;
- std::unique_ptr<InputFile> dotfile_input_file_;
- std::vector<Token> dotfile_tokens_;
- std::unique_ptr<ParseNode> dotfile_root_;
-
- // Default overrides, specified in the dotfile.
- // Owned by the Value (if it exists) in the dotfile_scope_.
- const Scope* default_args_ = nullptr;
-
- // Set to true when we should populate the build arguments from the command
- // line or build argument file. See setter above.
- bool fill_arguments_ = true;
-
- // Generate an empty args.gn file if it does not exists.
- bool gen_empty_args_ = false;
-
- // State for invoking the command line args. We specifically want to keep
- // this around for the entire run so that Values can blame to the command
- // line when we issue errors about them.
- std::unique_ptr<InputFile> args_input_file_;
- std::vector<Token> args_tokens_;
- std::unique_ptr<ParseNode> args_root_;
-
- DISALLOW_COPY_AND_ASSIGN(Setup);
-};
-
-#endif // TOOLS_GN_SETUP_H_
diff --git a/gn/tools/gn/setup_unittest.cc b/gn/tools/gn/setup_unittest.cc
deleted file mode 100644
index 20c8effa0a4..00000000000
--- a/gn/tools/gn/setup_unittest.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/setup.h"
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/switches.h"
-#include "tools/gn/test_with_scheduler.h"
-
-using SetupTest = TestWithScheduler;
-
-static void WriteFile(const base::FilePath& file, const std::string& data) {
- CHECK_EQ(static_cast<int>(data.size()), // Way smaller than INT_MAX.
- base::WriteFile(file, data.data(), data.size()));
-}
-
-TEST_F(SetupTest, DotGNFileIsGenDep) {
- base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
-
- // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
- // pass it as --root.
- base::ScopedTempDir in_temp_dir;
- ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
- base::FilePath in_path = in_temp_dir.GetPath();
- base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
- WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n");
- WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
- cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
-
- // Create another temp dir for writing the generated files to.
- base::ScopedTempDir build_temp_dir;
- ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
-
- // Run setup and check that the .gn file is in the scheduler's gen deps.
- Setup setup;
- EXPECT_TRUE(
- setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline));
- std::vector<base::FilePath> gen_deps = g_scheduler->GetGenDependencies();
- ASSERT_EQ(1u, gen_deps.size());
- EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
-}
diff --git a/gn/tools/gn/source_dir.cc b/gn/tools/gn/source_dir.cc
deleted file mode 100644
index 7def4b2ba0c..00000000000
--- a/gn/tools/gn/source_dir.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// 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 "tools/gn/source_dir.h"
-
-#include <string>
-
-#include "base/logging.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/source_file.h"
-#include "util/build_config.h"
-
-namespace {
-
-void AssertValueSourceDirString(const std::string& s) {
- if (!s.empty()) {
-#if defined(OS_WIN)
- DCHECK(s[0] == '/' ||
- (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
-#else
- DCHECK(s[0] == '/');
-#endif
- DCHECK(EndsWithSlash(s)) << s;
- }
-}
-
-// Validates input value (input_value) and sets proper error message.
-// Note: Parameter blame_input is used only for generating error message.
-template <typename StringType>
-bool ValidateResolveInput(bool as_file,
- const Value& blame_input_value,
- const StringType& input_value,
- Err* err) {
- if (as_file) {
- // It's an error to resolve an empty string or one that is a directory
- // (indicated by a trailing slash) because this is the function that expects
- // to return a file.
- if (input_value.empty()) {
- *err = Err(blame_input_value, "Empty file path.",
- "You can't use empty strings as file paths.");
- return false;
- } else if (input_value[input_value.size() - 1] == '/') {
- std::string help = "You specified the path\n ";
- help.append(std::string(input_value));
- help.append(
- "\nand it ends in a slash, indicating you think it's a directory."
- "\nBut here you're supposed to be listing a file.");
- *err = Err(blame_input_value, "File path ends in a slash.", help);
- return false;
- }
- } else if (input_value.empty()) {
- *err = Err(blame_input_value, "Empty directory path.",
- "You can't use empty strings as directories.");
- return false;
- }
- return true;
-}
-
-} // namespace
-
-SourceDir::SourceDir(const std::string& s) : value_(s) {
- if (!EndsWithSlash(value_))
- value_.push_back('/');
- AssertValueSourceDirString(value_);
-}
-
-SourceDir::SourceDir(std::string&& s) : value_(std::move(s)) {
- if (!EndsWithSlash(value_))
- value_.push_back('/');
- AssertValueSourceDirString(value_);
-}
-
-template <typename StringType>
-std::string SourceDir::ResolveRelativeAs(
- bool as_file,
- const Value& blame_input_value,
- const StringType& input_value,
- Err* err,
- const base::StringPiece& source_root) const {
- if (!ValidateResolveInput<StringType>(as_file, blame_input_value, input_value,
- err)) {
- return std::string();
- }
- return ResolveRelative(input_value, value_, as_file, source_root);
-}
-
-SourceFile SourceDir::ResolveRelativeFile(
- const Value& p,
- Err* err,
- const base::StringPiece& source_root) const {
- SourceFile ret;
-
- if (!p.VerifyTypeIs(Value::STRING, err))
- return ret;
-
- const std::string& input_string = p.string_value();
- if (!ValidateResolveInput<std::string>(true, p, input_string, err))
- return ret;
-
- ret.SetValue(ResolveRelative(input_string, value_, true, source_root));
- return ret;
-}
-
-std::string SourceDir::ResolveRelativeAs(bool as_file,
- const Value& v,
- Err* err,
- const base::StringPiece& source_root,
- const std::string* v_value) const {
- if (!v.VerifyTypeIs(Value::STRING, err))
- return std::string();
-
- if (!v_value) {
- v_value = &v.string_value();
- }
- std::string result =
- ResolveRelativeAs(as_file, v, *v_value, err, source_root);
- if (!as_file)
- AssertValueSourceDirString(result);
- return result;
-}
-
-SourceDir SourceDir::ResolveRelativeDir(
- const Value& v,
- Err* err,
- const base::StringPiece& source_root) const {
- if (!v.VerifyTypeIs(Value::STRING, err))
- return SourceDir();
-
- return ResolveRelativeDir<std::string>(v, v.string_value(), err, source_root);
-}
-
-base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
- return ResolvePath(value_, false, source_root);
-}
-
-// Explicit template instantiation
-template std::string SourceDir::ResolveRelativeAs(
- bool as_file,
- const Value& blame_input_value,
- const std::string& input_value,
- Err* err,
- const base::StringPiece& source_root) const;
-
-template std::string SourceDir::ResolveRelativeAs(
- bool as_file,
- const Value& blame_input_value,
- const base::StringPiece& input_value,
- Err* err,
- const base::StringPiece& source_root) const;
diff --git a/gn/tools/gn/source_dir.h b/gn/tools/gn/source_dir.h
deleted file mode 100644
index 8b6dcc9e454..00000000000
--- a/gn/tools/gn/source_dir.h
+++ /dev/null
@@ -1,153 +0,0 @@
-// 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 TOOLS_GN_SOURCE_DIR_H_
-#define TOOLS_GN_SOURCE_DIR_H_
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-
-class Err;
-class SourceFile;
-class Value;
-
-// Represents a directory within the source tree. Source dirs begin and end in
-// slashes.
-//
-// If there is one slash at the beginning, it will mean a system-absolute file
-// path. On Windows, absolute system paths will be of the form "/C:/foo/bar".
-//
-// Two slashes at the beginning indicate a path relative to the source root.
-class SourceDir {
- public:
- SourceDir() = default;
-
- SourceDir(const std::string& s);
- explicit SourceDir(std::string&& s);
-
- // Resolves a file or dir name (based on as_file parameter) relative
- // to this source directory. Will return an empty string on error
- // and set the give *err pointer (required). Empty input is always an error.
- //
- // Passed non null v_value will be used to resolve path (in cases where
- // a substring has been extracted from the value, as with label resolution).
- // In this use case parameter v is used to generate proper error.
- //
- // If source_root is supplied, these functions will additionally handle the
- // case where the input is a system-absolute but still inside the source
- // tree. This is the case for some external tools.
- std::string ResolveRelativeAs(
- bool as_file,
- const Value& v,
- Err* err,
- const base::StringPiece& source_root = base::StringPiece(),
- const std::string* v_value = nullptr) const;
-
- // Like ResolveRelativeAs above, but allows to produce result
- // without overhead for string conversion (on input value).
- template <typename StringType>
- std::string ResolveRelativeAs(
- bool as_file,
- const Value& blame_input_value,
- const StringType& input_value,
- Err* err,
- const base::StringPiece& source_root = base::StringPiece()) const;
-
- // Wrapper for ResolveRelativeAs.
- SourceFile ResolveRelativeFile(
- const Value& p,
- Err* err,
- const base::StringPiece& source_root = base::StringPiece()) const;
-
- // Wrapper for ResolveRelativeAs.
- template <typename StringType>
- SourceDir ResolveRelativeDir(
- const Value& blame_input_value,
- const StringType& input_value,
- Err* err,
- const base::StringPiece& source_root = base::StringPiece()) const {
- SourceDir ret;
- ret.value_ = ResolveRelativeAs<StringType>(false, blame_input_value,
- input_value, err, source_root);
- return ret;
- }
-
- // Wrapper for ResolveRelativeDir where input_value equals to
- // v.string_value().
- SourceDir ResolveRelativeDir(
- const Value& v,
- Err* err,
- const base::StringPiece& source_root = base::StringPiece()) const;
-
- // Resolves this source file relative to some given source root. Returns
- // an empty file path on error.
- base::FilePath Resolve(const base::FilePath& source_root) const;
-
- bool is_null() const { return value_.empty(); }
- const std::string& value() const { return value_; }
-
- // Returns true if this path starts with a "//" which indicates a path
- // from the source root.
- bool is_source_absolute() const {
- return value_.size() >= 2 && value_[0] == '/' && value_[1] == '/';
- }
-
- // Returns true if this path starts with a single slash which indicates a
- // system-absolute path.
- bool is_system_absolute() const { return !is_source_absolute(); }
-
- // Returns a source-absolute path starting with only one slash at the
- // beginning (normally source-absolute paths start with two slashes to mark
- // them as such). This is normally used when concatenating directories
- // together.
- //
- // This function asserts that the directory is actually source-absolute. The
- // return value points into our buffer.
- base::StringPiece SourceAbsoluteWithOneSlash() const {
- CHECK(is_source_absolute());
- return base::StringPiece(&value_[1], value_.size() - 1);
- }
-
- // Returns a path that does not end with a slash.
- //
- // This function simply returns the reference to the value if the path is a
- // root, e.g. "/" or "//".
- base::StringPiece SourceWithNoTrailingSlash() const {
- if (value_.size() > 2)
- return base::StringPiece(&value_[0], value_.size() - 1);
- return base::StringPiece(value_);
- }
-
- void SwapValue(std::string* v);
-
- bool operator==(const SourceDir& other) const {
- return value_ == other.value_;
- }
- bool operator!=(const SourceDir& other) const { return !operator==(other); }
- bool operator<(const SourceDir& other) const { return value_ < other.value_; }
-
- private:
- friend class SourceFile;
- std::string value_;
-};
-
-namespace std {
-
-template <>
-struct hash<SourceDir> {
- std::size_t operator()(const SourceDir& v) const {
- hash<std::string> h;
- return h(v.value());
- }
-};
-
-} // namespace std
-
-#endif // TOOLS_GN_SOURCE_DIR_H_
diff --git a/gn/tools/gn/source_dir_unittest.cc b/gn/tools/gn/source_dir_unittest.cc
deleted file mode 100644
index d5ada23f0c4..00000000000
--- a/gn/tools/gn/source_dir_unittest.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-// 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 "tools/gn/source_dir.h"
-#include "tools/gn/err.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/value.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-TEST(SourceDir, ResolveRelativeFile) {
- Err err;
- SourceDir base("//base/");
-#if defined(OS_WIN)
- base::StringPiece source_root("C:/source/root");
-#else
- base::StringPiece source_root("/source/root");
-#endif
-
- // Empty input is an error.
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, std::string()), &err,
- source_root) == SourceFile());
- EXPECT_TRUE(err.has_error());
-
- // These things are directories, so should be an error.
- err = Err();
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//foo/bar/"), &err,
- source_root) == SourceFile());
- EXPECT_TRUE(err.has_error());
-
- err = Err();
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "bar/"), &err,
- source_root) == SourceFile());
- EXPECT_TRUE(err.has_error());
-
- // Absolute paths should be passed unchanged.
- err = Err();
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//foo"), &err,
- source_root) == SourceFile("//foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "/foo"), &err,
- source_root) == SourceFile("/foo"));
- EXPECT_FALSE(err.has_error());
-
- // Basic relative stuff.
- EXPECT_TRUE(
- base.ResolveRelativeFile(Value(nullptr, "foo"), &err, source_root) ==
- SourceFile("//base/foo"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(
- base.ResolveRelativeFile(Value(nullptr, "./foo"), &err, source_root) ==
- SourceFile("//base/foo"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "../foo"), &err,
- source_root) == SourceFile("//foo"));
- EXPECT_FALSE(err.has_error());
-
-// If the given relative path points outside the source root, we
-// expect an absolute path.
-#if defined(OS_WIN)
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "../../foo"), &err,
- source_root) ==
- SourceFile("/C:/source/foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(
- base.ResolveRelativeFile(Value(nullptr, "//../foo"), &err, source_root) ==
- SourceFile("/C:/source/foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../root/foo"), &err,
- source_root) ==
- SourceFile("/C:/source/root/foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../../../foo/bar"),
- &err,
- source_root) == SourceFile("/foo/bar"));
- EXPECT_FALSE(err.has_error());
-#else
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "../../foo"), &err,
- source_root) ==
- SourceFile("/source/foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(
- base.ResolveRelativeFile(Value(nullptr, "//../foo"), &err, source_root) ==
- SourceFile("/source/foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../root/foo"), &err,
- source_root) ==
- SourceFile("/source/root/foo"));
- EXPECT_FALSE(err.has_error());
-
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "//../../../foo/bar"),
- &err,
- source_root) == SourceFile("/foo/bar"));
- EXPECT_FALSE(err.has_error());
-#endif
-
-#if defined(OS_WIN)
- // Note that we don't canonicalize the backslashes to forward slashes.
- // This could potentially be changed in the future which would mean we should
- // just change the expected result.
- EXPECT_TRUE(base.ResolveRelativeFile(Value(nullptr, "C:\\foo\\bar.txt"), &err,
- source_root) ==
- SourceFile("/C:/foo/bar.txt"));
- EXPECT_FALSE(err.has_error());
-#endif
-}
-
-TEST(SourceDir, ResolveRelativeDir) {
- Err err;
- SourceDir base("//base/");
-#if defined(OS_WIN)
- base::StringPiece source_root("C:/source/root");
-#else
- base::StringPiece source_root("/source/root");
-#endif
-
- // Empty input is an error.
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, std::string()), &err,
- source_root) == SourceDir());
- EXPECT_TRUE(err.has_error());
-
- // Absolute paths should be passed unchanged.
- err = Err();
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "//foo"), &err,
- source_root) == SourceDir("//foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "/foo"), &err,
- source_root) == SourceDir("/foo/"));
- EXPECT_FALSE(err.has_error());
-
- // Basic relative stuff.
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "foo"), &err,
- source_root) == SourceDir("//base/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "./foo"), &err,
- source_root) == SourceDir("//base/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "../foo"), &err,
- source_root) == SourceDir("//foo/"));
- EXPECT_FALSE(err.has_error());
-
-// If the given relative path points outside the source root, we
-// expect an absolute path.
-#if defined(OS_WIN)
- EXPECT_TRUE(
- base.ResolveRelativeDir(Value(nullptr, "../../foo"), &err, source_root) ==
- SourceDir("/C:/source/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(
- base.ResolveRelativeDir(Value(nullptr, "//../foo"), &err, source_root) ==
- SourceDir("/C:/source/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "//.."), &err,
- source_root) == SourceDir("/C:/source/"));
- EXPECT_FALSE(err.has_error());
-#else
- EXPECT_TRUE(
- base.ResolveRelativeDir(Value(nullptr, "../../foo"), &err, source_root) ==
- SourceDir("/source/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(
- base.ResolveRelativeDir(Value(nullptr, "//../foo"), &err, source_root) ==
- SourceDir("/source/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "//.."), &err,
- source_root) == SourceDir("/source/"));
- EXPECT_FALSE(err.has_error());
-#endif
-
-#if defined(OS_WIN)
- // Canonicalize the existing backslashes to forward slashes and add a
- // leading slash if necessary.
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "\\C:\\foo"), &err) ==
- SourceDir("/C:/foo/"));
- EXPECT_FALSE(err.has_error());
- EXPECT_TRUE(base.ResolveRelativeDir(Value(nullptr, "C:\\foo"), &err) ==
- SourceDir("/C:/foo/"));
- EXPECT_FALSE(err.has_error());
-#endif
-}
-
-TEST(SourceDir, SourceWithNoTrailingSlash) {
- Err err;
- SourceDir base("//base/");
- SourceDir base_no_slash("//base/");
- EXPECT_EQ(base.SourceWithNoTrailingSlash(), "//base");
- EXPECT_EQ(base_no_slash.SourceWithNoTrailingSlash(), "//base");
-
- SourceDir relative_root("//");
- EXPECT_EQ(relative_root.SourceWithNoTrailingSlash(), "//");
-
-#if defined(OS_WIN)
- SourceDir root("C:/");
- SourceDir root_no_slash("C:");
- EXPECT_EQ(root.SourceWithNoTrailingSlash(), "C:");
- EXPECT_EQ(root_no_slash.SourceWithNoTrailingSlash(), "C:");
-#else
- SourceDir root("/");
- EXPECT_EQ(root.SourceWithNoTrailingSlash(), "/");
-#endif
-}
diff --git a/gn/tools/gn/source_file.cc b/gn/tools/gn/source_file.cc
deleted file mode 100644
index ce24207e63e..00000000000
--- a/gn/tools/gn/source_file.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 "tools/gn/source_file.h"
-
-#include "base/logging.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/source_dir.h"
-#include "util/build_config.h"
-
-namespace {
-
-void AssertValueSourceFileString(const std::string& s) {
-#if defined(OS_WIN)
- DCHECK(s[0] == '/' ||
- (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
-#else
- DCHECK(s[0] == '/');
-#endif
- DCHECK(!EndsWithSlash(s)) << s;
-}
-
-SourceFile::Type GetSourceFileType(const std::string& file) {
- base::StringPiece extension = FindExtension(&file);
- if (extension == "cc" || extension == "cpp" || extension == "cxx")
- return SourceFile::SOURCE_CPP;
- if (extension == "h" || extension == "hpp" || extension == "hxx" ||
- extension == "hh" || extension == "inc" || extension == "ipp" ||
- extension == "inl")
- return SourceFile::SOURCE_H;
- if (extension == "c")
- return SourceFile::SOURCE_C;
- if (extension == "m")
- return SourceFile::SOURCE_M;
- if (extension == "mm")
- return SourceFile::SOURCE_MM;
- if (extension == "rc")
- return SourceFile::SOURCE_RC;
- if (extension == "S" || extension == "s" || extension == "asm")
- return SourceFile::SOURCE_S;
- if (extension == "o" || extension == "obj")
- return SourceFile::SOURCE_O;
- if (extension == "def")
- return SourceFile::SOURCE_DEF;
- if (extension == "rs")
- return SourceFile::SOURCE_RS;
- if (extension == "go")
- return SourceFile::SOURCE_GO;
-
- return SourceFile::SOURCE_UNKNOWN;
-}
-
-} // namespace
-
-SourceFile::SourceFile(const std::string& value) : value_(value) {
- DCHECK(!value_.empty());
- AssertValueSourceFileString(value_);
- NormalizePath(&value_);
- type_ = GetSourceFileType(value_);
-}
-
-SourceFile::SourceFile(std::string&& value) : value_(std::move(value)) {
- DCHECK(!value_.empty());
- AssertValueSourceFileString(value_);
- NormalizePath(&value_);
- type_ = GetSourceFileType(value_);
-}
-
-std::string SourceFile::GetName() const {
- if (is_null())
- return std::string();
-
- DCHECK(value_.find('/') != std::string::npos);
- size_t last_slash = value_.rfind('/');
- return std::string(&value_[last_slash + 1], value_.size() - last_slash - 1);
-}
-
-SourceDir SourceFile::GetDir() const {
- if (is_null())
- return SourceDir();
-
- DCHECK(value_.find('/') != std::string::npos);
- size_t last_slash = value_.rfind('/');
- return SourceDir(value_.substr(0, last_slash + 1));
-}
-
-base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const {
- return ResolvePath(value_, true, source_root);
-}
-
-void SourceFile::SetValue(const std::string& value) {
- value_ = value;
- type_ = GetSourceFileType(value_);
-}
-
-SourceFileTypeSet::SourceFileTypeSet() : empty_(true) {
- memset(flags_, 0,
- sizeof(bool) * static_cast<int>(SourceFile::SOURCE_NUMTYPES));
-}
-
-bool SourceFileTypeSet::CSourceUsed() const {
- return empty_ || Get(SourceFile::SOURCE_CPP) || Get(SourceFile::SOURCE_H) ||
- Get(SourceFile::SOURCE_C) || Get(SourceFile::SOURCE_M) ||
- Get(SourceFile::SOURCE_MM) || Get(SourceFile::SOURCE_RC) ||
- Get(SourceFile::SOURCE_S) || Get(SourceFile::SOURCE_O) ||
- Get(SourceFile::SOURCE_DEF);
-}
-
-bool SourceFileTypeSet::RustSourceUsed() const {
- return Get(SourceFile::SOURCE_RS);
-}
-
-bool SourceFileTypeSet::GoSourceUsed() const {
- return Get(SourceFile::SOURCE_GO);
-}
-
-bool SourceFileTypeSet::MixedSourceUsed() const {
- return (1 << static_cast<int>(CSourceUsed())
- << static_cast<int>(RustSourceUsed())
- << static_cast<int>(GoSourceUsed())) > 2;
-}
diff --git a/gn/tools/gn/source_file.h b/gn/tools/gn/source_file.h
deleted file mode 100644
index 081cda61db6..00000000000
--- a/gn/tools/gn/source_file.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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 TOOLS_GN_SOURCE_FILE_H_
-#define TOOLS_GN_SOURCE_FILE_H_
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-
-class SourceDir;
-
-// Represents a file within the source tree. Always begins in a slash, never
-// ends in one.
-class SourceFile {
- public:
- // This should be sequential integers starting from 0 so they can be used as
- // array indices.
- enum Type {
- SOURCE_UNKNOWN = 0,
- SOURCE_ASM,
- SOURCE_C,
- SOURCE_CPP,
- SOURCE_H,
- SOURCE_M,
- SOURCE_MM,
- SOURCE_S,
- SOURCE_RC,
- SOURCE_O, // Object files can be inputs, too. Also counts .obj.
- SOURCE_DEF,
-
- SOURCE_RS,
- SOURCE_GO,
-
- // Must be last.
- SOURCE_NUMTYPES,
- };
-
- SourceFile() = default;
-
- // Takes a known absolute source file. Always begins in a slash.
- explicit SourceFile(const std::string& value);
- explicit SourceFile(std::string&& value);
-
- ~SourceFile() = default;
-
- bool is_null() const { return value_.empty(); }
- const std::string& value() const { return value_; }
- Type type() const { return type_; }
-
- // Returns everything after the last slash.
- std::string GetName() const;
- SourceDir GetDir() const;
-
- // Resolves this source file relative to some given source root. Returns
- // an empty file path on error.
- base::FilePath Resolve(const base::FilePath& source_root) const;
-
- // Returns true if this file starts with a "//" which indicates a path
- // from the source root.
- bool is_source_absolute() const {
- return value_.size() >= 2 && value_[0] == '/' && value_[1] == '/';
- }
-
- // Returns true if this file starts with a single slash which indicates a
- // system-absolute path.
- bool is_system_absolute() const { return !is_source_absolute(); }
-
- // Returns a source-absolute path starting with only one slash at the
- // beginning (normally source-absolute paths start with two slashes to mark
- // them as such). This is normally used when concatenating names together.
- //
- // This function asserts that the file is actually source-absolute. The
- // return value points into our buffer.
- base::StringPiece SourceAbsoluteWithOneSlash() const {
- CHECK(is_source_absolute());
- return base::StringPiece(&value_[1], value_.size() - 1);
- }
-
- bool operator==(const SourceFile& other) const {
- return value_ == other.value_;
- }
- bool operator!=(const SourceFile& other) const { return !operator==(other); }
- bool operator<(const SourceFile& other) const {
- return value_ < other.value_;
- }
-
- private:
- friend class SourceDir;
-
- void SetValue(const std::string& value);
-
- std::string value_;
- Type type_ = SOURCE_UNKNOWN;
-};
-
-namespace std {
-
-template <>
-struct hash<SourceFile> {
- std::size_t operator()(const SourceFile& v) const {
- hash<std::string> h;
- return h(v.value());
- }
-};
-
-} // namespace std
-
-// Represents a set of tool types.
-class SourceFileTypeSet {
- public:
- SourceFileTypeSet();
-
- void Set(SourceFile::Type type) {
- flags_[static_cast<int>(type)] = true;
- empty_ = false;
- }
- bool Get(SourceFile::Type type) const {
- return flags_[static_cast<int>(type)];
- }
-
- bool empty() const { return empty_; }
-
- bool CSourceUsed() const;
- bool RustSourceUsed() const;
- bool GoSourceUsed() const;
-
- bool MixedSourceUsed() const;
-
- private:
- bool empty_;
- bool flags_[static_cast<int>(SourceFile::SOURCE_NUMTYPES)];
-};
-
-#endif // TOOLS_GN_SOURCE_FILE_H_
diff --git a/gn/tools/gn/source_file_unittest.cc b/gn/tools/gn/source_file_unittest.cc
deleted file mode 100644
index d26b35934fb..00000000000
--- a/gn/tools/gn/source_file_unittest.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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 "tools/gn/source_file.h"
-
-#include "util/test/test.h"
-
-// The SourceFile object should normalize the input passed to the constructor.
-// The normalizer unit test checks for all the weird edge cases for normalizing
-// so here just check that it gets called.
-TEST(SourceFile, Normalize) {
- SourceFile a("//foo/../bar.cc");
- EXPECT_EQ("//bar.cc", a.value());
-
- std::string b_str("//foo/././../bar.cc");
- SourceFile b(std::move(b_str));
- EXPECT_TRUE(b_str.empty()); // Should have been swapped in.
- EXPECT_EQ("//bar.cc", b.value());
-}
diff --git a/gn/tools/gn/standard_out.cc b/gn/tools/gn/standard_out.cc
deleted file mode 100644
index 3056418008d..00000000000
--- a/gn/tools/gn/standard_out.cc
+++ /dev/null
@@ -1,342 +0,0 @@
-// 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 "tools/gn/standard_out.h"
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/switches.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#else
-#include <stdio.h>
-#include <unistd.h>
-#endif
-
-namespace {
-
-bool initialized = false;
-
-#if defined(OS_WIN)
-HANDLE hstdout;
-WORD default_attributes;
-#endif
-bool is_console = false;
-
-bool is_markdown = false;
-
-// True while output is going into a markdown ```...``` code block.
-bool in_body = false;
-
-void EnsureInitialized() {
- if (initialized)
- return;
- initialized = true;
-
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- if (cmdline->HasSwitch(switches::kMarkdown)) {
- // Output help in Markdown's syntax, not color-highlighted.
- is_markdown = true;
- }
-
- if (cmdline->HasSwitch(switches::kNoColor)) {
- // Force color off.
- is_console = false;
- return;
- }
-
-#if defined(OS_WIN)
- // On Windows, we can't force the color on. If the output handle isn't a
- // console, there's nothing we can do about it.
- hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO info;
- is_console = !!::GetConsoleScreenBufferInfo(hstdout, &info);
- default_attributes = info.wAttributes;
-#else
- if (cmdline->HasSwitch(switches::kColor))
- is_console = true;
- else
- is_console = isatty(fileno(stdout));
-#endif
-}
-
-#if !defined(OS_WIN)
-void WriteToStdOut(const std::string& output) {
- size_t written_bytes = fwrite(output.data(), 1, output.size(), stdout);
- DCHECK_EQ(output.size(), written_bytes);
-}
-#endif // !defined(OS_WIN)
-
-void OutputMarkdownDec(TextDecoration dec) {
-// The markdown rendering turns "dim" text to italics and any
-// other colored text to bold.
-
-#if defined(OS_WIN)
- DWORD written = 0;
- if (dec == DECORATION_DIM)
- ::WriteFile(hstdout, "*", 1, &written, nullptr);
- else if (dec != DECORATION_NONE)
- ::WriteFile(hstdout, "**", 2, &written, nullptr);
-#else
- if (dec == DECORATION_DIM)
- WriteToStdOut("*");
- else if (dec != DECORATION_NONE)
- WriteToStdOut("**");
-#endif
-}
-
-} // namespace
-
-#if defined(OS_WIN)
-
-void OutputString(const std::string& output,
- TextDecoration dec,
- HtmlEscaping escaping) {
- EnsureInitialized();
- DWORD written = 0;
-
- if (is_markdown) {
- OutputMarkdownDec(dec);
- } else if (is_console) {
- switch (dec) {
- case DECORATION_NONE:
- break;
- case DECORATION_DIM:
- ::SetConsoleTextAttribute(hstdout, FOREGROUND_INTENSITY);
- break;
- case DECORATION_RED:
- ::SetConsoleTextAttribute(hstdout,
- FOREGROUND_RED | FOREGROUND_INTENSITY);
- break;
- case DECORATION_GREEN:
- // Keep green non-bold.
- ::SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN);
- break;
- case DECORATION_BLUE:
- ::SetConsoleTextAttribute(hstdout,
- FOREGROUND_BLUE | FOREGROUND_INTENSITY);
- break;
- case DECORATION_YELLOW:
- ::SetConsoleTextAttribute(hstdout, FOREGROUND_RED | FOREGROUND_GREEN);
- break;
- }
- }
-
- std::string tmpstr = output;
- if (is_markdown && dec == DECORATION_YELLOW) {
- // https://code.google.com/p/gitiles/issues/detail?id=77
- // Gitiles will replace "--" with an em dash in non-code text.
- // Figuring out all instances of this might be difficult, but we can
- // at least escape the instances where this shows up in a heading.
- base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
- }
- if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
- // Markdown auto-escapes < and > in code sections (and converts &lt; to
- // &amp;tl; there), but not elsewhere.
- base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
- base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
- }
- ::WriteFile(hstdout, tmpstr.c_str(), static_cast<DWORD>(tmpstr.size()),
- &written, nullptr);
-
- if (is_markdown) {
- OutputMarkdownDec(dec);
- } else if (is_console) {
- ::SetConsoleTextAttribute(hstdout, default_attributes);
- }
-}
-
-#else
-
-void OutputString(const std::string& output,
- TextDecoration dec,
- HtmlEscaping escaping) {
- EnsureInitialized();
- if (is_markdown) {
- OutputMarkdownDec(dec);
- } else if (is_console) {
- switch (dec) {
- case DECORATION_NONE:
- break;
- case DECORATION_DIM:
- WriteToStdOut("\e[2m");
- break;
- case DECORATION_RED:
- WriteToStdOut("\e[31m\e[1m");
- break;
- case DECORATION_GREEN:
- WriteToStdOut("\e[32m");
- break;
- case DECORATION_BLUE:
- WriteToStdOut("\e[34m\e[1m");
- break;
- case DECORATION_YELLOW:
- WriteToStdOut("\e[33m");
- break;
- }
- }
-
- std::string tmpstr = output;
- if (is_markdown && dec == DECORATION_YELLOW) {
- // https://code.google.com/p/gitiles/issues/detail?id=77
- // Gitiles will replace "--" with an em dash in non-code text.
- // Figuring out all instances of this might be difficult, but we can
- // at least escape the instances where this shows up in a heading.
- base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
- }
- if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
- // Markdown auto-escapes < and > in code sections (and converts &lt; to
- // &amp;tl; there), but not elsewhere.
- base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
- base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
- }
- WriteToStdOut(tmpstr.data());
-
- if (is_markdown) {
- OutputMarkdownDec(dec);
- } else if (is_console && dec != DECORATION_NONE) {
- WriteToStdOut("\e[0m");
- }
-}
-
-#endif
-
-void PrintSectionHelp(const std::string& line,
- const std::string& topic,
- const std::string& tag) {
- EnsureInitialized();
-
- if (is_markdown) {
- OutputString("* [" + line + "](#" + tag + ")\n");
- } else if (topic.size()) {
- OutputString("\n" + line + " (type \"gn help " + topic +
- "\" for more help):\n");
- } else {
- OutputString("\n" + line + ":\n");
- }
-}
-
-void PrintShortHelp(const std::string& line, const std::string& link_tag) {
- EnsureInitialized();
-
- if (is_markdown) {
- if (link_tag.empty())
- OutputString(" * " + line + "\n");
- else
- OutputString(" * [" + line + "](#" + link_tag + ")\n");
- return;
- }
-
- size_t colon_offset = line.find(':');
- size_t first_normal = 0;
- if (colon_offset != std::string::npos) {
- OutputString(" " + line.substr(0, colon_offset), DECORATION_YELLOW);
- first_normal = colon_offset;
- }
-
- // See if the colon is followed by a " [" and if so, dim the contents of [ ].
- if (first_normal > 0 && line.size() > first_normal + 2 &&
- line[first_normal + 1] == ' ' && line[first_normal + 2] == '[') {
- size_t begin_bracket = first_normal + 2;
- OutputString(": ");
- first_normal = line.find(']', begin_bracket);
- if (first_normal == std::string::npos)
- first_normal = line.size();
- else
- first_normal++;
- OutputString(line.substr(begin_bracket, first_normal - begin_bracket),
- DECORATION_DIM);
- }
-
- OutputString(line.substr(first_normal) + "\n");
-}
-
-void PrintLongHelp(const std::string& text, const std::string& tag) {
- EnsureInitialized();
-
- bool first_header = true;
- in_body = false;
- std::size_t empty_lines = 0;
- for (const std::string& line : base::SplitString(
- text, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
- // Check for a heading line.
- if (!line.empty() && line[0] != ' ') {
- // New paragraph, just skip any trailing empty lines.
- empty_lines = 0;
-
- if (is_markdown) {
- // GN's block-level formatting is converted to markdown as follows:
- // * The first heading is treated as an H3.
- // * Subsequent heading are treated as H4s.
- // * Any other text is wrapped in a code block and displayed as-is.
- //
- // Span-level formatting (the decorations) is converted inside
- // OutputString().
- if (in_body) {
- OutputString("```\n\n", DECORATION_NONE);
- in_body = false;
- }
-
- if (first_header && !tag.empty()) {
- OutputString("### <a name=\"" + tag + "\"></a>", DECORATION_NONE,
- NO_ESCAPING);
- first_header = false;
- } else {
- OutputString("#### ", DECORATION_NONE);
- }
- }
-
- // Highlight up to the colon (if any).
- size_t chars_to_highlight = line.find(':');
- if (chars_to_highlight == std::string::npos)
- chars_to_highlight = line.size();
-
- OutputString(line.substr(0, chars_to_highlight), DECORATION_YELLOW);
- OutputString(line.substr(chars_to_highlight));
- OutputString("\n");
- continue;
- } else if (is_markdown && !line.empty() && !in_body) {
- OutputString("```\n", DECORATION_NONE);
- in_body = true;
- }
-
- // We buffer empty lines, so we can skip them if needed
- // (i.e. new paragraph body, end of final paragraph body).
- if (in_body && is_markdown) {
- if (!line.empty() && empty_lines != 0) {
- OutputString(std::string(empty_lines, '\n'));
- empty_lines = 0;
- } else if (line.empty()) {
- ++empty_lines;
- continue;
- }
- }
-
- // Check for a comment.
- TextDecoration dec = DECORATION_NONE;
- for (const auto& elem : line) {
- if (elem == '#' && !is_markdown) {
- // Got a comment, draw dimmed.
- dec = DECORATION_DIM;
- break;
- } else if (elem != ' ') {
- break;
- }
- }
-
- OutputString(line + "\n", dec);
- }
-
- if (is_markdown && in_body)
- OutputString("```\n");
-}
diff --git a/gn/tools/gn/string_utils.cc b/gn/tools/gn/string_utils.cc
deleted file mode 100644
index 89fc4b0c9fb..00000000000
--- a/gn/tools/gn/string_utils.cc
+++ /dev/null
@@ -1,347 +0,0 @@
-// 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 "tools/gn/string_utils.h"
-
-#include <stddef.h>
-#include <cctype>
-
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/err.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/token.h"
-#include "tools/gn/tokenizer.h"
-#include "tools/gn/value.h"
-
-namespace {
-
-// Constructs an Err indicating a range inside a string. We assume that the
-// token has quotes around it that are not counted by the offset.
-Err ErrInsideStringToken(const Token& token,
- size_t offset,
- size_t size,
- const std::string& msg,
- const std::string& help = std::string()) {
- // The "+1" is skipping over the " at the beginning of the token.
- int int_offset = static_cast<int>(offset);
- Location begin_loc(token.location().file(), token.location().line_number(),
- token.location().column_number() + int_offset + 1,
- token.location().byte() + int_offset + 1);
- Location end_loc(
- token.location().file(), token.location().line_number(),
- token.location().column_number() + int_offset + 1 +
- static_cast<int>(size),
- token.location().byte() + int_offset + 1 + static_cast<int>(size));
- return Err(LocationRange(begin_loc, end_loc), msg, help);
-}
-
-// Notes about expression interpolation. This is based loosly on Dart but is
-// slightly less flexible. In Dart, seeing the ${ in a string is something
-// the toplevel parser knows about, and it will recurse into the block
-// treating it as a first-class {...} block. So even things like this work:
-// "hello ${"foo}"*2+"bar"}" => "hello foo}foo}bar"
-// (you can see it did not get confused by the nested strings or the nested "}"
-// inside the block).
-//
-// This is cool but complicates the parser for almost no benefit for this
-// non-general-purpose programming language. The main reason expressions are
-// supported here at all are to support "${scope.variable}" and "${list[0]}",
-// neither of which have any of these edge-cases.
-//
-// In this simplified approach, we search for the terminating '}' and execute
-// the result. This means we can't support any expressions with embedded '}'
-// or '"'. To keep people from getting confusing about what's supported and
-// what's not, only identifier and accessor expressions are allowed (neither
-// of these run into any of these edge-cases).
-bool AppendInterpolatedExpression(Scope* scope,
- const Token& token,
- const char* input,
- size_t begin_offset,
- size_t end_offset,
- std::string* output,
- Err* err) {
- SourceFile empty_source_file; // Prevent most vexing parse.
- InputFile input_file(empty_source_file);
- input_file.SetContents(
- std::string(&input[begin_offset], end_offset - begin_offset));
-
- // Tokenize.
- std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
- if (err->has_error()) {
- // The error will point into our temporary buffer, rewrite it to refer
- // to the original token. This will make the location information less
- // precise, but generally there won't be complicated things in string
- // interpolations.
- *err = ErrInsideStringToken(token, begin_offset, end_offset - begin_offset,
- err->message(), err->help_text());
- return false;
- }
-
- // Parse.
- std::unique_ptr<ParseNode> node = Parser::ParseExpression(tokens, err);
- if (err->has_error()) {
- // Rewrite error as above.
- *err = ErrInsideStringToken(token, begin_offset, end_offset - begin_offset,
- err->message(), err->help_text());
- return false;
- }
- if (!(node->AsIdentifier() || node->AsAccessor())) {
- *err = ErrInsideStringToken(
- token, begin_offset, end_offset - begin_offset,
- "Invalid string interpolation.",
- "The thing inside the ${} must be an identifier ${foo},\n"
- "a scope access ${foo.bar}, or a list access ${foo[0]}.");
- return false;
- }
-
- // Evaluate.
- Value result = node->Execute(scope, err);
- if (err->has_error()) {
- // Rewrite error as above.
- *err = ErrInsideStringToken(token, begin_offset, end_offset - begin_offset,
- err->message(), err->help_text());
- return false;
- }
-
- output->append(result.ToString(false));
- return true;
-}
-
-bool AppendInterpolatedIdentifier(Scope* scope,
- const Token& token,
- const char* input,
- size_t begin_offset,
- size_t end_offset,
- std::string* output,
- Err* err) {
- base::StringPiece identifier(&input[begin_offset], end_offset - begin_offset);
- const Value* value = scope->GetValue(identifier, true);
- if (!value) {
- // We assume the input points inside the token.
- *err = ErrInsideStringToken(
- token, identifier.data() - token.value().data() - 1, identifier.size(),
- "Undefined identifier in string expansion.",
- std::string("\"") + identifier + "\" is not currently in scope.");
- return false;
- }
-
- output->append(value->ToString(false));
- return true;
-}
-
-// Handles string interpolations: $identifier and ${expression}
-//
-// |*i| is the index into |input| after the $. This will be updated to point to
-// the last character consumed on success. The token is the original string
-// to blame on failure.
-//
-// On failure, returns false and sets the error. On success, appends the
-// result of the interpolation to |*output|.
-bool AppendStringInterpolation(Scope* scope,
- const Token& token,
- const char* input,
- size_t size,
- size_t* i,
- std::string* output,
- Err* err) {
- size_t dollars_index = *i - 1;
-
- if (input[*i] == '{') {
- // Bracketed expression.
- (*i)++;
- size_t begin_offset = *i;
-
- // Find the closing } and check for non-identifier chars. Don't need to
- // bother checking for the more-restricted first character of an identifier
- // since the {} unambiguously denotes the range, and identifiers with
- // invalid names just won't be found later.
- bool has_non_ident_chars = false;
- while (*i < size && input[*i] != '}') {
- has_non_ident_chars |= Tokenizer::IsIdentifierContinuingChar(input[*i]);
- (*i)++;
- }
- if (*i == size) {
- *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index,
- "Unterminated ${...");
- return false;
- }
-
- // In the common case, the thing inside the {} will actually be a
- // simple identifier. Avoid all the complicated parsing of accessors
- // in this case.
- if (!has_non_ident_chars) {
- return AppendInterpolatedIdentifier(scope, token, input, begin_offset, *i,
- output, err);
- }
- return AppendInterpolatedExpression(scope, token, input, begin_offset, *i,
- output, err);
- }
-
- // Simple identifier.
- // The first char of an identifier is more restricted.
- if (!Tokenizer::IsIdentifierFirstChar(input[*i])) {
- *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index + 1,
- "$ not followed by an identifier char.",
- "It you want a literal $ use \"\\$\".");
- return false;
- }
- size_t begin_offset = *i;
- (*i)++;
-
- // Find the first non-identifier char following the string.
- while (*i < size && Tokenizer::IsIdentifierContinuingChar(input[*i]))
- (*i)++;
- size_t end_offset = *i;
- (*i)--; // Back up to mark the last character consumed.
- return AppendInterpolatedIdentifier(scope, token, input, begin_offset,
- end_offset, output, err);
-}
-
-// Handles a hex literal: $0xFF
-//
-// |*i| is the index into |input| after the $. This will be updated to point to
-// the last character consumed on success. The token is the original string
-// to blame on failure.
-//
-// On failure, returns false and sets the error. On success, appends the
-// char with the given hex value to |*output|.
-bool AppendHexByte(Scope* scope,
- const Token& token,
- const char* input,
- size_t size,
- size_t* i,
- std::string* output,
- Err* err) {
- size_t dollars_index = *i - 1;
- // "$0" is already known to exist.
- if (*i + 3 >= size || input[*i + 1] != 'x' || !std::isxdigit(input[*i + 2]) ||
- !std::isxdigit(input[*i + 3])) {
- *err = ErrInsideStringToken(
- token, dollars_index, *i - dollars_index + 1,
- "Invalid hex character. Hex values must look like 0xFF.");
- return false;
- }
- int value = 0;
- if (!base::HexStringToInt(base::StringPiece(&input[*i + 2], 2), &value)) {
- *err = ErrInsideStringToken(token, dollars_index, *i - dollars_index + 1,
- "Could not convert hex value.");
- return false;
- }
- *i += 3;
- output->push_back(value);
- return true;
-}
-
-} // namespace
-
-bool ExpandStringLiteral(Scope* scope,
- const Token& literal,
- Value* result,
- Err* err) {
- DCHECK(literal.type() == Token::STRING);
- DCHECK(literal.value().size() > 1); // Should include quotes.
- DCHECK(result->type() == Value::STRING); // Should be already set.
-
- // The token includes the surrounding quotes, so strip those off.
- const char* input = &literal.value().data()[1];
- size_t size = literal.value().size() - 2;
-
- std::string& output = result->string_value();
- output.reserve(size);
- for (size_t i = 0; i < size; i++) {
- if (input[i] == '\\') {
- if (i < size - 1) {
- switch (input[i + 1]) {
- case '\\':
- case '"':
- case '$':
- output.push_back(input[i + 1]);
- i++;
- continue;
- default: // Everything else has no meaning: pass the literal.
- break;
- }
- }
- output.push_back(input[i]);
- } else if (input[i] == '$') {
- i++;
- if (i == size) {
- *err = ErrInsideStringToken(
- literal, i - 1, 1, "$ at end of string.",
- "I was expecting an identifier, 0xFF, or {...} after the $.");
- return false;
- }
- if (input[i] == '0') {
- if (!AppendHexByte(scope, literal, input, size, &i, &output, err))
- return false;
- } else if (!AppendStringInterpolation(scope, literal, input, size, &i,
- &output, err))
- return false;
- } else {
- output.push_back(input[i]);
- }
- }
- return true;
-}
-
-size_t EditDistance(const base::StringPiece& s1,
- const base::StringPiece& s2,
- size_t max_edit_distance) {
- // The algorithm implemented below is the "classic"
- // dynamic-programming algorithm for computing the Levenshtein
- // distance, which is described here:
- //
- // http://en.wikipedia.org/wiki/Levenshtein_distance
- //
- // Although the algorithm is typically described using an m x n
- // array, only one row plus one element are used at a time, so this
- // implementation just keeps one vector for the row. To update one entry,
- // only the entries to the left, top, and top-left are needed. The left
- // entry is in row[x-1], the top entry is what's in row[x] from the last
- // iteration, and the top-left entry is stored in previous.
- size_t m = s1.size();
- size_t n = s2.size();
-
- std::vector<size_t> row(n + 1);
- for (size_t i = 1; i <= n; ++i)
- row[i] = i;
-
- for (size_t y = 1; y <= m; ++y) {
- row[0] = y;
- size_t best_this_row = row[0];
-
- size_t previous = y - 1;
- for (size_t x = 1; x <= n; ++x) {
- size_t old_row = row[x];
- row[x] = std::min(previous + (s1[y - 1] == s2[x - 1] ? 0u : 1u),
- std::min(row[x - 1], row[x]) + 1u);
- previous = old_row;
- best_this_row = std::min(best_this_row, row[x]);
- }
-
- if (max_edit_distance && best_this_row > max_edit_distance)
- return max_edit_distance + 1;
- }
-
- return row[n];
-}
-
-base::StringPiece SpellcheckString(
- const base::StringPiece& text,
- const std::vector<base::StringPiece>& words) {
- const size_t kMaxValidEditDistance = 3u;
-
- size_t min_distance = kMaxValidEditDistance + 1u;
- base::StringPiece result;
- for (base::StringPiece word : words) {
- size_t distance = EditDistance(word, text, kMaxValidEditDistance);
- if (distance < min_distance) {
- min_distance = distance;
- result = word;
- }
- }
- return result;
-}
diff --git a/gn/tools/gn/string_utils.h b/gn/tools/gn/string_utils.h
deleted file mode 100644
index 744714a754a..00000000000
--- a/gn/tools/gn/string_utils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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 TOOLS_GN_STRING_UTILS_H_
-#define TOOLS_GN_STRING_UTILS_H_
-
-#include <vector>
-
-#include "base/strings/string_piece.h"
-
-class Err;
-class Scope;
-class Token;
-class Value;
-
-inline std::string operator+(const std::string& a, const base::StringPiece& b) {
- std::string ret;
- ret.reserve(a.size() + b.size());
- ret.assign(a);
- ret.append(b.data(), b.size());
- return ret;
-}
-
-inline std::string operator+(const base::StringPiece& a, const std::string& b) {
- std::string ret;
- ret.reserve(a.size() + b.size());
- ret.assign(a.data(), a.size());
- ret.append(b);
- return ret;
-}
-
-// Unescapes and expands variables in the given literal, writing the result
-// to the given value. On error, sets |err| and returns false.
-bool ExpandStringLiteral(Scope* scope,
- const Token& literal,
- Value* result,
- Err* err);
-
-// Returns the minimum number of inserts, deleted, and replacements of
-// characters needed to transform s1 to s2, or max_edit_distance + 1 if
-// transforming s1 into s2 isn't possible in at most max_edit_distance steps.
-size_t EditDistance(const base::StringPiece& s1,
- const base::StringPiece& s2,
- size_t max_edit_distance);
-
-// Given a string |text| and a vector of correctly-spelled strings |words|,
-// returns the first string in |words| closest to |text|, or an empty
-// StringPiece if none of the strings in |words| is close.
-base::StringPiece SpellcheckString(const base::StringPiece& text,
- const std::vector<base::StringPiece>& words);
-
-#endif // TOOLS_GN_STRING_UTILS_H_
diff --git a/gn/tools/gn/string_utils_unittest.cc b/gn/tools/gn/string_utils_unittest.cc
deleted file mode 100644
index bf10dd49603..00000000000
--- a/gn/tools/gn/string_utils_unittest.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-// 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 "tools/gn/string_utils.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/err.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/token.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-namespace {
-
-bool CheckExpansionCase(const char* input, const char* expected, bool success) {
- Scope scope(static_cast<const Settings*>(nullptr));
- int64_t one = 1;
- scope.SetValue("one", Value(nullptr, one), nullptr);
- scope.SetValue("onestring", Value(nullptr, "one"), nullptr);
-
- // Nested scope called "onescope" with a value "one" inside it.
- std::unique_ptr<Scope> onescope =
- std::make_unique<Scope>(static_cast<const Settings*>(nullptr));
- onescope->SetValue("one", Value(nullptr, one), nullptr);
- scope.SetValue("onescope", Value(nullptr, std::move(onescope)), nullptr);
-
- // List called "onelist" with one value that maps to 1.
- Value onelist(nullptr, Value::LIST);
- onelist.list_value().push_back(Value(nullptr, one));
- scope.SetValue("onelist", onelist, nullptr);
-
- // Construct the string token, which includes the quotes.
- std::string literal_string;
- literal_string.push_back('"');
- literal_string.append(input);
- literal_string.push_back('"');
- Token literal(Location(), Token::STRING, literal_string);
-
- Value result(nullptr, Value::STRING);
- Err err;
- bool ret = ExpandStringLiteral(&scope, literal, &result, &err);
-
- // Err and return value should agree.
- EXPECT_NE(ret, err.has_error());
-
- if (ret != success)
- return false;
-
- if (!success)
- return true; // Don't check result on failure.
- return result.string_value() == expected;
-}
-
-} // namespace
-
-TEST(StringUtils, ExpandStringLiteralIdentifier) {
- EXPECT_TRUE(CheckExpansionCase("", "", true));
- EXPECT_TRUE(CheckExpansionCase("hello", "hello", true));
- EXPECT_TRUE(CheckExpansionCase("hello #$one", "hello #1", true));
- EXPECT_TRUE(CheckExpansionCase("hello #$one/two", "hello #1/two", true));
- EXPECT_TRUE(CheckExpansionCase("hello #${one}", "hello #1", true));
- EXPECT_TRUE(CheckExpansionCase("hello #${one}one", "hello #1one", true));
- EXPECT_TRUE(CheckExpansionCase("hello #${one}$one", "hello #11", true));
- EXPECT_TRUE(CheckExpansionCase("$onestring${one}$one", "one11", true));
- EXPECT_TRUE(CheckExpansionCase("$onescope", "{\n one = 1\n}", true));
- EXPECT_TRUE(CheckExpansionCase("$onelist", "[1]", true));
-
- // Hex values
- EXPECT_TRUE(CheckExpansionCase("$0x0AA",
- "\x0A"
- "A",
- true));
- EXPECT_TRUE(CheckExpansionCase("$0x0a$0xfF", "\x0A\xFF", true));
-
- // Errors
- EXPECT_TRUE(CheckExpansionCase("hello #$", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hello #$%", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hello #${", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hello #${}", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hello #$nonexistant", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hello #${unterminated", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hex truncated: $0", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hex truncated: $0x", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hex truncated: $0x0", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hex with bad char: $0a", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hex with bad char: $0x1z", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("hex with bad char: $0xz1", nullptr, false));
-
- // Unknown backslash values aren't special.
- EXPECT_TRUE(CheckExpansionCase("\\", "\\", true));
- EXPECT_TRUE(CheckExpansionCase("\\b", "\\b", true));
-
- // Backslashes escape some special things. \"\$\\ -> "$\ Note that gtest
- // doesn't like this escape sequence so we have to put it out-of-line.
- const char* in = "\\\"\\$\\\\";
- const char* out = "\"$\\";
- EXPECT_TRUE(CheckExpansionCase(in, out, true));
-}
-
-TEST(StringUtils, ExpandStringLiteralExpression) {
- // Accessing the scope.
- EXPECT_TRUE(CheckExpansionCase("hello #${onescope.one}", "hello #1", true));
- EXPECT_TRUE(CheckExpansionCase("hello #${onescope.two}", nullptr, false));
-
- // Accessing the list.
- EXPECT_TRUE(CheckExpansionCase("hello #${onelist[0]}", "hello #1", true));
- EXPECT_TRUE(CheckExpansionCase("hello #${onelist[1]}", nullptr, false));
-
- // Trying some other (otherwise valid) expressions should fail.
- EXPECT_TRUE(CheckExpansionCase("${1 + 2}", nullptr, false));
- EXPECT_TRUE(CheckExpansionCase("${print(1)}", nullptr, false));
-}
-
-TEST(StringUtils, EditDistance) {
- EXPECT_EQ(3u, EditDistance("doom melon", "dune melon", 100));
- EXPECT_EQ(2u, EditDistance("doom melon", "dune melon", 1));
-
- EXPECT_EQ(2u, EditDistance("ab", "ba", 100));
- EXPECT_EQ(2u, EditDistance("ba", "ab", 100));
-
- EXPECT_EQ(2u, EditDistance("ananas", "banana", 100));
- EXPECT_EQ(2u, EditDistance("banana", "ananas", 100));
-
- EXPECT_EQ(2u, EditDistance("unclear", "nuclear", 100));
- EXPECT_EQ(2u, EditDistance("nuclear", "unclear", 100));
-
- EXPECT_EQ(3u, EditDistance("chrome", "chromium", 100));
- EXPECT_EQ(3u, EditDistance("chromium", "chrome", 100));
-
- EXPECT_EQ(4u, EditDistance("", "abcd", 100));
- EXPECT_EQ(4u, EditDistance("abcd", "", 100));
-
- EXPECT_EQ(4u, EditDistance("xxx", "xxxxxxx", 100));
- EXPECT_EQ(4u, EditDistance("xxxxxxx", "xxx", 100));
-
- EXPECT_EQ(7u, EditDistance("yyy", "xxxxxxx", 100));
- EXPECT_EQ(7u, EditDistance("xxxxxxx", "yyy", 100));
-}
-
-TEST(StringUtils, SpellcheckString) {
- std::vector<base::StringPiece> words;
- words.push_back("your");
- words.push_back("bravado");
- words.push_back("won\'t");
- words.push_back("help");
- words.push_back("you");
- words.push_back("now");
-
- EXPECT_EQ("help", SpellcheckString("halp", words));
-
- // barbados has an edit distance of 4 from bravado, so there's no suggestion.
- EXPECT_TRUE(SpellcheckString("barbados", words).empty());
-}
diff --git a/gn/tools/gn/substitution_list.cc b/gn/tools/gn/substitution_list.cc
deleted file mode 100644
index b588dad84be..00000000000
--- a/gn/tools/gn/substitution_list.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 "tools/gn/substitution_list.h"
-
-#include <stddef.h>
-#include <string.h>
-
-#include "tools/gn/value.h"
-
-SubstitutionList::SubstitutionList() = default;
-
-SubstitutionList::SubstitutionList(const SubstitutionList& other) = default;
-
-SubstitutionList::~SubstitutionList() = default;
-
-bool SubstitutionList::Parse(const Value& value, Err* err) {
- if (!value.VerifyTypeIs(Value::LIST, err))
- return false;
-
- const std::vector<Value>& input_list = value.list_value();
- list_.resize(input_list.size());
- for (size_t i = 0; i < input_list.size(); i++) {
- if (!list_[i].Parse(input_list[i], err))
- return false;
- }
-
- SubstitutionBits bits;
- FillRequiredTypes(&bits);
- bits.FillVector(&required_types_);
- return true;
-}
-
-bool SubstitutionList::Parse(const std::vector<std::string>& values,
- const ParseNode* origin,
- Err* err) {
- list_.resize(values.size());
- for (size_t i = 0; i < values.size(); i++) {
- if (!list_[i].Parse(values[i], origin, err))
- return false;
- }
-
- SubstitutionBits bits;
- FillRequiredTypes(&bits);
- bits.FillVector(&required_types_);
- return true;
-}
-
-SubstitutionList SubstitutionList::MakeForTest(const char* a,
- const char* b,
- const char* c) {
- std::vector<std::string> input_strings;
- input_strings.push_back(a);
- if (b)
- input_strings.push_back(b);
- if (c)
- input_strings.push_back(c);
-
- Err err;
- SubstitutionList result;
- result.Parse(input_strings, nullptr, &err);
- return result;
-}
-
-void SubstitutionList::FillRequiredTypes(SubstitutionBits* bits) const {
- for (const auto& item : list_)
- item.FillRequiredTypes(bits);
-}
diff --git a/gn/tools/gn/substitution_list.h b/gn/tools/gn/substitution_list.h
deleted file mode 100644
index e8614eb5200..00000000000
--- a/gn/tools/gn/substitution_list.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 TOOLS_GN_SUBSTITUTION_LIST_H_
-#define TOOLS_GN_SUBSTITUTION_LIST_H_
-
-#include <string>
-#include <vector>
-
-#include "tools/gn/substitution_pattern.h"
-
-// Represents a list of strings with {{substitution_patterns}} in them.
-class SubstitutionList {
- public:
- SubstitutionList();
- SubstitutionList(const SubstitutionList& other);
- ~SubstitutionList();
-
- bool Parse(const Value& value, Err* err);
- bool Parse(const std::vector<std::string>& values,
- const ParseNode* origin,
- Err* err);
-
- // Makes a SubstitutionList from the given hardcoded patterns.
- static SubstitutionList MakeForTest(const char* a,
- const char* b = nullptr,
- const char* c = nullptr);
-
- const std::vector<SubstitutionPattern>& list() const { return list_; }
-
- // Returns a list of all substitution types used by the patterns in this
- // list, with the exception of LITERAL.
- const std::vector<const Substitution*>& required_types() const {
- return required_types_;
- }
-
- void FillRequiredTypes(SubstitutionBits* bits) const;
-
- private:
- std::vector<SubstitutionPattern> list_;
-
- std::vector<const Substitution*> required_types_;
-};
-
-#endif // TOOLS_GN_SUBSTITUTION_LIST_H_
diff --git a/gn/tools/gn/substitution_pattern.cc b/gn/tools/gn/substitution_pattern.cc
deleted file mode 100644
index b02532d1696..00000000000
--- a/gn/tools/gn/substitution_pattern.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// 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 "tools/gn/substitution_pattern.h"
-
-#include <stddef.h>
-
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/value.h"
-
-SubstitutionPattern::Subrange::Subrange() : type(&SubstitutionLiteral) {}
-
-SubstitutionPattern::Subrange::Subrange(const Substitution* t,
- const std::string& l)
- : type(t), literal(l) {}
-
-SubstitutionPattern::Subrange::~Subrange() = default;
-
-SubstitutionPattern::SubstitutionPattern() : origin_(nullptr) {}
-
-SubstitutionPattern::SubstitutionPattern(const SubstitutionPattern& other) =
- default;
-
-SubstitutionPattern::~SubstitutionPattern() = default;
-
-bool SubstitutionPattern::Parse(const Value& value, Err* err) {
- if (!value.VerifyTypeIs(Value::STRING, err))
- return false;
- return Parse(value.string_value(), value.origin(), err);
-}
-
-bool SubstitutionPattern::Parse(const std::string& str,
- const ParseNode* origin,
- Err* err) {
- DCHECK(ranges_.empty()); // Should only be called once.
-
- size_t cur = 0;
- while (true) {
- size_t next = str.find("{{", cur);
-
- // Pick up everything from the previous spot to here as a literal.
- if (next == std::string::npos) {
- if (cur != str.size())
- ranges_.push_back(Subrange(&SubstitutionLiteral, str.substr(cur)));
- break;
- } else if (next > cur) {
- ranges_.push_back(
- Subrange(&SubstitutionLiteral, str.substr(cur, next - cur)));
- }
-
- // Find which specific pattern this corresponds to.
- bool found_match = false;
- for (const SubstitutionTypes* types : AllSubstitutions) {
- for (const Substitution* sub : *types) {
- const char* cur_pattern = sub->name;
- size_t cur_len = strlen(cur_pattern);
- if (str.compare(next, cur_len, cur_pattern) == 0) {
- ranges_.push_back(Subrange(sub));
- cur = next + cur_len;
- found_match = true;
- break;
- }
- }
- }
-
- // Expect all occurrances of {{ to resolve to a pattern.
- if (!found_match) {
- // Could make this error message more friendly if it comes up a lot. But
- // most people will not be writing substitution patterns and the code
- // to exactly indicate the error location is tricky.
- *err = Err(origin, "Unknown substitution pattern",
- "Found a {{ at offset " + base::NumberToString(next) +
- " and did not find a known substitution following it.");
- ranges_.clear();
- return false;
- }
- }
-
- origin_ = origin;
-
- // Fill required types vector.
- SubstitutionBits bits;
- FillRequiredTypes(&bits);
- bits.FillVector(&required_types_);
- return true;
-}
-
-// static
-SubstitutionPattern SubstitutionPattern::MakeForTest(const char* str) {
- Err err;
- SubstitutionPattern pattern;
- CHECK(pattern.Parse(str, nullptr, &err)) << err.message();
- return pattern;
-}
-
-std::string SubstitutionPattern::AsString() const {
- std::string result;
- for (const auto& elem : ranges_) {
- if (elem.type == &SubstitutionLiteral)
- result.append(elem.literal);
- else
- result.append(elem.type->name);
- }
- return result;
-}
-
-void SubstitutionPattern::FillRequiredTypes(SubstitutionBits* bits) const {
- for (const auto& elem : ranges_) {
- if (elem.type != &SubstitutionLiteral)
- bits->used.insert(elem.type);
- }
-}
-
-bool SubstitutionPattern::IsInOutputDir(const BuildSettings* build_settings,
- Err* err) const {
- if (ranges_.empty()) {
- *err = Err(origin_, "This is empty but I was expecting an output file.");
- return false;
- }
-
- if (ranges_[0].type == &SubstitutionLiteral) {
- // If the first thing is a literal, it must start with the output dir.
- if (!EnsureStringIsInOutputDir(build_settings->build_dir(),
- ranges_[0].literal, origin_, err))
- return false;
- } else {
- // Otherwise, the first subrange must be a pattern that expands to
- // something in the output directory.
- if (!SubstitutionIsInOutputDir(ranges_[0].type)) {
- *err =
- Err(origin_, "File is not inside output directory.",
- "The given file should be in the output directory. Normally you\n"
- "would specify\n\"$target_out_dir/foo\" or "
- "\"{{source_gen_dir}}/foo\".");
- return false;
- }
- }
-
- return true;
-}
diff --git a/gn/tools/gn/substitution_pattern.h b/gn/tools/gn/substitution_pattern.h
deleted file mode 100644
index 81bd89712a4..00000000000
--- a/gn/tools/gn/substitution_pattern.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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 TOOLS_GN_SUBSTITUTION_PATTERN_H_
-#define TOOLS_GN_SUBSTITUTION_PATTERN_H_
-
-#include <string>
-#include <vector>
-
-#include "tools/gn/substitution_type.h"
-
-class BuildSettings;
-class Err;
-class ParseNode;
-class Value;
-
-// Represents a string with {{substitution_patterns}} in them.
-class SubstitutionPattern {
- public:
- struct Subrange {
- Subrange();
- explicit Subrange(const Substitution* t, const std::string& l = std::string());
- ~Subrange();
-
- inline bool operator==(const Subrange& other) const {
- return type == other.type && literal == other.literal;
- }
-
- const Substitution* type;
-
- // When type_ == LITERAL, this specifies the literal.
- std::string literal;
- };
-
- SubstitutionPattern();
- SubstitutionPattern(const SubstitutionPattern& other);
- ~SubstitutionPattern();
-
- // Parses the given string and fills in the pattern. The pattern must only
- // be initialized once. On failure, returns false and sets the error.
- bool Parse(const Value& value, Err* err);
- bool Parse(const std::string& str, const ParseNode* origin, Err* err);
-
- // Makes a pattern given a hardcoded string. Will assert if the string is
- // not a valid pattern.
- static SubstitutionPattern MakeForTest(const char* str);
-
- // Returns the pattern as a string with substitutions in them.
- std::string AsString() const;
-
- // Sets the bits in the given vector corresponding to the substitutions used
- // by this pattern. SubstitutionLiteral is ignored.
- void FillRequiredTypes(SubstitutionBits* bits) const;
-
- // Checks whether this pattern resolves to something in the output directory
- // for the given build settings. If not, returns false and fills in the given
- // error.
- bool IsInOutputDir(const BuildSettings* build_settings, Err* err) const;
-
- // Returns a vector listing the substitutions used by this pattern, not
- // counting SubstitutionLiteral.
- const std::vector<const Substitution*>& required_types() const {
- return required_types_;
- }
-
- const std::vector<Subrange>& ranges() const { return ranges_; }
- bool empty() const { return ranges_.empty(); }
-
- const ParseNode* origin() const { return origin_; }
-
- private:
- std::vector<Subrange> ranges_;
- const ParseNode* origin_;
-
- std::vector<const Substitution*> required_types_;
-};
-
-#endif // TOOLS_GN_SUBSTITUTION_PATTERN_H_
diff --git a/gn/tools/gn/substitution_pattern_unittest.cc b/gn/tools/gn/substitution_pattern_unittest.cc
deleted file mode 100644
index 1a6498f71a1..00000000000
--- a/gn/tools/gn/substitution_pattern_unittest.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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 "tools/gn/substitution_pattern.h"
-
-#include "tools/gn/err.h"
-#include "tools/gn/rust_substitution_type.h"
-#include "util/test/test.h"
-
-TEST(SubstitutionPattern, ParseLiteral) {
- SubstitutionPattern pattern;
- Err err;
- EXPECT_TRUE(pattern.Parse("This is a literal", nullptr, &err));
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(1u, pattern.ranges().size());
- EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
- EXPECT_EQ("This is a literal", pattern.ranges()[0].literal);
-}
-
-TEST(SubstitutionPattern, ParseComplex) {
- SubstitutionPattern pattern;
- Err err;
- EXPECT_TRUE(pattern.Parse(
- "AA{{source}}{{source_name_part}}BB{{source_file_part}}", nullptr, &err));
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(5u, pattern.ranges().size());
-
- EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
- EXPECT_EQ("AA", pattern.ranges()[0].literal);
- EXPECT_EQ(&SubstitutionSource, pattern.ranges()[1].type);
- EXPECT_EQ(&SubstitutionSourceNamePart, pattern.ranges()[2].type);
- EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[3].type);
- EXPECT_EQ("BB", pattern.ranges()[3].literal);
- EXPECT_EQ(&SubstitutionSourceFilePart, pattern.ranges()[4].type);
-}
-
-TEST(SubstitutionPattern, ParseErrors) {
- SubstitutionPattern pattern;
- Err err;
- EXPECT_FALSE(pattern.Parse("AA{{source", nullptr, &err));
- EXPECT_TRUE(err.has_error());
-
- err = Err();
- EXPECT_FALSE(pattern.Parse("{{source_of_evil}}", nullptr, &err));
- EXPECT_TRUE(err.has_error());
-
- err = Err();
- EXPECT_FALSE(pattern.Parse("{{source{{source}}", nullptr, &err));
- EXPECT_TRUE(err.has_error());
-}
-
-TEST(SubstitutionPattern, ParseRust) {
- SubstitutionPattern pattern;
- Err err;
- EXPECT_TRUE(pattern.Parse(
- "AA{{rustflags}}{{rustenv}}BB{{crate_name}}{{rustdeps}}CC{{externs}}",
- nullptr, &err));
- EXPECT_FALSE(err.has_error());
- ASSERT_EQ(8u, pattern.ranges().size());
-
- EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
- EXPECT_EQ("AA", pattern.ranges()[0].literal);
- EXPECT_EQ(&kRustSubstitutionRustFlags, pattern.ranges()[1].type);
- EXPECT_EQ(&kRustSubstitutionRustEnv, pattern.ranges()[2].type);
- EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[3].type);
- EXPECT_EQ("BB", pattern.ranges()[3].literal);
- EXPECT_EQ(&kRustSubstitutionCrateName, pattern.ranges()[4].type);
- EXPECT_EQ(&kRustSubstitutionRustDeps, pattern.ranges()[5].type);
- EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[6].type);
- EXPECT_EQ("CC", pattern.ranges()[6].literal);
- EXPECT_EQ(&kRustSubstitutionExterns, pattern.ranges()[7].type);
-} \ No newline at end of file
diff --git a/gn/tools/gn/substitution_type.cc b/gn/tools/gn/substitution_type.cc
deleted file mode 100644
index 2f6e23e60a1..00000000000
--- a/gn/tools/gn/substitution_type.cc
+++ /dev/null
@@ -1,193 +0,0 @@
-// 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 "tools/gn/substitution_type.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "tools/gn/c_substitution_type.h"
-#include "tools/gn/err.h"
-#include "tools/gn/rust_substitution_type.h"
-
-const std::vector<SubstitutionTypes*> AllSubstitutions = {
- &GeneralSubstitutions, &CSubstitutions, &RustSubstitutions};
-
-const SubstitutionTypes GeneralSubstitutions = {
- &SubstitutionLiteral,
-
- &SubstitutionOutput,
- &SubstitutionLabel,
- &SubstitutionLabelName,
- &SubstitutionRootGenDir,
- &SubstitutionRootOutDir,
- &SubstitutionOutputDir,
- &SubstitutionTargetGenDir,
- &SubstitutionTargetOutDir,
- &SubstitutionTargetOutputName,
-
- &SubstitutionSource,
- &SubstitutionSourceNamePart,
- &SubstitutionSourceFilePart,
- &SubstitutionSourceDir,
- &SubstitutionSourceRootRelativeDir,
- &SubstitutionSourceGenDir,
- &SubstitutionSourceOutDir,
- &SubstitutionSourceTargetRelative,
-
- &SubstitutionBundleRootDir,
- &SubstitutionBundleContentsDir,
- &SubstitutionBundleResourcesDir,
- &SubstitutionBundleExecutableDir,
-
- &SubstitutionBundleProductType,
- &SubstitutionBundlePartialInfoPlist,
-
- &SubstitutionRspFileName,
-};
-
-const Substitution SubstitutionLiteral = {"<<literal>>", nullptr};
-
-const Substitution SubstitutionSource = {"{{source}}", "in"};
-const Substitution SubstitutionOutput = {"{{output}}", "out"};
-
-const Substitution SubstitutionSourceNamePart = {"{{source_name_part}}",
- "source_name_part"};
-const Substitution SubstitutionSourceFilePart = {"{{source_file_part}}",
- "source_file_part"};
-const Substitution SubstitutionSourceDir = {"{{source_dir}}", "source_dir"};
-const Substitution SubstitutionSourceRootRelativeDir = {
- "{{source_root_relative_dir}}", "source_root_relative_dir"};
-const Substitution SubstitutionSourceGenDir = {"{{source_gen_dir}}",
- "source_gen_dir"};
-const Substitution SubstitutionSourceOutDir = {"{{source_out_dir}}",
- "source_out_dir"};
-const Substitution SubstitutionSourceTargetRelative = {
- "{{source_target_relative}}", "source_target_relative"};
-
-// Valid for all compiler and linker tools. These depend on the target and
-// do not vary on a per-file basis.
-const Substitution SubstitutionLabel = {"{{label}}", "label"};
-const Substitution SubstitutionLabelName = {"{{label_name}}", "label_name"};
-const Substitution SubstitutionRootGenDir = {"{{root_gen_dir}}",
- "root_gen_dir"};
-const Substitution SubstitutionRootOutDir = {"{{root_out_dir}}",
- "root_out_dir"};
-const Substitution SubstitutionOutputDir = {"{{output_dir}}", "output_dir"};
-const Substitution SubstitutionTargetGenDir = {"{{target_gen_dir}}",
- "target_gen_dir"};
-const Substitution SubstitutionTargetOutDir = {"{{target_out_dir}}",
- "target_out_dir"};
-const Substitution SubstitutionTargetOutputName = {"{{target_output_name}}",
- "target_output_name"};
-
-// Valid for bundle_data targets.
-const Substitution SubstitutionBundleRootDir = {"{{bundle_root_dir}}",
- "bundle_root_dir"};
-const Substitution SubstitutionBundleContentsDir = {"{{bundle_contents_dir}}",
- "bundle_contents_dir"};
-const Substitution SubstitutionBundleResourcesDir = {"{{bundle_resources_dir}}",
- "bundle_resources_dir"};
-const Substitution SubstitutionBundleExecutableDir = {
- "{{bundle_executable_dir}}", "bundle_executable_dir"};
-
-// Valid for compile_xcassets tool.
-const Substitution SubstitutionBundleProductType = {"{{bundle_product_type}}",
- "product_type"};
-const Substitution SubstitutionBundlePartialInfoPlist = {
- "{{bundle_partial_info_plist}}", "partial_info_plist"};
-
-// Used only for the args of actions.
-const Substitution SubstitutionRspFileName = {"{{response_file_name}}",
- "rspfile"};
-
-SubstitutionBits::SubstitutionBits() = default;
-
-void SubstitutionBits::MergeFrom(const SubstitutionBits& other) {
- for (const Substitution* s : other.used)
- used.insert(s);
-}
-
-void SubstitutionBits::FillVector(
- std::vector<const Substitution*>* vect) const {
- for (const Substitution* s : used) {
- vect->push_back(s);
- }
-}
-
-bool SubstitutionIsInOutputDir(const Substitution* type) {
- return type == &SubstitutionSourceGenDir ||
- type == &SubstitutionSourceOutDir || type == &SubstitutionRootGenDir ||
- type == &SubstitutionRootOutDir || type == &SubstitutionTargetGenDir ||
- type == &SubstitutionTargetOutDir;
-}
-
-bool SubstitutionIsInBundleDir(const Substitution* type) {
- return type == &SubstitutionBundleRootDir ||
- type == &SubstitutionBundleContentsDir ||
- type == &SubstitutionBundleResourcesDir ||
- type == &SubstitutionBundleExecutableDir;
-}
-
-bool IsValidBundleDataSubstitution(const Substitution* type) {
- return type == &SubstitutionLiteral ||
- type == &SubstitutionSourceTargetRelative ||
- type == &SubstitutionSourceNamePart ||
- type == &SubstitutionSourceFilePart ||
- type == &SubstitutionSourceRootRelativeDir ||
- type == &SubstitutionBundleRootDir ||
- type == &SubstitutionBundleContentsDir ||
- type == &SubstitutionBundleResourcesDir ||
- type == &SubstitutionBundleExecutableDir;
-}
-
-bool IsValidSourceSubstitution(const Substitution* type) {
- return type == &SubstitutionLiteral || type == &SubstitutionSource ||
- type == &SubstitutionSourceNamePart ||
- type == &SubstitutionSourceFilePart ||
- type == &SubstitutionSourceDir ||
- type == &SubstitutionSourceRootRelativeDir ||
- type == &SubstitutionSourceGenDir ||
- type == &SubstitutionSourceOutDir ||
- type == &SubstitutionSourceTargetRelative;
-}
-
-bool IsValidScriptArgsSubstitution(const Substitution* type) {
- return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName;
-}
-
-bool IsValidToolSubstitution(const Substitution* type) {
- return type == &SubstitutionLiteral || type == &SubstitutionOutput ||
- type == &SubstitutionLabel || type == &SubstitutionLabelName ||
- type == &SubstitutionRootGenDir || type == &SubstitutionRootOutDir ||
- type == &SubstitutionTargetGenDir ||
- type == &SubstitutionTargetOutDir ||
- type == &SubstitutionTargetOutputName;
-}
-
-bool IsValidCopySubstitution(const Substitution* type) {
- return IsValidToolSubstitution(type) || type == &SubstitutionSource;
-}
-
-bool IsValidCompileXCassetsSubstitution(const Substitution* type) {
- return IsValidToolSubstitution(type) || type == &CSubstitutionLinkerInputs ||
- type == &SubstitutionBundleProductType ||
- type == &SubstitutionBundlePartialInfoPlist;
-}
-
-bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
- bool (*is_valid_subst)(const Substitution*),
- const ParseNode* origin,
- Err* err) {
- for (const Substitution* type : types) {
- if (!is_valid_subst(type)) {
- *err = Err(origin, "Invalid substitution type.",
- "The substitution " + std::string(type->name) +
- " isn't valid for something\n"
- "operating on a source file such as this.");
- return false;
- }
- }
- return true;
-}
diff --git a/gn/tools/gn/substitution_type.h b/gn/tools/gn/substitution_type.h
deleted file mode 100644
index 45e890c00eb..00000000000
--- a/gn/tools/gn/substitution_type.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// 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 TOOLS_GN_SUBSTITUTION_TYPE_H_
-#define TOOLS_GN_SUBSTITUTION_TYPE_H_
-
-#include <vector>
-
-#include "base/containers/flat_set.h"
-#include "base/macros.h"
-
-class Err;
-class ParseNode;
-
-// Each pair here represents the string representation of the substitution in GN
-// and in Ninja.
-struct Substitution {
- const char* name;
- const char* ninja_name;
- DISALLOW_COPY_AND_ASSIGN(Substitution);
-};
-
-using SubstitutionTypes = const std::vector<const Substitution*>;
-
-// All possible substitutions, organized into logical sets.
-extern const std::vector<SubstitutionTypes*> AllSubstitutions;
-
-// The set of substitutions available to all tools.
-extern const SubstitutionTypes GeneralSubstitutions;
-
-// Types of substitutions.
-extern const Substitution SubstitutionLiteral;
-
-// Valid for all tools. These depend on the target and
-// do not vary on a per-file basis.
-extern const Substitution SubstitutionOutput;
-extern const Substitution SubstitutionLabel;
-extern const Substitution SubstitutionLabelName;
-extern const Substitution SubstitutionRootGenDir;
-extern const Substitution SubstitutionRootOutDir;
-extern const Substitution SubstitutionOutputDir;
-extern const Substitution SubstitutionTargetGenDir;
-extern const Substitution SubstitutionTargetOutDir;
-extern const Substitution SubstitutionTargetOutputName;
-
-// Valid for all compiler tools.
-extern const Substitution SubstitutionSource;
-extern const Substitution SubstitutionSourceNamePart;
-extern const Substitution SubstitutionSourceFilePart;
-extern const Substitution SubstitutionSourceDir;
-extern const Substitution SubstitutionSourceRootRelativeDir;
-extern const Substitution SubstitutionSourceGenDir;
-extern const Substitution SubstitutionSourceOutDir;
-extern const Substitution SubstitutionSourceTargetRelative;
-
-// Valid for bundle_data targets.
-extern const Substitution SubstitutionBundleRootDir;
-extern const Substitution SubstitutionBundleContentsDir;
-extern const Substitution SubstitutionBundleResourcesDir;
-extern const Substitution SubstitutionBundleExecutableDir;
-
-// Valid for compile_xcassets tool.
-extern const Substitution SubstitutionBundleProductType;
-extern const Substitution SubstitutionBundlePartialInfoPlist;
-
-// Used only for the args of actions.
-extern const Substitution SubstitutionRspFileName;
-
-// A wrapper around an array if flags indicating whether a given substitution
-// type is required in some context. By convention, the LITERAL type bit is
-// not set.
-struct SubstitutionBits {
- SubstitutionBits();
-
- // Merges any bits set in the given "other" to this one. This object will
- // then be the union of all bits in the two lists.
- void MergeFrom(const SubstitutionBits& other);
-
- // Converts the substitution type set to a vector of the types listed. Does
- // not include SubstitutionLiteral.
- void FillVector(std::vector<const Substitution*>* vect) const;
-
- // This set depends on global uniqueness of pointers, and so all points in
- // this set should be the Substitution* constants.
- base::flat_set<const Substitution*> used;
-};
-
-// Returns true if the given substitution pattern references the output
-// directory. This is used to check strings that begin with a substitution to
-// verify that they produce a file in the output directory.
-bool SubstitutionIsInOutputDir(const Substitution* type);
-
-// Returns true if the given substitution pattern references the bundle
-// directory. This is used to check strings that begin with a substitution to
-// verify that they produce a file in the bundle directory.
-bool SubstitutionIsInBundleDir(const Substitution* type);
-
-// Returns true if the given substitution is valid for the named purpose.
-bool IsValidBundleDataSubstitution(const Substitution* type);
-bool IsValidSourceSubstitution(const Substitution* type);
-bool IsValidScriptArgsSubstitution(const Substitution* type);
-
-// Both compiler and linker tools.
-bool IsValidToolSubstitution(const Substitution* type);
-bool IsValidCopySubstitution(const Substitution* type);
-bool IsValidCompileXCassetsSubstitution(const Substitution* type);
-
-// Validates that each substitution type in the vector passes the given
-// is_valid_subst predicate. Returns true on success. On failure, fills in the
-// error object with an appropriate message and returns false.
-bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
- bool (*is_valid_subst)(const Substitution*),
- const ParseNode* origin,
- Err* err);
-
-#endif // TOOLS_GN_SUBSTITUTION_TYPE_H_
diff --git a/gn/tools/gn/substitution_writer.cc b/gn/tools/gn/substitution_writer.cc
deleted file mode 100644
index 41be9b4b9ec..00000000000
--- a/gn/tools/gn/substitution_writer.cc
+++ /dev/null
@@ -1,598 +0,0 @@
-// 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 "tools/gn/substitution_writer.h"
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/c_substitution_type.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/rust_substitution_type.h"
-#include "tools/gn/rust_tool.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/string_utils.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_pattern.h"
-#include "tools/gn/target.h"
-
-namespace {
-
-// Sets the given directory string to the destination, trimming any trailing
-// slash from the directory (SourceDirs and OutputFiles representing
-// directories will end in a trailing slash). If the directory is empty,
-// it will be replaced with a ".".
-void SetDirOrDotWithNoSlash(const std::string& dir, std::string* dest) {
- if (!dir.empty() && dir[dir.size() - 1] == '/')
- dest->assign(dir.data(), dir.size() - 1);
- else
- dest->assign(dir);
-
- if (dest->empty())
- dest->push_back('.');
-}
-
-} // namespace
-
-const char kSourceExpansion_Help[] =
- R"(How Source Expansion Works
-
- Source expansion is used for the action_foreach and copy target types to map
- source file names to output file names or arguments.
-
- To perform source expansion in the outputs, GN maps every entry in the
- sources to every entry in the outputs list, producing the cross product of
- all combinations, expanding placeholders (see below).
-
- Source expansion in the args works similarly, but performing the placeholder
- substitution produces a different set of arguments for each invocation of the
- script.
-
- If no placeholders are found, the outputs or args list will be treated as a
- static list of literal file names that do not depend on the sources.
-
- See "gn help copy" and "gn help action_foreach" for more on how this is
- applied.
-
-Placeholders
-
- This section discusses only placeholders for actions. There are other
- placeholders used in the definition of tools. See "gn help tool" for those.
-
- {{source}}
- The name of the source file including directory (*). This will generally
- be used for specifying inputs to a script in the "args" variable.
- "//foo/bar/baz.txt" => "../../foo/bar/baz.txt"
-
- {{source_file_part}}
- The file part of the source including the extension.
- "//foo/bar/baz.txt" => "baz.txt"
-
- {{source_name_part}}
- The filename part of the source file with no directory or extension. This
- will generally be used for specifying a transformation from a source file
- to a destination file with the same name but different extension.
- "//foo/bar/baz.txt" => "baz"
-
- {{source_dir}}
- The directory (*) containing the source file with no trailing slash.
- "//foo/bar/baz.txt" => "../../foo/bar"
-
- {{source_root_relative_dir}}
- The path to the source file's directory relative to the source root, with
- no leading "//" or trailing slashes. If the path is system-absolute,
- (beginning in a single slash) this will just return the path with no
- trailing slash. This value will always be the same, regardless of whether
- it appears in the "outputs" or "args" section.
- "//foo/bar/baz.txt" => "foo/bar"
-
- {{source_gen_dir}}
- The generated file directory (*) corresponding to the source file's path.
- This will be different than the target's generated file directory if the
- source file is in a different directory than the BUILD.gn file.
- "//foo/bar/baz.txt" => "gen/foo/bar"
-
- {{source_out_dir}}
- The object file directory (*) corresponding to the source file's path,
- relative to the build directory. this us be different than the target's
- out directory if the source file is in a different directory than the
- build.gn file.
- "//foo/bar/baz.txt" => "obj/foo/bar"
-
- {{source_target_relative}}
- The path to the source file relative to the target's directory. This will
- generally be used for replicating the source directory layout in the
- output directory. This can only be used in actions and bundle_data
- targets. It is an error to use in process_file_template where there is no
- "target".
- "//foo/bar/baz.txt" => "baz.txt"
-
-(*) Note on directories
-
- Paths containing directories (except the source_root_relative_dir) will be
- different depending on what context the expansion is evaluated in. Generally
- it should "just work" but it means you can't concatenate strings containing
- these values with reasonable results.
-
- Details: source expansions can be used in the "outputs" variable, the "args"
- variable, and in calls to "process_file_template". The "args" are passed to a
- script which is run from the build directory, so these directories will
- relative to the build directory for the script to find. In the other cases,
- the directories will be source- absolute (begin with a "//") because the
- results of those expansions will be handled by GN internally.
-
-Examples
-
- Non-varying outputs:
- action("hardcoded_outputs") {
- sources = [ "input1.idl", "input2.idl" ]
- outputs = [ "$target_out_dir/output1.dat",
- "$target_out_dir/output2.dat" ]
- }
- The outputs in this case will be the two literal files given.
-
- Varying outputs:
- action_foreach("varying_outputs") {
- sources = [ "input1.idl", "input2.idl" ]
- outputs = [ "{{source_gen_dir}}/{{source_name_part}}.h",
- "{{source_gen_dir}}/{{source_name_part}}.cc" ]
- }
- Performing source expansion will result in the following output names:
- //out/Debug/obj/mydirectory/input1.h
- //out/Debug/obj/mydirectory/input1.cc
- //out/Debug/obj/mydirectory/input2.h
- //out/Debug/obj/mydirectory/input2.cc
-)";
-
-// static
-void SubstitutionWriter::WriteWithNinjaVariables(
- const SubstitutionPattern& pattern,
- const EscapeOptions& escape_options,
- std::ostream& out) {
- // The result needs to be quoted as if it was one string, but the $ for
- // the inserted Ninja variables can't be escaped. So write to a buffer with
- // no quoting, and then quote the whole thing if necessary.
- EscapeOptions no_quoting(escape_options);
- no_quoting.inhibit_quoting = true;
-
- bool needs_quotes = false;
- std::string result;
- for (const auto& range : pattern.ranges()) {
- if (range.type == &SubstitutionLiteral) {
- result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
- } else {
- result.append("${");
- result.append(range.type->ninja_name);
- result.append("}");
- }
- }
-
- if (needs_quotes && !escape_options.inhibit_quoting)
- out << "\"" << result << "\"";
- else
- out << result;
-}
-
-// static
-void SubstitutionWriter::GetListAsSourceFiles(const SubstitutionList& list,
- std::vector<SourceFile>* output) {
- for (const auto& pattern : list.list()) {
- CHECK(pattern.ranges().size() == 1 &&
- pattern.ranges()[0].type == &SubstitutionLiteral)
- << "The substitution pattern \"" << pattern.AsString()
- << "\" was expected to be a literal with no {{substitutions}}.";
- const std::string& literal = pattern.ranges()[0].literal;
- CHECK(literal.size() >= 1 && literal[0] == '/')
- << "The result of the pattern \"" << pattern.AsString()
- << "\" was not an absolute path.";
- output->push_back(SourceFile(literal));
- }
-}
-
-// static
-void SubstitutionWriter::GetListAsOutputFiles(const Settings* settings,
- const SubstitutionList& list,
- std::vector<OutputFile>* output) {
- std::vector<SourceFile> output_as_sources;
- GetListAsSourceFiles(list, &output_as_sources);
- for (const auto& file : output_as_sources)
- output->push_back(OutputFile(settings->build_settings(), file));
-}
-
-// static
-SourceFile SubstitutionWriter::ApplyPatternToSource(
- const Target* target,
- const Settings* settings,
- const SubstitutionPattern& pattern,
- const SourceFile& source) {
- std::string result_value =
- ApplyPatternToSourceAsString(target, settings, pattern, source);
- CHECK(!result_value.empty() && result_value[0] == '/')
- << "The result of the pattern \"" << pattern.AsString()
- << "\" was not a path beginning in \"/\" or \"//\".";
- return SourceFile(std::move(result_value));
-}
-
-// static
-std::string SubstitutionWriter::ApplyPatternToSourceAsString(
- const Target* target,
- const Settings* settings,
- const SubstitutionPattern& pattern,
- const SourceFile& source) {
- std::string result_value;
- for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == &SubstitutionLiteral) {
- result_value.append(subrange.literal);
- } else {
- result_value.append(GetSourceSubstitution(target, settings, source,
- subrange.type, OUTPUT_ABSOLUTE,
- SourceDir()));
- }
- }
- return result_value;
-}
-
-// static
-OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
- const Target* target,
- const Settings* settings,
- const SubstitutionPattern& pattern,
- const SourceFile& source) {
- SourceFile result_as_source =
- ApplyPatternToSource(target, settings, pattern, source);
- return OutputFile(settings->build_settings(), result_as_source);
-}
-
-// static
-void SubstitutionWriter::ApplyListToSource(const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const SourceFile& source,
- std::vector<SourceFile>* output) {
- for (const auto& item : list.list())
- output->push_back(ApplyPatternToSource(target, settings, item, source));
-}
-
-// static
-void SubstitutionWriter::ApplyListToSourceAsString(
- const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const SourceFile& source,
- std::vector<std::string>* output) {
- for (const auto& item : list.list())
- output->push_back(
- ApplyPatternToSourceAsString(target, settings, item, source));
-}
-
-// static
-void SubstitutionWriter::ApplyListToSourceAsOutputFile(
- const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const SourceFile& source,
- std::vector<OutputFile>* output) {
- for (const auto& item : list.list())
- output->push_back(
- ApplyPatternToSourceAsOutputFile(target, settings, item, source));
-}
-
-// static
-void SubstitutionWriter::ApplyListToSources(
- const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const std::vector<SourceFile>& sources,
- std::vector<SourceFile>* output) {
- output->clear();
- for (const auto& source : sources)
- ApplyListToSource(target, settings, list, source, output);
-}
-
-// static
-void SubstitutionWriter::ApplyListToSourcesAsString(
- const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const std::vector<SourceFile>& sources,
- std::vector<std::string>* output) {
- output->clear();
- for (const auto& source : sources)
- ApplyListToSourceAsString(target, settings, list, source, output);
-}
-
-// static
-void SubstitutionWriter::ApplyListToSourcesAsOutputFile(
- const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const std::vector<SourceFile>& sources,
- std::vector<OutputFile>* output) {
- output->clear();
- for (const auto& source : sources)
- ApplyListToSourceAsOutputFile(target, settings, list, source, output);
-}
-
-// static
-void SubstitutionWriter::WriteNinjaVariablesForSource(
- const Target* target,
- const Settings* settings,
- const SourceFile& source,
- const std::vector<const Substitution*>& types,
- const EscapeOptions& escape_options,
- std::ostream& out) {
- for (const auto& type : types) {
- // Don't write SOURCE since that just maps to Ninja's $in variable, which
- // is implicit in the rule. RESPONSE_FILE_NAME is written separately
- // only when writing target rules since it can never be used in any
- // other context (like process_file_template).
- if (type != &SubstitutionSource && type != &SubstitutionRspFileName) {
- out << " " << type->ninja_name << " = ";
- EscapeStringToStream(
- out,
- GetSourceSubstitution(target, settings, source, type, OUTPUT_RELATIVE,
- settings->build_settings()->build_dir()),
- escape_options);
- out << std::endl;
- }
- }
-}
-
-// static
-std::string SubstitutionWriter::GetSourceSubstitution(
- const Target* target,
- const Settings* settings,
- const SourceFile& source,
- const Substitution* type,
- OutputStyle output_style,
- const SourceDir& relative_to) {
- std::string to_rebase;
- if (type == &SubstitutionSource) {
- if (source.is_system_absolute())
- return source.value();
- to_rebase = source.value();
- } else if (type == &SubstitutionSourceNamePart) {
- return FindFilenameNoExtension(&source.value()).as_string();
- } else if (type == &SubstitutionSourceFilePart) {
- return source.GetName();
- } else if (type == &SubstitutionSourceDir) {
- if (source.is_system_absolute())
- return DirectoryWithNoLastSlash(source.GetDir());
- to_rebase = DirectoryWithNoLastSlash(source.GetDir());
- } else if (type == &SubstitutionSourceRootRelativeDir) {
- if (source.is_system_absolute())
- return DirectoryWithNoLastSlash(source.GetDir());
- return RebasePath(DirectoryWithNoLastSlash(source.GetDir()),
- SourceDir("//"),
- settings->build_settings()->root_path_utf8());
- } else if (type == &SubstitutionSourceGenDir) {
- to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(settings), source.GetDir(), BuildDirType::GEN));
- } else if (type == &SubstitutionSourceOutDir) {
- to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ));
- } else if (type == &SubstitutionSourceTargetRelative) {
- if (target) {
- return RebasePath(source.value(), target->label().dir(),
- settings->build_settings()->root_path_utf8());
- }
- NOTREACHED() << "Cannot use substitution " << type->name
- << " without target";
- return std::string();
- } else {
- NOTREACHED() << "Unsupported substitution for this function: "
- << type->name;
- return std::string();
- }
-
- // If we get here, the result is a path that should be made relative or
- // absolute according to the output_style. Other cases (just file name or
- // extension extraction) will have been handled via early return above.
- if (output_style == OUTPUT_ABSOLUTE)
- return to_rebase;
- return RebasePath(to_rebase, relative_to,
- settings->build_settings()->root_path_utf8());
-}
-
-// static
-OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
- const Target* target,
- const Tool* tool,
- const SubstitutionPattern& pattern) {
- std::string result_value;
- for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == &SubstitutionLiteral) {
- result_value.append(subrange.literal);
- } else {
- std::string subst;
- CHECK(GetTargetSubstitution(target, subrange.type, &subst));
- result_value.append(subst);
- }
- }
- return OutputFile(result_value);
-}
-
-// static
-void SubstitutionWriter::ApplyListToTargetAsOutputFile(
- const Target* target,
- const Tool* tool,
- const SubstitutionList& list,
- std::vector<OutputFile>* output) {
- for (const auto& item : list.list())
- output->push_back(ApplyPatternToTargetAsOutputFile(target, tool, item));
-}
-
-// static
-bool SubstitutionWriter::GetTargetSubstitution(const Target* target,
- const Substitution* type,
- std::string* result) {
- if (type == &SubstitutionLabel) {
- // Only include the toolchain for non-default toolchains.
- *result =
- target->label().GetUserVisibleName(!target->settings()->is_default());
- } else if (type == &SubstitutionLabelName) {
- *result = target->label().name();
- } else if (type == &SubstitutionRootGenDir) {
- SetDirOrDotWithNoSlash(
- GetBuildDirAsOutputFile(BuildDirContext(target), BuildDirType::GEN)
- .value(),
- result);
- } else if (type == &SubstitutionRootOutDir) {
- SetDirOrDotWithNoSlash(
- target->settings()->toolchain_output_subdir().value(), result);
- } else if (type == &SubstitutionTargetGenDir) {
- SetDirOrDotWithNoSlash(
- GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN).value(),
- result);
- } else if (type == &SubstitutionTargetOutDir) {
- SetDirOrDotWithNoSlash(
- GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ).value(),
- result);
- } else if (type == &SubstitutionTargetOutputName) {
- *result = target->GetComputedOutputName();
- } else {
- return false;
- }
- return true;
-}
-
-// static
-std::string SubstitutionWriter::GetTargetSubstitution(
- const Target* target,
- const Substitution* type) {
- std::string result;
- GetTargetSubstitution(target, type, &result);
- return result;
-}
-
-// static
-OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
- const Target* target,
- const SourceFile& source,
- const SubstitutionPattern& pattern) {
- OutputFile result;
- for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == &SubstitutionLiteral) {
- result.value().append(subrange.literal);
- } else {
- result.value().append(
- GetCompilerSubstitution(target, source, subrange.type));
- }
- }
- return result;
-}
-
-// static
-void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
- const Target* target,
- const SourceFile& source,
- const SubstitutionList& list,
- std::vector<OutputFile>* output) {
- for (const auto& item : list.list())
- output->push_back(ApplyPatternToCompilerAsOutputFile(target, source, item));
-}
-
-// static
-std::string SubstitutionWriter::GetCompilerSubstitution(
- const Target* target,
- const SourceFile& source,
- const Substitution* type) {
- // First try the common tool ones.
- std::string result;
- if (GetTargetSubstitution(target, type, &result))
- return result;
-
- // Fall-through to the source ones.
- return GetSourceSubstitution(
- target, target->settings(), source, type, OUTPUT_RELATIVE,
- target->settings()->build_settings()->build_dir());
-}
-
-// static
-OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- const Target* target,
- const Tool* tool,
- const SubstitutionPattern& pattern) {
- OutputFile result;
- for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == &SubstitutionLiteral) {
- result.value().append(subrange.literal);
- } else {
- result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
- }
- }
- return result;
-}
-
-// static
-void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- const Target* target,
- const Tool* tool,
- const SubstitutionList& list,
- std::vector<OutputFile>* output) {
- for (const auto& item : list.list())
- output->push_back(ApplyPatternToLinkerAsOutputFile(target, tool, item));
-}
-
-// static
-std::string SubstitutionWriter::GetLinkerSubstitution(
- const Target* target,
- const Tool* tool,
- const Substitution* type) {
- // First try the common tool ones.
- std::string result;
- if (GetTargetSubstitution(target, type, &result))
- return result;
-
- // Fall-through to the linker-specific ones.
- if (type == &SubstitutionOutputDir) {
- // Use the target's value if there is one (it will have no expansion
- // patterns since it can directly use GN variables to compute whatever
- // path it wants), or the tool's default (which will contain further
- // expansions).
- if (target->output_dir().is_null()) {
- return ApplyPatternToLinkerAsOutputFile(target, tool,
- tool->default_output_dir())
- .value();
- }
- SetDirOrDotWithNoSlash(
- RebasePath(target->output_dir().value(),
- target->settings()->build_settings()->build_dir()),
- &result);
- return result;
- } else if (type == &CSubstitutionOutputExtension) {
- // Use the extension provided on the target if specified, otherwise
- // fall back on the default. Note that the target's output extension
- // does not include the dot but the tool's does.
- if (!target->output_extension_set())
- return tool->default_output_extension();
- if (target->output_extension().empty())
- return std::string(); // Explicitly set to no extension.
- return std::string(".") + target->output_extension();
- } else if (type == &kRustSubstitutionCrateName) {
- // Only include the toolchain for non-default toolchains.
- return target->rust_values().crate_name();
- } else if (type == &kRustSubstitutionOutputPrefix) {
- // Rustc expects specific output prefixes, so make sure we provide it if
- // necessary.
- if (target->output_type() == Target::RUST_LIBRARY ||
- target->output_type() == Target::SHARED_LIBRARY ||
- target->output_type() == Target::LOADABLE_MODULE)
- return "lib";
- return "";
- } else if (type == &kRustSubstitutionOutputExtension) {
- if (!target->output_extension_set()) {
- DCHECK(tool->AsRust());
- return tool->AsRust()->rustc_output_extension(
- target->output_type(), target->rust_values().crate_type());
- }
- if (target->output_extension().empty())
- return std::string(); // Explicitly set to no extension.
- return std::string(".") + target->output_extension();
- } else {
- NOTREACHED();
- return std::string();
- }
-}
diff --git a/gn/tools/gn/substitution_writer.h b/gn/tools/gn/substitution_writer.h
deleted file mode 100644
index 24cc283b24d..00000000000
--- a/gn/tools/gn/substitution_writer.h
+++ /dev/null
@@ -1,238 +0,0 @@
-// 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 TOOLS_GN_SUBSTITUTION_WRITER_H_
-#define TOOLS_GN_SUBSTITUTION_WRITER_H_
-
-#include <iosfwd>
-#include <string>
-#include <vector>
-
-#include "tools/gn/substitution_type.h"
-
-struct EscapeOptions;
-class OutputFile;
-class Settings;
-class SourceDir;
-class SourceFile;
-class SubstitutionList;
-class SubstitutionPattern;
-class Target;
-class Tool;
-
-// Help text for script source expansion.
-extern const char kSourceExpansion_Help[];
-
-// This class handles writing or applying substitution patterns to strings.
-//
-// There are several different uses:
-//
-// - Source substitutions: These are used to compute action_foreach
-// outputs and arguments. Functions are provided to expand these in terms
-// of both OutputFiles (for writing Ninja files) as well as SourceFiles
-// (for computing lists used by code).
-//
-// - Target substitutions: These are specific to the target+tool combination
-// and are shared between the compiler and linker ones. It includes things
-// like the target_gen_dir.
-//
-// - Compiler substitutions: These are used to compute compiler outputs.
-// It includes all source substitutions (since they depend on the various
-// parts of the source file) as well as the target substitutions.
-//
-// - Linker substitutions: These are used to compute linker outputs. It
-// includes the target substitutions.
-//
-// The compiler and linker specific substitutions do NOT include the various
-// cflags, ldflags, libraries, etc. These are written by the ninja target
-// writer since they depend on traversing the dependency tree.
-//
-// The methods which take a target as an argument can accept null target
-// pointer if there is no target context, in which case the substitutions
-// requiring target context will not work.
-class SubstitutionWriter {
- public:
- enum OutputStyle {
- OUTPUT_ABSOLUTE, // Dirs will be absolute "//foo/bar".
- OUTPUT_RELATIVE, // Dirs will be relative to a given directory.
- };
-
- // Writes the pattern to the given stream with no special handling, and with
- // Ninja variables replacing the patterns.
- static void WriteWithNinjaVariables(const SubstitutionPattern& pattern,
- const EscapeOptions& escape_options,
- std::ostream& out);
-
- // NOP substitutions ---------------------------------------------------------
-
- // Converts the given SubstitutionList to OutputFiles assuming there are
- // no substitutions (it will assert if there are). This is used for cases
- // like actions where the outputs are explicit, but the list is stored as
- // a SubstitutionList.
- static void GetListAsSourceFiles(const SubstitutionList& list,
- std::vector<SourceFile>* output);
- static void GetListAsOutputFiles(const Settings* settings,
- const SubstitutionList& list,
- std::vector<OutputFile>* output);
-
- // Source substitutions -----------------------------------------------------
-
- // Applies the substitution pattern to a source file, returning the result
- // as either a string, a SourceFile or an OutputFile. If the result is
- // expected to be a SourceFile or an OutputFile, this will CHECK if the
- // result isn't in the correct directory. The caller should validate this
- // first (see for example IsFileInOuputDir).
- //
- // The target can be null (see class comment above).
- static SourceFile ApplyPatternToSource(const Target* target,
- const Settings* settings,
- const SubstitutionPattern& pattern,
- const SourceFile& source);
- static std::string ApplyPatternToSourceAsString(
- const Target* target,
- const Settings* settings,
- const SubstitutionPattern& pattern,
- const SourceFile& source);
- static OutputFile ApplyPatternToSourceAsOutputFile(
- const Target* target,
- const Settings* settings,
- const SubstitutionPattern& pattern,
- const SourceFile& source);
-
- // Applies the substitution list to a source, APPENDING the result to the
- // given output vector. It works this way so one can call multiple times to
- // apply to multiple files and create a list. The result can either be
- // SourceFiles or OutputFiles.
- //
- // The target can be null (see class comment above).
- static void ApplyListToSource(const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const SourceFile& source,
- std::vector<SourceFile>* output);
- static void ApplyListToSourceAsString(const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const SourceFile& source,
- std::vector<std::string>* output);
- static void ApplyListToSourceAsOutputFile(const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const SourceFile& source,
- std::vector<OutputFile>* output);
-
- // Like ApplyListToSource but applies the list to all sources and replaces
- // rather than appends the output (this produces the complete output).
- //
- // The target can be null (see class comment above).
- static void ApplyListToSources(const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const std::vector<SourceFile>& sources,
- std::vector<SourceFile>* output);
- static void ApplyListToSourcesAsString(const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const std::vector<SourceFile>& sources,
- std::vector<std::string>* output);
- static void ApplyListToSourcesAsOutputFile(
- const Target* target,
- const Settings* settings,
- const SubstitutionList& list,
- const std::vector<SourceFile>& sources,
- std::vector<OutputFile>* output);
-
- // Given a list of source replacement types used, writes the Ninja variable
- // definitions for the given source file to use for those replacements. The
- // variables will be indented two spaces. Since this is for writing to
- // Ninja files, paths will be relative to the build dir, and no definition
- // for {{source}} will be written since that maps to Ninja's implicit $in
- // variable.
- //
- // The target can be null (see class comment above).
- static void WriteNinjaVariablesForSource(
- const Target* target,
- const Settings* settings,
- const SourceFile& source,
- const std::vector<const Substitution*>& types,
- const EscapeOptions& escape_options,
- std::ostream& out);
-
- // Extracts the given type of substitution related to a source file from the
- // given source file. If output_style is OUTPUT_RELATIVE, relative_to
- // indicates the directory that the relative directories should be relative
- // to, otherwise it is ignored.
- //
- // The target can be null (see class comment above).
- static std::string GetSourceSubstitution(const Target* target,
- const Settings* settings,
- const SourceFile& source,
- const Substitution* type,
- OutputStyle output_style,
- const SourceDir& relative_to);
-
- // Target substitutions ------------------------------------------------------
- //
- // Handles the target substitutions that apply to both compiler and linker
- // tools.
- static OutputFile ApplyPatternToTargetAsOutputFile(
- const Target* target,
- const Tool* tool,
- const SubstitutionPattern& pattern);
- static void ApplyListToTargetAsOutputFile(const Target* target,
- const Tool* tool,
- const SubstitutionList& list,
- std::vector<OutputFile>* output);
-
- // This function is slightly different than the other substitution getters
- // since it can handle failure (since it is designed to be used by the
- // compiler and linker ones which will fall through if it's not a common tool
- // one).
- static bool GetTargetSubstitution(const Target* target,
- const Substitution* type,
- std::string* result);
- static std::string GetTargetSubstitution(const Target* target,
- const Substitution* type);
-
- // Compiler substitutions ----------------------------------------------------
- //
- // A compiler substitution allows both source and tool substitutions. These
- // are used to compute output names for compiler tools.
-
- static OutputFile ApplyPatternToCompilerAsOutputFile(
- const Target* target,
- const SourceFile& source,
- const SubstitutionPattern& pattern);
- static void ApplyListToCompilerAsOutputFile(const Target* target,
- const SourceFile& source,
- const SubstitutionList& list,
- std::vector<OutputFile>* output);
-
- // Like GetSourceSubstitution but for strings based on the target or
- // toolchain. This type of result will always be relative to the build
- // directory.
- static std::string GetCompilerSubstitution(const Target* target,
- const SourceFile& source,
- const Substitution* type);
-
- // Linker substitutions ------------------------------------------------------
-
- static OutputFile ApplyPatternToLinkerAsOutputFile(
- const Target* target,
- const Tool* tool,
- const SubstitutionPattern& pattern);
- static void ApplyListToLinkerAsOutputFile(const Target* target,
- const Tool* tool,
- const SubstitutionList& list,
- std::vector<OutputFile>* output);
-
- // Like GetSourceSubstitution but for strings based on the target or
- // toolchain. This type of result will always be relative to the build
- // directory.
- static std::string GetLinkerSubstitution(const Target* target,
- const Tool* tool,
- const Substitution* type);
-};
-
-#endif // TOOLS_GN_SUBSTITUTION_WRITER_H_
diff --git a/gn/tools/gn/substitution_writer_unittest.cc b/gn/tools/gn/substitution_writer_unittest.cc
deleted file mode 100644
index bea8fec9abb..00000000000
--- a/gn/tools/gn/substitution_writer_unittest.cc
+++ /dev/null
@@ -1,321 +0,0 @@
-// 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 <sstream>
-
-#include "tools/gn/c_substitution_type.h"
-#include "tools/gn/err.h"
-#include "tools/gn/escape.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_pattern.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/target.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-TEST(SubstitutionWriter, GetListAs) {
- TestWithScope setup;
-
- SubstitutionList list =
- SubstitutionList::MakeForTest("//foo/bar/a.cc", "//foo/bar/b.cc");
-
- std::vector<SourceFile> sources;
- SubstitutionWriter::GetListAsSourceFiles(list, &sources);
- ASSERT_EQ(2u, sources.size());
- EXPECT_EQ("//foo/bar/a.cc", sources[0].value());
- EXPECT_EQ("//foo/bar/b.cc", sources[1].value());
-
- std::vector<OutputFile> outputs;
- SubstitutionWriter::GetListAsOutputFiles(setup.settings(), list, &outputs);
- ASSERT_EQ(2u, outputs.size());
- EXPECT_EQ("../../foo/bar/a.cc", outputs[0].value());
- EXPECT_EQ("../../foo/bar/b.cc", outputs[1].value());
-}
-
-TEST(SubstitutionWriter, ApplyPatternToSource) {
- TestWithScope setup;
-
- SubstitutionPattern pattern;
- Err err;
- ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp",
- nullptr, &err));
-
- SourceFile result = SubstitutionWriter::ApplyPatternToSource(
- nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
- ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value());
-}
-
-TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) {
- TestWithScope setup;
-
- SubstitutionPattern pattern;
- Err err;
- ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp",
- nullptr, &err));
-
- OutputFile result = SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
- nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
- ASSERT_EQ("gen/foo/bar/myfile.tmp", result.value());
-}
-
-TEST(SubstitutionWriter, WriteNinjaVariablesForSource) {
- TestWithScope setup;
-
- std::vector<const Substitution*> types;
- types.push_back(&SubstitutionSource);
- types.push_back(&SubstitutionSourceNamePart);
- types.push_back(&SubstitutionSourceDir);
-
- EscapeOptions options;
- options.mode = ESCAPE_NONE;
-
- std::ostringstream out;
- SubstitutionWriter::WriteNinjaVariablesForSource(
- nullptr, setup.settings(), SourceFile("//foo/bar/baz.txt"), types,
- options, out);
-
- // The "source" should be skipped since that will expand to $in which is
- // implicit.
- EXPECT_EQ(
- " source_name_part = baz\n"
- " source_dir = ../../foo/bar\n",
- out.str());
-}
-
-TEST(SubstitutionWriter, WriteWithNinjaVariables) {
- Err err;
- SubstitutionPattern pattern;
- ASSERT_TRUE(pattern.Parse("-i {{source}} --out=bar\"{{source_name_part}}\".o",
- nullptr, &err));
- EXPECT_FALSE(err.has_error());
-
- EscapeOptions options;
- options.mode = ESCAPE_NONE;
-
- std::ostringstream out;
- SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out);
-
- EXPECT_EQ("-i ${in} --out=bar\"${source_name_part}\".o", out.str());
-}
-
-TEST(SubstitutionWriter, SourceSubstitutions) {
- TestWithScope setup;
- Err err;
-
- Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
- target.set_output_type(Target::STATIC_LIBRARY);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
-// Call to get substitutions relative to the build dir.
-#define GetRelSubst(str, what) \
- SubstitutionWriter::GetSourceSubstitution( \
- &target, setup.settings(), SourceFile(str), what, \
- SubstitutionWriter::OUTPUT_RELATIVE, \
- setup.settings()->build_settings()->build_dir())
-
-// Call to get absolute directory substitutions.
-#define GetAbsSubst(str, what) \
- SubstitutionWriter::GetSourceSubstitution( \
- &target, setup.settings(), SourceFile(str), what, \
- SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir())
-
- // Try all possible templates with a normal looking string.
- EXPECT_EQ("../../foo/bar/baz.txt",
- GetRelSubst("//foo/bar/baz.txt", &SubstitutionSource));
- EXPECT_EQ("//foo/bar/baz.txt",
- GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSource));
-
- EXPECT_EQ("baz",
- GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
- EXPECT_EQ("baz",
- GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
-
- EXPECT_EQ("baz.txt",
- GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
- EXPECT_EQ("baz.txt",
- GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
-
- EXPECT_EQ("../../foo/bar",
- GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
- EXPECT_EQ("//foo/bar",
- GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
-
- EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt",
- &SubstitutionSourceRootRelativeDir));
- EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt",
- &SubstitutionSourceRootRelativeDir));
-
- EXPECT_EQ("gen/foo/bar",
- GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
- EXPECT_EQ("//out/Debug/gen/foo/bar",
- GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
-
- EXPECT_EQ("obj/foo/bar",
- GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
- EXPECT_EQ("//out/Debug/obj/foo/bar",
- GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
-
- // Operations on an absolute path.
- EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", &SubstitutionSource));
- EXPECT_EQ("/.", GetRelSubst("/baz.txt", &SubstitutionSourceDir));
- EXPECT_EQ("gen/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceGenDir));
- EXPECT_EQ("obj/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceOutDir));
-#if defined(OS_WIN)
- EXPECT_EQ("gen/ABS_PATH/C",
- GetRelSubst("/C:/baz.txt", &SubstitutionSourceGenDir));
- EXPECT_EQ("obj/ABS_PATH/C",
- GetRelSubst("/C:/baz.txt", &SubstitutionSourceOutDir));
-#endif
-
- EXPECT_EQ(".", GetRelSubst("//baz.txt", &SubstitutionSourceRootRelativeDir));
-
- EXPECT_EQ("baz.txt", GetRelSubst("//foo/bar/baz.txt",
- &SubstitutionSourceTargetRelative));
- EXPECT_EQ("baz.txt", GetAbsSubst("//foo/bar/baz.txt",
- &SubstitutionSourceTargetRelative));
-
-#undef GetAbsSubst
-#undef GetRelSubst
-}
-
-TEST(SubstitutionWriter, TargetSubstitutions) {
- TestWithScope setup;
- Err err;
-
- Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
- target.set_output_type(Target::STATIC_LIBRARY);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::string result;
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionLabel, &result));
- EXPECT_EQ("//foo/bar:baz", result);
-
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionLabelName, &result));
- EXPECT_EQ("baz", result);
-
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionRootGenDir, &result));
- EXPECT_EQ("gen", result);
-
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionRootOutDir, &result));
- EXPECT_EQ(".", result);
-
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionTargetGenDir, &result));
- EXPECT_EQ("gen/foo/bar", result);
-
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionTargetOutDir, &result));
- EXPECT_EQ("obj/foo/bar", result);
-
- EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, &SubstitutionTargetOutputName, &result));
- EXPECT_EQ("libbaz", result);
-}
-
-TEST(SubstitutionWriter, CompilerSubstitutions) {
- TestWithScope setup;
- Err err;
-
- Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
- target.set_output_type(Target::STATIC_LIBRARY);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // The compiler substitution is just source + target combined. So test one
- // of each of those classes of things to make sure this is hooked up.
- EXPECT_EQ("file", SubstitutionWriter::GetCompilerSubstitution(
- &target, SourceFile("//foo/bar/file.txt"),
- &SubstitutionSourceNamePart));
- EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetCompilerSubstitution(
- &target, SourceFile("//foo/bar/file.txt"),
- &SubstitutionTargetGenDir));
-}
-
-TEST(SubstitutionWriter, LinkerSubstitutions) {
- TestWithScope setup;
- Err err;
-
- Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target);
-
- // The compiler substitution is just target + OUTPUT_EXTENSION combined. So
- // test one target one plus the output extension.
- EXPECT_EQ(".so", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, &CSubstitutionOutputExtension));
- EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, &SubstitutionTargetGenDir));
-
- // Test that we handle paths that end up in the root build dir properly
- // (no leading "./" or "/").
- SubstitutionPattern pattern;
- ASSERT_TRUE(pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so",
- nullptr, &err));
-
- OutputFile output = SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- &target, tool, pattern);
- EXPECT_EQ("./libbaz.so", output.value());
-
- // Output extensions can be overridden.
- target.set_output_extension("extension");
- EXPECT_EQ(".extension", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, &CSubstitutionOutputExtension));
- target.set_output_extension("");
- EXPECT_EQ("", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, &CSubstitutionOutputExtension));
-
- // Output directory is tested in a separate test below.
-}
-
-TEST(SubstitutionWriter, OutputDir) {
- TestWithScope setup;
- Err err;
-
- // This tool has an output directory pattern and uses that for the
- // output name.
- std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolLink);
- SubstitutionPattern out_dir_pattern;
- ASSERT_TRUE(out_dir_pattern.Parse("{{root_out_dir}}/{{target_output_name}}",
- nullptr, &err));
- tool->set_default_output_dir(out_dir_pattern);
- tool->SetComplete();
-
- // Default target with no output dir overrides.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "baz"));
- target.set_output_type(Target::EXECUTABLE);
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // The output should expand the default from the patterns in the tool.
- SubstitutionPattern output_name;
- ASSERT_TRUE(output_name.Parse("{{output_dir}}/{{target_output_name}}.exe",
- nullptr, &err));
- EXPECT_EQ("./baz/baz.exe",
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- &target, tool.get(), output_name)
- .value());
-
- // Override the output name to the root build dir.
- target.set_output_dir(SourceDir("//out/Debug/"));
- EXPECT_EQ("./baz.exe", SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- &target, tool.get(), output_name)
- .value());
-
- // Override the output name to a new subdirectory.
- target.set_output_dir(SourceDir("//out/Debug/foo/bar"));
- EXPECT_EQ("foo/bar/baz.exe",
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- &target, tool.get(), output_name)
- .value());
-}
diff --git a/gn/tools/gn/switches.cc b/gn/tools/gn/switches.cc
deleted file mode 100644
index f6a47043845..00000000000
--- a/gn/tools/gn/switches.cc
+++ /dev/null
@@ -1,310 +0,0 @@
-// 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 "tools/gn/switches.h"
-
-namespace switches {
-
-const char kArgs[] = "args";
-const char kArgs_HelpShort[] = "--args: Specifies build arguments overrides.";
-const char kArgs_Help[] =
- R"(--args: Specifies build arguments overrides.
-
- See "gn help buildargs" for an overview of how build arguments work.
-
- Most operations take a build directory. The build arguments are taken from
- the previous build done in that directory. If a command specifies --args, it
- will override the previous arguments stored in the build directory, and use
- the specified ones.
-
- The args specified will be saved to the build directory for subsequent
- commands. Specifying --args="" will clear all build arguments.
-
-Formatting
-
- The value of the switch is interpreted in GN syntax. For typical usage of
- string arguments, you will need to be careful about escaping of quotes.
-
-Examples
-
- gn gen out/Default --args="foo=\"bar\""
-
- gn gen out/Default --args='foo="bar" enable=true blah=7'
-
- gn check out/Default --args=""
- Clears existing build args from the directory.
-
- gn desc out/Default --args="some_list=[1, false, \"foo\"]"
-)";
-
-#define COLOR_HELP_LONG \
- "--[no]color: Forces colored output on or off.\n" \
- "\n" \
- " Normally GN will try to detect whether it is outputting to a terminal\n" \
- " and will enable or disable color accordingly. Use of these switches\n" \
- " will override the default.\n" \
- "\n" \
- "Examples\n" \
- "\n" \
- " gn gen out/Default --color\n" \
- "\n" \
- " gn gen out/Default --nocolor\n"
-const char kColor[] = "color";
-const char kColor_HelpShort[] = "--color: Force colored output.";
-const char kColor_Help[] = COLOR_HELP_LONG;
-
-const char kDotfile[] = "dotfile";
-const char kDotfile_HelpShort[] =
- "--dotfile: Override the name of the \".gn\" file.";
-const char kDotfile_Help[] =
- R"(--dotfile: Override the name of the ".gn" file.
-
- Normally GN loads the ".gn"file from the source root for some basic
- configuration (see "gn help dotfile"). This flag allows you to
- use a different file.
-)";
-
-const char kFailOnUnusedArgs[] = "fail-on-unused-args";
-const char kFailOnUnusedArgs_HelpShort[] =
- "--fail-on-unused-args: Treat unused build args as fatal errors.";
-const char kFailOnUnusedArgs_Help[] =
- R"(--fail-on-unused-args: Treat unused build args as fatal errors.
-
- If you set a value in a build's "gn args" and never use it in the build (in
- a declare_args() block), GN will normally print an error but not fail the
- build.
-
- In many cases engineers would use build args to enable or disable features
- that would sometimes get removed. It would by annoying to block work for
- typically benign problems. In Chrome in particular, flags might be configured
- for build bots in a separate infrastructure repository, or a declare_args
- block might be changed in a third party repository. Treating these errors as
- blocking forced complex multi- way patches to land what would otherwise be
- simple changes.
-
- In some cases, such concerns are not as important, and a mismatch in build
- flags between the invoker of the build and the build files represents a
- critical mismatch that should be immediately fixed. Such users can set this
- flag to force GN to fail in that case.
-)";
-
-const char kMarkdown[] = "markdown";
-const char kMarkdown_HelpShort[] =
- "--markdown: Write help output in the Markdown format.";
-const char kMarkdown_Help[] =
- "--markdown: Write help output in the Markdown format.\n";
-
-const char kNoColor[] = "nocolor";
-const char kNoColor_HelpShort[] = "--nocolor: Force non-colored output.";
-const char kNoColor_Help[] = COLOR_HELP_LONG;
-
-const char kScriptExecutable[] = "script-executable";
-const char kScriptExecutable_HelpShort[] =
- "--script-executable: Set the executable used to execute scripts.";
-const char kScriptExecutable_Help[] =
- R"(--script-executable: Set the executable used to execute scripts.
-
- By default GN searches the PATH for Python to execute scripts in action
- targets and exec_script calls. This flag allows the specification of a
- specific Python executable or potentially a different language
- interpreter.
-)";
-
-const char kMetaDataKeys[] = "data";
-const char kMetaDataKeys_HelpShort[] =
- "--data: list of data keys to concatenate when collecting metadata.";
-const char kMetaDataKeys_Help[] =
- R"(--data: list of data keys to concatenate when collecting metadata.
-
- Data keys identify which variables in the given targets' `metadata`
- scopes should be collected. At least one data key must be specified.
-)";
-
-const char kMetaWalkKeys[] = "walk";
-const char kMetaWalkKeys_HelpShort[] =
- "--walk: list of walk keys to traverse when collecting metadata.";
-const char kMetaWalkKeys_Help[] =
- R"(--walk: list of walk keys to traverse when collecting metadata.
-
- Walk keys identify which variables in the given targets' `metadata`
- scopes contain the list of dependencies to walk next. Absence of any
- walk keys indicates that all deps and data_deps should be walked.
-)";
-
-const char kMetaRebaseFiles[] = "rebase-files";
-const char kMetaRebaseFiles_HelpShort[] =
- "--rebase-files (boolean): whether to rebase the paths of the collected "
- "metadata.";
-const char kMetaRebaseFiles_Help[] =
- R"(--rebase-files: whether to rebase the paths of the collected metadata.
-
- This flag indicates whether or not to rebase the collected results onto their
- declaring source directory path. Note that this requires the data key(s) to
- contain only lists of strings, which will be interpreted as file names.
-)";
-
-const char kQuiet[] = "q";
-const char kQuiet_HelpShort[] =
- "-q: Quiet mode. Don't print output on success.";
-const char kQuiet_Help[] =
- R"(-q: Quiet mode. Don't print output on success.
-
- This is useful when running as a part of another script.
-)";
-
-const char kRoot[] = "root";
-const char kRoot_HelpShort[] = "--root: Explicitly specify source root.";
-const char kRoot_Help[] =
- R"(--root: Explicitly specify source root.
-
- Normally GN will look up in the directory tree from the current directory to
- find a ".gn" file. The source root directory specifies the meaning of "//"
- beginning with paths, and the BUILD.gn file in that directory will be the
- first thing loaded.
-
- Specifying --root allows GN to do builds in a specific directory regardless
- of the current directory.
-
-Examples
-
- gn gen //out/Default --root=/home/baracko/src
-
- gn desc //out/Default --root="C:\Users\BObama\My Documents\foo"
-)";
-
-const char kRuntimeDepsListFile[] = "runtime-deps-list-file";
-const char kRuntimeDepsListFile_HelpShort[] =
- "--runtime-deps-list-file: Save runtime dependencies for targets in file.";
-const char kRuntimeDepsListFile_Help[] =
- R"(--runtime-deps-list-file: Save runtime dependencies for targets in file.
-
- --runtime-deps-list-file=<filename>
-
- Where <filename> is a text file consisting of the labels, one per line, of
- the targets for which runtime dependencies are desired.
-
- See "gn help runtime_deps" for a description of how runtime dependencies are
- computed.
-
-Runtime deps output file
-
- For each target requested, GN will write a separate runtime dependency file.
- The runtime dependency file will be in the output directory alongside the
- output file of the target, with a ".runtime_deps" extension. For example, if
- the target "//foo:bar" is listed in the input file, and that target produces
- an output file "bar.so", GN will create a file "bar.so.runtime_deps" in the
- build directory.
-
- If a source set, action, copy, or group is listed, the runtime deps file will
- correspond to the .stamp file corresponding to that target. This is probably
- not useful; the use-case for this feature is generally executable targets.
-
- The runtime dependency file will list one file per line, with no escaping.
- The files will be relative to the root_build_dir. The first line of the file
- will be the main output file of the target itself (in the above example,
- "bar.so").
-)";
-
-const char kThreads[] = "threads";
-const char kThreads_HelpShort[] =
- "--threads: Specify number of worker threads.";
-const char kThreads_Help[] =
- R"(--threads: Specify number of worker threads.
-
- GN runs many threads to load and run build files. This can make debugging
- challenging. Or you may want to experiment with different values to see how
- it affects performance.
-
- The parameter is the number of worker threads. This does not count the main
- thread (so there are always at least two).
-
-Examples
-
- gen gen out/Default --threads=1
-)";
-
-const char kTime[] = "time";
-const char kTime_HelpShort[] =
- "--time: Outputs a summary of how long everything took.";
-const char kTime_Help[] =
- R"(--time: Outputs a summary of how long everything took.
-
- Hopefully self-explanatory.
-
-Examples
-
- gn gen out/Default --time
-)";
-
-const char kTracelog[] = "tracelog";
-const char kTracelog_HelpShort[] =
- "--tracelog: Writes a Chrome-compatible trace log to the given file.";
-const char kTracelog_Help[] =
- R"(--tracelog: Writes a Chrome-compatible trace log to the given file.
-
- The trace log will show file loads, executions, scripts, and writes. This
- allows performance analysis of the generation step.
-
- To view the trace, open Chrome and navigate to "chrome://tracing/", then
- press "Load" and specify the file you passed to this parameter.
-
-Examples
-
- gn gen out/Default --tracelog=mytrace.trace
-)";
-
-const char kVerbose[] = "v";
-const char kVerbose_HelpShort[] = "-v: Verbose logging.";
-const char kVerbose_Help[] =
- R"(-v: Verbose logging.
-
- This will spew logging events to the console for debugging issues.
-
- Good luck!
-)";
-
-const char kVersion[] = "version";
-const char kVersion_HelpShort[] =
- "--version: Prints the GN version number and exits.";
-// It's impossible to see this since gn_main prints the version and exits
-// immediately if this switch is used.
-const char kVersion_Help[] = "";
-
-const char kAllToolchains[] = "all-toolchains";
-
-// -----------------------------------------------------------------------------
-
-SwitchInfo::SwitchInfo() : short_help(""), long_help("") {}
-
-SwitchInfo::SwitchInfo(const char* short_help, const char* long_help)
- : short_help(short_help), long_help(long_help) {}
-
-#define INSERT_VARIABLE(var) \
- info_map[k##var] = SwitchInfo(k##var##_HelpShort, k##var##_Help);
-
-const SwitchInfoMap& GetSwitches() {
- static SwitchInfoMap info_map;
- if (info_map.empty()) {
- INSERT_VARIABLE(Args)
- INSERT_VARIABLE(Color)
- INSERT_VARIABLE(Dotfile)
- INSERT_VARIABLE(FailOnUnusedArgs)
- INSERT_VARIABLE(Markdown)
- INSERT_VARIABLE(NoColor)
- INSERT_VARIABLE(Root)
- INSERT_VARIABLE(Quiet)
- INSERT_VARIABLE(RuntimeDepsListFile)
- INSERT_VARIABLE(ScriptExecutable)
- INSERT_VARIABLE(Threads)
- INSERT_VARIABLE(Time)
- INSERT_VARIABLE(Tracelog)
- INSERT_VARIABLE(Verbose)
- INSERT_VARIABLE(Version)
- }
- return info_map;
-}
-
-#undef INSERT_VARIABLE
-
-} // namespace switches
diff --git a/gn/tools/gn/switches.h b/gn/tools/gn/switches.h
deleted file mode 100644
index d1abb1ff6d0..00000000000
--- a/gn/tools/gn/switches.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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 TOOLS_GN_SWITCHES_H_
-#define TOOLS_GN_SWITCHES_H_
-
-#include <map>
-
-#include "base/strings/string_piece.h"
-
-namespace switches {
-
-struct SwitchInfo {
- SwitchInfo();
- SwitchInfo(const char* short_help, const char* long_help);
-
- const char* short_help;
- const char* long_help;
-};
-
-typedef std::map<base::StringPiece, SwitchInfo> SwitchInfoMap;
-
-// Returns the mapping of all global switches.
-const SwitchInfoMap& GetSwitches();
-
-// This file contains global switches. If a command takes specific ones only
-// to that command, just put them in that command's .cc file.
-
-extern const char kArgs[];
-extern const char kArgs_HelpShort[];
-extern const char kArgs_Help[];
-
-extern const char kColor[];
-extern const char kColor_HelpShort[];
-extern const char kColor_Help[];
-
-extern const char kDotfile[];
-extern const char kDotfile_HelpShort[];
-extern const char kDotfile_Help[];
-
-extern const char kFailOnUnusedArgs[];
-extern const char kFailOnUnusedArgs_HelpShort[];
-extern const char kFailOnUnusedArgs_Help[];
-
-extern const char kMarkdown[];
-extern const char kMarkdown_HelpShort[];
-extern const char kMarkdown_Help[];
-
-extern const char kMetaDataKeys[];
-extern const char kMetaDataKeys_HelpShort[];
-extern const char kMetaDataKeys_Help[];
-
-extern const char kMetaWalkKeys[];
-extern const char kMetaWalkKeys_HelpShort[];
-extern const char kMetaWalkKeys_Help[];
-
-extern const char kMetaRebaseFiles[];
-extern const char kMetaRebaseFiles_HelpShort[];
-extern const char kMetaRebaseFiles_Help[];
-
-extern const char kNoColor[];
-extern const char kNoColor_HelpShort[];
-extern const char kNoColor_Help[];
-
-extern const char kScriptExecutable[];
-extern const char kScriptExecutable_HelpShort[];
-extern const char kScriptExecutable_Help[];
-
-extern const char kQuiet[];
-extern const char kQuiet_HelpShort[];
-extern const char kQuiet_Help[];
-
-extern const char kRoot[];
-extern const char kRoot_HelpShort[];
-extern const char kRoot_Help[];
-
-extern const char kRuntimeDepsListFile[];
-extern const char kRuntimeDepsListFile_HelpShort[];
-extern const char kRuntimeDepsListFile_Help[];
-
-extern const char kThreads[];
-extern const char kThreads_HelpShort[];
-extern const char kThreads_Help[];
-
-extern const char kTime[];
-extern const char kTime_HelpShort[];
-extern const char kTime_Help[];
-
-extern const char kTracelog[];
-extern const char kTracelog_HelpShort[];
-extern const char kTracelog_Help[];
-
-extern const char kVerbose[];
-extern const char kVerbose_HelpShort[];
-extern const char kVerbose_Help[];
-
-extern const char kVersion[];
-extern const char kVersion_HelpShort[];
-extern const char kVersion_Help[];
-
-// This switch is used by several commands. It is here so it can be shared,
-// but it's documented in the individual commands it applies to rather than
-// globally.
-extern const char kAllToolchains[];
-#define ALL_TOOLCHAINS_SWITCH_HELP \
- " --all-toolchains\n" \
- " Normally only inputs in the default toolchain will be included.\n" \
- " This switch will turn on matching all toolchains.\n" \
- "\n" \
- " For example, a file is in a target might be compiled twice:\n" \
- " once in the default toolchain and once in a secondary one. Without\n" \
- " this flag, only the default toolchain one will be matched by\n" \
- " wildcards. With this flag, both will be matched.\n"
-
-} // namespace switches
-
-#endif // TOOLS_GN_SWITCHES_H_
diff --git a/gn/tools/gn/target.cc b/gn/tools/gn/target.cc
deleted file mode 100644
index 27dce258fec..00000000000
--- a/gn/tools/gn/target.cc
+++ /dev/null
@@ -1,985 +0,0 @@
-// 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 "tools/gn/target.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "tools/gn/c_tool.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/substitution_writer.h"
-#include "tools/gn/tool.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/trace.h"
-
-namespace {
-
-typedef std::set<const Config*> ConfigSet;
-
-// Merges the public configs from the given target to the given config list.
-void MergePublicConfigsFrom(const Target* from_target,
- UniqueVector<LabelConfigPair>* dest) {
- const UniqueVector<LabelConfigPair>& pub = from_target->public_configs();
- dest->Append(pub.begin(), pub.end());
-}
-
-// Like MergePublicConfigsFrom above except does the "all dependent" ones. This
-// additionally adds all configs to the all_dependent_configs_ of the dest
-// target given in *all_dest.
-void MergeAllDependentConfigsFrom(const Target* from_target,
- UniqueVector<LabelConfigPair>* dest,
- UniqueVector<LabelConfigPair>* all_dest) {
- for (const auto& pair : from_target->all_dependent_configs()) {
- all_dest->push_back(pair);
- dest->push_back(pair);
- }
-}
-
-Err MakeTestOnlyError(const Target* from, const Target* to) {
- return Err(
- from->defined_from(), "Test-only dependency not allowed.",
- from->label().GetUserVisibleName(false) +
- "\n"
- "which is NOT marked testonly can't depend on\n" +
- to->label().GetUserVisibleName(false) +
- "\n"
- "which is marked testonly. Only targets with \"testonly = true\"\n"
- "can depend on other test-only targets.\n"
- "\n"
- "Either mark it test-only or don't do this dependency.");
-}
-
-// Set check_private_deps to true for the first invocation since a target
-// can see all of its dependencies. For recursive invocations this will be set
-// to false to follow only public dependency paths.
-//
-// Pass a pointer to an empty set for the first invocation. This will be used
-// to avoid duplicate checking.
-//
-// Checking of object files is optional because it is much slower. This allows
-// us to check targets for normal outputs, and then as a second pass check
-// object files (since we know it will be an error otherwise). This allows
-// us to avoid computing all object file names in the common case.
-bool EnsureFileIsGeneratedByDependency(const Target* target,
- const OutputFile& file,
- bool check_private_deps,
- bool consider_object_files,
- bool check_data_deps,
- std::set<const Target*>* seen_targets) {
- if (seen_targets->find(target) != seen_targets->end())
- return false; // Already checked this one and it's not found.
- seen_targets->insert(target);
-
- // Assume that we have relatively few generated inputs so brute-force
- // searching here is OK. If this becomes a bottleneck, consider storing
- // computed_outputs as a hash set.
- for (const OutputFile& cur : target->computed_outputs()) {
- if (file == cur)
- return true;
- }
-
- if (file == target->write_runtime_deps_output())
- return true;
-
- // Check binary target intermediate files if requested.
- if (consider_object_files && target->IsBinary()) {
- std::vector<OutputFile> source_outputs;
- for (const SourceFile& source : target->sources()) {
- const char* tool_name;
- if (!target->GetOutputFilesForSource(source, &tool_name, &source_outputs))
- continue;
- if (base::ContainsValue(source_outputs, file))
- return true;
- }
- }
-
- if (check_data_deps) {
- check_data_deps = false; // Consider only direct data_deps.
- for (const auto& pair : target->data_deps()) {
- if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
- consider_object_files,
- check_data_deps, seen_targets))
- return true; // Found a path.
- }
- }
-
- // Check all public dependencies (don't do data ones since those are
- // runtime-only).
- for (const auto& pair : target->public_deps()) {
- if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
- consider_object_files,
- check_data_deps, seen_targets))
- return true; // Found a path.
- }
-
- // Only check private deps if requested.
- if (check_private_deps) {
- for (const auto& pair : target->private_deps()) {
- if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false,
- consider_object_files,
- check_data_deps, seen_targets))
- return true; // Found a path.
- }
- if (target->output_type() == Target::CREATE_BUNDLE) {
- for (auto* dep : target->bundle_data().bundle_deps()) {
- if (EnsureFileIsGeneratedByDependency(dep, file, false,
- consider_object_files,
- check_data_deps, seen_targets))
- return true; // Found a path.
- }
- }
- }
- return false;
-}
-
-// check_this indicates if the given target should be matched against the
-// patterns. It should be set to false for the first call since assert_no_deps
-// shouldn't match the target itself.
-//
-// visited should point to an empty set, this will be used to prevent
-// multiple visits.
-//
-// *failure_path_str will be filled with a string describing the path of the
-// dependency failure, and failure_pattern will indicate the pattern in
-// assert_no that matched the target.
-//
-// Returns true if everything is OK. failure_path_str and failure_pattern_index
-// will be unchanged in this case.
-bool RecursiveCheckAssertNoDeps(const Target* target,
- bool check_this,
- const std::vector<LabelPattern>& assert_no,
- std::set<const Target*>* visited,
- std::string* failure_path_str,
- const LabelPattern** failure_pattern) {
- static const char kIndentPath[] = " ";
-
- if (visited->find(target) != visited->end())
- return true; // Already checked this target.
- visited->insert(target);
-
- if (check_this) {
- // Check this target against the given list of patterns.
- for (const LabelPattern& pattern : assert_no) {
- if (pattern.Matches(target->label())) {
- // Found a match.
- *failure_pattern = &pattern;
- *failure_path_str =
- kIndentPath + target->label().GetUserVisibleName(false);
- return false;
- }
- }
- }
-
- // Recursively check dependencies.
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
- if (pair.ptr->output_type() == Target::EXECUTABLE)
- continue;
- if (!RecursiveCheckAssertNoDeps(pair.ptr, true, assert_no, visited,
- failure_path_str, failure_pattern)) {
- // To reconstruct the path, prepend the current target to the error.
- std::string prepend_path =
- kIndentPath + target->label().GetUserVisibleName(false) + " ->\n";
- failure_path_str->insert(0, prepend_path);
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace
-
-const char kExecution_Help[] =
- R"(Build graph and execution overview
-
-Overall build flow
-
- 1. Look for ".gn" file (see "gn help dotfile") in the current directory and
- walk up the directory tree until one is found. Set this directory to be
- the "source root" and interpret this file to find the name of the build
- config file.
-
- 2. Execute the build config file identified by .gn to set up the global
- variables and default toolchain name. Any arguments, variables, defaults,
- etc. set up in this file will be visible to all files in the build.
-
- 3. Load the //BUILD.gn (in the source root directory).
-
- 4. Recursively evaluate rules and load BUILD.gn in other directories as
- necessary to resolve dependencies. If a BUILD file isn't found in the
- specified location, GN will look in the corresponding location inside
- the secondary_source defined in the dotfile (see "gn help dotfile").
-
- 5. When a target's dependencies are resolved, write out the `.ninja`
- file to disk.
-
- 6. When all targets are resolved, write out the root build.ninja file.
-
-Executing target definitions and templates
-
- Build files are loaded in parallel. This means it is impossible to
- interrogate a target from GN code for any information not derivable from its
- label (see "gn help label"). The exception is the get_target_outputs()
- function which requires the target being interrogated to have been defined
- previously in the same file.
-
- Targets are declared by their type and given a name:
-
- static_library("my_static_library") {
- ... target parameter definitions ...
- }
-
- There is also a generic "target" function for programmatically defined types
- (see "gn help target"). You can define new types using templates (see "gn
- help template"). A template defines some custom code that expands to one or
- more other targets.
-
- Before executing the code inside the target's { }, the target defaults are
- applied (see "gn help set_defaults"). It will inject implicit variable
- definitions that can be overridden by the target code as necessary. Typically
- this mechanism is used to inject a default set of configs that define the
- global compiler and linker flags.
-
-Which targets are built
-
- All targets encountered in the default toolchain (see "gn help toolchain")
- will have build rules generated for them, even if no other targets reference
- them. Their dependencies must resolve and they will be added to the implicit
- "all" rule (see "gn help ninja_rules").
-
- Targets in non-default toolchains will only be generated when they are
- required (directly or transitively) to build a target in the default
- toolchain.
-
- See also "gn help ninja_rules".
-
-Dependencies
-
- The only difference between "public_deps" and "deps" except for pushing
- configs around the build tree and allowing includes for the purposes of "gn
- check".
-
- A target's "data_deps" are guaranteed to be built whenever the target is
- built, but the ordering is not defined. The meaning of this is dependencies
- required at runtime. Currently data deps will be complete before the target
- is linked, but this is not semantically guaranteed and this is undesirable
- from a build performance perspective. Since we hope to change this in the
- future, do not rely on this behavior.
-)";
-
-Target::Target(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files)
- : Item(settings, label, build_dependency_files) {}
-
-Target::~Target() = default;
-
-// static
-const char* Target::GetStringForOutputType(OutputType type) {
- switch (type) {
- case UNKNOWN:
- return "unknown";
- case GROUP:
- return functions::kGroup;
- case EXECUTABLE:
- return functions::kExecutable;
- case LOADABLE_MODULE:
- return functions::kLoadableModule;
- case SHARED_LIBRARY:
- return functions::kSharedLibrary;
- case STATIC_LIBRARY:
- return functions::kStaticLibrary;
- case SOURCE_SET:
- return functions::kSourceSet;
- case COPY_FILES:
- return functions::kCopy;
- case ACTION:
- return functions::kAction;
- case ACTION_FOREACH:
- return functions::kActionForEach;
- case BUNDLE_DATA:
- return functions::kBundleData;
- case CREATE_BUNDLE:
- return functions::kCreateBundle;
- case GENERATED_FILE:
- return functions::kGeneratedFile;
- default:
- return "";
- }
-}
-
-Target* Target::AsTarget() {
- return this;
-}
-
-const Target* Target::AsTarget() const {
- return this;
-}
-
-bool Target::OnResolved(Err* err) {
- DCHECK(output_type_ != UNKNOWN);
- DCHECK(toolchain_) << "Toolchain should have been set before resolving.";
-
- ScopedTrace trace(TraceItem::TRACE_ON_RESOLVED, label());
- trace.SetToolchain(settings()->toolchain_label());
-
- // Copy this target's own dependent and public configs to the list of configs
- // applying to it.
- configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
- MergePublicConfigsFrom(this, &configs_);
-
- // Copy public configs from all dependencies into the list of configs
- // applying to this target (configs_).
- PullDependentTargetConfigs();
-
- // Copies public dependencies' public configs to this target's public
- // configs. These configs have already been applied to this target by
- // PullDependentTargetConfigs above, along with the public configs from
- // private deps. This step re-exports them as public configs for targets that
- // depend on this one.
- for (const auto& dep : public_deps_) {
- if (dep.ptr->toolchain() == toolchain() ||
- dep.ptr->toolchain()->propagates_configs())
- public_configs_.Append(dep.ptr->public_configs().begin(),
- dep.ptr->public_configs().end());
- }
-
- // Copy our own libs and lib_dirs to the final set. This will be from our
- // target and all of our configs. We do this specially since these must be
- // inherited through the dependency tree (other flags don't work this way).
- //
- // This needs to happen after we pull dependent target configs for the
- // public config's libs to be included here. And it needs to happen
- // before pulling the dependent target libs so the libs are in the correct
- // order (local ones first, then the dependency's).
- for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
- const ConfigValues& cur = iter.cur();
- all_lib_dirs_.append(cur.lib_dirs().begin(), cur.lib_dirs().end());
- all_libs_.append(cur.libs().begin(), cur.libs().end());
- }
-
- PullRecursiveBundleData();
- PullDependentTargetLibs();
- PullRecursiveHardDeps();
- if (!ResolvePrecompiledHeaders(err))
- return false;
-
- if (!FillOutputFiles(err))
- return false;
-
- if (!CheckVisibility(err))
- return false;
- if (!CheckTestonly(err))
- return false;
- if (!CheckAssertNoDeps(err))
- return false;
- CheckSourcesGenerated();
-
- if (!write_runtime_deps_output_.value().empty())
- g_scheduler->AddWriteRuntimeDepsTarget(this);
-
- if (output_type_ == GENERATED_FILE) {
- DCHECK(!computed_outputs_.empty());
- g_scheduler->AddGeneratedFile(
- computed_outputs_[0].AsSourceFile(settings()->build_settings()));
- }
-
- return true;
-}
-
-bool Target::IsBinary() const {
- return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
- output_type_ == LOADABLE_MODULE || output_type_ == STATIC_LIBRARY ||
- output_type_ == SOURCE_SET || output_type_ == RUST_LIBRARY;
-}
-
-bool Target::IsLinkable() const {
- return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY ||
- output_type_ == RUST_LIBRARY;
-}
-
-bool Target::IsFinal() const {
- return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
- output_type_ == LOADABLE_MODULE || output_type_ == ACTION ||
- output_type_ == ACTION_FOREACH || output_type_ == COPY_FILES ||
- output_type_ == CREATE_BUNDLE ||
- (output_type_ == STATIC_LIBRARY && complete_static_lib_);
-}
-
-DepsIteratorRange Target::GetDeps(DepsIterationType type) const {
- if (type == DEPS_LINKED) {
- return DepsIteratorRange(
- DepsIterator(&public_deps_, &private_deps_, nullptr));
- }
- // All deps.
- return DepsIteratorRange(
- DepsIterator(&public_deps_, &private_deps_, &data_deps_));
-}
-
-std::string Target::GetComputedOutputName() const {
- DCHECK(toolchain_)
- << "Toolchain must be specified before getting the computed output name.";
-
- const std::string& name =
- output_name_.empty() ? label().name() : output_name_;
-
- std::string result;
- const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
- if (tool) {
- // Only add the prefix if the name doesn't already have it and it's not
- // being overridden.
- if (!output_prefix_override_ &&
- !base::StartsWith(name, tool->output_prefix(),
- base::CompareCase::SENSITIVE))
- result = tool->output_prefix();
- }
- result.append(name);
- return result;
-}
-
-bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
- DCHECK(!toolchain_);
- DCHECK_NE(UNKNOWN, output_type_);
- toolchain_ = toolchain;
-
- const Tool* tool = toolchain->GetToolForTargetFinalOutput(this);
- if (tool)
- return true;
-
- // Tool not specified for this target type.
- if (err) {
- *err =
- Err(defined_from(), "This target uses an undefined tool.",
- base::StringPrintf(
- "The target %s\n"
- "of type \"%s\"\n"
- "uses toolchain %s\n"
- "which doesn't have the tool \"%s\" defined.\n\n"
- "Alas, I can not continue.",
- label().GetUserVisibleName(false).c_str(),
- GetStringForOutputType(output_type_),
- label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
- Tool::GetToolTypeForTargetFinalOutput(this)));
- }
- return false;
-}
-
-bool Target::GetOutputFilesForSource(const SourceFile& source,
- const char** computed_tool_type,
- std::vector<OutputFile>* outputs) const {
- outputs->clear();
- *computed_tool_type = Tool::kToolNone;
-
- SourceFile::Type file_type = source.type();
- if (file_type == SourceFile::SOURCE_UNKNOWN)
- return false;
- if (file_type == SourceFile::SOURCE_O) {
- // Object files just get passed to the output and not compiled.
- outputs->push_back(OutputFile(settings()->build_settings(), source));
- return true;
- }
-
- *computed_tool_type = Tool::GetToolTypeForSourceType(file_type);
- if (*computed_tool_type == Tool::kToolNone)
- return false; // No tool for this file (it's a header file or something).
- const Tool* tool = toolchain_->GetTool(*computed_tool_type);
- if (!tool)
- return false; // Tool does not apply for this toolchain.file.
-
- // Figure out what output(s) this compiler produces.
- SubstitutionWriter::ApplyListToCompilerAsOutputFile(this, source,
- tool->outputs(), outputs);
- return !outputs->empty();
-}
-
-void Target::PullDependentTargetConfigs() {
- for (const auto& pair : GetDeps(DEPS_LINKED)) {
- if (pair.ptr->toolchain() == toolchain() ||
- pair.ptr->toolchain()->propagates_configs())
- MergeAllDependentConfigsFrom(pair.ptr, &configs_,
- &all_dependent_configs_);
- }
- for (const auto& pair : GetDeps(DEPS_LINKED)) {
- if (pair.ptr->toolchain() == toolchain() ||
- pair.ptr->toolchain()->propagates_configs())
- MergePublicConfigsFrom(pair.ptr, &configs_);
- }
-}
-
-void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) {
- // Direct dependent libraries.
- if (dep->output_type() == STATIC_LIBRARY ||
- dep->output_type() == SHARED_LIBRARY ||
- dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY)
- inherited_libraries_.Append(dep, is_public);
-
- if (dep->output_type() == SHARED_LIBRARY) {
- // Shared library dependendencies are inherited across public shared
- // library boundaries.
- //
- // In this case:
- // EXE -> INTERMEDIATE_SHLIB --[public]--> FINAL_SHLIB
- // The EXE will also link to to FINAL_SHLIB. The public dependeny means
- // that the EXE can use the headers in FINAL_SHLIB so the FINAL_SHLIB
- // will need to appear on EXE's link line.
- //
- // However, if the dependency is private:
- // EXE -> INTERMEDIATE_SHLIB --[private]--> FINAL_SHLIB
- // the dependency will not be propagated because INTERMEDIATE_SHLIB is
- // not granting permission to call functiosn from FINAL_SHLIB. If EXE
- // wants to use functions (and link to) FINAL_SHLIB, it will need to do
- // so explicitly.
- //
- // Static libraries and source sets aren't inherited across shared
- // library boundaries because they will be linked into the shared
- // library.
- inherited_libraries_.AppendPublicSharedLibraries(dep->inherited_libraries(),
- is_public);
- } else if (!dep->IsFinal()) {
- // The current target isn't linked, so propogate linked deps and
- // libraries up the dependency tree.
- inherited_libraries_.AppendInherited(dep->inherited_libraries(), is_public);
- } else if (dep->complete_static_lib()) {
- // Inherit only final targets through _complete_ static libraries.
- //
- // Inherited final libraries aren't linked into complete static libraries.
- // They are forwarded here so that targets that depend on complete
- // static libraries can link them in. Conversely, since complete static
- // libraries link in non-final targets they shouldn't be inherited.
- for (const auto& inherited :
- dep->inherited_libraries().GetOrderedAndPublicFlag()) {
- if (inherited.first->IsFinal()) {
- inherited_libraries_.Append(inherited.first,
- is_public && inherited.second);
- }
- }
- }
-
- // Library settings are always inherited across static library boundaries.
- if (!dep->IsFinal() || dep->output_type() == STATIC_LIBRARY) {
- all_lib_dirs_.append(dep->all_lib_dirs());
- all_libs_.append(dep->all_libs());
- }
-}
-
-void Target::PullDependentTargetLibs() {
- for (const auto& dep : public_deps_)
- PullDependentTargetLibsFrom(dep.ptr, true);
- for (const auto& dep : private_deps_)
- PullDependentTargetLibsFrom(dep.ptr, false);
-}
-
-void Target::PullRecursiveHardDeps() {
- for (const auto& pair : GetDeps(DEPS_LINKED)) {
- // Direct hard dependencies.
- if (hard_dep() || pair.ptr->hard_dep()) {
- recursive_hard_deps_.insert(pair.ptr);
- continue;
- }
-
- // If |pair.ptr| is binary target and |pair.ptr| has no public header,
- // |this| target does not need to have |pair.ptr|'s hard_deps as its
- // hard_deps to start compiles earlier.
- if (pair.ptr->IsBinary() && !pair.ptr->all_headers_public() &&
- pair.ptr->public_headers().empty()) {
- continue;
- }
-
- // Recursive hard dependencies of all dependencies.
- recursive_hard_deps_.insert(pair.ptr->recursive_hard_deps().begin(),
- pair.ptr->recursive_hard_deps().end());
- }
-}
-
-void Target::PullRecursiveBundleData() {
- for (const auto& pair : GetDeps(DEPS_LINKED)) {
- // Don't propagate bundle_data once they are added to a bundle.
- if (pair.ptr->output_type() == CREATE_BUNDLE)
- continue;
-
- // Don't propagate across toolchain.
- if (pair.ptr->toolchain() != toolchain())
- continue;
-
- // Direct dependency on a bundle_data target.
- if (pair.ptr->output_type() == BUNDLE_DATA)
- bundle_data_.AddBundleData(pair.ptr);
-
- // Recursive bundle_data informations from all dependencies.
- for (auto* target : pair.ptr->bundle_data().bundle_deps())
- bundle_data_.AddBundleData(target);
- }
-
- bundle_data_.OnTargetResolved(this);
-}
-
-bool Target::FillOutputFiles(Err* err) {
- const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
- bool check_tool_outputs = false;
- switch (output_type_) {
- case GROUP:
- case BUNDLE_DATA:
- case CREATE_BUNDLE:
- case SOURCE_SET:
- case COPY_FILES:
- case ACTION:
- case ACTION_FOREACH:
- case GENERATED_FILE: {
- // These don't get linked to and use stamps which should be the first
- // entry in the outputs. These stamps are named
- // "<target_out_dir>/<targetname>.stamp".
- dependency_output_file_ =
- GetBuildDirForTargetAsOutputFile(this, BuildDirType::OBJ);
- dependency_output_file_.value().append(GetComputedOutputName());
- dependency_output_file_.value().append(".stamp");
- break;
- }
- case EXECUTABLE:
- case LOADABLE_MODULE:
- // Executables and loadable modules don't get linked to, but the first
- // output is used for dependency management.
- CHECK_GE(tool->outputs().list().size(), 1u);
- check_tool_outputs = true;
- dependency_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, tool->outputs().list()[0]);
-
- if (tool->runtime_outputs().list().empty()) {
- // Default to the first output for the runtime output.
- runtime_outputs_.push_back(dependency_output_file_);
- } else {
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- this, tool, tool->runtime_outputs(), &runtime_outputs_);
- }
- break;
- case RUST_LIBRARY:
- case STATIC_LIBRARY:
- // Static libraries both have dependencies and linking going off of the
- // first output.
- CHECK(tool->outputs().list().size() >= 1);
- check_tool_outputs = true;
- link_output_file_ = dependency_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, tool->outputs().list()[0]);
- break;
- case SHARED_LIBRARY:
- CHECK(tool->outputs().list().size() >= 1);
- check_tool_outputs = true;
- if (const CTool* ctool = tool->AsC()) {
- if (ctool->link_output().empty() && ctool->depend_output().empty()) {
- // Default behavior, use the first output file for both.
- link_output_file_ = dependency_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, tool->outputs().list()[0]);
- } else {
- // Use the tool-specified ones.
- if (!ctool->link_output().empty()) {
- link_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, ctool->link_output());
- }
- if (!ctool->depend_output().empty()) {
- dependency_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, ctool->depend_output());
- }
- }
- if (tool->runtime_outputs().list().empty()) {
- // Default to the link output for the runtime output.
- runtime_outputs_.push_back(link_output_file_);
- } else {
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- this, tool, tool->runtime_outputs(), &runtime_outputs_);
- }
- } else if (const RustTool* rstool = tool->AsRust()) {
- // Default behavior, use the first output file for both.
- link_output_file_ = dependency_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, tool->outputs().list()[0]);
- }
- break;
- case UNKNOWN:
- default:
- NOTREACHED();
- }
-
- // Count anything generated from bundle_data dependencies.
- if (output_type_ == CREATE_BUNDLE) {
- if (!bundle_data_.GetOutputFiles(settings(), this, &computed_outputs_, err))
- return false;
- }
-
- // Count all outputs from this tool as something generated by this target.
- if (check_tool_outputs) {
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- this, tool, tool->outputs(), &computed_outputs_);
-
- // Output names aren't canonicalized in the same way that source files
- // are. For example, the tool outputs often use
- // {{some_var}}/{{output_name}} which expands to "./foo", but this won't
- // match "foo" which is what we'll compute when converting a SourceFile to
- // an OutputFile.
- for (auto& out : computed_outputs_)
- NormalizePath(&out.value());
- }
-
- // Also count anything the target has declared to be an output.
- std::vector<SourceFile> outputs_as_sources;
- action_values_.GetOutputsAsSourceFiles(this, &outputs_as_sources);
- for (const SourceFile& out : outputs_as_sources)
- computed_outputs_.push_back(OutputFile(settings()->build_settings(), out));
-
- return true;
-}
-
-bool Target::ResolvePrecompiledHeaders(Err* err) {
- // Precompiled headers are stored on a ConfigValues struct. This way, the
- // build can set all the precompiled header settings in a config and apply
- // it to many targets. Likewise, the precompiled header values may be
- // specified directly on a target.
- //
- // Unlike other values on configs which are lists that just get concatenated,
- // the precompiled header settings are unique values. We allow them to be
- // specified anywhere, but if they are specified in more than one place all
- // places must match.
-
- // Track where the current settings came from for issuing errors.
- const Label* pch_header_settings_from = NULL;
- if (config_values_.has_precompiled_headers())
- pch_header_settings_from = &label();
-
- for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
- if (!iter.GetCurrentConfig())
- continue; // Skip the one on the target itself.
-
- const Config* config = iter.GetCurrentConfig();
- const ConfigValues& cur = config->resolved_values();
- if (!cur.has_precompiled_headers())
- continue; // This one has no precompiled header info, skip.
-
- if (config_values_.has_precompiled_headers()) {
- // Already have a precompiled header values, the settings must match.
- if (config_values_.precompiled_header() != cur.precompiled_header() ||
- config_values_.precompiled_source() != cur.precompiled_source()) {
- *err = Err(
- defined_from(), "Precompiled header setting conflict.",
- "The target " + label().GetUserVisibleName(false) +
- "\n"
- "has conflicting precompiled header settings.\n"
- "\n"
- "From " +
- pch_header_settings_from->GetUserVisibleName(false) +
- "\n header: " + config_values_.precompiled_header() +
- "\n source: " + config_values_.precompiled_source().value() +
- "\n\n"
- "From " +
- config->label().GetUserVisibleName(false) +
- "\n header: " + cur.precompiled_header() +
- "\n source: " + cur.precompiled_source().value());
- return false;
- }
- } else {
- // Have settings from a config, apply them to ourselves.
- pch_header_settings_from = &config->label();
- config_values_.set_precompiled_header(cur.precompiled_header());
- config_values_.set_precompiled_source(cur.precompiled_source());
- }
- }
-
- return true;
-}
-
-bool Target::CheckVisibility(Err* err) const {
- for (const auto& pair : GetDeps(DEPS_ALL)) {
- if (!Visibility::CheckItemVisibility(this, pair.ptr, err))
- return false;
- }
- return true;
-}
-
-bool Target::CheckTestonly(Err* err) const {
- // If the current target is marked testonly, it can include both testonly
- // and non-testonly targets, so there's nothing to check.
- if (testonly())
- return true;
-
- // Verify no deps have "testonly" set.
- for (const auto& pair : GetDeps(DEPS_ALL)) {
- if (pair.ptr->testonly()) {
- *err = MakeTestOnlyError(this, pair.ptr);
- return false;
- }
- }
-
- return true;
-}
-
-bool Target::CheckAssertNoDeps(Err* err) const {
- if (assert_no_deps_.empty())
- return true;
-
- std::set<const Target*> visited;
- std::string failure_path_str;
- const LabelPattern* failure_pattern = nullptr;
-
- if (!RecursiveCheckAssertNoDeps(this, false, assert_no_deps_, &visited,
- &failure_path_str, &failure_pattern)) {
- *err = Err(
- defined_from(), "assert_no_deps failed.",
- label().GetUserVisibleName(false) +
- " has an assert_no_deps entry:\n " + failure_pattern->Describe() +
- "\nwhich fails for the dependency path:\n" + failure_path_str);
- return false;
- }
- return true;
-}
-
-void Target::CheckSourcesGenerated() const {
- // Checks that any inputs or sources to this target that are in the build
- // directory are generated by a target that this one transitively depends on
- // in some way. We already guarantee that all generated files are written
- // to the build dir.
- //
- // See Scheduler::AddUnknownGeneratedInput's declaration for more.
- for (const SourceFile& file : sources_)
- CheckSourceGenerated(file);
- for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
- for (const SourceFile& file : iter.cur().inputs())
- CheckSourceGenerated(file);
- }
- // TODO(agrieve): Check all_libs_ here as well (those that are source files).
- // http://crbug.com/571731
-}
-
-void Target::CheckSourceGenerated(const SourceFile& source) const {
- if (!IsStringInOutputDir(settings()->build_settings()->build_dir(),
- source.value()))
- return; // Not in output dir, this is OK.
-
- // Tell the scheduler about unknown files. This will be noted for later so
- // the list of files written by the GN build itself (often response files)
- // can be filtered out of this list.
- OutputFile out_file(settings()->build_settings(), source);
- std::set<const Target*> seen_targets;
- bool check_data_deps = false;
- bool consider_object_files = false;
- if (!EnsureFileIsGeneratedByDependency(this, out_file, true,
- consider_object_files, check_data_deps,
- &seen_targets)) {
- seen_targets.clear();
- // Allow dependency to be through data_deps for files generated by gn.
- check_data_deps =
- g_scheduler->IsFileGeneratedByWriteRuntimeDeps(out_file) ||
- g_scheduler->IsFileGeneratedByTarget(source);
- // Check object files (much slower and very rare) only if the "normal"
- // output check failed.
- consider_object_files = !check_data_deps;
- if (!EnsureFileIsGeneratedByDependency(this, out_file, true,
- consider_object_files,
- check_data_deps, &seen_targets))
- g_scheduler->AddUnknownGeneratedInput(this, source);
- }
-}
-
-bool Target::GetMetadata(const std::vector<std::string>& keys_to_extract,
- const std::vector<std::string>& keys_to_walk,
- const SourceDir& rebase_dir,
- bool deps_only,
- std::vector<Value>* result,
- std::set<const Target*>* targets_walked,
- Err* err) const {
- std::vector<Value> next_walk_keys;
- std::vector<Value> current_result;
- // If deps_only, this is the top-level target and thus we don't want to
- // collect its metadata, only that of its deps and data_deps.
- if (deps_only) {
- // Empty string will be converted below to mean all deps and data_deps.
- // Origin is null because this isn't declared anywhere, and should never
- // trigger any errors.
- next_walk_keys.push_back(Value(nullptr, ""));
- } else {
- // Otherwise, we walk this target and collect the appropriate data.
- if (!metadata_.WalkStep(settings()->build_settings(), keys_to_extract,
- keys_to_walk, rebase_dir, &next_walk_keys,
- &current_result, err))
- return false;
- }
-
- // Gather walk keys and find the appropriate target. Targets identified in
- // the walk key set must be deps or data_deps of the declaring target.
- const DepsIteratorRange& all_deps = GetDeps(Target::DEPS_ALL);
- const SourceDir current_dir("//");
- for (const auto& next : next_walk_keys) {
- DCHECK(next.type() == Value::STRING);
-
- // If we hit an empty string in this list, add all deps and data_deps. The
- // ordering in the resulting list of values as a result will be the data
- // from each explicitly listed dep prior to this, followed by all data in
- // walk order of the remaining deps.
- if (next.string_value().empty()) {
- for (const auto& dep : all_deps) {
- // If we haven't walked this dep yet, go down into it.
- auto pair = targets_walked->insert(dep.ptr);
- if (pair.second) {
- if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
- false, result, targets_walked, err))
- return false;
- }
- }
-
- // Any other walk keys are superfluous, as they can only be a subset of
- // all deps.
- break;
- }
-
- // Otherwise, look through the target's deps for the specified one.
- // Canonicalize the label if possible.
- Label next_label =
- Label::Resolve(current_dir, settings()->toolchain_label(), next, err);
- if (next_label.is_null()) {
- *err = Err(next.origin(), std::string("Failed to canonicalize ") +
- next.string_value() + std::string("."));
- }
- std::string canonicalize_next_label = next_label.GetUserVisibleName(true);
-
- bool found_next = false;
- for (const auto& dep : all_deps) {
- // Match against the label with the toolchain.
- if (dep.label.GetUserVisibleName(true) == canonicalize_next_label) {
- // If we haven't walked this dep yet, go down into it.
- auto pair = targets_walked->insert(dep.ptr);
- if (pair.second) {
- if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
- false, result, targets_walked, err))
- return false;
- }
- // We found it, so we can exit this search now.
- found_next = true;
- break;
- }
- }
- // If we didn't find the specified dep in the target, that's an error.
- // Propagate it back to the user.
- if (!found_next) {
- *err = Err(next.origin(),
- std::string("I was expecting ") + canonicalize_next_label +
- std::string(" to be a dependency of ") +
- label().GetUserVisibleName(true) +
- ". Make sure it's included in the deps or data_deps, and "
- "that you've specified the appropriate toolchain.");
- return false;
- }
- }
- result->insert(result->end(), std::make_move_iterator(current_result.begin()),
- std::make_move_iterator(current_result.end()));
- return true;
-}
diff --git a/gn/tools/gn/target.h b/gn/tools/gn/target.h
deleted file mode 100644
index 549b3545eb1..00000000000
--- a/gn/tools/gn/target.h
+++ /dev/null
@@ -1,453 +0,0 @@
-// 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 TOOLS_GN_TARGET_H_
-#define TOOLS_GN_TARGET_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/action_values.h"
-#include "tools/gn/bundle_data.h"
-#include "tools/gn/config_values.h"
-#include "tools/gn/inherited_libraries.h"
-#include "tools/gn/item.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/lib_file.h"
-#include "tools/gn/metadata.h"
-#include "tools/gn/ordered_set.h"
-#include "tools/gn/output_file.h"
-#include "tools/gn/rust_values.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/unique_vector.h"
-
-class DepsIteratorRange;
-class Settings;
-class Toolchain;
-
-class Target : public Item {
- public:
- enum OutputType {
- UNKNOWN,
- GROUP,
- EXECUTABLE,
- SHARED_LIBRARY,
- LOADABLE_MODULE,
- STATIC_LIBRARY,
- SOURCE_SET,
- COPY_FILES,
- ACTION,
- ACTION_FOREACH,
- BUNDLE_DATA,
- CREATE_BUNDLE,
- GENERATED_FILE,
- RUST_LIBRARY,
- };
-
- enum DepsIterationType {
- DEPS_ALL, // Iterates through all public, private, and data deps.
- DEPS_LINKED, // Iterates through all non-data dependencies.
- };
-
- typedef std::vector<SourceFile> FileList;
- typedef std::vector<std::string> StringVector;
-
- // We track the set of build files that may affect this target, please refer
- // to Scope for how this is determined.
- Target(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files = {});
- ~Target() override;
-
- // Returns a string naming the output type.
- static const char* GetStringForOutputType(OutputType type);
-
- // Item overrides.
- Target* AsTarget() override;
- const Target* AsTarget() const override;
- bool OnResolved(Err* err) override;
-
- OutputType output_type() const { return output_type_; }
- void set_output_type(OutputType t) { output_type_ = t; }
-
- // True for targets that compile source code (all types of libaries and
- // executables).
- bool IsBinary() const;
-
- // Can be linked into other targets.
- bool IsLinkable() const;
-
- // True if the target links dependencies rather than propogated up the graph.
- // This is also true of action and copy steps even though they don't link
- // dependencies, because they also don't propogate libraries up.
- bool IsFinal() const;
-
- // Will be the empty string to use the target label as the output name.
- // See GetComputedOutputName().
- const std::string& output_name() const { return output_name_; }
- void set_output_name(const std::string& name) { output_name_ = name; }
-
- // Returns the output name for this target, which is the output_name if
- // specified, or the target label if not.
- //
- // Because this depends on the tool for this target, the toolchain must
- // have been set before calling.
- std::string GetComputedOutputName() const;
-
- bool output_prefix_override() const { return output_prefix_override_; }
- void set_output_prefix_override(bool prefix_override) {
- output_prefix_override_ = prefix_override;
- }
-
- // Desired output directory for the final output. This will be used for
- // the {{output_dir}} substitution in the tool if it is specified. If
- // is_null, the tool default will be used.
- const SourceDir& output_dir() const { return output_dir_; }
- void set_output_dir(const SourceDir& dir) { output_dir_ = dir; }
-
- // The output extension is really a tri-state: unset (output_extension_set
- // is false and the string is empty, meaning the default extension should be
- // used), the output extension is set but empty (output should have no
- // extension) and the output extension is set but nonempty (use the given
- // extension).
- const std::string& output_extension() const { return output_extension_; }
- void set_output_extension(const std::string& extension) {
- output_extension_ = extension;
- output_extension_set_ = true;
- }
- bool output_extension_set() const { return output_extension_set_; }
-
- const FileList& sources() const { return sources_; }
- FileList& sources() { return sources_; }
-
- const SourceFileTypeSet& source_types_used() const {
- return source_types_used_;
- }
- SourceFileTypeSet& source_types_used() { return source_types_used_; }
-
- // Set to true when all sources are public. This is the default. In this case
- // the public headers list should be empty.
- bool all_headers_public() const { return all_headers_public_; }
- void set_all_headers_public(bool p) { all_headers_public_ = p; }
-
- // When all_headers_public is false, this is the list of public headers. It
- // could be empty which would mean no headers are public.
- const FileList& public_headers() const { return public_headers_; }
- FileList& public_headers() { return public_headers_; }
-
- // Whether this target's includes should be checked by "gn check".
- bool check_includes() const { return check_includes_; }
- void set_check_includes(bool ci) { check_includes_ = ci; }
-
- // Whether this static_library target should have code linked in.
- bool complete_static_lib() const { return complete_static_lib_; }
- void set_complete_static_lib(bool complete) {
- DCHECK_EQ(STATIC_LIBRARY, output_type_);
- complete_static_lib_ = complete;
- }
-
- // Metadata. Target takes ownership of the resulting scope.
- const Metadata& metadata() const { return metadata_; }
- Metadata& metadata() { return metadata_; }
-
- // Get metadata from this target and its dependencies. This is intended to
- // be called after the target is resolved.
- bool GetMetadata(const std::vector<std::string>& keys_to_extract,
- const std::vector<std::string>& keys_to_walk,
- const SourceDir& rebase_dir,
- bool deps_only,
- std::vector<Value>* result,
- std::set<const Target*>* targets_walked,
- Err* err) const;
-
- // GeneratedFile-related methods.
- bool GenerateFile(Err* err) const;
-
- const Value& contents() const { return contents_; }
- void set_contents(const Value& value) { contents_ = value; }
- const Value& output_conversion() const { return output_conversion_; }
- void set_output_conversion(const Value& value) { output_conversion_ = value; }
-
- // Metadata collection methods for GeneratedFile targets.
- const SourceDir& rebase() const { return rebase_; }
- void set_rebase(const SourceDir& value) { rebase_ = value; }
- const std::vector<std::string>& data_keys() const { return data_keys_; }
- std::vector<std::string>& data_keys() { return data_keys_; }
- const std::vector<std::string>& walk_keys() const { return walk_keys_; }
- std::vector<std::string>& walk_keys() { return walk_keys_; }
-
- bool testonly() const { return testonly_; }
- void set_testonly(bool value) { testonly_ = value; }
-
- OutputFile write_runtime_deps_output() const {
- return write_runtime_deps_output_;
- }
- void set_write_runtime_deps_output(const OutputFile& value) {
- write_runtime_deps_output_ = value;
- }
-
- // Runtime dependencies. These are "file-like things" that can either be
- // directories or files. They do not need to exist, these are just passed as
- // runtime dependencies to external test systems as necessary.
- const std::vector<std::string>& data() const { return data_; }
- std::vector<std::string>& data() { return data_; }
-
- // Information about the bundle. Only valid for CREATE_BUNDLE target after
- // they have been resolved.
- const BundleData& bundle_data() const { return bundle_data_; }
- BundleData& bundle_data() { return bundle_data_; }
-
- // Returns true if targets depending on this one should have an order
- // dependency.
- bool hard_dep() const {
- return output_type_ == ACTION || output_type_ == ACTION_FOREACH ||
- output_type_ == COPY_FILES || output_type_ == CREATE_BUNDLE ||
- output_type_ == BUNDLE_DATA || output_type_ == GENERATED_FILE;
- }
-
- // Returns the iterator range which can be used in range-based for loops
- // to iterate over multiple types of deps in one loop:
- // for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) ...
- DepsIteratorRange GetDeps(DepsIterationType type) const;
-
- // Linked private dependencies.
- const LabelTargetVector& private_deps() const { return private_deps_; }
- LabelTargetVector& private_deps() { return private_deps_; }
-
- // Linked public dependencies.
- const LabelTargetVector& public_deps() const { return public_deps_; }
- LabelTargetVector& public_deps() { return public_deps_; }
-
- // Non-linked dependencies.
- const LabelTargetVector& data_deps() const { return data_deps_; }
- LabelTargetVector& data_deps() { return data_deps_; }
-
- // List of configs that this class inherits settings from. Once a target is
- // resolved, this will also list all-dependent and public configs.
- const UniqueVector<LabelConfigPair>& configs() const { return configs_; }
- UniqueVector<LabelConfigPair>& configs() { return configs_; }
-
- // List of configs that all dependencies (direct and indirect) of this
- // target get. These configs are not added to this target. Note that due
- // to the way this is computed, there may be duplicates in this list.
- const UniqueVector<LabelConfigPair>& all_dependent_configs() const {
- return all_dependent_configs_;
- }
- UniqueVector<LabelConfigPair>& all_dependent_configs() {
- return all_dependent_configs_;
- }
-
- // List of configs that targets depending directly on this one get. These
- // configs are also added to this target.
- const UniqueVector<LabelConfigPair>& public_configs() const {
- return public_configs_;
- }
- UniqueVector<LabelConfigPair>& public_configs() { return public_configs_; }
-
- // Dependencies that can include files from this target.
- const std::set<Label>& allow_circular_includes_from() const {
- return allow_circular_includes_from_;
- }
- std::set<Label>& allow_circular_includes_from() {
- return allow_circular_includes_from_;
- }
-
- const InheritedLibraries& inherited_libraries() const {
- return inherited_libraries_;
- }
-
- // This config represents the configuration set directly on this target.
- ConfigValues& config_values() { return config_values_; }
- const ConfigValues& config_values() const { return config_values_; }
-
- ActionValues& action_values() { return action_values_; }
- const ActionValues& action_values() const { return action_values_; }
-
- RustValues& rust_values() { return rust_values_; }
- const RustValues& rust_values() const { return rust_values_; }
-
- const OrderedSet<SourceDir>& all_lib_dirs() const { return all_lib_dirs_; }
- const OrderedSet<LibFile>& all_libs() const { return all_libs_; }
-
- const std::set<const Target*>& recursive_hard_deps() const {
- return recursive_hard_deps_;
- }
-
- std::vector<LabelPattern>& friends() { return friends_; }
- const std::vector<LabelPattern>& friends() const { return friends_; }
-
- std::vector<LabelPattern>& assert_no_deps() { return assert_no_deps_; }
- const std::vector<LabelPattern>& assert_no_deps() const {
- return assert_no_deps_;
- }
-
- // The toolchain is only known once this target is resolved (all if its
- // dependencies are known). They will be null until then. Generally, this can
- // only be used during target writing.
- const Toolchain* toolchain() const { return toolchain_; }
-
- // Sets the toolchain. The toolchain must include a tool for this target
- // or the error will be set and the function will return false. Unusually,
- // this function's "err" output is optional since this is commonly used
- // frequently by unit tests which become needlessly verbose.
- bool SetToolchain(const Toolchain* toolchain, Err* err = nullptr);
-
- // Once this target has been resolved, all outputs from the target will be
- // listed here. This will include things listed in the "outputs" for an
- // action or a copy step, and the output library or executable file(s) from
- // binary targets.
- //
- // It will NOT include stamp files and object files.
- const std::vector<OutputFile>& computed_outputs() const {
- return computed_outputs_;
- }
-
- // Returns outputs from this target. The link output file is the one that
- // other targets link to when they depend on this target. This will only be
- // valid for libraries and will be empty for all other target types.
- //
- // The dependency output file is the file that should be used to express
- // a dependency on this one. It could be the same as the link output file
- // (this will be the case for static libraries). For shared libraries it
- // could be the same or different than the link output file, depending on the
- // system. For actions this will be the stamp file.
- //
- // These are only known once the target is resolved and will be empty before
- // that. This is a cache of the files to prevent every target that depends on
- // a given library from recomputing the same pattern.
- const OutputFile& link_output_file() const { return link_output_file_; }
- const OutputFile& dependency_output_file() const {
- return dependency_output_file_;
- }
-
- // The subset of computed_outputs that are considered runtime outputs.
- const std::vector<OutputFile>& runtime_outputs() const {
- return runtime_outputs_;
- }
-
- // Computes the set of output files resulting from compiling the given source
- // file. If the file can be compiled and the tool exists, fills the outputs
- // in and writes the tool type to computed_tool_type. If the file is not
- // compilable, returns false.
- //
- // The function can succeed with a "NONE" tool type for object files which
- // are just passed to the output. The output will always be overwritten, not
- // appended to.
- bool GetOutputFilesForSource(const SourceFile& source,
- const char** computed_tool_type,
- std::vector<OutputFile>* outputs) const;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(TargetTest, ResolvePrecompiledHeaders);
-
- // Pulls necessary information from dependencies to this one when all
- // dependencies have been resolved.
- void PullDependentTargetConfigs();
- void PullDependentTargetLibsFrom(const Target* dep, bool is_public);
- void PullDependentTargetLibs();
- void PullRecursiveHardDeps();
- void PullRecursiveBundleData();
-
- // Fills the link and dependency output files when a target is resolved.
- bool FillOutputFiles(Err* err);
-
- // Checks precompiled headers from configs and makes sure the resulting
- // values are in config_values_.
- bool ResolvePrecompiledHeaders(Err* err);
-
- // Validates the given thing when a target is resolved.
- bool CheckVisibility(Err* err) const;
- bool CheckTestonly(Err* err) const;
- bool CheckAssertNoDeps(Err* err) const;
- void CheckSourcesGenerated() const;
- void CheckSourceGenerated(const SourceFile& source) const;
-
- OutputType output_type_ = UNKNOWN;
- std::string output_name_;
- bool output_prefix_override_ = false;
- SourceDir output_dir_;
- std::string output_extension_;
- bool output_extension_set_ = false;
-
- FileList sources_;
- SourceFileTypeSet source_types_used_;
- bool all_headers_public_ = true;
- FileList public_headers_;
- bool check_includes_ = true;
- bool complete_static_lib_ = false;
- bool testonly_ = false;
- std::vector<std::string> data_;
- BundleData bundle_data_;
- OutputFile write_runtime_deps_output_;
-
- LabelTargetVector private_deps_;
- LabelTargetVector public_deps_;
- LabelTargetVector data_deps_;
-
- // See getters for more info.
- UniqueVector<LabelConfigPair> configs_;
- UniqueVector<LabelConfigPair> all_dependent_configs_;
- UniqueVector<LabelConfigPair> public_configs_;
-
- std::set<Label> allow_circular_includes_from_;
-
- // Static libraries, shared libraries, and source sets from transitive deps
- // that need to be linked.
- InheritedLibraries inherited_libraries_;
-
- // These libs and dirs are inherited from statically linked deps and all
- // configs applying to this target.
- OrderedSet<SourceDir> all_lib_dirs_;
- OrderedSet<LibFile> all_libs_;
-
- // All hard deps from this target and all dependencies. Filled in when this
- // target is marked resolved. This will not include the current target.
- std::set<const Target*> recursive_hard_deps_;
-
- std::vector<LabelPattern> friends_;
- std::vector<LabelPattern> assert_no_deps_;
-
- // Used for all binary targets, and for inputs in regular targets. The
- // precompiled header values in this struct will be resolved to the ones to
- // use for this target, if precompiled headers are used.
- ConfigValues config_values_;
-
- // Used for action[_foreach] targets.
- ActionValues action_values_;
-
- // Used for Rust targets.
- RustValues rust_values_;
-
- // Toolchain used by this target. Null until target is resolved.
- const Toolchain* toolchain_ = nullptr;
-
- // Output files. Empty until the target is resolved.
- std::vector<OutputFile> computed_outputs_;
- OutputFile link_output_file_;
- OutputFile dependency_output_file_;
- std::vector<OutputFile> runtime_outputs_;
-
- Metadata metadata_;
-
- // GeneratedFile values.
- Value output_conversion_;
- Value contents_; // Value::NONE if metadata collection should occur.
-
- // GeneratedFile as metadata collection values.
- SourceDir rebase_;
- std::vector<std::string> data_keys_;
- std::vector<std::string> walk_keys_;
-
- DISALLOW_COPY_AND_ASSIGN(Target);
-};
-
-extern const char kExecution_Help[];
-
-#endif // TOOLS_GN_TARGET_H_
diff --git a/gn/tools/gn/target_generator.cc b/gn/tools/gn/target_generator.cc
deleted file mode 100644
index 09de19dec78..00000000000
--- a/gn/tools/gn/target_generator.cc
+++ /dev/null
@@ -1,444 +0,0 @@
-// 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 "tools/gn/target_generator.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/action_target_generator.h"
-#include "tools/gn/binary_target_generator.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/bundle_data_target_generator.h"
-#include "tools/gn/config.h"
-#include "tools/gn/copy_target_generator.h"
-#include "tools/gn/create_bundle_target_generator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/generated_file_target_generator.h"
-#include "tools/gn/group_target_generator.h"
-#include "tools/gn/metadata.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/token.h"
-#include "tools/gn/value.h"
-#include "tools/gn/value_extractors.h"
-#include "tools/gn/variables.h"
-
-TargetGenerator::TargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err)
- : target_(target),
- scope_(scope),
- function_call_(function_call),
- err_(err) {}
-
-TargetGenerator::~TargetGenerator() = default;
-
-void TargetGenerator::Run() {
- // All target types use these.
- if (!FillDependentConfigs())
- return;
-
- if (!FillData())
- return;
-
- if (!FillDependencies())
- return;
-
- if (!FillMetadata())
- return;
-
- if (!FillTestonly())
- return;
-
- if (!FillAssertNoDeps())
- return;
-
- if (!Visibility::FillItemVisibility(target_, scope_, err_))
- return;
-
- if (!FillWriteRuntimeDeps())
- return;
-
- // Do type-specific generation.
- DoRun();
-}
-
-// static
-void TargetGenerator::GenerateTarget(Scope* scope,
- const FunctionCallNode* function_call,
- const std::vector<Value>& args,
- const std::string& output_type,
- Err* err) {
- // Name is the argument to the function.
- if (args.size() != 1u || args[0].type() != Value::STRING) {
- *err = Err(function_call, "Target generator requires one string argument.",
- "Otherwise I'm not sure what to call this target.");
- return;
- }
-
- // The location of the target is the directory name with no slash at the end.
- // FIXME(brettw) validate name.
- const Label& toolchain_label = ToolchainLabelForScope(scope);
- Label label(scope->GetSourceDir(), args[0].string_value(),
- toolchain_label.dir(), toolchain_label.name());
-
- if (g_scheduler->verbose_logging())
- g_scheduler->Log("Defining target", label.GetUserVisibleName(true));
-
- std::unique_ptr<Target> target = std::make_unique<Target>(
- scope->settings(), label, scope->build_dependency_files());
- target->set_defined_from(function_call);
-
- // Create and call out to the proper generator.
- if (output_type == functions::kBundleData) {
- BundleDataTargetGenerator generator(target.get(), scope, function_call,
- err);
- generator.Run();
- } else if (output_type == functions::kCreateBundle) {
- CreateBundleTargetGenerator generator(target.get(), scope, function_call,
- err);
- generator.Run();
- } else if (output_type == functions::kCopy) {
- CopyTargetGenerator generator(target.get(), scope, function_call, err);
- generator.Run();
- } else if (output_type == functions::kAction) {
- ActionTargetGenerator generator(target.get(), scope, function_call,
- Target::ACTION, err);
- generator.Run();
- } else if (output_type == functions::kActionForEach) {
- ActionTargetGenerator generator(target.get(), scope, function_call,
- Target::ACTION_FOREACH, err);
- generator.Run();
- } else if (output_type == functions::kExecutable) {
- BinaryTargetGenerator generator(target.get(), scope, function_call,
- Target::EXECUTABLE, err);
- generator.Run();
- } else if (output_type == functions::kGroup) {
- GroupTargetGenerator generator(target.get(), scope, function_call, err);
- generator.Run();
- } else if (output_type == functions::kLoadableModule) {
- BinaryTargetGenerator generator(target.get(), scope, function_call,
- Target::LOADABLE_MODULE, err);
- generator.Run();
- } else if (output_type == functions::kSharedLibrary) {
- BinaryTargetGenerator generator(target.get(), scope, function_call,
- Target::SHARED_LIBRARY, err);
- generator.Run();
- } else if (output_type == functions::kSourceSet) {
- BinaryTargetGenerator generator(target.get(), scope, function_call,
- Target::SOURCE_SET, err);
- generator.Run();
- } else if (output_type == functions::kStaticLibrary) {
- BinaryTargetGenerator generator(target.get(), scope, function_call,
- Target::STATIC_LIBRARY, err);
- generator.Run();
- } else if (output_type == functions::kGeneratedFile) {
- GeneratedFileTargetGenerator generator(target.get(), scope, function_call,
- Target::GENERATED_FILE, err);
- generator.Run();
- } else if (output_type == functions::kRustLibrary) {
- BinaryTargetGenerator generator(target.get(), scope, function_call,
- Target::RUST_LIBRARY, err);
- generator.Run();
- } else {
- *err = Err(function_call, "Not a known target type",
- "I am very confused by the target type \"" + output_type + "\"");
- }
-
- if (err->has_error())
- return;
-
- // Save this target for the file.
- Scope::ItemVector* collector = scope->GetItemCollector();
- if (!collector) {
- *err = Err(function_call, "Can't define a target in this context.");
- return;
- }
- collector->push_back(std::move(target));
-}
-
-const BuildSettings* TargetGenerator::GetBuildSettings() const {
- return scope_->settings()->build_settings();
-}
-
-bool TargetGenerator::FillSources() {
- const Value* value = scope_->GetValue(variables::kSources, true);
- if (!value)
- return true;
-
- Target::FileList dest_sources;
- if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
- scope_->GetSourceDir(), &dest_sources, err_))
- return false;
- target_->sources() = std::move(dest_sources);
- return true;
-}
-
-bool TargetGenerator::FillPublic() {
- const Value* value = scope_->GetValue(variables::kPublic, true);
- if (!value)
- return true;
-
- // If the public headers are defined, don't default to public.
- target_->set_all_headers_public(false);
-
- Target::FileList dest_public;
- if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
- scope_->GetSourceDir(), &dest_public, err_))
- return false;
- target_->public_headers() = std::move(dest_public);
- return true;
-}
-
-bool TargetGenerator::FillConfigs() {
- return FillGenericConfigs(variables::kConfigs, &target_->configs());
-}
-
-bool TargetGenerator::FillDependentConfigs() {
- if (!FillGenericConfigs(variables::kAllDependentConfigs,
- &target_->all_dependent_configs()))
- return false;
-
- if (!FillGenericConfigs(variables::kPublicConfigs,
- &target_->public_configs()))
- return false;
-
- return true;
-}
-
-bool TargetGenerator::FillData() {
- const Value* value = scope_->GetValue(variables::kData, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::LIST, err_))
- return false;
-
- const std::vector<Value>& input_list = value->list_value();
- std::vector<std::string>& output_list = target_->data();
- output_list.reserve(input_list.size());
-
- const SourceDir& dir = scope_->GetSourceDir();
- const std::string& root_path =
- scope_->settings()->build_settings()->root_path_utf8();
-
- for (size_t i = 0; i < input_list.size(); i++) {
- const Value& input = input_list[i];
- if (!input.VerifyTypeIs(Value::STRING, err_))
- return false;
- const std::string input_str = input.string_value();
-
- // Treat each input as either a file or a directory, depending on the
- // last character.
- bool as_dir = !input_str.empty() && input_str[input_str.size() - 1] == '/';
-
- std::string resolved =
- dir.ResolveRelativeAs(!as_dir, input, err_, root_path, &input_str);
- if (err_->has_error())
- return false;
-
- output_list.push_back(resolved);
- }
- return true;
-}
-
-bool TargetGenerator::FillDependencies() {
- if (!FillGenericDeps(variables::kDeps, &target_->private_deps()))
- return false;
- if (!FillGenericDeps(variables::kPublicDeps, &target_->public_deps()))
- return false;
- if (!FillGenericDeps(variables::kDataDeps, &target_->data_deps()))
- return false;
-
- // "data_deps" was previously named "datadeps". For backwards-compat, read
- // the old one if no "data_deps" were specified.
- if (!scope_->GetValue(variables::kDataDeps, false)) {
- if (!FillGenericDeps("datadeps", &target_->data_deps()))
- return false;
- }
-
- return true;
-}
-
-bool TargetGenerator::FillMetadata() {
- // Need to get a mutable value to mark all values in the scope as used. This
- // cannot be done on a const Scope.
- Value* value = scope_->GetMutableValue(variables::kMetadata,
- Scope::SEARCH_CURRENT, true);
-
- if (!value)
- return true;
-
- if (!value->VerifyTypeIs(Value::SCOPE, err_))
- return false;
-
- Scope* scope_value = value->scope_value();
-
- scope_value->GetCurrentScopeValues(&target_->metadata().contents());
- scope_value->MarkAllUsed();
-
- // Metadata values should always hold lists of Values, such that they can be
- // collected and concatenated. Any additional specific type verification is
- // done at walk time.
- for (const auto& iter : target_->metadata().contents()) {
- if (!iter.second.VerifyTypeIs(Value::LIST, err_))
- return false;
- }
-
- target_->metadata().set_source_dir(scope_->GetSourceDir());
- target_->metadata().set_origin(value->origin());
- return true;
-}
-
-bool TargetGenerator::FillTestonly() {
- const Value* value = scope_->GetValue(variables::kTestonly, true);
- if (value) {
- if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
- return false;
- target_->set_testonly(value->boolean_value());
- }
- return true;
-}
-
-bool TargetGenerator::FillAssertNoDeps() {
- const Value* value = scope_->GetValue(variables::kAssertNoDeps, true);
- if (value) {
- return ExtractListOfLabelPatterns(*value, scope_->GetSourceDir(),
- &target_->assert_no_deps(), err_);
- }
- return true;
-}
-
-bool TargetGenerator::FillOutputs(bool allow_substitutions) {
- const Value* value = scope_->GetValue(variables::kOutputs, true);
- if (!value)
- return true;
-
- SubstitutionList& outputs = target_->action_values().outputs();
- if (!outputs.Parse(*value, err_))
- return false;
-
- if (!allow_substitutions) {
- // Verify no substitutions were actually used.
- if (!outputs.required_types().empty()) {
- *err_ =
- Err(*value, "Source expansions not allowed here.",
- "The outputs of this target used source {{expansions}} but this "
- "target type\ndoesn't support them. Just express the outputs "
- "literally.");
- return false;
- }
- }
-
- // Check the substitutions used are valid for this purpose.
- if (!EnsureValidSubstitutions(outputs.required_types(),
- &IsValidSourceSubstitution, value->origin(),
- err_))
- return false;
-
- // Validate that outputs are in the output dir.
- CHECK(outputs.list().size() == value->list_value().size());
- for (size_t i = 0; i < outputs.list().size(); i++) {
- if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
- value->list_value()[i]))
- return false;
- }
- return true;
-}
-
-bool TargetGenerator::FillCheckIncludes() {
- const Value* value = scope_->GetValue(variables::kCheckIncludes, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
- return false;
- target_->set_check_includes(value->boolean_value());
- return true;
-}
-
-bool TargetGenerator::FillOutputExtension() {
- const Value* value = scope_->GetValue(variables::kOutputExtension, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
- target_->set_output_extension(value->string_value());
- return true;
-}
-
-bool TargetGenerator::EnsureSubstitutionIsInOutputDir(
- const SubstitutionPattern& pattern,
- const Value& original_value) {
- if (pattern.ranges().empty()) {
- // Pattern is empty, error out (this prevents weirdness below).
- *err_ = Err(original_value, "This has an empty value in it.");
- return false;
- }
-
- if (pattern.ranges()[0].type == &SubstitutionLiteral) {
- // If the first thing is a literal, it must start with the output dir.
- if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(),
- pattern.ranges()[0].literal,
- original_value.origin(), err_))
- return false;
- } else {
- // Otherwise, the first subrange must be a pattern that expands to
- // something in the output directory.
- if (!SubstitutionIsInOutputDir(pattern.ranges()[0].type)) {
- *err_ =
- Err(original_value, "File is not inside output directory.",
- "The given file should be in the output directory. Normally you\n"
- "would specify\n\"$target_out_dir/foo\" or "
- "\"{{source_gen_dir}}/foo\".");
- return false;
- }
- }
-
- return true;
-}
-
-bool TargetGenerator::FillGenericConfigs(const char* var_name,
- UniqueVector<LabelConfigPair>* dest) {
- const Value* value = scope_->GetValue(var_name, true);
- if (value) {
- ExtractListOfUniqueLabels(*value, scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), dest, err_);
- }
- return !err_->has_error();
-}
-
-bool TargetGenerator::FillGenericDeps(const char* var_name,
- LabelTargetVector* dest) {
- const Value* value = scope_->GetValue(var_name, true);
- if (value) {
- ExtractListOfLabels(*value, scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), dest, err_);
- }
- return !err_->has_error();
-}
-
-bool TargetGenerator::FillWriteRuntimeDeps() {
- const Value* value = scope_->GetValue(variables::kWriteRuntimeDeps, true);
- if (!value)
- return true;
-
- // Compute the file name and make sure it's in the output dir.
- SourceFile source_file = scope_->GetSourceDir().ResolveRelativeFile(
- *value, err_, GetBuildSettings()->root_path_utf8());
- if (err_->has_error())
- return false;
- if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(),
- source_file.value(), value->origin(), err_))
- return false;
- OutputFile output_file(GetBuildSettings(), source_file);
- target_->set_write_runtime_deps_output(output_file);
-
- return true;
-}
diff --git a/gn/tools/gn/target_generator.h b/gn/tools/gn/target_generator.h
deleted file mode 100644
index ae54882c79b..00000000000
--- a/gn/tools/gn/target_generator.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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 TOOLS_GN_TARGET_GENERATOR_H_
-#define TOOLS_GN_TARGET_GENERATOR_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/unique_vector.h"
-
-class BuildSettings;
-class Err;
-class FunctionCallNode;
-class Scope;
-class SubstitutionPattern;
-class Value;
-
-// Fills the variables in a Target object from a Scope (the result of a script
-// execution). Target-type-specific derivations of this class will be used
-// for each different type of function call. This class implements the common
-// behavior.
-class TargetGenerator {
- public:
- TargetGenerator(Target* target,
- Scope* scope,
- const FunctionCallNode* function_call,
- Err* err);
- virtual ~TargetGenerator();
-
- void Run();
-
- // The function call is the parse tree node that invoked the target.
- // err() will be set on failure.
- static void GenerateTarget(Scope* scope,
- const FunctionCallNode* function_call,
- const std::vector<Value>& args,
- const std::string& output_type,
- Err* err);
-
- protected:
- // Derived classes implement this to do type-specific generation.
- virtual void DoRun() = 0;
-
- const BuildSettings* GetBuildSettings() const;
-
- virtual bool FillSources();
- bool FillPublic();
- bool FillConfigs();
- bool FillOutputs(bool allow_substitutions);
- bool FillCheckIncludes();
- bool FillOutputExtension();
-
- // Rrturns true if the given pattern will expand to a file in the output
- // directory. If not, returns false and sets the error, blaming the given
- // Value.
- bool EnsureSubstitutionIsInOutputDir(const SubstitutionPattern& pattern,
- const Value& original_value);
-
- Target* target_;
- Scope* scope_;
- const FunctionCallNode* function_call_;
- Err* err_;
-
- private:
- bool FillDependentConfigs(); // Includes all types of dependent configs.
- bool FillData();
- bool FillDependencies(); // Includes data dependencies.
- bool FillMetadata();
- bool FillTestonly();
- bool FillAssertNoDeps();
- bool FillWriteRuntimeDeps();
-
- // Reads configs/deps from the given var name, and uses the given setting on
- // the target to save them.
- bool FillGenericConfigs(const char* var_name,
- UniqueVector<LabelConfigPair>* dest);
- bool FillGenericDeps(const char* var_name, LabelTargetVector* dest);
-
- DISALLOW_COPY_AND_ASSIGN(TargetGenerator);
-};
-
-#endif // TOOLS_GN_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/target_unittest.cc b/gn/tools/gn/target_unittest.cc
deleted file mode 100644
index f1178462fe1..00000000000
--- a/gn/tools/gn/target_unittest.cc
+++ /dev/null
@@ -1,1310 +0,0 @@
-// 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 "tools/gn/target.h"
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/config.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/test_with_scheduler.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/toolchain.h"
-#include "util/test/test.h"
-
-namespace {
-
-// Asserts that the current global scheduler has a single unknown generated
-// file with the given name from the given target.
-void AssertSchedulerHasOneUnknownFileMatching(const Target* target,
- const SourceFile& file) {
- auto unknown = g_scheduler->GetUnknownGeneratedInputs();
- ASSERT_EQ(1u, unknown.size()); // Should be one unknown file.
- auto found = unknown.find(file);
- ASSERT_TRUE(found != unknown.end()) << file.value();
- EXPECT_TRUE(target == found->second)
- << "Target doesn't match. Expected\n "
- << target->label().GetUserVisibleName(false) << "\nBut got\n "
- << found->second->label().GetUserVisibleName(false);
-}
-
-} // namespace
-
-using TargetTest = TestWithScheduler;
-
-// Tests that lib[_dir]s are inherited across deps boundaries for static
-// libraries but not executables.
-TEST_F(TargetTest, LibInheritance) {
- TestWithScope setup;
- Err err;
-
- const LibFile lib("foo");
- const SourceDir libdir("/foo_dir/");
-
- // Leaf target with ldflags set.
- TestTarget z(setup, "//foo:z", Target::STATIC_LIBRARY);
- z.config_values().libs().push_back(lib);
- z.config_values().lib_dirs().push_back(libdir);
- ASSERT_TRUE(z.OnResolved(&err));
-
- // All lib[_dir]s should be set when target is resolved.
- ASSERT_EQ(1u, z.all_libs().size());
- EXPECT_EQ(lib, z.all_libs()[0]);
- ASSERT_EQ(1u, z.all_lib_dirs().size());
- EXPECT_EQ(libdir, z.all_lib_dirs()[0]);
-
- // Shared library target should inherit the libs from the static library
- // and its own. Its own flag should be before the inherited one.
- const LibFile second_lib("bar");
- const SourceDir second_libdir("/bar_dir/");
- TestTarget shared(setup, "//foo:shared", Target::SHARED_LIBRARY);
- shared.config_values().libs().push_back(second_lib);
- shared.config_values().lib_dirs().push_back(second_libdir);
- shared.private_deps().push_back(LabelTargetPair(&z));
- ASSERT_TRUE(shared.OnResolved(&err));
-
- ASSERT_EQ(2u, shared.all_libs().size());
- EXPECT_EQ(second_lib, shared.all_libs()[0]);
- EXPECT_EQ(lib, shared.all_libs()[1]);
- ASSERT_EQ(2u, shared.all_lib_dirs().size());
- EXPECT_EQ(second_libdir, shared.all_lib_dirs()[0]);
- EXPECT_EQ(libdir, shared.all_lib_dirs()[1]);
-
- // Executable target shouldn't get either by depending on shared.
- TestTarget exec(setup, "//foo:exec", Target::EXECUTABLE);
- exec.private_deps().push_back(LabelTargetPair(&shared));
- ASSERT_TRUE(exec.OnResolved(&err));
- EXPECT_EQ(0u, exec.all_libs().size());
- EXPECT_EQ(0u, exec.all_lib_dirs().size());
-}
-
-// Test all_dependent_configs and public_config inheritance.
-TEST_F(TargetTest, DependentConfigs) {
- TestWithScope setup;
- Err err;
-
- // Set up a dependency chain of a -> b -> c
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
- TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
- a.private_deps().push_back(LabelTargetPair(&b));
- b.private_deps().push_back(LabelTargetPair(&c));
-
- // Normal non-inherited config.
- Config config(setup.settings(), Label(SourceDir("//foo/"), "config"));
- ASSERT_TRUE(config.OnResolved(&err));
- c.configs().push_back(LabelConfigPair(&config));
-
- // All dependent config.
- Config all(setup.settings(), Label(SourceDir("//foo/"), "all"));
- ASSERT_TRUE(all.OnResolved(&err));
- c.all_dependent_configs().push_back(LabelConfigPair(&all));
-
- // Direct dependent config.
- Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- ASSERT_TRUE(direct.OnResolved(&err));
- c.public_configs().push_back(LabelConfigPair(&direct));
-
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // B should have gotten both dependent configs from C.
- ASSERT_EQ(2u, b.configs().size());
- EXPECT_EQ(&all, b.configs()[0].ptr);
- EXPECT_EQ(&direct, b.configs()[1].ptr);
- ASSERT_EQ(1u, b.all_dependent_configs().size());
- EXPECT_EQ(&all, b.all_dependent_configs()[0].ptr);
-
- // A should have just gotten the "all" dependent config from C.
- ASSERT_EQ(1u, a.configs().size());
- EXPECT_EQ(&all, a.configs()[0].ptr);
- EXPECT_EQ(&all, a.all_dependent_configs()[0].ptr);
-
- // Making an an alternate A and B with B forwarding the direct dependents.
- TestTarget a_fwd(setup, "//foo:a_fwd", Target::EXECUTABLE);
- TestTarget b_fwd(setup, "//foo:b_fwd", Target::STATIC_LIBRARY);
- a_fwd.private_deps().push_back(LabelTargetPair(&b_fwd));
- b_fwd.private_deps().push_back(LabelTargetPair(&c));
-
- ASSERT_TRUE(b_fwd.OnResolved(&err));
- ASSERT_TRUE(a_fwd.OnResolved(&err));
-
- // A_fwd should now have both configs.
- ASSERT_EQ(1u, a_fwd.configs().size());
- EXPECT_EQ(&all, a_fwd.configs()[0].ptr);
- ASSERT_EQ(1u, a_fwd.all_dependent_configs().size());
- EXPECT_EQ(&all, a_fwd.all_dependent_configs()[0].ptr);
-}
-
-// Tests that dependent configs don't propagate between toolchains.
-TEST_F(TargetTest, NoDependentConfigsBetweenToolchains) {
- TestWithScope setup;
- Err err;
-
- // Create another toolchain.
- Toolchain other_toolchain(setup.settings(),
- Label(SourceDir("//other/"), "toolchain"));
- TestWithScope::SetupToolchain(&other_toolchain);
-
- // Set up a dependency chain of |a| -> |b| -> |c| where |a| has a different
- // toolchain.
- Target a(setup.settings(),
- Label(SourceDir("//foo/"), "a", other_toolchain.label().dir(),
- other_toolchain.label().name()));
- a.set_output_type(Target::EXECUTABLE);
- EXPECT_TRUE(a.SetToolchain(&other_toolchain, &err));
- TestTarget b(setup, "//foo:b", Target::EXECUTABLE);
- TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
- a.private_deps().push_back(LabelTargetPair(&b));
- b.private_deps().push_back(LabelTargetPair(&c));
-
- // All dependent config.
- Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all"));
- ASSERT_TRUE(all_dependent.OnResolved(&err));
- c.all_dependent_configs().push_back(LabelConfigPair(&all_dependent));
-
- // Public config.
- Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public"));
- ASSERT_TRUE(public_config.OnResolved(&err));
- c.public_configs().push_back(LabelConfigPair(&public_config));
-
- // Another public config.
- Config public_config2(setup.settings(),
- Label(SourceDir("//foo/"), "public2"));
- ASSERT_TRUE(public_config2.OnResolved(&err));
- b.public_configs().push_back(LabelConfigPair(&public_config2));
-
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // B should have gotten the configs from C.
- ASSERT_EQ(3u, b.configs().size());
- EXPECT_EQ(&public_config2, b.configs()[0].ptr);
- EXPECT_EQ(&all_dependent, b.configs()[1].ptr);
- EXPECT_EQ(&public_config, b.configs()[2].ptr);
- ASSERT_EQ(1u, b.all_dependent_configs().size());
- EXPECT_EQ(&all_dependent, b.all_dependent_configs()[0].ptr);
-
- // A should not have gotten any configs from B or C.
- ASSERT_EQ(0u, a.configs().size());
- ASSERT_EQ(0u, a.all_dependent_configs().size());
-}
-
-// Tests that dependent configs propagate between toolchains if
-// propagates_configs is set.
-TEST_F(TargetTest, DependentConfigsBetweenToolchainsWhenSet) {
- TestWithScope setup;
- Err err;
-
- // Create another toolchain.
- Toolchain other_toolchain(setup.settings(),
- Label(SourceDir("//other/"), "toolchain"));
- TestWithScope::SetupToolchain(&other_toolchain);
- other_toolchain.set_propagates_configs(true);
-
- // Set up a dependency chain of |a| -> |b| where |b| has a different
- // toolchain (with propagate_configs set).
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- Target b(setup.settings(),
- Label(SourceDir("//foo/"), "b", other_toolchain.label().dir(),
- other_toolchain.label().name()));
- b.visibility().SetPublic();
- b.set_output_type(Target::SHARED_LIBRARY);
- EXPECT_TRUE(b.SetToolchain(&other_toolchain, &err));
- a.private_deps().push_back(LabelTargetPair(&b));
-
- // All dependent config.
- Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all"));
- ASSERT_TRUE(all_dependent.OnResolved(&err));
- b.all_dependent_configs().push_back(LabelConfigPair(&all_dependent));
-
- // Public config.
- Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public"));
- ASSERT_TRUE(public_config.OnResolved(&err));
- b.public_configs().push_back(LabelConfigPair(&public_config));
-
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // A should have gotten the configs from B.
- ASSERT_EQ(2u, a.configs().size());
- EXPECT_EQ(&all_dependent, a.configs()[0].ptr);
- EXPECT_EQ(&public_config, a.configs()[1].ptr);
- ASSERT_EQ(1u, a.all_dependent_configs().size());
- EXPECT_EQ(&all_dependent, a.all_dependent_configs()[0].ptr);
-}
-
-TEST_F(TargetTest, InheritLibs) {
- TestWithScope setup;
- Err err;
-
- // Create a dependency chain:
- // A (executable) -> B (shared lib) -> C (static lib) -> D (source set)
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- TestTarget b(setup, "//foo:b", Target::SHARED_LIBRARY);
- TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
- TestTarget d(setup, "//foo:d", Target::SOURCE_SET);
- a.private_deps().push_back(LabelTargetPair(&b));
- b.private_deps().push_back(LabelTargetPair(&c));
- c.private_deps().push_back(LabelTargetPair(&d));
-
- ASSERT_TRUE(d.OnResolved(&err));
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // C should have D in its inherited libs.
- std::vector<const Target*> c_inherited = c.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, c_inherited.size());
- EXPECT_EQ(&d, c_inherited[0]);
-
- // B should have C and D in its inherited libs.
- std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
- ASSERT_EQ(2u, b_inherited.size());
- EXPECT_EQ(&c, b_inherited[0]);
- EXPECT_EQ(&d, b_inherited[1]);
-
- // A should have B in its inherited libs, but not any others (the shared
- // library will include the static library and source set).
- std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, a_inherited.size());
- EXPECT_EQ(&b, a_inherited[0]);
-}
-
-TEST_F(TargetTest, InheritCompleteStaticLib) {
- TestWithScope setup;
- Err err;
-
- // Create a dependency chain:
- // A (executable) -> B (complete static lib) -> C (source set)
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
- b.set_complete_static_lib(true);
-
- const LibFile lib("foo");
- const SourceDir lib_dir("/foo_dir/");
- TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
- c.config_values().libs().push_back(lib);
- c.config_values().lib_dirs().push_back(lib_dir);
-
- a.public_deps().push_back(LabelTargetPair(&b));
- b.public_deps().push_back(LabelTargetPair(&c));
-
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // B should have C in its inherited libs.
- std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, b_inherited.size());
- EXPECT_EQ(&c, b_inherited[0]);
-
- // A should have B in its inherited libs, but not any others (the complete
- // static library will include the source set).
- std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, a_inherited.size());
- EXPECT_EQ(&b, a_inherited[0]);
-
- // A should inherit the libs and lib_dirs from the C.
- ASSERT_EQ(1u, a.all_libs().size());
- EXPECT_EQ(lib, a.all_libs()[0]);
- ASSERT_EQ(1u, a.all_lib_dirs().size());
- EXPECT_EQ(lib_dir, a.all_lib_dirs()[0]);
-}
-
-TEST_F(TargetTest, InheritCompleteStaticLibStaticLibDeps) {
- TestWithScope setup;
- Err err;
-
- // Create a dependency chain:
- // A (executable) -> B (complete static lib) -> C (static lib)
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
- b.set_complete_static_lib(true);
- TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
- a.public_deps().push_back(LabelTargetPair(&b));
- b.public_deps().push_back(LabelTargetPair(&c));
-
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // B should have C in its inherited libs.
- std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, b_inherited.size());
- EXPECT_EQ(&c, b_inherited[0]);
-
- // A should have B in its inherited libs, but not any others (the complete
- // static library will include the static library).
- std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, a_inherited.size());
- EXPECT_EQ(&b, a_inherited[0]);
-}
-
-TEST_F(TargetTest, InheritCompleteStaticLibInheritedCompleteStaticLibDeps) {
- TestWithScope setup;
- Err err;
-
- // Create a dependency chain:
- // A (executable) -> B (complete static lib) -> C (complete static lib)
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
- b.set_complete_static_lib(true);
- TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
- c.set_complete_static_lib(true);
-
- a.private_deps().push_back(LabelTargetPair(&b));
- b.private_deps().push_back(LabelTargetPair(&c));
-
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // B should have C in its inherited libs.
- std::vector<const Target*> b_inherited = b.inherited_libraries().GetOrdered();
- ASSERT_EQ(1u, b_inherited.size());
- EXPECT_EQ(&c, b_inherited[0]);
-
- // A should have B and C in its inherited libs.
- std::vector<const Target*> a_inherited = a.inherited_libraries().GetOrdered();
- ASSERT_EQ(2u, a_inherited.size());
- EXPECT_EQ(&b, a_inherited[0]);
- EXPECT_EQ(&c, a_inherited[1]);
-}
-
-TEST_F(TargetTest, NoActionDepPropgation) {
- TestWithScope setup;
- Err err;
-
- // Create a dependency chain:
- // A (exe) -> B (action) -> C (source_set)
- {
- TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
- TestTarget b(setup, "//foo:b", Target::ACTION);
- TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
-
- a.private_deps().push_back(LabelTargetPair(&b));
- b.private_deps().push_back(LabelTargetPair(&c));
-
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(b.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // The executable should not have inherited the source set across the
- // action.
- std::vector<const Target*> libs = a.inherited_libraries().GetOrdered();
- ASSERT_TRUE(libs.empty());
- }
-}
-
-TEST_F(TargetTest, GetComputedOutputName) {
- TestWithScope setup;
- Err err;
-
- // Basic target with no prefix (executable type tool in the TestWithScope has
- // no prefix) or output name.
- TestTarget basic(setup, "//foo:bar", Target::EXECUTABLE);
- ASSERT_TRUE(basic.OnResolved(&err));
- EXPECT_EQ("bar", basic.GetComputedOutputName());
-
- // Target with no prefix but an output name.
- TestTarget with_name(setup, "//foo:bar", Target::EXECUTABLE);
- with_name.set_output_name("myoutput");
- ASSERT_TRUE(with_name.OnResolved(&err));
- EXPECT_EQ("myoutput", with_name.GetComputedOutputName());
-
- // Target with a "lib" prefix (the static library tool in the TestWithScope
- // should specify a "lib" output prefix).
- TestTarget with_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY);
- ASSERT_TRUE(with_prefix.OnResolved(&err));
- EXPECT_EQ("libbar", with_prefix.GetComputedOutputName());
-
- // Target with a "lib" prefix that already has it applied. The prefix should
- // not duplicate something already in the target name.
- TestTarget dup_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY);
- dup_prefix.set_output_name("libbar");
- ASSERT_TRUE(dup_prefix.OnResolved(&err));
- EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName());
-
- // Target with an output prefix override should not have a prefix.
- TestTarget override_prefix(setup, "//foo:bar", Target::SHARED_LIBRARY);
- override_prefix.set_output_prefix_override(true);
- ASSERT_TRUE(dup_prefix.OnResolved(&err));
- EXPECT_EQ("bar", override_prefix.GetComputedOutputName());
-}
-
-// Test visibility failure case.
-TEST_F(TargetTest, VisibilityFails) {
- TestWithScope setup;
- Err err;
-
- TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY);
- b.visibility().SetPrivate(b.label().dir());
- ASSERT_TRUE(b.OnResolved(&err));
-
- // Make a target depending on "b". The dependency must have an origin to mark
- // it as user-set so we check visibility. This check should fail.
- TestTarget a(setup, "//app:a", Target::EXECUTABLE);
- a.private_deps().push_back(LabelTargetPair(&b));
- IdentifierNode origin; // Dummy origin.
- a.private_deps()[0].origin = &origin;
- ASSERT_FALSE(a.OnResolved(&err));
-}
-
-// Test visibility with a single data_dep.
-TEST_F(TargetTest, VisibilityDatadeps) {
- TestWithScope setup;
- Err err;
-
- TestTarget b(setup, "//public:b", Target::STATIC_LIBRARY);
- ASSERT_TRUE(b.OnResolved(&err));
-
- // Make a target depending on "b". The dependency must have an origin to mark
- // it as user-set so we check visibility. This check should fail.
- TestTarget a(setup, "//app:a", Target::EXECUTABLE);
- a.data_deps().push_back(LabelTargetPair(&b));
- IdentifierNode origin; // Dummy origin.
- a.data_deps()[0].origin = &origin;
- ASSERT_TRUE(a.OnResolved(&err)) << err.help_text();
-}
-
-// Tests that A -> Group -> B where the group is visible from A but B isn't,
-// passes visibility even though the group's deps get expanded into A.
-TEST_F(TargetTest, VisibilityGroup) {
- TestWithScope setup;
- Err err;
-
- IdentifierNode origin; // Dummy origin.
-
- // B has private visibility. This lets the group see it since the group is in
- // the same directory.
- TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY);
- b.visibility().SetPrivate(b.label().dir());
- ASSERT_TRUE(b.OnResolved(&err));
-
- // The group has public visibility and depends on b.
- TestTarget g(setup, "//public:g", Target::GROUP);
- g.private_deps().push_back(LabelTargetPair(&b));
- g.private_deps()[0].origin = &origin;
- ASSERT_TRUE(b.OnResolved(&err));
-
- // Make a target depending on "g". This should succeed.
- TestTarget a(setup, "//app:a", Target::EXECUTABLE);
- a.private_deps().push_back(LabelTargetPair(&g));
- a.private_deps()[0].origin = &origin;
- ASSERT_TRUE(a.OnResolved(&err));
-}
-
-// Verifies that only testonly targets can depend on other testonly targets.
-// Many of the above dependency checking cases covered the non-testonly
-// case.
-TEST_F(TargetTest, Testonly) {
- TestWithScope setup;
- Err err;
-
- // "testlib" is a test-only library.
- TestTarget testlib(setup, "//test:testlib", Target::STATIC_LIBRARY);
- testlib.set_testonly(true);
- ASSERT_TRUE(testlib.OnResolved(&err));
-
- // "test" is a test-only executable depending on testlib, this is OK.
- TestTarget test(setup, "//test:test", Target::EXECUTABLE);
- test.set_testonly(true);
- test.private_deps().push_back(LabelTargetPair(&testlib));
- ASSERT_TRUE(test.OnResolved(&err));
-
- // "product" is a non-test depending on testlib. This should fail.
- TestTarget product(setup, "//app:product", Target::EXECUTABLE);
- product.set_testonly(false);
- product.private_deps().push_back(LabelTargetPair(&testlib));
- ASSERT_FALSE(product.OnResolved(&err));
-}
-
-TEST_F(TargetTest, PublicConfigs) {
- TestWithScope setup;
- Err err;
-
- Label pub_config_label(SourceDir("//a/"), "pubconfig");
- Config pub_config(setup.settings(), pub_config_label);
- LibFile lib_name("testlib");
- pub_config.own_values().libs().push_back(lib_name);
- ASSERT_TRUE(pub_config.OnResolved(&err));
-
- // This is the destination target that has a public config.
- TestTarget dest(setup, "//a:a", Target::SOURCE_SET);
- dest.public_configs().push_back(LabelConfigPair(&pub_config));
- ASSERT_TRUE(dest.OnResolved(&err));
-
- // This target has a public dependency on dest.
- TestTarget pub(setup, "//a:pub", Target::SOURCE_SET);
- pub.public_deps().push_back(LabelTargetPair(&dest));
- ASSERT_TRUE(pub.OnResolved(&err));
-
- // Depending on the target with the public dependency should forward dest's
- // to the current target.
- TestTarget dep_on_pub(setup, "//a:dop", Target::SOURCE_SET);
- dep_on_pub.private_deps().push_back(LabelTargetPair(&pub));
- ASSERT_TRUE(dep_on_pub.OnResolved(&err));
- ASSERT_EQ(1u, dep_on_pub.configs().size());
- EXPECT_EQ(&pub_config, dep_on_pub.configs()[0].ptr);
-
- // Libs have special handling, check that they were forwarded from the
- // public config to all_libs.
- ASSERT_EQ(1u, dep_on_pub.all_libs().size());
- ASSERT_EQ(lib_name, dep_on_pub.all_libs()[0]);
-
- // This target has a private dependency on dest for forwards configs.
- TestTarget forward(setup, "//a:f", Target::SOURCE_SET);
- forward.private_deps().push_back(LabelTargetPair(&dest));
- ASSERT_TRUE(forward.OnResolved(&err));
-}
-
-// Tests that configs are ordered properly between local and pulled ones.
-TEST_F(TargetTest, ConfigOrdering) {
- TestWithScope setup;
- Err err;
-
- // Make Dep1. It has all_dependent_configs and public_configs.
- TestTarget dep1(setup, "//:dep1", Target::SOURCE_SET);
- Label dep1_all_config_label(SourceDir("//"), "dep1_all_config");
- Config dep1_all_config(setup.settings(), dep1_all_config_label);
- ASSERT_TRUE(dep1_all_config.OnResolved(&err));
- dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all_config));
-
- Label dep1_public_config_label(SourceDir("//"), "dep1_public_config");
- Config dep1_public_config(setup.settings(), dep1_public_config_label);
- ASSERT_TRUE(dep1_public_config.OnResolved(&err));
- dep1.public_configs().push_back(LabelConfigPair(&dep1_public_config));
- ASSERT_TRUE(dep1.OnResolved(&err));
-
- // Make Dep2 with the same structure.
- TestTarget dep2(setup, "//:dep2", Target::SOURCE_SET);
- Label dep2_all_config_label(SourceDir("//"), "dep2_all_config");
- Config dep2_all_config(setup.settings(), dep2_all_config_label);
- ASSERT_TRUE(dep2_all_config.OnResolved(&err));
- dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all_config));
-
- Label dep2_public_config_label(SourceDir("//"), "dep2_public_config");
- Config dep2_public_config(setup.settings(), dep2_public_config_label);
- ASSERT_TRUE(dep2_public_config.OnResolved(&err));
- dep2.public_configs().push_back(LabelConfigPair(&dep2_public_config));
- ASSERT_TRUE(dep2.OnResolved(&err));
-
- // This target depends on both previous targets.
- TestTarget target(setup, "//:foo", Target::SOURCE_SET);
- target.private_deps().push_back(LabelTargetPair(&dep1));
- target.private_deps().push_back(LabelTargetPair(&dep2));
-
- // It also has a private and public config.
- Label public_config_label(SourceDir("//"), "public");
- Config public_config(setup.settings(), public_config_label);
- ASSERT_TRUE(public_config.OnResolved(&err));
- target.public_configs().push_back(LabelConfigPair(&public_config));
-
- Label private_config_label(SourceDir("//"), "private");
- Config private_config(setup.settings(), private_config_label);
- ASSERT_TRUE(private_config.OnResolved(&err));
- target.configs().push_back(LabelConfigPair(&private_config));
-
- // Resolve to get the computed list of configs applying.
- ASSERT_TRUE(target.OnResolved(&err));
- const auto& computed = target.configs();
-
- // Order should be:
- // 1. local private
- // 2. local public
- // 3. inherited all dependent
- // 4. inherited public
- ASSERT_EQ(6u, computed.size());
- EXPECT_EQ(private_config_label, computed[0].label);
- EXPECT_EQ(public_config_label, computed[1].label);
- EXPECT_EQ(dep1_all_config_label, computed[2].label);
- EXPECT_EQ(dep2_all_config_label, computed[3].label);
- EXPECT_EQ(dep1_public_config_label, computed[4].label);
- EXPECT_EQ(dep2_public_config_label, computed[5].label);
-}
-
-// Tests that different link/depend outputs work for solink tools.
-TEST_F(TargetTest, LinkAndDepOutputs) {
- TestWithScope setup;
- Err err;
-
- Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
-
- std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
- CTool* solink_tool = solink->AsC();
- solink_tool->set_output_prefix("lib");
- solink_tool->set_default_output_extension(".so");
-
- const char kLinkPattern[] =
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
- SubstitutionPattern link_output =
- SubstitutionPattern::MakeForTest(kLinkPattern);
- solink_tool->set_link_output(link_output);
-
- const char kDependPattern[] =
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC";
- SubstitutionPattern depend_output =
- SubstitutionPattern::MakeForTest(kDependPattern);
- solink_tool->set_depend_output(depend_output);
-
- solink_tool->set_outputs(
- SubstitutionList::MakeForTest(kLinkPattern, kDependPattern));
-
- toolchain.SetTool(std::move(solink));
-
- Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.SetToolchain(&toolchain);
- ASSERT_TRUE(target.OnResolved(&err));
-
- EXPECT_EQ("./liba.so", target.link_output_file().value());
- EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value());
-
- ASSERT_EQ(1u, target.runtime_outputs().size());
- EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value());
-}
-
-// Tests that runtime_outputs works without an explicit link_output for
-// solink tools.
-TEST_F(TargetTest, RuntimeOuputs) {
- TestWithScope setup;
- Err err;
-
- Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
-
- std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
- CTool* solink_tool = solink->AsC();
- solink_tool->set_output_prefix("");
- solink_tool->set_default_output_extension(".dll");
-
- // Say the linker makes a DLL< an import library, and a symbol file we want
- // to treat as a runtime output.
- const char kLibPattern[] =
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib";
- const char kDllPattern[] =
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
- const char kPdbPattern[] = "{{root_out_dir}}/{{target_output_name}}.pdb";
- SubstitutionPattern pdb_pattern =
- SubstitutionPattern::MakeForTest(kPdbPattern);
-
- solink_tool->set_outputs(
- SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern));
-
- // Say we only want the DLL and symbol file treaded as runtime outputs.
- solink_tool->set_runtime_outputs(
- SubstitutionList::MakeForTest(kDllPattern, kPdbPattern));
-
- toolchain.SetTool(std::move(solink));
-
- Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.SetToolchain(&toolchain);
- ASSERT_TRUE(target.OnResolved(&err));
-
- EXPECT_EQ("./a.dll.lib", target.link_output_file().value());
- EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value());
-
- ASSERT_EQ(2u, target.runtime_outputs().size());
- EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value());
- EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value());
-}
-
-// Shared libraries should be inherited across public shared liobrary
-// boundaries.
-TEST_F(TargetTest, SharedInheritance) {
- TestWithScope setup;
- Err err;
-
- // Create two leaf shared libraries.
- TestTarget pub(setup, "//foo:pub", Target::SHARED_LIBRARY);
- ASSERT_TRUE(pub.OnResolved(&err));
-
- TestTarget priv(setup, "//foo:priv", Target::SHARED_LIBRARY);
- ASSERT_TRUE(priv.OnResolved(&err));
-
- // Intermediate shared library with the leaf shared libraries as
- // dependencies, one public, one private.
- TestTarget inter(setup, "//foo:inter", Target::SHARED_LIBRARY);
- inter.public_deps().push_back(LabelTargetPair(&pub));
- inter.private_deps().push_back(LabelTargetPair(&priv));
- ASSERT_TRUE(inter.OnResolved(&err));
-
- // The intermediate shared library should have both "pub" and "priv" in its
- // inherited libraries.
- std::vector<const Target*> inter_inherited =
- inter.inherited_libraries().GetOrdered();
- ASSERT_EQ(2u, inter_inherited.size());
- EXPECT_EQ(&pub, inter_inherited[0]);
- EXPECT_EQ(&priv, inter_inherited[1]);
-
- // Make a toplevel executable target depending on the intermediate one.
- TestTarget exe(setup, "//foo:exe", Target::SHARED_LIBRARY);
- exe.private_deps().push_back(LabelTargetPair(&inter));
- ASSERT_TRUE(exe.OnResolved(&err));
-
- // The exe's inherited libraries should be "inter" (because it depended
- // directly on it) and "pub" (because inter depended publicly on it).
- std::vector<const Target*> exe_inherited =
- exe.inherited_libraries().GetOrdered();
- ASSERT_EQ(2u, exe_inherited.size());
- EXPECT_EQ(&inter, exe_inherited[0]);
- EXPECT_EQ(&pub, exe_inherited[1]);
-}
-
-TEST_F(TargetTest, GeneratedInputs) {
- TestWithScope setup;
- Err err;
-
- SourceFile generated_file("//out/Debug/generated.cc");
-
- // This target has a generated input and no dependency makes it.
- TestTarget non_existent_generator(setup, "//foo:non_existent_generator",
- Target::EXECUTABLE);
- non_existent_generator.sources().push_back(generated_file);
- EXPECT_TRUE(non_existent_generator.OnResolved(&err)) << err.message();
- AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator,
- generated_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // Make a target that generates the file.
- TestTarget generator(setup, "//foo:generator", Target::ACTION);
- generator.action_values().outputs() =
- SubstitutionList::MakeForTest(generated_file.value().c_str());
- err = Err();
- EXPECT_TRUE(generator.OnResolved(&err)) << err.message();
-
- // A target that depends on the generator that uses the file as a source
- // should be OK. This uses a private dep (will be used later).
- TestTarget existent_generator(setup, "//foo:existent_generator",
- Target::SHARED_LIBRARY);
- existent_generator.sources().push_back(generated_file);
- existent_generator.private_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(existent_generator.OnResolved(&err)) << err.message();
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-
- // A target that depends on the previous one should *not* be allowed to
- // use the generated file, because existent_generator used private deps.
- // This is:
- // indirect_private --> existent_generator --[private]--> generator
- TestTarget indirect_private(setup, "//foo:indirect_private",
- Target::EXECUTABLE);
- indirect_private.sources().push_back(generated_file);
- indirect_private.public_deps().push_back(
- LabelTargetPair(&existent_generator));
- EXPECT_TRUE(indirect_private.OnResolved(&err));
- AssertSchedulerHasOneUnknownFileMatching(&indirect_private, generated_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // Now make a chain like the above but with all public deps, it should be OK.
- TestTarget existent_public(setup, "//foo:existent_public",
- Target::SHARED_LIBRARY);
- existent_public.public_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(existent_public.OnResolved(&err)) << err.message();
- TestTarget indirect_public(setup, "//foo:indirect_public",
- Target::EXECUTABLE);
- indirect_public.sources().push_back(generated_file);
- indirect_public.public_deps().push_back(LabelTargetPair(&existent_public));
- EXPECT_TRUE(indirect_public.OnResolved(&err)) << err.message();
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-}
-
-// This is sort of a Scheduler test, but is related to the above test more.
-TEST_F(TargetTest, WriteFileGeneratedInputs) {
- TestWithScope setup;
- Err err;
-
- SourceFile generated_file("//out/Debug/generated.data");
-
- // This target has a generated input and no dependency makes it.
- TestTarget non_existent_generator(setup, "//foo:non_existent_generator",
- Target::EXECUTABLE);
- non_existent_generator.sources().push_back(generated_file);
- EXPECT_TRUE(non_existent_generator.OnResolved(&err));
- AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator,
- generated_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // This target has a generated file and we've decared we write it.
- TestTarget existent_generator(setup, "//foo:existent_generator",
- Target::EXECUTABLE);
- existent_generator.sources().push_back(generated_file);
- EXPECT_TRUE(existent_generator.OnResolved(&err));
- scheduler().AddWrittenFile(generated_file);
-
- // Should be OK.
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-}
-
-TEST_F(TargetTest, WriteRuntimeDepsGeneratedInputs) {
- TestWithScope setup;
- Err err;
-
- SourceFile source_file("//out/Debug/generated.runtime_deps");
- OutputFile output_file(setup.build_settings(), source_file);
-
- TestTarget generator(setup, "//foo:generator", Target::EXECUTABLE);
- generator.set_write_runtime_deps_output(output_file);
- g_scheduler->AddWriteRuntimeDepsTarget(&generator);
-
- TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
- middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));
-
- // This target has a generated input and no dependency makes it.
- TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
- dep_missing.sources().push_back(source_file);
- EXPECT_TRUE(dep_missing.OnResolved(&err));
- AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // This target has a generated file and we've directly dependended on it.
- TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
- dep_present.sources().push_back(source_file);
- dep_present.private_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(dep_present.OnResolved(&err));
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-
- // This target has a generated file and we've indirectly dependended on it
- // via data_deps.
- TestTarget dep_indirect(setup, "//foo:with_dep", Target::EXECUTABLE);
- dep_indirect.sources().push_back(source_file);
- dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
- EXPECT_TRUE(dep_indirect.OnResolved(&err));
- AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // This target has a generated file and we've directly dependended on it
- // via data_deps.
- TestTarget data_dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
- data_dep_present.sources().push_back(source_file);
- data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(data_dep_present.OnResolved(&err));
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-}
-
-// Tests that intermediate object files generated by binary targets are also
-// considered generated for the purposes of input checking. Above, we tested
-// the failure cases for generated inputs, so here only test .o files that are
-// present.
-TEST_F(TargetTest, ObjectGeneratedInputs) {
- TestWithScope setup;
- Err err;
-
- // This target compiles the source.
- SourceFile source_file("//source.cc");
- TestTarget source_generator(setup, "//:source_target", Target::SOURCE_SET);
- source_generator.sources().push_back(source_file);
- EXPECT_TRUE(source_generator.OnResolved(&err));
-
- // This is the object file that the test toolchain generates for the source.
- SourceFile object_file("//out/Debug/obj/source_target.source.o");
-
- TestTarget final_target(setup, "//:final", Target::ACTION);
- final_target.config_values().inputs().push_back(object_file);
- EXPECT_TRUE(final_target.OnResolved(&err));
-
- AssertSchedulerHasOneUnknownFileMatching(&final_target, object_file);
-}
-
-TEST_F(TargetTest, ResolvePrecompiledHeaders) {
- TestWithScope setup;
- Err err;
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
-
- // Target with no settings, no configs, should be a no-op.
- EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
-
- // Config with PCH values.
- Config config_1(setup.settings(), Label(SourceDir("//foo/"), "c1"));
- std::string pch_1("pch.h");
- SourceFile pcs_1("//pcs.cc");
- config_1.own_values().set_precompiled_header(pch_1);
- config_1.own_values().set_precompiled_source(pcs_1);
- ASSERT_TRUE(config_1.OnResolved(&err));
- target.configs().push_back(LabelConfigPair(&config_1));
-
- // No PCH info specified on TargetTest, but the config specifies one, the
- // values should get copied to the target.
- EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
- EXPECT_EQ(pch_1, target.config_values().precompiled_header());
- EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);
-
- // Now both target and config have matching PCH values. Resolving again
- // should be a no-op since they all match.
- EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
- EXPECT_TRUE(target.config_values().precompiled_header() == pch_1);
- EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);
-
- // Second config with different PCH values.
- Config config_2(setup.settings(), Label(SourceDir("//foo/"), "c2"));
- std::string pch_2("pch2.h");
- SourceFile pcs_2("//pcs2.cc");
- config_2.own_values().set_precompiled_header(pch_2);
- config_2.own_values().set_precompiled_source(pcs_2);
- ASSERT_TRUE(config_2.OnResolved(&err));
- target.configs().push_back(LabelConfigPair(&config_2));
-
- // This should be an error since they don't match.
- EXPECT_FALSE(target.ResolvePrecompiledHeaders(&err));
-
- // Make sure the proper labels are blamed.
- EXPECT_EQ(
- "The target //foo:bar\n"
- "has conflicting precompiled header settings.\n"
- "\n"
- "From //foo:bar\n"
- " header: pch.h\n"
- " source: //pcs.cc\n"
- "\n"
- "From //foo:c2\n"
- " header: pch2.h\n"
- " source: //pcs2.cc",
- err.help_text());
-}
-
-TEST_F(TargetTest, AssertNoDeps) {
- TestWithScope setup;
- Err err;
-
- // A target.
- TestTarget a(setup, "//a", Target::SHARED_LIBRARY);
- ASSERT_TRUE(a.OnResolved(&err));
-
- // B depends on A and has an assert_no_deps for a random dir.
- TestTarget b(setup, "//b", Target::SHARED_LIBRARY);
- b.private_deps().push_back(LabelTargetPair(&a));
- b.assert_no_deps().push_back(LabelPattern(LabelPattern::RECURSIVE_DIRECTORY,
- SourceDir("//disallowed/"),
- std::string(), Label()));
- ASSERT_TRUE(b.OnResolved(&err));
-
- LabelPattern disallow_a(LabelPattern::RECURSIVE_DIRECTORY, SourceDir("//a/"),
- std::string(), Label());
-
- // C depends on B and disallows depending on A. This should fail.
- TestTarget c(setup, "//c", Target::EXECUTABLE);
- c.private_deps().push_back(LabelTargetPair(&b));
- c.assert_no_deps().push_back(disallow_a);
- ASSERT_FALSE(c.OnResolved(&err));
-
- // Validate the error message has the proper path.
- EXPECT_EQ(
- "//c:c has an assert_no_deps entry:\n"
- " //a/*\n"
- "which fails for the dependency path:\n"
- " //c:c ->\n"
- " //b:b ->\n"
- " //a:a",
- err.help_text());
- err = Err();
-
- // Add an intermediate executable with: exe -> b -> a
- TestTarget exe(setup, "//exe", Target::EXECUTABLE);
- exe.private_deps().push_back(LabelTargetPair(&b));
- ASSERT_TRUE(exe.OnResolved(&err));
-
- // D depends on the executable and disallows depending on A. Since there is
- // an intermediate executable, this should be OK.
- TestTarget d(setup, "//d", Target::EXECUTABLE);
- d.private_deps().push_back(LabelTargetPair(&exe));
- d.assert_no_deps().push_back(disallow_a);
- ASSERT_TRUE(d.OnResolved(&err));
-
- // A2 disallows depending on anything in its own directory, but the
- // assertions should not match the target itself so this should be OK.
- TestTarget a2(setup, "//a:a2", Target::EXECUTABLE);
- a2.assert_no_deps().push_back(disallow_a);
- ASSERT_TRUE(a2.OnResolved(&err));
-}
-
-TEST_F(TargetTest, PullRecursiveBundleData) {
- TestWithScope setup;
- Err err;
-
- // We have the following dependency graph:
- // A (create_bundle) -> B (bundle_data)
- // \-> C (create_bundle) -> D (bundle_data)
- // \-> E (group) -> F (bundle_data)
- // \-> B (bundle_data)
- TestTarget a(setup, "//foo:a", Target::CREATE_BUNDLE);
- TestTarget b(setup, "//foo:b", Target::BUNDLE_DATA);
- TestTarget c(setup, "//foo:c", Target::CREATE_BUNDLE);
- TestTarget d(setup, "//foo:d", Target::BUNDLE_DATA);
- TestTarget e(setup, "//foo:e", Target::GROUP);
- TestTarget f(setup, "//foo:f", Target::BUNDLE_DATA);
- a.public_deps().push_back(LabelTargetPair(&b));
- a.public_deps().push_back(LabelTargetPair(&c));
- a.public_deps().push_back(LabelTargetPair(&e));
- c.public_deps().push_back(LabelTargetPair(&d));
- e.public_deps().push_back(LabelTargetPair(&f));
- e.public_deps().push_back(LabelTargetPair(&b));
-
- a.bundle_data().root_dir() = SourceDir("//out/foo_a.bundle");
- a.bundle_data().resources_dir() = SourceDir("//out/foo_a.bundle/Resources");
-
- b.sources().push_back(SourceFile("//foo/b1.txt"));
- b.sources().push_back(SourceFile("//foo/b2.txt"));
- b.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- ASSERT_TRUE(b.OnResolved(&err));
-
- c.bundle_data().root_dir() = SourceDir("//out/foo_c.bundle");
- c.bundle_data().resources_dir() = SourceDir("//out/foo_c.bundle/Resources");
-
- d.sources().push_back(SourceFile("//foo/d.txt"));
- d.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- ASSERT_TRUE(d.OnResolved(&err));
-
- f.sources().push_back(SourceFile("//foo/f1.txt"));
- f.sources().push_back(SourceFile("//foo/f2.txt"));
- f.sources().push_back(SourceFile("//foo/f3.txt"));
- f.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
- f.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29.png"));
- f.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@2x.png"));
- f.sources().push_back(
- SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@3x.png"));
- f.action_values().outputs() = SubstitutionList::MakeForTest(
- "{{bundle_resources_dir}}/{{source_file_part}}");
- ASSERT_TRUE(f.OnResolved(&err));
-
- ASSERT_TRUE(e.OnResolved(&err));
- ASSERT_TRUE(c.OnResolved(&err));
- ASSERT_TRUE(a.OnResolved(&err));
-
- // A gets its data from B and F.
- ASSERT_EQ(a.bundle_data().file_rules().size(), 2u);
- ASSERT_EQ(a.bundle_data().file_rules()[0].sources().size(), 2u);
- ASSERT_EQ(a.bundle_data().file_rules()[1].sources().size(), 3u);
- ASSERT_EQ(a.bundle_data().assets_catalog_sources().size(), 1u);
- ASSERT_EQ(a.bundle_data().bundle_deps().size(), 2u);
-
- // C gets its data from D.
- ASSERT_EQ(c.bundle_data().file_rules().size(), 1u);
- ASSERT_EQ(c.bundle_data().file_rules()[0].sources().size(), 1u);
- ASSERT_EQ(c.bundle_data().bundle_deps().size(), 1u);
-
- // E does not have any bundle_data information but gets a list of
- // bundle_deps to propagate them during target resolution.
- ASSERT_TRUE(e.bundle_data().file_rules().empty());
- ASSERT_TRUE(e.bundle_data().assets_catalog_sources().empty());
- ASSERT_EQ(e.bundle_data().bundle_deps().size(), 2u);
-}
-
-TEST(TargetTest, CollectMetadataNoRecurse) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value b_expected(nullptr, Value::LIST);
- b_expected.list_value().push_back(Value(nullptr, true));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("b", b_expected));
-
- one.metadata().set_source_dir(SourceDir("/usr/home/files/"));
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
- data_keys.push_back("b");
-
- std::vector<std::string> walk_keys;
-
- Err err;
- std::vector<Value> result;
- std::set<const Target*> targets;
- one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
- &err);
- EXPECT_FALSE(err.has_error());
-
- std::vector<Value> expected;
- expected.push_back(Value(nullptr, "foo"));
- expected.push_back(Value(nullptr, true));
- EXPECT_EQ(result, expected);
-}
-
-TEST(TargetTest, CollectMetadataWithRecurse) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value b_expected(nullptr, Value::LIST);
- b_expected.list_value().push_back(Value(nullptr, true));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("b", b_expected));
-
- TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
- Value a_2_expected(nullptr, Value::LIST);
- a_2_expected.list_value().push_back(Value(nullptr, "bar"));
- two.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_2_expected));
-
- one.public_deps().push_back(LabelTargetPair(&two));
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
- data_keys.push_back("b");
-
- std::vector<std::string> walk_keys;
-
- Err err;
- std::vector<Value> result;
- std::set<const Target*> targets;
- one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
- &err);
- EXPECT_FALSE(err.has_error());
-
- std::vector<Value> expected;
- expected.push_back(Value(nullptr, "bar"));
- expected.push_back(Value(nullptr, "foo"));
- expected.push_back(Value(nullptr, true));
- EXPECT_EQ(result, expected);
-}
-
-TEST(TargetTest, CollectMetadataWithBarrier) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value walk_expected(nullptr, Value::LIST);
- walk_expected.list_value().push_back(
- Value(nullptr, "//foo:two(//toolchain:default)"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("walk", walk_expected));
-
- TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
- Value a_2_expected(nullptr, Value::LIST);
- a_2_expected.list_value().push_back(Value(nullptr, "bar"));
- two.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_2_expected));
-
- TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
- Value a_3_expected(nullptr, Value::LIST);
- a_3_expected.list_value().push_back(Value(nullptr, "baz"));
- three.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_3_expected));
-
- one.public_deps().push_back(LabelTargetPair(&two));
- one.public_deps().push_back(LabelTargetPair(&three));
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
-
- std::vector<std::string> walk_keys;
- walk_keys.push_back("walk");
-
- Err err;
- std::vector<Value> result;
- std::set<const Target*> targets;
- one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
- &err);
- EXPECT_FALSE(err.has_error()) << err.message();
-
- std::vector<Value> expected;
- expected.push_back(Value(nullptr, "bar"));
- expected.push_back(Value(nullptr, "foo"));
- EXPECT_EQ(result, expected) << result.size();
-}
-
-TEST(TargetTest, CollectMetadataWithError) {
- TestWithScope setup;
-
- TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
-
- Value walk_expected(nullptr, Value::LIST);
- walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
- one.metadata().contents().insert(
- std::pair<base::StringPiece, Value>("walk", walk_expected));
-
- std::vector<std::string> data_keys;
- data_keys.push_back("a");
-
- std::vector<std::string> walk_keys;
- walk_keys.push_back("walk");
-
- Err err;
- std::vector<Value> result;
- std::set<const Target*> targets;
- one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
- &err);
- EXPECT_TRUE(err.has_error());
- EXPECT_EQ(err.message(),
- "I was expecting //foo:missing(//toolchain:default) to be a "
- "dependency of //foo:one(//toolchain:default). "
- "Make sure it's included in the deps or data_deps, and that you've "
- "specified the appropriate toolchain.")
- << err.message();
-}
-
-TEST_F(TargetTest, WriteMetadataCollection) {
- TestWithScope setup;
- Err err;
-
- SourceFile source_file("//out/Debug/metadata.json");
- OutputFile output_file(setup.build_settings(), source_file);
-
- TestTarget generator(setup, "//foo:write", Target::GENERATED_FILE);
- generator.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/metadata.json");
- EXPECT_TRUE(generator.OnResolved(&err));
-
- TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
- middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(middle_data_dep.OnResolved(&err));
-
- // This target has a generated metadata input and no dependency makes it.
- TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
- dep_missing.sources().push_back(source_file);
- EXPECT_TRUE(dep_missing.OnResolved(&err));
- AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // This target has a generated file and we've directly dependended on it.
- TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
- dep_present.sources().push_back(source_file);
- dep_present.private_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(dep_present.OnResolved(&err));
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-
- // This target has a generated file and we've indirectly dependended on it
- // via data_deps.
- TestTarget dep_indirect(setup, "//foo:indirect_dep", Target::EXECUTABLE);
- dep_indirect.sources().push_back(source_file);
- dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
- EXPECT_TRUE(dep_indirect.OnResolved(&err));
- AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
- scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
-
- // This target has a generated file and we've directly dependended on it
- // via data_deps.
- TestTarget data_dep_present(setup, "//foo:with_data_dep", Target::EXECUTABLE);
- data_dep_present.sources().push_back(source_file);
- data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
- EXPECT_TRUE(data_dep_present.OnResolved(&err));
- EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
-}
diff --git a/gn/tools/gn/template.cc b/gn/tools/gn/template.cc
deleted file mode 100644
index 2b40fb3c3b1..00000000000
--- a/gn/tools/gn/template.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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 "tools/gn/template.h"
-
-#include <memory>
-#include <utility>
-
-#include "tools/gn/err.h"
-#include "tools/gn/functions.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/scope_per_file_provider.h"
-#include "tools/gn/value.h"
-#include "tools/gn/variables.h"
-
-Template::Template(const Scope* scope, const FunctionCallNode* def)
- : closure_(scope->MakeClosure()), definition_(def) {}
-
-Template::Template(std::unique_ptr<Scope> scope, const FunctionCallNode* def)
- : closure_(std::move(scope)), definition_(def) {}
-
-Template::~Template() = default;
-
-Value Template::Invoke(Scope* scope,
- const FunctionCallNode* invocation,
- const std::string& template_name,
- const std::vector<Value>& args,
- BlockNode* block,
- Err* err) const {
- // Don't allow templates to be executed from imported files. Imports are for
- // simple values only.
- if (!EnsureNotProcessingImport(invocation, scope, err))
- return Value();
-
- // First run the invocation's block. Need to allocate the scope on the heap
- // so we can pass ownership to the template.
- std::unique_ptr<Scope> invocation_scope = std::make_unique<Scope>(scope);
- if (!FillTargetBlockScope(scope, invocation, template_name, block, args,
- invocation_scope.get(), err))
- return Value();
-
- {
- // Don't allow the block of the template invocation to include other
- // targets configs, or template invocations. This must only be applied
- // to the invoker's block rather than the whole function because the
- // template execution itself must be able to define targets, etc.
- NonNestableBlock non_nestable(scope, invocation, "template invocation");
- if (!non_nestable.Enter(err))
- return Value();
-
- block->Execute(invocation_scope.get(), err);
- if (err->has_error())
- return Value();
- }
-
- // Set up the scope to run the template and set the current directory for the
- // template (which ScopePerFileProvider uses to base the target-related
- // variables target_gen_dir and target_out_dir on) to be that of the invoker.
- // This way, files don't have to be rebased and target_*_dir works the way
- // people expect (otherwise its to easy to be putting generated files in the
- // gen dir corresponding to an imported file).
- Scope template_scope(closure_.get());
- template_scope.set_source_dir(scope->GetSourceDir());
-
- ScopePerFileProvider per_file_provider(&template_scope, true);
-
- // Targets defined in the template go in the collector for the invoking file.
- template_scope.set_item_collector(scope->GetItemCollector());
-
- // We jump through some hoops to avoid copying the invocation scope when
- // setting it in the template scope (since the invocation scope may have
- // large lists of source files in it and could be expensive to copy).
- //
- // Scope.SetValue will copy the value which will in turn copy the scope, but
- // if we instead create a value and then set the scope on it, the copy can
- // be avoided.
- template_scope.SetValue(variables::kInvoker,
- Value(nullptr, std::unique_ptr<Scope>()), invocation);
- Value* invoker_value = template_scope.GetMutableValue(
- variables::kInvoker, Scope::SEARCH_NESTED, false);
- invoker_value->SetScopeValue(std::move(invocation_scope));
- template_scope.set_source_dir(scope->GetSourceDir());
-
- const base::StringPiece target_name(variables::kTargetName);
- template_scope.SetValue(
- target_name, Value(invocation, args[0].string_value()), invocation);
-
- // Actually run the template code.
- Value result = definition_->block()->Execute(&template_scope, err);
- if (err->has_error()) {
- // If there was an error, append the caller location so the error message
- // displays a stack trace of how it got here.
- err->AppendSubErr(Err(invocation, "whence it was called."));
- return Value();
- }
-
- // Check for unused variables in the invocation scope. This will find typos
- // of things the caller meant to pass to the template but the template didn't
- // read out.
- //
- // This is a bit tricky because it's theoretically possible for the template
- // to overwrite the value of "invoker" and free the Scope owned by the
- // value. So we need to look it up again and don't do anything if it doesn't
- // exist.
- invoker_value = template_scope.GetMutableValue(variables::kInvoker,
- Scope::SEARCH_NESTED, false);
- if (invoker_value && invoker_value->type() == Value::SCOPE) {
- if (!invoker_value->scope_value()->CheckForUnusedVars(err))
- return Value();
- }
-
- // Check for unused variables in the template itself.
- if (!template_scope.CheckForUnusedVars(err))
- return Value();
-
- return result;
-}
-
-LocationRange Template::GetDefinitionRange() const {
- return definition_->GetRange();
-}
diff --git a/gn/tools/gn/template_unittest.cc b/gn/tools/gn/template_unittest.cc
deleted file mode 100644
index a7a7aee4a9e..00000000000
--- a/gn/tools/gn/template_unittest.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// 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 "base/strings/string_number_conversions.h"
-#include "tools/gn/test_with_scope.h"
-#include "util/test/test.h"
-
-TEST(Template, Basic) {
- TestWithScope setup;
- TestParseInput input(
- "template(\"foo\") {\n"
- " print(target_name)\n"
- " print(invoker.bar)\n"
- "}\n"
- "foo(\"lala\") {\n"
- " bar = 42\n"
- "}");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(err.has_error()) << err.message();
-
- EXPECT_EQ("lala\n42\n", setup.print_output());
-}
-
-TEST(Template, UnusedTargetNameShouldThrowError) {
- TestWithScope setup;
- TestParseInput input(
- "template(\"foo\") {\n"
- " print(invoker.bar)\n"
- "}\n"
- "foo(\"lala\") {\n"
- " bar = 42\n"
- "}");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
-}
-
-TEST(Template, UnusedInvokerShouldThrowError) {
- TestWithScope setup;
- TestParseInput input(
- "template(\"foo\") {\n"
- " print(target_name)\n"
- "}\n"
- "foo(\"lala\") {\n"
- " bar = 42\n"
- "}");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
-}
-
-TEST(Template, UnusedVarInInvokerShouldThrowError) {
- TestWithScope setup;
- TestParseInput input(
- "template(\"foo\") {\n"
- " print(target_name)\n"
- " print(invoker.bar)\n"
- "}\n"
- "foo(\"lala\") {\n"
- " bar = 42\n"
- " baz = [ \"foo\" ]\n"
- "}");
- ASSERT_FALSE(input.has_error());
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- EXPECT_TRUE(err.has_error());
-}
-
-// Previous versions of the template implementation would copy templates by
-// value when it makes a closure. Doing a sequence of them means that every new
-// one copies all previous ones, which gives a significant blow-up in memory.
-// If this test doesn't crash with out-of-memory, it passed.
-TEST(Template, MemoryBlowUp) {
- TestWithScope setup;
- std::string code;
- for (int i = 0; i < 100; i++)
- code += "template(\"test" + base::IntToString(i) + "\") {}\n";
-
- TestParseInput input(code);
-
- Err err;
- input.parsed()->Execute(setup.scope(), &err);
- ASSERT_FALSE(input.has_error());
-}
diff --git a/gn/tools/gn/test_with_scheduler.cc b/gn/tools/gn/test_with_scheduler.cc
deleted file mode 100644
index ce7a90c7110..00000000000
--- a/gn/tools/gn/test_with_scheduler.cc
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2018 The Chromium 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 "tools/gn/test_with_scheduler.h"
-
-TestWithScheduler::TestWithScheduler() = default;
-TestWithScheduler::~TestWithScheduler() = default;
diff --git a/gn/tools/gn/test_with_scheduler.h b/gn/tools/gn/test_with_scheduler.h
deleted file mode 100644
index f380bbc0549..00000000000
--- a/gn/tools/gn/test_with_scheduler.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium 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 TOOLS_GN_TEST_WITH_SCHEDULER_H_
-#define TOOLS_GN_TEST_WITH_SCHEDULER_H_
-
-#include "base/macros.h"
-#include "tools/gn/scheduler.h"
-#include "util/msg_loop.h"
-#include "util/test/test.h"
-
-class TestWithScheduler : public testing::Test {
- protected:
- TestWithScheduler();
- ~TestWithScheduler() override;
-
- Scheduler& scheduler() { return scheduler_; }
-
- private:
- MsgLoop run_loop_;
- Scheduler scheduler_;
-
- DISALLOW_COPY_AND_ASSIGN(TestWithScheduler);
-};
-
-#endif // TOOLS_GN_TEST_WITH_SCHEDULER_H_
diff --git a/gn/tools/gn/test_with_scope.cc b/gn/tools/gn/test_with_scope.cc
deleted file mode 100644
index 80d969295be..00000000000
--- a/gn/tools/gn/test_with_scope.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// 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 "tools/gn/test_with_scope.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "tools/gn/parser.h"
-#include "tools/gn/tokenizer.h"
-
-namespace {
-
-BuildSettings CreateBuildSettingsForTest() {
- BuildSettings build_settings;
- build_settings.SetBuildDir(SourceDir("//out/Debug/"));
- return build_settings;
-}
-
-} // namespace
-
-TestWithScope::TestWithScope()
- : build_settings_(CreateBuildSettingsForTest()),
- settings_(&build_settings_, std::string()),
- toolchain_(&settings_, Label(SourceDir("//toolchain/"), "default")),
- scope_(&settings_),
- scope_progammatic_provider_(&scope_, true) {
- build_settings_.set_print_callback(
- base::Bind(&TestWithScope::AppendPrintOutput, base::Unretained(this)));
-
- settings_.set_toolchain_label(toolchain_.label());
- settings_.set_default_toolchain_label(toolchain_.label());
-
- SetupToolchain(&toolchain_);
- scope_.set_item_collector(&items_);
-}
-
-TestWithScope::~TestWithScope() = default;
-
-Label TestWithScope::ParseLabel(const std::string& str) const {
- Err err;
- Label result = Label::Resolve(SourceDir("//"), toolchain_.label(),
- Value(nullptr, str), &err);
- CHECK(!err.has_error());
- return result;
-}
-
-bool TestWithScope::ExecuteSnippet(const std::string& str, Err* err) {
- TestParseInput input(str);
- if (input.has_error()) {
- *err = input.parse_err();
- return false;
- }
-
- size_t first_item = items_.size();
- input.parsed()->Execute(&scope_, err);
- if (err->has_error())
- return false;
-
- for (size_t i = first_item; i < items_.size(); ++i) {
- CHECK(items_[i]->AsTarget() != nullptr)
- << "Only targets are supported in ExecuteSnippet()";
- items_[i]->AsTarget()->SetToolchain(&toolchain_);
- if (!items_[i]->OnResolved(err))
- return false;
- }
- return true;
-}
-
-// static
-void TestWithScope::SetupToolchain(Toolchain* toolchain) {
- Err err;
-
- // CC
- std::unique_ptr<Tool> cc_tool = Tool::CreateTool(CTool::kCToolCc);
- SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool.get());
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(std::move(cc_tool));
-
- // CXX
- std::unique_ptr<Tool> cxx_tool = Tool::CreateTool(CTool::kCToolCxx);
- SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool.get());
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_command_launcher("launcher");
- toolchain->SetTool(std::move(cxx_tool));
-
- // OBJC
- std::unique_ptr<Tool> objc_tool = Tool::CreateTool(CTool::kCToolObjC);
- SetCommandForTool(
- "objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} "
- "{{include_dirs}} -o {{output}}",
- objc_tool.get());
- objc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(std::move(objc_tool));
-
- // OBJC
- std::unique_ptr<Tool> objcxx_tool = Tool::CreateTool(CTool::kCToolObjCxx);
- SetCommandForTool(
- "objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} "
- "{{include_dirs}} -o {{output}}",
- objcxx_tool.get());
- objcxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(std::move(objcxx_tool));
-
- // Don't use RC and ASM tools in unit tests yet. Add here if needed.
-
- // ALINK
- std::unique_ptr<Tool> alink = Tool::CreateTool(CTool::kCToolAlink);
- CTool* alink_tool = alink->AsC();
- SetCommandForTool("ar {{output}} {{source}}", alink_tool);
- alink_tool->set_lib_switch("-l");
- alink_tool->set_lib_dir_switch("-L");
- alink_tool->set_output_prefix("lib");
- alink_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{target_out_dir}}/{{target_output_name}}.a"));
- toolchain->SetTool(std::move(alink));
-
- // SOLINK
- std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
- CTool* solink_tool = solink->AsC();
- SetCommandForTool(
- "ld -shared -o {{target_output_name}}.so {{inputs}} "
- "{{ldflags}} {{libs}}",
- solink_tool);
- solink_tool->set_lib_switch("-l");
- solink_tool->set_lib_dir_switch("-L");
- solink_tool->set_output_prefix("lib");
- solink_tool->set_default_output_extension(".so");
- solink_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
- toolchain->SetTool(std::move(solink));
-
- // SOLINK_MODULE
- std::unique_ptr<Tool> solink_module =
- Tool::CreateTool(CTool::kCToolSolinkModule);
- CTool* solink_module_tool = solink_module->AsC();
- SetCommandForTool(
- "ld -bundle -o {{target_output_name}}.so {{inputs}} "
- "{{ldflags}} {{libs}}",
- solink_module_tool);
- solink_module_tool->set_lib_switch("-l");
- solink_module_tool->set_lib_dir_switch("-L");
- solink_module_tool->set_output_prefix("lib");
- solink_module_tool->set_default_output_extension(".so");
- solink_module_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
- toolchain->SetTool(std::move(solink_module));
-
- // LINK
- std::unique_ptr<Tool> link = Tool::CreateTool(CTool::kCToolLink);
- CTool* link_tool = link->AsC();
- SetCommandForTool(
- "ld -o {{target_output_name}} {{source}} "
- "{{ldflags}} {{libs}}",
- link_tool);
- link_tool->set_lib_switch("-l");
- link_tool->set_lib_dir_switch("-L");
- link_tool->set_outputs(
- SubstitutionList::MakeForTest("{{root_out_dir}}/{{target_output_name}}"));
- toolchain->SetTool(std::move(link));
-
- // STAMP
- std::unique_ptr<Tool> stamp_tool =
- Tool::CreateTool(GeneralTool::kGeneralToolStamp);
- SetCommandForTool("touch {{output}}", stamp_tool.get());
- toolchain->SetTool(std::move(stamp_tool));
-
- // COPY
- std::unique_ptr<Tool> copy_tool =
- Tool::CreateTool(GeneralTool::kGeneralToolCopy);
- SetCommandForTool("cp {{source}} {{output}}", copy_tool.get());
- toolchain->SetTool(std::move(copy_tool));
-
- // COPY_BUNDLE_DATA
- std::unique_ptr<Tool> copy_bundle_data_tool =
- Tool::CreateTool(GeneralTool::kGeneralToolCopyBundleData);
- SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get());
- toolchain->SetTool(std::move(copy_bundle_data_tool));
-
- // COMPILE_XCASSETS
- std::unique_ptr<Tool> compile_xcassets_tool =
- Tool::CreateTool(GeneralTool::kGeneralToolCompileXCAssets);
- SetCommandForTool("touch {{output}}", compile_xcassets_tool.get());
- toolchain->SetTool(std::move(compile_xcassets_tool));
-
- // RUST
- std::unique_ptr<Tool> rustc_tool = Tool::CreateTool(RustTool::kRsToolRustc);
- SetCommandForTool(
- "{{rustenv}} rustc --edition=2018 --crate-name {{crate_name}} {{source}} "
- "--crate-type {{crate_type}} {{rustflags}} -o "
- "{{target_out_dir}}/"
- "{{rustc_output_prefix}}{{crate_name}}{{rustc_output_extension}} "
- "{{rustdeps}} {{externs}}",
- rustc_tool.get());
- rustc_tool->AsRust()->set_dylib_output_extension(".so");
- rustc_tool->AsRust()->set_cdylib_output_extension(".so");
- rustc_tool->AsRust()->set_staticlib_output_extension(".a");
- rustc_tool->AsRust()->set_proc_macro_output_extension(".so");
- rustc_tool->AsRust()->set_outputs(SubstitutionList::MakeForTest(
- "{{target_out_dir}}/"
- "{{rustc_output_prefix}}{{crate_name}}{{rustc_output_extension}}"));
- toolchain->SetTool(std::move(rustc_tool));
-
- toolchain->ToolchainSetupComplete();
-}
-
-// static
-void TestWithScope::SetCommandForTool(const std::string& cmd, Tool* tool) {
- Err err;
- SubstitutionPattern command;
- command.Parse(cmd, nullptr, &err);
- CHECK(!err.has_error()) << "Couldn't parse \"" << cmd << "\", "
- << "got " << err.message();
- tool->set_command(command);
-}
-
-void TestWithScope::AppendPrintOutput(const std::string& str) {
- print_output_.append(str);
-}
-
-TestParseInput::TestParseInput(const std::string& input)
- : input_file_(SourceFile("//test")) {
- input_file_.SetContents(input);
-
- tokens_ = Tokenizer::Tokenize(&input_file_, &parse_err_);
- if (!parse_err_.has_error())
- parsed_ = Parser::Parse(tokens_, &parse_err_);
-}
-
-TestParseInput::~TestParseInput() = default;
-
-TestTarget::TestTarget(const TestWithScope& setup,
- const std::string& label_string,
- Target::OutputType type)
- : Target(setup.settings(), setup.ParseLabel(label_string)) {
- visibility().SetPublic();
- set_output_type(type);
- SetToolchain(setup.toolchain());
-}
-
-TestTarget::~TestTarget() = default;
diff --git a/gn/tools/gn/test_with_scope.h b/gn/tools/gn/test_with_scope.h
deleted file mode 100644
index 897ef1b9c88..00000000000
--- a/gn/tools/gn/test_with_scope.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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 TOOLS_GN_TEST_WITH_SCOPE_H_
-#define TOOLS_GN_TEST_WITH_SCOPE_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/c_tool.h"
-#include "tools/gn/err.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/input_file.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/rust_tool.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/scope_per_file_provider.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target.h"
-#include "tools/gn/token.h"
-#include "tools/gn/toolchain.h"
-#include "tools/gn/value.h"
-
-// A helper class for setting up a Scope that a test can use. It makes a
-// toolchain and sets up all the build state.
-class TestWithScope {
- public:
- TestWithScope();
- ~TestWithScope();
-
- BuildSettings* build_settings() { return &build_settings_; }
- Settings* settings() { return &settings_; }
- const Settings* settings() const { return &settings_; }
- Toolchain* toolchain() { return &toolchain_; }
- const Toolchain* toolchain() const { return &toolchain_; }
- Scope* scope() { return &scope_; }
- const Scope::ItemVector& items() { return items_; }
-
- // This buffer accumulates output from any print() commands executed in the
- // context of this test. Note that the implementation of this is not
- // threadsafe so don't write tests that call print from multiple threads.
- std::string& print_output() { return print_output_; }
-
- // Parse the given string into a label in the default toolchain. This will
- // assert if the label isn't valid (this is intended for hardcoded labels).
- Label ParseLabel(const std::string& str) const;
-
- // Parses, evaluates, and resolves targets from the given snippet of code.
- // All targets must be defined in dependency order (does not use a Builder,
- // just blindly resolves all targets in order).
- bool ExecuteSnippet(const std::string& str, Err* err);
-
- // Fills in the tools for the given toolchain with reasonable default values.
- // The toolchain in this object will be automatically set up with this
- // function, it is exposed to allow tests to get the same functionality for
- // other toolchains they make.
- static void SetupToolchain(Toolchain* toolchain);
-
- // Sets the given text command on the given tool, parsing it as a
- // substitution pattern. This will assert if the input is malformed. This is
- // designed to help setting up Tools for tests.
- static void SetCommandForTool(const std::string& cmd, Tool* tool);
-
- private:
- void AppendPrintOutput(const std::string& str);
-
- BuildSettings build_settings_;
- Settings settings_;
- Toolchain toolchain_;
- Scope scope_;
- Scope::ItemVector items_;
-
- // Supplies the scope with built-in variables like root_out_dir.
- ScopePerFileProvider scope_progammatic_provider_;
-
- std::string print_output_;
-
- DISALLOW_COPY_AND_ASSIGN(TestWithScope);
-};
-
-// Helper class to treat some string input as a file.
-//
-// Instantiate it with the contents you want, be sure to check for error, and
-// then you can execute the ParseNode or whatever.
-class TestParseInput {
- public:
- explicit TestParseInput(const std::string& input);
- ~TestParseInput();
-
- // Indicates whether and what error occurred during tokenizing and parsing.
- bool has_error() const { return parse_err_.has_error(); }
- const Err& parse_err() const { return parse_err_; }
-
- const InputFile& input_file() const { return input_file_; }
- const std::vector<Token>& tokens() const { return tokens_; }
- const ParseNode* parsed() const { return parsed_.get(); }
-
- private:
- InputFile input_file_;
-
- std::vector<Token> tokens_;
- std::unique_ptr<ParseNode> parsed_;
-
- Err parse_err_;
-
- DISALLOW_COPY_AND_ASSIGN(TestParseInput);
-};
-
-// Shortcut for creating targets for tests that take the test setup, a pretty-
-// style label, and a target type and sets everything up. The target will
-// default to public visibility.
-class TestTarget : public Target {
- public:
- TestTarget(const TestWithScope& setup,
- const std::string& label_string,
- Target::OutputType type);
- ~TestTarget() override;
-};
-
-#endif // TOOLS_GN_TEST_WITH_SCOPE_H_
diff --git a/gn/tools/gn/token.cc b/gn/tools/gn/token.cc
deleted file mode 100644
index 3c016fdb135..00000000000
--- a/gn/tools/gn/token.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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 "tools/gn/token.h"
-
-#include "base/logging.h"
-
-Token::Token() : type_(INVALID), value_() {}
-
-Token::Token(const Location& location, Type t, const base::StringPiece& v)
- : type_(t), value_(v), location_(location) {}
-
-Token::Token(const Token& other) = default;
-
-bool Token::IsIdentifierEqualTo(const char* v) const {
- return type_ == IDENTIFIER && value_ == v;
-}
-
-bool Token::IsStringEqualTo(const char* v) const {
- return type_ == STRING && value_ == v;
-}
diff --git a/gn/tools/gn/token.h b/gn/tools/gn/token.h
deleted file mode 100644
index 126201ca772..00000000000
--- a/gn/tools/gn/token.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 TOOLS_GN_TOKEN_H_
-#define TOOLS_GN_TOKEN_H_
-
-#include "base/strings/string_piece.h"
-#include "tools/gn/location.h"
-
-class Token {
- public:
- enum Type {
- INVALID,
- INTEGER, // 123
- STRING, // "blah"
- TRUE_TOKEN, // Not "TRUE" to avoid collisions with #define in windows.h.
- FALSE_TOKEN,
-
- // Various operators.
- EQUAL,
- PLUS,
- MINUS,
- PLUS_EQUALS,
- MINUS_EQUALS,
- EQUAL_EQUAL,
- NOT_EQUAL,
- LESS_EQUAL,
- GREATER_EQUAL,
- LESS_THAN,
- GREATER_THAN,
- BOOLEAN_AND,
- BOOLEAN_OR,
- BANG,
- DOT,
-
- LEFT_PAREN,
- RIGHT_PAREN,
- LEFT_BRACKET,
- RIGHT_BRACKET,
- LEFT_BRACE,
- RIGHT_BRACE,
-
- IF,
- ELSE,
- IDENTIFIER, // foo
- COMMA, // ,
- UNCLASSIFIED_COMMENT, // #...\n, of unknown style (will be converted
- // to one below)
- LINE_COMMENT, // #...\n on a line alone.
- SUFFIX_COMMENT, // #...\n on a line following other code.
- BLOCK_COMMENT, // #...\n line comment, but free-standing.
-
- UNCLASSIFIED_OPERATOR,
-
- NUM_TYPES
- };
-
- Token();
- Token(const Location& location, Type t, const base::StringPiece& v);
- Token(const Token& other);
-
- Type type() const { return type_; }
- const base::StringPiece& value() const { return value_; }
- const Location& location() const { return location_; }
- void set_location(Location location) { location_ = location; }
- LocationRange range() const {
- return LocationRange(
- location_,
- Location(location_.file(), location_.line_number(),
- location_.column_number() + static_cast<int>(value_.size()),
- location_.byte() + static_cast<int>(value_.size())));
- }
-
- // Helper functions for comparing this token to something.
- bool IsIdentifierEqualTo(const char* v) const;
- bool IsStringEqualTo(const char* v) const;
-
- private:
- Type type_;
- base::StringPiece value_;
- Location location_;
-};
-
-#endif // TOOLS_GN_TOKEN_H_
diff --git a/gn/tools/gn/tokenizer.cc b/gn/tools/gn/tokenizer.cc
deleted file mode 100644
index 09f0b4b7d14..00000000000
--- a/gn/tools/gn/tokenizer.cc
+++ /dev/null
@@ -1,402 +0,0 @@
-// 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 "tools/gn/tokenizer.h"
-
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/input_file.h"
-
-namespace {
-
-bool CouldBeTwoCharOperatorBegin(char c) {
- return c == '<' || c == '>' || c == '!' || c == '=' || c == '-' || c == '+' ||
- c == '|' || c == '&';
-}
-
-bool CouldBeTwoCharOperatorEnd(char c) {
- return c == '=' || c == '|' || c == '&';
-}
-
-bool CouldBeOneCharOperator(char c) {
- return c == '=' || c == '<' || c == '>' || c == '+' || c == '!' || c == ':' ||
- c == '|' || c == '&' || c == '-';
-}
-
-bool CouldBeOperator(char c) {
- return CouldBeOneCharOperator(c) || CouldBeTwoCharOperatorBegin(c);
-}
-
-bool IsScoperChar(char c) {
- return c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}';
-}
-
-Token::Type GetSpecificOperatorType(base::StringPiece value) {
- if (value == "=")
- return Token::EQUAL;
- if (value == "+")
- return Token::PLUS;
- if (value == "-")
- return Token::MINUS;
- if (value == "+=")
- return Token::PLUS_EQUALS;
- if (value == "-=")
- return Token::MINUS_EQUALS;
- if (value == "==")
- return Token::EQUAL_EQUAL;
- if (value == "!=")
- return Token::NOT_EQUAL;
- if (value == "<=")
- return Token::LESS_EQUAL;
- if (value == ">=")
- return Token::GREATER_EQUAL;
- if (value == "<")
- return Token::LESS_THAN;
- if (value == ">")
- return Token::GREATER_THAN;
- if (value == "&&")
- return Token::BOOLEAN_AND;
- if (value == "||")
- return Token::BOOLEAN_OR;
- if (value == "!")
- return Token::BANG;
- if (value == ".")
- return Token::DOT;
- return Token::INVALID;
-}
-
-} // namespace
-
-Tokenizer::Tokenizer(const InputFile* input_file, Err* err)
- : input_file_(input_file), input_(input_file->contents()), err_(err) {}
-
-Tokenizer::~Tokenizer() = default;
-
-// static
-std::vector<Token> Tokenizer::Tokenize(const InputFile* input_file, Err* err) {
- Tokenizer t(input_file, err);
- return t.Run();
-}
-
-std::vector<Token> Tokenizer::Run() {
- DCHECK(tokens_.empty());
- while (!done()) {
- AdvanceToNextToken();
- if (done())
- break;
- Location location = GetCurrentLocation();
-
- Token::Type type = ClassifyCurrent();
- if (type == Token::INVALID) {
- *err_ = GetErrorForInvalidToken(location);
- break;
- }
- size_t token_begin = cur_;
- AdvanceToEndOfToken(location, type);
- if (has_error())
- break;
- size_t token_end = cur_;
-
- base::StringPiece token_value(&input_.data()[token_begin],
- token_end - token_begin);
-
- if (type == Token::UNCLASSIFIED_OPERATOR) {
- type = GetSpecificOperatorType(token_value);
- } else if (type == Token::IDENTIFIER) {
- if (token_value == "if")
- type = Token::IF;
- else if (token_value == "else")
- type = Token::ELSE;
- else if (token_value == "true")
- type = Token::TRUE_TOKEN;
- else if (token_value == "false")
- type = Token::FALSE_TOKEN;
- } else if (type == Token::UNCLASSIFIED_COMMENT) {
- if (AtStartOfLine(token_begin) &&
- // If it's a standalone comment, but is a continuation of a comment on
- // a previous line, then instead make it a continued suffix comment.
- (tokens_.empty() || tokens_.back().type() != Token::SUFFIX_COMMENT ||
- tokens_.back().location().line_number() + 1 !=
- location.line_number() ||
- tokens_.back().location().column_number() !=
- location.column_number())) {
- type = Token::LINE_COMMENT;
- if (!at_end()) // Could be EOF.
- Advance(); // The current \n.
- // If this comment is separated from the next syntax element, then we
- // want to tag it as a block comment. This will become a standalone
- // statement at the parser level to keep this comment separate, rather
- // than attached to the subsequent statement.
- while (!at_end() && IsCurrentWhitespace()) {
- if (IsCurrentNewline()) {
- type = Token::BLOCK_COMMENT;
- break;
- }
- Advance();
- }
- } else {
- type = Token::SUFFIX_COMMENT;
- }
- }
-
- tokens_.push_back(Token(location, type, token_value));
- }
- if (err_->has_error())
- tokens_.clear();
- return tokens_;
-}
-
-// static
-size_t Tokenizer::ByteOffsetOfNthLine(const base::StringPiece& buf, int n) {
- DCHECK_GT(n, 0);
-
- if (n == 1)
- return 0;
-
- int cur_line = 1;
- size_t cur_byte = 0;
- while (cur_byte < buf.size()) {
- if (IsNewline(buf, cur_byte)) {
- cur_line++;
- if (cur_line == n)
- return cur_byte + 1;
- }
- cur_byte++;
- }
- return static_cast<size_t>(-1);
-}
-
-// static
-bool Tokenizer::IsNewline(const base::StringPiece& buffer, size_t offset) {
- DCHECK(offset < buffer.size());
- // We may need more logic here to handle different line ending styles.
- return buffer[offset] == '\n';
-}
-
-// static
-bool Tokenizer::IsIdentifierFirstChar(char c) {
- return base::IsAsciiAlpha(c) || c == '_';
-}
-
-// static
-bool Tokenizer::IsIdentifierContinuingChar(char c) {
- // Also allow digits after the first char.
- return IsIdentifierFirstChar(c) || base::IsAsciiDigit(c);
-}
-
-void Tokenizer::AdvanceToNextToken() {
- while (!at_end() && IsCurrentWhitespace())
- Advance();
-}
-
-Token::Type Tokenizer::ClassifyCurrent() const {
- DCHECK(!at_end());
- char next_char = cur_char();
- if (base::IsAsciiDigit(next_char))
- return Token::INTEGER;
- if (next_char == '"')
- return Token::STRING;
-
- // Note: '-' handled specially below.
- if (next_char != '-' && CouldBeOperator(next_char))
- return Token::UNCLASSIFIED_OPERATOR;
-
- if (IsIdentifierFirstChar(next_char))
- return Token::IDENTIFIER;
-
- if (next_char == '[')
- return Token::LEFT_BRACKET;
- if (next_char == ']')
- return Token::RIGHT_BRACKET;
- if (next_char == '(')
- return Token::LEFT_PAREN;
- if (next_char == ')')
- return Token::RIGHT_PAREN;
- if (next_char == '{')
- return Token::LEFT_BRACE;
- if (next_char == '}')
- return Token::RIGHT_BRACE;
-
- if (next_char == '.')
- return Token::DOT;
- if (next_char == ',')
- return Token::COMMA;
-
- if (next_char == '#')
- return Token::UNCLASSIFIED_COMMENT;
-
- // For the case of '-' differentiate between a negative number and anything
- // else.
- if (next_char == '-') {
- if (!CanIncrement())
- return Token::UNCLASSIFIED_OPERATOR; // Just the minus before end of
- // file.
- char following_char = input_[cur_ + 1];
- if (base::IsAsciiDigit(following_char))
- return Token::INTEGER;
- return Token::UNCLASSIFIED_OPERATOR;
- }
-
- return Token::INVALID;
-}
-
-void Tokenizer::AdvanceToEndOfToken(const Location& location,
- Token::Type type) {
- switch (type) {
- case Token::INTEGER:
- do {
- Advance();
- } while (!at_end() && base::IsAsciiDigit(cur_char()));
- if (!at_end()) {
- // Require the char after a number to be some kind of space, scope,
- // or operator.
- char c = cur_char();
- if (!IsCurrentWhitespace() && !CouldBeOperator(c) && !IsScoperChar(c) &&
- c != ',') {
- *err_ = Err(GetCurrentLocation(), "This is not a valid number.");
- // Highlight the number.
- err_->AppendRange(LocationRange(location, GetCurrentLocation()));
- }
- }
- break;
-
- case Token::STRING: {
- char initial = cur_char();
- Advance(); // Advance past initial "
- for (;;) {
- if (at_end()) {
- *err_ = Err(LocationRange(location, GetCurrentLocation()),
- "Unterminated string literal.",
- "Don't leave me hanging like this!");
- break;
- }
- if (IsCurrentStringTerminator(initial)) {
- Advance(); // Skip past last "
- break;
- } else if (IsCurrentNewline()) {
- *err_ = Err(LocationRange(location, GetCurrentLocation()),
- "Newline in string constant.");
- }
- Advance();
- }
- break;
- }
-
- case Token::UNCLASSIFIED_OPERATOR:
- // Some operators are two characters, some are one.
- if (CouldBeTwoCharOperatorBegin(cur_char())) {
- if (CanIncrement() && CouldBeTwoCharOperatorEnd(input_[cur_ + 1]))
- Advance();
- }
- Advance();
- break;
-
- case Token::IDENTIFIER:
- while (!at_end() && IsIdentifierContinuingChar(cur_char()))
- Advance();
- break;
-
- case Token::LEFT_BRACKET:
- case Token::RIGHT_BRACKET:
- case Token::LEFT_BRACE:
- case Token::RIGHT_BRACE:
- case Token::LEFT_PAREN:
- case Token::RIGHT_PAREN:
- case Token::DOT:
- case Token::COMMA:
- Advance(); // All are one char.
- break;
-
- case Token::UNCLASSIFIED_COMMENT:
- // Eat to EOL.
- while (!at_end() && !IsCurrentNewline())
- Advance();
- break;
-
- case Token::INVALID:
- default:
- *err_ = Err(location, "Everything is all messed up",
- "Please insert system disk in drive A: and press any key.");
- NOTREACHED();
- return;
- }
-}
-
-bool Tokenizer::AtStartOfLine(size_t location) const {
- while (location > 0) {
- --location;
- char c = input_[location];
- if (c == '\n')
- return true;
- if (c != ' ')
- return false;
- }
- return true;
-}
-
-bool Tokenizer::IsCurrentWhitespace() const {
- DCHECK(!at_end());
- char c = input_[cur_];
- // Note that tab (0x09), vertical tab (0x0B), and formfeed (0x0C) are illegal.
- return c == 0x0A || c == 0x0D || c == 0x20;
-}
-
-bool Tokenizer::IsCurrentStringTerminator(char quote_char) const {
- DCHECK(!at_end());
- if (cur_char() != quote_char)
- return false;
-
- // Check for escaping. \" is not a string terminator, but \\" is. Count
- // the number of preceding backslashes.
- int num_backslashes = 0;
- for (int i = static_cast<int>(cur_) - 1; i >= 0 && input_[i] == '\\'; i--)
- num_backslashes++;
-
- // Even backslashes mean that they were escaping each other and don't count
- // as escaping this quote.
- return (num_backslashes % 2) == 0;
-}
-
-bool Tokenizer::IsCurrentNewline() const {
- return IsNewline(input_, cur_);
-}
-
-void Tokenizer::Advance() {
- DCHECK(cur_ < input_.size());
- if (IsCurrentNewline()) {
- line_number_++;
- column_number_ = 1;
- } else {
- column_number_++;
- }
- cur_++;
-}
-
-Location Tokenizer::GetCurrentLocation() const {
- return Location(input_file_, line_number_, column_number_,
- static_cast<int>(cur_));
-}
-
-Err Tokenizer::GetErrorForInvalidToken(const Location& location) const {
- std::string help;
- if (cur_char() == ';') {
- // Semicolon.
- help = "Semicolons are not needed, delete this one.";
- } else if (cur_char() == '\t') {
- // Tab.
- help =
- "You got a tab character in here. Tabs are evil. "
- "Convert to spaces.";
- } else if (cur_char() == '/' && cur_ + 1 < input_.size() &&
- (input_[cur_ + 1] == '/' || input_[cur_ + 1] == '*')) {
- // Different types of comments.
- help = "Comments should start with # instead";
- } else if (cur_char() == '\'') {
- help = "Strings are delimited by \" characters, not apostrophes.";
- } else {
- help = "I have no idea what this is.";
- }
-
- return Err(location, "Invalid token.", help);
-}
diff --git a/gn/tools/gn/tokenizer.h b/gn/tools/gn/tokenizer.h
deleted file mode 100644
index 7b66ac0dab4..00000000000
--- a/gn/tools/gn/tokenizer.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 TOOLS_GN_TOKENIZER_H_
-#define TOOLS_GN_TOKENIZER_H_
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "tools/gn/err.h"
-#include "tools/gn/token.h"
-
-class InputFile;
-
-class Tokenizer {
- public:
- static std::vector<Token> Tokenize(const InputFile* input_file, Err* err);
-
- // Counts lines in the given buffer (the first line is "1") and returns
- // the byte offset of the beginning of that line, or (size_t)-1 if there
- // aren't that many lines in the file. Note that this will return the byte
- // one past the end of the input if the last character is a newline.
- //
- // This is a helper function for error output so that the tokenizer's
- // notion of lines can be used elsewhere.
- static size_t ByteOffsetOfNthLine(const base::StringPiece& buf, int n);
-
- // Returns true if the given offset of the string piece counts as a newline.
- // The offset must be in the buffer.
- static bool IsNewline(const base::StringPiece& buffer, size_t offset);
-
- static bool IsIdentifierFirstChar(char c);
-
- static bool IsIdentifierContinuingChar(char c);
-
- private:
- // InputFile must outlive the tokenizer and all generated tokens.
- Tokenizer(const InputFile* input_file, Err* err);
- ~Tokenizer();
-
- std::vector<Token> Run();
-
- void AdvanceToNextToken();
- Token::Type ClassifyCurrent() const;
- void AdvanceToEndOfToken(const Location& location, Token::Type type);
-
- // Whether from this location back to the beginning of the line is only
- // whitespace. |location| should be the first character of the token to be
- // checked.
- bool AtStartOfLine(size_t location) const;
-
- bool IsCurrentWhitespace() const;
- bool IsCurrentNewline() const;
- bool IsCurrentStringTerminator(char quote_char) const;
-
- bool CanIncrement() const { return cur_ < input_.size() - 1; }
-
- // Increments the current location by one.
- void Advance();
-
- // Returns the current character in the file as a location.
- Location GetCurrentLocation() const;
-
- Err GetErrorForInvalidToken(const Location& location) const;
-
- bool done() const { return at_end() || has_error(); }
-
- bool at_end() const { return cur_ == input_.size(); }
- char cur_char() const { return input_[cur_]; }
-
- bool has_error() const { return err_->has_error(); }
-
- std::vector<Token> tokens_;
-
- const InputFile* input_file_;
- const base::StringPiece input_;
- Err* err_;
- size_t cur_ = 0; // Byte offset into input buffer.
-
- int line_number_ = 1;
- int column_number_ = 1;
-
- DISALLOW_COPY_AND_ASSIGN(Tokenizer);
-};
-
-#endif // TOOLS_GN_TOKENIZER_H_
diff --git a/gn/tools/gn/tokenizer_unittest.cc b/gn/tools/gn/tokenizer_unittest.cc
deleted file mode 100644
index bc6d4b18ef3..00000000000
--- a/gn/tools/gn/tokenizer_unittest.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// 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 <stddef.h>
-
-#include "tools/gn/input_file.h"
-#include "tools/gn/token.h"
-#include "tools/gn/tokenizer.h"
-#include "util/test/test.h"
-
-namespace {
-
-struct TokenExpectation {
- Token::Type type;
- const char* value;
-};
-
-template <size_t len>
-bool CheckTokenizer(const char* input, const TokenExpectation (&expect)[len]) {
- InputFile input_file(SourceFile("/test"));
- input_file.SetContents(input);
-
- Err err;
- std::vector<Token> results = Tokenizer::Tokenize(&input_file, &err);
-
- if (results.size() != len)
- return false;
- for (size_t i = 0; i < len; i++) {
- if (expect[i].type != results[i].type())
- return false;
- if (expect[i].value != results[i].value())
- return false;
- }
- return true;
-}
-
-} // namespace
-
-TEST(Tokenizer, Empty) {
- InputFile empty_string_input(SourceFile("/test"));
- empty_string_input.SetContents("");
-
- Err err;
- std::vector<Token> results = Tokenizer::Tokenize(&empty_string_input, &err);
- EXPECT_TRUE(results.empty());
-
- InputFile whitespace_input(SourceFile("/test"));
- whitespace_input.SetContents(" \r \n \r\n");
-
- results = Tokenizer::Tokenize(&whitespace_input, &err);
- EXPECT_TRUE(results.empty());
-}
-
-TEST(Tokenizer, Identifier) {
- TokenExpectation one_ident[] = {{Token::IDENTIFIER, "foo"}};
- EXPECT_TRUE(CheckTokenizer(" foo ", one_ident));
-}
-
-TEST(Tokenizer, Integer) {
- TokenExpectation integers[] = {{Token::INTEGER, "123"},
- {Token::INTEGER, "-123"}};
- EXPECT_TRUE(CheckTokenizer(" 123 -123 ", integers));
-}
-
-TEST(Tokenizer, IntegerNoSpace) {
- TokenExpectation integers[] = {{Token::INTEGER, "123"},
- {Token::INTEGER, "-123"}};
- EXPECT_TRUE(CheckTokenizer(" 123-123 ", integers));
-}
-
-TEST(Tokenizer, String) {
- TokenExpectation strings[] = {{Token::STRING, "\"foo\""},
- {Token::STRING, "\"bar\\\"baz\""},
- {Token::STRING, "\"asdf\\\\\""}};
- EXPECT_TRUE(
- CheckTokenizer(" \"foo\" \"bar\\\"baz\" \"asdf\\\\\" ", strings));
-}
-
-TEST(Tokenizer, Operator) {
- TokenExpectation operators[] = {
- {Token::MINUS, "-"},
- {Token::PLUS, "+"},
- {Token::EQUAL, "="},
- {Token::PLUS_EQUALS, "+="},
- {Token::MINUS_EQUALS, "-="},
- {Token::NOT_EQUAL, "!="},
- {Token::EQUAL_EQUAL, "=="},
- {Token::LESS_THAN, "<"},
- {Token::GREATER_THAN, ">"},
- {Token::LESS_EQUAL, "<="},
- {Token::GREATER_EQUAL, ">="},
- {Token::BANG, "!"},
- {Token::BOOLEAN_OR, "||"},
- {Token::BOOLEAN_AND, "&&"},
- {Token::DOT, "."},
- {Token::COMMA, ","},
- };
- EXPECT_TRUE(
- CheckTokenizer("- + = += -= != == < > <= >= ! || && . ,", operators));
-}
-
-TEST(Tokenizer, Scoper) {
- TokenExpectation scopers[] = {
- {Token::LEFT_BRACE, "{"}, {Token::LEFT_BRACKET, "["},
- {Token::RIGHT_BRACKET, "]"}, {Token::RIGHT_BRACE, "}"},
- {Token::LEFT_PAREN, "("}, {Token::RIGHT_PAREN, ")"},
- };
- EXPECT_TRUE(CheckTokenizer("{[ ]} ()", scopers));
-}
-
-TEST(Tokenizer, FunctionCall) {
- TokenExpectation fn[] = {
- {Token::IDENTIFIER, "fun"}, {Token::LEFT_PAREN, "("},
- {Token::STRING, "\"foo\""}, {Token::RIGHT_PAREN, ")"},
- {Token::LEFT_BRACE, "{"}, {Token::IDENTIFIER, "foo"},
- {Token::EQUAL, "="}, {Token::INTEGER, "12"},
- {Token::RIGHT_BRACE, "}"},
- };
- EXPECT_TRUE(CheckTokenizer("fun(\"foo\") {\nfoo = 12}", fn));
-}
-
-TEST(Tokenizer, Locations) {
- InputFile input(SourceFile("/test"));
- input.SetContents("1 2 \"three\"\n 4");
- Err err;
- std::vector<Token> results = Tokenizer::Tokenize(&input, &err);
-
- ASSERT_EQ(4u, results.size());
- ASSERT_TRUE(results[0].location() == Location(&input, 1, 1, 1));
- ASSERT_TRUE(results[1].location() == Location(&input, 1, 3, 3));
- ASSERT_TRUE(results[2].location() == Location(&input, 1, 5, 5));
- ASSERT_TRUE(results[3].location() == Location(&input, 2, 3, 8));
-}
-
-TEST(Tokenizer, ByteOffsetOfNthLine) {
- EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine("foo", 1));
-
- // Windows and Posix have different line endings, so check the byte at the
- // location rather than the offset.
- char input1[] = "aaa\nxaa\n\nya";
- EXPECT_EQ('x', input1[Tokenizer::ByteOffsetOfNthLine(input1, 2)]);
- EXPECT_EQ('y', input1[Tokenizer::ByteOffsetOfNthLine(input1, 4)]);
-
- char input2[3];
- input2[0] = 'a';
- input2[1] = '\n'; // Manually set to avoid Windows double-byte endings.
- input2[2] = 0;
- EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine(input2, 1));
- EXPECT_EQ(2u, Tokenizer::ByteOffsetOfNthLine(input2, 2));
-}
-
-TEST(Tokenizer, Comments) {
- TokenExpectation fn[] = {
- {Token::LINE_COMMENT, "# Stuff"},
- {Token::IDENTIFIER, "fun"},
- {Token::LEFT_PAREN, "("},
- {Token::STRING, "\"foo\""},
- {Token::RIGHT_PAREN, ")"},
- {Token::LEFT_BRACE, "{"},
- {Token::SUFFIX_COMMENT, "# Things"},
- {Token::LINE_COMMENT, "#Wee"},
- {Token::IDENTIFIER, "foo"},
- {Token::EQUAL, "="},
- {Token::INTEGER, "12"},
- {Token::SUFFIX_COMMENT, "#Zip"},
- {Token::RIGHT_BRACE, "}"},
- };
- EXPECT_TRUE(
- CheckTokenizer("# Stuff\n"
- "fun(\"foo\") { # Things\n"
- "#Wee\n"
- "foo = 12 #Zip\n"
- "}",
- fn));
-}
-
-TEST(Tokenizer, CommentsContinued) {
- // In the first test, the comments aren't horizontally aligned, so they're
- // considered separate. In the second test, they are, so "B" is a
- // continuation of "A" (another SUFFIX comment).
- TokenExpectation fn1[] = {
- {Token::IDENTIFIER, "fun"}, {Token::LEFT_PAREN, "("},
- {Token::STRING, "\"foo\""}, {Token::RIGHT_PAREN, ")"},
- {Token::LEFT_BRACE, "{"}, {Token::SUFFIX_COMMENT, "# A"},
- {Token::LINE_COMMENT, "# B"}, {Token::RIGHT_BRACE, "}"},
- };
- EXPECT_TRUE(
- CheckTokenizer("fun(\"foo\") { # A\n"
- " # B\n"
- "}",
- fn1));
-
- TokenExpectation fn2[] = {
- {Token::IDENTIFIER, "fun"}, {Token::LEFT_PAREN, "("},
- {Token::STRING, "\"foo\""}, {Token::RIGHT_PAREN, ")"},
- {Token::LEFT_BRACE, "{"}, {Token::SUFFIX_COMMENT, "# A"},
- {Token::SUFFIX_COMMENT, "# B"}, {Token::RIGHT_BRACE, "}"},
- };
- EXPECT_TRUE(CheckTokenizer(
- "fun(\"foo\") { # A\n"
- " # B\n" // Note that these are aligned, the \"s move A out.
- "}",
- fn2));
-}
diff --git a/gn/tools/gn/tool.cc b/gn/tools/gn/tool.cc
deleted file mode 100644
index d67933ee39e..00000000000
--- a/gn/tools/gn/tool.cc
+++ /dev/null
@@ -1,344 +0,0 @@
-// 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 "tools/gn/tool.h"
-
-#include "tools/gn/c_tool.h"
-#include "tools/gn/general_tool.h"
-#include "tools/gn/rust_tool.h"
-#include "tools/gn/target.h"
-
-const char* Tool::kToolNone = "";
-
-Tool::Tool(const char* n) : name_(n) {}
-
-Tool::~Tool() = default;
-
-void Tool::SetToolComplete() {
- DCHECK(!complete_);
- complete_ = true;
-
- command_.FillRequiredTypes(&substitution_bits_);
- depfile_.FillRequiredTypes(&substitution_bits_);
- description_.FillRequiredTypes(&substitution_bits_);
- outputs_.FillRequiredTypes(&substitution_bits_);
- rspfile_.FillRequiredTypes(&substitution_bits_);
- rspfile_content_.FillRequiredTypes(&substitution_bits_);
-}
-
-GeneralTool* Tool::AsGeneral() {
- return nullptr;
-}
-
-const GeneralTool* Tool::AsGeneral() const {
- return nullptr;
-}
-
-CTool* Tool::AsC() {
- return nullptr;
-}
-
-const CTool* Tool::AsC() const {
- return nullptr;
-}
-
-RustTool* Tool::AsRust() {
- return nullptr;
-}
-const RustTool* Tool::AsRust() const {
- return nullptr;
-}
-
-bool Tool::IsPatternInOutputList(const SubstitutionList& output_list,
- const SubstitutionPattern& pattern) const {
- for (const auto& cur : output_list.list()) {
- if (pattern.ranges().size() == cur.ranges().size() &&
- std::equal(pattern.ranges().begin(), pattern.ranges().end(),
- cur.ranges().begin()))
- return true;
- }
- return false;
-}
-
-bool Tool::ValidateSubstitutionList(
- const std::vector<const Substitution*>& list,
- const Value* origin,
- Err* err) const {
- for (const auto& cur_type : list) {
- if (!ValidateSubstitution(cur_type)) {
- *err = Err(*origin, "Pattern not valid here.",
- "You used the pattern " + std::string(cur_type->name) +
- " which is not valid\nfor this variable.");
- return false;
- }
- }
- return true;
-}
-
-bool Tool::ReadBool(Scope* scope, const char* var, bool* field, Err* err) {
- DCHECK(!complete_);
- const Value* v = scope->GetValue(var, true);
- if (!v)
- return true; // Not present is fine.
- if (!v->VerifyTypeIs(Value::BOOLEAN, err))
- return false;
- *field = v->boolean_value();
- return true;
-}
-
-bool Tool::ReadString(Scope* scope,
- const char* var,
- std::string* field,
- Err* err) {
- DCHECK(!complete_);
- const Value* v = scope->GetValue(var, true);
- if (!v)
- return true; // Not present is fine.
- if (!v->VerifyTypeIs(Value::STRING, err))
- return false;
- *field = v->string_value();
- return true;
-}
-
-bool Tool::ReadPattern(Scope* scope,
- const char* var,
- SubstitutionPattern* field,
- Err* err) {
- DCHECK(!complete_);
- const Value* value = scope->GetValue(var, true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- SubstitutionPattern pattern;
- if (!pattern.Parse(*value, err))
- return false;
- if (!ValidateSubstitutionList(pattern.required_types(), value, err))
- return false;
-
- *field = std::move(pattern);
- return true;
-}
-
-bool Tool::ReadPatternList(Scope* scope,
- const char* var,
- SubstitutionList* field,
- Err* err) {
- DCHECK(!complete_);
- const Value* value = scope->GetValue(var, true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::LIST, err))
- return false;
-
- SubstitutionList list;
- if (!list.Parse(*value, err))
- return false;
-
- // Validate the right kinds of patterns are used.
- if (!ValidateSubstitutionList(list.required_types(), value, err))
- return false;
-
- *field = std::move(list);
- return true;
-}
-
-bool Tool::ReadLabel(Scope* scope,
- const char* var,
- const Label& current_toolchain,
- LabelPtrPair<Pool>* field,
- Err* err) {
- DCHECK(!complete_);
- const Value* v = scope->GetValue(var, true);
- if (!v)
- return true; // Not present is fine.
-
- Label label =
- Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
- if (err->has_error())
- return false;
-
- LabelPtrPair<Pool> pair(label);
- pair.origin = defined_from();
-
- *field = std::move(pair);
- return true;
-}
-
-bool Tool::ReadOutputExtension(Scope* scope, Err* err) {
- DCHECK(!complete_);
- const Value* value = scope->GetValue("default_output_extension", true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- if (value->string_value().empty())
- return true; // Accept empty string.
-
- if (value->string_value()[0] != '.') {
- *err = Err(*value, "default_output_extension must begin with a '.'");
- return false;
- }
-
- set_default_output_extension(value->string_value());
- return true;
-}
-
-bool Tool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
- if (!ReadPattern(scope, "command", &command_, err) ||
- !ReadString(scope, "command_launcher", &command_launcher_, err) ||
- !ReadOutputExtension(scope, err) ||
- !ReadPattern(scope, "depfile", &depfile_, err) ||
- !ReadPattern(scope, "description", &description_, err) ||
- !ReadPatternList(scope, "runtime_outputs", &runtime_outputs_, err) ||
- !ReadString(scope, "output_prefix", &output_prefix_, err) ||
- !ReadPattern(scope, "default_output_dir", &default_output_dir_, err) ||
- !ReadBool(scope, "restat", &restat_, err) ||
- !ReadPattern(scope, "rspfile", &rspfile_, err) ||
- !ReadPattern(scope, "rspfile_content", &rspfile_content_, err) ||
- !ReadLabel(scope, "pool", toolchain->label(), &pool_, err)) {
- return false;
- }
- return true;
-}
-
-std::unique_ptr<Tool> Tool::CreateTool(const ParseNode* function,
- const std::string& name,
- Scope* scope,
- Toolchain* toolchain,
- Err* err) {
- std::unique_ptr<Tool> tool = CreateTool(name);
- if (!tool) {
- *err = Err(function, "Unknown tool type.");
- return nullptr;
- }
- if (CTool* c_tool = tool->AsC()) {
- if (c_tool->InitTool(scope, toolchain, err))
- return tool;
- return nullptr;
- }
- if (GeneralTool* general_tool = tool->AsGeneral()) {
- if (general_tool->InitTool(scope, toolchain, err))
- return tool;
- return nullptr;
- }
- if (RustTool* rust_tool = tool->AsRust()) {
- if (rust_tool->InitTool(scope, toolchain, err))
- return tool;
- return nullptr;
- }
- NOTREACHED();
- *err = Err(function, "Unknown tool type.");
- return nullptr;
-}
-
-// static
-std::unique_ptr<Tool> Tool::CreateTool(const std::string& name) {
- // C tools
- if (name == CTool::kCToolCc)
- return std::make_unique<CTool>(CTool::kCToolCc);
- else if (name == CTool::kCToolCxx)
- return std::make_unique<CTool>(CTool::kCToolCxx);
- else if (name == CTool::kCToolObjC)
- return std::make_unique<CTool>(CTool::kCToolObjC);
- else if (name == CTool::kCToolObjCxx)
- return std::make_unique<CTool>(CTool::kCToolObjCxx);
- else if (name == CTool::kCToolRc)
- return std::make_unique<CTool>(CTool::kCToolRc);
- else if (name == CTool::kCToolAsm)
- return std::make_unique<CTool>(CTool::kCToolAsm);
- else if (name == CTool::kCToolAlink)
- return std::make_unique<CTool>(CTool::kCToolAlink);
- else if (name == CTool::kCToolSolink)
- return std::make_unique<CTool>(CTool::kCToolSolink);
- else if (name == CTool::kCToolSolinkModule)
- return std::make_unique<CTool>(CTool::kCToolSolinkModule);
- else if (name == CTool::kCToolLink)
- return std::make_unique<CTool>(CTool::kCToolLink);
-
- // General tools
- else if (name == GeneralTool::kGeneralToolAction)
- return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolAction);
- else if (name == GeneralTool::kGeneralToolStamp)
- return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolStamp);
- else if (name == GeneralTool::kGeneralToolCopy)
- return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolCopy);
- else if (name == GeneralTool::kGeneralToolCopyBundleData)
- return std::make_unique<GeneralTool>(
- GeneralTool::kGeneralToolCopyBundleData);
- else if (name == GeneralTool::kGeneralToolCompileXCAssets)
- return std::make_unique<GeneralTool>(
- GeneralTool::kGeneralToolCompileXCAssets);
-
- // Rust tool
- else if (name == RustTool::kRsToolRustc)
- return std::make_unique<RustTool>(RustTool::kRsToolRustc);
-
- return nullptr;
-}
-
-// static
-const char* Tool::GetToolTypeForSourceType(SourceFile::Type type) {
- switch (type) {
- case SourceFile::SOURCE_C:
- return CTool::kCToolCc;
- case SourceFile::SOURCE_CPP:
- return CTool::kCToolCxx;
- case SourceFile::SOURCE_M:
- return CTool::kCToolObjC;
- case SourceFile::SOURCE_MM:
- return CTool::kCToolObjCxx;
- case SourceFile::SOURCE_ASM:
- case SourceFile::SOURCE_S:
- return CTool::kCToolAsm;
- case SourceFile::SOURCE_RC:
- return CTool::kCToolRc;
- case SourceFile::SOURCE_RS:
- return RustTool::kRsToolRustc;
- case SourceFile::SOURCE_UNKNOWN:
- case SourceFile::SOURCE_H:
- case SourceFile::SOURCE_O:
- case SourceFile::SOURCE_DEF:
- case SourceFile::SOURCE_GO:
- return kToolNone;
- default:
- NOTREACHED();
- return kToolNone;
- }
-}
-
-// static
-const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
- // The contents of this list might be suprising (i.e. stamp tool for copy
- // rules). See the header for why.
- // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
- if (target->source_types_used().RustSourceUsed())
- return RustTool::kRsToolRustc;
- switch (target->output_type()) {
- case Target::GROUP:
- return GeneralTool::kGeneralToolStamp;
- case Target::EXECUTABLE:
- return CTool::kCToolLink;
- case Target::SHARED_LIBRARY:
- return CTool::kCToolSolink;
- case Target::LOADABLE_MODULE:
- return CTool::kCToolSolinkModule;
- case Target::STATIC_LIBRARY:
- return CTool::kCToolAlink;
- case Target::SOURCE_SET:
- return GeneralTool::kGeneralToolStamp;
- case Target::ACTION:
- case Target::ACTION_FOREACH:
- case Target::BUNDLE_DATA:
- case Target::CREATE_BUNDLE:
- case Target::COPY_FILES:
- case Target::GENERATED_FILE:
- return GeneralTool::kGeneralToolStamp;
- default:
- NOTREACHED();
- return kToolNone;
- }
-}
diff --git a/gn/tools/gn/tool.h b/gn/tools/gn/tool.h
deleted file mode 100644
index b963ebf69cd..00000000000
--- a/gn/tools/gn/tool.h
+++ /dev/null
@@ -1,244 +0,0 @@
-// 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 TOOLS_GN_TOOL_H_
-#define TOOLS_GN_TOOL_H_
-
-#include <string>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/label.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/substitution_list.h"
-#include "tools/gn/substitution_pattern.h"
-
-class ParseNode;
-class Pool;
-class Target;
-class Toolchain;
-
-class CTool;
-class GeneralTool;
-class RustTool;
-
-// To add a new Tool category, create a subclass implementing SetComplete()
-// Add a new category to ToolCategories
-// Add a GetAs* function
-class Tool {
- public:
- static const char* kToolNone;
-
- virtual ~Tool();
-
- // Manual RTTI and required functions ---------------------------------------
- //
- // To implement a new tool category to compile binaries in a different way,
- // inherit this class, implement the following functions, and add the
- // appropriate ToolTypes and RTTI getters. New tools will also need to
- // implement a corresponding class inheriting NinjaBinaryTargetWriter that
- // does the actual rule writing.
-
- // Initialize tool from a scope. Child classes should override this function
- // and call the parent.
- bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
-
- // Validate the char* passed to the creation.
- virtual bool ValidateName(const char* name) const = 0;
-
- // Called when the toolchain is saving this tool, after everything is filled
- // in.
- virtual void SetComplete() = 0;
-
- // Validate substitutions in this tool.
- virtual bool ValidateSubstitution(const Substitution* sub_type) const = 0;
-
- // Manual RTTI
- virtual CTool* AsC();
- virtual const CTool* AsC() const;
- virtual GeneralTool* AsGeneral();
- virtual const GeneralTool* AsGeneral() const;
- virtual RustTool* AsRust();
- virtual const RustTool* AsRust() const;
-
- // Basic information ---------------------------------------------------------
-
- const ParseNode* defined_from() const { return defined_from_; }
- void set_defined_from(const ParseNode* df) { defined_from_ = df; }
-
- const char* name() const { return name_; }
-
- // Getters/setters ----------------------------------------------------------
- //
- // After the tool has had its attributes set, the caller must call
- // SetComplete(), at which point no other changes can be made.
-
- // Command to run.
- const SubstitutionPattern& command() const { return command_; }
- void set_command(SubstitutionPattern cmd) {
- DCHECK(!complete_);
- command_ = std::move(cmd);
- }
-
- // Launcher for the command (e.g. goma)
- const std::string& command_launcher() const {
- return command_launcher_;
- }
- void set_command_launcher(std::string l) {
- DCHECK(!complete_);
- command_launcher_ = std::move(l);
- }
-
- // Should include a leading "." if nonempty.
- const std::string& default_output_extension() const {
- return default_output_extension_;
- }
- void set_default_output_extension(std::string ext) {
- DCHECK(!complete_);
- DCHECK(ext.empty() || ext[0] == '.');
- default_output_extension_ = std::move(ext);
- }
-
- const SubstitutionPattern& default_output_dir() const {
- return default_output_dir_;
- }
- void set_default_output_dir(SubstitutionPattern dir) {
- DCHECK(!complete_);
- default_output_dir_ = std::move(dir);
- }
-
- // Dependency file (if supported).
- const SubstitutionPattern& depfile() const { return depfile_; }
- void set_depfile(SubstitutionPattern df) {
- DCHECK(!complete_);
- depfile_ = std::move(df);
- }
-
- const SubstitutionPattern& description() const { return description_; }
- void set_description(SubstitutionPattern desc) {
- DCHECK(!complete_);
- description_ = std::move(desc);
- }
-
- const SubstitutionList& outputs() const { return outputs_; }
- void set_outputs(SubstitutionList out) {
- DCHECK(!complete_);
- outputs_ = std::move(out);
- }
-
- const SubstitutionList& runtime_outputs() const { return runtime_outputs_; }
- void set_runtime_outputs(SubstitutionList run_out) {
- DCHECK(!complete_);
- runtime_outputs_ = std::move(run_out);
- }
-
- const std::string& output_prefix() const { return output_prefix_; }
- void set_output_prefix(std::string s) {
- DCHECK(!complete_);
- output_prefix_ = std::move(s);
- }
-
- bool restat() const { return restat_; }
- void set_restat(bool r) {
- DCHECK(!complete_);
- restat_ = r;
- }
-
- const SubstitutionPattern& rspfile() const { return rspfile_; }
- void set_rspfile(SubstitutionPattern rsp) {
- DCHECK(!complete_);
- rspfile_ = std::move(rsp);
- }
-
- const SubstitutionPattern& rspfile_content() const {
- return rspfile_content_;
- }
- void set_rspfile_content(SubstitutionPattern content) {
- DCHECK(!complete_);
- rspfile_content_ = std::move(content);
- }
-
- const LabelPtrPair<Pool>& pool() const { return pool_; }
- void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
-
- // Other functions ----------------------------------------------------------
-
- // Function for the above override to call to complete the tool.
- void SetToolComplete();
-
- // Substitutions required by this tool.
- const SubstitutionBits& substitution_bits() const {
- DCHECK(complete_);
- return substitution_bits_;
- }
-
- // Create a tool based on given features.
- static std::unique_ptr<Tool> CreateTool(const std::string& name);
- static std::unique_ptr<Tool> CreateTool(const ParseNode* function,
- const std::string& name,
- Scope* scope,
- Toolchain* toolchain,
- Err* err);
-
- static const char* GetToolTypeForSourceType(SourceFile::Type type);
- static const char* GetToolTypeForTargetFinalOutput(const Target* target);
-
- protected:
- explicit Tool(const char* t);
-
- // Initialization functions -------------------------------------------------
- //
- // Initialization methods used by InitTool(). If successful, will set the
- // field and return true, otherwise will return false. Must be called before
- // SetComplete().
- bool IsPatternInOutputList(const SubstitutionList& output_list,
- const SubstitutionPattern& pattern) const;
- bool ValidateSubstitutionList(const std::vector<const Substitution*>& list,
- const Value* origin,
- Err* err) const;
- bool ValidateOutputs(Err* err) const;
- bool ReadBool(Scope* scope, const char* var, bool* field, Err* err);
- bool ReadString(Scope* scope, const char* var, std::string* field, Err* err);
- bool ReadPattern(Scope* scope,
- const char* var,
- SubstitutionPattern* field,
- Err* err);
- bool ReadPatternList(Scope* scope,
- const char* var,
- SubstitutionList* field,
- Err* err);
- bool ReadLabel(Scope* scope,
- const char* var,
- const Label& current_toolchain,
- LabelPtrPair<Pool>* field,
- Err* err);
- bool ReadOutputExtension(Scope* scope, Err* err);
-
- const ParseNode* defined_from_ = nullptr;
- const char* name_ = nullptr;
-
- SubstitutionPattern command_;
- std::string command_launcher_;
- std::string default_output_extension_;
- SubstitutionPattern default_output_dir_;
- SubstitutionPattern depfile_;
- SubstitutionPattern description_;
- SubstitutionList outputs_;
- SubstitutionList runtime_outputs_;
- std::string output_prefix_;
- bool restat_ = false;
- SubstitutionPattern rspfile_;
- SubstitutionPattern rspfile_content_;
- LabelPtrPair<Pool> pool_;
-
- bool complete_ = false;
-
- SubstitutionBits substitution_bits_;
-
- DISALLOW_COPY_AND_ASSIGN(Tool);
-};
-
-#endif // TOOLS_GN_TOOL_H_
diff --git a/gn/tools/gn/toolchain.cc b/gn/tools/gn/toolchain.cc
deleted file mode 100644
index 906068d030a..00000000000
--- a/gn/tools/gn/toolchain.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// 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 "tools/gn/toolchain.h"
-
-#include <stddef.h>
-#include <string.h>
-#include <utility>
-
-#include "base/logging.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value.h"
-
-Toolchain::Toolchain(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files)
- : Item(settings, label, build_dependency_files) {}
-
-Toolchain::~Toolchain() = default;
-
-Toolchain* Toolchain::AsToolchain() {
- return this;
-}
-
-const Toolchain* Toolchain::AsToolchain() const {
- return this;
-}
-
-Tool* Toolchain::GetTool(const char* name) {
- DCHECK(name != Tool::kToolNone);
- auto pair = tools_.find(name);
- if (pair != tools_.end()) {
- return pair->second.get();
- }
- return nullptr;
-}
-
-const Tool* Toolchain::GetTool(const char* name) const {
- DCHECK(name != Tool::kToolNone);
- auto pair = tools_.find(name);
- if (pair != tools_.end()) {
- return pair->second.get();
- }
- return nullptr;
-}
-
-GeneralTool* Toolchain::GetToolAsGeneral(const char* name) {
- if (Tool* tool = GetTool(name)) {
- return tool->AsGeneral();
- }
- return nullptr;
-}
-
-const GeneralTool* Toolchain::GetToolAsGeneral(const char* name) const {
- if (const Tool* tool = GetTool(name)) {
- return tool->AsGeneral();
- }
- return nullptr;
-}
-
-CTool* Toolchain::GetToolAsC(const char* name) {
- if (Tool* tool = GetTool(name)) {
- return tool->AsC();
- }
- return nullptr;
-}
-
-const CTool* Toolchain::GetToolAsC(const char* name) const {
- if (const Tool* tool = GetTool(name)) {
- return tool->AsC();
- }
- return nullptr;
-}
-
-RustTool* Toolchain::GetToolAsRust(const char* name) {
- if (Tool* tool = GetTool(name)) {
- return tool->AsRust();
- }
- return nullptr;
-}
-
-const RustTool* Toolchain::GetToolAsRust(const char* name) const {
- if (const Tool* tool = GetTool(name)) {
- return tool->AsRust();
- }
- return nullptr;
-}
-
-void Toolchain::SetTool(std::unique_ptr<Tool> t) {
- DCHECK(t->name() != Tool::kToolNone);
- DCHECK(tools_.find(t->name()) == tools_.end());
- t->SetComplete();
- tools_[t->name()] = std::move(t);
-}
-
-void Toolchain::ToolchainSetupComplete() {
- // Collect required bits from all tools.
- for (const auto& tool : tools_) {
- substitution_bits_.MergeFrom(tool.second->substitution_bits());
- }
- setup_complete_ = true;
-}
-
-const Tool* Toolchain::GetToolForSourceType(SourceFile::Type type) const {
- return GetTool(Tool::GetToolTypeForSourceType(type));
-}
-
-const CTool* Toolchain::GetToolForSourceTypeAsC(SourceFile::Type type) const {
- return GetToolAsC(Tool::GetToolTypeForSourceType(type));
-}
-
-const GeneralTool* Toolchain::GetToolForSourceTypeAsGeneral(
- SourceFile::Type type) const {
- return GetToolAsGeneral(Tool::GetToolTypeForSourceType(type));
-}
-
-const RustTool* Toolchain::GetToolForSourceTypeAsRust(
- SourceFile::Type type) const {
- return GetToolAsRust(Tool::GetToolTypeForSourceType(type));
-}
-
-const Tool* Toolchain::GetToolForTargetFinalOutput(const Target* target) const {
- return GetTool(Tool::GetToolTypeForTargetFinalOutput(target));
-}
-
-const CTool* Toolchain::GetToolForTargetFinalOutputAsC(
- const Target* target) const {
- return GetToolAsC(Tool::GetToolTypeForTargetFinalOutput(target));
-}
-
-const GeneralTool* Toolchain::GetToolForTargetFinalOutputAsGeneral(
- const Target* target) const {
- return GetToolAsGeneral(Tool::GetToolTypeForTargetFinalOutput(target));
-}
-
-const RustTool* Toolchain::GetToolForTargetFinalOutputAsRust(
- const Target* target) const {
- return GetToolAsRust(Tool::GetToolTypeForTargetFinalOutput(target));
-}
diff --git a/gn/tools/gn/toolchain.h b/gn/tools/gn/toolchain.h
deleted file mode 100644
index afab7f302c8..00000000000
--- a/gn/tools/gn/toolchain.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// 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 TOOLS_GN_TOOLCHAIN_H_
-#define TOOLS_GN_TOOLCHAIN_H_
-
-#include <memory>
-
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-#include "tools/gn/item.h"
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/substitution_type.h"
-#include "tools/gn/tool.h"
-#include "tools/gn/value.h"
-
-// Holds information on a specific toolchain. This data is filled in when we
-// encounter a toolchain definition.
-//
-// This class is an Item so it can participate in dependency management. In
-// particular, when a target uses a toolchain, it should have a dependency on
-// that toolchain's object so that we can be sure we loaded the toolchain
-// before generating the build for that target.
-//
-// Note on threadsafety: The label of the toolchain never changes so can
-// safely be accessed from any thread at any time (we do this when asking for
-// the toolchain name). But the values in the toolchain do, so these can't
-// be accessed until this Item is resolved.
-class Toolchain : public Item {
- public:
- // The Settings of an Item is always the context in which the Item was
- // defined. For a toolchain this is confusing because this is NOT the
- // settings object that applies to the things in the toolchain.
- //
- // To get the Settings object corresponding to objects loaded in the context
- // of this toolchain (probably what you want instead), see
- // Loader::GetToolchainSettings(). Many toolchain objects may be created in a
- // given build, but only a few might be used, and the Loader is in charge of
- // this process.
- //
- // We also track the set of build files that may affect this target, please
- // refer to Scope for how this is determined.
- Toolchain(const Settings* settings,
- const Label& label,
- const std::set<SourceFile>& build_dependency_files = {});
- ~Toolchain() override;
-
- // Item overrides.
- Toolchain* AsToolchain() override;
- const Toolchain* AsToolchain() const override;
-
- // Returns null if the tool hasn't been defined.
- Tool* GetTool(const char* name);
- const Tool* GetTool(const char* name) const;
-
- // Returns null if the tool hasn't been defined or is not the correct type.
- GeneralTool* GetToolAsGeneral(const char* name);
- const GeneralTool* GetToolAsGeneral(const char* name) const;
- CTool* GetToolAsC(const char* name);
- const CTool* GetToolAsC(const char* name) const;
- RustTool* GetToolAsRust(const char* name);
- const RustTool* GetToolAsRust(const char* name) const;
-
- // Set a tool. When all tools are configured, you should call
- // ToolchainSetupComplete().
- void SetTool(std::unique_ptr<Tool> t);
-
- // Does final setup on the toolchain once all tools are known.
- void ToolchainSetupComplete();
-
- // Targets that must be resolved before compiling any targets.
- const LabelTargetVector& deps() const { return deps_; }
- LabelTargetVector& deps() { return deps_; }
-
- // Specifies build argument overrides that will be set on the base scope. It
- // will be as if these arguments were passed in on the command line. This
- // allows a toolchain to override the OS type of the default toolchain or
- // pass in other settings.
- Scope::KeyValueMap& args() { return args_; }
- const Scope::KeyValueMap& args() const { return args_; }
-
- // Specifies whether public_configs and all_dependent_configs in this
- // toolchain propagate to targets in other toolchains.
- bool propagates_configs() const { return propagates_configs_; }
- void set_propagates_configs(bool propagates_configs) {
- propagates_configs_ = propagates_configs;
- }
-
- // Returns the tool for compiling the given source file type.
- const Tool* GetToolForSourceType(SourceFile::Type type) const;
- const CTool* GetToolForSourceTypeAsC(SourceFile::Type type) const;
- const GeneralTool* GetToolForSourceTypeAsGeneral(SourceFile::Type type) const;
- const RustTool* GetToolForSourceTypeAsRust(SourceFile::Type type) const;
-
- // Returns the tool that produces the final output for the given target type.
- // This isn't necessarily the tool you would expect. For copy target, this
- // will return the stamp tool instead since the final output of a copy
- // target is to stamp the set of copies done so there is one output.
- const Tool* GetToolForTargetFinalOutput(const Target* target) const;
- const CTool* GetToolForTargetFinalOutputAsC(const Target* target) const;
- const GeneralTool* GetToolForTargetFinalOutputAsGeneral(
- const Target* target) const;
- const RustTool* GetToolForTargetFinalOutputAsRust(const Target* target) const;
-
- const SubstitutionBits& substitution_bits() const {
- DCHECK(setup_complete_);
- return substitution_bits_;
- }
-
- const std::map<const char*, std::unique_ptr<Tool>>& tools() const {
- return tools_;
- }
-
- private:
- std::map<const char*, std::unique_ptr<Tool>> tools_;
-
- bool setup_complete_ = false;
-
- // Substitutions used by the tools in this toolchain.
- SubstitutionBits substitution_bits_;
-
- LabelTargetVector deps_;
- Scope::KeyValueMap args_;
- bool propagates_configs_ = false;
-};
-
-#endif // TOOLS_GN_TOOLCHAIN_H_
diff --git a/gn/tools/gn/trace.cc b/gn/tools/gn/trace.cc
deleted file mode 100644
index 58a8c3c72e4..00000000000
--- a/gn/tools/gn/trace.cc
+++ /dev/null
@@ -1,334 +0,0 @@
-// 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 "tools/gn/trace.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <map>
-#include <mutex>
-#include <sstream>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/string_escape.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/stringprintf.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/label.h"
-
-namespace {
-
-constexpr uint64_t kNanosecondsToMicroseconds = 1'000;
-
-class TraceLog {
- public:
- TraceLog() { events_.reserve(16384); }
- // Trace items leaked intentionally.
-
- void Add(TraceItem* item) {
- std::lock_guard<std::mutex> lock(lock_);
- events_.push_back(item);
- }
-
- // Returns a copy for threadsafety.
- std::vector<TraceItem*> events() const { return events_; }
-
- private:
- std::mutex lock_;
-
- std::vector<TraceItem*> events_;
-
- DISALLOW_COPY_AND_ASSIGN(TraceLog);
-};
-
-TraceLog* trace_log = nullptr;
-
-struct Coalesced {
- Coalesced() : name_ptr(nullptr), total_duration(0.0), count(0) {}
-
- const std::string* name_ptr; // Pointer to a string with the name in it.
- double total_duration;
- int count;
-};
-
-bool DurationGreater(const TraceItem* a, const TraceItem* b) {
- return a->delta().raw() > b->delta().raw();
-}
-
-bool CoalescedDurationGreater(const Coalesced& a, const Coalesced& b) {
- return a.total_duration > b.total_duration;
-}
-
-void SummarizeParses(std::vector<const TraceItem*>& loads, std::ostream& out) {
- out << "File parse times: (time in ms, name)\n";
-
- std::sort(loads.begin(), loads.end(), &DurationGreater);
- for (auto* load : loads) {
- out << base::StringPrintf(" %8.2f ", load->delta().InMillisecondsF());
- out << load->name() << std::endl;
- }
-}
-
-void SummarizeCoalesced(std::vector<const TraceItem*>& items,
- std::ostream& out) {
- // Group by file name.
- std::map<std::string, Coalesced> coalesced;
- for (auto* item : items) {
- Coalesced& c = coalesced[item->name()];
- c.name_ptr = &item->name();
- c.total_duration += item->delta().InMillisecondsF();
- c.count++;
- }
-
- // Sort by duration.
- std::vector<Coalesced> sorted;
- for (const auto& pair : coalesced)
- sorted.push_back(pair.second);
- std::sort(sorted.begin(), sorted.end(), &CoalescedDurationGreater);
-
- for (const auto& cur : sorted) {
- out << base::StringPrintf(" %8.2f %d ", cur.total_duration, cur.count);
- out << *cur.name_ptr << std::endl;
- }
-}
-
-void SummarizeFileExecs(std::vector<const TraceItem*>& execs,
- std::ostream& out) {
- out << "File execute times: (total time in ms, # executions, name)\n";
- SummarizeCoalesced(execs, out);
-}
-
-void SummarizeScriptExecs(std::vector<const TraceItem*>& execs,
- std::ostream& out) {
- out << "Script execute times: (total time in ms, # executions, name)\n";
- SummarizeCoalesced(execs, out);
-}
-
-} // namespace
-
-TraceItem::TraceItem(Type type,
- const std::string& name,
- std::thread::id thread_id)
- : type_(type), name_(name), thread_id_(thread_id) {}
-
-TraceItem::~TraceItem() = default;
-
-ScopedTrace::ScopedTrace(TraceItem::Type t, const std::string& name)
- : item_(nullptr), done_(false) {
- if (trace_log) {
- item_ = new TraceItem(t, name, std::this_thread::get_id());
- item_->set_begin(TicksNow());
- }
-}
-
-ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label)
- : item_(nullptr), done_(false) {
- if (trace_log) {
- item_ = new TraceItem(t, label.GetUserVisibleName(false),
- std::this_thread::get_id());
- item_->set_begin(TicksNow());
- }
-}
-
-ScopedTrace::~ScopedTrace() {
- Done();
-}
-
-void ScopedTrace::SetToolchain(const Label& label) {
- if (item_)
- item_->set_toolchain(label.GetUserVisibleName(false));
-}
-
-void ScopedTrace::SetCommandLine(const base::CommandLine& cmdline) {
- if (item_)
- item_->set_cmdline(FilePathToUTF8(cmdline.GetArgumentsString()));
-}
-
-void ScopedTrace::Done() {
- if (!done_) {
- done_ = true;
- if (trace_log) {
- item_->set_end(TicksNow());
- AddTrace(item_);
- }
- }
-}
-
-void EnableTracing() {
- if (!trace_log)
- trace_log = new TraceLog;
-}
-
-bool TracingEnabled() {
- return !!trace_log;
-}
-
-void AddTrace(TraceItem* item) {
- trace_log->Add(item);
-}
-
-std::string SummarizeTraces() {
- if (!trace_log)
- return std::string();
-
- std::vector<TraceItem*> events = trace_log->events();
-
- // Classify all events.
- std::vector<const TraceItem*> parses;
- std::vector<const TraceItem*> file_execs;
- std::vector<const TraceItem*> script_execs;
- std::vector<const TraceItem*> check_headers;
- int headers_checked = 0;
- for (auto* event : events) {
- switch (event->type()) {
- case TraceItem::TRACE_FILE_PARSE:
- parses.push_back(event);
- break;
- case TraceItem::TRACE_FILE_EXECUTE:
- file_execs.push_back(event);
- break;
- case TraceItem::TRACE_SCRIPT_EXECUTE:
- script_execs.push_back(event);
- break;
- case TraceItem::TRACE_CHECK_HEADERS:
- check_headers.push_back(event);
- break;
- case TraceItem::TRACE_CHECK_HEADER:
- headers_checked++;
- break;
- case TraceItem::TRACE_IMPORT_LOAD:
- case TraceItem::TRACE_IMPORT_BLOCK:
- case TraceItem::TRACE_SETUP:
- case TraceItem::TRACE_FILE_LOAD:
- case TraceItem::TRACE_FILE_WRITE:
- case TraceItem::TRACE_DEFINE_TARGET:
- case TraceItem::TRACE_ON_RESOLVED:
- break; // Ignore these for the summary.
- }
- }
-
- std::ostringstream out;
- SummarizeParses(parses, out);
- out << std::endl;
- SummarizeFileExecs(file_execs, out);
- out << std::endl;
- SummarizeScriptExecs(script_execs, out);
- out << std::endl;
-
- // Generally there will only be one header check, but it's theoretically
- // possible for more than one to run if more than one build is going in
- // parallel. Just report the total of all of them.
- if (!check_headers.empty()) {
- double check_headers_time = 0;
- for (auto* cur : check_headers)
- check_headers_time += cur->delta().InMillisecondsF();
-
- out << "Header check time: (total time in ms, files checked)\n";
- out << base::StringPrintf(" %8.2f %d\n", check_headers_time,
- headers_checked);
- }
-
- return out.str();
-}
-
-void SaveTraces(const base::FilePath& file_name) {
- std::ostringstream out;
-
- out << "{\"traceEvents\":[";
-
- std::string quote_buffer; // Allocate outside loop to prevent reallocationg.
-
- // Write main thread metadata (assume this is being written on the main
- // thread).
- out << "{\"pid\":0,\"tid\":\"" << std::this_thread::get_id() << "\"";
- out << ",\"ts\":0,\"ph\":\"M\",";
- out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},";
-
- std::vector<TraceItem*> events = trace_log->events();
- for (size_t i = 0; i < events.size(); i++) {
- const TraceItem& item = *events[i];
-
- if (i != 0)
- out << ",";
- out << "{\"pid\":0,\"tid\":\"" << item.thread_id() << "\"";
- out << ",\"ts\":" << item.begin() / kNanosecondsToMicroseconds;
- out << ",\"ph\":\"X\""; // "X" = complete event with begin & duration.
- out << ",\"dur\":" << item.delta().InMicroseconds();
-
- quote_buffer.resize(0);
- base::EscapeJSONString(item.name(), true, &quote_buffer);
- out << ",\"name\":" << quote_buffer;
-
- out << ",\"cat\":";
- switch (item.type()) {
- case TraceItem::TRACE_SETUP:
- out << "\"setup\"";
- break;
- case TraceItem::TRACE_FILE_LOAD:
- out << "\"load\"";
- break;
- case TraceItem::TRACE_FILE_PARSE:
- out << "\"parse\"";
- break;
- case TraceItem::TRACE_FILE_EXECUTE:
- out << "\"file_exec\"";
- break;
- case TraceItem::TRACE_FILE_WRITE:
- out << "\"file_write\"";
- break;
- case TraceItem::TRACE_IMPORT_LOAD:
- out << "\"import_load\"";
- break;
- case TraceItem::TRACE_IMPORT_BLOCK:
- out << "\"import_block\"";
- break;
- case TraceItem::TRACE_SCRIPT_EXECUTE:
- out << "\"script_exec\"";
- break;
- case TraceItem::TRACE_DEFINE_TARGET:
- out << "\"define\"";
- break;
- case TraceItem::TRACE_ON_RESOLVED:
- out << "\"onresolved\"";
- break;
- case TraceItem::TRACE_CHECK_HEADER:
- out << "\"hdr\"";
- break;
- case TraceItem::TRACE_CHECK_HEADERS:
- out << "\"header_check\"";
- break;
- }
-
- if (!item.toolchain().empty() || !item.cmdline().empty()) {
- out << ",\"args\":{";
- bool needs_comma = false;
- if (!item.toolchain().empty()) {
- quote_buffer.resize(0);
- base::EscapeJSONString(item.toolchain(), true, &quote_buffer);
- out << "\"toolchain\":" << quote_buffer;
- needs_comma = true;
- }
- if (!item.cmdline().empty()) {
- quote_buffer.resize(0);
- base::EscapeJSONString(item.cmdline(), true, &quote_buffer);
- if (needs_comma)
- out << ",";
- out << "\"cmdline\":" << quote_buffer;
- needs_comma = true;
- }
- out << "}";
- }
- out << "}";
- }
-
- out << "]}";
-
- std::string out_str = out.str();
- base::WriteFile(file_name, out_str.data(), static_cast<int>(out_str.size()));
-}
diff --git a/gn/tools/gn/unique_vector.h b/gn/tools/gn/unique_vector.h
deleted file mode 100644
index ec8bb275b41..00000000000
--- a/gn/tools/gn/unique_vector.h
+++ /dev/null
@@ -1,163 +0,0 @@
-// 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 TOOLS_GN_UNIQUE_VECTOR_H_
-#define TOOLS_GN_UNIQUE_VECTOR_H_
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <unordered_set>
-#include <vector>
-
-namespace internal {
-
-// This lass allows us to insert things by reference into a hash table which
-// avoids copies. The hash function of a UniquifyRef is that of the object it
-// points to.
-//
-// There are two ways it can store data: (1) by (vector*, index) which is used
-// to refer to the array in UniqueVector and make it work even when the
-// underlying vector is reallocated; (2) by T* which is used to do lookups
-// into the hash table of things that aren't in a vector yet.
-//
-// It also caches the hash value which allows us to query and then insert
-// without recomputing the hash.
-template <typename T>
-class UniquifyRef {
- public:
- UniquifyRef() = default;
-
- // Initialize with a pointer to a value.
- explicit UniquifyRef(const T* v) : value_(v) { FillHashValue(); }
-
- // Initialize with an array + index.
- UniquifyRef(const std::vector<T>* v, size_t i) : vect_(v), index_(i) {
- FillHashValue();
- }
-
- // Initialize with an array + index and a known hash value to prevent
- // re-hashing.
- UniquifyRef(const std::vector<T>* v, size_t i, size_t hash_value)
- : vect_(v), index_(i), hash_val_(hash_value) {}
-
- const T& value() const { return value_ ? *value_ : (*vect_)[index_]; }
- size_t hash_val() const { return hash_val_; }
- size_t index() const { return index_; }
-
- private:
- void FillHashValue() {
- std::hash<T> h;
- hash_val_ = h(value());
- }
-
- // When non-null, points to the object.
- const T* value_ = nullptr;
-
- // When value is null these are used.
- const std::vector<T>* vect_ = nullptr;
- size_t index_ = static_cast<size_t>(-1);
-
- size_t hash_val_ = 0;
-};
-
-template <typename T>
-inline bool operator==(const UniquifyRef<T>& a, const UniquifyRef<T>& b) {
- return a.value() == b.value();
-}
-
-template <typename T>
-inline bool operator<(const UniquifyRef<T>& a, const UniquifyRef<T>& b) {
- return a.value() < b.value();
-}
-
-} // namespace internal
-
-namespace std {
-
-template <typename T>
-struct hash<internal::UniquifyRef<T>> {
- std::size_t operator()(const internal::UniquifyRef<T>& v) const {
- return v.hash_val();
- }
-};
-
-} // namespace std
-
-// An ordered set optimized for GN's usage. Such sets are used to store lists
-// of configs and libraries, and are appended to but not randomly inserted
-// into.
-template <typename T>
-class UniqueVector {
- public:
- typedef std::vector<T> Vector;
- typedef typename Vector::iterator iterator;
- typedef typename Vector::const_iterator const_iterator;
-
- const Vector& vector() const { return vector_; }
- size_t size() const { return vector_.size(); }
- bool empty() const { return vector_.empty(); }
- void clear() {
- vector_.clear();
- set_.clear();
- }
- void reserve(size_t s) { vector_.reserve(s); }
-
- const T& operator[](size_t index) const { return vector_[index]; }
-
- const_iterator begin() const { return vector_.begin(); }
- iterator begin() { return vector_.begin(); }
- const_iterator end() const { return vector_.end(); }
- iterator end() { return vector_.end(); }
-
- // Returns true if the item was appended, false if it already existed (and
- // thus the vector was not modified).
- bool push_back(const T& t) {
- Ref ref(&t);
- if (set_.find(ref) != set_.end())
- return false; // Already have this one.
-
- vector_.push_back(t);
- set_.insert(Ref(&vector_, vector_.size() - 1, ref.hash_val()));
- return true;
- }
-
- bool push_back(T&& t) {
- Ref ref(&t);
- if (set_.find(ref) != set_.end())
- return false; // Already have this one.
-
- auto ref_hash_val = ref.hash_val(); // Save across moving t.
-
- vector_.push_back(std::move(t)); // Invalidates |ref|.
- set_.insert(Ref(&vector_, vector_.size() - 1, ref_hash_val));
- return true;
- }
-
- // Appends a range of items from an iterator.
- template <typename iter>
- void Append(const iter& begin, const iter& end) {
- for (iter i = begin; i != end; ++i)
- push_back(*i);
- }
-
- // Returns the index of the item matching the given value in the list, or
- // (size_t)(-1) if it's not found.
- size_t IndexOf(const T& t) const {
- Ref ref(&t);
- typename HashSet::const_iterator found = set_.find(ref);
- if (found == set_.end())
- return static_cast<size_t>(-1);
- return found->index();
- }
-
- private:
- typedef internal::UniquifyRef<T> Ref;
- typedef std::unordered_set<Ref> HashSet;
-
- HashSet set_;
- Vector vector_;
-};
-
-#endif // TOOLS_GN_UNIQUE_VECTOR_H_
diff --git a/gn/tools/gn/unique_vector_unittest.cc b/gn/tools/gn/unique_vector_unittest.cc
deleted file mode 100644
index 008d2846632..00000000000
--- a/gn/tools/gn/unique_vector_unittest.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 "tools/gn/unique_vector.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-
-#include "util/test/test.h"
-
-TEST(UniqueVector, PushBack) {
- UniqueVector<int> foo;
- EXPECT_TRUE(foo.push_back(1));
- EXPECT_FALSE(foo.push_back(1));
- EXPECT_TRUE(foo.push_back(2));
- EXPECT_TRUE(foo.push_back(0));
- EXPECT_FALSE(foo.push_back(2));
- EXPECT_FALSE(foo.push_back(1));
-
- EXPECT_EQ(3u, foo.size());
- EXPECT_EQ(1, foo[0]);
- EXPECT_EQ(2, foo[1]);
- EXPECT_EQ(0, foo[2]);
-
- // Verify those results with IndexOf as well.
- EXPECT_EQ(0u, foo.IndexOf(1));
- EXPECT_EQ(1u, foo.IndexOf(2));
- EXPECT_EQ(2u, foo.IndexOf(0));
- EXPECT_EQ(static_cast<size_t>(-1), foo.IndexOf(99));
-}
-
-TEST(UniqueVector, PushBackMove) {
- UniqueVector<std::string> vect;
- std::string a("a");
- EXPECT_TRUE(vect.push_back(std::move(a)));
- EXPECT_EQ("", a);
-
- a = "a";
- EXPECT_FALSE(vect.push_back(std::move(a)));
- EXPECT_EQ("a", a);
-
- EXPECT_EQ(0u, vect.IndexOf("a"));
-}
diff --git a/gn/tools/gn/value.cc b/gn/tools/gn/value.cc
deleted file mode 100644
index a54db03030a..00000000000
--- a/gn/tools/gn/value.cc
+++ /dev/null
@@ -1,272 +0,0 @@
-// 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 "tools/gn/value.h"
-
-#include <stddef.h>
-#include <utility>
-
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/scope.h"
-
-// NOTE: Cannot use = default here due to the use of a union member.
-Value::Value() {}
-
-Value::Value(const ParseNode* origin, Type t) : type_(t), origin_(origin) {
- switch (type_) {
- case NONE:
- break;
- case BOOLEAN:
- boolean_value_ = false;
- break;
- case INTEGER:
- int_value_ = 0;
- break;
- case STRING:
- new (&string_value_) std::string();
- break;
- case LIST:
- new (&list_value_) std::vector<Value>();
- break;
- case SCOPE:
- new (&scope_value_) std::unique_ptr<Scope>();
- break;
- }
-}
-
-Value::Value(const ParseNode* origin, bool bool_val)
- : type_(BOOLEAN),
- boolean_value_(bool_val),
- origin_(origin) {}
-
-Value::Value(const ParseNode* origin, int64_t int_val)
- : type_(INTEGER),
- int_value_(int_val),
- origin_(origin) {}
-
-Value::Value(const ParseNode* origin, std::string str_val)
- : type_(STRING),
- string_value_(std::move(str_val)),
- origin_(origin) {}
-
-Value::Value(const ParseNode* origin, const char* str_val)
- : type_(STRING),
- string_value_(str_val),
- origin_(origin) {}
-
-Value::Value(const ParseNode* origin, std::unique_ptr<Scope> scope)
- : type_(SCOPE),
- scope_value_(std::move(scope)),
- origin_(origin) {}
-
-Value::Value(const Value& other) : type_(other.type_), origin_(other.origin_) {
- switch (type_) {
- case NONE:
- break;
- case BOOLEAN:
- boolean_value_ = other.boolean_value_;
- break;
- case INTEGER:
- int_value_ = other.int_value_;
- break;
- case STRING:
- new (&string_value_) std::string(other.string_value_);
- break;
- case LIST:
- new (&list_value_) std::vector<Value>(other.list_value_);
- break;
- case SCOPE:
- new (&scope_value_) std::unique_ptr<Scope>(
- other.scope_value_.get() ? other.scope_value_->MakeClosure()
- : nullptr);
- break;
- }
-}
-
-Value::Value(Value&& other) noexcept
- : type_(other.type_), origin_(other.origin_) {
- switch (type_) {
- case NONE:
- break;
- case BOOLEAN:
- boolean_value_ = other.boolean_value_;
- break;
- case INTEGER:
- int_value_ = other.int_value_;
- break;
- case STRING:
- new (&string_value_) std::string(std::move(other.string_value_));
- break;
- case LIST:
- new (&list_value_) std::vector<Value>(std::move(other.list_value_));
- break;
- case SCOPE:
- new (&scope_value_) std::unique_ptr<Scope>(std::move(other.scope_value_));
- break;
- }
-}
-
-Value& Value::operator=(const Value& other) {
- if (this != &other) {
- this->~Value();
- new (this) Value(other);
- }
- return *this;
-}
-
-Value& Value::operator=(Value&& other) noexcept {
- if (this != &other) {
- this->~Value();
- new (this) Value(std::move(other));
- }
- return *this;
-}
-
-Value::~Value() {
- using namespace std;
- switch (type_) {
- case STRING:
- string_value_.~string();
- break;
- case LIST:
- list_value_.~vector<Value>();
- break;
- case SCOPE:
- scope_value_.~unique_ptr<Scope>();
- break;
- default:;
- }
-}
-
-// static
-const char* Value::DescribeType(Type t) {
- switch (t) {
- case NONE:
- return "none";
- case BOOLEAN:
- return "boolean";
- case INTEGER:
- return "integer";
- case STRING:
- return "string";
- case LIST:
- return "list";
- case SCOPE:
- return "scope";
- default:
- NOTREACHED();
- return "UNKNOWN";
- }
-}
-
-void Value::SetScopeValue(std::unique_ptr<Scope> scope) {
- DCHECK(type_ == SCOPE);
- scope_value_ = std::move(scope);
-}
-
-std::string Value::ToString(bool quote_string) const {
- switch (type_) {
- case NONE:
- return "<void>";
- case BOOLEAN:
- return boolean_value_ ? "true" : "false";
- case INTEGER:
- return base::Int64ToString(int_value_);
- case STRING:
- if (quote_string) {
- std::string result = "\"";
- bool hanging_backslash = false;
- for (char ch : string_value_) {
- // If the last character was a literal backslash and the next
- // character could form a valid escape sequence, we need to insert
- // an extra backslash to prevent that.
- if (hanging_backslash && (ch == '$' || ch == '"' || ch == '\\'))
- result += '\\';
- // If the next character is a dollar sign or double quote, it needs
- // to be escaped; otherwise it can be printed as is.
- if (ch == '$' || ch == '"')
- result += '\\';
- result += ch;
- hanging_backslash = (ch == '\\');
- }
- // Again, we need to prevent the closing double quotes from becoming
- // an escape sequence.
- if (hanging_backslash)
- result += '\\';
- result += '"';
- return result;
- }
- return string_value_;
- case LIST: {
- std::string result = "[";
- for (size_t i = 0; i < list_value_.size(); i++) {
- if (i > 0)
- result += ", ";
- result += list_value_[i].ToString(true);
- }
- result.push_back(']');
- return result;
- }
- case SCOPE: {
- Scope::KeyValueMap scope_values;
- scope_value_->GetCurrentScopeValues(&scope_values);
- if (scope_values.empty())
- return std::string("{ }");
-
- std::string result = "{\n";
- for (const auto& pair : scope_values) {
- result += " " + pair.first.as_string() + " = " +
- pair.second.ToString(true) + "\n";
- }
- result += "}";
-
- return result;
- }
- }
- return std::string();
-}
-
-bool Value::VerifyTypeIs(Type t, Err* err) const {
- if (type_ == t)
- return true;
-
- *err = Err(origin(), std::string("This is not a ") + DescribeType(t) + ".",
- std::string("Instead I see a ") + DescribeType(type_) + " = " +
- ToString(true));
- return false;
-}
-
-bool Value::operator==(const Value& other) const {
- if (type_ != other.type_)
- return false;
-
- switch (type_) {
- case Value::BOOLEAN:
- return boolean_value() == other.boolean_value();
- case Value::INTEGER:
- return int_value() == other.int_value();
- case Value::STRING:
- return string_value() == other.string_value();
- case Value::LIST:
- if (list_value().size() != other.list_value().size())
- return false;
- for (size_t i = 0; i < list_value().size(); i++) {
- if (list_value()[i] != other.list_value()[i])
- return false;
- }
- return true;
- case Value::SCOPE:
- return scope_value()->CheckCurrentScopeValuesEqual(other.scope_value());
- case Value::NONE:
- return false;
- default:
- NOTREACHED();
- return false;
- }
-}
-
-bool Value::operator!=(const Value& other) const {
- return !operator==(other);
-}
diff --git a/gn/tools/gn/value.h b/gn/tools/gn/value.h
deleted file mode 100644
index 38349107e38..00000000000
--- a/gn/tools/gn/value.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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 TOOLS_GN_VALUE_H_
-#define TOOLS_GN_VALUE_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "tools/gn/err.h"
-
-class ParseNode;
-class Scope;
-
-// Represents a variable value in the interpreter.
-class Value {
- public:
- enum Type {
- NONE = 0,
- BOOLEAN,
- INTEGER,
- STRING,
- LIST,
- SCOPE,
- };
-
- Value();
- Value(const ParseNode* origin, Type t);
- Value(const ParseNode* origin, bool bool_val);
- Value(const ParseNode* origin, int64_t int_val);
- Value(const ParseNode* origin, std::string str_val);
- Value(const ParseNode* origin, const char* str_val);
- // Values "shouldn't" have null scopes when type == Scope, so be sure to
- // always set one. However, this is not asserted since there are some
- // use-cases for creating values and immediately setting the scope on it. So
- // you can pass a null scope here if you promise to set it before any other
- // code gets it (code will generally assume the scope is not null).
- Value(const ParseNode* origin, std::unique_ptr<Scope> scope);
-
- Value(const Value& other);
- Value(Value&& other) noexcept;
- ~Value();
-
- Value& operator=(const Value& other);
- Value& operator=(Value&& other) noexcept;
-
- Type type() const { return type_; }
-
- // Returns a string describing the given type.
- static const char* DescribeType(Type t);
-
- // Returns the node that made this. May be NULL.
- const ParseNode* origin() const { return origin_; }
- void set_origin(const ParseNode* o) { origin_ = o; }
-
- bool& boolean_value() {
- DCHECK(type_ == BOOLEAN);
- return boolean_value_;
- }
- const bool& boolean_value() const {
- DCHECK(type_ == BOOLEAN);
- return boolean_value_;
- }
-
- int64_t& int_value() {
- DCHECK(type_ == INTEGER);
- return int_value_;
- }
- const int64_t& int_value() const {
- DCHECK(type_ == INTEGER);
- return int_value_;
- }
-
- std::string& string_value() {
- DCHECK(type_ == STRING);
- return string_value_;
- }
- const std::string& string_value() const {
- DCHECK(type_ == STRING);
- return string_value_;
- }
-
- std::vector<Value>& list_value() {
- DCHECK(type_ == LIST);
- return list_value_;
- }
- const std::vector<Value>& list_value() const {
- DCHECK(type_ == LIST);
- return list_value_;
- }
-
- Scope* scope_value() {
- DCHECK(type_ == SCOPE);
- return scope_value_.get();
- }
- const Scope* scope_value() const {
- DCHECK(type_ == SCOPE);
- return scope_value_.get();
- }
- void SetScopeValue(std::unique_ptr<Scope> scope);
-
- // Converts the given value to a string. Returns true if strings should be
- // quoted or the ToString of a string should be the string itself. If the
- // string is quoted, it will also enable escaping.
- std::string ToString(bool quote_strings) const;
-
- // Verifies that the value is of the given type. If it isn't, returns
- // false and sets the error.
- bool VerifyTypeIs(Type t, Err* err) const;
-
- // Compares values. Only the "value" is compared, not the origin. Scope
- // values check only the contents of the current scope, and do not go to
- // parent scopes.
- bool operator==(const Value& other) const;
- bool operator!=(const Value& other) const;
-
- private:
- void Deallocate();
-
- Type type_ = NONE;
- const ParseNode* origin_ = nullptr;
-
- union {
- bool boolean_value_;
- int64_t int_value_;
- std::string string_value_;
- std::vector<Value> list_value_;
- std::unique_ptr<Scope> scope_value_;
- };
-};
-
-#endif // TOOLS_GN_VALUE_H_
diff --git a/gn/tools/gn/value_extractors.cc b/gn/tools/gn/value_extractors.cc
deleted file mode 100644
index 6eb09accd59..00000000000
--- a/gn/tools/gn/value_extractors.cc
+++ /dev/null
@@ -1,249 +0,0 @@
-// 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 "tools/gn/value_extractors.h"
-
-#include <stddef.h>
-
-#include "tools/gn/build_settings.h"
-#include "tools/gn/err.h"
-#include "tools/gn/label.h"
-#include "tools/gn/source_dir.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value.h"
-
-namespace {
-
-// Sets the error and returns false on failure.
-template <typename T, class Converter>
-bool ListValueExtractor(const Value& value,
- std::vector<T>* dest,
- Err* err,
- const Converter& converter) {
- if (!value.VerifyTypeIs(Value::LIST, err))
- return false;
- const std::vector<Value>& input_list = value.list_value();
- dest->resize(input_list.size());
- for (size_t i = 0; i < input_list.size(); i++) {
- if (!converter(input_list[i], &(*dest)[i], err))
- return false;
- }
- return true;
-}
-
-// Like the above version but extracts to a UniqueVector and sets the error if
-// there are duplicates.
-template <typename T, class Converter>
-bool ListValueUniqueExtractor(const Value& value,
- UniqueVector<T>* dest,
- Err* err,
- const Converter& converter) {
- if (!value.VerifyTypeIs(Value::LIST, err))
- return false;
- const std::vector<Value>& input_list = value.list_value();
-
- for (const auto& item : input_list) {
- T new_one;
- if (!converter(item, &new_one, err))
- return false;
- if (!dest->push_back(new_one)) {
- // Already in the list, throw error.
- *err = Err(item, "Duplicate item in list");
- size_t previous_index = dest->IndexOf(new_one);
- err->AppendSubErr(
- Err(input_list[previous_index], "This was the previous definition."));
- return false;
- }
- }
- return true;
-}
-
-struct RelativeFileConverter {
- RelativeFileConverter(const BuildSettings* build_settings_in,
- const SourceDir& current_dir_in)
- : build_settings(build_settings_in), current_dir(current_dir_in) {}
- bool operator()(const Value& v, SourceFile* out, Err* err) const {
- *out = current_dir.ResolveRelativeFile(v, err,
- build_settings->root_path_utf8());
- return !err->has_error();
- }
- const BuildSettings* build_settings;
- const SourceDir& current_dir;
-};
-
-struct LibFileConverter {
- LibFileConverter(const BuildSettings* build_settings_in,
- const SourceDir& current_dir_in)
- : build_settings(build_settings_in), current_dir(current_dir_in) {}
- bool operator()(const Value& v, LibFile* out, Err* err) const {
- if (!v.VerifyTypeIs(Value::STRING, err))
- return false;
- if (v.string_value().find('/') == std::string::npos) {
- *out = LibFile(v.string_value());
- } else {
- *out = LibFile(current_dir.ResolveRelativeFile(
- v, err, build_settings->root_path_utf8()));
- }
- return !err->has_error();
- }
- const BuildSettings* build_settings;
- const SourceDir& current_dir;
-};
-
-struct RelativeDirConverter {
- RelativeDirConverter(const BuildSettings* build_settings_in,
- const SourceDir& current_dir_in)
- : build_settings(build_settings_in), current_dir(current_dir_in) {}
- bool operator()(const Value& v, SourceDir* out, Err* err) const {
- *out = current_dir.ResolveRelativeDir(v, err,
- build_settings->root_path_utf8());
- return true;
- }
- const BuildSettings* build_settings;
- const SourceDir& current_dir;
-};
-
-// Fills in a label.
-template <typename T>
-struct LabelResolver {
- LabelResolver(const SourceDir& current_dir_in,
- const Label& current_toolchain_in)
- : current_dir(current_dir_in), current_toolchain(current_toolchain_in) {}
- bool operator()(const Value& v, Label* out, Err* err) const {
- if (!v.VerifyTypeIs(Value::STRING, err))
- return false;
- *out = Label::Resolve(current_dir, current_toolchain, v, err);
- return !err->has_error();
- }
- const SourceDir& current_dir;
- const Label& current_toolchain;
-};
-
-// Fills the label part of a LabelPtrPair, leaving the pointer null.
-template <typename T>
-struct LabelPtrResolver {
- LabelPtrResolver(const SourceDir& current_dir_in,
- const Label& current_toolchain_in)
- : current_dir(current_dir_in), current_toolchain(current_toolchain_in) {}
- bool operator()(const Value& v, LabelPtrPair<T>* out, Err* err) const {
- if (!v.VerifyTypeIs(Value::STRING, err))
- return false;
- out->label = Label::Resolve(current_dir, current_toolchain, v, err);
- out->origin = v.origin();
- return !err->has_error();
- }
- const SourceDir& current_dir;
- const Label& current_toolchain;
-};
-
-struct LabelPatternResolver {
- LabelPatternResolver(const SourceDir& current_dir_in)
- : current_dir(current_dir_in) {}
- bool operator()(const Value& v, LabelPattern* out, Err* err) const {
- *out = LabelPattern::GetPattern(current_dir, v, err);
- return !err->has_error();
- }
- const SourceDir& current_dir;
-};
-
-} // namespace
-
-bool ExtractListOfStringValues(const Value& value,
- std::vector<std::string>* dest,
- Err* err) {
- if (!value.VerifyTypeIs(Value::LIST, err))
- return false;
- const std::vector<Value>& input_list = value.list_value();
- dest->reserve(input_list.size());
- for (const auto& item : input_list) {
- if (!item.VerifyTypeIs(Value::STRING, err))
- return false;
- dest->push_back(item.string_value());
- }
- return true;
-}
-
-bool ExtractListOfRelativeFiles(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- std::vector<SourceFile>* files,
- Err* err) {
- return ListValueExtractor(value, files, err,
- RelativeFileConverter(build_settings, current_dir));
-}
-
-bool ExtractListOfLibs(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- std::vector<LibFile>* libs,
- Err* err) {
- return ListValueExtractor(value, libs, err,
- LibFileConverter(build_settings, current_dir));
-}
-
-bool ExtractListOfRelativeDirs(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- std::vector<SourceDir>* dest,
- Err* err) {
- return ListValueExtractor(value, dest, err,
- RelativeDirConverter(build_settings, current_dir));
-}
-
-bool ExtractListOfLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- LabelTargetVector* dest,
- Err* err) {
- return ListValueExtractor(
- value, dest, err,
- LabelPtrResolver<Target>(current_dir, current_toolchain));
-}
-
-bool ExtractListOfUniqueLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- UniqueVector<Label>* dest,
- Err* err) {
- return ListValueUniqueExtractor(
- value, dest, err, LabelResolver<Config>(current_dir, current_toolchain));
-}
-
-bool ExtractListOfUniqueLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- UniqueVector<LabelConfigPair>* dest,
- Err* err) {
- return ListValueUniqueExtractor(
- value, dest, err,
- LabelPtrResolver<Config>(current_dir, current_toolchain));
-}
-
-bool ExtractListOfUniqueLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- UniqueVector<LabelTargetPair>* dest,
- Err* err) {
- return ListValueUniqueExtractor(
- value, dest, err,
- LabelPtrResolver<Target>(current_dir, current_toolchain));
-}
-
-bool ExtractRelativeFile(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- SourceFile* file,
- Err* err) {
- RelativeFileConverter converter(build_settings, current_dir);
- return converter(value, file, err);
-}
-
-bool ExtractListOfLabelPatterns(const Value& value,
- const SourceDir& current_dir,
- std::vector<LabelPattern>* patterns,
- Err* err) {
- return ListValueExtractor(value, patterns, err,
- LabelPatternResolver(current_dir));
-}
diff --git a/gn/tools/gn/value_extractors.h b/gn/tools/gn/value_extractors.h
deleted file mode 100644
index 1e426502036..00000000000
--- a/gn/tools/gn/value_extractors.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// 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 TOOLS_GN_VALUE_EXTRACTORS_H_
-#define TOOLS_GN_VALUE_EXTRACTORS_H_
-
-#include <string>
-#include <vector>
-
-#include "tools/gn/label_ptr.h"
-#include "tools/gn/lib_file.h"
-#include "tools/gn/unique_vector.h"
-
-class BuildSettings;
-class Err;
-class Label;
-class LabelPattern;
-class SourceDir;
-class SourceFile;
-class Value;
-
-// On failure, returns false and sets the error.
-bool ExtractListOfStringValues(const Value& value,
- std::vector<std::string>* dest,
- Err* err);
-
-// Looks for a list of source files relative to a given current dir.
-bool ExtractListOfRelativeFiles(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- std::vector<SourceFile>* files,
- Err* err);
-
-// Extracts a list of libraries. When they contain a "/" they are treated as
-// source paths and are otherwise treated as plain strings.
-bool ExtractListOfLibs(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- std::vector<LibFile>* libs,
- Err* err);
-
-// Looks for a list of source directories relative to a given current dir.
-bool ExtractListOfRelativeDirs(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- std::vector<SourceDir>* dest,
- Err* err);
-
-// Extracts the list of labels and their origins to the given vector. Only the
-// labels are filled in, the ptr for each pair in the vector will be null.
-bool ExtractListOfLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- LabelTargetVector* dest,
- Err* err);
-
-// Extracts the list of labels and their origins to the given vector. For the
-// version taking Label*Pair, only the labels are filled in, the ptr for each
-// pair in the vector will be null. Sets an error and returns false if a label
-// is maformed or there are duplicates.
-bool ExtractListOfUniqueLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- UniqueVector<Label>* dest,
- Err* err);
-bool ExtractListOfUniqueLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- UniqueVector<LabelConfigPair>* dest,
- Err* err);
-bool ExtractListOfUniqueLabels(const Value& value,
- const SourceDir& current_dir,
- const Label& current_toolchain,
- UniqueVector<LabelTargetPair>* dest,
- Err* err);
-
-bool ExtractRelativeFile(const BuildSettings* build_settings,
- const Value& value,
- const SourceDir& current_dir,
- SourceFile* file,
- Err* err);
-
-bool ExtractListOfLabelPatterns(const Value& value,
- const SourceDir& current_dir,
- std::vector<LabelPattern>* patterns,
- Err* err);
-
-#endif // TOOLS_GN_VALUE_EXTRACTORS_H_
diff --git a/gn/tools/gn/value_unittest.cc b/gn/tools/gn/value_unittest.cc
deleted file mode 100644
index dd9be955134..00000000000
--- a/gn/tools/gn/value_unittest.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 <stdint.h>
-
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-TEST(Value, ToString) {
- Value strval(nullptr, "hi\" $me\\you\\$\\\"");
- EXPECT_EQ("hi\" $me\\you\\$\\\"", strval.ToString(false));
- EXPECT_EQ("\"hi\\\" \\$me\\you\\\\\\$\\\\\\\"\"", strval.ToString(true));
-
- // crbug.com/470217
- Value strval2(nullptr, "\\foo\\\\bar\\");
- EXPECT_EQ("\"\\foo\\\\\\bar\\\\\"", strval2.ToString(true));
-
- // Void type.
- EXPECT_EQ("<void>", Value().ToString(false));
-
- // Test lists, bools, and ints.
- Value listval(nullptr, Value::LIST);
- listval.list_value().push_back(Value(nullptr, "hi\"me"));
- listval.list_value().push_back(Value(nullptr, true));
- listval.list_value().push_back(Value(nullptr, false));
- listval.list_value().push_back(Value(nullptr, static_cast<int64_t>(42)));
- // Printing lists always causes embedded strings to be quoted (ignoring the
- // quote flag), or else they wouldn't make much sense.
- EXPECT_EQ("[\"hi\\\"me\", true, false, 42]", listval.ToString(false));
- EXPECT_EQ("[\"hi\\\"me\", true, false, 42]", listval.ToString(true));
-
- // Scopes.
- TestWithScope setup;
- Scope* scope = new Scope(setup.settings());
- Value scopeval(nullptr, std::unique_ptr<Scope>(scope));
- EXPECT_EQ("{ }", scopeval.ToString(false));
-
- // Test that an empty scope equals an empty scope.
- EXPECT_TRUE(scopeval == scopeval);
-
- scope->SetValue("a", Value(nullptr, static_cast<int64_t>(42)), nullptr);
- scope->SetValue("b", Value(nullptr, "hello, world"), nullptr);
- EXPECT_EQ("{\n a = 42\n b = \"hello, world\"\n}", scopeval.ToString(false));
- EXPECT_TRUE(scopeval == scopeval);
-
- Scope* inner_scope = new Scope(setup.settings());
- Value inner_scopeval(nullptr, std::unique_ptr<Scope>(inner_scope));
- inner_scope->SetValue("d", Value(nullptr, static_cast<int64_t>(42)), nullptr);
- scope->SetValue("c", inner_scopeval, nullptr);
-
- // Test inner scope equality.
- EXPECT_TRUE(scopeval == scopeval);
-
- // Nested scopes should not be equal.
- Scope* nested_scope = new Scope(scope);
- Value nested_scopeval(nullptr, std::unique_ptr<Scope>(nested_scope));
- EXPECT_FALSE(nested_scopeval == nested_scopeval);
-}
diff --git a/gn/tools/gn/variables.cc b/gn/tools/gn/variables.cc
deleted file mode 100644
index 74781aab7bb..00000000000
--- a/gn/tools/gn/variables.cc
+++ /dev/null
@@ -1,2219 +0,0 @@
-// 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 "tools/gn/variables.h"
-
-#include "tools/gn/rust_variables.h"
-
-namespace variables {
-
-// Built-in variables ----------------------------------------------------------
-
-const char kHostCpu[] = "host_cpu";
-const char kHostCpu_HelpShort[] =
- "host_cpu: [string] The processor architecture that GN is running on.";
-const char kHostCpu_Help[] =
- R"(host_cpu: The processor architecture that GN is running on.
-
- This is value is exposed so that cross-compile toolchains can access the host
- architecture when needed.
-
- The value should generally be considered read-only, but it can be overriden
- in order to handle unusual cases where there might be multiple plausible
- values for the host architecture (e.g., if you can do either 32-bit or 64-bit
- builds). The value is not used internally by GN for any purpose.
-
-Some possible values
-
- - "x64"
- - "x86"
-)";
-
-const char kHostOs[] = "host_os";
-const char kHostOs_HelpShort[] =
- "host_os: [string] The operating system that GN is running on.";
-const char kHostOs_Help[] =
- R"(host_os: [string] The operating system that GN is running on.
-
- This value is exposed so that cross-compiles can access the host build
- system's settings.
-
- This value should generally be treated as read-only. It, however, is not used
- internally by GN for any purpose.
-
-Some possible values
-
- - "linux"
- - "mac"
- - "win"
-)";
-
-const char kInvoker[] = "invoker";
-const char kInvoker_HelpShort[] =
- "invoker: [string] The invoking scope inside a template.";
-const char kInvoker_Help[] =
- R"(invoker: [string] The invoking scope inside a template.
-
- Inside a template invocation, this variable refers to the scope of the
- invoker of the template. Outside of template invocations, this variable is
- undefined.
-
- All of the variables defined inside the template invocation are accessible as
- members of the "invoker" scope. This is the way that templates read values
- set by the callers.
-
- This is often used with "defined" to see if a value is set on the invoking
- scope.
-
- See "gn help template" for more examples.
-
-Example
-
- template("my_template") {
- print(invoker.sources) # Prints [ "a.cc", "b.cc" ]
- print(defined(invoker.foo)) # Prints false.
- print(defined(invoker.bar)) # Prints true.
- }
-
- my_template("doom_melon") {
- sources = [ "a.cc", "b.cc" ]
- bar = 123
- }
-)";
-
-const char kTargetCpu[] = "target_cpu";
-const char kTargetCpu_HelpShort[] =
- "target_cpu: [string] The desired cpu architecture for the build.";
-const char kTargetCpu_Help[] =
- R"(target_cpu: The desired cpu architecture for the build.
-
- This value should be used to indicate the desired architecture for the
- primary objects of the build. It will match the cpu architecture of the
- default toolchain, but not necessarily the current toolchain.
-
- In many cases, this is the same as "host_cpu", but in the case of
- cross-compiles, this can be set to something different. This value is
- different from "current_cpu" in that it does not change based on the current
- toolchain. When writing rules, "current_cpu" should be used rather than
- "target_cpu" most of the time.
-
- This value is not used internally by GN for any purpose, so it may be set to
- whatever value is needed for the build. GN defaults this value to the empty
- string ("") and the configuration files should set it to an appropriate value
- (e.g., setting it to the value of "host_cpu") if it is not overridden on the
- command line or in the args.gn file.
-
-Possible values
-
- - "x86"
- - "x64"
- - "arm"
- - "arm64"
- - "mipsel"
-)";
-
-const char kTargetName[] = "target_name";
-const char kTargetName_HelpShort[] =
- "target_name: [string] The name of the current target.";
-const char kTargetName_Help[] =
- R"(target_name: [string] The name of the current target.
-
- Inside a target or template invocation, this variable refers to the name
- given to the target or template invocation. Outside of these, this variable
- is undefined.
-
- This is most often used in template definitions to name targets defined in
- the template based on the name of the invocation. This is necessary both to
- ensure generated targets have unique names and to generate a target with the
- exact name of the invocation that other targets can depend on.
-
- Be aware that this value will always reflect the innermost scope. So when
- defining a target inside a template, target_name will refer to the target
- rather than the template invocation. To get the name of the template
- invocation in this case, you should save target_name to a temporary variable
- outside of any target definitions.
-
- See "gn help template" for more examples.
-
-Example
-
- executable("doom_melon") {
- print(target_name) # Prints "doom_melon".
- }
-
- template("my_template") {
- print(target_name) # Prints "space_ray" when invoked below.
-
- executable(target_name + "_impl") {
- print(target_name) # Prints "space_ray_impl".
- }
- }
-
- my_template("space_ray") {
- }
-)";
-
-const char kTargetOs[] = "target_os";
-const char kTargetOs_HelpShort[] =
- "target_os: [string] The desired operating system for the build.";
-const char kTargetOs_Help[] =
- R"(target_os: The desired operating system for the build.
-
- This value should be used to indicate the desired operating system for the
- primary object(s) of the build. It will match the OS of the default
- toolchain.
-
- In many cases, this is the same as "host_os", but in the case of
- cross-compiles, it may be different. This variable differs from "current_os"
- in that it can be referenced from inside any toolchain and will always return
- the initial value.
-
- This should be set to the most specific value possible. So, "android" or
- "chromeos" should be used instead of "linux" where applicable, even though
- Android and ChromeOS are both Linux variants. This can mean that one needs to
- write
-
- if (target_os == "android" || target_os == "linux") {
- # ...
- }
-
- and so forth.
-
- This value is not used internally by GN for any purpose, so it may be set to
- whatever value is needed for the build. GN defaults this value to the empty
- string ("") and the configuration files should set it to an appropriate value
- (e.g., setting it to the value of "host_os") if it is not set via the command
- line or in the args.gn file.
-
-Possible values
-
- - "android"
- - "chromeos"
- - "ios"
- - "linux"
- - "nacl"
- - "mac"
- - "win"
-)";
-
-const char kCurrentCpu[] = "current_cpu";
-const char kCurrentCpu_HelpShort[] =
- "current_cpu: [string] The processor architecture of the current "
- "toolchain.";
-const char kCurrentCpu_Help[] =
- R"(current_cpu: The processor architecture of the current toolchain.
-
- The build configuration usually sets this value based on the value of
- "host_cpu" (see "gn help host_cpu") and then threads this through the
- toolchain definitions to ensure that it always reflects the appropriate
- value.
-
- This value is not used internally by GN for any purpose. It is set to the
- empty string ("") by default but is declared so that it can be overridden on
- the command line if so desired.
-
- See "gn help target_cpu" for a list of common values returned.)";
-
-const char kCurrentOs[] = "current_os";
-const char kCurrentOs_HelpShort[] =
- "current_os: [string] The operating system of the current toolchain.";
-const char kCurrentOs_Help[] =
- R"(current_os: The operating system of the current toolchain.
-
- The build configuration usually sets this value based on the value of
- "target_os" (see "gn help target_os"), and then threads this through the
- toolchain definitions to ensure that it always reflects the appropriate
- value.
-
- This value is not used internally by GN for any purpose. It is set to the
- empty string ("") by default but is declared so that it can be overridden on
- the command line if so desired.
-
- See "gn help target_os" for a list of common values returned.
-)";
-
-const char kCurrentToolchain[] = "current_toolchain";
-const char kCurrentToolchain_HelpShort[] =
- "current_toolchain: [string] Label of the current toolchain.";
-const char kCurrentToolchain_Help[] =
- R"(current_toolchain: Label of the current toolchain.
-
- A fully-qualified label representing the current toolchain. You can use this
- to make toolchain-related decisions in the build. See also
- "default_toolchain".
-
-Example
-
- if (current_toolchain == "//build:64_bit_toolchain") {
- executable("output_thats_64_bit_only") {
- ...
-)";
-
-const char kDefaultToolchain[] = "default_toolchain";
-const char kDefaultToolchain_HelpShort[] =
- "default_toolchain: [string] Label of the default toolchain.";
-const char kDefaultToolchain_Help[] =
- R"(default_toolchain: [string] Label of the default toolchain.
-
- A fully-qualified label representing the default toolchain, which may not
- necessarily be the current one (see "current_toolchain").
-)";
-
-const char kPythonPath[] = "python_path";
-const char kPythonPath_HelpShort[] =
- "python_path: [string] Absolute path of Python.";
-const char kPythonPath_Help[] =
- R"(python_path: Absolute path of Python.
-
- Normally used in toolchain definitions if running some command requires
- Python. You will normally not need this when invoking scripts since GN
- automatically finds it for you.
-)";
-
-const char kRootBuildDir[] = "root_build_dir";
-const char kRootBuildDir_HelpShort[] =
- "root_build_dir: [string] Directory where build commands are run.";
-const char kRootBuildDir_Help[] =
- R"(root_build_dir: [string] Directory where build commands are run.
-
- This is the root build output directory which will be the current directory
- when executing all compilers and scripts.
-
- Most often this is used with rebase_path (see "gn help rebase_path") to
- convert arguments to be relative to a script's current directory.
-)";
-
-const char kRootGenDir[] = "root_gen_dir";
-const char kRootGenDir_HelpShort[] =
- "root_gen_dir: [string] Directory for the toolchain's generated files.";
-const char kRootGenDir_Help[] =
- R"(root_gen_dir: Directory for the toolchain's generated files.
-
- Absolute path to the root of the generated output directory tree for the
- current toolchain. An example would be "//out/Debug/gen" for the default
- toolchain, or "//out/Debug/arm/gen" for the "arm" toolchain.
-
- This is primarily useful for setting up include paths for generated files. If
- you are passing this to a script, you will want to pass it through
- rebase_path() (see "gn help rebase_path") to convert it to be relative to the
- build directory.
-
- See also "target_gen_dir" which is usually a better location for generated
- files. It will be inside the root generated dir.
-)";
-
-const char kRootOutDir[] = "root_out_dir";
-const char kRootOutDir_HelpShort[] =
- "root_out_dir: [string] Root directory for toolchain output files.";
-const char kRootOutDir_Help[] =
- R"(root_out_dir: [string] Root directory for toolchain output files.
-
- Absolute path to the root of the output directory tree for the current
- toolchain. It will not have a trailing slash.
-
- For the default toolchain this will be the same as the root_build_dir. An
- example would be "//out/Debug" for the default toolchain, or
- "//out/Debug/arm" for the "arm" toolchain.
-
- This is primarily useful for setting up script calls. If you are passing this
- to a script, you will want to pass it through rebase_path() (see "gn help
- rebase_path") to convert it to be relative to the build directory.
-
- See also "target_out_dir" which is usually a better location for output
- files. It will be inside the root output dir.
-
-Example
-
- action("myscript") {
- # Pass the output dir to the script.
- args = [ "-o", rebase_path(root_out_dir, root_build_dir) ]
- }
-)";
-
-const char kTargetGenDir[] = "target_gen_dir";
-const char kTargetGenDir_HelpShort[] =
- "target_gen_dir: [string] Directory for a target's generated files.";
-const char kTargetGenDir_Help[] =
- R"(target_gen_dir: Directory for a target's generated files.
-
- Absolute path to the target's generated file directory. This will be the
- "root_gen_dir" followed by the relative path to the current build file. If
- your file is in "//tools/doom_melon" then target_gen_dir would be
- "//out/Debug/gen/tools/doom_melon". It will not have a trailing slash.
-
- This is primarily useful for setting up include paths for generated files. If
- you are passing this to a script, you will want to pass it through
- rebase_path() (see "gn help rebase_path") to convert it to be relative to the
- build directory.
-
- See also "gn help root_gen_dir".
-
-Example
-
- action("myscript") {
- # Pass the generated output dir to the script.
- args = [ "-o", rebase_path(target_gen_dir, root_build_dir) ]"
- }
-)";
-
-const char kTargetOutDir[] = "target_out_dir";
-const char kTargetOutDir_HelpShort[] =
- "target_out_dir: [string] Directory for target output files.";
-const char kTargetOutDir_Help[] =
- R"(target_out_dir: [string] Directory for target output files.
-
- Absolute path to the target's generated file directory. If your current
- target is in "//tools/doom_melon" then this value might be
- "//out/Debug/obj/tools/doom_melon". It will not have a trailing slash.
-
- This is primarily useful for setting up arguments for calling scripts. If you
- are passing this to a script, you will want to pass it through rebase_path()
- (see "gn help rebase_path") to convert it to be relative to the build
- directory.
-
- See also "gn help root_out_dir".
-
-Example
-
- action("myscript") {
- # Pass the output dir to the script.
- args = [ "-o", rebase_path(target_out_dir, root_build_dir) ]"
- }
-)";
-
-// Target variables ------------------------------------------------------------
-
-#define COMMON_ORDERING_HELP \
- "\n" \
- "Ordering of flags and values\n" \
- "\n" \
- " 1. Those set on the current target (not in a config).\n" \
- " 2. Those set on the \"configs\" on the target in order that the\n" \
- " configs appear in the list.\n" \
- " 3. Those set on the \"all_dependent_configs\" on the target in order\n" \
- " that the configs appear in the list.\n" \
- " 4. Those set on the \"public_configs\" on the target in order that\n" \
- " those configs appear in the list.\n" \
- " 5. all_dependent_configs pulled from dependencies, in the order of\n" \
- " the \"deps\" list. This is done recursively. If a config appears\n" \
- " more than once, only the first occurence will be used.\n" \
- " 6. public_configs pulled from dependencies, in the order of the\n" \
- " \"deps\" list. If a dependency is public, they will be applied\n" \
- " recursively.\n"
-
-const char kAllDependentConfigs[] = "all_dependent_configs";
-const char kAllDependentConfigs_HelpShort[] =
- "all_dependent_configs: [label list] Configs to be forced on dependents.";
-const char kAllDependentConfigs_Help[] =
- R"(all_dependent_configs: Configs to be forced on dependents.
-
- A list of config labels.
-
- All targets depending on this one, and recursively, all targets depending on
- those, will have the configs listed in this variable added to them. These
- configs will also apply to the current target.
-
- This addition happens in a second phase once a target and all of its
- dependencies have been resolved. Therefore, a target will not see these
- force-added configs in their "configs" variable while the script is running,
- and they can not be removed. As a result, this capability should generally
- only be used to add defines and include directories necessary to compile a
- target's headers.
-
- See also "public_configs".
-)" COMMON_ORDERING_HELP;
-
-const char kAllowCircularIncludesFrom[] = "allow_circular_includes_from";
-const char kAllowCircularIncludesFrom_HelpShort[] =
- "allow_circular_includes_from: [label list] Permit includes from deps.";
-const char kAllowCircularIncludesFrom_Help[] =
- R"(allow_circular_includes_from: Permit includes from deps.
-
- A list of target labels. Must be a subset of the target's "deps". These
- targets will be permitted to include headers from the current target despite
- the dependency going in the opposite direction.
-
- When you use this, both targets must be included in a final binary for it to
- link. To keep linker errors from happening, it is good practice to have all
- external dependencies depend only on one of the two targets, and to set the
- visibility on the other to enforce this. Thus the targets will always be
- linked together in any output.
-
-Details
-
- Normally, for a file in target A to include a file from target B, A must list
- B as a dependency. This invariant is enforced by the "gn check" command (and
- the --check flag to "gn gen" -- see "gn help check").
-
- Sometimes, two targets might be the same unit for linking purposes (two
- source sets or static libraries that would always be linked together in a
- final executable or shared library) and they each include headers from the
- other: you want A to be able to include B's headers, and B to include A's
- headers. This is not an ideal situation but is sometimes unavoidable.
-
- This list, if specified, lists which of the dependencies of the current
- target can include header files from the current target. That is, if A
- depends on B, B can only include headers from A if it is in A's
- allow_circular_includes_from list. Normally includes must follow the
- direction of dependencies, this flag allows them to go in the opposite
- direction.
-
-Danger
-
- In the above example, A's headers are likely to include headers from A's
- dependencies. Those dependencies may have public_configs that apply flags,
- defines, and include paths that make those headers work properly.
-
- With allow_circular_includes_from, B can include A's headers, and
- transitively from A's dependencies, without having the dependencies that
- would bring in the public_configs those headers need. The result may be
- errors or inconsistent builds.
-
- So when you use allow_circular_includes_from, make sure that any compiler
- settings, flags, and include directories are the same between both targets
- (consider putting such things in a shared config they can both reference).
- Make sure the dependencies are also the same (you might consider a group to
- collect such dependencies they both depend on).
-
-Example
-
- source_set("a") {
- deps = [ ":b", ":a_b_shared_deps" ]
- allow_circular_includes_from = [ ":b" ]
- ...
- }
-
- source_set("b") {
- deps = [ ":a_b_shared_deps" ]
- # Sources here can include headers from a despite lack of deps.
- ...
- }
-
- group("a_b_shared_deps") {
- public_deps = [ ":c" ]
- }
-)";
-
-const char kArflags[] = "arflags";
-const char kArflags_HelpShort[] =
- "arflags: [string list] Arguments passed to static_library archiver.";
-const char kArflags_Help[] =
- R"(arflags: Arguments passed to static_library archiver.
-
- A list of flags passed to the archive/lib command that creates static
- libraries.
-
- arflags are NOT pushed to dependents, so applying arflags to source sets or
- any other target type will be a no-op. As with ldflags, you could put the
- arflags in a config and set that as a public or "all dependent" config, but
- that will likely not be what you want. If you have a chain of static
- libraries dependent on each other, this can cause the flags to propagate up
- to other static libraries. Due to the nature of how arflags are typically
- used, you will normally want to apply them directly on static_library targets
- themselves.
-)" COMMON_ORDERING_HELP;
-
-const char kArgs[] = "args";
-const char kArgs_HelpShort[] =
- "args: [string list] Arguments passed to an action.";
-const char kArgs_Help[] =
- R"(args: (target variable) Arguments passed to an action.
-
- For action and action_foreach targets, args is the list of arguments to pass
- to the script. Typically you would use source expansion (see "gn help
- source_expansion") to insert the source file names.
-
- See also "gn help action" and "gn help action_foreach".
-)";
-
-const char kAssertNoDeps[] = "assert_no_deps";
-const char kAssertNoDeps_HelpShort[] =
- "assert_no_deps: [label pattern list] Ensure no deps on these targets.";
-const char kAssertNoDeps_Help[] =
- R"(assert_no_deps: Ensure no deps on these targets.
-
- A list of label patterns.
-
- This list is a list of patterns that must not match any of the transitive
- dependencies of the target. These include all public, private, and data
- dependencies, and cross shared library boundaries. This allows you to express
- that undesirable code isn't accidentally added to downstream dependencies in
- a way that might otherwise be difficult to notice.
-
- Checking does not cross executable boundaries. If a target depends on an
- executable, it's assumed that the executable is a tool that is producing part
- of the build rather than something that is linked and distributed. This
- allows assert_no_deps to express what is distributed in the final target
- rather than depend on the internal build steps (which may include
- non-distributable code).
-
- See "gn help label_pattern" for the format of the entries in the list. These
- patterns allow blacklisting individual targets or whole directory
- hierarchies.
-
- Sometimes it is desirable to enforce that many targets have no dependencies
- on a target or set of targets. One efficient way to express this is to create
- a group with the assert_no_deps rule on it, and make that group depend on all
- targets you want to apply that assertion to.
-
-Example
-
- executable("doom_melon") {
- deps = [ "//foo:bar" ]
- ...
- assert_no_deps = [
- "//evil/*", # Don't link any code from the evil directory.
- "//foo:test_support", # This target is also disallowed.
- ]
- }
-)";
-
-const char kBundleRootDir[] = "bundle_root_dir";
-const char kBundleRootDir_HelpShort[] =
- "bundle_root_dir: Expansion of {{bundle_root_dir}} in create_bundle.";
-const char kBundleRootDir_Help[] =
- R"(bundle_root_dir: Expansion of {{bundle_root_dir}} in create_bundle.
-
- A string corresponding to a path in root_build_dir.
-
- This string is used by the "create_bundle" target to expand the
- {{bundle_root_dir}} of the "bundle_data" target it depends on. This must
- correspond to a path under root_build_dir.
-
-Example
-
- bundle_data("info_plist") {
- sources = [ "Info.plist" ]
- outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
- }
-
- create_bundle("doom_melon.app") {
- deps = [ ":info_plist" ]
- bundle_root_dir = "${root_build_dir}/doom_melon.app"
- bundle_contents_dir = "${bundle_root_dir}/Contents"
- bundle_resources_dir = "${bundle_contents_dir}/Resources"
- bundle_executable_dir = "${bundle_contents_dir}/MacOS"
- }
-)";
-
-const char kBundleContentsDir[] = "bundle_contents_dir";
-const char kBundleContentsDir_HelpShort[] =
- "bundle_contents_dir: "
- "Expansion of {{bundle_contents_dir}} in create_bundle.";
-const char kBundleContentsDir_Help[] =
- R"(bundle_contents_dir: Expansion of {{bundle_contents_dir}} in
- create_bundle.
-
- A string corresponding to a path in $root_build_dir.
-
- This string is used by the "create_bundle" target to expand the
- {{bundle_contents_dir}} of the "bundle_data" target it depends on. This must
- correspond to a path under "bundle_root_dir".
-
- See "gn help bundle_root_dir" for examples.
-)";
-
-const char kBundleResourcesDir[] = "bundle_resources_dir";
-const char kBundleResourcesDir_HelpShort[] =
- "bundle_resources_dir: "
- "Expansion of {{bundle_resources_dir}} in create_bundle.";
-const char kBundleResourcesDir_Help[] =
- R"(bundle_resources_dir
-
- bundle_resources_dir: Expansion of {{bundle_resources_dir}} in
- create_bundle.
-
- A string corresponding to a path in $root_build_dir.
-
- This string is used by the "create_bundle" target to expand the
- {{bundle_resources_dir}} of the "bundle_data" target it depends on. This must
- correspond to a path under "bundle_root_dir".
-
- See "gn help bundle_root_dir" for examples.
-)";
-
-const char kBundleDepsFilter[] = "bundle_deps_filter";
-const char kBundleDepsFilter_HelpShort[] =
- "bundle_deps_filter: [label list] A list of labels that are filtered out.";
-const char kBundleDepsFilter_Help[] =
- R"(bundle_deps_filter: [label list] A list of labels that are filtered out.
-
- A list of target labels.
-
- This list contains target label patterns that should be filtered out when
- creating the bundle. Any target matching one of those label will be removed
- from the dependencies of the create_bundle target.
-
- This is mostly useful when creating application extension bundle as the
- application extension has access to runtime resources from the application
- bundle and thus do not require a second copy.
-
- See "gn help create_bundle" for more information.
-
-Example
-
- create_bundle("today_extension") {
- deps = [
- "//base"
- ]
- bundle_root_dir = "$root_out_dir/today_extension.appex"
- bundle_deps_filter = [
- # The extension uses //base but does not use any function calling into
- # third_party/icu and thus does not need the icudtl.dat file.
- "//third_party/icu:icudata",
- ]
- }
-)";
-
-const char kBundleExecutableDir[] = "bundle_executable_dir";
-const char kBundleExecutableDir_HelpShort[] =
- "bundle_executable_dir: "
- "Expansion of {{bundle_executable_dir}} in create_bundle";
-const char kBundleExecutableDir_Help[] =
- R"(bundle_executable_dir
-
- bundle_executable_dir: Expansion of {{bundle_executable_dir}} in
- create_bundle.
-
- A string corresponding to a path in $root_build_dir.
-
- This string is used by the "create_bundle" target to expand the
- {{bundle_executable_dir}} of the "bundle_data" target it depends on. This
- must correspond to a path under "bundle_root_dir".
-
- See "gn help bundle_root_dir" for examples.
-)";
-
-const char kCflags[] = "cflags";
-const char kCflags_HelpShort[] =
- "cflags: [string list] Flags passed to all C compiler variants.";
-const char kCommonCflagsHelp[] =
- R"(cflags*: Flags passed to the C compiler.
-
- A list of strings.
-
- "cflags" are passed to all invocations of the C, C++, Objective C, and
- Objective C++ compilers.
-
- To target one of these variants individually, use "cflags_c", "cflags_cc",
- "cflags_objc", and "cflags_objcc", respectively. These variant-specific
- versions of cflags* will be appended on the compiler command line after
- "cflags".
-
- See also "asmflags" for flags for assembly-language files.
-)" COMMON_ORDERING_HELP;
-const char* kCflags_Help = kCommonCflagsHelp;
-
-const char kAsmflags[] = "asmflags";
-const char kAsmflags_HelpShort[] =
- "asmflags: [string list] Flags passed to the assembler.";
-const char* kAsmflags_Help =
- R"(asmflags: Flags passed to the assembler.
-
- A list of strings.
-
- "asmflags" are passed to any invocation of a tool that takes an .asm or .S
- file as input.
-)" COMMON_ORDERING_HELP;
-
-const char kCflagsC[] = "cflags_c";
-const char kCflagsC_HelpShort[] =
- "cflags_c: [string list] Flags passed to the C compiler.";
-const char* kCflagsC_Help = kCommonCflagsHelp;
-
-const char kCflagsCC[] = "cflags_cc";
-const char kCflagsCC_HelpShort[] =
- "cflags_cc: [string list] Flags passed to the C++ compiler.";
-const char* kCflagsCC_Help = kCommonCflagsHelp;
-
-const char kCflagsObjC[] = "cflags_objc";
-const char kCflagsObjC_HelpShort[] =
- "cflags_objc: [string list] Flags passed to the Objective C compiler.";
-const char* kCflagsObjC_Help = kCommonCflagsHelp;
-
-const char kCflagsObjCC[] = "cflags_objcc";
-const char kCflagsObjCC_HelpShort[] =
- "cflags_objcc: [string list] Flags passed to the Objective C++ compiler.";
-const char* kCflagsObjCC_Help = kCommonCflagsHelp;
-
-const char kCheckIncludes[] = "check_includes";
-const char kCheckIncludes_HelpShort[] =
- "check_includes: [boolean] Controls whether a target's files are checked.";
-const char kCheckIncludes_Help[] =
- R"(check_includes: [boolean] Controls whether a target's files are checked.
-
- When true (the default), the "gn check" command (as well as "gn gen" with the
- --check flag) will check this target's sources and headers for proper
- dependencies.
-
- When false, the files in this target will be skipped by default. This does
- not affect other targets that depend on the current target, it just skips
- checking the includes of the current target's files.
-
- If there are a few conditionally included headers that trip up checking, you
- can exclude headers individually by annotating them with "nogncheck" (see "gn
- help nogncheck").
-
- The topic "gn help check" has general information on how checking works and
- advice on how to pass a check in problematic cases.
-
-Example
-
- source_set("busted_includes") {
- # This target's includes are messed up, exclude it from checking.
- check_includes = false
- ...
- }
-)";
-
-const char kCodeSigningArgs[] = "code_signing_args";
-const char kCodeSigningArgs_HelpShort[] =
- "code_signing_args: [string list] Arguments passed to code signing script.";
-const char kCodeSigningArgs_Help[] =
- R"(code_signing_args: [string list] Arguments passed to code signing script.
-
- For create_bundle targets, code_signing_args is the list of arguments to pass
- to the code signing script. Typically you would use source expansion (see "gn
- help source_expansion") to insert the source file names.
-
- See also "gn help create_bundle".
-)";
-
-const char kCodeSigningScript[] = "code_signing_script";
-const char kCodeSigningScript_HelpShort[] =
- "code_signing_script: [file name] Script for code signing.";
-const char kCodeSigningScript_Help[] =
- R"(code_signing_script: [file name] Script for code signing."
-
- An absolute or buildfile-relative file name of a Python script to run for a
- create_bundle target to perform code signing step.
-
- See also "gn help create_bundle".
-)";
-
-const char kCodeSigningSources[] = "code_signing_sources";
-const char kCodeSigningSources_HelpShort[] =
- "code_signing_sources: [file list] Sources for code signing step.";
-const char kCodeSigningSources_Help[] =
- R"(code_signing_sources: [file list] Sources for code signing step.
-
- A list of files used as input for code signing script step of a create_bundle
- target. Non-absolute paths will be resolved relative to the current build
- file.
-
- See also "gn help create_bundle".
-)";
-
-const char kCodeSigningOutputs[] = "code_signing_outputs";
-const char kCodeSigningOutputs_HelpShort[] =
- "code_signing_outputs: [file list] Output files for code signing step.";
-const char kCodeSigningOutputs_Help[] =
- R"(code_signing_outputs: [file list] Output files for code signing step.
-
- Outputs from the code signing step of a create_bundle target. Must refer to
- files in the build directory.
-
- See also "gn help create_bundle".
-)";
-
-const char kCompleteStaticLib[] = "complete_static_lib";
-const char kCompleteStaticLib_HelpShort[] =
- "complete_static_lib: [boolean] Links all deps into a static library.";
-const char kCompleteStaticLib_Help[] =
- R"(complete_static_lib: [boolean] Links all deps into a static library.
-
- A static library normally doesn't include code from dependencies, but instead
- forwards the static libraries and source sets in its deps up the dependency
- chain until a linkable target (an executable or shared library) is reached.
- The final linkable target only links each static library once, even if it
- appears more than once in its dependency graph.
-
- In some cases the static library might be the final desired output. For
- example, you may be producing a static library for distribution to third
- parties. In this case, the static library should include code for all
- dependencies in one complete package. However, complete static libraries
- themselves are never linked into other complete static libraries. All
- complete static libraries are for distribution and linking them in would
- cause code duplication in this case. If the static library is not for
- distribution, it should not be complete.
-
- GN treats non-complete static libraries as source sets when they are linked
- into complete static libraries. This is done because some tools like AR do
- not handle dependent static libraries properly. This makes it easier to write
- "alink" rules.
-
- In rare cases it makes sense to list a header in more than one target if it
- could be considered conceptually a member of both. libraries.
-
-Example
-
- static_library("foo") {
- complete_static_lib = true
- deps = [ "bar" ]
- }
-)";
-
-const char kConfigs[] = "configs";
-const char kConfigs_HelpShort[] =
- "configs: [label list] Configs applying to this target or config.";
-const char kConfigs_Help[] =
- R"(configs: Configs applying to this target or config.
-
- A list of config labels.
-
-Configs on a target
-
- When used on a target, the include_dirs, defines, etc. in each config are
- appended in the order they appear to the compile command for each file in the
- target. They will appear after the include_dirs, defines, etc. that the
- target sets directly.
-
- Since configs apply after the values set on a target, directly setting a
- compiler flag will prepend it to the command line. If you want to append a
- flag instead, you can put that flag in a one-off config and append that
- config to the target's configs list.
-
- The build configuration script will generally set up the default configs
- applying to a given target type (see "set_defaults"). When a target is being
- defined, it can add to or remove from this list.
-
-Configs on a config
-
- It is possible to create composite configs by specifying configs on a config.
- One might do this to forward values, or to factor out blocks of settings from
- very large configs into more manageable named chunks.
-
- In this case, the composite config is expanded to be the concatenation of its
- own values, and in order, the values from its sub-configs *before* anything
- else happens. This has some ramifications:
-
- - A target has no visibility into a config's sub-configs. Target code only
- sees the name of the composite config. It can't remove sub-configs or opt
- in to only parts of it. The composite config may not even be defined
- before the target is.
-
- - You can get duplication of values if a config is listed twice, say, on a
- target and in a sub-config that also applies. In other cases, the configs
- applying to a target are de-duped. It's expected that if a config is
- listed as a sub-config that it is only used in that context. (Note that
- it's possible to fix this and de-dupe, but it's not normally relevant and
- complicates the implementation.)
-)" COMMON_ORDERING_HELP
- R"(
-Example
-
- # Configs on a target.
- source_set("foo") {
- # Don't use the default RTTI config that BUILDCONFIG applied to us.
- configs -= [ "//build:no_rtti" ]
-
- # Add some of our own settings.
- configs += [ ":mysettings" ]
- }
-
- # Create a default_optimization config that forwards to one of a set of more
- # specialized configs depending on build flags. This pattern is useful
- # because it allows a target to opt in to either a default set, or a more
- # specific set, while avoid duplicating the settings in two places.
- config("super_optimization") {
- cflags = [ ... ]
- }
- config("default_optimization") {
- if (optimize_everything) {
- configs = [ ":super_optimization" ]
- } else {
- configs = [ ":no_optimization" ]
- }
- }
-)";
-
-const char kData[] = "data";
-const char kData_HelpShort[] =
- "data: [file list] Runtime data file dependencies.";
-const char kData_Help[] =
- R"(data: Runtime data file dependencies.
-
- Lists files or directories required to run the given target. These are
- typically data files or directories of data files. The paths are interpreted
- as being relative to the current build file. Since these are runtime
- dependencies, they do not affect which targets are built or when. To declare
- input files to a script, use "inputs".
-
- Appearing in the "data" section does not imply any special handling such as
- copying them to the output directory. This is just used for declaring runtime
- dependencies. Runtime dependencies can be queried using the "runtime_deps"
- category of "gn desc" or written during build generation via
- "--runtime-deps-list-file".
-
- GN doesn't require data files to exist at build-time. So actions that produce
- files that are in turn runtime dependencies can list those generated files
- both in the "outputs" list as well as the "data" list.
-
- By convention, directories are listed with a trailing slash:
- data = [ "test/data/" ]
- However, no verification is done on these so GN doesn't enforce this. The
- paths are just rebased and passed along when requested.
-
- Note: On iOS and macOS, create_bundle targets will not be recursed into when
- gathering data. See "gn help create_bundle" for details.
-
- See "gn help runtime_deps" for how these are used.
-)";
-
-const char kDataDeps[] = "data_deps";
-const char kDataDeps_HelpShort[] =
- "data_deps: [label list] Non-linked dependencies.";
-const char kDataDeps_Help[] =
- R"(data_deps: Non-linked dependencies.
-
- A list of target labels.
-
- Specifies dependencies of a target that are not actually linked into the
- current target. Such dependencies will be built and will be available at
- runtime.
-
- This is normally used for things like plugins or helper programs that a
- target needs at runtime.
-
- Note: On iOS and macOS, create_bundle targets will not be recursed into when
- gathering data_deps. See "gn help create_bundle" for details.
-
- See also "gn help deps" and "gn help data".
-
-Example
-
- executable("foo") {
- deps = [ "//base" ]
- data_deps = [ "//plugins:my_runtime_plugin" ]
- }
-)";
-
-const char kDataKeys[] = "data_keys";
-const char kDataKeys_HelpShort[] =
- "data_keys: [string list] Keys from which to collect metadata.";
-const char kDataKeys_Help[] =
- R"(data_keys: Keys from which to collect metadata.
-
- These keys are used to identify metadata to collect. If a walked target
- defines this key in its metadata, its value will be appended to the resulting
- collection.
-
- See "gn help generated_file".
-)";
-
-const char kDefines[] = "defines";
-const char kDefines_HelpShort[] =
- "defines: [string list] C preprocessor defines.";
-const char kDefines_Help[] =
- R"(defines: C preprocessor defines.
-
- A list of strings
-
- These strings will be passed to the C/C++ compiler as #defines. The strings
- may or may not include an "=" to assign a value.
-)" COMMON_ORDERING_HELP
- R"(
-Example
-
- defines = [ "AWESOME_FEATURE", "LOG_LEVEL=3" ]
-)";
-
-const char kDepfile[] = "depfile";
-const char kDepfile_HelpShort[] =
- "depfile: [string] File name for input dependencies for actions.";
-const char kDepfile_Help[] =
- R"(depfile: [string] File name for input dependencies for actions.
-
- If nonempty, this string specifies that the current action or action_foreach
- target will generate the given ".d" file containing the dependencies of the
- input. Empty or unset means that the script doesn't generate the files.
-
- A depfile should be used only when a target depends on files that are not
- already specified by a target's inputs and sources. Likewise, depfiles should
- specify only those dependencies not already included in sources or inputs.
-
- The .d file should go in the target output directory. If you have more than
- one source file that the script is being run over, you can use the output
- file expansions described in "gn help action_foreach" to name the .d file
- according to the input."
-
- The format is that of a Makefile and all paths must be relative to the root
- build directory. Only one output may be listed and it must match the first
- output of the action.
-
- Although depfiles are created by an action, they should not be listed in the
- action's "outputs" unless another target will use the file as an input.
-
-Example
-
- action_foreach("myscript_target") {
- script = "myscript.py"
- sources = [ ... ]
-
- # Locate the depfile in the output directory named like the
- # inputs but with a ".d" appended.
- depfile = "$relative_target_output_dir/{{source_name}}.d"
-
- # Say our script uses "-o <d file>" to indicate the depfile.
- args = [ "{{source}}", "-o", depfile ]
- }
-)";
-
-const char kDeps[] = "deps";
-const char kDeps_HelpShort[] =
- "deps: [label list] Private linked dependencies.";
-const char kDeps_Help[] =
- R"(deps: Private linked dependencies.
-
- A list of target labels.
-
- Specifies private dependencies of a target. Private dependencies are
- propagated up the dependency tree and linked to dependent targets, but do not
- grant the ability to include headers from the dependency. Public configs are
- not forwarded.
-
-Details of dependency propagation
-
- Source sets, shared libraries, and non-complete static libraries will be
- propagated up the dependency tree across groups, non-complete static
- libraries and source sets.
-
- Executables, shared libraries, and complete static libraries will link all
- propagated targets and stop propagation. Actions and copy steps also stop
- propagation, allowing them to take a library as an input but not force
- dependents to link to it.
-
- Propagation of all_dependent_configs and public_configs happens independently
- of target type. all_dependent_configs are always propagated across all types
- of targets, and public_configs are always propagated across public deps of
- all types of targets.
-
- Data dependencies are propagated differently. See "gn help data_deps" and
- "gn help runtime_deps".
-
- See also "public_deps".
-)";
-
-const char kFriend[] = "friend";
-const char kFriend_HelpShort[] =
- "friend: [label pattern list] Allow targets to include private headers.";
-const char kFriend_Help[] =
- R"(friend: Allow targets to include private headers.
-
- A list of label patterns (see "gn help label_pattern") that allow dependent
- targets to include private headers. Applies to all binary targets.
-
- Normally if a target lists headers in the "public" list (see "gn help
- public"), other headers are implicitly marked as private. Private headers
- can not be included by other targets, even with a public dependency path.
- The "gn check" function performs this validation.
-
- A friend declaration allows one or more targets to include private headers.
- This is useful for things like unit tests that are closely associated with a
- target and require internal knowledge without opening up all headers to be
- included by all dependents.
-
- A friend target does not allow that target to include headers when no
- dependency exists. A public dependency path must still exist between two
- targets to include any headers from a destination target. The friend
- annotation merely allows the use of headers that would otherwise be
- prohibited because they are private.
-
- The friend annotation is matched only against the target containing the file
- with the include directive. Friend annotations are not propagated across
- public or private dependencies. Friend annotations do not affect visibility.
-
-Example
-
- static_library("lib") {
- # This target can include our private headers.
- friend = [ ":unit_tests" ]
-
- public = [
- "public_api.h", # Normal public API for dependent targets.
- ]
-
- # Private API and sources.
- sources = [
- "a_source_file.cc",
-
- # Normal targets that depend on this one won't be able to include this
- # because this target defines a list of "public" headers. Without the
- # "public" list, all headers are implicitly public.
- "private_api.h",
- ]
- }
-
- executable("unit_tests") {
- sources = [
- # This can include "private_api.h" from the :lib target because it
- # depends on that target and because of the friend annotation.
- "my_test.cc",
- ]
-
- deps = [
- ":lib", # Required for the include to be allowed.
- ]
- }
-)";
-
-const char kIncludeDirs[] = "include_dirs";
-const char kIncludeDirs_HelpShort[] =
- "include_dirs: [directory list] Additional include directories.";
-const char kIncludeDirs_Help[] =
- R"(include_dirs: Additional include directories.
-
- A list of source directories.
-
- The directories in this list will be added to the include path for the files
- in the affected target.
-)" COMMON_ORDERING_HELP
- R"(
-Example
-
- include_dirs = [ "src/include", "//third_party/foo" ]
-)";
-
-const char kInputs[] = "inputs";
-const char kInputs_HelpShort[] =
- "inputs: [file list] Additional compile-time dependencies.";
-const char kInputs_Help[] =
- R"(inputs: Additional compile-time dependencies.
-
- Inputs are compile-time dependencies of the current target. This means that
- all inputs must be available before compiling any of the sources or executing
- any actions.
-
- Inputs are typically only used for action and action_foreach targets.
-
-Inputs for actions
-
- For action and action_foreach targets, inputs should be the inputs to script
- that don't vary. These should be all .py files that the script uses via
- imports (the main script itself will be an implicit dependency of the action
- so need not be listed).
-
- For action targets, inputs and sources are treated the same, but from a style
- perspective, it's recommended to follow the same rule as action_foreach and
- put helper files in the inputs, and the data used by the script (if any) in
- sources.
-
- Note that another way to declare input dependencies from an action is to have
- the action write a depfile (see "gn help depfile"). This allows the script to
- dynamically write input dependencies, that might not be known until actually
- executing the script. This is more efficient than doing processing while
- running GN to determine the inputs, and is easier to keep in-sync than
- hardcoding the list.
-
-Script input gotchas
-
- It may be tempting to write a script that enumerates all files in a directory
- as inputs. Don't do this! Even if you specify all the files in the inputs or
- sources in the GN target (or worse, enumerate the files in an exec_script
- call when running GN, which will be slow), the dependencies will be broken.
-
- The problem happens if a file is ever removed because the inputs are not
- listed on the command line to the script. Because the script hasn't changed
- and all inputs are up to date, the script will not re-run and you will get a
- stale build. Instead, either list all inputs on the command line to the
- script, or if there are many, create a separate list file that the script
- reads. As long as this file is listed in the inputs, the build will detect
- when it has changed in any way and the action will re-run.
-
-Inputs for binary targets
-
- Any input dependencies will be resolved before compiling any sources or
- linking the target. Normally, all actions that a target depends on will be run
- before any files in a target are compiled. So if you depend on generated
- headers, you do not typically need to list them in the inputs section.
-
- Inputs for binary targets will be treated as implicit dependencies, meaning
- that changes in any of the inputs will force all sources in the target to be
- recompiled. If an input only applies to a subset of source files, you may
- want to split those into a separate target to avoid unnecessary recompiles.
-
-Example
-
- action("myscript") {
- script = "domything.py"
- inputs = [ "input.data" ]
- }
-)";
-
-const char kLdflags[] = "ldflags";
-const char kLdflags_HelpShort[] =
- "ldflags: [string list] Flags passed to the linker.";
-const char kLdflags_Help[] =
- R"(ldflags: Flags passed to the linker.
-
- A list of strings.
-
- These flags are passed on the command-line to the linker and generally
- specify various linking options. Most targets will not need these and will
- use "libs" and "lib_dirs" instead.
-
- ldflags are NOT pushed to dependents, so applying ldflags to source sets or
- static libraries will be a no-op. If you want to apply ldflags to dependent
- targets, put them in a config and set it in the all_dependent_configs or
- public_configs.
-)" COMMON_ORDERING_HELP;
-
-#define COMMON_LIB_INHERITANCE_HELP \
- "\n" \
- " libs and lib_dirs work differently than other flags in two respects.\n" \
- " First, they are inherited across static library boundaries until a\n" \
- " shared library or executable target is reached. Second, they are\n" \
- " uniquified so each one is only passed once (the first instance of it\n" \
- " will be the one used).\n"
-
-#define LIBS_AND_LIB_DIRS_ORDERING_HELP \
- "\n" \
- " For \"libs\" and \"lib_dirs\" only, the values propagated from\n" \
- " dependencies (as described above) are applied last assuming they\n" \
- " are not already in the list.\n"
-
-const char kLibDirs[] = "lib_dirs";
-const char kLibDirs_HelpShort[] =
- "lib_dirs: [directory list] Additional library directories.";
-const char kLibDirs_Help[] =
- R"(lib_dirs: Additional library directories.
-
- A list of directories.
-
- Specifies additional directories passed to the linker for searching for the
- required libraries. If an item is not an absolute path, it will be treated as
- being relative to the current build file.
-)" COMMON_LIB_INHERITANCE_HELP COMMON_ORDERING_HELP
- LIBS_AND_LIB_DIRS_ORDERING_HELP
- R"(
-Example
-
- lib_dirs = [ "/usr/lib/foo", "lib/doom_melon" ]
-)";
-
-const char kLibs[] = "libs";
-const char kLibs_HelpShort[] =
- "libs: [string list] Additional libraries to link.";
-const char kLibs_Help[] =
- R"(libs: Additional libraries to link.
-
- A list of library names or library paths.
-
- These libraries will be linked into the final binary (executable or shared
- library) containing the current target.
-)" COMMON_LIB_INHERITANCE_HELP
- R"(
-Types of libs
-
- There are several different things that can be expressed in libs:
-
- File paths
- Values containing '/' will be treated as references to files in the
- checkout. They will be rebased to be relative to the build directory and
- specified in the "libs" for linker tools. This facility should be used
- for libraries that are checked in to the version control. For libraries
- that are generated by the build, use normal GN deps to link them.
-
- System libraries
- Values not containing '/' will be treated as system library names. These
- will be passed unmodified to the linker and prefixed with the
- "lib_switch" attribute of the linker tool. Generally you would set the
- "lib_dirs" so the given library is found. Your BUILD.gn file should not
- specify the switch (like "-l"): this will be encoded in the "lib_switch"
- of the tool.
-
- Apple frameworks
- System libraries ending in ".framework" will be special-cased: the switch
- "-framework" will be prepended instead of the lib_switch, and the
- ".framework" suffix will be trimmed. This is to support the way Mac links
- framework dependencies.
-)" COMMON_ORDERING_HELP LIBS_AND_LIB_DIRS_ORDERING_HELP
- R"(
-Examples
-
- On Windows:
- libs = [ "ctl3d.lib" ]
-
- On Linux:
- libs = [ "ld" ]
-)";
-
-const char kMetadata[] = "metadata";
-const char kMetadata_HelpShort[] = "metadata: [scope] Metadata of this target.";
-const char kMetadata_Help[] =
- R"(metadata: Metadata of this target.
-
- Metadata is a collection of keys and values relating to a particular target.
- Values must be lists, allowing for sane and predictable collection behavior.
- Generally, these keys will include three types of lists: lists of ordinary
- strings, lists of filenames intended to be rebased according to their
- particular source directory, and lists of target labels intended to be used
- as barriers to the walk. Verfication of these categories occurs at walk time,
- not creation time (since it is not clear until the walk which values are
- intended for which purpose).
-
-Example
-
- group("doom_melon") {
- metadata = {
- # These keys are not built in to GN but are interpreted when consuming
- # metadata.
- my_barrier = []
- my_files = [ "a.txt", "b.txt" ]
- }
- }
-)";
-
-const char kOutputExtension[] = "output_extension";
-const char kOutputExtension_HelpShort[] =
- "output_extension: [string] Value to use for the output's file extension.";
-const char kOutputExtension_Help[] =
- R"(output_extension: Value to use for the output's file extension.
-
- Normally the file extension for a target is based on the target type and the
- operating system, but in rare cases you will need to override the name (for
- example to use "libfreetype.so.6" instead of libfreetype.so on Linux).
-
- This value should not include a leading dot. If undefined, the default
- specified on the tool will be used. If set to the empty string, no output
- extension will be used.
-
- The output_extension will be used to set the "{{output_extension}}" expansion
- which the linker tool will generally use to specify the output file name. See
- "gn help tool".
-
-Example
-
- shared_library("freetype") {
- if (is_linux) {
- # Call the output "libfreetype.so.6"
- output_extension = "so.6"
- }
- ...
- }
-
- # On Windows, generate a "mysettings.cpl" control panel applet. Control panel
- # applets are actually special shared libraries.
- if (is_win) {
- shared_library("mysettings") {
- output_extension = "cpl"
- ...
- }
- }
-)";
-
-const char kOutputDir[] = "output_dir";
-const char kOutputDir_HelpShort[] =
- "output_dir: [directory] Directory to put output file in.";
-const char kOutputDir_Help[] =
- R"(output_dir: [directory] Directory to put output file in.
-
- For library and executable targets, overrides the directory for the final
- output. This must be in the root_build_dir or a child thereof.
-
- This should generally be in the root_out_dir or a subdirectory thereof (the
- root_out_dir will be the same as the root_build_dir for the default
- toolchain, and will be a subdirectory for other toolchains). Not putting the
- output in a subdirectory of root_out_dir can result in collisions between
- different toolchains, so you will need to take steps to ensure that your
- target is only present in one toolchain.
-
- Normally the toolchain specifies the output directory for libraries and
- executables (see "gn help tool"). You will have to consult that for the
- default location. The default location will be used if output_dir is
- undefined or empty.
-
-Example
-
- shared_library("doom_melon") {
- output_dir = "$root_out_dir/plugin_libs"
- ...
- }
-)";
-
-const char kOutputName[] = "output_name";
-const char kOutputName_HelpShort[] =
- "output_name: [string] Name for the output file other than the default.";
-const char kOutputName_Help[] =
- R"(output_name: Define a name for the output file other than the default.
-
- Normally the output name of a target will be based on the target name, so the
- target "//foo/bar:bar_unittests" will generate an output file such as
- "bar_unittests.exe" (using Windows as an example).
-
- Sometimes you will want an alternate name to avoid collisions or if the
- internal name isn't appropriate for public distribution.
-
- The output name should have no extension or prefixes, these will be added
- using the default system rules. For example, on Linux an output name of "foo"
- will produce a shared library "libfoo.so". There is no way to override the
- output prefix of a linker tool on a per- target basis. If you need more
- flexibility, create a copy target to produce the file you want.
-
- This variable is valid for all binary output target types.
-
-Example
-
- static_library("doom_melon") {
- output_name = "fluffy_bunny"
- }
-)";
-
-const char kOutputPrefixOverride[] = "output_prefix_override";
-const char kOutputPrefixOverride_HelpShort[] =
- "output_prefix_override: [boolean] Don't use prefix for output name.";
-const char kOutputPrefixOverride_Help[] =
- R"(output_prefix_override: Don't use prefix for output name.
-
- A boolean that overrides the output prefix for a target. Defaults to false.
-
- Some systems use prefixes for the names of the final target output file. The
- normal example is "libfoo.so" on Linux for a target named "foo".
-
- The output prefix for a given target type is specified on the linker tool
- (see "gn help tool"). Sometimes this prefix is undesired.
-
- See also "gn help output_extension".
-
-Example
-
- shared_library("doom_melon") {
- # Normally this will produce "libdoom_melon.so" on Linux. Setting this flag
- # will produce "doom_melon.so".
- output_prefix_override = true
- ...
- }
-)";
-
-const char kPartialInfoPlist[] = "partial_info_plist";
-const char kPartialInfoPlist_HelpShort[] =
- "partial_info_plist: [filename] Path plist from asset catalog compiler.";
-const char kPartialInfoPlist_Help[] =
- R"(partial_info_plist: [filename] Path plist from asset catalog compiler.
-
- Valid for create_bundle target, corresponds to the path for the partial
- Info.plist created by the asset catalog compiler that needs to be merged
- with the application Info.plist (usually done by the code signing script).
-
- The file will be generated regardless of whether the asset compiler has
- been invoked or not. See "gn help create_bundle".
-)";
-
-const char kOutputs[] = "outputs";
-const char kOutputs_HelpShort[] =
- "outputs: [file list] Output files for actions and copy targets.";
-const char kOutputs_Help[] =
- R"(outputs: Output files for actions and copy targets.
-
- Outputs is valid for "copy", "action", and "action_foreach" target types and
- indicates the resulting files. Outputs must always refer to files in the
- build directory.
-
- copy
- Copy targets should have exactly one entry in the outputs list. If there is
- exactly one source, this can be a literal file name or a source expansion.
- If there is more than one source, this must contain a source expansion to
- map a single input name to a single output name. See "gn help copy".
-
- action_foreach
- Action_foreach targets must always use source expansions to map input files
- to output files. There can be more than one output, which means that each
- invocation of the script will produce a set of files (presumably based on
- the name of the input file). See "gn help action_foreach".
-
- action
- Action targets (excluding action_foreach) must list literal output file(s)
- with no source expansions. See "gn help action".
-)";
-
-const char kPool[] = "pool";
-const char kPool_HelpShort[] =
- "pool: [string] Label of the pool used by the action.";
-const char kPool_Help[] =
- R"(pool: Label of the pool used by the action.
-
- A fully-qualified label representing the pool that will be used for the
- action. Pools are defined using the pool() {...} declaration.
-
-Example
-
- action("action") {
- pool = "//build:custom_pool"
- ...
- }
-)";
-
-const char kPrecompiledHeader[] = "precompiled_header";
-const char kPrecompiledHeader_HelpShort[] =
- "precompiled_header: [string] Header file to precompile.";
-const char kPrecompiledHeader_Help[] =
- R"(precompiled_header: [string] Header file to precompile.
-
- Precompiled headers will be used when a target specifies this value, or a
- config applying to this target specifies this value. In addition, the tool
- corresponding to the source files must also specify precompiled headers (see
- "gn help tool"). The tool will also specify what type of precompiled headers
- to use, by setting precompiled_header_type to either "gcc" or "msvc".
-
- The precompiled header/source variables can be specified on a target or a
- config, but must be the same for all configs applying to a given target since
- a target can only have one precompiled header.
-
- If you use both C and C++ sources, the precompiled header and source file
- will be compiled once per language. You will want to make sure to wrap C++
- includes in __cplusplus #ifdefs so the file will compile in C mode.
-
-GCC precompiled headers
-
- When using GCC-style precompiled headers, "precompiled_source" contains the
- path of a .h file that is precompiled and then included by all source files
- in targets that set "precompiled_source".
-
- The value of "precompiled_header" is not used with GCC-style precompiled
- headers.
-
-MSVC precompiled headers
-
- When using MSVC-style precompiled headers, the "precompiled_header" value is
- a string corresponding to the header. This is NOT a path to a file that GN
- recognises, but rather the exact string that appears in quotes after
- an #include line in source code. The compiler will match this string against
- includes or forced includes (/FI).
-
- MSVC also requires a source file to compile the header with. This must be
- specified by the "precompiled_source" value. In contrast to the header value,
- this IS a GN-style file name, and tells GN which source file to compile to
- make the .pch file used for subsequent compiles.
-
- For example, if the toolchain specifies MSVC headers:
-
- toolchain("vc_x64") {
- ...
- tool("cxx") {
- precompiled_header_type = "msvc"
- ...
-
- You might make a config like this:
-
- config("use_precompiled_headers") {
- precompiled_header = "build/precompile.h"
- precompiled_source = "//build/precompile.cc"
-
- # Either your source files should #include "build/precompile.h"
- # first, or you can do this to force-include the header.
- cflags = [ "/FI$precompiled_header" ]
- }
-
- And then define a target that uses the config:
-
- executable("doom_melon") {
- configs += [ ":use_precompiled_headers" ]
- ...
-)";
-
-const char kPrecompiledHeaderType[] = "precompiled_header_type";
-const char kPrecompiledHeaderType_HelpShort[] =
- "precompiled_header_type: [string] \"gcc\" or \"msvc\".";
-const char kPrecompiledHeaderType_Help[] =
- R"(precompiled_header_type: [string] "gcc" or "msvc".
-
- See "gn help precompiled_header".
-)";
-
-const char kPrecompiledSource[] = "precompiled_source";
-const char kPrecompiledSource_HelpShort[] =
- "precompiled_source: [file name] Source file to precompile.";
-const char kPrecompiledSource_Help[] =
- R"(precompiled_source: [file name] Source file to precompile.
-
- The source file that goes along with the precompiled_header when using
- "msvc"-style precompiled headers. It will be implicitly added to the sources
- of the target. See "gn help precompiled_header".
-)";
-
-const char kProductType[] = "product_type";
-const char kProductType_HelpShort[] =
- "product_type: [string] Product type for Xcode projects.";
-const char kProductType_Help[] =
- R"(product_type: Product type for Xcode projects.
-
- Correspond to the type of the product of a create_bundle target. Only
- meaningful to Xcode (used as part of the Xcode project generation).
-
- When generating Xcode project files, only create_bundle target with a
- non-empty product_type will have a corresponding target in Xcode project.
-)";
-
-const char kPublic[] = "public";
-const char kPublic_HelpShort[] =
- "public: [file list] Declare public header files for a target.";
-const char kPublic_Help[] =
- R"(public: Declare public header files for a target.
-
- A list of files that other targets can include. These permissions are checked
- via the "check" command (see "gn help check").
-
- If no public files are declared, other targets (assuming they have visibility
- to depend on this target) can include any file in the sources list. If this
- variable is defined on a target, dependent targets may only include files on
- this whitelist unless that target is marked as a friend (see "gn help
- friend").
-
- Header file permissions are also subject to visibility. A target must be
- visible to another target to include any files from it at all and the public
- headers indicate which subset of those files are permitted. See "gn help
- visibility" for more.
-
- Public files are inherited through the dependency tree. So if there is a
- dependency A -> B -> C, then A can include C's public headers. However, the
- same is NOT true of visibility, so unless A is in C's visibility list, the
- include will be rejected.
-
- GN only knows about files declared in the "sources" and "public" sections of
- targets. If a file is included that is not known to the build, it will be
- allowed.
-
- It is common for test targets to need to include private headers for their
- associated code. In this case, list the test target in the "friend" list of
- the target that owns the private header to allow the inclusion. See
- "gn help friend" for more.
-
- When a binary target has no explicit or implicit public headers (a "public"
- list is defined but is empty), GN assumes that the target can not propagate
- any compile-time dependencies up the dependency tree. In this case, the build
- can be parallelized more efficiently.
- Say there are dependencies:
- A (shared library) -> B (shared library) -> C (action).
- Normally C must complete before any source files in A can compile (because
- there might be generated includes). But when B explicitly declares no public
- headers, C can execute in parallel with A's compile steps. C must still be
- complete before any dependents link.
-
-Examples
-
- These exact files are public:
- public = [ "foo.h", "bar.h" ]
-
- No files are public (no targets may include headers from this one):
- # This allows starting compilation in dependent targets earlier.
- public = []
-)";
-
-const char kPublicConfigs[] = "public_configs";
-const char kPublicConfigs_HelpShort[] =
- "public_configs: [label list] Configs applied to dependents.";
-const char kPublicConfigs_Help[] =
- R"(public_configs: Configs to be applied on dependents.
-
- A list of config labels.
-
- Targets directly depending on this one will have the configs listed in this
- variable added to them. These configs will also apply to the current target.
- Generally, public configs are used to apply defines and include directories
- necessary to compile this target's header files.
-
- See also "gn help all_dependent_configs".
-
-Propagation of public configs
-
- Public configs are applied to all targets that depend directly on this one.
- These dependant targets can further push this target's public configs
- higher in the dependency tree by depending on it via public_deps (see "gn
- help public_deps").
-
- static_library("toplevel") {
- # This target will get "my_config" applied to it. However, since this
- # target uses "deps" and not "public_deps", targets that depend on this
- # one won't get it.
- deps = [ ":intermediate" ]
- }
-
- static_library("intermediate") {
- # Depending on "lower" in any way will apply "my_config" to this target.
- # Additionall, since this target depends on "lower" via public_deps,
- # targets that depend on this one will also get "my_config".
- public_deps = [ ":lower" ]
- }
-
- static_library("lower") {
- # This will get applied to all targets that depend on this one.
- public_configs = [ ":my_config" ]
- }
-
- Public config propagation happens in a second phase once a target and all of
- its dependencies have been resolved. Therefore, a target will not see these
- force-added configs in their "configs" variable while the script is running,
- and they can not be removed. As a result, this capability should generally
- only be used to add defines and include directories rather than setting
- complicated flags that some targets may not want.
-
- Public configs may or may not be propagated across toolchain boundaries
- depending on the value of the propagates_configs flag (see "gn help
- toolchain") on the toolchain of the target declaring the public_config.
-
-Avoiding applying public configs to this target
-
- If you want the config to apply to targets that depend on this one, but NOT
- this one, define an extra layer of indirection using a group:
-
- # External targets depend on this group.
- group("my_target") {
- # Config to apply to all targets that depend on this one.
- public_configs = [ ":external_settings" ]
- deps = [ ":internal_target" ]
- }
-
- # Internal target to actually compile the sources.
- static_library("internal_target") {
- # Force all external targets to depend on the group instead of directly
- # on this so the "external_settings" config will get applied.
- visibility = [ ":my_target" ]
- ...
- }
-
-)" COMMON_ORDERING_HELP;
-
-const char kPublicDeps[] = "public_deps";
-const char kPublicDeps_HelpShort[] =
- "public_deps: [label list] Declare public dependencies.";
-const char kPublicDeps_Help[] =
- R"(public_deps: Declare public dependencies.
-
- Public dependencies are like private dependencies (see "gn help deps") but
- additionally express that the current target exposes the listed deps as part
- of its public API.
-
- This has several ramifications:
-
- - public_configs that are part of the dependency are forwarded to direct
- dependents.
-
- - Public headers in the dependency are usable by dependents (includes do
- not require a direct dependency or visibility).
-
- - If the current target is a shared library, other shared libraries that it
- publicly depends on (directly or indirectly) are propagated up the
- dependency tree to dependents for linking.
-
- See also "gn help public_configs".
-
-Discussion
-
- Say you have three targets: A -> B -> C. C's visibility may allow B to depend
- on it but not A. Normally, this would prevent A from including any headers
- from C, and C's public_configs would apply only to B.
-
- If B lists C in its public_deps instead of regular deps, A will now inherit
- C's public_configs and the ability to include C's public headers.
-
- Generally if you are writing a target B and you include C's headers as part
- of B's public headers, or targets depending on B should consider B and C to
- be part of a unit, you should use public_deps instead of deps.
-
-Example
-
- # This target can include files from "c" but not from
- # "super_secret_implementation_details".
- executable("a") {
- deps = [ ":b" ]
- }
-
- shared_library("b") {
- deps = [ ":super_secret_implementation_details" ]
- public_deps = [ ":c" ]
- }
-)";
-
-const char kRebase[] = "rebase";
-const char kRebase_HelpShort[] =
- "rebase: [boolean] Rebase collected metadata as files.";
-const char kRebase_Help[] =
- R"(rebase: Rebase collected metadata as files.
-
- A boolean that triggers a rebase of collected metadata strings based on their
- declared file. Defaults to false.
-
- Metadata generally declares files as strings relative to the local build file.
- However, this data is often used in other contexts, and so setting this flag
- will force the metadata collection to be rebased according to the local build
- file's location and thus allow the filename to be used anywhere.
-
- Setting this flag will raise an error if any target's specified metadata is
- not a string value.
-
- See also "gn help generated_file".
-)";
-
-const char kResponseFileContents[] = "response_file_contents";
-const char kResponseFileContents_HelpShort[] =
- "response_file_contents: [string list] Contents of .rsp file for actions.";
-const char kResponseFileContents_Help[] =
- R"*(response_file_contents: Contents of a response file for actions.
-
- Sometimes the arguments passed to a script can be too long for the system's
- command-line capabilities. This is especially the case on Windows where the
- maximum command-line length is less than 8K. A response file allows you to
- pass an unlimited amount of data to a script in a temporary file for an
- action or action_foreach target.
-
- If the response_file_contents variable is defined and non-empty, the list
- will be treated as script args (including possibly substitution patterns)
- that will be written to a temporary file at build time. The name of the
- temporary file will be substituted for "{{response_file_name}}" in the script
- args.
-
- The response file contents will always be quoted and escaped according to
- Unix shell rules. To parse the response file, the Python script should use
- "shlex.split(file_contents)".
-
-Example
-
- action("process_lots_of_files") {
- script = "process.py",
- inputs = [ ... huge list of files ... ]
-
- # Write all the inputs to a response file for the script. Also,
- # make the paths relative to the script working directory.
- response_file_contents = rebase_path(inputs, root_build_dir)
-
- # The script expects the name of the response file in --file-list.
- args = [
- "--enable-foo",
- "--file-list={{response_file_name}}",
- ]
- }
-)*";
-
-const char kScript[] = "script";
-const char kScript_HelpShort[] = "script: [file name] Script file for actions.";
-const char kScript_Help[] =
- R"(script: Script file for actions.
-
- An absolute or buildfile-relative file name of a Python script to run for a
- action and action_foreach targets (see "gn help action" and "gn help
- action_foreach").
-)";
-
-const char kSources[] = "sources";
-const char kSources_HelpShort[] =
- "sources: [file list] Source files for a target.";
-const char kSources_Help[] =
- R"(sources: Source files for a target
-
- A list of files. Non-absolute paths will be resolved relative to the current
- build file.
-
-Sources for binary targets
-
- For binary targets (source sets, executables, and libraries), the known file
- types will be compiled with the associated tools. Unknown file types and
- headers will be skipped. However, you should still list all C/C+ header files
- so GN knows about the existence of those files for the purposes of include
- checking.
-
- As a special case, a file ending in ".def" will be treated as a Windows
- module definition file. It will be appended to the link line with a
- preceding "/DEF:" string. There must be at most one .def file in a target
- and they do not cross dependency boundaries (so specifying a .def file in a
- static library or source set will have no effect on the executable or shared
- library they're linked into).
-
- For Rust targets that do not specify a crate_root, then the crate_root will
- look for a lib.rs file (or main.rs for executable) or a single file in
- sources, if sources contains only one file.
-
-Sources for non-binary targets
-
- action_foreach
- The sources are the set of files that the script will be executed over. The
- script will run once per file.
-
- action
- The sources will be treated the same as inputs. See "gn help inputs" for
- more information and usage advice.
-
- copy
- The source are the source files to copy.
-)";
-
-const char kXcodeTestApplicationName[] = "xcode_test_application_name";
-const char kXcodeTestApplicationName_HelpShort[] =
- "xcode_test_application_name: [string] Name for Xcode test target.";
-const char kXcodeTestApplicationName_Help[] =
- R"(xcode_test_application_name: Name for Xcode test target.
-
- Each unit and ui test target must have a test application target, and this
- value is used to specify the relationship. Only meaningful to Xcode (used as
- part of the Xcode project generation).
-
- See "gn help create_bundle" for more information.
-
-Example
-
- create_bundle("chrome_xctest") {
- test_application_name = "chrome"
- ...
- }
-)";
-
-const char kTestonly[] = "testonly";
-const char kTestonly_HelpShort[] =
- "testonly: [boolean] Declares a target must only be used for testing.";
-const char kTestonly_Help[] =
- R"(testonly: Declares a target must only be used for testing.
-
- Boolean. Defaults to false.
-
- When a target is marked "testonly = true", it must only be depended on by
- other test-only targets. Otherwise, GN will issue an error that the
- depenedency is not allowed.
-
- This feature is intended to prevent accidentally shipping test code in a
- final product.
-
-Example
-
- source_set("test_support") {
- testonly = true
- ...
- }
-)";
-
-const char kVisibility[] = "visibility";
-const char kVisibility_HelpShort[] =
- "visibility: [label list] A list of labels that can depend on a target.";
-const char kVisibility_Help[] =
- R"(visibility: A list of labels that can depend on a target.
-
- A list of labels and label patterns that define which targets can depend on
- the current one. These permissions are checked via the "check" command (see
- "gn help check").
-
- If visibility is not defined, it defaults to public ("*").
-
- If visibility is defined, only the targets with labels that match it can
- depend on the current target. The empty list means no targets can depend on
- the current target.
-
- Tip: Often you will want the same visibility for all targets in a BUILD file.
- In this case you can just put the definition at the top, outside of any
- target, and the targets will inherit that scope and see the definition.
-
-Patterns
-
- See "gn help label_pattern" for more details on what types of patterns are
- supported. If a toolchain is specified, only targets in that toolchain will
- be matched. If a toolchain is not specified on a pattern, targets in all
- toolchains will be matched.
-
-Examples
-
- Only targets in the current buildfile ("private"):
- visibility = [ ":*" ]
-
- No targets (used for targets that should be leaf nodes):
- visibility = []
-
- Any target ("public", the default):
- visibility = [ "*" ]
-
- All targets in the current directory and any subdirectory:
- visibility = [ "./*" ]
-
- Any target in "//bar/BUILD.gn":
- visibility = [ "//bar:*" ]
-
- Any target in "//bar/" or any subdirectory thereof:
- visibility = [ "//bar/*" ]
-
- Just these specific targets:
- visibility = [ ":mything", "//foo:something_else" ]
-
- Any target in the current directory and any subdirectory thereof, plus
- any targets in "//bar/" and any subdirectory thereof.
- visibility = [ "./*", "//bar/*" ]
-)";
-
-const char kWalkKeys[] = "walk_keys";
-const char kWalkKeys_HelpShort[] =
- "walk_keys: [string list] Key(s) for managing the metadata collection "
- "walk.";
-const char kWalkKeys_Help[] =
- R"(walk_keys: Key(s) for managing the metadata collection walk.
-
- Defaults to [""].
-
- These keys are used to control the next step in a collection walk, acting as
- barriers. If a specified key is defined in a target's metadata, the walk will
- use the targets listed in that value to determine which targets are walked.
-
- If no walk_keys are specified for a generated_file target (i.e. "[""]"), the
- walk will touch all deps and data_deps of the specified target recursively.
-
- See "gn help generated_file".
-)";
-
-const char kWriteValueContents[] = "contents";
-const char kWriteValueContents_HelpShort[] =
- "contents: Contents to write to file.";
-const char kWriteValueContents_Help[] =
- R"(contents: Contents to write to file.
-
- The contents of the file for a generated_file target.
- See "gn help generated_file".
-)";
-
-const char kWriteOutputConversion[] = "output_conversion";
-const char kWriteOutputConversion_HelpShort[] =
- "output_conversion: Data format for generated_file targets.";
-const char kWriteOutputConversion_Help[] =
- R"("output_conversion: Data format for generated_file targets.
-
- Controls how the "contents" of a generated_file target is formatted.
- See "gn help io_conversion".
-)";
-
-const char kWriteRuntimeDeps[] = "write_runtime_deps";
-const char kWriteRuntimeDeps_HelpShort[] =
- "write_runtime_deps: Writes the target's runtime_deps to the given path.";
-const char kWriteRuntimeDeps_Help[] =
- R"(write_runtime_deps: Writes the target's runtime_deps to the given path.
-
- Does not synchronously write the file, but rather schedules it to be written
- at the end of generation.
-
- If the file exists and the contents are identical to that being written, the
- file will not be updated. This will prevent unnecessary rebuilds of targets
- that depend on this file.
-
- Path must be within the output directory.
-
- See "gn help runtime_deps" for how the runtime dependencies are computed.
-
- The format of this file will list one file per line with no escaping. The
- files will be relative to the root_build_dir. The first line of the file will
- be the main output file of the target itself. The file contents will be the
- same as requesting the runtime deps be written on the command line (see "gn
- help --runtime-deps-list-file").
-)";
-
-const char kXcodeExtraAttributes[] = "xcode_extra_attributes";
-const char kXcodeExtraAttributes_HelpShort[] =
- "xcode_extra_attributes: [scope] Extra attributes for Xcode projects.";
-const char kXcodeExtraAttributes_Help[] =
- R"(xcode_extra_attributes: [scope] Extra attributes for Xcode projects.
-
- The value defined in this scope will be copied to the EXTRA_ATTRIBUTES
- property of the generated Xcode project. They are only meaningful when
- generating with --ide=xcode.
-
- See "gn help create_bundle" for more information.
-)";
-
-// -----------------------------------------------------------------------------
-
-VariableInfo::VariableInfo() : help_short(""), help("") {}
-
-VariableInfo::VariableInfo(const char* in_help_short, const char* in_help)
- : help_short(in_help_short), help(in_help) {}
-
-#define INSERT_VARIABLE(var) \
- info_map[k##var] = VariableInfo(k##var##_HelpShort, k##var##_Help);
-
-const VariableInfoMap& GetBuiltinVariables() {
- static VariableInfoMap info_map;
- if (info_map.empty()) {
- INSERT_VARIABLE(CurrentCpu)
- INSERT_VARIABLE(CurrentOs)
- INSERT_VARIABLE(CurrentToolchain)
- INSERT_VARIABLE(DefaultToolchain)
- INSERT_VARIABLE(HostCpu)
- INSERT_VARIABLE(HostOs)
- INSERT_VARIABLE(Invoker)
- INSERT_VARIABLE(PythonPath)
- INSERT_VARIABLE(RootBuildDir)
- INSERT_VARIABLE(RootGenDir)
- INSERT_VARIABLE(RootOutDir)
- INSERT_VARIABLE(TargetCpu)
- INSERT_VARIABLE(TargetOs)
- INSERT_VARIABLE(TargetGenDir)
- INSERT_VARIABLE(TargetName)
- INSERT_VARIABLE(TargetOutDir)
- }
- return info_map;
-}
-
-const VariableInfoMap& GetTargetVariables() {
- static VariableInfoMap info_map;
- if (info_map.empty()) {
- INSERT_VARIABLE(AllDependentConfigs)
- INSERT_VARIABLE(AllowCircularIncludesFrom)
- INSERT_VARIABLE(Arflags)
- INSERT_VARIABLE(Args)
- INSERT_VARIABLE(Asmflags)
- INSERT_VARIABLE(AssertNoDeps)
- INSERT_VARIABLE(BundleRootDir)
- INSERT_VARIABLE(BundleContentsDir)
- INSERT_VARIABLE(BundleResourcesDir)
- INSERT_VARIABLE(BundleDepsFilter)
- INSERT_VARIABLE(BundleExecutableDir)
- INSERT_VARIABLE(Cflags)
- INSERT_VARIABLE(CflagsC)
- INSERT_VARIABLE(CflagsCC)
- INSERT_VARIABLE(CflagsObjC)
- INSERT_VARIABLE(CflagsObjCC)
- INSERT_VARIABLE(CheckIncludes)
- INSERT_VARIABLE(CodeSigningArgs)
- INSERT_VARIABLE(CodeSigningScript)
- INSERT_VARIABLE(CodeSigningSources)
- INSERT_VARIABLE(CodeSigningOutputs)
- INSERT_VARIABLE(CompleteStaticLib)
- INSERT_VARIABLE(Configs)
- INSERT_VARIABLE(Data)
- INSERT_VARIABLE(DataDeps)
- INSERT_VARIABLE(DataKeys)
- INSERT_VARIABLE(Defines)
- INSERT_VARIABLE(Depfile)
- INSERT_VARIABLE(Deps)
- INSERT_VARIABLE(Friend)
- INSERT_VARIABLE(IncludeDirs)
- INSERT_VARIABLE(Inputs)
- INSERT_VARIABLE(Ldflags)
- INSERT_VARIABLE(Libs)
- INSERT_VARIABLE(LibDirs)
- INSERT_VARIABLE(Metadata)
- INSERT_VARIABLE(OutputDir)
- INSERT_VARIABLE(OutputExtension)
- INSERT_VARIABLE(OutputName)
- INSERT_VARIABLE(OutputPrefixOverride)
- INSERT_VARIABLE(Outputs)
- INSERT_VARIABLE(PartialInfoPlist)
- INSERT_VARIABLE(Pool)
- INSERT_VARIABLE(PrecompiledHeader)
- INSERT_VARIABLE(PrecompiledHeaderType)
- INSERT_VARIABLE(PrecompiledSource)
- INSERT_VARIABLE(ProductType)
- INSERT_VARIABLE(Public)
- INSERT_VARIABLE(PublicConfigs)
- INSERT_VARIABLE(PublicDeps)
- INSERT_VARIABLE(Rebase)
- INSERT_VARIABLE(ResponseFileContents)
- INSERT_VARIABLE(Script)
- INSERT_VARIABLE(Sources)
- INSERT_VARIABLE(XcodeTestApplicationName)
- INSERT_VARIABLE(Testonly)
- INSERT_VARIABLE(Visibility)
- INSERT_VARIABLE(WalkKeys)
- INSERT_VARIABLE(WriteOutputConversion)
- INSERT_VARIABLE(WriteValueContents)
- INSERT_VARIABLE(WriteRuntimeDeps)
- INSERT_VARIABLE(XcodeExtraAttributes)
- InsertRustVariables(&info_map);
- }
- return info_map;
-}
-
-#undef INSERT_VARIABLE
-
-} // namespace variables
diff --git a/gn/tools/gn/variables.h b/gn/tools/gn/variables.h
deleted file mode 100644
index 09f2c1872ed..00000000000
--- a/gn/tools/gn/variables.h
+++ /dev/null
@@ -1,352 +0,0 @@
-// 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 TOOLS_GN_VARIABLES_H_
-#define TOOLS_GN_VARIABLES_H_
-
-#include <map>
-
-#include "base/strings/string_piece.h"
-
-namespace variables {
-
-// Builtin vars ----------------------------------------------------------------
-
-extern const char kHostCpu[];
-extern const char kHostCpu_HelpShort[];
-extern const char kHostCpu_Help[];
-
-extern const char kHostOs[];
-extern const char kHostOs_HelpShort[];
-extern const char kHostOs_Help[];
-
-extern const char kCurrentCpu[];
-extern const char kCurrentCpu_HelpShort[];
-extern const char kCurrentCpu_Help[];
-
-extern const char kCurrentOs[];
-extern const char kCurrentOs_HelpShort[];
-extern const char kCurrentOs_Help[];
-
-extern const char kCurrentToolchain[];
-extern const char kCurrentToolchain_HelpShort[];
-extern const char kCurrentToolchain_Help[];
-
-extern const char kDefaultToolchain[];
-extern const char kDefaultToolchain_HelpShort[];
-extern const char kDefaultToolchain_Help[];
-
-extern const char kInvoker[];
-extern const char kInvoker_HelpShort[];
-extern const char kInvoker_Help[];
-
-extern const char kPythonPath[];
-extern const char kPythonPath_HelpShort[];
-extern const char kPythonPath_Help[];
-
-extern const char kRootBuildDir[];
-extern const char kRootBuildDir_HelpShort[];
-extern const char kRootBuildDir_Help[];
-
-extern const char kRootGenDir[];
-extern const char kRootGenDir_HelpShort[];
-extern const char kRootGenDir_Help[];
-
-extern const char kRootOutDir[];
-extern const char kRootOutDir_HelpShort[];
-extern const char kRootOutDir_Help[];
-
-extern const char kTargetCpu[];
-extern const char kTargetCpu_HelpShort[];
-extern const char kTargetCpu_Help[];
-
-extern const char kTargetName[];
-extern const char kTargetName_HelpShort[];
-extern const char kTargetName_Help[];
-
-extern const char kTargetOs[];
-extern const char kTargetOs_HelpShort[];
-extern const char kTargetOs_Help[];
-
-extern const char kTargetGenDir[];
-extern const char kTargetGenDir_HelpShort[];
-extern const char kTargetGenDir_Help[];
-
-extern const char kTargetOutDir[];
-extern const char kTargetOutDir_HelpShort[];
-extern const char kTargetOutDir_Help[];
-
-// Target vars -----------------------------------------------------------------
-
-extern const char kAllDependentConfigs[];
-extern const char kAllDependentConfigs_HelpShort[];
-extern const char kAllDependentConfigs_Help[];
-
-extern const char kAllowCircularIncludesFrom[];
-extern const char kAllowCircularIncludesFrom_HelpShort[];
-extern const char kAllowCircularIncludesFrom_Help[];
-
-extern const char kArflags[];
-extern const char kArflags_HelpShort[];
-extern const char kArflags_Help[];
-
-extern const char kArgs[];
-extern const char kArgs_HelpShort[];
-extern const char kArgs_Help[];
-
-extern const char kAsmflags[];
-extern const char kAsmflags_HelpShort[];
-extern const char* kAsmflags_Help;
-
-extern const char kAssertNoDeps[];
-extern const char kAssertNoDeps_HelpShort[];
-extern const char kAssertNoDeps_Help[];
-
-extern const char kBundleRootDir[];
-extern const char kBundleRootDir_HelpShort[];
-extern const char kBundleRootDir_Help[];
-
-extern const char kBundleContentsDir[];
-extern const char kBundleContentsDir_HelpShort[];
-extern const char kBundleContentsDir_Help[];
-
-extern const char kBundleResourcesDir[];
-extern const char kBundleResourcesDir_HelpShort[];
-extern const char kBundleResourcesDir_Help[];
-
-extern const char kBundleDepsFilter[];
-extern const char kBundleDepsFilter_HelpShort[];
-extern const char kBundleDepsFilter_Help[];
-
-extern const char kBundleExecutableDir[];
-extern const char kBundleExecutableDir_HelpShort[];
-extern const char kBundleExecutableDir_Help[];
-
-extern const char kCflags[];
-extern const char kCflags_HelpShort[];
-extern const char* kCflags_Help;
-
-extern const char kCflagsC[];
-extern const char kCflagsC_HelpShort[];
-extern const char* kCflagsC_Help;
-
-extern const char kCflagsCC[];
-extern const char kCflagsCC_HelpShort[];
-extern const char* kCflagsCC_Help;
-
-extern const char kCflagsObjC[];
-extern const char kCflagsObjC_HelpShort[];
-extern const char* kCflagsObjC_Help;
-
-extern const char kCflagsObjCC[];
-extern const char kCflagsObjCC_HelpShort[];
-extern const char* kCflagsObjCC_Help;
-
-extern const char kCheckIncludes[];
-extern const char kCheckIncludes_HelpShort[];
-extern const char kCheckIncludes_Help[];
-
-extern const char kCodeSigningArgs[];
-extern const char kCodeSigningArgs_HelpShort[];
-extern const char kCodeSigningArgs_Help[];
-
-extern const char kCodeSigningScript[];
-extern const char kCodeSigningScript_HelpShort[];
-extern const char kCodeSigningScript_Help[];
-
-extern const char kCodeSigningSources[];
-extern const char kCodeSigningSources_HelpShort[];
-extern const char kCodeSigningSources_Help[];
-
-extern const char kCodeSigningOutputs[];
-extern const char kCodeSigningOutputs_HelpShort[];
-extern const char kCodeSigningOutputs_Help[];
-
-extern const char kCompleteStaticLib[];
-extern const char kCompleteStaticLib_HelpShort[];
-extern const char kCompleteStaticLib_Help[];
-
-extern const char kConfigs[];
-extern const char kConfigs_HelpShort[];
-extern const char kConfigs_Help[];
-
-extern const char kData[];
-extern const char kData_HelpShort[];
-extern const char kData_Help[];
-
-extern const char kDataDeps[];
-extern const char kDataDeps_HelpShort[];
-extern const char kDataDeps_Help[];
-
-extern const char kDataKeys[];
-extern const char kDataKeys_HelpShort[];
-extern const char kDataKeys_Help[];
-
-extern const char kDefines[];
-extern const char kDefines_HelpShort[];
-extern const char kDefines_Help[];
-
-extern const char kDepfile[];
-extern const char kDepfile_HelpShort[];
-extern const char kDepfile_Help[];
-
-extern const char kDeps[];
-extern const char kDeps_HelpShort[];
-extern const char kDeps_Help[];
-
-extern const char kFriend[];
-extern const char kFriend_HelpShort[];
-extern const char kFriend_Help[];
-
-extern const char kIncludeDirs[];
-extern const char kIncludeDirs_HelpShort[];
-extern const char kIncludeDirs_Help[];
-
-extern const char kInputs[];
-extern const char kInputs_HelpShort[];
-extern const char kInputs_Help[];
-
-extern const char kLdflags[];
-extern const char kLdflags_HelpShort[];
-extern const char kLdflags_Help[];
-
-extern const char kLibDirs[];
-extern const char kLibDirs_HelpShort[];
-extern const char kLibDirs_Help[];
-
-extern const char kLibs[];
-extern const char kLibs_HelpShort[];
-extern const char kLibs_Help[];
-
-extern const char kMetadata[];
-extern const char kMetadata_HelpShort[];
-extern const char kMetadata_Help[];
-
-extern const char kOutputDir[];
-extern const char kOutputDir_HelpShort[];
-extern const char kOutputDir_Help[];
-
-extern const char kOutputExtension[];
-extern const char kOutputExtension_HelpShort[];
-extern const char kOutputExtension_Help[];
-
-extern const char kOutputName[];
-extern const char kOutputName_HelpShort[];
-extern const char kOutputName_Help[];
-
-extern const char kOutputPrefixOverride[];
-extern const char kOutputPrefixOverride_HelpShort[];
-extern const char kOutputPrefixOverride_Help[];
-
-extern const char kOutputs[];
-extern const char kOutputs_HelpShort[];
-extern const char kOutputs_Help[];
-
-extern const char kPartialInfoPlist[];
-extern const char kPartialInfoPlist_HelpShort[];
-extern const char kPartialInfoPlist_Help[];
-
-extern const char kPool[];
-extern const char kPool_HelpShort[];
-extern const char kPool_Help[];
-
-extern const char kPrecompiledHeader[];
-extern const char kPrecompiledHeader_HelpShort[];
-extern const char kPrecompiledHeader_Help[];
-
-extern const char kPrecompiledHeaderType[];
-extern const char kPrecompiledHeaderType_HelpShort[];
-extern const char kPrecompiledHeaderType_Help[];
-
-extern const char kPrecompiledSource[];
-extern const char kPrecompiledSource_HelpShort[];
-extern const char kPrecompiledSource_Help[];
-
-extern const char kProductType[];
-extern const char kProductType_HelpShort[];
-extern const char kProductType_Help[];
-
-extern const char kPublic[];
-extern const char kPublic_HelpShort[];
-extern const char kPublic_Help[];
-
-extern const char kPublicConfigs[];
-extern const char kPublicConfigs_HelpShort[];
-extern const char kPublicConfigs_Help[];
-
-extern const char kPublicDeps[];
-extern const char kPublicDeps_HelpShort[];
-extern const char kPublicDeps_Help[];
-
-extern const char kRebase[];
-extern const char kRebase_HelpShort[];
-extern const char kRebase_Help[];
-
-extern const char kResponseFileContents[];
-extern const char kResponseFileContents_HelpShort[];
-extern const char kResponseFileContents_Help[];
-
-extern const char kScript[];
-extern const char kScript_HelpShort[];
-extern const char kScript_Help[];
-
-extern const char kSources[];
-extern const char kSources_HelpShort[];
-extern const char kSources_Help[];
-
-extern const char kXcodeTestApplicationName[];
-extern const char kXcodeTestApplicationName_HelpShort[];
-extern const char kXcodeTestApplicationName_Help[];
-
-extern const char kTestonly[];
-extern const char kTestonly_HelpShort[];
-extern const char kTestonly_Help[];
-
-extern const char kVisibility[];
-extern const char kVisibility_HelpShort[];
-extern const char kVisibility_Help[];
-
-extern const char kWalkKeys[];
-extern const char kWalkKeys_HelpShort[];
-extern const char kWalkKeys_Help[];
-
-extern const char kWriteValueContents[];
-extern const char kWriteValueContents_HelpShort[];
-extern const char kWriteValueContents_Help[];
-
-extern const char kWriteOutputConversion[];
-extern const char kWriteOutputConversion_HelpShort[];
-extern const char kWriteOutputConversion_Help[];
-
-extern const char kWriteRuntimeDeps[];
-extern const char kWriteRuntimeDeps_HelpShort[];
-extern const char kWriteRuntimeDeps_Help[];
-
-extern const char kXcodeExtraAttributes[];
-extern const char kXcodeExtraAttributes_HelpShort[];
-extern const char kXcodeExtraAttributes_Help[];
-
-// -----------------------------------------------------------------------------
-
-struct VariableInfo {
- VariableInfo();
- VariableInfo(const char* in_help_short, const char* in_help);
-
- const char* help_short;
- const char* help;
-};
-
-typedef std::map<base::StringPiece, VariableInfo> VariableInfoMap;
-
-// Returns the built-in readonly variables.
-// Note: this is used only for help so this getter is not threadsafe.
-const VariableInfoMap& GetBuiltinVariables();
-
-// Returns the variables used by target generators.
-// Note: this is used only for help so this getter is not threadsafe.
-const VariableInfoMap& GetTargetVariables();
-
-} // namespace variables
-
-#endif // TOOLS_GN_VARIABLES_H_
diff --git a/gn/tools/gn/visibility.cc b/gn/tools/gn/visibility.cc
deleted file mode 100644
index 75039e9e237..00000000000
--- a/gn/tools/gn/visibility.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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 "tools/gn/visibility.h"
-
-#include <memory>
-
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "tools/gn/err.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/item.h"
-#include "tools/gn/label.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/value.h"
-#include "tools/gn/variables.h"
-
-Visibility::Visibility() = default;
-
-Visibility::~Visibility() = default;
-
-bool Visibility::Set(const SourceDir& current_dir,
- const Value& value,
- Err* err) {
- patterns_.clear();
-
- if (!value.VerifyTypeIs(Value::LIST, err)) {
- CHECK(err->has_error());
- return false;
- }
-
- for (const auto& item : value.list_value()) {
- patterns_.push_back(LabelPattern::GetPattern(current_dir, item, err));
- if (err->has_error())
- return false;
- }
- return true;
-}
-
-void Visibility::SetPublic() {
- patterns_.clear();
- patterns_.push_back(LabelPattern(LabelPattern::RECURSIVE_DIRECTORY,
- SourceDir(), std::string(), Label()));
-}
-
-void Visibility::SetPrivate(const SourceDir& current_dir) {
- patterns_.clear();
- patterns_.push_back(LabelPattern(LabelPattern::DIRECTORY, current_dir,
- std::string(), Label()));
-}
-
-bool Visibility::CanSeeMe(const Label& label) const {
- return LabelPattern::VectorMatches(patterns_, label);
-}
-
-std::string Visibility::Describe(int indent, bool include_brackets) const {
- std::string outer_indent_string(indent, ' ');
-
- if (patterns_.empty())
- return outer_indent_string + "[] (no visibility)\n";
-
- std::string result;
-
- std::string inner_indent_string = outer_indent_string;
- if (include_brackets) {
- result += outer_indent_string + "[\n";
- // Indent the insides more if brackets are requested.
- inner_indent_string += " ";
- }
-
- for (const auto& pattern : patterns_)
- result += inner_indent_string + pattern.Describe() + "\n";
-
- if (include_brackets)
- result += outer_indent_string + "]\n";
- return result;
-}
-
-std::unique_ptr<base::Value> Visibility::AsValue() const {
- auto res = std::make_unique<base::ListValue>();
- for (const auto& pattern : patterns_)
- res->AppendString(pattern.Describe());
- return std::move(res);
-}
-
-// static
-bool Visibility::CheckItemVisibility(const Item* from,
- const Item* to,
- Err* err) {
- if (!to->visibility().CanSeeMe(from->label())) {
- std::string to_label = to->label().GetUserVisibleName(false);
- *err = Err(from->defined_from(), "Dependency not allowed.",
- "The item " + from->label().GetUserVisibleName(false) +
- "\n"
- "can not depend on " +
- to_label +
- "\n"
- "because it is not in " +
- to_label +
- "'s visibility list: " + to->visibility().Describe(0, true));
- return false;
- }
- return true;
-}
-
-// static
-bool Visibility::FillItemVisibility(Item* item, Scope* scope, Err* err) {
- const Value* vis_value = scope->GetValue(variables::kVisibility, true);
- if (vis_value)
- item->visibility().Set(scope->GetSourceDir(), *vis_value, err);
- else // Default to public.
- item->visibility().SetPublic();
- return !err->has_error();
-}
diff --git a/gn/tools/gn/visibility.h b/gn/tools/gn/visibility.h
deleted file mode 100644
index 4228f9dae77..00000000000
--- a/gn/tools/gn/visibility.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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 TOOLS_GN_VISIBILITY_H_
-#define TOOLS_GN_VISIBILITY_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/source_dir.h"
-
-namespace base {
-class Value;
-}
-
-class Err;
-class Item;
-class Label;
-class Scope;
-class Value;
-
-class Visibility {
- public:
- // Defaults to private visibility (only the current file).
- Visibility();
- ~Visibility();
-
- // Set the visibility to the thing specified by the given value. On failure,
- // returns false and sets the error.
- bool Set(const SourceDir& current_dir, const Value& value, Err* err);
-
- // Sets the visibility to be public.
- void SetPublic();
-
- // Sets the visibility to be private to the given directory.
- void SetPrivate(const SourceDir& current_dir);
-
- // Returns true if the target with the given label can depend on one with the
- // current visibility.
- bool CanSeeMe(const Label& label) const;
-
- // Returns a string listing the visibility. |indent| number of spaces will
- // be added on the left side of the output. If |include_brackets| is set, the
- // result will be wrapped in "[ ]" and the contents further indented. The
- // result will end in a newline.
- std::string Describe(int indent, bool include_brackets) const;
-
- // Returns value representation of this visibility
- std::unique_ptr<base::Value> AsValue() const;
-
- // Helper function to check visibility between the given two items. If
- // to is invisible to from, returns false and sets the error.
- static bool CheckItemVisibility(const Item* from, const Item* to, Err* err);
-
- // Helper function to fill an item's visibility from the "visibility" value
- // in the current scope.
- static bool FillItemVisibility(Item* item, Scope* scope, Err* err);
-
- private:
- std::vector<LabelPattern> patterns_;
-
- DISALLOW_COPY_AND_ASSIGN(Visibility);
-};
-
-#endif // TOOLS_GN_VISIBILITY_H_
diff --git a/gn/tools/gn/visibility_unittest.cc b/gn/tools/gn/visibility_unittest.cc
deleted file mode 100644
index 2f6febc5a16..00000000000
--- a/gn/tools/gn/visibility_unittest.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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 "tools/gn/visibility.h"
-#include "tools/gn/err.h"
-#include "tools/gn/label.h"
-#include "tools/gn/scope.h"
-#include "tools/gn/value.h"
-#include "util/test/test.h"
-
-TEST(Visibility, CanSeeMe) {
- Value list(nullptr, Value::LIST);
- list.list_value().push_back(Value(nullptr, "//rec/*")); // Recursive.
- list.list_value().push_back(Value(nullptr, "//dir:*")); // One dir.
- list.list_value().push_back(Value(nullptr, "//my:name")); // Exact match.
-
- Err err;
- Visibility vis;
- ASSERT_TRUE(vis.Set(SourceDir("//"), list, &err));
-
- EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
- EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//my/"), "notname")));
-
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//my/"), "name")));
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//rec/"), "anything")));
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//rec/a/"), "anything")));
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//rec/b/"), "anything")));
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//dir/"), "anything")));
- EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//dir/a/"), "anything")));
- EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//directory/"), "anything")));
-}
-
-TEST(Visibility, Public) {
- Err err;
- Visibility vis;
-
- Value list(nullptr, Value::LIST);
- list.list_value().push_back(Value(nullptr, "*"));
- ASSERT_TRUE(vis.Set(SourceDir("//"), list, &err));
-
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
- EXPECT_TRUE(vis.CanSeeMe(Label(SourceDir("//"), "")));
-}
-
-TEST(Visibility, Private) {
- Err err;
- Visibility vis;
- ASSERT_TRUE(vis.Set(SourceDir("//"), Value(nullptr, Value::LIST), &err));
-
- EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//random/"), "thing")));
- EXPECT_FALSE(vis.CanSeeMe(Label(SourceDir("//"), "")));
-}
diff --git a/gn/tools/gn/visual_studio_utils.cc b/gn/tools/gn/visual_studio_utils.cc
deleted file mode 100644
index 00cd2a32e54..00000000000
--- a/gn/tools/gn/visual_studio_utils.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/visual_studio_utils.h"
-
-#include <vector>
-
-#include "base/md5.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-
-CompilerOptions::CompilerOptions() = default;
-
-CompilerOptions::~CompilerOptions() = default;
-
-LinkerOptions::LinkerOptions() = default;
-
-LinkerOptions::~LinkerOptions() = default;
-
-std::string MakeGuid(const std::string& entry_path, const std::string& seed) {
- std::string str = base::ToUpperASCII(base::MD5String(seed + entry_path));
- return '{' + str.substr(0, 8) + '-' + str.substr(8, 4) + '-' +
- str.substr(12, 4) + '-' + str.substr(16, 4) + '-' +
- str.substr(20, 12) + '}';
-}
-
-#define SetOption(condition, member, value) \
- if (condition) { \
- options->member = value; \
- return; \
- }
-
-#define AppendOption(condition, member, value, separator) \
- if (condition) { \
- options->member += value + separator; \
- return; \
- }
-
-void ParseCompilerOption(const std::string& cflag, CompilerOptions* options) {
- if (cflag.size() > 2 && cflag[0] == '/') {
- switch (cflag[1]) {
- case 'F':
- AppendOption(cflag.size() > 3 && cflag[2] == 'I', forced_include_files,
- cflag.substr(3), ';') break;
-
- case 'G':
- if (cflag[2] == 'S') {
- SetOption(cflag.size() == 3, buffer_security_check, "true")
- SetOption(cflag.size() == 4 && cflag[3] == '-',
- buffer_security_check, "false")
- }
- break;
-
- case 'M':
- switch (cflag[2]) {
- case 'D':
- SetOption(cflag.size() == 3, runtime_library, "MultiThreadedDLL")
- SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library,
- "MultiThreadedDebugDLL") break;
-
- case 'T':
- SetOption(cflag.size() == 3, runtime_library, "MultiThreaded")
- SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library,
- "MultiThreadedDebug") break;
- }
- break;
-
- case 'O':
- switch (cflag[2]) {
- case '1':
- SetOption(cflag.size() == 3, optimization, "MinSpace") break;
-
- case '2':
- SetOption(cflag.size() == 3, optimization, "MaxSpeed") break;
-
- case 'd':
- SetOption(cflag.size() == 3, optimization, "Disabled") break;
-
- case 'x':
- SetOption(cflag.size() == 3, optimization, "Full") break;
- }
- break;
-
- case 'T':
- // Skip flags that cause treating all source files as C and C++ files.
- if (cflag.size() == 3 && (cflag[2] == 'C' || cflag[2] == 'P'))
- return;
- break;
-
- case 'W':
- switch (cflag[2]) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- SetOption(cflag.size() == 3, warning_level,
- std::string("Level") + cflag[2]) break;
-
- case 'X':
- SetOption(cflag.size() == 3, treat_warning_as_error, "true") break;
- }
- break;
-
- case 'w':
- AppendOption(cflag.size() > 3 && cflag[2] == 'd',
- disable_specific_warnings, cflag.substr(3), ';') break;
- }
- }
-
- // Put everything else into additional_options.
- options->additional_options += cflag + ' ';
-}
-
-// Parses |ldflags| value and stores it in |options|.
-void ParseLinkerOption(const std::string& ldflag, LinkerOptions* options) {
- const char kSubsytemPrefix[] = "/SUBSYSTEM:";
- if (base::StartsWith(ldflag, kSubsytemPrefix, base::CompareCase::SENSITIVE)) {
- const std::string subsystem(
- ldflag.begin() + std::string(kSubsytemPrefix).length(), ldflag.end());
- const std::vector<std::string> tokens = base::SplitString(
- subsystem, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (!tokens.empty())
- options->subsystem = tokens[0];
- }
-}
diff --git a/gn/tools/gn/visual_studio_utils_unittest.cc b/gn/tools/gn/visual_studio_utils_unittest.cc
deleted file mode 100644
index d77781407fa..00000000000
--- a/gn/tools/gn/visual_studio_utils_unittest.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/visual_studio_utils.h"
-
-#include "base/strings/string_util.h"
-#include "util/test/test.h"
-
-TEST(VisualStudioUtils, MakeGuid) {
- std::string pattern = "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}";
- std::string guid = MakeGuid(__FILE__, "foo");
- ASSERT_EQ(pattern.size(), guid.size());
- for (size_t i = 0; i < pattern.size(); ++i) {
- if (pattern[i] == 'x')
- ASSERT_TRUE(base::IsAsciiAlpha(guid[i]) || base::IsAsciiDigit(guid[i]));
- else
- ASSERT_EQ(pattern[i], guid[i]);
- }
-
- // Calling function again should produce the same GUID.
- ASSERT_EQ(guid, MakeGuid(__FILE__, "foo"));
-
- // GUIDs should be different if path or seed is different.
- ASSERT_NE(guid, MakeGuid(std::string(__FILE__) + ".txt", "foo"));
- ASSERT_NE(guid, MakeGuid(__FILE__, "bar"));
-}
-
-TEST(VisualStudioUtils, ParseCompilerOption) {
- CompilerOptions options;
- ParseCompilerOption("/FIinclude.h", &options);
- ParseCompilerOption("/FIC:/path/file.h", &options);
- ASSERT_EQ("include.h;C:/path/file.h;", options.forced_include_files);
-
- CHECK(options.buffer_security_check.empty());
- ParseCompilerOption("/GS", &options);
- ASSERT_EQ("true", options.buffer_security_check);
- ParseCompilerOption("/GS-", &options);
- ASSERT_EQ("false", options.buffer_security_check);
-
- CHECK(options.runtime_library.empty());
- ParseCompilerOption("/MD", &options);
- ASSERT_EQ("MultiThreadedDLL", options.runtime_library);
- ParseCompilerOption("/MDd", &options);
- ASSERT_EQ("MultiThreadedDebugDLL", options.runtime_library);
- ParseCompilerOption("/MT", &options);
- ASSERT_EQ("MultiThreaded", options.runtime_library);
- ParseCompilerOption("/MTd", &options);
- ASSERT_EQ("MultiThreadedDebug", options.runtime_library);
-
- CHECK(options.optimization.empty());
- ParseCompilerOption("/O1", &options);
- ASSERT_EQ("MinSpace", options.optimization);
- ParseCompilerOption("/O2", &options);
- ASSERT_EQ("MaxSpeed", options.optimization);
- ParseCompilerOption("/Od", &options);
- ASSERT_EQ("Disabled", options.optimization);
- ParseCompilerOption("/Ox", &options);
- ASSERT_EQ("Full", options.optimization);
-
- CHECK(options.additional_options.empty());
- ParseCompilerOption("/TC", &options);
- ASSERT_TRUE(options.additional_options.empty());
- ParseCompilerOption("/TP", &options);
- ASSERT_TRUE(options.additional_options.empty());
-
- CHECK(options.warning_level.empty());
- ParseCompilerOption("/W0", &options);
- ASSERT_EQ("Level0", options.warning_level);
- ParseCompilerOption("/W1", &options);
- ASSERT_EQ("Level1", options.warning_level);
- ParseCompilerOption("/W2", &options);
- ASSERT_EQ("Level2", options.warning_level);
- ParseCompilerOption("/W3", &options);
- ASSERT_EQ("Level3", options.warning_level);
- ParseCompilerOption("/W4", &options);
- ASSERT_EQ("Level4", options.warning_level);
-
- CHECK(options.treat_warning_as_error.empty());
- ParseCompilerOption("/WX", &options);
- ASSERT_EQ("true", options.treat_warning_as_error);
-
- CHECK(options.disable_specific_warnings.empty());
- ParseCompilerOption("/wd1234", &options);
- ParseCompilerOption("/wd56", &options);
- ASSERT_EQ("1234;56;", options.disable_specific_warnings);
-
- CHECK(options.additional_options.empty());
- ParseCompilerOption("/MP", &options);
- ParseCompilerOption("/bigobj", &options);
- ParseCompilerOption("/Zc:sizedDealloc", &options);
- ASSERT_EQ("/MP /bigobj /Zc:sizedDealloc ", options.additional_options);
-}
-
-TEST(VisualStudioUtils, ParseLinkerOption) {
- LinkerOptions options;
- ParseLinkerOption("/SUBSYSTEM:CONSOLE,5.02h", &options);
- ASSERT_EQ("CONSOLE", options.subsystem);
-
- ParseLinkerOption("/SUBSYSTEM:WINDOWS", &options);
- ASSERT_EQ("WINDOWS", options.subsystem);
-}
diff --git a/gn/tools/gn/visual_studio_writer.cc b/gn/tools/gn/visual_studio_writer.cc
deleted file mode 100644
index 5f4380cdab5..00000000000
--- a/gn/tools/gn/visual_studio_writer.cc
+++ /dev/null
@@ -1,913 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/visual_studio_writer.h"
-
-#include <algorithm>
-#include <iterator>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-#include "base/containers/queue.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/config.h"
-#include "tools/gn/config_values_extractors.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/label_pattern.h"
-#include "tools/gn/parse_tree.h"
-#include "tools/gn/path_output.h"
-#include "tools/gn/standard_out.h"
-#include "tools/gn/target.h"
-#include "tools/gn/variables.h"
-#include "tools/gn/visual_studio_utils.h"
-#include "tools/gn/xml_element_writer.h"
-
-#if defined(OS_WIN)
-#include "base/win/registry.h"
-#endif
-
-namespace {
-
-struct SemicolonSeparatedWriter {
- void operator()(const std::string& value, std::ostream& out) const {
- out << XmlEscape(value) + ';';
- }
-};
-
-struct IncludeDirWriter {
- explicit IncludeDirWriter(PathOutput& path_output)
- : path_output_(path_output) {}
- ~IncludeDirWriter() = default;
-
- void operator()(const SourceDir& dir, std::ostream& out) const {
- path_output_.WriteDir(out, dir, PathOutput::DIR_NO_LAST_SLASH);
- out << ";";
- }
-
- PathOutput& path_output_;
-};
-
-struct SourceFileWriter {
- SourceFileWriter(PathOutput& path_output, const SourceFile& source_file)
- : path_output_(path_output), source_file_(source_file) {}
- ~SourceFileWriter() = default;
-
- void operator()(std::ostream& out) const {
- path_output_.WriteFile(out, source_file_);
- }
-
- PathOutput& path_output_;
- const SourceFile& source_file_;
-};
-
-const char kToolsetVersionVs2013[] = "v120"; // Visual Studio 2013
-const char kToolsetVersionVs2015[] = "v140"; // Visual Studio 2015
-const char kToolsetVersionVs2017[] = "v141"; // Visual Studio 2017
-const char kToolsetVersionVs2019[] = "v142"; // Visual Studio 2019
-const char kProjectVersionVs2013[] = "12.0"; // Visual Studio 2013
-const char kProjectVersionVs2015[] = "14.0"; // Visual Studio 2015
-const char kProjectVersionVs2017[] = "15.0"; // Visual Studio 2017
-const char kProjectVersionVs2019[] = "16.0"; // Visual Studio 2019
-const char kVersionStringVs2013[] = "Visual Studio 2013"; // Visual Studio 2013
-const char kVersionStringVs2015[] = "Visual Studio 2015"; // Visual Studio 2015
-const char kVersionStringVs2017[] = "Visual Studio 2017"; // Visual Studio 2017
-const char kVersionStringVs2019[] = "Visual Studio 2019"; // Visual Studio 2019
-const char kWindowsKitsVersion[] = "10"; // Windows 10 SDK
-const char kWindowsKitsDefaultVersion[] = "10.0.17134.0"; // Windows 10 SDK
-
-const char kGuidTypeProject[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
-const char kGuidTypeFolder[] = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
-const char kGuidSeedProject[] = "project";
-const char kGuidSeedFolder[] = "folder";
-const char kGuidSeedFilter[] = "filter";
-
-const char kConfigurationName[] = "GN";
-
-const char kCharSetUnicode[] = "_UNICODE";
-const char kCharSetMultiByte[] = "_MBCS";
-
-std::string GetWindowsKitsIncludeDirs(const std::string& win_kit) {
- std::string kits_path;
-
-#if defined(OS_WIN)
- const base::char16* const subkeys[] = {
- L"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
- L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots"};
-
- base::string16 value_name =
- base::ASCIIToUTF16("KitsRoot") + base::ASCIIToUTF16(kWindowsKitsVersion);
-
- for (const base::char16* subkey : subkeys) {
- base::win::RegKey key(HKEY_LOCAL_MACHINE, subkey, KEY_READ);
- base::string16 value;
- if (key.ReadValue(value_name.c_str(), &value) == ERROR_SUCCESS) {
- kits_path = base::UTF16ToUTF8(value);
- break;
- }
- }
-#endif // OS_WIN
-
- if (kits_path.empty()) {
- kits_path = std::string("C:\\Program Files (x86)\\Windows Kits\\") +
- kWindowsKitsVersion + "\\";
- }
-
- const std::string kit_prefix = kits_path + "Include\\" + win_kit + "\\";
- return kit_prefix + "shared;" + kit_prefix + "um;" + kit_prefix + "winrt;";
-}
-
-std::string GetConfigurationType(const Target* target, Err* err) {
- switch (target->output_type()) {
- case Target::EXECUTABLE:
- return "Application";
- case Target::SHARED_LIBRARY:
- case Target::LOADABLE_MODULE:
- return "DynamicLibrary";
- case Target::STATIC_LIBRARY:
- case Target::SOURCE_SET:
- return "StaticLibrary";
- case Target::GROUP:
- return "Utility";
-
- default:
- *err = Err(Location(),
- "Visual Studio doesn't support '" + target->label().name() +
- "' target output type: " +
- Target::GetStringForOutputType(target->output_type()));
- return std::string();
- }
-}
-
-void ParseCompilerOptions(const std::vector<std::string>& cflags,
- CompilerOptions* options) {
- for (const std::string& flag : cflags)
- ParseCompilerOption(flag, options);
-}
-
-void ParseCompilerOptions(const Target* target, CompilerOptions* options) {
- for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
- ParseCompilerOptions(iter.cur().cflags(), options);
- ParseCompilerOptions(iter.cur().cflags_c(), options);
- ParseCompilerOptions(iter.cur().cflags_cc(), options);
- }
-}
-
-void ParseLinkerOptions(const std::vector<std::string>& ldflags,
- LinkerOptions* options) {
- for (const std::string& flag : ldflags)
- ParseLinkerOption(flag, options);
-}
-
-void ParseLinkerOptions(const Target* target, LinkerOptions* options) {
- for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
- ParseLinkerOptions(iter.cur().ldflags(), options);
- }
-}
-
-// Returns a string piece pointing into the input string identifying the parent
-// directory path, excluding the last slash. Note that the input pointer must
-// outlive the output.
-base::StringPiece FindParentDir(const std::string* path) {
- DCHECK(path && !path->empty());
- for (int i = static_cast<int>(path->size()) - 2; i >= 0; --i) {
- if (IsSlash((*path)[i]))
- return base::StringPiece(path->data(), i);
- }
- return base::StringPiece();
-}
-
-bool FilterTargets(const BuildSettings* build_settings,
- const Builder& builder,
- const std::string& filters,
- bool no_deps,
- std::vector<const Target*>* targets,
- Err* err) {
- if (filters.empty()) {
- *targets = builder.GetAllResolvedTargets();
- return true;
- }
-
- std::vector<LabelPattern> patterns;
- if (!commands::FilterPatternsFromString(build_settings, filters, &patterns,
- err))
- return false;
-
- commands::FilterTargetsByPatterns(builder.GetAllResolvedTargets(), patterns,
- targets);
-
- if (no_deps)
- return true;
-
- std::set<Label> labels;
- base::queue<const Target*> to_process;
- for (const Target* target : *targets) {
- labels.insert(target->label());
- to_process.push(target);
- }
-
- while (!to_process.empty()) {
- const Target* target = to_process.front();
- to_process.pop();
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
- if (labels.find(pair.label) == labels.end()) {
- targets->push_back(pair.ptr);
- to_process.push(pair.ptr);
- labels.insert(pair.label);
- }
- }
- }
-
- return true;
-}
-
-bool UnicodeTarget(const Target* target) {
- for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
- for (const std::string& define : it.cur().defines()) {
- if (define == kCharSetUnicode)
- return true;
- if (define == kCharSetMultiByte)
- return false;
- }
- }
- return true;
-}
-
-} // namespace
-
-VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name,
- const std::string& _path,
- const std::string& _guid)
- : name(_name), path(_path), guid(_guid), parent_folder(nullptr) {}
-
-VisualStudioWriter::SolutionEntry::~SolutionEntry() = default;
-
-VisualStudioWriter::SolutionProject::SolutionProject(
- const std::string& _name,
- const std::string& _path,
- const std::string& _guid,
- const std::string& _label_dir_path,
- const std::string& _config_platform)
- : SolutionEntry(_name, _path, _guid),
- label_dir_path(_label_dir_path),
- config_platform(_config_platform) {
- // Make sure all paths use the same drive letter case. This is especially
- // important when searching for the common path prefix.
- label_dir_path[0] = base::ToUpperASCII(label_dir_path[0]);
-}
-
-VisualStudioWriter::SolutionProject::~SolutionProject() = default;
-
-VisualStudioWriter::SourceFileCompileTypePair::SourceFileCompileTypePair(
- const SourceFile* _file,
- const char* _compile_type)
- : file(_file), compile_type(_compile_type) {}
-
-VisualStudioWriter::SourceFileCompileTypePair::~SourceFileCompileTypePair() =
- default;
-
-VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings,
- const char* config_platform,
- Version version,
- const std::string& win_kit)
- : build_settings_(build_settings),
- config_platform_(config_platform),
- ninja_path_output_(build_settings->build_dir(),
- build_settings->root_path_utf8(),
- EscapingMode::ESCAPE_NINJA_COMMAND),
- windows_sdk_version_(win_kit) {
- DCHECK(!win_kit.empty());
-
- switch (version) {
- case Version::Vs2013:
- project_version_ = kProjectVersionVs2013;
- toolset_version_ = kToolsetVersionVs2013;
- version_string_ = kVersionStringVs2013;
- break;
- case Version::Vs2015:
- project_version_ = kProjectVersionVs2015;
- toolset_version_ = kToolsetVersionVs2015;
- version_string_ = kVersionStringVs2015;
- break;
- case Version::Vs2017:
- project_version_ = kProjectVersionVs2017;
- toolset_version_ = kToolsetVersionVs2017;
- version_string_ = kVersionStringVs2017;
- break;
- case Version::Vs2019:
- project_version_ = kProjectVersionVs2019;
- toolset_version_ = kToolsetVersionVs2019;
- version_string_ = kVersionStringVs2019;
- break;
- default:
- NOTREACHED() << "Not a valid Visual Studio Version: " << version;
- }
-
- windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs(win_kit);
-}
-
-VisualStudioWriter::~VisualStudioWriter() = default;
-
-// static
-bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings,
- const Builder& builder,
- Version version,
- const std::string& sln_name,
- const std::string& filters,
- const std::string& win_sdk,
- const std::string& ninja_extra_args,
- bool no_deps,
- Err* err) {
- std::vector<const Target*> targets;
- if (!FilterTargets(build_settings, builder, filters, no_deps, &targets, err))
- return false;
-
- std::string win_kit = kWindowsKitsDefaultVersion;
- if (!win_sdk.empty())
- win_kit = win_sdk;
-
- const char* config_platform = "Win32";
-
- // Assume the "target_cpu" variable does not change between different
- // toolchains.
- if (!targets.empty()) {
- const Scope* scope = targets.front()->settings()->base_config();
- const Value* target_cpu_value = scope->GetValue(variables::kTargetCpu);
- if (target_cpu_value != nullptr &&
- target_cpu_value->string_value() == "x64")
- config_platform = "x64";
- }
-
- VisualStudioWriter writer(build_settings, config_platform, version, win_kit);
- writer.projects_.reserve(targets.size());
- writer.folders_.reserve(targets.size());
-
- for (const Target* target : targets) {
- // Skip actions and bundle targets.
- if (target->output_type() == Target::COPY_FILES ||
- target->output_type() == Target::ACTION ||
- target->output_type() == Target::ACTION_FOREACH ||
- target->output_type() == Target::BUNDLE_DATA) {
- continue;
- }
-
- if (!writer.WriteProjectFiles(target, ninja_extra_args, err))
- return false;
- }
-
- if (writer.projects_.empty()) {
- *err = Err(Location(), "No Visual Studio projects generated.");
- return false;
- }
-
- // Sort projects so they appear always in the same order in solution file.
- // Otherwise solution file is rewritten and reloaded by Visual Studio.
- std::sort(writer.projects_.begin(), writer.projects_.end(),
- [](const std::unique_ptr<SolutionProject>& a,
- const std::unique_ptr<SolutionProject>& b) {
- return a->path < b->path;
- });
-
- writer.ResolveSolutionFolders();
- return writer.WriteSolutionFile(sln_name, err);
-}
-
-bool VisualStudioWriter::WriteProjectFiles(const Target* target,
- const std::string& ninja_extra_args,
- Err* err) {
- std::string project_name = target->label().name();
- const char* project_config_platform = config_platform_;
- if (!target->settings()->is_default()) {
- project_name += "_" + target->toolchain()->label().name();
- const Value* value =
- target->settings()->base_config()->GetValue(variables::kCurrentCpu);
- if (value != nullptr && value->string_value() == "x64")
- project_config_platform = "x64";
- else
- project_config_platform = "Win32";
- }
-
- SourceFile target_file =
- GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ)
- .ResolveRelativeFile(Value(nullptr, project_name + ".vcxproj"), err);
- if (target_file.is_null())
- return false;
-
- base::FilePath vcxproj_path = build_settings_->GetFullPath(target_file);
- std::string vcxproj_path_str = FilePathToUTF8(vcxproj_path);
-
- projects_.push_back(std::make_unique<SolutionProject>(
- project_name, vcxproj_path_str,
- MakeGuid(vcxproj_path_str, kGuidSeedProject),
- FilePathToUTF8(build_settings_->GetFullPath(target->label().dir())),
- project_config_platform));
-
- std::stringstream vcxproj_string_out;
- SourceFileCompileTypePairs source_types;
- if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target,
- ninja_extra_args, &source_types, err)) {
- projects_.pop_back();
- return false;
- }
-
- // Only write the content to the file if it's different. That is
- // both a performance optimization and more importantly, prevents
- // Visual Studio from reloading the projects.
- if (!WriteFileIfChanged(vcxproj_path, vcxproj_string_out.str(), err))
- return false;
-
- base::FilePath filters_path = UTF8ToFilePath(vcxproj_path_str + ".filters");
- std::stringstream filters_string_out;
- WriteFiltersFileContents(filters_string_out, target, source_types);
- return WriteFileIfChanged(filters_path, filters_string_out.str(), err);
-}
-
-bool VisualStudioWriter::WriteProjectFileContents(
- std::ostream& out,
- const SolutionProject& solution_project,
- const Target* target,
- const std::string& ninja_extra_args,
- SourceFileCompileTypePairs* source_types,
- Err* err) {
- PathOutput path_output(
- GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ),
- build_settings_->root_path_utf8(), EscapingMode::ESCAPE_NONE);
-
- out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
- XmlElementWriter project(
- out, "Project",
- XmlAttributes("DefaultTargets", "Build")
- .add("ToolsVersion", project_version_)
- .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
-
- {
- std::unique_ptr<XmlElementWriter> configurations = project.SubElement(
- "ItemGroup", XmlAttributes("Label", "ProjectConfigurations"));
- std::unique_ptr<XmlElementWriter> project_config =
- configurations->SubElement(
- "ProjectConfiguration",
- XmlAttributes("Include", std::string(kConfigurationName) + '|' +
- solution_project.config_platform));
- project_config->SubElement("Configuration")->Text(kConfigurationName);
- project_config->SubElement("Platform")
- ->Text(solution_project.config_platform);
- }
-
- {
- std::unique_ptr<XmlElementWriter> globals =
- project.SubElement("PropertyGroup", XmlAttributes("Label", "Globals"));
- globals->SubElement("ProjectGuid")->Text(solution_project.guid);
- globals->SubElement("Keyword")->Text("Win32Proj");
- globals->SubElement("RootNamespace")->Text(target->label().name());
- globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true");
- globals->SubElement("PreferredToolArchitecture")->Text("x64");
- globals->SubElement("WindowsTargetPlatformVersion")
- ->Text(windows_sdk_version_);
- }
-
- project.SubElement(
- "Import", XmlAttributes("Project",
- "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"));
-
- {
- std::unique_ptr<XmlElementWriter> configuration = project.SubElement(
- "PropertyGroup", XmlAttributes("Label", "Configuration"));
- bool unicode_target = UnicodeTarget(target);
- configuration->SubElement("CharacterSet")
- ->Text(unicode_target ? "Unicode" : "MultiByte");
- std::string configuration_type = GetConfigurationType(target, err);
- if (configuration_type.empty())
- return false;
- configuration->SubElement("ConfigurationType")->Text(configuration_type);
- }
-
- {
- std::unique_ptr<XmlElementWriter> locals =
- project.SubElement("PropertyGroup", XmlAttributes("Label", "Locals"));
- locals->SubElement("PlatformToolset")->Text(toolset_version_);
- }
-
- project.SubElement(
- "Import",
- XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"));
- project.SubElement(
- "Import",
- XmlAttributes("Project",
- "$(VCTargetsPath)\\BuildCustomizations\\masm.props"));
- project.SubElement("ImportGroup",
- XmlAttributes("Label", "ExtensionSettings"));
-
- {
- std::unique_ptr<XmlElementWriter> property_sheets = project.SubElement(
- "ImportGroup", XmlAttributes("Label", "PropertySheets"));
- property_sheets->SubElement(
- "Import",
- XmlAttributes(
- "Condition",
- "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')")
- .add("Label", "LocalAppDataPlatform")
- .add("Project",
- "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"));
- }
-
- project.SubElement("PropertyGroup", XmlAttributes("Label", "UserMacros"));
-
- std::string ninja_target = GetNinjaTarget(target);
-
- {
- std::unique_ptr<XmlElementWriter> properties =
- project.SubElement("PropertyGroup");
- properties->SubElement("OutDir")->Text("$(SolutionDir)");
- properties->SubElement("TargetName")->Text("$(ProjectName)");
- if (target->output_type() != Target::GROUP) {
- properties->SubElement("TargetPath")->Text("$(OutDir)\\" + ninja_target);
- }
- }
-
- {
- std::unique_ptr<XmlElementWriter> item_definitions =
- project.SubElement("ItemDefinitionGroup");
- {
- std::unique_ptr<XmlElementWriter> cl_compile =
- item_definitions->SubElement("ClCompile");
- {
- std::unique_ptr<XmlElementWriter> include_dirs =
- cl_compile->SubElement("AdditionalIncludeDirectories");
- RecursiveTargetConfigToStream<SourceDir>(
- target, &ConfigValues::include_dirs, IncludeDirWriter(path_output),
- include_dirs->StartContent(false));
- include_dirs->Text(windows_kits_include_dirs_ +
- "$(VSInstallDir)\\VC\\atlmfc\\include;" +
- "%(AdditionalIncludeDirectories)");
- }
- CompilerOptions options;
- ParseCompilerOptions(target, &options);
- if (!options.additional_options.empty()) {
- cl_compile->SubElement("AdditionalOptions")
- ->Text(options.additional_options + "%(AdditionalOptions)");
- }
- if (!options.buffer_security_check.empty()) {
- cl_compile->SubElement("BufferSecurityCheck")
- ->Text(options.buffer_security_check);
- }
- cl_compile->SubElement("CompileAsWinRT")->Text("false");
- cl_compile->SubElement("DebugInformationFormat")->Text("ProgramDatabase");
- if (!options.disable_specific_warnings.empty()) {
- cl_compile->SubElement("DisableSpecificWarnings")
- ->Text(options.disable_specific_warnings +
- "%(DisableSpecificWarnings)");
- }
- cl_compile->SubElement("ExceptionHandling")->Text("false");
- if (!options.forced_include_files.empty()) {
- cl_compile->SubElement("ForcedIncludeFiles")
- ->Text(options.forced_include_files);
- }
- cl_compile->SubElement("MinimalRebuild")->Text("false");
- if (!options.optimization.empty())
- cl_compile->SubElement("Optimization")->Text(options.optimization);
- cl_compile->SubElement("PrecompiledHeader")->Text("NotUsing");
- {
- std::unique_ptr<XmlElementWriter> preprocessor_definitions =
- cl_compile->SubElement("PreprocessorDefinitions");
- RecursiveTargetConfigToStream<std::string>(
- target, &ConfigValues::defines, SemicolonSeparatedWriter(),
- preprocessor_definitions->StartContent(false));
- preprocessor_definitions->Text("%(PreprocessorDefinitions)");
- }
- if (!options.runtime_library.empty())
- cl_compile->SubElement("RuntimeLibrary")->Text(options.runtime_library);
- if (!options.treat_warning_as_error.empty()) {
- cl_compile->SubElement("TreatWarningAsError")
- ->Text(options.treat_warning_as_error);
- }
- if (!options.warning_level.empty())
- cl_compile->SubElement("WarningLevel")->Text(options.warning_level);
- }
-
- std::unique_ptr<XmlElementWriter> link =
- item_definitions->SubElement("Link");
- {
- LinkerOptions options;
- ParseLinkerOptions(target, &options);
- if (!options.subsystem.empty())
- link->SubElement("SubSystem")->Text(options.subsystem);
- }
-
- // We don't include resource compilation and other link options as ninja
- // files are used to generate real build.
- }
-
- {
- std::unique_ptr<XmlElementWriter> group = project.SubElement("ItemGroup");
- std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
-
- for (const SourceFile& file : target->sources()) {
- const char* compile_type;
- const char* tool_name = Tool::kToolNone;
- if (target->GetOutputFilesForSource(file, &tool_name, &tool_outputs)) {
- compile_type = "CustomBuild";
- std::unique_ptr<XmlElementWriter> build = group->SubElement(
- compile_type, "Include", SourceFileWriter(path_output, file));
- build->SubElement("Command")->Text("call ninja.exe -C $(OutDir) " +
- ninja_extra_args + " " +
- tool_outputs[0].value());
- build->SubElement("Outputs")->Text("$(OutDir)" +
- tool_outputs[0].value());
- } else {
- compile_type = "None";
- group->SubElement(compile_type, "Include",
- SourceFileWriter(path_output, file));
- }
- source_types->push_back(SourceFileCompileTypePair(&file, compile_type));
- }
- }
-
- project.SubElement(
- "Import",
- XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"));
- project.SubElement(
- "Import",
- XmlAttributes("Project",
- "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"));
- project.SubElement("ImportGroup", XmlAttributes("Label", "ExtensionTargets"));
-
- {
- std::unique_ptr<XmlElementWriter> build =
- project.SubElement("Target", XmlAttributes("Name", "Build"));
- build->SubElement(
- "Exec",
- XmlAttributes("Command", "call ninja.exe -C $(OutDir) " +
- ninja_extra_args + " " + ninja_target));
- }
-
- {
- std::unique_ptr<XmlElementWriter> clean =
- project.SubElement("Target", XmlAttributes("Name", "Clean"));
- clean->SubElement(
- "Exec",
- XmlAttributes("Command",
- "call ninja.exe -C $(OutDir) -tclean " + ninja_target));
- }
-
- return true;
-}
-
-void VisualStudioWriter::WriteFiltersFileContents(
- std::ostream& out,
- const Target* target,
- const SourceFileCompileTypePairs& source_types) {
- out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
- XmlElementWriter project(
- out, "Project",
- XmlAttributes("ToolsVersion", "4.0")
- .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
-
- std::ostringstream files_out;
-
- {
- std::unique_ptr<XmlElementWriter> filters_group =
- project.SubElement("ItemGroup");
- XmlElementWriter files_group(files_out, "ItemGroup", XmlAttributes(), 2);
-
- // File paths are relative to vcxproj files which are generated to out dirs.
- // Filters tree structure need to reflect source directories and be relative
- // to target file. We need two path outputs then.
- PathOutput file_path_output(
- GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ),
- build_settings_->root_path_utf8(), EscapingMode::ESCAPE_NONE);
- PathOutput filter_path_output(target->label().dir(),
- build_settings_->root_path_utf8(),
- EscapingMode::ESCAPE_NONE);
-
- std::set<std::string> processed_filters;
-
- for (const auto& file_and_type : source_types) {
- std::unique_ptr<XmlElementWriter> cl_item = files_group.SubElement(
- file_and_type.compile_type, "Include",
- SourceFileWriter(file_path_output, *file_and_type.file));
-
- std::ostringstream target_relative_out;
- filter_path_output.WriteFile(target_relative_out, *file_and_type.file);
- std::string target_relative_path = target_relative_out.str();
- ConvertPathToSystem(&target_relative_path);
- base::StringPiece filter_path = FindParentDir(&target_relative_path);
-
- if (!filter_path.empty()) {
- std::string filter_path_str = filter_path.as_string();
- while (processed_filters.find(filter_path_str) ==
- processed_filters.end()) {
- auto it = processed_filters.insert(filter_path_str).first;
- filters_group
- ->SubElement("Filter", XmlAttributes("Include", filter_path_str))
- ->SubElement("UniqueIdentifier")
- ->Text(MakeGuid(filter_path_str, kGuidSeedFilter));
- filter_path_str = FindParentDir(&(*it)).as_string();
- if (filter_path_str.empty())
- break;
- }
- cl_item->SubElement("Filter")->Text(filter_path);
- }
- }
- }
-
- project.Text(files_out.str());
-}
-
-bool VisualStudioWriter::WriteSolutionFile(const std::string& sln_name,
- Err* err) {
- std::string name = sln_name.empty() ? "all" : sln_name;
- SourceFile sln_file = build_settings_->build_dir().ResolveRelativeFile(
- Value(nullptr, name + ".sln"), err);
- if (sln_file.is_null())
- return false;
-
- base::FilePath sln_path = build_settings_->GetFullPath(sln_file);
-
- std::stringstream string_out;
- WriteSolutionFileContents(string_out, sln_path.DirName());
-
- // Only write the content to the file if it's different. That is
- // both a performance optimization and more importantly, prevents
- // Visual Studio from reloading the projects.
- return WriteFileIfChanged(sln_path, string_out.str(), err);
-}
-
-void VisualStudioWriter::WriteSolutionFileContents(
- std::ostream& out,
- const base::FilePath& solution_dir_path) {
- out << "Microsoft Visual Studio Solution File, Format Version 12.00"
- << std::endl;
- out << "# " << version_string_ << std::endl;
-
- SourceDir solution_dir(FilePathToUTF8(solution_dir_path));
- for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
- out << "Project(\"" << kGuidTypeFolder << "\") = \"(" << folder->name
- << ")\", \"" << RebasePath(folder->path, solution_dir) << "\", \""
- << folder->guid << "\"" << std::endl;
- out << "EndProject" << std::endl;
- }
-
- for (const std::unique_ptr<SolutionProject>& project : projects_) {
- out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name
- << "\", \"" << RebasePath(project->path, solution_dir) << "\", \""
- << project->guid << "\"" << std::endl;
- out << "EndProject" << std::endl;
- }
-
- out << "Global" << std::endl;
-
- out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"
- << std::endl;
- const std::string config_mode_prefix = std::string(kConfigurationName) + '|';
- const std::string config_mode = config_mode_prefix + config_platform_;
- out << "\t\t" << config_mode << " = " << config_mode << std::endl;
- out << "\tEndGlobalSection" << std::endl;
-
- out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"
- << std::endl;
- for (const std::unique_ptr<SolutionProject>& project : projects_) {
- const std::string project_config_mode =
- config_mode_prefix + project->config_platform;
- out << "\t\t" << project->guid << '.' << config_mode
- << ".ActiveCfg = " << project_config_mode << std::endl;
- out << "\t\t" << project->guid << '.' << config_mode
- << ".Build.0 = " << project_config_mode << std::endl;
- }
- out << "\tEndGlobalSection" << std::endl;
-
- out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl;
- out << "\t\tHideSolutionNode = FALSE" << std::endl;
- out << "\tEndGlobalSection" << std::endl;
-
- out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl;
- for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
- if (folder->parent_folder) {
- out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid
- << std::endl;
- }
- }
- for (const std::unique_ptr<SolutionProject>& project : projects_) {
- out << "\t\t" << project->guid << " = " << project->parent_folder->guid
- << std::endl;
- }
- out << "\tEndGlobalSection" << std::endl;
-
- out << "EndGlobal" << std::endl;
-}
-
-void VisualStudioWriter::ResolveSolutionFolders() {
- root_folder_path_.clear();
-
- // Get all project directories. Create solution folder for each directory.
- std::map<base::StringPiece, SolutionEntry*> processed_paths;
- for (const std::unique_ptr<SolutionProject>& project : projects_) {
- base::StringPiece folder_path = project->label_dir_path;
- if (IsSlash(folder_path[folder_path.size() - 1]))
- folder_path = folder_path.substr(0, folder_path.size() - 1);
- auto it = processed_paths.find(folder_path);
- if (it != processed_paths.end()) {
- project->parent_folder = it->second;
- } else {
- std::string folder_path_str = folder_path.as_string();
- std::unique_ptr<SolutionEntry> folder = std::make_unique<SolutionEntry>(
- FindLastDirComponent(SourceDir(std::string(folder_path))).as_string(),
- folder_path_str, MakeGuid(folder_path_str, kGuidSeedFolder));
- project->parent_folder = folder.get();
- processed_paths[folder_path] = folder.get();
- folders_.push_back(std::move(folder));
-
- if (root_folder_path_.empty()) {
- root_folder_path_ = folder_path_str;
- } else {
- size_t common_prefix_len = 0;
- size_t max_common_length =
- std::min(root_folder_path_.size(), folder_path.size());
- size_t i;
- for (i = common_prefix_len; i < max_common_length; ++i) {
- if (IsSlash(root_folder_path_[i]) && IsSlash(folder_path[i]))
- common_prefix_len = i + 1;
- else if (root_folder_path_[i] != folder_path[i])
- break;
- }
- if (i == max_common_length &&
- (i == folder_path.size() || IsSlash(folder_path[i])))
- common_prefix_len = max_common_length;
- if (common_prefix_len < root_folder_path_.size()) {
- if (IsSlash(root_folder_path_[common_prefix_len - 1]))
- --common_prefix_len;
- root_folder_path_ = root_folder_path_.substr(0, common_prefix_len);
- }
- }
- }
- }
-
- // Create also all parent folders up to |root_folder_path_|.
- SolutionFolders additional_folders;
- for (const std::unique_ptr<SolutionEntry>& solution_folder : folders_) {
- if (solution_folder->path == root_folder_path_)
- continue;
-
- SolutionEntry* folder = solution_folder.get();
- base::StringPiece parent_path;
- while ((parent_path = FindParentDir(&folder->path)) != root_folder_path_) {
- auto it = processed_paths.find(parent_path);
- if (it != processed_paths.end()) {
- folder = it->second;
- } else {
- std::unique_ptr<SolutionEntry> new_folder =
- std::make_unique<SolutionEntry>(
- FindLastDirComponent(SourceDir(std::string(parent_path)))
- .as_string(),
- parent_path.as_string(),
- MakeGuid(parent_path.as_string(), kGuidSeedFolder));
- processed_paths[parent_path] = new_folder.get();
- folder = new_folder.get();
- additional_folders.push_back(std::move(new_folder));
- }
- }
- }
- folders_.insert(folders_.end(),
- std::make_move_iterator(additional_folders.begin()),
- std::make_move_iterator(additional_folders.end()));
-
- // Sort folders by path.
- std::sort(folders_.begin(), folders_.end(),
- [](const std::unique_ptr<SolutionEntry>& a,
- const std::unique_ptr<SolutionEntry>& b) {
- return a->path < b->path;
- });
-
- // Match subfolders with their parents. Since |folders_| are sorted by path we
- // know that parent folder always precedes its children in vector.
- std::vector<SolutionEntry*> parents;
- for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
- while (!parents.empty()) {
- if (base::StartsWith(folder->path, parents.back()->path,
- base::CompareCase::SENSITIVE)) {
- folder->parent_folder = parents.back();
- break;
- } else {
- parents.pop_back();
- }
- }
- parents.push_back(folder.get());
- }
-}
-
-std::string VisualStudioWriter::GetNinjaTarget(const Target* target) {
- std::ostringstream ninja_target_out;
- DCHECK(!target->dependency_output_file().value().empty());
- ninja_path_output_.WriteFile(ninja_target_out,
- target->dependency_output_file());
- std::string s = ninja_target_out.str();
- if (s.compare(0, 2, "./") == 0)
- s = s.substr(2);
- return s;
-}
diff --git a/gn/tools/gn/visual_studio_writer.h b/gn/tools/gn/visual_studio_writer.h
deleted file mode 100644
index 9b2a239f11c..00000000000
--- a/gn/tools/gn/visual_studio_writer.h
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_VISUAL_STUDIO_WRITER_H_
-#define TOOLS_GN_VISUAL_STUDIO_WRITER_H_
-
-#include <iosfwd>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "tools/gn/path_output.h"
-
-namespace base {
-class FilePath;
-}
-
-class Builder;
-class BuildSettings;
-class Err;
-class SourceFile;
-class Target;
-
-class VisualStudioWriter {
- public:
- enum Version {
- Vs2013 = 1, // Visual Studio 2013
- Vs2015, // Visual Studio 2015
- Vs2017, // Visual Studio 2017
- Vs2019, // Visual Studio 2019
- };
-
- // Writes Visual Studio project and solution files. |sln_name| is the optional
- // solution file name ("all" is used if not specified). |filters| is optional
- // semicolon-separated list of label patterns used to limit the set of
- // generated projects. Only matching targets and their dependencies (unless
- // |no_deps| is true) will be included to the solution. On failure will
- // populate |err| and will return false. |win_sdk| is the Windows SDK version
- // which will be used by Visual Studio IntelliSense.
- static bool RunAndWriteFiles(const BuildSettings* build_settings,
- const Builder& builder,
- Version version,
- const std::string& sln_name,
- const std::string& filters,
- const std::string& win_sdk,
- const std::string& ninja_extra_args,
- bool no_deps,
- Err* err);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(VisualStudioWriterTest, ResolveSolutionFolders);
- FRIEND_TEST_ALL_PREFIXES(VisualStudioWriterTest,
- ResolveSolutionFolders_AbsPath);
- FRIEND_TEST_ALL_PREFIXES(VisualStudioWriterTest, NoDotSlash);
-
- // Solution project or folder.
- struct SolutionEntry {
- SolutionEntry(const std::string& name,
- const std::string& path,
- const std::string& guid);
- virtual ~SolutionEntry();
-
- // Entry name. For projects must be unique in the solution.
- std::string name;
- // Absolute project file or folder directory path.
- std::string path;
- // GUID-like string.
- std::string guid;
- // Pointer to parent folder. nullptr if entry has no parent.
- SolutionEntry* parent_folder;
- };
-
- struct SolutionProject : public SolutionEntry {
- SolutionProject(const std::string& name,
- const std::string& path,
- const std::string& guid,
- const std::string& label_dir_path,
- const std::string& config_platform);
- ~SolutionProject() override;
-
- // Absolute label dir path.
- std::string label_dir_path;
- // Configuration platform. May be different than solution config platform.
- std::string config_platform;
- };
-
- struct SourceFileCompileTypePair {
- SourceFileCompileTypePair(const SourceFile* file, const char* compile_type);
- ~SourceFileCompileTypePair();
-
- // Source file.
- const SourceFile* file;
- // Compile type string.
- const char* compile_type;
- };
-
- using SolutionProjects = std::vector<std::unique_ptr<SolutionProject>>;
- using SolutionFolders = std::vector<std::unique_ptr<SolutionEntry>>;
- using SourceFileCompileTypePairs = std::vector<SourceFileCompileTypePair>;
-
- VisualStudioWriter(const BuildSettings* build_settings,
- const char* config_platform,
- Version version,
- const std::string& win_kit);
- ~VisualStudioWriter();
-
- bool WriteProjectFiles(const Target* target,
- const std::string& ninja_extra_args,
- Err* err);
- bool WriteProjectFileContents(std::ostream& out,
- const SolutionProject& solution_project,
- const Target* target,
- const std::string& ninja_extra_args,
- SourceFileCompileTypePairs* source_types,
- Err* err);
- void WriteFiltersFileContents(std::ostream& out,
- const Target* target,
- const SourceFileCompileTypePairs& source_types);
- bool WriteSolutionFile(const std::string& sln_name, Err* err);
- void WriteSolutionFileContents(std::ostream& out,
- const base::FilePath& solution_dir_path);
-
- // Resolves all solution folders (parent folders for projects) into |folders_|
- // and updates |root_folder_dir_|. Also sets |parent_folder| for |projects_|.
- void ResolveSolutionFolders();
-
- std::string GetNinjaTarget(const Target* target);
-
- const BuildSettings* build_settings_;
-
- // Toolset version.
- const char* toolset_version_;
-
- // Project version.
- const char* project_version_;
-
- // Visual Studio version string.
- const char* version_string_;
-
- // Platform for solution configuration (Win32, x64). Some projects may be
- // configured for different platform.
- const char* config_platform_;
-
- // All projects contained by solution.
- SolutionProjects projects_;
-
- // Absolute root solution folder path.
- std::string root_folder_path_;
-
- // Folders for all solution projects.
- SolutionFolders folders_;
-
- // Semicolon-separated Windows SDK include directories.
- std::string windows_kits_include_dirs_;
-
- // Path formatter for ninja targets.
- PathOutput ninja_path_output_;
-
- // Windows 10 SDK version string (e.g. 10.0.14393.0)
- std::string windows_sdk_version_;
-
- DISALLOW_COPY_AND_ASSIGN(VisualStudioWriter);
-};
-
-#endif // TOOLS_GN_VISUAL_STUDIO_WRITER_H_
diff --git a/gn/tools/gn/visual_studio_writer_unittest.cc b/gn/tools/gn/visual_studio_writer_unittest.cc
deleted file mode 100644
index 36b9bcce709..00000000000
--- a/gn/tools/gn/visual_studio_writer_unittest.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/visual_studio_writer.h"
-
-#include <memory>
-
-#include "base/strings/string_util.h"
-#include "tools/gn/test_with_scope.h"
-#include "tools/gn/visual_studio_utils.h"
-#include "util/test/test.h"
-
-namespace {
-
-class VisualStudioWriterTest : public testing::Test {
- protected:
- TestWithScope setup_;
-};
-
-std::string MakeTestPath(const std::string& path) {
-#if defined(OS_WIN)
- return "C:" + path;
-#else
- return path;
-#endif
-}
-
-} // namespace
-
-TEST_F(VisualStudioWriterTest, ResolveSolutionFolders) {
- VisualStudioWriter writer(setup_.build_settings(), "Win32",
- VisualStudioWriter::Version::Vs2015,
- "10.0.17134.0");
-
- std::string path =
- MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "base", path, MakeGuid(path, "project"),
- MakeTestPath("/foo/chromium/src/base"), "Win32"));
-
- path = MakeTestPath("/foo/chromium/src/out/Debug/obj/tools/gn/gn.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "gn", path, MakeGuid(path, "project"),
- MakeTestPath("/foo/chromium/src/tools/gn"), "Win32"));
-
- path = MakeTestPath("/foo/chromium/src/out/Debug/obj/chrome/chrome.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "chrome", path, MakeGuid(path, "project"),
- MakeTestPath("/foo/chromium/src/chrome"), "Win32"));
-
- path = MakeTestPath("/foo/chromium/src/out/Debug/obj/base/bar.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "bar", path, MakeGuid(path, "project"),
- MakeTestPath("/foo/chromium/src/base"), "Win32"));
-
- writer.ResolveSolutionFolders();
-
- ASSERT_EQ(MakeTestPath("/foo/chromium/src"), writer.root_folder_path_);
-
- ASSERT_EQ(4u, writer.folders_.size());
-
- ASSERT_EQ("base", writer.folders_[0]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/base"), writer.folders_[0]->path);
- ASSERT_EQ(nullptr, writer.folders_[0]->parent_folder);
-
- ASSERT_EQ("chrome", writer.folders_[1]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/chrome"), writer.folders_[1]->path);
- ASSERT_EQ(nullptr, writer.folders_[1]->parent_folder);
-
- ASSERT_EQ("tools", writer.folders_[2]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools"), writer.folders_[2]->path);
- ASSERT_EQ(nullptr, writer.folders_[2]->parent_folder);
-
- ASSERT_EQ("gn", writer.folders_[3]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools/gn"),
- writer.folders_[3]->path);
- ASSERT_EQ(writer.folders_[2].get(), writer.folders_[3]->parent_folder);
-
- ASSERT_EQ(writer.folders_[0].get(), writer.projects_[0]->parent_folder);
- ASSERT_EQ(writer.folders_[3].get(), writer.projects_[1]->parent_folder);
- ASSERT_EQ(writer.folders_[1].get(), writer.projects_[2]->parent_folder);
- ASSERT_EQ(writer.folders_[0].get(), writer.projects_[3]->parent_folder);
-}
-
-TEST_F(VisualStudioWriterTest, ResolveSolutionFolders_AbsPath) {
- VisualStudioWriter writer(setup_.build_settings(), "Win32",
- VisualStudioWriter::Version::Vs2015,
- "10.0.17134.0");
-
- std::string path =
- MakeTestPath("/foo/chromium/src/out/Debug/obj/base/base.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "base", path, MakeGuid(path, "project"),
- MakeTestPath("/foo/chromium/src/base"), "Win32"));
-
- path = MakeTestPath("/foo/chromium/src/out/Debug/obj/tools/gn/gn.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "gn", path, MakeGuid(path, "project"),
- MakeTestPath("/foo/chromium/src/tools/gn"), "Win32"));
-
- path = MakeTestPath(
- "/foo/chromium/src/out/Debug/obj/ABS_PATH/C/foo/bar/bar.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "bar", path, MakeGuid(path, "project"), MakeTestPath("/foo/bar"),
- "Win32"));
-
- std::string baz_label_dir_path = MakeTestPath("/foo/bar/baz");
-#if defined(OS_WIN)
- // Make sure mixed lower and upper-case drive letters are handled properly.
- baz_label_dir_path[0] = base::ToLowerASCII(baz_label_dir_path[0]);
-#endif
- path = MakeTestPath(
- "/foo/chromium/src/out/Debug/obj/ABS_PATH/C/foo/bar/baz/baz.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "baz", path, MakeGuid(path, "project"), baz_label_dir_path, "Win32"));
-
- writer.ResolveSolutionFolders();
-
- ASSERT_EQ(MakeTestPath("/foo"), writer.root_folder_path_);
-
- ASSERT_EQ(7u, writer.folders_.size());
-
- ASSERT_EQ("bar", writer.folders_[0]->name);
- ASSERT_EQ(MakeTestPath("/foo/bar"), writer.folders_[0]->path);
- ASSERT_EQ(nullptr, writer.folders_[0]->parent_folder);
-
- ASSERT_EQ("baz", writer.folders_[1]->name);
- ASSERT_EQ(MakeTestPath("/foo/bar/baz"), writer.folders_[1]->path);
- ASSERT_EQ(writer.folders_[0].get(), writer.folders_[1]->parent_folder);
-
- ASSERT_EQ("chromium", writer.folders_[2]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium"), writer.folders_[2]->path);
- ASSERT_EQ(nullptr, writer.folders_[2]->parent_folder);
-
- ASSERT_EQ("src", writer.folders_[3]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src"), writer.folders_[3]->path);
- ASSERT_EQ(writer.folders_[2].get(), writer.folders_[3]->parent_folder);
-
- ASSERT_EQ("base", writer.folders_[4]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/base"), writer.folders_[4]->path);
- ASSERT_EQ(writer.folders_[3].get(), writer.folders_[4]->parent_folder);
-
- ASSERT_EQ("tools", writer.folders_[5]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools"), writer.folders_[5]->path);
- ASSERT_EQ(writer.folders_[3].get(), writer.folders_[5]->parent_folder);
-
- ASSERT_EQ("gn", writer.folders_[6]->name);
- ASSERT_EQ(MakeTestPath("/foo/chromium/src/tools/gn"),
- writer.folders_[6]->path);
- ASSERT_EQ(writer.folders_[5].get(), writer.folders_[6]->parent_folder);
-
- ASSERT_EQ(writer.folders_[4].get(), writer.projects_[0]->parent_folder);
- ASSERT_EQ(writer.folders_[6].get(), writer.projects_[1]->parent_folder);
- ASSERT_EQ(writer.folders_[0].get(), writer.projects_[2]->parent_folder);
- ASSERT_EQ(writer.folders_[1].get(), writer.projects_[3]->parent_folder);
-}
-
-TEST_F(VisualStudioWriterTest, NoDotSlash) {
- VisualStudioWriter writer(setup_.build_settings(), "Win32",
- VisualStudioWriter::Version::Vs2015,
- "10.0.17134.0");
-
- std::string path = MakeTestPath("blah.vcxproj");
- writer.projects_.push_back(
- std::make_unique<VisualStudioWriter::SolutionProject>(
- "base", path, MakeGuid(path, "project"), MakeTestPath("/foo"),
- "Win32"));
-
- std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolAlink);
- tool->set_outputs(SubstitutionList::MakeForTest(
- "{{root_out_dir}}/{{target_output_name}}{{output_extension}}", ""));
-
- Toolchain toolchain(setup_.settings(), Label(SourceDir("//tc/"), "tc"));
- toolchain.SetTool(std::move(tool));
-
- Target target(setup_.settings(), Label(SourceDir("//baz/"), "baz"));
- target.set_output_type(Target::STATIC_LIBRARY);
- target.SetToolchain(&toolchain);
-
- Err err;
- ASSERT_TRUE(target.OnResolved(&err));
-
- VisualStudioWriter::SourceFileCompileTypePairs source_types;
-
- std::stringstream file_contents;
- writer.WriteProjectFileContents(file_contents, *writer.projects_.back(),
- &target, "", &source_types, &err);
-
- // Should find args of a ninja clean command, with no ./ before the file name.
- ASSERT_NE(file_contents.str().find("-tclean baz"), std::string::npos);
-}
diff --git a/gn/tools/gn/xcode_object.cc b/gn/tools/gn/xcode_object.cc
deleted file mode 100644
index bd5c440b984..00000000000
--- a/gn/tools/gn/xcode_object.cc
+++ /dev/null
@@ -1,990 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/xcode_object.h"
-
-#include <iomanip>
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/filesystem_utils.h"
-
-// Helper methods -------------------------------------------------------------
-
-namespace {
-struct IndentRules {
- bool one_line;
- unsigned level;
-};
-
-std::vector<std::unique_ptr<PBXObject>> EmptyPBXObjectVector() {
- return std::vector<std::unique_ptr<PBXObject>>();
-}
-
-bool CharNeedEscaping(char c) {
- if (base::IsAsciiAlpha(c) || base::IsAsciiDigit(c))
- return false;
- if (c == '$' || c == '.' || c == '/' || c == '_')
- return false;
- return true;
-}
-
-bool StringNeedEscaping(const std::string& string) {
- if (string.empty())
- return true;
- if (string.find("___") != std::string::npos)
- return true;
-
- for (char c : string) {
- if (CharNeedEscaping(c))
- return true;
- }
- return false;
-}
-
-std::string EncodeString(const std::string& string) {
- if (!StringNeedEscaping(string))
- return string;
-
- std::stringstream buffer;
- buffer << '"';
- for (char c : string) {
- if (c <= 31) {
- switch (c) {
- case '\a':
- buffer << "\\a";
- break;
- case '\b':
- buffer << "\\b";
- break;
- case '\t':
- buffer << "\\t";
- break;
- case '\n':
- case '\r':
- buffer << "\\n";
- break;
- case '\v':
- buffer << "\\v";
- break;
- case '\f':
- buffer << "\\f";
- break;
- default:
- buffer << std::hex << std::setw(4) << std::left << "\\U"
- << static_cast<unsigned>(c);
- break;
- }
- } else {
- if (c == '"' || c == '\\')
- buffer << '\\';
- buffer << c;
- }
- }
- buffer << '"';
- return buffer.str();
-}
-
-struct SourceTypeForExt {
- const char* ext;
- const char* source_type;
-};
-
-const SourceTypeForExt kSourceTypeForExt[] = {
- {"a", "archive.ar"},
- {"app", "wrapper.application"},
- {"appex", "wrapper.app-extension"},
- {"bdic", "file"},
- {"bundle", "wrapper.cfbundle"},
- {"c", "sourcecode.c.c"},
- {"cc", "sourcecode.cpp.cpp"},
- {"cpp", "sourcecode.cpp.cpp"},
- {"css", "text.css"},
- {"cxx", "sourcecode.cpp.cpp"},
- {"dart", "sourcecode"},
- {"dylib", "compiled.mach-o.dylib"},
- {"framework", "wrapper.framework"},
- {"h", "sourcecode.c.h"},
- {"hxx", "sourcecode.cpp.h"},
- {"icns", "image.icns"},
- {"java", "sourcecode.java"},
- {"js", "sourcecode.javascript"},
- {"kext", "wrapper.kext"},
- {"m", "sourcecode.c.objc"},
- {"mm", "sourcecode.cpp.objcpp"},
- {"nib", "wrapper.nib"},
- {"o", "compiled.mach-o.objfile"},
- {"pdf", "image.pdf"},
- {"pl", "text.script.perl"},
- {"plist", "text.plist.xml"},
- {"pm", "text.script.perl"},
- {"png", "image.png"},
- {"py", "text.script.python"},
- {"r", "sourcecode.rez"},
- {"rez", "sourcecode.rez"},
- {"s", "sourcecode.asm"},
- {"storyboard", "file.storyboard"},
- {"strings", "text.plist.strings"},
- {"swift", "sourcecode.swift"},
- {"ttf", "file"},
- {"xcassets", "folder.assetcatalog"},
- {"xcconfig", "text.xcconfig"},
- {"xcdatamodel", "wrapper.xcdatamodel"},
- {"xcdatamodeld", "wrapper.xcdatamodeld"},
- {"xib", "file.xib"},
- {"y", "sourcecode.yacc"},
-};
-
-const char* GetSourceType(const base::StringPiece& ext) {
- for (size_t i = 0; i < arraysize(kSourceTypeForExt); ++i) {
- if (kSourceTypeForExt[i].ext == ext)
- return kSourceTypeForExt[i].source_type;
- }
-
- return "text";
-}
-
-bool HasExplicitFileType(const base::StringPiece& ext) {
- return ext == "dart";
-}
-
-bool IsSourceFileForIndexing(const base::StringPiece& ext) {
- return ext == "c" || ext == "cc" || ext == "cpp" || ext == "cxx" ||
- ext == "m" || ext == "mm";
-}
-
-void PrintValue(std::ostream& out, IndentRules rules, unsigned value) {
- out << value;
-}
-
-void PrintValue(std::ostream& out, IndentRules rules, const char* value) {
- out << EncodeString(value);
-}
-
-void PrintValue(std::ostream& out,
- IndentRules rules,
- const std::string& value) {
- out << EncodeString(value);
-}
-
-void PrintValue(std::ostream& out, IndentRules rules, const PBXObject* value) {
- out << value->Reference();
-}
-
-template <typename ObjectClass>
-void PrintValue(std::ostream& out,
- IndentRules rules,
- const std::unique_ptr<ObjectClass>& value) {
- PrintValue(out, rules, value.get());
-}
-
-template <typename ValueType>
-void PrintValue(std::ostream& out,
- IndentRules rules,
- const std::vector<ValueType>& values) {
- IndentRules sub_rule{rules.one_line, rules.level + 1};
- out << "(" << (rules.one_line ? " " : "\n");
- for (const auto& value : values) {
- if (!sub_rule.one_line)
- out << std::string(sub_rule.level, '\t');
-
- PrintValue(out, sub_rule, value);
- out << "," << (rules.one_line ? " " : "\n");
- }
-
- if (!rules.one_line && rules.level)
- out << std::string(rules.level, '\t');
- out << ")";
-}
-
-template <typename ValueType>
-void PrintValue(std::ostream& out,
- IndentRules rules,
- const std::map<std::string, ValueType>& values) {
- IndentRules sub_rule{rules.one_line, rules.level + 1};
- out << "{" << (rules.one_line ? " " : "\n");
- for (const auto& pair : values) {
- if (!sub_rule.one_line)
- out << std::string(sub_rule.level, '\t');
-
- out << pair.first << " = ";
- PrintValue(out, sub_rule, pair.second);
- out << ";" << (rules.one_line ? " " : "\n");
- }
-
- if (!rules.one_line && rules.level)
- out << std::string(rules.level, '\t');
- out << "}";
-}
-
-template <typename ValueType>
-void PrintProperty(std::ostream& out,
- IndentRules rules,
- const char* name,
- ValueType&& value) {
- if (!rules.one_line && rules.level)
- out << std::string(rules.level, '\t');
-
- out << name << " = ";
- PrintValue(out, rules, std::forward<ValueType>(value));
- out << ";" << (rules.one_line ? " " : "\n");
-}
-} // namespace
-
-// PBXObjectClass -------------------------------------------------------------
-
-const char* ToString(PBXObjectClass cls) {
- switch (cls) {
- case PBXAggregateTargetClass:
- return "PBXAggregateTarget";
- case PBXBuildFileClass:
- return "PBXBuildFile";
- case PBXContainerItemProxyClass:
- return "PBXContainerItemProxy";
- case PBXFileReferenceClass:
- return "PBXFileReference";
- case PBXFrameworksBuildPhaseClass:
- return "PBXFrameworksBuildPhase";
- case PBXGroupClass:
- return "PBXGroup";
- case PBXNativeTargetClass:
- return "PBXNativeTarget";
- case PBXProjectClass:
- return "PBXProject";
- case PBXShellScriptBuildPhaseClass:
- return "PBXShellScriptBuildPhase";
- case PBXSourcesBuildPhaseClass:
- return "PBXSourcesBuildPhase";
- case PBXTargetDependencyClass:
- return "PBXTargetDependency";
- case XCBuildConfigurationClass:
- return "XCBuildConfiguration";
- case XCConfigurationListClass:
- return "XCConfigurationList";
- }
- NOTREACHED();
- return nullptr;
-}
-
-// PBXObjectVisitor -----------------------------------------------------------
-
-PBXObjectVisitor::PBXObjectVisitor() = default;
-
-PBXObjectVisitor::~PBXObjectVisitor() = default;
-
-// PBXObject ------------------------------------------------------------------
-
-PBXObject::PBXObject() = default;
-
-PBXObject::~PBXObject() = default;
-
-void PBXObject::SetId(const std::string& id) {
- DCHECK(id_.empty());
- DCHECK(!id.empty());
- id_.assign(id);
-}
-
-std::string PBXObject::Reference() const {
- std::string comment = Comment();
- if (comment.empty())
- return id_;
-
- return id_ + " /* " + comment + " */";
-}
-
-std::string PBXObject::Comment() const {
- return Name();
-}
-
-void PBXObject::Visit(PBXObjectVisitor& visitor) {
- visitor.Visit(this);
-}
-
-// PBXBuildPhase --------------------------------------------------------------
-
-PBXBuildPhase::PBXBuildPhase() = default;
-
-PBXBuildPhase::~PBXBuildPhase() = default;
-
-// PBXTarget ------------------------------------------------------------------
-
-PBXTarget::PBXTarget(const std::string& name,
- const std::string& shell_script,
- const std::string& config_name,
- const PBXAttributes& attributes)
- : configurations_(new XCConfigurationList(config_name, attributes, this)),
- name_(name) {
- if (!shell_script.empty()) {
- build_phases_.push_back(
- std::make_unique<PBXShellScriptBuildPhase>(name, shell_script));
- }
-}
-
-PBXTarget::~PBXTarget() = default;
-
-void PBXTarget::AddDependency(std::unique_ptr<PBXTargetDependency> dependency) {
- DCHECK(dependency);
- dependencies_.push_back(std::move(dependency));
-}
-
-std::string PBXTarget::Name() const {
- return name_;
-}
-
-void PBXTarget::Visit(PBXObjectVisitor& visitor) {
- PBXObject::Visit(visitor);
- configurations_->Visit(visitor);
- for (const auto& dependency : dependencies_)
- dependency->Visit(visitor);
- for (const auto& build_phase : build_phases_)
- build_phase->Visit(visitor);
-}
-
-// PBXAggregateTarget ---------------------------------------------------------
-
-PBXAggregateTarget::PBXAggregateTarget(const std::string& name,
- const std::string& shell_script,
- const std::string& config_name,
- const PBXAttributes& attributes)
- : PBXTarget(name, shell_script, config_name, attributes) {}
-
-PBXAggregateTarget::~PBXAggregateTarget() = default;
-
-PBXObjectClass PBXAggregateTarget::Class() const {
- return PBXAggregateTargetClass;
-}
-
-void PBXAggregateTarget::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildConfigurationList", configurations_);
- PrintProperty(out, rules, "buildPhases", build_phases_);
- PrintProperty(out, rules, "dependencies", EmptyPBXObjectVector());
- PrintProperty(out, rules, "name", name_);
- PrintProperty(out, rules, "productName", name_);
- out << indent_str << "};\n";
-}
-
-// PBXBuildFile ---------------------------------------------------------------
-
-PBXBuildFile::PBXBuildFile(const PBXFileReference* file_reference,
- const PBXSourcesBuildPhase* build_phase,
- const CompilerFlags compiler_flag)
- : file_reference_(file_reference),
- build_phase_(build_phase),
- compiler_flag_(compiler_flag) {
- DCHECK(file_reference_);
- DCHECK(build_phase_);
-}
-
-PBXBuildFile::~PBXBuildFile() = default;
-
-PBXObjectClass PBXBuildFile::Class() const {
- return PBXBuildFileClass;
-}
-
-std::string PBXBuildFile::Name() const {
- return file_reference_->Name() + " in " + build_phase_->Name();
-}
-
-void PBXBuildFile::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {true, 0};
- out << indent_str << Reference() << " = {";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "fileRef", file_reference_);
- if (compiler_flag_ == CompilerFlags::HELP) {
- std::map<std::string, std::string> settings = {
- {"COMPILER_FLAGS", "--help"},
- };
- PrintProperty(out, rules, "settings", settings);
- }
- out << "};\n";
-}
-
-// PBXContainerItemProxy ------------------------------------------------------
-PBXContainerItemProxy::PBXContainerItemProxy(const PBXProject* project,
- const PBXTarget* target)
- : project_(project), target_(target) {}
-
-PBXContainerItemProxy::~PBXContainerItemProxy() = default;
-
-PBXObjectClass PBXContainerItemProxy::Class() const {
- return PBXContainerItemProxyClass;
-}
-
-void PBXContainerItemProxy::Visit(PBXObjectVisitor& visitor) {
- PBXObject::Visit(visitor);
-}
-
-std::string PBXContainerItemProxy::Name() const {
- return "PBXContainerItemProxy";
-}
-
-void PBXContainerItemProxy::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {true, 0};
- out << indent_str << Reference() << " = {";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "containerPortal", project_);
- PrintProperty(out, rules, "proxyType", 1u);
- PrintProperty(out, rules, "remoteGlobalIDString", target_);
- PrintProperty(out, rules, "remoteInfo", target_->Name());
- out << indent_str << "};\n";
-}
-
-// PBXFileReference -----------------------------------------------------------
-
-PBXFileReference::PBXFileReference(const std::string& name,
- const std::string& path,
- const std::string& type)
- : name_(name), path_(path), type_(type) {}
-
-PBXFileReference::~PBXFileReference() = default;
-
-PBXObjectClass PBXFileReference::Class() const {
- return PBXFileReferenceClass;
-}
-
-std::string PBXFileReference::Name() const {
- return name_;
-}
-
-void PBXFileReference::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {true, 0};
- out << indent_str << Reference() << " = {";
- PrintProperty(out, rules, "isa", ToString(Class()));
-
- if (!type_.empty()) {
- PrintProperty(out, rules, "explicitFileType", type_);
- PrintProperty(out, rules, "includeInIndex", 0u);
- } else {
- base::StringPiece ext = FindExtension(&name_);
- if (HasExplicitFileType(ext))
- PrintProperty(out, rules, "explicitFileType", GetSourceType(ext));
- else
- PrintProperty(out, rules, "lastKnownFileType", GetSourceType(ext));
- }
-
- if (!name_.empty())
- PrintProperty(out, rules, "name", name_);
-
- DCHECK(!path_.empty());
- PrintProperty(out, rules, "path", path_);
- PrintProperty(out, rules, "sourceTree",
- type_.empty() ? "<group>" : "BUILT_PRODUCTS_DIR");
- out << "};\n";
-}
-
-// PBXFrameworksBuildPhase ----------------------------------------------------
-
-PBXFrameworksBuildPhase::PBXFrameworksBuildPhase() = default;
-
-PBXFrameworksBuildPhase::~PBXFrameworksBuildPhase() = default;
-
-PBXObjectClass PBXFrameworksBuildPhase::Class() const {
- return PBXFrameworksBuildPhaseClass;
-}
-
-std::string PBXFrameworksBuildPhase::Name() const {
- return "Frameworks";
-}
-
-void PBXFrameworksBuildPhase::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
- PrintProperty(out, rules, "files", EmptyPBXObjectVector());
- PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
- out << indent_str << "};\n";
-}
-
-// PBXGroup -------------------------------------------------------------------
-
-PBXGroup::PBXGroup(const std::string& path, const std::string& name)
- : name_(name), path_(path) {}
-
-PBXGroup::~PBXGroup() = default;
-
-PBXObject* PBXGroup::AddChild(std::unique_ptr<PBXObject> child) {
- DCHECK(child);
- children_.push_back(std::move(child));
- return children_.back().get();
-}
-
-PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path,
- const std::string& source_path) {
- DCHECK(!navigator_path.empty());
- DCHECK(!source_path.empty());
- std::string::size_type sep = navigator_path.find("/");
- if (sep == std::string::npos) {
- // Prevent same file reference being created and added multiple times.
- for (const auto& child : children_) {
- if (child->Class() != PBXFileReferenceClass)
- continue;
-
- PBXFileReference* child_as_file_reference =
- static_cast<PBXFileReference*>(child.get());
- if (child_as_file_reference->Name() == navigator_path &&
- child_as_file_reference->path() == source_path) {
- return child_as_file_reference;
- }
- }
-
- children_.push_back(std::make_unique<PBXFileReference>(
- navigator_path, source_path, std::string()));
- return static_cast<PBXFileReference*>(children_.back().get());
- }
-
- PBXGroup* group = nullptr;
- base::StringPiece component(navigator_path.data(), sep);
- for (const auto& child : children_) {
- if (child->Class() != PBXGroupClass)
- continue;
-
- PBXGroup* child_as_group = static_cast<PBXGroup*>(child.get());
- if (child_as_group->name_ == component) {
- group = child_as_group;
- break;
- }
- }
-
- if (!group) {
- children_.push_back(std::make_unique<PBXGroup>(component.as_string(),
- component.as_string()));
- group = static_cast<PBXGroup*>(children_.back().get());
- }
-
- DCHECK(group);
- DCHECK(group->name_ == component);
- return group->AddSourceFile(navigator_path.substr(sep + 1), source_path);
-}
-
-PBXObjectClass PBXGroup::Class() const {
- return PBXGroupClass;
-}
-
-std::string PBXGroup::Name() const {
- if (!name_.empty())
- return name_;
- if (!path_.empty())
- return path_;
- return std::string();
-}
-
-void PBXGroup::Visit(PBXObjectVisitor& visitor) {
- PBXObject::Visit(visitor);
- for (const auto& child : children_) {
- child->Visit(visitor);
- }
-}
-
-void PBXGroup::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "children", children_);
- if (!name_.empty())
- PrintProperty(out, rules, "name", name_);
- if (is_source_ && !path_.empty())
- PrintProperty(out, rules, "path", path_);
- PrintProperty(out, rules, "sourceTree", "<group>");
- out << indent_str << "};\n";
-}
-
-// PBXNativeTarget ------------------------------------------------------------
-
-PBXNativeTarget::PBXNativeTarget(const std::string& name,
- const std::string& shell_script,
- const std::string& config_name,
- const PBXAttributes& attributes,
- const std::string& product_type,
- const std::string& product_name,
- const PBXFileReference* product_reference)
- : PBXTarget(name, shell_script, config_name, attributes),
- product_reference_(product_reference),
- product_type_(product_type),
- product_name_(product_name) {
- DCHECK(product_reference_);
- build_phases_.push_back(std::make_unique<PBXSourcesBuildPhase>());
- source_build_phase_ =
- static_cast<PBXSourcesBuildPhase*>(build_phases_.back().get());
-
- build_phases_.push_back(std::make_unique<PBXFrameworksBuildPhase>());
-}
-
-PBXNativeTarget::~PBXNativeTarget() = default;
-
-void PBXNativeTarget::AddFileForIndexing(const PBXFileReference* file_reference,
- const CompilerFlags compiler_flag) {
- DCHECK(file_reference);
- source_build_phase_->AddBuildFile(std::make_unique<PBXBuildFile>(
- file_reference, source_build_phase_, compiler_flag));
-}
-
-PBXObjectClass PBXNativeTarget::Class() const {
- return PBXNativeTargetClass;
-}
-
-void PBXNativeTarget::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildConfigurationList", configurations_);
- PrintProperty(out, rules, "buildPhases", build_phases_);
- PrintProperty(out, rules, "buildRules", EmptyPBXObjectVector());
- PrintProperty(out, rules, "dependencies", dependencies_);
- PrintProperty(out, rules, "name", name_);
- PrintProperty(out, rules, "productName", product_name_);
- PrintProperty(out, rules, "productReference", product_reference_);
- PrintProperty(out, rules, "productType", product_type_);
- out << indent_str << "};\n";
-}
-
-// PBXProject -----------------------------------------------------------------
-
-PBXProject::PBXProject(const std::string& name,
- const std::string& config_name,
- const std::string& source_path,
- const PBXAttributes& attributes)
- : name_(name), config_name_(config_name), target_for_indexing_(nullptr) {
- attributes_["BuildIndependentTargetsInParallel"] = "YES";
-
- main_group_.reset(new PBXGroup);
- sources_ = static_cast<PBXGroup*>(
- main_group_->AddChild(std::make_unique<PBXGroup>(source_path, "Source")));
- sources_->set_is_source(true);
- products_ = static_cast<PBXGroup*>(main_group_->AddChild(
- std::make_unique<PBXGroup>(std::string(), "Product")));
- main_group_->AddChild(std::make_unique<PBXGroup>(std::string(), "Build"));
-
- configurations_.reset(new XCConfigurationList(config_name, attributes, this));
-}
-
-PBXProject::~PBXProject() = default;
-
-void PBXProject::AddSourceFileToIndexingTarget(
- const std::string& navigator_path,
- const std::string& source_path,
- const CompilerFlags compiler_flag) {
- if (!target_for_indexing_) {
- AddIndexingTarget();
- }
- AddSourceFile(navigator_path, source_path, compiler_flag,
- target_for_indexing_);
-}
-
-void PBXProject::AddSourceFile(const std::string& navigator_path,
- const std::string& source_path,
- const CompilerFlags compiler_flag,
- PBXNativeTarget* target) {
- PBXFileReference* file_reference =
- sources_->AddSourceFile(navigator_path, source_path);
- base::StringPiece ext = FindExtension(&source_path);
- if (!IsSourceFileForIndexing(ext))
- return;
-
- DCHECK(target);
- target->AddFileForIndexing(file_reference, compiler_flag);
-}
-
-void PBXProject::AddAggregateTarget(const std::string& name,
- const std::string& shell_script) {
- PBXAttributes attributes;
- attributes["CODE_SIGNING_REQUIRED"] = "NO";
- attributes["CONFIGURATION_BUILD_DIR"] = ".";
- attributes["PRODUCT_NAME"] = name;
-
- targets_.push_back(std::make_unique<PBXAggregateTarget>(
- name, shell_script, config_name_, attributes));
-}
-
-void PBXProject::AddIndexingTarget() {
- DCHECK(!target_for_indexing_);
- PBXAttributes attributes;
- attributes["EXECUTABLE_PREFIX"] = "";
- attributes["HEADER_SEARCH_PATHS"] = sources_->path();
- attributes["PRODUCT_NAME"] = "sources";
-
- PBXFileReference* product_reference = static_cast<PBXFileReference*>(
- products_->AddChild(std::make_unique<PBXFileReference>(
- std::string(), "sources", "compiled.mach-o.executable")));
-
- const char product_type[] = "com.apple.product-type.tool";
- targets_.push_back(std::make_unique<PBXNativeTarget>(
- "sources", std::string(), config_name_, attributes, product_type,
- "sources", product_reference));
- target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get());
-}
-
-PBXNativeTarget* PBXProject::AddNativeTarget(
- const std::string& name,
- const std::string& type,
- const std::string& output_name,
- const std::string& output_type,
- const std::string& shell_script,
- const PBXAttributes& extra_attributes) {
- base::StringPiece ext = FindExtension(&output_name);
- PBXFileReference* product = static_cast<PBXFileReference*>(
- products_->AddChild(std::make_unique<PBXFileReference>(
- std::string(), output_name,
- type.empty() ? GetSourceType(ext) : type)));
-
- // Per Xcode build settings documentation: Product Name (PRODUCT_NAME) should
- // the basename of the product generated by the target.
- // Therefore, take the basename of output name without file extension as the
- // "PRODUCT_NAME".
- size_t basename_offset = FindFilenameOffset(output_name);
- std::string output_basename = basename_offset != std::string::npos
- ? output_name.substr(basename_offset)
- : output_name;
- size_t ext_offset = FindExtensionOffset(output_basename);
- std::string product_name = ext_offset != std::string::npos
- ? output_basename.substr(0, ext_offset - 1)
- : output_basename;
-
- PBXAttributes attributes = extra_attributes;
- attributes["CODE_SIGNING_REQUIRED"] = "NO";
- attributes["CONFIGURATION_BUILD_DIR"] = ".";
- attributes["PRODUCT_NAME"] = product_name;
-
- targets_.push_back(std::make_unique<PBXNativeTarget>(
- name, shell_script, config_name_, attributes, output_type, product_name,
- product));
- return static_cast<PBXNativeTarget*>(targets_.back().get());
-}
-
-void PBXProject::SetProjectDirPath(const std::string& project_dir_path) {
- DCHECK(!project_dir_path.empty());
- project_dir_path_.assign(project_dir_path);
-}
-
-void PBXProject::SetProjectRoot(const std::string& project_root) {
- DCHECK(!project_root.empty());
- project_root_.assign(project_root);
-}
-
-void PBXProject::AddTarget(std::unique_ptr<PBXTarget> target) {
- DCHECK(target);
- targets_.push_back(std::move(target));
-}
-
-PBXObjectClass PBXProject::Class() const {
- return PBXProjectClass;
-}
-
-std::string PBXProject::Name() const {
- return name_;
-}
-
-std::string PBXProject::Comment() const {
- return "Project object";
-}
-
-void PBXProject::Visit(PBXObjectVisitor& visitor) {
- PBXObject::Visit(visitor);
- configurations_->Visit(visitor);
- main_group_->Visit(visitor);
- for (const auto& target : targets_) {
- target->Visit(visitor);
- }
-}
-
-void PBXProject::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "attributes", attributes_);
- PrintProperty(out, rules, "buildConfigurationList", configurations_);
- PrintProperty(out, rules, "compatibilityVersion", "Xcode 3.2");
- PrintProperty(out, rules, "developmentRegion", "English");
- PrintProperty(out, rules, "hasScannedForEncodings", 1u);
- PrintProperty(out, rules, "knownRegions", std::vector<std::string>({"en"}));
- PrintProperty(out, rules, "mainGroup", main_group_);
- PrintProperty(out, rules, "projectDirPath", project_dir_path_);
- PrintProperty(out, rules, "projectRoot", project_root_);
- PrintProperty(out, rules, "targets", targets_);
- out << indent_str << "};\n";
-}
-
-// PBXShellScriptBuildPhase ---------------------------------------------------
-
-PBXShellScriptBuildPhase::PBXShellScriptBuildPhase(
- const std::string& name,
- const std::string& shell_script)
- : name_("Action \"Compile and copy " + name + " via ninja\""),
- shell_script_(shell_script) {}
-
-PBXShellScriptBuildPhase::~PBXShellScriptBuildPhase() = default;
-
-PBXObjectClass PBXShellScriptBuildPhase::Class() const {
- return PBXShellScriptBuildPhaseClass;
-}
-
-std::string PBXShellScriptBuildPhase::Name() const {
- return name_;
-}
-
-void PBXShellScriptBuildPhase::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
- PrintProperty(out, rules, "files", EmptyPBXObjectVector());
- PrintProperty(out, rules, "inputPaths", EmptyPBXObjectVector());
- PrintProperty(out, rules, "name", name_);
- PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector());
- PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
- PrintProperty(out, rules, "shellPath", "/bin/sh");
- PrintProperty(out, rules, "shellScript", shell_script_);
- PrintProperty(out, rules, "showEnvVarsInLog", 0u);
- out << indent_str << "};\n";
-}
-
-// PBXSourcesBuildPhase -------------------------------------------------------
-
-PBXSourcesBuildPhase::PBXSourcesBuildPhase() = default;
-
-PBXSourcesBuildPhase::~PBXSourcesBuildPhase() = default;
-
-void PBXSourcesBuildPhase::AddBuildFile(
- std::unique_ptr<PBXBuildFile> build_file) {
- files_.push_back(std::move(build_file));
-}
-
-PBXObjectClass PBXSourcesBuildPhase::Class() const {
- return PBXSourcesBuildPhaseClass;
-}
-
-std::string PBXSourcesBuildPhase::Name() const {
- return "Sources";
-}
-
-void PBXSourcesBuildPhase::Visit(PBXObjectVisitor& visitor) {
- PBXBuildPhase::Visit(visitor);
- for (const auto& file : files_) {
- file->Visit(visitor);
- }
-}
-
-void PBXSourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
- PrintProperty(out, rules, "files", files_);
- PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
- out << indent_str << "};\n";
-}
-
-PBXTargetDependency::PBXTargetDependency(
- const PBXTarget* target,
- std::unique_ptr<PBXContainerItemProxy> container_item_proxy)
- : target_(target), container_item_proxy_(std::move(container_item_proxy)) {}
-
-PBXTargetDependency::~PBXTargetDependency() = default;
-
-PBXObjectClass PBXTargetDependency::Class() const {
- return PBXTargetDependencyClass;
-}
-std::string PBXTargetDependency::Name() const {
- return "PBXTargetDependency";
-}
-void PBXTargetDependency::Visit(PBXObjectVisitor& visitor) {
- PBXObject::Visit(visitor);
- container_item_proxy_->Visit(visitor);
-}
-void PBXTargetDependency::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "target", target_);
- PrintProperty(out, rules, "targetProxy", container_item_proxy_);
- out << indent_str << "};\n";
-}
-
-// XCBuildConfiguration -------------------------------------------------------
-
-XCBuildConfiguration::XCBuildConfiguration(const std::string& name,
- const PBXAttributes& attributes)
- : attributes_(attributes), name_(name) {}
-
-XCBuildConfiguration::~XCBuildConfiguration() = default;
-
-PBXObjectClass XCBuildConfiguration::Class() const {
- return XCBuildConfigurationClass;
-}
-
-std::string XCBuildConfiguration::Name() const {
- return name_;
-}
-
-void XCBuildConfiguration::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildSettings", attributes_);
- PrintProperty(out, rules, "name", name_);
- out << indent_str << "};\n";
-}
-
-// XCConfigurationList --------------------------------------------------------
-
-XCConfigurationList::XCConfigurationList(const std::string& name,
- const PBXAttributes& attributes,
- const PBXObject* owner_reference)
- : owner_reference_(owner_reference) {
- DCHECK(owner_reference_);
- configurations_.push_back(
- std::make_unique<XCBuildConfiguration>(name, attributes));
-}
-
-XCConfigurationList::~XCConfigurationList() = default;
-
-PBXObjectClass XCConfigurationList::Class() const {
- return XCConfigurationListClass;
-}
-
-std::string XCConfigurationList::Name() const {
- std::stringstream buffer;
- buffer << "Build configuration list for "
- << ToString(owner_reference_->Class()) << " \""
- << owner_reference_->Name() << "\"";
- return buffer.str();
-}
-
-void XCConfigurationList::Visit(PBXObjectVisitor& visitor) {
- PBXObject::Visit(visitor);
- for (const auto& configuration : configurations_) {
- configuration->Visit(visitor);
- }
-}
-
-void XCConfigurationList::Print(std::ostream& out, unsigned indent) const {
- const std::string indent_str(indent, '\t');
- const IndentRules rules = {false, indent + 1};
- out << indent_str << Reference() << " = {\n";
- PrintProperty(out, rules, "isa", ToString(Class()));
- PrintProperty(out, rules, "buildConfigurations", configurations_);
- PrintProperty(out, rules, "defaultConfigurationIsVisible", 1u);
- PrintProperty(out, rules, "defaultConfigurationName",
- configurations_[0]->Name());
- out << indent_str << "};\n";
-}
diff --git a/gn/tools/gn/xcode_object.h b/gn/tools/gn/xcode_object.h
deleted file mode 100644
index 715090b652b..00000000000
--- a/gn/tools/gn/xcode_object.h
+++ /dev/null
@@ -1,465 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_XCODE_OBJECT_H_
-#define TOOLS_GN_XCODE_OBJECT_H_
-
-#include <iosfwd>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-
-// Helper classes to generate Xcode project files.
-//
-// This code is based on gyp xcodeproj_file.py generator. It does not support
-// all features of Xcode project but instead just enough to implement a hybrid
-// mode where Xcode uses external scripts to perform the compilation steps.
-//
-// See
-// https://chromium.googlesource.com/external/gyp/+/master/pylib/gyp/xcodeproj_file.py
-// for more information on Xcode project file format.
-
-enum class CompilerFlags {
- NONE,
- HELP,
-};
-
-// PBXObjectClass -------------------------------------------------------------
-
-enum PBXObjectClass {
- // Those values needs to stay sorted in alphabetic order.
- PBXAggregateTargetClass,
- PBXBuildFileClass,
- PBXContainerItemProxyClass,
- PBXFileReferenceClass,
- PBXFrameworksBuildPhaseClass,
- PBXGroupClass,
- PBXNativeTargetClass,
- PBXProjectClass,
- PBXShellScriptBuildPhaseClass,
- PBXSourcesBuildPhaseClass,
- PBXTargetDependencyClass,
- XCBuildConfigurationClass,
- XCConfigurationListClass,
-};
-
-const char* ToString(PBXObjectClass cls);
-
-// Forward-declarations -------------------------------------------------------
-
-class PBXAggregateTarget;
-class PBXBuildFile;
-class PBXBuildPhase;
-class PBXContainerItemProxy;
-class PBXFileReference;
-class PBXFrameworksBuildPhase;
-class PBXGroup;
-class PBXNativeTarget;
-class PBXObject;
-class PBXProject;
-class PBXShellScriptBuildPhase;
-class PBXSourcesBuildPhase;
-class PBXTarget;
-class PBXTargetDependency;
-class XCBuildConfiguration;
-class XCConfigurationList;
-
-using PBXAttributes = std::map<std::string, std::string>;
-
-// PBXObjectVisitor -----------------------------------------------------------
-
-class PBXObjectVisitor {
- public:
- PBXObjectVisitor();
- virtual ~PBXObjectVisitor();
- virtual void Visit(PBXObject* object) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PBXObjectVisitor);
-};
-
-// PBXObject ------------------------------------------------------------------
-
-class PBXObject {
- public:
- PBXObject();
- virtual ~PBXObject();
-
- const std::string id() const { return id_; }
- void SetId(const std::string& id);
-
- std::string Reference() const;
-
- virtual PBXObjectClass Class() const = 0;
- virtual std::string Name() const = 0;
- virtual std::string Comment() const;
- virtual void Visit(PBXObjectVisitor& visitor);
- virtual void Print(std::ostream& out, unsigned indent) const = 0;
-
- private:
- std::string id_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXObject);
-};
-
-// PBXBuildPhase --------------------------------------------------------------
-
-class PBXBuildPhase : public PBXObject {
- public:
- PBXBuildPhase();
- ~PBXBuildPhase() override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PBXBuildPhase);
-};
-
-// PBXTarget ------------------------------------------------------------------
-
-class PBXTarget : public PBXObject {
- public:
- PBXTarget(const std::string& name,
- const std::string& shell_script,
- const std::string& config_name,
- const PBXAttributes& attributes);
- ~PBXTarget() override;
-
- void AddDependency(std::unique_ptr<PBXTargetDependency> dependency);
-
- // PBXObject implementation.
- std::string Name() const override;
- void Visit(PBXObjectVisitor& visitor) override;
-
- protected:
- std::unique_ptr<XCConfigurationList> configurations_;
- std::vector<std::unique_ptr<PBXBuildPhase>> build_phases_;
- std::vector<std::unique_ptr<PBXTargetDependency>> dependencies_;
- PBXSourcesBuildPhase* source_build_phase_;
- std::string name_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PBXTarget);
-};
-
-// PBXAggregateTarget ---------------------------------------------------------
-
-class PBXAggregateTarget : public PBXTarget {
- public:
- PBXAggregateTarget(const std::string& name,
- const std::string& shell_script,
- const std::string& config_name,
- const PBXAttributes& attributes);
- ~PBXAggregateTarget() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PBXAggregateTarget);
-};
-
-// PBXBuildFile ---------------------------------------------------------------
-
-class PBXBuildFile : public PBXObject {
- public:
- PBXBuildFile(const PBXFileReference* file_reference,
- const PBXSourcesBuildPhase* build_phase,
- const CompilerFlags compiler_flag);
- ~PBXBuildFile() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- const PBXFileReference* file_reference_;
- const PBXSourcesBuildPhase* build_phase_;
- const CompilerFlags compiler_flag_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXBuildFile);
-};
-
-// PBXContainerItemProxy ------------------------------------------------------
-class PBXContainerItemProxy : public PBXObject {
- public:
- PBXContainerItemProxy(const PBXProject* project, const PBXTarget* target);
- ~PBXContainerItemProxy() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Visit(PBXObjectVisitor& visitor) override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- const PBXProject* project_;
- const PBXTarget* target_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXContainerItemProxy);
-};
-
-// PBXFileReference -----------------------------------------------------------
-
-class PBXFileReference : public PBXObject {
- public:
- PBXFileReference(const std::string& name,
- const std::string& path,
- const std::string& type);
- ~PBXFileReference() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- const std::string& path() const { return path_; }
-
- private:
- std::string name_;
- std::string path_;
- std::string type_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXFileReference);
-};
-
-// PBXFrameworksBuildPhase ----------------------------------------------------
-
-class PBXFrameworksBuildPhase : public PBXBuildPhase {
- public:
- PBXFrameworksBuildPhase();
- ~PBXFrameworksBuildPhase() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PBXFrameworksBuildPhase);
-};
-
-// PBXGroup -------------------------------------------------------------------
-
-class PBXGroup : public PBXObject {
- public:
- explicit PBXGroup(const std::string& path = std::string(),
- const std::string& name = std::string());
- ~PBXGroup() override;
-
- const std::string& path() const { return path_; }
-
- PBXObject* AddChild(std::unique_ptr<PBXObject> child);
- PBXFileReference* AddSourceFile(const std::string& navigator_path,
- const std::string& source_path);
- bool is_source() { return is_source_; }
- void set_is_source(const bool is_source) { is_source_ = is_source; }
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Visit(PBXObjectVisitor& visitor) override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- std::vector<std::unique_ptr<PBXObject>> children_;
- std::string name_;
- std::string path_;
- bool is_source_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(PBXGroup);
-};
-
-// PBXNativeTarget ------------------------------------------------------------
-
-class PBXNativeTarget : public PBXTarget {
- public:
- PBXNativeTarget(const std::string& name,
- const std::string& shell_script,
- const std::string& config_name,
- const PBXAttributes& attributes,
- const std::string& product_type,
- const std::string& product_name,
- const PBXFileReference* product_reference);
- ~PBXNativeTarget() override;
-
- void AddFileForIndexing(const PBXFileReference* file_reference,
- const CompilerFlags compiler_flag);
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- const PBXFileReference* product_reference_;
- std::string product_type_;
- std::string product_name_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXNativeTarget);
-};
-
-// PBXProject -----------------------------------------------------------------
-
-class PBXProject : public PBXObject {
- public:
- PBXProject(const std::string& name,
- const std::string& config_name,
- const std::string& source_path,
- const PBXAttributes& attributes);
- ~PBXProject() override;
-
- void AddSourceFileToIndexingTarget(const std::string& navigator_path,
- const std::string& source_path,
- const CompilerFlags compiler_flag);
- void AddSourceFile(const std::string& navigator_path,
- const std::string& source_path,
- const CompilerFlags compiler_flag,
- PBXNativeTarget* target);
- void AddAggregateTarget(const std::string& name,
- const std::string& shell_script);
- void AddIndexingTarget();
- PBXNativeTarget* AddNativeTarget(
- const std::string& name,
- const std::string& type,
- const std::string& output_name,
- const std::string& output_type,
- const std::string& shell_script,
- const PBXAttributes& extra_attributes = PBXAttributes());
-
- void SetProjectDirPath(const std::string& project_dir_path);
- void SetProjectRoot(const std::string& project_root);
- void AddTarget(std::unique_ptr<PBXTarget> target);
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- std::string Comment() const override;
- void Visit(PBXObjectVisitor& visitor) override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- PBXAttributes attributes_;
- std::unique_ptr<XCConfigurationList> configurations_;
- std::unique_ptr<PBXGroup> main_group_;
- std::string project_dir_path_;
- std::string project_root_;
- std::vector<std::unique_ptr<PBXTarget>> targets_;
- std::string name_;
- std::string config_name_;
-
- PBXGroup* sources_;
- PBXGroup* products_;
- PBXNativeTarget* target_for_indexing_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXProject);
-};
-
-// PBXShellScriptBuildPhase ---------------------------------------------------
-
-class PBXShellScriptBuildPhase : public PBXBuildPhase {
- public:
- PBXShellScriptBuildPhase(const std::string& name,
- const std::string& shell_script);
- ~PBXShellScriptBuildPhase() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- std::string name_;
- std::string shell_script_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXShellScriptBuildPhase);
-};
-
-// PBXSourcesBuildPhase -------------------------------------------------------
-
-class PBXSourcesBuildPhase : public PBXBuildPhase {
- public:
- PBXSourcesBuildPhase();
- ~PBXSourcesBuildPhase() override;
-
- void AddBuildFile(std::unique_ptr<PBXBuildFile> build_file);
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Visit(PBXObjectVisitor& visitor) override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- std::vector<std::unique_ptr<PBXBuildFile>> files_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXSourcesBuildPhase);
-};
-
-// PBXTargetDependency -----------------------------------------------------
-class PBXTargetDependency : public PBXObject {
- public:
- PBXTargetDependency(
- const PBXTarget* target,
- std::unique_ptr<PBXContainerItemProxy> container_item_proxy);
- ~PBXTargetDependency() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Visit(PBXObjectVisitor& visitor) override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- const PBXTarget* target_;
- std::unique_ptr<PBXContainerItemProxy> container_item_proxy_;
-
- DISALLOW_COPY_AND_ASSIGN(PBXTargetDependency);
-};
-
-// XCBuildConfiguration -------------------------------------------------------
-
-class XCBuildConfiguration : public PBXObject {
- public:
- XCBuildConfiguration(const std::string& name,
- const PBXAttributes& attributes);
- ~XCBuildConfiguration() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- PBXAttributes attributes_;
- std::string name_;
-
- DISALLOW_COPY_AND_ASSIGN(XCBuildConfiguration);
-};
-
-// XCConfigurationList --------------------------------------------------------
-
-class XCConfigurationList : public PBXObject {
- public:
- XCConfigurationList(const std::string& name,
- const PBXAttributes& attributes,
- const PBXObject* owner_reference);
- ~XCConfigurationList() override;
-
- // PBXObject implementation.
- PBXObjectClass Class() const override;
- std::string Name() const override;
- void Visit(PBXObjectVisitor& visitor) override;
- void Print(std::ostream& out, unsigned indent) const override;
-
- private:
- std::vector<std::unique_ptr<XCBuildConfiguration>> configurations_;
- const PBXObject* owner_reference_;
-
- DISALLOW_COPY_AND_ASSIGN(XCConfigurationList);
-};
-
-#endif // TOOLS_GN_XCODE_OBJECT_H_
diff --git a/gn/tools/gn/xcode_object_unittest.cc b/gn/tools/gn/xcode_object_unittest.cc
deleted file mode 100644
index 70ac2c76706..00000000000
--- a/gn/tools/gn/xcode_object_unittest.cc
+++ /dev/null
@@ -1,435 +0,0 @@
-// Copyright 2017 The Chromium 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 "tools/gn/xcode_object.h"
-
-#include "util/test/test.h"
-
-namespace {
-
-// Instantiate a PBXSourcesBuildPhase object.
-std::unique_ptr<PBXSourcesBuildPhase> GetPBXSourcesBuildPhaseObject() {
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase(
- new PBXSourcesBuildPhase());
- return pbx_sources_build_phase;
-}
-
-// Instantiate a PBXFrameworksBuildPhase object.
-std::unique_ptr<PBXFrameworksBuildPhase> GetPBXFrameworksBuildPhaseObject() {
- std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase(
- new PBXFrameworksBuildPhase());
- return pbx_frameworks_build_phase;
-}
-
-// Instantiate a PBXShellScriptBuildPhase object with arbitrary names.
-std::unique_ptr<PBXShellScriptBuildPhase> GetPBXShellScriptBuildPhaseObject() {
- std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase(
- new PBXShellScriptBuildPhase("name", "shell_script"));
- return pbx_shell_script_build_phase;
-}
-
-// Instantiate a PBXGroup object with arbitrary names.
-std::unique_ptr<PBXGroup> GetPBXGroupObject() {
- std::unique_ptr<PBXGroup> pbx_group(new PBXGroup("/dir1/dir2", "group"));
- return pbx_group;
-}
-
-// Instantiate a PBXProject object with arbitrary names.
-std::unique_ptr<PBXProject> GetPBXProjectObject() {
- std::unique_ptr<PBXProject> pbx_project(
- new PBXProject("project", "config", "out/build", PBXAttributes()));
- return pbx_project;
-}
-
-// Instantiate a PBXFileReference object with arbitrary names.
-std::unique_ptr<PBXFileReference> GetPBXFileReferenceObject() {
- std::unique_ptr<PBXFileReference> pbx_file_reference(new PBXFileReference(
- "product.app", "product.app", "wrapper.application"));
- return pbx_file_reference;
-}
-
-// Instantiate a PBXBuildFile object.
-std::unique_ptr<PBXBuildFile> GetPBXBuildFileObject(
- const PBXFileReference* file_reference,
- const PBXSourcesBuildPhase* build_phase) {
- std::unique_ptr<PBXBuildFile> pbx_build_file(
- new PBXBuildFile(file_reference, build_phase, CompilerFlags::NONE));
- return pbx_build_file;
-}
-
-// Instantiate a PBXAggregateTarget object with arbitrary names.
-std::unique_ptr<PBXAggregateTarget> GetPBXAggregateTargetObject() {
- std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target(
- new PBXAggregateTarget("target_name", "shell_script", "config_name",
- PBXAttributes()));
- return pbx_aggregate_target;
-}
-
-// Instantiate a PBXNativeTarget object with arbitrary names.
-std::unique_ptr<PBXNativeTarget> GetPBXNativeTargetObject(
- const PBXFileReference* product_reference) {
- std::unique_ptr<PBXNativeTarget> pbx_native_target(new PBXNativeTarget(
- "target_name", "ninja gn_unittests", "config_name", PBXAttributes(),
- "com.apple.product-type.application", "product_name", product_reference));
- return pbx_native_target;
-}
-
-// Instantiate a PBXContainerItemProxy object.
-std::unique_ptr<PBXContainerItemProxy> GetPBXContainerItemProxyObject(
- const PBXProject* project,
- const PBXTarget* target) {
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy(
- new PBXContainerItemProxy(project, target));
- return pbx_container_item_proxy;
-}
-
-// Instantiate a PBXTargetDependency object.
-std::unique_ptr<PBXTargetDependency> GetPBXTargetDependencyObject(
- const PBXTarget* target,
- std::unique_ptr<PBXContainerItemProxy> container_item_proxy) {
- std::unique_ptr<PBXTargetDependency> pbx_target_dependency(
- new PBXTargetDependency(target, std::move(container_item_proxy)));
- return pbx_target_dependency;
-}
-
-// Instantiate a XCBuildConfiguration object with arbitrary names.
-std::unique_ptr<XCBuildConfiguration> GetXCBuildConfigurationObject() {
- std::unique_ptr<XCBuildConfiguration> xc_build_configuration(
- new XCBuildConfiguration("config_name", PBXAttributes()));
- return xc_build_configuration;
-}
-
-// Instantiate a XCConfigurationList object with arbitrary names.
-std::unique_ptr<XCConfigurationList> GetXCConfigurationListObject(
- const PBXObject* owner_reference) {
- std::unique_ptr<XCConfigurationList> xc_configuration_list(
- new XCConfigurationList("config_list_name", PBXAttributes(),
- owner_reference));
- return xc_configuration_list;
-}
-
-} // namespace
-
-// Tests that instantiating Xcode objects doesn't crash.
-TEST(XcodeObject, InstantiatePBXSourcesBuildPhase) {
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
- GetPBXSourcesBuildPhaseObject();
-}
-
-TEST(XcodeObject, InstantiatePBXFrameworksBuildPhase) {
- std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase =
- GetPBXFrameworksBuildPhaseObject();
-}
-
-TEST(XcodeObject, InstantiatePBXShellScriptBuildPhase) {
- std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase =
- GetPBXShellScriptBuildPhaseObject();
-}
-
-TEST(XcodeObject, InstantiatePBXGroup) {
- std::unique_ptr<PBXGroup> pbx_group = GetPBXGroupObject();
-}
-
-TEST(XcodeObject, InstantiatePBXProject) {
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
-}
-
-TEST(XcodeObject, InstantiatePBXFileReference) {
- std::unique_ptr<PBXFileReference> pbx_file_reference =
- GetPBXFileReferenceObject();
-}
-
-TEST(XcodeObject, InstantiatePBXBuildFile) {
- std::unique_ptr<PBXFileReference> pbx_file_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
- GetPBXSourcesBuildPhaseObject();
- std::unique_ptr<PBXBuildFile> pbx_build_file = GetPBXBuildFileObject(
- pbx_file_reference.get(), pbx_sources_build_phase.get());
-}
-
-TEST(XcodeObject, InstantiatePBXAggregateTarget) {
- std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target =
- GetPBXAggregateTargetObject();
-}
-
-TEST(XcodeObject, InstantiatePBXNativeTarget) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
-}
-
-TEST(XcodeObject, InstantiatePBXContainerItemProxy) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
- GetPBXContainerItemProxyObject(pbx_project.get(),
- pbx_native_target.get());
-}
-
-TEST(XcodeObject, InstantiatePBXTargetDependency) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
- GetPBXContainerItemProxyObject(pbx_project.get(),
- pbx_native_target.get());
- std::unique_ptr<PBXTargetDependency> pbx_target_dependency =
- GetPBXTargetDependencyObject(pbx_native_target.get(),
- std::move(pbx_container_item_proxy));
-}
-
-TEST(XcodeObject, InstantiateXCBuildConfiguration) {
- std::unique_ptr<XCBuildConfiguration> xc_build_configuration =
- GetXCBuildConfigurationObject();
-}
-
-TEST(XcodeObject, InstantiateXCConfigurationList) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<XCConfigurationList> xc_configuration_list =
- GetXCConfigurationListObject(pbx_native_target.get());
-}
-
-// Tests that the mapping between PBXObject and PBXObjectClass.
-TEST(XcodeObject, PBXSourcesBuildPhaseObjectToClass) {
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
- GetPBXSourcesBuildPhaseObject();
- EXPECT_EQ(PBXSourcesBuildPhaseClass, pbx_sources_build_phase->Class());
-}
-
-TEST(XcodeObject, PBXFrameworksBuildPhaseObjectToClass) {
- std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase =
- GetPBXFrameworksBuildPhaseObject();
- EXPECT_EQ(PBXFrameworksBuildPhaseClass, pbx_frameworks_build_phase->Class());
-}
-
-TEST(XcodeObject, PBXShellScriptBuildPhaseObjectToClass) {
- std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase =
- GetPBXShellScriptBuildPhaseObject();
- EXPECT_EQ(PBXShellScriptBuildPhaseClass,
- pbx_shell_script_build_phase->Class());
-}
-
-TEST(XcodeObject, PBXGroupObjectToClass) {
- std::unique_ptr<PBXGroup> pbx_group = GetPBXGroupObject();
- EXPECT_EQ(PBXGroupClass, pbx_group->Class());
-}
-
-TEST(XcodeObject, PBXProjectObjectToClass) {
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- EXPECT_EQ(PBXProjectClass, pbx_project->Class());
-}
-
-TEST(XcodeObject, PBXFileReferenceObjectToClass) {
- std::unique_ptr<PBXFileReference> pbx_file_reference =
- GetPBXFileReferenceObject();
- EXPECT_EQ(PBXFileReferenceClass, pbx_file_reference->Class());
-}
-
-TEST(XcodeObject, PBXBuildFileObjectToClass) {
- std::unique_ptr<PBXFileReference> pbx_file_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
- GetPBXSourcesBuildPhaseObject();
- std::unique_ptr<PBXBuildFile> pbx_build_file = GetPBXBuildFileObject(
- pbx_file_reference.get(), pbx_sources_build_phase.get());
- EXPECT_EQ(PBXBuildFileClass, pbx_build_file->Class());
-}
-
-TEST(XcodeObject, PBXAggregateTargetObjectToClass) {
- std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target =
- GetPBXAggregateTargetObject();
- EXPECT_EQ(PBXAggregateTargetClass, pbx_aggregate_target->Class());
-}
-
-TEST(XcodeObject, PBXNativeTargetObjectToClass) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- EXPECT_EQ(PBXNativeTargetClass, pbx_native_target->Class());
-}
-
-TEST(XcodeObject, PBXContainerItemProxyObjectToClass) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
- GetPBXContainerItemProxyObject(pbx_project.get(),
- pbx_native_target.get());
- EXPECT_EQ(PBXContainerItemProxyClass, pbx_container_item_proxy->Class());
-}
-
-TEST(XcodeObject, PBXTargetDependencyObjectToClass) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
- GetPBXContainerItemProxyObject(pbx_project.get(),
- pbx_native_target.get());
- std::unique_ptr<PBXTargetDependency> pbx_target_dependency =
- GetPBXTargetDependencyObject(pbx_native_target.get(),
- std::move(pbx_container_item_proxy));
- EXPECT_EQ(PBXTargetDependencyClass, pbx_target_dependency->Class());
-}
-
-TEST(XcodeObject, XCBuildConfigurationObjectToClass) {
- std::unique_ptr<XCBuildConfiguration> xc_build_configuration =
- GetXCBuildConfigurationObject();
- EXPECT_EQ(XCBuildConfigurationClass, xc_build_configuration->Class());
-}
-
-TEST(XcodeObject, XCConfigurationListObjectToClass) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<XCConfigurationList> xc_configuration_list =
- GetXCConfigurationListObject(pbx_native_target.get());
- EXPECT_EQ(XCConfigurationListClass, xc_configuration_list->Class());
-}
-
-// Tests the mapping between PBXObjectClass and it's name as a string.
-TEST(XcodeObject, ClassToString) {
- EXPECT_STREQ("PBXAggregateTarget", ToString(PBXAggregateTargetClass));
- EXPECT_STREQ("PBXBuildFile", ToString(PBXBuildFileClass));
- EXPECT_STREQ("PBXAggregateTarget", ToString(PBXAggregateTargetClass));
- EXPECT_STREQ("PBXBuildFile", ToString(PBXBuildFileClass));
- EXPECT_STREQ("PBXContainerItemProxy", ToString(PBXContainerItemProxyClass));
- EXPECT_STREQ("PBXFileReference", ToString(PBXFileReferenceClass));
- EXPECT_STREQ("PBXFrameworksBuildPhase",
- ToString(PBXFrameworksBuildPhaseClass));
- EXPECT_STREQ("PBXGroup", ToString(PBXGroupClass));
- EXPECT_STREQ("PBXNativeTarget", ToString(PBXNativeTargetClass));
- EXPECT_STREQ("PBXProject", ToString(PBXProjectClass));
- EXPECT_STREQ("PBXSourcesBuildPhase", ToString(PBXSourcesBuildPhaseClass));
- EXPECT_STREQ("PBXTargetDependency", ToString(PBXTargetDependencyClass));
- EXPECT_STREQ("XCBuildConfiguration", ToString(XCBuildConfigurationClass));
- EXPECT_STREQ("XCConfigurationList", ToString(XCConfigurationListClass));
- EXPECT_STREQ("PBXShellScriptBuildPhase",
- ToString(PBXShellScriptBuildPhaseClass));
-}
-
-// Tests the mapping between PBXObject and it's name as a string.
-TEST(XcodeObject, PBXSourcesBuildPhaseName) {
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
- GetPBXSourcesBuildPhaseObject();
- EXPECT_EQ("Sources", pbx_sources_build_phase->Name());
-}
-
-TEST(XcodeObject, PBXFrameworksBuildPhaseName) {
- std::unique_ptr<PBXFrameworksBuildPhase> pbx_frameworks_build_phase =
- GetPBXFrameworksBuildPhaseObject();
- EXPECT_EQ("Frameworks", pbx_frameworks_build_phase->Name());
-}
-
-TEST(XcodeObject, PBXShellScriptBuildPhaseName) {
- std::unique_ptr<PBXShellScriptBuildPhase> pbx_shell_script_build_phase =
- GetPBXShellScriptBuildPhaseObject();
- EXPECT_EQ("Action \"Compile and copy name via ninja\"",
- pbx_shell_script_build_phase->Name());
-}
-
-TEST(XcodeObject, PBXGroupName) {
- PBXGroup pbx_group_with_name(std::string(), "name");
- EXPECT_EQ("name", pbx_group_with_name.Name());
-
- PBXGroup pbx_group_with_path("path", std::string());
- EXPECT_EQ("path", pbx_group_with_path.Name());
-
- PBXGroup pbx_group_empty{std::string(), std::string()};
- EXPECT_EQ(std::string(), pbx_group_empty.Name());
-}
-
-TEST(XcodeObject, PBXProjectName) {
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- EXPECT_EQ("project", pbx_project->Name());
-}
-
-TEST(XcodeObject, PBXFileReferenceName) {
- std::unique_ptr<PBXFileReference> pbx_file_reference =
- GetPBXFileReferenceObject();
- EXPECT_EQ("product.app", pbx_file_reference->Name());
-}
-
-TEST(XcodeObject, PBXBuildFileName) {
- std::unique_ptr<PBXFileReference> pbx_file_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXSourcesBuildPhase> pbx_sources_build_phase =
- GetPBXSourcesBuildPhaseObject();
- std::unique_ptr<PBXBuildFile> pbx_build_file = GetPBXBuildFileObject(
- pbx_file_reference.get(), pbx_sources_build_phase.get());
- EXPECT_EQ("product.app in Sources", pbx_build_file->Name());
-}
-
-TEST(XcodeObject, PBXAggregateTargetName) {
- std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target =
- GetPBXAggregateTargetObject();
- EXPECT_EQ("target_name", pbx_aggregate_target->Name());
-}
-
-TEST(XcodeObject, PBXNativeTargetName) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- EXPECT_EQ("target_name", pbx_native_target->Name());
-}
-
-TEST(XcodeObject, PBXContainerItemProxyName) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
- GetPBXContainerItemProxyObject(pbx_project.get(),
- pbx_native_target.get());
- EXPECT_EQ("PBXContainerItemProxy", pbx_container_item_proxy->Name());
-}
-
-TEST(XcodeObject, PBXTargetDependencyName) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXProject> pbx_project = GetPBXProjectObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<PBXContainerItemProxy> pbx_container_item_proxy =
- GetPBXContainerItemProxyObject(pbx_project.get(),
- pbx_native_target.get());
- std::unique_ptr<PBXTargetDependency> pbx_target_dependency =
- GetPBXTargetDependencyObject(pbx_native_target.get(),
- std::move(pbx_container_item_proxy));
- EXPECT_EQ("PBXTargetDependency", pbx_target_dependency->Name());
-}
-
-TEST(XcodeObject, XCBuildConfigurationName) {
- std::unique_ptr<XCBuildConfiguration> xc_build_configuration =
- GetXCBuildConfigurationObject();
- EXPECT_EQ("config_name", xc_build_configuration->Name());
-}
-
-TEST(XcodeObject, XCConfigurationListName) {
- std::unique_ptr<PBXFileReference> product_reference =
- GetPBXFileReferenceObject();
- std::unique_ptr<PBXNativeTarget> pbx_native_target =
- GetPBXNativeTargetObject(product_reference.get());
- std::unique_ptr<XCConfigurationList> xc_configuration_list =
- GetXCConfigurationListObject(pbx_native_target.get());
- EXPECT_EQ("Build configuration list for PBXNativeTarget \"target_name\"",
- xc_configuration_list->Name());
-}
diff --git a/gn/tools/gn/xcode_writer.cc b/gn/tools/gn/xcode_writer.cc
deleted file mode 100644
index 0d4a35a7227..00000000000
--- a/gn/tools/gn/xcode_writer.cc
+++ /dev/null
@@ -1,664 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/xcode_writer.h"
-
-#include <iomanip>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <utility>
-
-#include "base/environment.h"
-#include "base/logging.h"
-#include "base/sha1.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "tools/gn/args.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/builder.h"
-#include "tools/gn/commands.h"
-#include "tools/gn/deps_iterator.h"
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/target.h"
-#include "tools/gn/value.h"
-#include "tools/gn/variables.h"
-#include "tools/gn/xcode_object.h"
-
-namespace {
-
-using TargetToFileList = std::unordered_map<const Target*, Target::FileList>;
-using TargetToTarget = std::unordered_map<const Target*, const Target*>;
-using TargetToPBXTarget = std::unordered_map<const Target*, PBXTarget*>;
-
-const char* kXCTestFileSuffixes[] = {
- "egtest.m",
- "egtest.mm",
- "xctest.m",
- "xctest.mm",
-};
-
-const char kXCTestModuleTargetNamePostfix[] = "_module";
-const char kXCUITestRunnerTargetNamePostfix[] = "_runner";
-
-struct SafeEnvironmentVariableInfo {
- const char* name;
- bool capture_at_generation;
-};
-
-SafeEnvironmentVariableInfo kSafeEnvironmentVariables[] = {
- {"HOME", true},
- {"LANG", true},
- {"PATH", true},
- {"USER", true},
- {"TMPDIR", false},
- {"ICECC_VERSION", true},
- {"ICECC_CLANG_REMOTE_CPP", true}};
-
-XcodeWriter::TargetOsType GetTargetOs(const Args& args) {
- const Value* target_os_value = args.GetArgOverride(variables::kTargetOs);
- if (target_os_value) {
- if (target_os_value->type() == Value::STRING) {
- if (target_os_value->string_value() == "ios")
- return XcodeWriter::WRITER_TARGET_OS_IOS;
- }
- }
- return XcodeWriter::WRITER_TARGET_OS_MACOS;
-}
-
-std::string GetBuildScript(const std::string& target_name,
- const std::string& ninja_extra_args,
- base::Environment* environment) {
- std::stringstream script;
- script << "echo note: \"Compile and copy " << target_name << " via ninja\"\n"
- << "exec ";
-
- // Launch ninja with a sanitized environment (Xcode sets many environment
- // variable overridding settings, including the SDK, thus breaking hermetic
- // build).
- script << "env -i ";
- for (const auto& variable : kSafeEnvironmentVariables) {
- script << variable.name << "=\"";
-
- std::string value;
- if (variable.capture_at_generation)
- environment->GetVar(variable.name, &value);
-
- if (!value.empty())
- script << value;
- else
- script << "$" << variable.name;
- script << "\" ";
- }
-
- script << "ninja -C .";
- if (!ninja_extra_args.empty())
- script << " " << ninja_extra_args;
- if (!target_name.empty())
- script << " " << target_name;
- script << "\nexit 1\n";
- return script.str();
-}
-
-bool IsApplicationTarget(const Target* target) {
- return target->output_type() == Target::CREATE_BUNDLE &&
- target->bundle_data().product_type() ==
- "com.apple.product-type.application";
-}
-
-bool IsXCUITestRunnerTarget(const Target* target) {
- return IsApplicationTarget(target) &&
- base::EndsWith(target->label().name(),
- kXCUITestRunnerTargetNamePostfix,
- base::CompareCase::SENSITIVE);
-}
-
-bool IsXCTestModuleTarget(const Target* target) {
- return target->output_type() == Target::CREATE_BUNDLE &&
- target->bundle_data().product_type() ==
- "com.apple.product-type.bundle.unit-test" &&
- base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix,
- base::CompareCase::SENSITIVE);
-}
-
-bool IsXCUITestModuleTarget(const Target* target) {
- return target->output_type() == Target::CREATE_BUNDLE &&
- target->bundle_data().product_type() ==
- "com.apple.product-type.bundle.ui-testing" &&
- base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix,
- base::CompareCase::SENSITIVE);
-}
-
-bool IsXCTestFile(const SourceFile& file) {
- std::string file_name = file.GetName();
- for (size_t i = 0; i < arraysize(kXCTestFileSuffixes); ++i) {
- if (base::EndsWith(file_name, kXCTestFileSuffixes[i],
- base::CompareCase::SENSITIVE)) {
- return true;
- }
- }
-
- return false;
-}
-
-const Target* FindApplicationTargetByName(
- const std::string& target_name,
- const std::vector<const Target*>& targets) {
- for (const Target* target : targets) {
- if (target->label().name() == target_name) {
- DCHECK(IsApplicationTarget(target));
- return target;
- }
- }
- NOTREACHED();
- return nullptr;
-}
-
-// Adds |base_pbxtarget| as a dependency of |dependent_pbxtarget| in the
-// generated Xcode project.
-void AddPBXTargetDependency(const PBXTarget* base_pbxtarget,
- PBXTarget* dependent_pbxtarget,
- const PBXProject* project) {
- auto container_item_proxy =
- std::make_unique<PBXContainerItemProxy>(project, base_pbxtarget);
- auto dependency = std::make_unique<PBXTargetDependency>(
- base_pbxtarget, std::move(container_item_proxy));
-
- dependent_pbxtarget->AddDependency(std::move(dependency));
-}
-
-// Adds the corresponding test application target as dependency of xctest or
-// xcuitest module target in the generated Xcode project.
-void AddDependencyTargetForTestModuleTargets(
- const std::vector<const Target*>& targets,
- const TargetToPBXTarget& bundle_target_to_pbxtarget,
- const PBXProject* project) {
- for (const Target* target : targets) {
- if (!IsXCTestModuleTarget(target) && !IsXCUITestModuleTarget(target))
- continue;
-
- const Target* test_application_target = FindApplicationTargetByName(
- target->bundle_data().xcode_test_application_name(), targets);
- const PBXTarget* test_application_pbxtarget =
- bundle_target_to_pbxtarget.at(test_application_target);
- PBXTarget* module_pbxtarget = bundle_target_to_pbxtarget.at(target);
- DCHECK(test_application_pbxtarget);
- DCHECK(module_pbxtarget);
-
- AddPBXTargetDependency(test_application_pbxtarget, module_pbxtarget,
- project);
- }
-}
-
-// Searches the list of xctest files recursively under |target|.
-void SearchXCTestFilesForTarget(const Target* target,
- TargetToFileList* xctest_files_per_target) {
- // Early return if already visited and processed.
- if (xctest_files_per_target->find(target) != xctest_files_per_target->end())
- return;
-
- Target::FileList xctest_files;
- for (const SourceFile& file : target->sources()) {
- if (IsXCTestFile(file)) {
- xctest_files.push_back(file);
- }
- }
-
- // Call recursively on public and private deps.
- for (const auto& t : target->public_deps()) {
- SearchXCTestFilesForTarget(t.ptr, xctest_files_per_target);
- const Target::FileList& deps_xctest_files =
- (*xctest_files_per_target)[t.ptr];
- xctest_files.insert(xctest_files.end(), deps_xctest_files.begin(),
- deps_xctest_files.end());
- }
-
- for (const auto& t : target->private_deps()) {
- SearchXCTestFilesForTarget(t.ptr, xctest_files_per_target);
- const Target::FileList& deps_xctest_files =
- (*xctest_files_per_target)[t.ptr];
- xctest_files.insert(xctest_files.end(), deps_xctest_files.begin(),
- deps_xctest_files.end());
- }
-
- // Sort xctest_files to remove duplicates.
- std::sort(xctest_files.begin(), xctest_files.end());
- xctest_files.erase(std::unique(xctest_files.begin(), xctest_files.end()),
- xctest_files.end());
-
- xctest_files_per_target->insert(std::make_pair(target, xctest_files));
-}
-
-// Add all source files for indexing, both private and public.
-void AddSourceFilesToProjectForIndexing(
- const std::vector<const Target*>& targets,
- PBXProject* project,
- SourceDir source_dir,
- const BuildSettings* build_settings) {
- std::vector<SourceFile> sources;
- for (const Target* target : targets) {
- for (const SourceFile& source : target->sources()) {
- if (IsStringInOutputDir(build_settings->build_dir(), source.value()))
- continue;
-
- sources.push_back(source);
- }
-
- if (target->all_headers_public())
- continue;
-
- for (const SourceFile& source : target->public_headers()) {
- if (IsStringInOutputDir(build_settings->build_dir(), source.value()))
- continue;
-
- sources.push_back(source);
- }
- }
-
- // Sort sources to ensure determinism of the project file generation and
- // remove duplicate reference to the source files (can happen due to the
- // bundle_data targets).
- std::sort(sources.begin(), sources.end());
- sources.erase(std::unique(sources.begin(), sources.end()), sources.end());
-
- for (const SourceFile& source : sources) {
- std::string source_file = RebasePath(source.value(), source_dir,
- build_settings->root_path_utf8());
- project->AddSourceFileToIndexingTarget(source_file, source_file,
- CompilerFlags::NONE);
- }
-}
-
-// Add xctest files to the "Compiler Sources" of corresponding test module
-// native targets.
-void AddXCTestFilesToTestModuleTarget(const Target::FileList& xctest_file_list,
- PBXNativeTarget* native_target,
- PBXProject* project,
- SourceDir source_dir,
- const BuildSettings* build_settings) {
- for (const SourceFile& source : xctest_file_list) {
- std::string source_path = RebasePath(source.value(), source_dir,
- build_settings->root_path_utf8());
-
- // Test files need to be known to Xcode for proper indexing and for
- // discovery of tests function for XCTest and XCUITest, but the compilation
- // is done via ninja and thus must prevent Xcode from compiling the files by
- // adding '-help' as per file compiler flag.
- project->AddSourceFile(source_path, source_path, CompilerFlags::HELP,
- native_target);
- }
-}
-
-class CollectPBXObjectsPerClassHelper : public PBXObjectVisitor {
- public:
- CollectPBXObjectsPerClassHelper() = default;
-
- void Visit(PBXObject* object) override {
- DCHECK(object);
- objects_per_class_[object->Class()].push_back(object);
- }
-
- const std::map<PBXObjectClass, std::vector<const PBXObject*>>&
- objects_per_class() const {
- return objects_per_class_;
- }
-
- private:
- std::map<PBXObjectClass, std::vector<const PBXObject*>> objects_per_class_;
-
- DISALLOW_COPY_AND_ASSIGN(CollectPBXObjectsPerClassHelper);
-};
-
-std::map<PBXObjectClass, std::vector<const PBXObject*>>
-CollectPBXObjectsPerClass(PBXProject* project) {
- CollectPBXObjectsPerClassHelper visitor;
- project->Visit(visitor);
- return visitor.objects_per_class();
-}
-
-class RecursivelyAssignIdsHelper : public PBXObjectVisitor {
- public:
- RecursivelyAssignIdsHelper(const std::string& seed)
- : seed_(seed), counter_(0) {}
-
- void Visit(PBXObject* object) override {
- std::stringstream buffer;
- buffer << seed_ << " " << object->Name() << " " << counter_;
- std::string hash = base::SHA1HashString(buffer.str());
- DCHECK_EQ(hash.size() % 4, 0u);
-
- uint32_t id[3] = {0, 0, 0};
- const uint32_t* ptr = reinterpret_cast<const uint32_t*>(hash.data());
- for (size_t i = 0; i < hash.size() / 4; i++)
- id[i % 3] ^= ptr[i];
-
- object->SetId(base::HexEncode(id, sizeof(id)));
- ++counter_;
- }
-
- private:
- std::string seed_;
- int64_t counter_;
-
- DISALLOW_COPY_AND_ASSIGN(RecursivelyAssignIdsHelper);
-};
-
-void RecursivelyAssignIds(PBXProject* project) {
- RecursivelyAssignIdsHelper visitor(project->Name());
- project->Visit(visitor);
-}
-
-} // namespace
-
-// static
-bool XcodeWriter::RunAndWriteFiles(const std::string& workspace_name,
- const std::string& root_target_name,
- const std::string& ninja_extra_args,
- const std::string& dir_filters_string,
- const BuildSettings* build_settings,
- const Builder& builder,
- Err* err) {
- const XcodeWriter::TargetOsType target_os =
- GetTargetOs(build_settings->build_args());
-
- PBXAttributes attributes;
- switch (target_os) {
- case XcodeWriter::WRITER_TARGET_OS_IOS:
- attributes["SDKROOT"] = "iphoneos";
- attributes["TARGETED_DEVICE_FAMILY"] = "1,2";
- break;
- case XcodeWriter::WRITER_TARGET_OS_MACOS:
- attributes["SDKROOT"] = "macosx";
- break;
- }
-
- const std::string source_path = FilePathToUTF8(
- UTF8ToFilePath(RebasePath("//", build_settings->build_dir()))
- .StripTrailingSeparators());
-
- std::string config_name = FilePathToUTF8(build_settings->build_dir()
- .Resolve(base::FilePath())
- .StripTrailingSeparators()
- .BaseName());
- DCHECK(!config_name.empty());
-
- std::string::size_type separator = config_name.find('-');
- if (separator != std::string::npos)
- config_name = config_name.substr(0, separator);
-
- std::vector<const Target*> targets;
- std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
- if (!XcodeWriter::FilterTargets(build_settings, all_targets,
- dir_filters_string, &targets, err)) {
- return false;
- }
-
- XcodeWriter workspace(workspace_name);
- workspace.CreateProductsProject(targets, all_targets, attributes, source_path,
- config_name, root_target_name,
- ninja_extra_args, build_settings, target_os);
-
- return workspace.WriteFiles(build_settings, err);
-}
-
-XcodeWriter::XcodeWriter(const std::string& name) : name_(name) {
- if (name_.empty())
- name_.assign("all");
-}
-
-XcodeWriter::~XcodeWriter() = default;
-
-// static
-bool XcodeWriter::FilterTargets(const BuildSettings* build_settings,
- const std::vector<const Target*>& all_targets,
- const std::string& dir_filters_string,
- std::vector<const Target*>* targets,
- Err* err) {
- // Filter targets according to the semicolon-delimited list of label patterns,
- // if defined, first.
- targets->reserve(all_targets.size());
- if (dir_filters_string.empty()) {
- *targets = all_targets;
- } else {
- std::vector<LabelPattern> filters;
- if (!commands::FilterPatternsFromString(build_settings, dir_filters_string,
- &filters, err)) {
- return false;
- }
-
- commands::FilterTargetsByPatterns(all_targets, filters, targets);
- }
-
- // Filter out all target of type EXECUTABLE that are direct dependency of
- // a BUNDLE_DATA target (under the assumption that they will be part of a
- // CREATE_BUNDLE target generating an application bundle). Sort the list
- // of targets per pointer to use binary search for the removal.
- std::sort(targets->begin(), targets->end());
-
- for (const Target* target : all_targets) {
- if (!target->settings()->is_default())
- continue;
-
- if (target->output_type() != Target::BUNDLE_DATA)
- continue;
-
- for (const auto& pair : target->GetDeps(Target::DEPS_LINKED)) {
- if (pair.ptr->output_type() != Target::EXECUTABLE)
- continue;
-
- auto iter = std::lower_bound(targets->begin(), targets->end(), pair.ptr);
- if (iter != targets->end() && *iter == pair.ptr)
- targets->erase(iter);
- }
- }
-
- // Sort the list of targets per-label to get a consistent ordering of them
- // in the generated Xcode project (and thus stability of the file generated).
- std::sort(targets->begin(), targets->end(),
- [](const Target* a, const Target* b) {
- return a->label().name() < b->label().name();
- });
-
- return true;
-}
-
-void XcodeWriter::CreateProductsProject(
- const std::vector<const Target*>& targets,
- const std::vector<const Target*>& all_targets,
- const PBXAttributes& attributes,
- const std::string& source_path,
- const std::string& config_name,
- const std::string& root_target,
- const std::string& ninja_extra_args,
- const BuildSettings* build_settings,
- TargetOsType target_os) {
- std::unique_ptr<PBXProject> main_project(
- new PBXProject("products", config_name, source_path, attributes));
-
- std::vector<const Target*> bundle_targets;
- TargetToPBXTarget bundle_target_to_pbxtarget;
-
- std::string build_path;
- std::unique_ptr<base::Environment> env(base::Environment::Create());
- SourceDir source_dir("//");
- AddSourceFilesToProjectForIndexing(all_targets, main_project.get(),
- source_dir, build_settings);
- main_project->AddAggregateTarget(
- "All", GetBuildScript(root_target, ninja_extra_args, env.get()));
-
- // Needs to search for xctest files under the application targets, and this
- // variable is used to store the results of visited targets, thus making the
- // search more efficient.
- TargetToFileList xctest_files_per_target;
-
- for (const Target* target : targets) {
- switch (target->output_type()) {
- case Target::EXECUTABLE:
- if (target_os == XcodeWriter::WRITER_TARGET_OS_IOS)
- continue;
-
- main_project->AddNativeTarget(
- target->label().name(), "compiled.mach-o.executable",
- target->output_name().empty() ? target->label().name()
- : target->output_name(),
- "com.apple.product-type.tool",
- GetBuildScript(target->label().name(), ninja_extra_args,
- env.get()));
- break;
-
- case Target::CREATE_BUNDLE: {
- if (target->bundle_data().product_type().empty())
- continue;
-
- // For XCUITest, two CREATE_BUNDLE targets are generated:
- // ${target_name}_runner and ${target_name}_module, however, Xcode
- // requires only one target named ${target_name} to run tests.
- if (IsXCUITestRunnerTarget(target))
- continue;
- std::string pbxtarget_name = target->label().name();
- if (IsXCUITestModuleTarget(target)) {
- std::string target_name = target->label().name();
- pbxtarget_name = target_name.substr(
- 0, target_name.rfind(kXCTestModuleTargetNamePostfix));
- }
-
- PBXAttributes xcode_extra_attributes =
- target->bundle_data().xcode_extra_attributes();
-
- const std::string& target_output_name =
- RebasePath(target->bundle_data()
- .GetBundleRootDirOutput(target->settings())
- .value(),
- build_settings->build_dir());
- PBXNativeTarget* native_target = main_project->AddNativeTarget(
- pbxtarget_name, std::string(), target_output_name,
- target->bundle_data().product_type(),
- GetBuildScript(pbxtarget_name, ninja_extra_args, env.get()),
- xcode_extra_attributes);
-
- bundle_targets.push_back(target);
- bundle_target_to_pbxtarget.insert(
- std::make_pair(target, native_target));
-
- if (!IsXCTestModuleTarget(target) && !IsXCUITestModuleTarget(target))
- continue;
-
- // For XCTest, test files are compiled into the application bundle.
- // For XCUITest, test files are compiled into the test module bundle.
- const Target* target_with_xctest_files = nullptr;
- if (IsXCTestModuleTarget(target)) {
- target_with_xctest_files = FindApplicationTargetByName(
- target->bundle_data().xcode_test_application_name(), targets);
- } else if (IsXCUITestModuleTarget(target)) {
- target_with_xctest_files = target;
- } else {
- NOTREACHED();
- }
-
- SearchXCTestFilesForTarget(target_with_xctest_files,
- &xctest_files_per_target);
- const Target::FileList& xctest_file_list =
- xctest_files_per_target[target_with_xctest_files];
-
- // Add xctest files to the "Compiler Sources" of corresponding xctest
- // and xcuitest native targets for proper indexing and for discovery of
- // tests function.
- AddXCTestFilesToTestModuleTarget(xctest_file_list, native_target,
- main_project.get(), source_dir,
- build_settings);
- break;
- }
-
- default:
- break;
- }
- }
-
- // Adding the corresponding test application target as a dependency of xctest
- // or xcuitest module target in the generated Xcode project so that the
- // application target is re-compiled when compiling the test module target.
- AddDependencyTargetForTestModuleTargets(
- bundle_targets, bundle_target_to_pbxtarget, main_project.get());
-
- projects_.push_back(std::move(main_project));
-}
-
-bool XcodeWriter::WriteFiles(const BuildSettings* build_settings, Err* err) {
- for (const auto& project : projects_) {
- if (!WriteProjectFile(build_settings, project.get(), err))
- return false;
- }
-
- SourceFile xcworkspacedata_file =
- build_settings->build_dir().ResolveRelativeFile(
- Value(nullptr, name_ + ".xcworkspace/contents.xcworkspacedata"), err);
- if (xcworkspacedata_file.is_null())
- return false;
-
- std::stringstream xcworkspacedata_string_out;
- WriteWorkspaceContent(xcworkspacedata_string_out);
-
- return WriteFileIfChanged(build_settings->GetFullPath(xcworkspacedata_file),
- xcworkspacedata_string_out.str(), err);
-}
-
-bool XcodeWriter::WriteProjectFile(const BuildSettings* build_settings,
- PBXProject* project,
- Err* err) {
- SourceFile pbxproj_file = build_settings->build_dir().ResolveRelativeFile(
- Value(nullptr, project->Name() + ".xcodeproj/project.pbxproj"), err);
- if (pbxproj_file.is_null())
- return false;
-
- std::stringstream pbxproj_string_out;
- WriteProjectContent(pbxproj_string_out, project);
-
- if (!WriteFileIfChanged(build_settings->GetFullPath(pbxproj_file),
- pbxproj_string_out.str(), err))
- return false;
-
- return true;
-}
-
-void XcodeWriter::WriteWorkspaceContent(std::ostream& out) {
- out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- << "<Workspace version = \"1.0\">\n";
- for (const auto& project : projects_) {
- out << " <FileRef location = \"group:" << project->Name()
- << ".xcodeproj\"></FileRef>\n";
- }
- out << "</Workspace>\n";
-}
-
-void XcodeWriter::WriteProjectContent(std::ostream& out, PBXProject* project) {
- RecursivelyAssignIds(project);
-
- out << "// !$*UTF8*$!\n"
- << "{\n"
- << "\tarchiveVersion = 1;\n"
- << "\tclasses = {\n"
- << "\t};\n"
- << "\tobjectVersion = 46;\n"
- << "\tobjects = {\n";
-
- for (auto& pair : CollectPBXObjectsPerClass(project)) {
- out << "\n"
- << "/* Begin " << ToString(pair.first) << " section */\n";
- std::sort(pair.second.begin(), pair.second.end(),
- [](const PBXObject* a, const PBXObject* b) {
- return a->id() < b->id();
- });
- for (auto* object : pair.second) {
- object->Print(out, 2);
- }
- out << "/* End " << ToString(pair.first) << " section */\n";
- }
-
- out << "\t};\n"
- << "\trootObject = " << project->Reference() << ";\n"
- << "}\n";
-}
diff --git a/gn/tools/gn/xcode_writer.h b/gn/tools/gn/xcode_writer.h
deleted file mode 100644
index 24953dcb9cd..00000000000
--- a/gn/tools/gn/xcode_writer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_XCODE_WRITER_H_
-#define TOOLS_GN_XCODE_WRITER_H_
-
-#include <iosfwd>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-
-class Builder;
-class BuildSettings;
-class Err;
-class Target;
-
-using PBXAttributes = std::map<std::string, std::string>;
-class PBXProject;
-
-class XcodeWriter {
- public:
- enum TargetOsType {
- WRITER_TARGET_OS_IOS,
- WRITER_TARGET_OS_MACOS,
- };
-
- // Writes Xcode workspace and project files.
- //
- // |workspace_name| is the optional name of the workspace file name ("all"
- // is used if not specified). |root_target_name| is the name of the main
- // target corresponding to building "All" (for example "gn_all" in Chromium).
- // |ninja_extra_args| are additional arguments to pass to ninja invocation
- // (can be used to increase limit of concurrent processes when using goma).
- // |dir_filters_string| is optional semicolon-separated list of label patterns
- // used to limit the set of generated projects. Only matching targets will be
- // included to the workspace. On failure will populate |err| and return false.
- static bool RunAndWriteFiles(const std::string& workspace_name,
- const std::string& root_target_name,
- const std::string& ninja_extra_args,
- const std::string& dir_filters_string,
- const BuildSettings* build_settings,
- const Builder& builder,
- Err* err);
-
- private:
- XcodeWriter(const std::string& name);
- ~XcodeWriter();
-
- // Filters the list of targets to only return the targets with artifacts
- // usable from Xcode (mostly application bundles). On failure populate |err|
- // and return false.
- static bool FilterTargets(const BuildSettings* build_settings,
- const std::vector<const Target*>& all_targets,
- const std::string& dir_filters_string,
- std::vector<const Target*>* targets,
- Err* err);
-
- // Generate the "products.xcodeproj" project that reference all products
- // (i.e. targets that have a build artefact usable from Xcode, mostly
- // application bundles).
- void CreateProductsProject(const std::vector<const Target*>& targets,
- const std::vector<const Target*>& all_targets,
- const PBXAttributes& attributes,
- const std::string& source_path,
- const std::string& config_name,
- const std::string& root_target,
- const std::string& ninja_extra_args,
- const BuildSettings* build_settings,
- TargetOsType target_os);
-
- bool WriteFiles(const BuildSettings* build_settings, Err* err);
- bool WriteProjectFile(const BuildSettings* build_settings,
- PBXProject* project,
- Err* err);
-
- void WriteWorkspaceContent(std::ostream& out);
- void WriteProjectContent(std::ostream& out, PBXProject* project);
-
- std::string name_;
- std::vector<std::unique_ptr<PBXProject>> projects_;
-
- DISALLOW_COPY_AND_ASSIGN(XcodeWriter);
-};
-
-#endif // TOOLS_GN_XCODE_WRITER_H_
diff --git a/gn/tools/gn/xml_element_writer.cc b/gn/tools/gn/xml_element_writer.cc
deleted file mode 100644
index 360d8af18d5..00000000000
--- a/gn/tools/gn/xml_element_writer.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/xml_element_writer.h"
-
-#include <memory>
-
-XmlAttributes::XmlAttributes() = default;
-
-XmlAttributes::XmlAttributes(const base::StringPiece& attr_key,
- const base::StringPiece& attr_value) {
- add(attr_key, attr_value);
-}
-
-XmlAttributes& XmlAttributes::add(const base::StringPiece& attr_key,
- const base::StringPiece& attr_value) {
- push_back(std::make_pair(attr_key, attr_value));
- return *this;
-}
-
-XmlElementWriter::XmlElementWriter(std::ostream& out,
- const std::string& tag,
- const XmlAttributes& attributes)
- : XmlElementWriter(out, tag, attributes, 0) {}
-
-XmlElementWriter::XmlElementWriter(std::ostream& out,
- const std::string& tag,
- const XmlAttributes& attributes,
- int indent)
- : out_(out),
- tag_(tag),
- indent_(indent),
- opening_tag_finished_(false),
- one_line_(true) {
- out << std::string(indent, ' ') << '<' << tag;
- for (auto attribute : attributes)
- out << ' ' << attribute.first << "=\"" << attribute.second << '"';
-}
-
-XmlElementWriter::~XmlElementWriter() {
- if (!opening_tag_finished_) {
- // The XML spec does not require a space before the closing slash. However,
- // Eclipse is unable to parse XML settings files if there is no space.
- out_ << " />" << std::endl;
- } else {
- if (!one_line_)
- out_ << std::string(indent_, ' ');
- out_ << "</" << tag_ << '>' << std::endl;
- }
-}
-
-void XmlElementWriter::Text(const base::StringPiece& content) {
- StartContent(false);
- out_ << content;
-}
-
-std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement(
- const std::string& tag) {
- return SubElement(tag, XmlAttributes());
-}
-
-std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement(
- const std::string& tag,
- const XmlAttributes& attributes) {
- StartContent(true);
- return std::make_unique<XmlElementWriter>(out_, tag, attributes, indent_ + 2);
-}
-
-std::ostream& XmlElementWriter::StartContent(bool start_new_line) {
- if (!opening_tag_finished_) {
- out_ << '>';
- opening_tag_finished_ = true;
-
- if (start_new_line && one_line_) {
- out_ << std::endl;
- one_line_ = false;
- }
- }
-
- return out_;
-}
-
-std::string XmlEscape(const std::string& value) {
- std::string result;
- for (char c : value) {
- switch (c) {
- case '\n':
- result += "&#10;";
- break;
- case '\r':
- result += "&#13;";
- break;
- case '\t':
- result += "&#9;";
- break;
- case '"':
- result += "&quot;";
- break;
- case '<':
- result += "&lt;";
- break;
- case '>':
- result += "&gt;";
- break;
- case '&':
- result += "&amp;";
- break;
- default:
- result += c;
- }
- }
- return result;
-}
diff --git a/gn/tools/gn/xml_element_writer.h b/gn/tools/gn/xml_element_writer.h
deleted file mode 100644
index 4aa5f7e34a4..00000000000
--- a/gn/tools/gn/xml_element_writer.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2016 The Chromium 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 TOOLS_GN_XML_ELEMENT_WRITER_H_
-#define TOOLS_GN_XML_ELEMENT_WRITER_H_
-
-#include <iosfwd>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-// Vector of XML attribute key-value pairs.
-class XmlAttributes
- : public std::vector<std::pair<base::StringPiece, base::StringPiece>> {
- public:
- XmlAttributes();
- XmlAttributes(const base::StringPiece& attr_key,
- const base::StringPiece& attr_value);
-
- XmlAttributes& add(const base::StringPiece& attr_key,
- const base::StringPiece& attr_value);
-};
-
-// Helper class for writing XML elements. New XML element is started in
-// XmlElementWriter constructor and ended in its destructor. XmlElementWriter
-// handles XML file formatting in order to produce human-readable document.
-class XmlElementWriter {
- public:
- // Starts new XML element. This constructor adds no indentation and is
- // designed for XML root element.
- XmlElementWriter(std::ostream& out,
- const std::string& tag,
- const XmlAttributes& attributes);
- // Starts new XML element with specified indentation.
- XmlElementWriter(std::ostream& out,
- const std::string& tag,
- const XmlAttributes& attributes,
- int indent);
- // Starts new XML element with specified indentation. Specialized constructor
- // that allows writting XML element with single attribute without copying
- // attribute value.
- template <class Writer>
- XmlElementWriter(std::ostream& out,
- const std::string& tag,
- const std::string& attribute_name,
- const Writer& attribute_value_writer,
- int indent);
- // Ends XML element. All sub-elements should be ended at this point.
- ~XmlElementWriter();
-
- // Writes arbitrary XML element text.
- void Text(const base::StringPiece& content);
-
- // Starts new XML sub-element. Caller must ensure that parent element outlives
- // its children.
- std::unique_ptr<XmlElementWriter> SubElement(const std::string& tag);
- std::unique_ptr<XmlElementWriter> SubElement(const std::string& tag,
- const XmlAttributes& attributes);
- template <class Writer>
- std::unique_ptr<XmlElementWriter> SubElement(
- const std::string& tag,
- const std::string& attribute_name,
- const Writer& attribute_value_writer);
-
- // Finishes opening tag if it isn't finished yet and optionally starts new
- // document line. Returns the stream where XML element content can be written.
- // This is an alternative to Text() and SubElement() methods.
- std::ostream& StartContent(bool start_new_line);
-
- private:
- // Output stream. XmlElementWriter objects for XML element and its
- // sub-elements share the same output stream.
- std::ostream& out_;
-
- // XML element tag name.
- std::string tag_;
-
- // XML element indentation in the document.
- int indent_;
-
- // Flag indicating if opening tag is finished with '>' character already.
- bool opening_tag_finished_;
-
- // Flag indicating if XML element should be written in one document line.
- bool one_line_;
-
- DISALLOW_COPY_AND_ASSIGN(XmlElementWriter);
-};
-
-template <class Writer>
-XmlElementWriter::XmlElementWriter(std::ostream& out,
- const std::string& tag,
- const std::string& attribute_name,
- const Writer& attribute_value_writer,
- int indent)
- : out_(out),
- tag_(tag),
- indent_(indent),
- opening_tag_finished_(false),
- one_line_(true) {
- out << std::string(indent, ' ') << '<' << tag;
- out << ' ' << attribute_name << "=\"";
- attribute_value_writer(out);
- out << '\"';
-}
-
-template <class Writer>
-std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement(
- const std::string& tag,
- const std::string& attribute_name,
- const Writer& attribute_value_writer) {
- StartContent(true);
- return std::make_unique<XmlElementWriter>(
- out_, tag, attribute_name, attribute_value_writer, indent_ + 2);
-}
-
-std::string XmlEscape(const std::string& value);
-
-#endif // TOOLS_GN_XML_ELEMENT_WRITER_H_
diff --git a/gn/tools/gn/xml_element_writer_unittest.cc b/gn/tools/gn/xml_element_writer_unittest.cc
deleted file mode 100644
index 1b9eb13aba4..00000000000
--- a/gn/tools/gn/xml_element_writer_unittest.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 The Chromium 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 "tools/gn/xml_element_writer.h"
-
-#include <sstream>
-
-#include "util/test/test.h"
-
-namespace {
-
-class MockValueWriter {
- public:
- explicit MockValueWriter(const std::string& value) : value_(value) {}
- void operator()(std::ostream& out) const { out << value_; }
-
- private:
- std::string value_;
-};
-
-} // namespace
-
-TEST(XmlElementWriter, EmptyElement) {
- std::ostringstream out;
- { XmlElementWriter writer(out, "foo", XmlAttributes()); }
- EXPECT_EQ("<foo />\n", out.str());
-
- std::ostringstream out_attr;
- {
- XmlElementWriter writer(out_attr, "foo",
- XmlAttributes("bar", "abc").add("baz", "123"));
- }
- EXPECT_EQ("<foo bar=\"abc\" baz=\"123\" />\n", out_attr.str());
-
- std::ostringstream out_indent;
- {
- XmlElementWriter writer(out_indent, "foo", XmlAttributes("bar", "baz"), 2);
- }
- EXPECT_EQ(" <foo bar=\"baz\" />\n", out_indent.str());
-
- std::ostringstream out_writer;
- {
- XmlElementWriter writer(out_writer, "foo", "bar", MockValueWriter("baz"),
- 2);
- }
- EXPECT_EQ(" <foo bar=\"baz\" />\n", out_writer.str());
-}
-
-TEST(XmlElementWriter, ElementWithText) {
- std::ostringstream out;
- {
- XmlElementWriter writer(out, "foo", XmlAttributes("bar", "baz"));
- writer.Text("Hello world!");
- }
- EXPECT_EQ("<foo bar=\"baz\">Hello world!</foo>\n", out.str());
-}
-
-TEST(XmlElementWriter, SubElements) {
- std::ostringstream out;
- {
- XmlElementWriter writer(out, "root", XmlAttributes("aaa", "000"));
- writer.SubElement("foo", XmlAttributes());
- writer.SubElement("bar", XmlAttributes("bbb", "111"))->Text("hello");
- writer.SubElement("baz", "ccc", MockValueWriter("222"))
- ->SubElement("grandchild");
- }
- std::string expected =
- "<root aaa=\"000\">\n"
- " <foo />\n"
- " <bar bbb=\"111\">hello</bar>\n"
- " <baz ccc=\"222\">\n"
- " <grandchild />\n"
- " </baz>\n"
- "</root>\n";
- EXPECT_EQ(expected, out.str());
-}
-
-TEST(XmlElementWriter, StartContent) {
- std::ostringstream out;
- {
- XmlElementWriter writer(out, "foo", XmlAttributes("bar", "baz"));
- writer.StartContent(false) << "Hello world!";
- }
- EXPECT_EQ("<foo bar=\"baz\">Hello world!</foo>\n", out.str());
-}
-
-TEST(XmlElementWriter, TestXmlEscape) {
- std::string input = "\r \n \t & < > \"";
- std::string output = XmlEscape(input);
- std::string expected = "&#13; &#10; &#9; &amp; &lt; &gt; &quot;";
- EXPECT_EQ(expected, output);
-}
diff --git a/gn/util/build_config.h b/gn/util/build_config.h
deleted file mode 100644
index addd7cfb081..00000000000
--- a/gn/util/build_config.h
+++ /dev/null
@@ -1,196 +0,0 @@
-// 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 adds defines about the platform we're currently building on.
-// Operating System:
-// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) /
-// OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI
-// OS_CHROMEOS is set by the build system
-// Compiler:
-// COMPILER_MSVC / COMPILER_GCC
-// Processor:
-// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
-// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
-
-#ifndef BUILD_BUILD_CONFIG_H_
-#define BUILD_BUILD_CONFIG_H_
-
-// A set of macros to use for platform detection.
-#if defined(__native_client__)
-// __native_client__ must be first, so that other OS_ defines are not set.
-#define OS_NACL 1
-// OS_NACL comes in two sandboxing technology flavors, SFI or Non-SFI.
-// PNaCl toolchain defines __native_client_nonsfi__ macro in Non-SFI build
-// mode, while it does not in SFI build mode.
-#if defined(__native_client_nonsfi__)
-#define OS_NACL_NONSFI
-#else
-#define OS_NACL_SFI
-#endif
-#elif defined(ANDROID)
-#define OS_ANDROID 1
-#elif defined(__APPLE__)
-// only include TargetConditions after testing ANDROID as some android builds
-// on mac don't have this header available and it's not needed unless the target
-// is really mac/ios.
-#include <TargetConditionals.h>
-#define OS_MACOSX 1
-#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-#define OS_IOS 1
-#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
-#elif defined(__linux__)
-#define OS_LINUX 1
-// include a system header to pull in features.h for glibc/uclibc macros.
-#include <unistd.h>
-#if defined(__GLIBC__) && !defined(__UCLIBC__)
-// we really are using glibc, not uClibc pretending to be glibc
-#define LIBC_GLIBC 1
-#endif
-#elif defined(_WIN32)
-#define OS_WIN 1
-#elif defined(__Fuchsia__)
-#define OS_FUCHSIA 1
-#elif defined(__FreeBSD__)
-#define OS_FREEBSD 1
-#elif defined(__NetBSD__)
-#define OS_NETBSD 1
-#elif defined(__OpenBSD__)
-#define OS_OPENBSD 1
-#elif defined(__sun)
-#define OS_SOLARIS 1
-#elif defined(__QNXNTO__)
-#define OS_QNX 1
-#elif defined(_AIX)
-#define OS_AIX 1
-#elif defined(__asmjs__)
-#define OS_ASMJS
-#else
-#error Please add support for your platform in build_config.h
-#endif
-// NOTE: Adding a new port? Please follow
-// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
-
-// For access to standard BSD features, use OS_BSD instead of a
-// more specific macro.
-#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)
-#define OS_BSD 1
-#endif
-
-// For access to standard POSIXish features, use OS_POSIX instead of a
-// more specific macro.
-#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \
- defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \
- defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || \
- defined(OS_QNX) || defined(OS_SOLARIS)
-#define OS_POSIX 1
-#endif
-
-// Use tcmalloc
-#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \
- !defined(NO_TCMALLOC)
-#define USE_TCMALLOC 1
-#endif
-
-// Compiler detection.
-#if defined(__GNUC__)
-#define COMPILER_GCC 1
-#elif defined(_MSC_VER)
-#define COMPILER_MSVC 1
-#else
-#error Please add support for your compiler in build_config.h
-#endif
-
-// Processor architecture detection. For more info on what's defined, see:
-// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
-// http://www.agner.org/optimize/calling_conventions.pdf
-// or with gcc, run: "echo | gcc -E -dM -"
-#if defined(_M_X64) || defined(__x86_64__)
-#define ARCH_CPU_X86_FAMILY 1
-#define ARCH_CPU_X86_64 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(_M_IX86) || defined(__i386__)
-#define ARCH_CPU_X86_FAMILY 1
-#define ARCH_CPU_X86 1
-#define ARCH_CPU_32_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(__s390x__)
-#define ARCH_CPU_S390_FAMILY 1
-#define ARCH_CPU_S390X 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_BIG_ENDIAN 1
-#elif defined(__s390__)
-#define ARCH_CPU_S390_FAMILY 1
-#define ARCH_CPU_S390 1
-#define ARCH_CPU_31_BITS 1
-#define ARCH_CPU_BIG_ENDIAN 1
-#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__)
-#define ARCH_CPU_PPC64_FAMILY 1
-#define ARCH_CPU_PPC64 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_BIG_ENDIAN 1
-#elif defined(__PPC64__)
-#define ARCH_CPU_PPC64_FAMILY 1
-#define ARCH_CPU_PPC64 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(__ARMEL__)
-#define ARCH_CPU_ARM_FAMILY 1
-#define ARCH_CPU_ARMEL 1
-#define ARCH_CPU_32_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(__aarch64__)
-#define ARCH_CPU_ARM_FAMILY 1
-#define ARCH_CPU_ARM64 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(__pnacl__) || defined(__asmjs__)
-#define ARCH_CPU_32_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#elif defined(__MIPSEL__)
-#if defined(__LP64__)
-#define ARCH_CPU_MIPS_FAMILY 1
-#define ARCH_CPU_MIPS64EL 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#else
-#define ARCH_CPU_MIPS_FAMILY 1
-#define ARCH_CPU_MIPSEL 1
-#define ARCH_CPU_32_BITS 1
-#define ARCH_CPU_LITTLE_ENDIAN 1
-#endif
-#elif defined(__MIPSEB__)
-#if defined(__LP64__)
-#define ARCH_CPU_MIPS_FAMILY 1
-#define ARCH_CPU_MIPS64 1
-#define ARCH_CPU_64_BITS 1
-#define ARCH_CPU_BIG_ENDIAN 1
-#else
-#define ARCH_CPU_MIPS_FAMILY 1
-#define ARCH_CPU_MIPS 1
-#define ARCH_CPU_32_BITS 1
-#define ARCH_CPU_BIG_ENDIAN 1
-#endif
-#else
-#error Please add support for your architecture in build_config.h
-#endif
-
-// Type detection for wchar_t.
-#if defined(OS_WIN)
-#define WCHAR_T_IS_UTF16
-#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
- (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
-#define WCHAR_T_IS_UTF32
-#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
- (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
-// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
-// compile in this mode (in particular, Chrome doesn't). This is intended for
-// other projects using base who manage their own dependencies and make sure
-// short wchar works for them.
-#define WCHAR_T_IS_UTF16
-#else
-#error Please add support for your compiler in build_config.h
-#endif
-
-#endif // BUILD_BUILD_CONFIG_H_
diff --git a/gn/util/exe_path.cc b/gn/util/exe_path.cc
deleted file mode 100644
index 3c9c9deb35b..00000000000
--- a/gn/util/exe_path.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2018 The Chromium 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 "util/exe_path.h"
-
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "util/build_config.h"
-
-#if defined(OS_MACOSX)
-#include <mach-o/dyld.h>
-#elif defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_FREEBSD)
-#include <limits.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#endif
-
-#if defined(OS_MACOSX)
-
-base::FilePath GetExePath() {
- // Executable path can have relative references ("..") depending on
- // how the app was launched.
- uint32_t executable_length = 0;
- _NSGetExecutablePath(NULL, &executable_length);
- DCHECK_GT(executable_length, 1u);
- std::string executable_path;
- int rv = _NSGetExecutablePath(
- base::WriteInto(&executable_path, executable_length), &executable_length);
- DCHECK_EQ(rv, 0);
-
- // _NSGetExecutablePath may return paths containing ./ or ../ which makes
- // FilePath::DirName() work incorrectly, convert it to absolute path so that
- // paths such as DIR_SOURCE_ROOT can work, since we expect absolute paths to
- // be returned here.
- return base::MakeAbsoluteFilePath(base::FilePath(executable_path));
-}
-
-#elif defined(OS_WIN)
-
-base::FilePath GetExePath() {
- wchar_t system_buffer[MAX_PATH];
- system_buffer[0] = 0;
- if (GetModuleFileName(NULL, system_buffer, MAX_PATH) == 0) {
- return base::FilePath();
- }
- return base::FilePath(system_buffer);
-}
-
-#elif defined(OS_FREEBSD)
-
-base::FilePath GetExePath() {
- int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
- char buf[PATH_MAX];
- size_t buf_size = PATH_MAX;
- if (sysctl(mib, 4, buf, &buf_size, nullptr, 0) == -1) {
- return base::FilePath();
- }
- return base::FilePath(buf);
-}
-
-#else
-
-base::FilePath GetExePath() {
- base::FilePath result;
- const char kProcSelfExe[] = "/proc/self/exe";
- if (!ReadSymbolicLink(base::FilePath(kProcSelfExe), &result)) {
- NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
- return base::FilePath();
- }
- return result;
-}
-
-#endif
diff --git a/gn/util/msg_loop.cc b/gn/util/msg_loop.cc
deleted file mode 100644
index dbb92cf4485..00000000000
--- a/gn/util/msg_loop.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2018 The Chromium 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 "util/msg_loop.h"
-
-#include "base/logging.h"
-
-namespace {
-
-thread_local MsgLoop* g_current;
-}
-
-MsgLoop::MsgLoop() {
- DCHECK(g_current == nullptr);
- g_current = this;
-}
-
-MsgLoop::~MsgLoop() {
- DCHECK(g_current == this);
- g_current = nullptr;
-}
-
-void MsgLoop::Run() {
- while (!should_quit_) {
- Task task;
- {
- std::unique_lock<std::mutex> queue_lock(queue_mutex_);
- notifier_.wait(queue_lock, [this]() {
- return (!task_queue_.empty()) || should_quit_;
- });
-
- if (should_quit_)
- return;
-
- task = std::move(task_queue_.front());
- task_queue_.pop();
- }
-
- std::move(task).Run();
- }
-}
-
-void MsgLoop::PostQuit() {
- PostTask(
- base::BindOnce([](MsgLoop* self) { self->should_quit_ = true; }, this));
-}
-
-void MsgLoop::PostTask(Task work) {
- {
- std::unique_lock<std::mutex> queue_lock(queue_mutex_);
- task_queue_.emplace(std::move(work));
- }
-
- notifier_.notify_one();
-}
-
-void MsgLoop::RunUntilIdleForTesting() {
- for (bool done = false; !done;) {
- Task task;
- {
- std::unique_lock<std::mutex> queue_lock(queue_mutex_);
- task = std::move(task_queue_.front());
- task_queue_.pop();
-
- if (task_queue_.empty())
- done = true;
- }
-
- std::move(task).Run();
- }
-}
-
-MsgLoop* MsgLoop::Current() {
- return g_current;
-}
diff --git a/gn/util/msg_loop.h b/gn/util/msg_loop.h
deleted file mode 100644
index 267d2a9f681..00000000000
--- a/gn/util/msg_loop.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2018 The Chromium 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 UTIL_RUN_LOOP_H_
-#define UTIL_RUN_LOOP_H_
-
-#include "base/macros.h"
-#include "util/task.h"
-
-#include <condition_variable>
-#include <mutex>
-#include <queue>
-
-class MsgLoop {
- public:
- MsgLoop();
- ~MsgLoop();
-
- // Blocks until PostQuit() is called, processing work items posted via
- void Run();
-
- // Schedules Run() to exit, but will not happen until other outstanding tasks
- // complete. Can be called from any thread.
- void PostQuit();
-
- // Posts a work item to this queue. All items will be run on the thread from
- // which Run() was called. Can be called from any thread.
- void PostTask(Task task);
-
- // Run()s until the queue is empty. Should only be used (carefully) in tests.
- void RunUntilIdleForTesting();
-
- // Gets the MsgLoop for the thread from which it's called, or nullptr if
- // there's no MsgLoop for the current thread.
- static MsgLoop* Current();
-
- private:
- std::mutex queue_mutex_;
- std::queue<Task> task_queue_;
- std::condition_variable notifier_;
- bool should_quit_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(MsgLoop);
-};
-
-#endif // UTIL_RUN_LOOP_H_
diff --git a/gn/util/semaphore.cc b/gn/util/semaphore.cc
deleted file mode 100644
index d5c82be0738..00000000000
--- a/gn/util/semaphore.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2013 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Based on
-// https://cs.chromium.org/chromium/src/v8/src/base/platform/semaphore.cc
-
-#include "util/semaphore.h"
-
-#include "base/logging.h"
-
-#if defined(OS_MACOSX)
-
-Semaphore::Semaphore(int count) {
- kern_return_t result = semaphore_create(mach_task_self(), &native_handle_,
- SYNC_POLICY_FIFO, count);
- DCHECK_EQ(KERN_SUCCESS, result);
-}
-
-Semaphore::~Semaphore() {
- kern_return_t result = semaphore_destroy(mach_task_self(), native_handle_);
- DCHECK_EQ(KERN_SUCCESS, result);
-}
-
-void Semaphore::Signal() {
- kern_return_t result = semaphore_signal(native_handle_);
- DCHECK_EQ(KERN_SUCCESS, result);
-}
-
-void Semaphore::Wait() {
- while (true) {
- kern_return_t result = semaphore_wait(native_handle_);
- if (result == KERN_SUCCESS)
- return; // Semaphore was signalled.
- DCHECK_EQ(KERN_ABORTED, result);
- }
-}
-
-#elif defined(OS_POSIX)
-
-Semaphore::Semaphore(int count) {
- DCHECK_GE(count, 0);
- int result = sem_init(&native_handle_, 0, count);
- DCHECK_EQ(0, result);
-}
-
-Semaphore::~Semaphore() {
- int result = sem_destroy(&native_handle_);
- DCHECK_EQ(0, result);
-}
-
-void Semaphore::Signal() {
- int result = sem_post(&native_handle_);
- // This check may fail with <libc-2.21, which we use on the try bots, if the
- // semaphore is destroyed while sem_post is still executed. A work around is
- // to extend the lifetime of the semaphore.
- CHECK_EQ(0, result);
-}
-
-void Semaphore::Wait() {
- while (true) {
- int result = sem_wait(&native_handle_);
- if (result == 0)
- return; // Semaphore was signalled.
- // Signal caused spurious wakeup.
- DCHECK_EQ(-1, result);
- DCHECK_EQ(EINTR, errno);
- }
-}
-
-#elif defined(OS_WIN)
-
-Semaphore::Semaphore(int count) {
- DCHECK_GE(count, 0);
- native_handle_ = ::CreateSemaphoreA(nullptr, count, 0x7FFFFFFF, nullptr);
- DCHECK(native_handle_);
-}
-
-Semaphore::~Semaphore() {
- BOOL result = CloseHandle(native_handle_);
- DCHECK(result);
-}
-
-void Semaphore::Signal() {
- LONG dummy;
- BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy);
- DCHECK(result);
-}
-
-void Semaphore::Wait() {
- DWORD result = WaitForSingleObject(native_handle_, INFINITE);
- DCHECK(result == WAIT_OBJECT_0);
-}
-
-#endif
diff --git a/gn/util/semaphore.h b/gn/util/semaphore.h
deleted file mode 100644
index 92a1df6453f..00000000000
--- a/gn/util/semaphore.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2013 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Based on
-// https://cs.chromium.org/chromium/src/v8/src/base/platform/semaphore.h
-
-#ifndef UTIL_SEMAPHORE_H_
-#define UTIL_SEMAPHORE_H_
-
-#include "base/macros.h"
-#include "util/build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_MACOSX)
-#include <mach/mach.h>
-#elif defined(OS_POSIX)
-#include <semaphore.h>
-#else
-#error Port.
-#endif
-
-class Semaphore {
- public:
- explicit Semaphore(int count);
- ~Semaphore();
-
- // Increments the semaphore counter.
- void Signal();
-
- // Decrements the semaphore counter if it is positive, or blocks until it
- // becomes positive and then decrements the counter.
- void Wait();
-
-#if defined(OS_MACOSX)
- typedef semaphore_t NativeHandle;
-#elif defined(OS_POSIX)
- typedef sem_t NativeHandle;
-#elif defined(OS_WIN)
- typedef HANDLE NativeHandle;
-#endif
-
- NativeHandle& native_handle() { return native_handle_; }
- const NativeHandle& native_handle() const { return native_handle_; }
-
- private:
- NativeHandle native_handle_;
-
- DISALLOW_COPY_AND_ASSIGN(Semaphore);
-};
-
-#endif // UTIL_SEMAPHORE_H_
diff --git a/gn/util/sys_info.cc b/gn/util/sys_info.cc
deleted file mode 100644
index 5fefc9b4393..00000000000
--- a/gn/util/sys_info.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2018 The Chromium 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 "util/sys_info.h"
-
-#include "base/logging.h"
-#include "util/build_config.h"
-
-#if defined(OS_POSIX)
-#include <sys/utsname.h>
-#include <unistd.h>
-#endif
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-std::string OperatingSystemArchitecture() {
-#if defined(OS_POSIX)
- struct utsname info;
- if (uname(&info) < 0) {
- NOTREACHED();
- return std::string();
- }
- std::string arch(info.machine);
- if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") {
- arch = "x86";
- } else if (arch == "amd64") {
- arch = "x86_64";
- } else if (std::string(info.sysname) == "AIX") {
- arch = "ppc64";
- }
- return arch;
-#elif defined(OS_WIN)
- SYSTEM_INFO system_info = {};
- ::GetNativeSystemInfo(&system_info);
- switch (system_info.wProcessorArchitecture) {
- case PROCESSOR_ARCHITECTURE_INTEL:
- return "x86";
- case PROCESSOR_ARCHITECTURE_AMD64:
- return "x86_64";
- case PROCESSOR_ARCHITECTURE_IA64:
- return "ia64";
- }
- return std::string();
-#else
-#error
-#endif
-}
-
-int NumberOfProcessors() {
-#if defined(OS_POSIX)
- // sysconf returns the number of "logical" (not "physical") processors on both
- // Mac and Linux. So we get the number of max available "logical" processors.
- //
- // Note that the number of "currently online" processors may be fewer than the
- // returned value of NumberOfProcessors(). On some platforms, the kernel may
- // make some processors offline intermittently, to save power when system
- // loading is low.
- //
- // One common use case that needs to know the processor count is to create
- // optimal number of threads for optimization. It should make plan according
- // to the number of "max available" processors instead of "currently online"
- // ones. The kernel should be smart enough to make all processors online when
- // it has sufficient number of threads waiting to run.
- long res = sysconf(_SC_NPROCESSORS_CONF);
- if (res == -1) {
- NOTREACHED();
- return 1;
- }
-
- return static_cast<int>(res);
-#elif defined(OS_WIN)
- return ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
-#else
-#error
-#endif
-}
diff --git a/gn/util/task.h b/gn/util/task.h
deleted file mode 100644
index 278ff133985..00000000000
--- a/gn/util/task.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2018 The Chromium 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 UTIL_TASK_H_
-#define UTIL_TASK_H_
-
-#include "base/bind.h"
-#include "base/callback.h"
-
-using Task = base::OnceClosure;
-
-#endif // UTIL_TASK_H_
diff --git a/gn/util/test/gn_test.cc b/gn/util/test/gn_test.cc
deleted file mode 100644
index 6b2e26d6b96..00000000000
--- a/gn/util/test/gn_test.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2013 Google Inc. All Rights Reserved.
-//
-// 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.
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "base/command_line.h"
-#include "util/build_config.h"
-#include "util/test/test.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#else
-#include <unistd.h>
-#endif
-
-namespace testing {
-Test* g_current_test;
-} // namespace testing
-
-struct RegisteredTest {
- testing::Test* (*factory)();
- const char* name;
- bool should_run;
-};
-
-// This can't be a vector because tests call RegisterTest from static
-// initializers and the order static initializers run it isn't specified. So
-// the vector constructor isn't guaranteed to run before all of the
-// RegisterTest() calls.
-static RegisteredTest tests[10000];
-static int ntests;
-
-void RegisterTest(testing::Test* (*factory)(), const char* name) {
- tests[ntests].factory = factory;
- tests[ntests++].name = name;
-}
-
-namespace {
-
-bool PatternMatchesString(const char* pattern, const char* str) {
- switch (*pattern) {
- case '\0':
- case '-':
- return *str == '\0';
- case '*':
- return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
- PatternMatchesString(pattern + 1, str);
- default:
- return *pattern == *str && PatternMatchesString(pattern + 1, str + 1);
- }
-}
-
-bool TestMatchesFilter(const char* test, const char* filter) {
- // Split --gtest_filter at '-' into positive and negative filters.
- const char* const dash = strchr(filter, '-');
- const char* pos =
- dash == filter ? "*" : filter; // Treat '-test1' as '*-test1'
- const char* neg = dash ? dash + 1 : "";
- return PatternMatchesString(pos, test) && !PatternMatchesString(neg, test);
-}
-
-#if defined(OS_WIN)
-struct ScopedEnableVTEscapeProcessing {
- ScopedEnableVTEscapeProcessing() {
- console_ = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- if (GetConsoleScreenBufferInfo(console_, &csbi) &&
- GetConsoleMode(console_, &original_mode_)) {
- SetConsoleMode(console_, original_mode_ |
- ENABLE_VIRTUAL_TERMINAL_PROCESSING |
- DISABLE_NEWLINE_AUTO_RETURN);
- } else {
- console_ = INVALID_HANDLE_VALUE;
- }
- }
-
- ~ScopedEnableVTEscapeProcessing() {
- if (is_valid())
- SetConsoleMode(console_, original_mode_);
- }
-
- bool is_valid() const { return console_ != INVALID_HANDLE_VALUE; }
-
- HANDLE console_;
- DWORD original_mode_;
-};
-#endif
-
-} // namespace
-
-int main(int argc, char** argv) {
- base::CommandLine::Init(argc, argv);
-
-#if defined(OS_WIN)
- ScopedEnableVTEscapeProcessing enable_vt_processing;
-#endif
- setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
-
- int tests_started = 0;
-
- const char* test_filter = "*";
- for (int i = 1; i < argc; ++i) {
- const char kTestFilterPrefix[] = "--gtest_filter=";
- if (strncmp(argv[i], kTestFilterPrefix, strlen(kTestFilterPrefix)) == 0) {
- test_filter = &argv[i][strlen(kTestFilterPrefix)];
- }
- }
-
- int num_active_tests = 0;
- for (int i = 0; i < ntests; i++) {
- tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter);
- if (tests[i].should_run) {
- ++num_active_tests;
- }
- }
-
- const char* prefix = "";
- const char* suffix = "\n";
-#if defined(OS_WIN)
- if (enable_vt_processing.is_valid())
-#else
- if (isatty(1))
-#endif
- {
- prefix = "\r";
- suffix = "\x1B[K";
- }
- bool passed = true;
- for (int i = 0; i < ntests; i++) {
- if (!tests[i].should_run)
- continue;
-
- ++tests_started;
- testing::Test* test = tests[i].factory();
- printf("%s[%d/%d] %s%s", prefix, tests_started, num_active_tests,
- tests[i].name, suffix);
- test->SetUp();
- test->Run();
- test->TearDown();
- if (test->Failed())
- passed = false;
- delete test;
- }
-
- printf("\n%s\n", passed ? "PASSED" : "FAILED");
- fflush(stdout);
- return passed ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/gn/util/test/test.h b/gn/util/test/test.h
deleted file mode 100644
index b5539d7fa4d..00000000000
--- a/gn/util/test/test.h
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2018 The Chromium 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 UTIL_TEST_TEST_H_
-#define UTIL_TEST_TEST_H_
-
-#include <string.h>
-
-#include <sstream>
-#include <string>
-
-// This is a minimal googletest-like testing framework. It's originally derived
-// from Ninja's src/test.h. You might prefer that one if you have different
-// tradeoffs (in particular, if you don't need to stream message to assertion
-// failures, Ninja's is a bit simpler.)
-namespace testing {
-
-class Test {
- public:
- Test() : failed_(false) {}
- virtual ~Test() {}
- virtual void SetUp() {}
- virtual void TearDown() {}
- virtual void Run() = 0;
-
- bool Failed() const { return failed_; }
-
- private:
- friend class TestResult;
-
- bool failed_;
- int assertion_failures_;
-};
-
-extern testing::Test* g_current_test;
-
-class TestResult {
- public:
- TestResult(bool condition, const char* error)
- : condition_(condition), error_(error) {
- if (!condition)
- g_current_test->failed_ = true;
- }
-
- operator bool() const { return condition_; }
- const char* error() const { return error_; }
-
- private:
- bool condition_;
- const char* error_;
-};
-
-class Message {
- public:
- Message() {}
- ~Message() { printf("%s\n\n", ss_.str().c_str()); }
-
- template <typename T>
- inline Message& operator<<(const T& val) {
- ss_ << val;
- return *this;
- }
-
- private:
- std::stringstream ss_;
-};
-
-class AssertHelper {
- public:
- AssertHelper(const char* file, int line, const TestResult& test_result)
- : file_(file), line_(line), error_(test_result.error()) {}
-
- void operator=(const Message& message) const {
- printf("\n*** FAILURE %s:%d: %s\n", file_, line_, error_);
- }
-
- private:
- const char* file_;
- int line_;
- const char* error_;
-};
-
-} // namespace testing
-
-void RegisterTest(testing::Test* (*)(), const char*);
-
-#define TEST_F_(x, y, name) \
- struct y : public x { \
- static testing::Test* Create() { return testing::g_current_test = new y; } \
- virtual void Run(); \
- }; \
- struct Register##y { \
- Register##y() { RegisterTest(y::Create, name); } \
- }; \
- Register##y g_register_##y; \
- void y::Run()
-
-#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y)
-#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y)
-
-#define FRIEND_TEST(x, y) friend class x##y
-
-// Some compilers emit a warning if nested "if" statements are followed by an
-// "else" statement and braces are not used to explicitly disambiguate the
-// "else" binding. This leads to problems with code like:
-//
-// if (something)
-// ASSERT_TRUE(condition) << "Some message";
-#define TEST_AMBIGUOUS_ELSE_BLOCKER_ \
- switch (0) \
- case 0: \
- default:
-
-#define TEST_ASSERT_(expression, on_failure) \
- TEST_AMBIGUOUS_ELSE_BLOCKER_ \
- if (const ::testing::TestResult test_result = (expression)) \
- ; \
- else \
- on_failure(test_result)
-
-#define TEST_NONFATAL_FAILURE_(message) \
- ::testing::AssertHelper(__FILE__, __LINE__, message) = ::testing::Message()
-
-#define TEST_FATAL_FAILURE_(message) \
- return ::testing::AssertHelper(__FILE__, __LINE__, message) = \
- ::testing::Message()
-
-#define EXPECT_EQ(a, b) \
- TEST_ASSERT_(::testing::TestResult(a == b, #a " == " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_NE(a, b) \
- TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_LT(a, b) \
- TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_GT(a, b) \
- TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_LE(a, b) \
- TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_GE(a, b) \
- TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_TRUE(a) \
- TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_FALSE(a) \
- TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \
- TEST_NONFATAL_FAILURE_)
-
-#define EXPECT_STREQ(a, b) \
- TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \
- TEST_NONFATAL_FAILURE_)
-
-#define ASSERT_EQ(a, b) \
- TEST_ASSERT_(::testing::TestResult(a == b, #a " == " #b), TEST_FATAL_FAILURE_)
-
-#define ASSERT_NE(a, b) \
- TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), TEST_FATAL_FAILURE_)
-
-#define ASSERT_LT(a, b) \
- TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), TEST_FATAL_FAILURE_)
-
-#define ASSERT_GT(a, b) \
- TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), TEST_FATAL_FAILURE_)
-
-#define ASSERT_LE(a, b) \
- TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), TEST_FATAL_FAILURE_)
-
-#define ASSERT_GE(a, b) \
- TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), TEST_FATAL_FAILURE_)
-
-#define ASSERT_TRUE(a) \
- TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \
- TEST_FATAL_FAILURE_)
-
-#define ASSERT_FALSE(a) \
- TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \
- TEST_FATAL_FAILURE_)
-
-#define ASSERT_STREQ(a, b) \
- TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \
- TEST_FATAL_FAILURE_)
-
-#endif // UTIL_TEST_TEST_H_
diff --git a/gn/util/ticks.cc b/gn/util/ticks.cc
deleted file mode 100644
index f26b5d80960..00000000000
--- a/gn/util/ticks.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2018 The Chromium 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 "ticks.h"
-
-#include "base/logging.h"
-#include "build_config.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#elif defined(OS_MACOSX)
-#include <mach/mach_time.h>
-#elif defined(OS_POSIX)
-#include <time.h>
-#else
-#error Port.
-#endif
-
-namespace {
-
-bool g_initialized;
-
-#if defined(OS_WIN)
-LARGE_INTEGER g_frequency;
-LARGE_INTEGER g_start;
-#elif defined(OS_MACOSX)
-mach_timebase_info_data_t g_timebase;
-uint64_t g_start;
-#elif defined(OS_POSIX)
-uint64_t g_start;
-#else
-#error Port.
-#endif
-
-constexpr uint64_t kNano = 1'000'000'000;
-
-void Init() {
- DCHECK(!g_initialized);
-
-#if defined(OS_WIN)
- QueryPerformanceFrequency(&g_frequency);
- QueryPerformanceCounter(&g_start);
-#elif defined(OS_MACOSX)
- mach_timebase_info(&g_timebase);
- g_start = (mach_absolute_time() * g_timebase.numer) / g_timebase.denom;
-#elif defined(OS_POSIX)
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- g_start = static_cast<uint64_t>(ts.tv_sec) * kNano +
- static_cast<uint64_t>(ts.tv_nsec);
-#else
-#error Port.
-#endif
-
- g_initialized = true;
-}
-
-} // namespace
-
-Ticks TicksNow() {
- static bool initialized = []() {
- Init();
- return true;
- }();
- DCHECK(initialized);
-
- Ticks now;
-
-#if defined(OS_WIN)
- LARGE_INTEGER t;
- QueryPerformanceCounter(&t);
- now = ((t.QuadPart - g_start.QuadPart) * kNano) / g_frequency.QuadPart;
-#elif defined(OS_MACOSX)
- now =
- ((mach_absolute_time() * g_timebase.numer) / g_timebase.denom) - g_start;
-#elif defined(OS_POSIX)
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- now = (static_cast<uint64_t>(ts.tv_sec) * kNano +
- static_cast<uint64_t>(ts.tv_nsec)) -
- g_start;
-#else
-#error Port.
-#endif
-
- return now;
-}
-
-TickDelta TicksDelta(Ticks new_ticks, Ticks old_ticks) {
- DCHECK(new_ticks >= old_ticks);
- return TickDelta(new_ticks - old_ticks);
-}
diff --git a/gn/util/worker_pool.cc b/gn/util/worker_pool.cc
deleted file mode 100644
index 60bcfbf099e..00000000000
--- a/gn/util/worker_pool.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2018 The Chromium 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 "util/worker_pool.h"
-
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "tools/gn/switches.h"
-#include "util/build_config.h"
-#include "util/sys_info.h"
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif
-
-namespace {
-
-#if defined(OS_WIN)
-class ProcessorGroupSetter {
- public:
- void SetProcessorGroup(std::thread* thread);
-
- private:
- int group_ = 0;
- GROUP_AFFINITY group_affinity_;
- int num_available_cores_in_group_ = ::GetActiveProcessorCount(group_) / 2;
- const int num_groups_ = ::GetActiveProcessorGroupCount();
-};
-
-void ProcessorGroupSetter::SetProcessorGroup(std::thread* thread) {
- if (num_groups_ <= 1)
- return;
-
- const HANDLE thread_handle = thread->native_handle();
- ::GetThreadGroupAffinity(thread_handle, &group_affinity_);
- group_affinity_.Group = group_;
- const bool success =
- ::SetThreadGroupAffinity(thread_handle, &group_affinity_, nullptr);
- DCHECK(success);
-
- // Move to next group once one thread has been assigned per core in |group_|.
- num_available_cores_in_group_--;
- if (num_available_cores_in_group_ <= 0) {
- group_++;
- if (group_ >= num_groups_) {
- group_ = 0;
- }
- num_available_cores_in_group_ = ::GetActiveProcessorCount(group_) / 2;
- }
-}
-#endif
-
-int GetThreadCount() {
- std::string thread_count =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kThreads);
-
- // See if an override was specified on the command line.
- int result;
- if (!thread_count.empty() && base::StringToInt(thread_count, &result) &&
- result >= 1) {
- return result;
- }
-
- // Base the default number of worker threads on number of cores in the
- // system. When building large projects, the speed can be limited by how fast
- // the main thread can dispatch work and connect the dependency graph. If
- // there are too many worker threads, the main thread can be starved and it
- // will run slower overall.
- //
- // One less worker thread than the number of physical CPUs seems to be a
- // good value, both theoretically and experimentally. But always use at
- // least some workers to prevent us from being too sensitive to I/O latency
- // on low-end systems.
- //
- // The minimum thread count is based on measuring the optimal threads for the
- // Chrome build on a several-year-old 4-core MacBook.
- // Almost all CPUs now are hyperthreaded.
- int num_cores = NumberOfProcessors() / 2;
- return std::max(num_cores - 1, 8);
-}
-
-} // namespace
-
-WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {}
-
-WorkerPool::WorkerPool(size_t thread_count) : should_stop_processing_(false) {
-#if defined(OS_WIN)
- ProcessorGroupSetter processor_group_setter;
-#endif
-
- threads_.reserve(thread_count);
- for (size_t i = 0; i < thread_count; ++i) {
- threads_.emplace_back([this]() { Worker(); });
-
-#if defined(OS_WIN)
- // Set thread processor group. This is needed for systems with more than 64
- // logical processors, wherein available processors are divided into groups,
- // and applications that need to use more than one group's processors must
- // manually assign their threads to groups.
- processor_group_setter.SetProcessorGroup(&threads_.back());
-#endif
- }
-}
-
-WorkerPool::~WorkerPool() {
- {
- std::unique_lock<std::mutex> queue_lock(queue_mutex_);
- should_stop_processing_ = true;
- }
-
- pool_notifier_.notify_all();
-
- for (auto& task_thread : threads_) {
- task_thread.join();
- }
-}
-
-void WorkerPool::PostTask(Task work) {
- {
- std::unique_lock<std::mutex> queue_lock(queue_mutex_);
- CHECK(!should_stop_processing_);
- task_queue_.emplace(std::move(work));
- }
-
- pool_notifier_.notify_one();
-}
-
-void WorkerPool::Worker() {
- for (;;) {
- Task task;
-
- {
- std::unique_lock<std::mutex> queue_lock(queue_mutex_);
-
- pool_notifier_.wait(queue_lock, [this]() {
- return (!task_queue_.empty()) || should_stop_processing_;
- });
-
- if (should_stop_processing_ && task_queue_.empty())
- return;
-
- task = std::move(task_queue_.front());
- task_queue_.pop();
- }
-
- std::move(task).Run();
- }
-}
diff --git a/gn/util/worker_pool.h b/gn/util/worker_pool.h
deleted file mode 100644
index d0616449097..00000000000
--- a/gn/util/worker_pool.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 The Chromium 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 UTIL_WORKER_POOL_H_
-#define UTIL_WORKER_POOL_H_
-
-#include <condition_variable>
-#include <mutex>
-#include <queue>
-#include <thread>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "util/task.h"
-
-class WorkerPool {
- public:
- WorkerPool();
- WorkerPool(size_t thread_count);
- ~WorkerPool();
-
- void PostTask(Task work);
-
- private:
- void Worker();
-
- std::vector<std::thread> threads_;
- std::queue<base::OnceClosure> task_queue_;
- std::mutex queue_mutex_;
- std::condition_variable_any pool_notifier_;
- bool should_stop_processing_;
-
- DISALLOW_COPY_AND_ASSIGN(WorkerPool);
-};
-
-#endif // UTIL_WORKER_POOL_H_