summaryrefslogtreecommitdiff
path: root/gn
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 15:28:34 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:54:51 +0000
commit2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch)
treeeb17888e8531aa6ee5e85721bd553b832a7e5156 /gn
parentb014812705fc80bff0a5c120dfcef88f349816dc (diff)
downloadqtwebengine-chromium-2a19c63448c84c1805fb1a585c3651318bb86ca7.tar.gz
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'gn')
-rw-r--r--gn/.clang-format2
-rw-r--r--gn/.editorconfig3
-rw-r--r--gn/.style.yapf2
-rw-r--r--gn/AUTHORS42
-rw-r--r--gn/LICENSE27
-rw-r--r--gn/OWNERS4
-rw-r--r--gn/README.md53
-rw-r--r--gn/base/atomic_ref_count.h67
-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/circular_deque.h1111
-rw-r--r--gn/base/containers/flat_map.h362
-rw-r--r--gn/base/containers/flat_tree.h1004
-rw-r--r--gn/base/containers/queue.h23
-rw-r--r--gn/base/containers/span.h453
-rw-r--r--gn/base/containers/stack.h23
-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.cc25
-rw-r--r--gn/base/files/file_enumerator.h171
-rw-r--r--gn/base/files/file_enumerator_posix.cc170
-rw-r--r--gn/base/files/file_enumerator_win.cc192
-rw-r--r--gn/base/files/file_path.cc690
-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.cc269
-rw-r--r--gn/base/files/file_util.h468
-rw-r--r--gn/base/files/file_util_linux.cc63
-rw-r--r--gn/base/files/file_util_mac.mm64
-rw-r--r--gn/base/files/file_util_posix.cc977
-rw-r--r--gn/base/files/file_util_win.cc896
-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/files/scoped_file.h59
-rw-r--r--gn/base/files/scoped_temp_dir.cc97
-rw-r--r--gn/base/files/scoped_temp_dir.h70
-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.cc331
-rw-r--r--gn/base/logging.h956
-rw-r--r--gn/base/mac/bundle_locations.h65
-rw-r--r--gn/base/mac/bundle_locations.mm83
-rw-r--r--gn/base/mac/foundation_util.h406
-rw-r--r--gn/base/mac/foundation_util.mm475
-rw-r--r--gn/base/mac/mac_logging.h74
-rw-r--r--gn/base/mac/mac_logging.mm42
-rw-r--r--gn/base/mac/scoped_cftyperef.h48
-rw-r--r--gn/base/mac/scoped_typeref.h138
-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/free_deleter.h23
-rw-r--r--gn/base/memory/ptr_util.h23
-rw-r--r--gn/base/memory/raw_scoped_refptr_mismatch_checker.h52
-rw-r--r--gn/base/memory/ref_counted.cc35
-rw-r--r--gn/base/memory/ref_counted.h317
-rw-r--r--gn/base/memory/scoped_policy.h25
-rw-r--r--gn/base/memory/scoped_refptr.h333
-rw-r--r--gn/base/memory/weak_ptr.cc75
-rw-r--r--gn/base/memory/weak_ptr.h378
-rw-r--r--gn/base/numerics/checked_math.h393
-rw-r--r--gn/base/numerics/checked_math_impl.h567
-rw-r--r--gn/base/numerics/clamped_math.h262
-rw-r--r--gn/base/numerics/clamped_math_impl.h341
-rw-r--r--gn/base/numerics/math_constants.h15
-rw-r--r--gn/base/numerics/ranges.h27
-rw-r--r--gn/base/numerics/safe_conversions.h344
-rw-r--r--gn/base/numerics/safe_conversions_impl.h848
-rw-r--r--gn/base/numerics/safe_math.h12
-rw-r--r--gn/base/numerics/safe_math_clang_gcc_impl.h157
-rw-r--r--gn/base/numerics/safe_math_shared_impl.h237
-rw-r--r--gn/base/optional.h922
-rw-r--r--gn/base/posix/eintr_wrapper.h71
-rw-r--r--gn/base/posix/file_descriptor_shuffle.cc101
-rw-r--r--gn/base/posix/file_descriptor_shuffle.h82
-rw-r--r--gn/base/posix/safe_strerror.cc126
-rw-r--r--gn/base/posix/safe_strerror.h42
-rw-r--r--gn/base/scoped_clear_errno.h32
-rw-r--r--gn/base/scoped_generic.h185
-rw-r--r--gn/base/sha1.cc213
-rw-r--r--gn/base/sha1.h28
-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/stringize_macros.h31
-rw-r--r--gn/base/strings/stringprintf.cc190
-rw-r--r--gn/base/strings/stringprintf.h60
-rw-r--r--gn/base/strings/sys_string_conversions.h82
-rw-r--r--gn/base/strings/sys_string_conversions_mac.mm176
-rw-r--r--gn/base/strings/sys_string_conversions_posix.cc163
-rw-r--r--gn/base/strings/sys_string_conversions_win.cc71
-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/sys_byteorder.h139
-rw-r--r--gn/base/template_util.h156
-rw-r--r--gn/base/third_party/icu/LICENSE76
-rw-r--r--gn/base/third_party/icu/README.chromium17
-rw-r--r--gn/base/third_party/icu/icu_utf.cc129
-rw-r--r--gn/base/third_party/icu/icu_utf.h464
-rw-r--r--gn/base/timer/elapsed_timer.cc25
-rw-r--r--gn/base/timer/elapsed_timer.h32
-rw-r--r--gn/base/value_iterators.cc228
-rw-r--r--gn/base/value_iterators.h191
-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/scoped_process_information.cc109
-rw-r--r--gn/base/win/scoped_process_information.h74
-rw-r--r--gn/base/win/windows_types.h254
-rw-r--r--gn/build/build_aix.ninja.template16
-rw-r--r--gn/build/build_linux.ninja.template19
-rw-r--r--gn/build/build_mac.ninja.template19
-rw-r--r--gn/build/build_win.ninja.template25
-rwxr-xr-xgn/build/full_test.py81
-rwxr-xr-xgn/build/gen.py732
-rw-r--r--gn/docs/cross_compiles.md125
-rw-r--r--gn/docs/faq.md52
-rw-r--r--gn/docs/hacking.md23
-rw-r--r--gn/docs/language.md540
-rw-r--r--gn/docs/quick_start.md365
-rw-r--r--gn/docs/reference.md6653
-rw-r--r--gn/docs/standalone.md44
-rw-r--r--gn/docs/style_guide.md286
-rw-r--r--gn/docs/update_binaries.md5
-rw-r--r--gn/infra/README.recipes.md60
-rw-r--r--gn/infra/config/cq.cfg27
-rw-r--r--gn/infra/config/recipes.cfg12
-rw-r--r--gn/infra/config/refs.cfg8
-rw-r--r--gn/infra/recipe_modules/windows_sdk/__init__.py27
-rw-r--r--gn/infra/recipe_modules/windows_sdk/api.py113
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json23
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json23
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json108
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.py25
-rwxr-xr-xgn/infra/recipes.py218
-rw-r--r--gn/infra/recipes/gn.expected/ci.json375
-rw-r--r--gn/infra/recipes/gn.expected/cipd_exists.json254
-rw-r--r--gn/infra/recipes/gn.expected/cipd_register.json270
-rw-r--r--gn/infra/recipes/gn.expected/cq.json380
-rw-r--r--gn/infra/recipes/gn.py178
-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.cc488
-rw-r--r--gn/tools/gn/analyzer.h104
-rw-r--r--gn/tools/gn/analyzer_unittest.cc594
-rw-r--r--gn/tools/gn/args.cc404
-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.cc181
-rw-r--r--gn/tools/gn/binary_target_generator.h40
-rw-r--r--gn/tools/gn/build_settings.cc76
-rw-r--r--gn/tools/gn/build_settings.h140
-rw-r--r--gn/tools/gn/builder.cc605
-rw-r--r--gn/tools/gn/builder.h146
-rw-r--r--gn/tools/gn/builder_record.cc73
-rw-r--r--gn/tools/gn/builder_record.h111
-rw-r--r--gn/tools/gn/builder_unittest.cc244
-rw-r--r--gn/tools/gn/bundle_data.cc169
-rw-r--r--gn/tools/gn/bundle_data.h197
-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.cc66
-rw-r--r--gn/tools/gn/bundle_file_rule.h51
-rw-r--r--gn/tools/gn/c_include_iterator.cc171
-rw-r--r--gn/tools/gn/c_include_iterator.h57
-rw-r--r--gn/tools/gn/c_include_iterator_unittest.cc161
-rw-r--r--gn/tools/gn/command_analyze.cc133
-rw-r--r--gn/tools/gn/command_args.cc506
-rw-r--r--gn/tools/gn/command_check.cc256
-rw-r--r--gn/tools/gn/command_clean.cc129
-rw-r--r--gn/tools/gn/command_desc.cc516
-rw-r--r--gn/tools/gn/command_format.cc1109
-rw-r--r--gn/tools/gn/command_format.h26
-rw-r--r--gn/tools/gn/command_format_unittest.cc108
-rw-r--r--gn/tools/gn/command_gen.cc517
-rw-r--r--gn/tools/gn/command_help.cc323
-rw-r--r--gn/tools/gn/command_ls.cc107
-rw-r--r--gn/tools/gn/command_path.cc412
-rw-r--r--gn/tools/gn/command_refs.cc498
-rw-r--r--gn/tools/gn/commands.cc558
-rw-r--r--gn/tools/gn/commands.h201
-rw-r--r--gn/tools/gn/compile_commands_writer.cc286
-rw-r--r--gn/tools/gn/compile_commands_writer.h26
-rw-r--r--gn/tools/gn/compile_commands_writer_unittest.cc590
-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.cc46
-rw-r--r--gn/tools/gn/config_values.h90
-rw-r--r--gn/tools/gn/config_values_extractors.cc34
-rw-r--r--gn/tools/gn/config_values_extractors.h103
-rw-r--r--gn/tools/gn/config_values_extractors_unittest.cc137
-rw-r--r--gn/tools/gn/config_values_generator.cc123
-rw-r--r--gn/tools/gn/config_values_generator.h45
-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.cc729
-rw-r--r--gn/tools/gn/desc_builder.h27
-rw-r--r--gn/tools/gn/eclipse_writer.cc171
-rw-r--r--gn/tools/gn/eclipse_writer.h67
-rw-r--r--gn/tools/gn/err.cc192
-rw-r--r--gn/tools/gn/err.h99
-rw-r--r--gn/tools/gn/escape.cc202
-rw-r--r--gn/tools/gn/escape.h79
-rw-r--r--gn/tools/gn/escape_unittest.cc64
-rw-r--r--gn/tools/gn/example/.gn2
-rw-r--r--gn/tools/gn/example/BUILD.gn30
-rw-r--r--gn/tools/gn/example/README.txt4
-rw-r--r--gn/tools/gn/example/build/BUILD.gn19
-rw-r--r--gn/tools/gn/example/build/BUILDCONFIG.gn38
-rw-r--r--gn/tools/gn/example/build/toolchain/BUILD.gn86
-rw-r--r--gn/tools/gn/example/hello.cc13
-rw-r--r--gn/tools/gn/example/hello_shared.cc9
-rw-r--r--gn/tools/gn/example/hello_shared.h32
-rw-r--r--gn/tools/gn/example/hello_static.cc9
-rw-r--r--gn/tools/gn/example/hello_static.h10
-rw-r--r--gn/tools/gn/exec_process.cc280
-rw-r--r--gn/tools/gn/exec_process.h36
-rw-r--r--gn/tools/gn/exec_process_unittest.cc122
-rw-r--r--gn/tools/gn/filesystem_utils.cc1059
-rw-r--r--gn/tools/gn/filesystem_utils.h305
-rw-r--r--gn/tools/gn/filesystem_utils_unittest.cc846
-rw-r--r--gn/tools/gn/format_test_data/001.gn2
-rw-r--r--gn/tools/gn/format_test_data/001.golden3
-rw-r--r--gn/tools/gn/format_test_data/002.gn6
-rw-r--r--gn/tools/gn/format_test_data/002.golden6
-rw-r--r--gn/tools/gn/format_test_data/003.gn10
-rw-r--r--gn/tools/gn/format_test_data/003.golden10
-rw-r--r--gn/tools/gn/format_test_data/004.gn10
-rw-r--r--gn/tools/gn/format_test_data/004.golden13
-rw-r--r--gn/tools/gn/format_test_data/005.gn5
-rw-r--r--gn/tools/gn/format_test_data/005.golden5
-rw-r--r--gn/tools/gn/format_test_data/006.gn9
-rw-r--r--gn/tools/gn/format_test_data/006.golden5
-rw-r--r--gn/tools/gn/format_test_data/007.gn9
-rw-r--r--gn/tools/gn/format_test_data/007.golden11
-rw-r--r--gn/tools/gn/format_test_data/008.gn1
-rw-r--r--gn/tools/gn/format_test_data/008.golden5
-rw-r--r--gn/tools/gn/format_test_data/009.gn2
-rw-r--r--gn/tools/gn/format_test_data/009.golden9
-rw-r--r--gn/tools/gn/format_test_data/010.gn2
-rw-r--r--gn/tools/gn/format_test_data/010.golden9
-rw-r--r--gn/tools/gn/format_test_data/011.gn4
-rw-r--r--gn/tools/gn/format_test_data/011.golden13
-rw-r--r--gn/tools/gn/format_test_data/012.gn16
-rw-r--r--gn/tools/gn/format_test_data/012.golden22
-rw-r--r--gn/tools/gn/format_test_data/013.gn7
-rw-r--r--gn/tools/gn/format_test_data/013.golden7
-rw-r--r--gn/tools/gn/format_test_data/014.gn6
-rw-r--r--gn/tools/gn/format_test_data/014.golden5
-rw-r--r--gn/tools/gn/format_test_data/015.gn4
-rw-r--r--gn/tools/gn/format_test_data/015.golden6
-rw-r--r--gn/tools/gn/format_test_data/016.gn1
-rw-r--r--gn/tools/gn/format_test_data/016.golden1
-rw-r--r--gn/tools/gn/format_test_data/017.gn15
-rw-r--r--gn/tools/gn/format_test_data/017.golden16
-rw-r--r--gn/tools/gn/format_test_data/018.gn3
-rw-r--r--gn/tools/gn/format_test_data/018.golden3
-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/020.gn5
-rw-r--r--gn/tools/gn/format_test_data/020.golden5
-rw-r--r--gn/tools/gn/format_test_data/021.gn33
-rw-r--r--gn/tools/gn/format_test_data/021.golden61
-rw-r--r--gn/tools/gn/format_test_data/022.gn6
-rw-r--r--gn/tools/gn/format_test_data/022.golden6
-rw-r--r--gn/tools/gn/format_test_data/023.gn38
-rw-r--r--gn/tools/gn/format_test_data/023.golden88
-rw-r--r--gn/tools/gn/format_test_data/024.gn1
-rw-r--r--gn/tools/gn/format_test_data/024.golden2
-rw-r--r--gn/tools/gn/format_test_data/025.gn5
-rw-r--r--gn/tools/gn/format_test_data/025.golden9
-rw-r--r--gn/tools/gn/format_test_data/026.gn6
-rw-r--r--gn/tools/gn/format_test_data/026.golden7
-rw-r--r--gn/tools/gn/format_test_data/027.gn3
-rw-r--r--gn/tools/gn/format_test_data/027.golden5
-rw-r--r--gn/tools/gn/format_test_data/028.gn9
-rw-r--r--gn/tools/gn/format_test_data/028.golden7
-rw-r--r--gn/tools/gn/format_test_data/029.gn9
-rw-r--r--gn/tools/gn/format_test_data/029.golden9
-rw-r--r--gn/tools/gn/format_test_data/030.gn12
-rw-r--r--gn/tools/gn/format_test_data/030.golden12
-rw-r--r--gn/tools/gn/format_test_data/031.gn8
-rw-r--r--gn/tools/gn/format_test_data/031.golden8
-rw-r--r--gn/tools/gn/format_test_data/032.gn6
-rw-r--r--gn/tools/gn/format_test_data/032.golden7
-rw-r--r--gn/tools/gn/format_test_data/033.gn8
-rw-r--r--gn/tools/gn/format_test_data/033.golden8
-rw-r--r--gn/tools/gn/format_test_data/034.gn13
-rw-r--r--gn/tools/gn/format_test_data/035.gn1
-rw-r--r--gn/tools/gn/format_test_data/035.golden1
-rw-r--r--gn/tools/gn/format_test_data/036.gn9
-rw-r--r--gn/tools/gn/format_test_data/036.golden9
-rw-r--r--gn/tools/gn/format_test_data/037.gn5
-rw-r--r--gn/tools/gn/format_test_data/037.golden6
-rw-r--r--gn/tools/gn/format_test_data/038.gn4
-rw-r--r--gn/tools/gn/format_test_data/038.golden3
-rw-r--r--gn/tools/gn/format_test_data/039.gn6
-rw-r--r--gn/tools/gn/format_test_data/039.golden4
-rw-r--r--gn/tools/gn/format_test_data/040.gn9
-rw-r--r--gn/tools/gn/format_test_data/041.gn12
-rw-r--r--gn/tools/gn/format_test_data/041.golden12
-rw-r--r--gn/tools/gn/format_test_data/042.gn44
-rw-r--r--gn/tools/gn/format_test_data/042.golden110
-rw-r--r--gn/tools/gn/format_test_data/043.gn6
-rw-r--r--gn/tools/gn/format_test_data/043.golden7
-rw-r--r--gn/tools/gn/format_test_data/044.gn10
-rw-r--r--gn/tools/gn/format_test_data/044.golden11
-rw-r--r--gn/tools/gn/format_test_data/045.gn10
-rw-r--r--gn/tools/gn/format_test_data/045.golden14
-rw-r--r--gn/tools/gn/format_test_data/046.gn22
-rw-r--r--gn/tools/gn/format_test_data/046.golden19
-rw-r--r--gn/tools/gn/format_test_data/047.gn7
-rw-r--r--gn/tools/gn/format_test_data/047.golden10
-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/049.gn14
-rw-r--r--gn/tools/gn/format_test_data/050.gn10
-rw-r--r--gn/tools/gn/format_test_data/050.golden27
-rw-r--r--gn/tools/gn/format_test_data/051.gn6
-rw-r--r--gn/tools/gn/format_test_data/051.golden7
-rw-r--r--gn/tools/gn/format_test_data/052.gn11
-rw-r--r--gn/tools/gn/format_test_data/052.golden12
-rw-r--r--gn/tools/gn/format_test_data/053.gn7
-rw-r--r--gn/tools/gn/format_test_data/053.golden8
-rw-r--r--gn/tools/gn/format_test_data/054.gn7
-rw-r--r--gn/tools/gn/format_test_data/054.golden8
-rw-r--r--gn/tools/gn/format_test_data/055.gn10
-rw-r--r--gn/tools/gn/format_test_data/055.golden11
-rw-r--r--gn/tools/gn/format_test_data/056.gn45
-rw-r--r--gn/tools/gn/format_test_data/056.golden45
-rw-r--r--gn/tools/gn/format_test_data/057.gn24
-rw-r--r--gn/tools/gn/format_test_data/057.golden24
-rw-r--r--gn/tools/gn/format_test_data/058.gn2
-rw-r--r--gn/tools/gn/format_test_data/058.golden2
-rw-r--r--gn/tools/gn/format_test_data/059.gn10
-rw-r--r--gn/tools/gn/format_test_data/059.golden11
-rw-r--r--gn/tools/gn/format_test_data/060.gn2
-rw-r--r--gn/tools/gn/format_test_data/060.golden2
-rw-r--r--gn/tools/gn/format_test_data/061.gn9
-rw-r--r--gn/tools/gn/format_test_data/061.golden9
-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.gn3
-rw-r--r--gn/tools/gn/format_test_data/064.golden5
-rw-r--r--gn/tools/gn/format_test_data/065.gn4
-rw-r--r--gn/tools/gn/format_test_data/065.golden8
-rw-r--r--gn/tools/gn/format_test_data/066.gn30
-rw-r--r--gn/tools/gn/format_test_data/066.golden28
-rw-r--r--gn/tools/gn/format_test_data/067.gn8
-rw-r--r--gn/tools/gn/format_test_data/067.golden17
-rw-r--r--gn/tools/gn/format_test_data/068.gn3
-rw-r--r--gn/tools/gn/format_test_data/068.golden3
-rw-r--r--gn/tools/gn/format_test_data/069.gn3
-rw-r--r--gn/tools/gn/format_test_data/069.golden5
-rw-r--r--gn/tools/gn/format_test_data/070.gn15
-rw-r--r--gn/tools/gn/format_test_data/070.golden14
-rw-r--r--gn/tools/gn/function_exec_script.cc266
-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.cc242
-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.cc142
-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.cc180
-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.cc1121
-rw-r--r--gn/tools/gn/function_toolchain_unittest.cc60
-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.cc1354
-rw-r--r--gn/tools/gn/functions.h520
-rw-r--r--gn/tools/gn/functions_target.cc773
-rw-r--r--gn/tools/gn/functions_target_unittest.cc124
-rw-r--r--gn/tools/gn/functions_unittest.cc204
-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.cc602
-rw-r--r--gn/tools/gn/header_checker.h195
-rw-r--r--gn/tools/gn/header_checker_unittest.cc382
-rw-r--r--gn/tools/gn/import_manager.cc159
-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.cc357
-rw-r--r--gn/tools/gn/input_conversion.h30
-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.h26
-rw-r--r--gn/tools/gn/label.cc329
-rw-r--r--gn/tools/gn/label.h126
-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.h116
-rw-r--r--gn/tools/gn/label_unittest.cc95
-rw-r--r--gn/tools/gn/lib_file.cc30
-rw-r--r--gn/tools/gn/lib_file.h58
-rw-r--r--gn/tools/gn/loader.cc426
-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/location.h60
-rw-r--r--gn/tools/gn/misc/emacs/gn-mode.el191
-rwxr-xr-xgn/tools/gn/misc/help_as_html.py105
-rw-r--r--gn/tools/gn/misc/tm/GN.tmLanguage102
-rw-r--r--gn/tools/gn/misc/tm/GN.tmPreferences22
-rw-r--r--gn/tools/gn/misc/vim/README.chromium5
-rw-r--r--gn/tools/gn/misc/vim/autoload/gn.vim26
-rw-r--r--gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim27
-rw-r--r--gn/tools/gn/misc/vim/ftplugin/gn.vim12
-rw-r--r--gn/tools/gn/misc/vim/gn-format.py59
-rw-r--r--gn/tools/gn/misc/vim/syntax/gn.vim84
-rw-r--r--gn/tools/gn/ninja_action_target_writer.cc238
-rw-r--r--gn/tools/gn/ninja_action_target_writer.h62
-rw-r--r--gn/tools/gn/ninja_action_target_writer_unittest.cc477
-rw-r--r--gn/tools/gn/ninja_binary_target_writer.cc890
-rw-r--r--gn/tools/gn/ninja_binary_target_writer.h141
-rw-r--r--gn/tools/gn/ninja_binary_target_writer_unittest.cc1155
-rw-r--r--gn/tools/gn/ninja_build_writer.cc596
-rw-r--r--gn/tools/gn/ninja_build_writer.h71
-rw-r--r--gn/tools/gn/ninja_build_writer_unittest.cc152
-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.cc53
-rw-r--r--gn/tools/gn/ninja_copy_target_writer.cc117
-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.cc337
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer.h71
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc456
-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_target_command_util.cc179
-rw-r--r--gn/tools/gn/ninja_target_command_util.h84
-rw-r--r--gn/tools/gn/ninja_target_writer.cc324
-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.cc126
-rw-r--r--gn/tools/gn/ninja_toolchain_writer.h58
-rw-r--r--gn/tools/gn/ninja_toolchain_writer_unittest.cc25
-rw-r--r--gn/tools/gn/ninja_utils.cc30
-rw-r--r--gn/tools/gn/ninja_utils.h25
-rw-r--r--gn/tools/gn/ninja_writer.cc52
-rw-r--r--gn/tools/gn/ninja_writer.h49
-rw-r--r--gn/tools/gn/operators.cc784
-rw-r--r--gn/tools/gn/operators.h25
-rw-r--r--gn/tools/gn/operators_unittest.cc383
-rw-r--r--gn/tools/gn/ordered_set.h63
-rw-r--r--gn/tools/gn/output_conversion.cc174
-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.cc44
-rw-r--r--gn/tools/gn/output_file.h66
-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.cc873
-rw-r--r--gn/tools/gn/parse_tree.h546
-rw-r--r--gn/tools/gn/parse_tree_unittest.cc255
-rw-r--r--gn/tools/gn/parser.cc886
-rw-r--r--gn/tools/gn/parser.h150
-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.cc176
-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/scheduler.cc186
-rw-r--r--gn/tools/gn/scheduler.h149
-rw-r--r--gn/tools/gn/scope.cc576
-rw-r--r--gn/tools/gn/scope.h392
-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.cc34
-rw-r--r--gn/tools/gn/settings.h116
-rw-r--r--gn/tools/gn/setup.cc823
-rw-r--r--gn/tools/gn/setup.h171
-rw-r--r--gn/tools/gn/source_dir.cc158
-rw-r--r--gn/tools/gn/source_dir.h155
-rw-r--r--gn/tools/gn/source_dir_unittest.cc187
-rw-r--r--gn/tools/gn/source_file.cc64
-rw-r--r--gn/tools/gn/source_file.h103
-rw-r--r--gn/tools/gn/source_file_type.cc33
-rw-r--r--gn/tools/gn/source_file_type.h31
-rw-r--r--gn/tools/gn/source_file_unittest.cc19
-rw-r--r--gn/tools/gn/standard_out.cc332
-rw-r--r--gn/tools/gn/standard_out.h41
-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.cc143
-rw-r--r--gn/tools/gn/substitution_pattern.h77
-rw-r--r--gn/tools/gn/substitution_pattern_unittest.cc49
-rw-r--r--gn/tools/gn/substitution_type.cc248
-rw-r--r--gn/tools/gn/substitution_type.h143
-rw-r--r--gn/tools/gn/substitution_writer.cc596
-rw-r--r--gn/tools/gn/substitution_writer.h237
-rw-r--r--gn/tools/gn/substitution_writer_unittest.cc323
-rw-r--r--gn/tools/gn/switches.cc280
-rw-r--r--gn/tools/gn/switches.h106
-rw-r--r--gn/tools/gn/target.cc875
-rw-r--r--gn/tools/gn/target.h396
-rw-r--r--gn/tools/gn/target_generator.cc391
-rw-r--r--gn/tools/gn/target_generator.h84
-rw-r--r--gn/tools/gn/target_unittest.cc1094
-rw-r--r--gn/tools/gn/template.cc123
-rw-r--r--gn/tools/gn/template.h67
-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.cc228
-rw-r--r--gn/tools/gn/test_with_scope.h120
-rw-r--r--gn/tools/gn/token.cc22
-rw-r--r--gn/tools/gn/token.h85
-rw-r--r--gn/tools/gn/tokenizer.cc407
-rw-r--r--gn/tools/gn/tokenizer.h90
-rw-r--r--gn/tools/gn/tokenizer_unittest.cc205
-rw-r--r--gn/tools/gn/tool.cc28
-rw-r--r--gn/tools/gn/tool.h203
-rw-r--r--gn/tools/gn/toolchain.cc210
-rw-r--r--gn/tools/gn/toolchain.h154
-rw-r--r--gn/tools/gn/trace.cc332
-rw-r--r--gn/tools/gn/trace.h104
-rw-r--r--gn/tools/gn/unique_vector.h174
-rw-r--r--gn/tools/gn/unique_vector_unittest.cc44
-rw-r--r--gn/tools/gn/value.cc210
-rw-r--r--gn/tools/gn/value.h135
-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.cc43
-rw-r--r--gn/tools/gn/variables.cc2124
-rw-r--r--gn/tools/gn/variables.h332
-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.h50
-rw-r--r--gn/tools/gn/visual_studio_utils_unittest.cc102
-rw-r--r--gn/tools/gn/visual_studio_writer.cc911
-rw-r--r--gn/tools/gn/visual_studio_writer.h167
-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.cc667
-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/auto_reset_event.h53
-rw-r--r--gn/util/build_config.h196
-rw-r--r--gn/util/exe_path.cc61
-rw-r--r--gn/util/exe_path.h12
-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.cc81
-rw-r--r--gn/util/sys_info.h13
-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/ticks.h45
-rw-r--r--gn/util/worker_pool.cc97
-rw-r--r--gn/util/worker_pool.h37
649 files changed, 114835 insertions, 0 deletions
diff --git a/gn/.clang-format b/gn/.clang-format
new file mode 100644
index 00000000000..b17a52a80ac
--- /dev/null
+++ b/gn/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: Chromium
+Standard: Cpp11
diff --git a/gn/.editorconfig b/gn/.editorconfig
new file mode 100644
index 00000000000..2491fdfae55
--- /dev/null
+++ b/gn/.editorconfig
@@ -0,0 +1,3 @@
+[*.py]
+indent_style = space
+indent_size = 2
diff --git a/gn/.style.yapf b/gn/.style.yapf
new file mode 100644
index 00000000000..de0c6a70f38
--- /dev/null
+++ b/gn/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = chromium
diff --git a/gn/AUTHORS b/gn/AUTHORS
new file mode 100644
index 00000000000..ca2667131c3
--- /dev/null
+++ b/gn/AUTHORS
@@ -0,0 +1,42 @@
+# Names should be added to this file with this pattern:
+#
+# For individuals:
+# Name <email address>
+#
+# For organizations:
+# Organization <fnmatch pattern>
+#
+# See python fnmatch module documentation for more information.
+
+Google Inc. <*@google.com>
+IBM Inc. <*@*.ibm.com>
+Loongson Technology Corporation Limited. <*@loongson.cn>
+MIPS Technologies, Inc. <*@mips.com>
+Opera Software ASA <*@opera.com>
+The Chromium Authors <*@chromium.org>
+Vewd Software AS <*@vewd.com>
+Vivaldi Technologies AS <*@vivaldi.com>
+Yandex LLC <*@yandex-team.ru>
+
+Alexis Menard <alexis.menard@intel.com>
+Andrew Boyarshin <andrew.boyarshin@gmail.com>
+Anuj Kumar Sharma <anujk.sharma@samsung.com>
+DanCraft99 <simputest@gmail.com>
+Gergely Nagy <ngg@ngg.hu>
+Ilia K <ki.stfu@gmail.com>
+Ivan Naydonov <samogot@gmail.com>
+Julien Brianceau <jbriance@cisco.com>
+Kal Conley <kcconley@gmail.com>
+Kamil Rytarowski <krytarowski@gmail.com>
+Martijn Croonen <martijn@martijnc.be>
+Matej Knopp <matej.knopp@gmail.com>
+Michael Gilbert <floppymaster@gmail.com>
+Milko Leporis <milko.leporis@imgtec.com>
+Mohan Reddy <mohan.reddy@samsung.com>
+Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
+Riku Voipio <riku.voipio@linaro.org>
+Saikrishna Arcot <saiarcot895@gmail.com>
+Tim Niederhausen <tim@rnc-ag.de>
+Tomas Popela <tomas.popela@gmail.com>
+Tripta Gupta <tripta.g@samsung.com>
+Yuriy Taraday <yorik.sar@gmail.com>
diff --git a/gn/LICENSE b/gn/LICENSE
new file mode 100644
index 00000000000..a32e00ce6be
--- /dev/null
+++ b/gn/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/gn/OWNERS b/gn/OWNERS
new file mode 100644
index 00000000000..0200965cc3c
--- /dev/null
+++ b/gn/OWNERS
@@ -0,0 +1,4 @@
+brettw@chromium.org
+dpranke@chromium.org
+phosek@chromium.org
+scottmg@chromium.org
diff --git a/gn/README.md b/gn/README.md
new file mode 100644
index 00000000000..5c98d1f4879
--- /dev/null
+++ b/gn/README.md
@@ -0,0 +1,53 @@
+# GN
+
+GN is a meta-build system that generates build files for
+[Ninja](https://ninja-build.org).
+
+## Getting started
+
+ git clone https://gn.googlesource.com/gn
+ cd gn
+ python build/gen.py
+ ninja -C out
+
+On Windows, it is expected that `cl.exe`, `link.exe`, and `lib.exe` can be found
+in `PATH`, so you'll want to run from a Visual Studio command prompt, or
+similar.
+
+On Linux and Mac, the default compiler is `clang++`, a recent version is
+expected to be found in `PATH`. This can be overridden by setting `CC`, `CXX`,
+and `AR`.
+
+## Sending patches
+
+GN uses [Gerrit](https://www.gerritcodereview.com/) for code review. The short
+version of how to patch is:
+
+ Register at https://gn-review.googlesource.com.
+
+ ... edit code ...
+ ninja -C out && out/gn_unittests
+
+Then, to upload a change for review:
+
+ git commit
+ git cl upload --gerrit
+
+When revising a change, use:
+
+ git commit --amend
+ git cl upload --gerrit
+
+which will add the new changes to the existing code review, rather than creating
+a new one.
+
+We ask that all contributors
+[sign Google's Contributor License Agreement](https://cla.developers.google.com/)
+(either individual or corporate as appropriate, select 'any other Google
+project').
+
+## Community
+
+You may ask questions and follow along w/ GN's development on Chromium's
+[gn-dev@](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev)
+Google Group.
diff --git a/gn/base/atomic_ref_count.h b/gn/base/atomic_ref_count.h
new file mode 100644
index 00000000000..3ffa017acd3
--- /dev/null
+++ b/gn/base/atomic_ref_count.h
@@ -0,0 +1,67 @@
+// 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 is a low level implementation of atomic semantics for reference
+// counting. Please use base/memory/ref_counted.h directly instead.
+
+#ifndef BASE_ATOMIC_REF_COUNT_H_
+#define BASE_ATOMIC_REF_COUNT_H_
+
+#include <atomic>
+
+namespace base {
+
+class AtomicRefCount {
+ public:
+ constexpr AtomicRefCount() : ref_count_(0) {}
+ explicit constexpr AtomicRefCount(int initial_value)
+ : ref_count_(initial_value) {}
+
+ // Increment a reference count.
+ void Increment() { Increment(1); }
+
+ // Increment a reference count by "increment", which must exceed 0.
+ void Increment(int increment) {
+ ref_count_.fetch_add(increment, std::memory_order_relaxed);
+ }
+
+ // Decrement a reference count, and return whether the result is non-zero.
+ // Insert barriers to ensure that state written before the reference count
+ // became zero will be visible to a thread that has just made the count zero.
+ bool Decrement() {
+ // TODO(jbroman): Technically this doesn't need to be an acquire operation
+ // unless the result is 1 (i.e., the ref count did indeed reach zero).
+ // However, there are toolchain issues that make that not work as well at
+ // present (notably TSAN doesn't like it).
+ return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
+ }
+
+ // Return whether the reference count is one. If the reference count is used
+ // in the conventional way, a refrerence count of 1 implies that the current
+ // thread owns the reference and no other thread shares it. This call
+ // performs the test for a reference count of one, and performs the memory
+ // barrier needed for the owning thread to act on the object, knowing that it
+ // has exclusive access to the object.
+ bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; }
+
+ // Return whether the reference count is zero. With conventional object
+ // referencing counting, the object will be destroyed, so the reference count
+ // should never be zero. Hence this is generally used for a debug check.
+ bool IsZero() const {
+ return ref_count_.load(std::memory_order_acquire) == 0;
+ }
+
+ // Returns the current reference count (with no barriers). This is subtle, and
+ // should be used only for debugging.
+ int SubtleRefCountForDebug() const {
+ return ref_count_.load(std::memory_order_relaxed);
+ }
+
+ private:
+ std::atomic_int ref_count_;
+};
+
+} // namespace base
+
+#endif // BASE_ATOMIC_REF_COUNT_H_
diff --git a/gn/base/bind.h b/gn/base/bind.h
new file mode 100644
index 00000000000..71df0fe952a
--- /dev/null
+++ b/gn/base/bind.h
@@ -0,0 +1,457 @@
+// 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
new file mode 100644
index 00000000000..0dc59e60c9a
--- /dev/null
+++ b/gn/base/bind_internal.h
@@ -0,0 +1,912 @@
+// 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
new file mode 100644
index 00000000000..1b84f403969
--- /dev/null
+++ b/gn/base/callback.h
@@ -0,0 +1,142 @@
+// 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
new file mode 100644
index 00000000000..f1851c4fbbf
--- /dev/null
+++ b/gn/base/callback_forward.h
@@ -0,0 +1,27 @@
+// 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
new file mode 100644
index 00000000000..c52d8afaba2
--- /dev/null
+++ b/gn/base/callback_internal.cc
@@ -0,0 +1,93 @@
+// 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
new file mode 100644
index 00000000000..7e5180f317a
--- /dev/null
+++ b/gn/base/callback_internal.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.
+
+// 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
new file mode 100644
index 00000000000..792d322fe1e
--- /dev/null
+++ b/gn/base/command_line.cc
@@ -0,0 +1,486 @@
+// 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
new file mode 100644
index 00000000000..01614576832
--- /dev/null
+++ b/gn/base/command_line.h
@@ -0,0 +1,255 @@
+// 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
new file mode 100644
index 00000000000..42241976483
--- /dev/null
+++ b/gn/base/compiler_specific.h
@@ -0,0 +1,231 @@
+// 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/circular_deque.h b/gn/base/containers/circular_deque.h
new file mode 100644
index 00000000000..bf42a958448
--- /dev/null
+++ b/gn/base/containers/circular_deque.h
@@ -0,0 +1,1111 @@
+// 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_CIRCULAR_DEQUE_H_
+#define BASE_CONTAINERS_CIRCULAR_DEQUE_H_
+
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "base/containers/vector_buffer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/template_util.h"
+
+// base::circular_deque is similar to std::deque. Unlike std::deque, the
+// storage is provided in a flat circular buffer conceptually similar to a
+// vector. The beginning and end will wrap around as necessary so that
+// pushes and pops will be constant time as long as a capacity expansion is
+// not required.
+//
+// The API should be identical to std::deque with the following differences:
+//
+// - ITERATORS ARE NOT STABLE. Mutating the container will invalidate all
+// iterators.
+//
+// - Insertions may resize the vector and so are not constant time (std::deque
+// guarantees constant time for insertions at the ends).
+//
+// - Container-wide comparisons are not implemented. If you want to compare
+// two containers, use an algorithm so the expensive iteration is explicit.
+//
+// If you want a similar container with only a queue API, use base::queue in
+// base/containers/queue.h.
+//
+// Constructors:
+// circular_deque();
+// circular_deque(size_t count);
+// circular_deque(size_t count, const T& value);
+// circular_deque(InputIterator first, InputIterator last);
+// circular_deque(const circular_deque&);
+// circular_deque(circular_deque&&);
+// circular_deque(std::initializer_list<value_type>);
+//
+// Assignment functions:
+// circular_deque& operator=(const circular_deque&);
+// circular_deque& operator=(circular_deque&&);
+// circular_deque& operator=(std::initializer_list<T>);
+// void assign(size_t count, const T& value);
+// void assign(InputIterator first, InputIterator last);
+// void assign(std::initializer_list<T> value);
+//
+// Random accessors:
+// T& at(size_t);
+// const T& at(size_t) const;
+// T& operator[](size_t);
+// const T& operator[](size_t) const;
+//
+// End accessors:
+// T& front();
+// const T& front() const;
+// T& back();
+// const T& back() const;
+//
+// Iterator functions:
+// 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;
+//
+// Memory management:
+// void reserve(size_t); // SEE IMPLEMENTATION FOR SOME GOTCHAS.
+// size_t capacity() const;
+// void shrink_to_fit();
+//
+// Size management:
+// void clear();
+// bool empty() const;
+// size_t size() const;
+// void resize(size_t);
+// void resize(size_t count, const T& value);
+//
+// Positional insert and erase:
+// void insert(const_iterator pos, size_type count, const T& value);
+// void insert(const_iterator pos,
+// InputIterator first, InputIterator last);
+// iterator insert(const_iterator pos, const T& value);
+// iterator insert(const_iterator pos, T&& value);
+// iterator emplace(const_iterator pos, Args&&... args);
+// iterator erase(const_iterator pos);
+// iterator erase(const_iterator first, const_iterator last);
+//
+// End insert and erase:
+// void push_front(const T&);
+// void push_front(T&&);
+// void push_back(const T&);
+// void push_back(T&&);
+// T& emplace_front(Args&&...);
+// T& emplace_back(Args&&...);
+// void pop_front();
+// void pop_back();
+//
+// General:
+// void swap(circular_deque&);
+
+namespace base {
+
+template <class T>
+class circular_deque;
+
+namespace internal {
+
+// Start allocating nonempty buffers with this many entries. This is the
+// external capacity so the internal buffer will be one larger (= 4) which is
+// more even for the allocator. See the descriptions of internal vs. external
+// capacity on the comment above the buffer_ variable below.
+constexpr size_t kCircularBufferInitialCapacity = 3;
+
+template <typename T>
+class circular_deque_const_iterator {
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = T;
+ using pointer = const T*;
+ using reference = const T&;
+ using iterator_category = std::random_access_iterator_tag;
+
+ circular_deque_const_iterator() : parent_deque_(nullptr), index_(0) {
+#if DCHECK_IS_ON()
+ created_generation_ = 0;
+#endif // DCHECK_IS_ON()
+ }
+
+ // Dereferencing.
+ const T& operator*() const {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndex(index_);
+ return parent_deque_->buffer_[index_];
+ }
+ const T* operator->() const {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndex(index_);
+ return &parent_deque_->buffer_[index_];
+ }
+ const value_type& operator[](difference_type i) const { return *(*this + i); }
+
+ // Increment and decrement.
+ circular_deque_const_iterator& operator++() {
+ Increment();
+ return *this;
+ }
+ circular_deque_const_iterator operator++(int) {
+ circular_deque_const_iterator ret = *this;
+ Increment();
+ return ret;
+ }
+ circular_deque_const_iterator& operator--() {
+ Decrement();
+ return *this;
+ }
+ circular_deque_const_iterator operator--(int) {
+ circular_deque_const_iterator ret = *this;
+ Decrement();
+ return ret;
+ }
+
+ // Random access mutation.
+ friend circular_deque_const_iterator operator+(
+ const circular_deque_const_iterator& iter,
+ difference_type offset) {
+ circular_deque_const_iterator ret = iter;
+ ret.Add(offset);
+ return ret;
+ }
+ circular_deque_const_iterator& operator+=(difference_type offset) {
+ Add(offset);
+ return *this;
+ }
+ friend circular_deque_const_iterator operator-(
+ const circular_deque_const_iterator& iter,
+ difference_type offset) {
+ circular_deque_const_iterator ret = iter;
+ ret.Add(-offset);
+ return ret;
+ }
+ circular_deque_const_iterator& operator-=(difference_type offset) {
+ Add(-offset);
+ return *this;
+ }
+
+ friend std::ptrdiff_t operator-(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.OffsetFromBegin() - rhs.OffsetFromBegin();
+ }
+
+ // Comparisons.
+ friend bool operator==(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.index_ == rhs.index_;
+ }
+ friend bool operator!=(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ return !(lhs == rhs);
+ }
+ friend bool operator<(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.OffsetFromBegin() < rhs.OffsetFromBegin();
+ }
+ friend bool operator<=(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ return !(lhs > rhs);
+ }
+ friend bool operator>(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.OffsetFromBegin() > rhs.OffsetFromBegin();
+ }
+ friend bool operator>=(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ return !(lhs < rhs);
+ }
+
+ protected:
+ friend class circular_deque<T>;
+
+ circular_deque_const_iterator(const circular_deque<T>* parent, size_t index)
+ : parent_deque_(parent), index_(index) {
+#if DCHECK_IS_ON()
+ created_generation_ = parent->generation_;
+#endif // DCHECK_IS_ON()
+ }
+
+ // Returns the offset from the beginning index of the buffer to the current
+ // item.
+ size_t OffsetFromBegin() const {
+ if (index_ >= parent_deque_->begin_)
+ return index_ - parent_deque_->begin_; // On the same side as begin.
+ return parent_deque_->buffer_.capacity() - parent_deque_->begin_ + index_;
+ }
+
+ // Most uses will be ++ and -- so use a simplified implementation.
+ void Increment() {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndex(index_);
+ index_++;
+ if (index_ == parent_deque_->buffer_.capacity())
+ index_ = 0;
+ }
+ void Decrement() {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndexOrEnd(index_);
+ if (index_ == 0)
+ index_ = parent_deque_->buffer_.capacity() - 1;
+ else
+ index_--;
+ }
+ void Add(difference_type delta) {
+ CheckUnstableUsage();
+#if DCHECK_IS_ON()
+ if (delta <= 0)
+ parent_deque_->CheckValidIndexOrEnd(index_);
+ else
+ parent_deque_->CheckValidIndex(index_);
+#endif
+ // It should be valid to add 0 to any iterator, even if the container is
+ // empty and the iterator points to end(). The modulo below will divide
+ // by 0 if the buffer capacity is empty, so it's important to check for
+ // this case explicitly.
+ if (delta == 0)
+ return;
+
+ difference_type new_offset = OffsetFromBegin() + delta;
+ DCHECK(new_offset >= 0 &&
+ new_offset <= static_cast<difference_type>(parent_deque_->size()));
+ index_ = (new_offset + parent_deque_->begin_) %
+ parent_deque_->buffer_.capacity();
+ }
+
+#if DCHECK_IS_ON()
+ void CheckUnstableUsage() const {
+ DCHECK(parent_deque_);
+ // Since circular_deque doesn't guarantee stability, any attempt to
+ // dereference this iterator after a mutation (i.e. the generation doesn't
+ // match the original) in the container is illegal.
+ DCHECK_EQ(created_generation_, parent_deque_->generation_)
+ << "circular_deque iterator dereferenced after mutation.";
+ }
+ void CheckComparable(const circular_deque_const_iterator& other) const {
+ DCHECK_EQ(parent_deque_, other.parent_deque_);
+ // Since circular_deque doesn't guarantee stability, two iterators that
+ // are compared must have been generated without mutating the container.
+ // If this fires, the container was mutated between generating the two
+ // iterators being compared.
+ DCHECK_EQ(created_generation_, other.created_generation_);
+ }
+#else
+ inline void CheckUnstableUsage() const {}
+ inline void CheckComparable(const circular_deque_const_iterator&) const {}
+#endif // DCHECK_IS_ON()
+
+ const circular_deque<T>* parent_deque_;
+ size_t index_;
+
+#if DCHECK_IS_ON()
+ // The generation of the parent deque when this iterator was created. The
+ // container will update the generation for every modification so we can
+ // test if the container was modified by comparing them.
+ uint64_t created_generation_;
+#endif // DCHECK_IS_ON()
+};
+
+template <typename T>
+class circular_deque_iterator : public circular_deque_const_iterator<T> {
+ using base = circular_deque_const_iterator<T>;
+
+ public:
+ friend class circular_deque<T>;
+
+ using difference_type = std::ptrdiff_t;
+ using value_type = T;
+ using pointer = T*;
+ using reference = T&;
+ using iterator_category = std::random_access_iterator_tag;
+
+ // Expose the base class' constructor.
+ circular_deque_iterator() : circular_deque_const_iterator<T>() {}
+
+ // Dereferencing.
+ T& operator*() const { return const_cast<T&>(base::operator*()); }
+ T* operator->() const { return const_cast<T*>(base::operator->()); }
+ T& operator[](difference_type i) {
+ return const_cast<T&>(base::operator[](i));
+ }
+
+ // Random access mutation.
+ friend circular_deque_iterator operator+(const circular_deque_iterator& iter,
+ difference_type offset) {
+ circular_deque_iterator ret = iter;
+ ret.Add(offset);
+ return ret;
+ }
+ circular_deque_iterator& operator+=(difference_type offset) {
+ base::Add(offset);
+ return *this;
+ }
+ friend circular_deque_iterator operator-(const circular_deque_iterator& iter,
+ difference_type offset) {
+ circular_deque_iterator ret = iter;
+ ret.Add(-offset);
+ return ret;
+ }
+ circular_deque_iterator& operator-=(difference_type offset) {
+ base::Add(-offset);
+ return *this;
+ }
+
+ // Increment and decrement.
+ circular_deque_iterator& operator++() {
+ base::Increment();
+ return *this;
+ }
+ circular_deque_iterator operator++(int) {
+ circular_deque_iterator ret = *this;
+ base::Increment();
+ return ret;
+ }
+ circular_deque_iterator& operator--() {
+ base::Decrement();
+ return *this;
+ }
+ circular_deque_iterator operator--(int) {
+ circular_deque_iterator ret = *this;
+ base::Decrement();
+ return ret;
+ }
+
+ private:
+ circular_deque_iterator(const circular_deque<T>* parent, size_t index)
+ : circular_deque_const_iterator<T>(parent, index) {}
+};
+
+} // namespace internal
+
+template <typename T>
+class circular_deque {
+ private:
+ using VectorBuffer = internal::VectorBuffer<T>;
+
+ public:
+ using value_type = T;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = value_type*;
+ using const_pointer = const value_type*;
+
+ using iterator = internal::circular_deque_iterator<T>;
+ using const_iterator = internal::circular_deque_const_iterator<T>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // ---------------------------------------------------------------------------
+ // Constructor
+
+ constexpr circular_deque() = default;
+
+ // Constructs with |count| copies of |value| or default constructed version.
+ circular_deque(size_type count) { resize(count); }
+ circular_deque(size_type count, const T& value) { resize(count, value); }
+
+ // Range constructor.
+ template <class InputIterator>
+ circular_deque(InputIterator first, InputIterator last) {
+ assign(first, last);
+ }
+
+ // Copy/move.
+ circular_deque(const circular_deque& other) : buffer_(other.size() + 1) {
+ assign(other.begin(), other.end());
+ }
+ circular_deque(circular_deque&& other) noexcept
+ : buffer_(std::move(other.buffer_)),
+ begin_(other.begin_),
+ end_(other.end_) {
+ other.begin_ = 0;
+ other.end_ = 0;
+ }
+
+ circular_deque(std::initializer_list<value_type> init) { assign(init); }
+
+ ~circular_deque() { DestructRange(begin_, end_); }
+
+ // ---------------------------------------------------------------------------
+ // Assignments.
+ //
+ // All of these may invalidate iterators and references.
+
+ circular_deque& operator=(const circular_deque& other) {
+ if (&other == this)
+ return *this;
+
+ reserve(other.size());
+ assign(other.begin(), other.end());
+ return *this;
+ }
+ circular_deque& operator=(circular_deque&& other) noexcept {
+ if (&other == this)
+ return *this;
+
+ // We're about to overwrite the buffer, so don't free it in clear to
+ // avoid doing it twice.
+ ClearRetainCapacity();
+ buffer_ = std::move(other.buffer_);
+ begin_ = other.begin_;
+ end_ = other.end_;
+
+ other.begin_ = 0;
+ other.end_ = 0;
+
+ IncrementGeneration();
+ return *this;
+ }
+ circular_deque& operator=(std::initializer_list<value_type> ilist) {
+ reserve(ilist.size());
+ assign(std::begin(ilist), std::end(ilist));
+ return *this;
+ }
+
+ void assign(size_type count, const value_type& value) {
+ ClearRetainCapacity();
+ reserve(count);
+ for (size_t i = 0; i < count; i++)
+ emplace_back(value);
+ IncrementGeneration();
+ }
+
+ // This variant should be enabled only when InputIterator is an iterator.
+ template <typename InputIterator>
+ typename std::enable_if<::base::internal::is_iterator<InputIterator>::value,
+ void>::type
+ assign(InputIterator first, InputIterator last) {
+ // Possible future enhancement, dispatch on iterator tag type. For forward
+ // iterators we can use std::difference to preallocate the space required
+ // and only do one copy.
+ ClearRetainCapacity();
+ for (; first != last; ++first)
+ emplace_back(*first);
+ IncrementGeneration();
+ }
+
+ void assign(std::initializer_list<value_type> value) {
+ reserve(std::distance(value.begin(), value.end()));
+ assign(value.begin(), value.end());
+ }
+
+ // ---------------------------------------------------------------------------
+ // Accessors.
+ //
+ // Since this class assumes no exceptions, at() and operator[] are equivalent.
+
+ const value_type& at(size_type i) const {
+ DCHECK(i < size());
+ size_t right_size = buffer_.capacity() - begin_;
+ if (begin_ <= end_ || i < right_size)
+ return buffer_[begin_ + i];
+ return buffer_[i - right_size];
+ }
+ value_type& at(size_type i) {
+ return const_cast<value_type&>(
+ const_cast<const circular_deque*>(this)->at(i));
+ }
+
+ value_type& operator[](size_type i) { return at(i); }
+ const value_type& operator[](size_type i) const {
+ return const_cast<circular_deque*>(this)->at(i);
+ }
+
+ value_type& front() {
+ DCHECK(!empty());
+ return buffer_[begin_];
+ }
+ const value_type& front() const {
+ DCHECK(!empty());
+ return buffer_[begin_];
+ }
+
+ value_type& back() {
+ DCHECK(!empty());
+ return *(--end());
+ }
+ const value_type& back() const {
+ DCHECK(!empty());
+ return *(--end());
+ }
+
+ // ---------------------------------------------------------------------------
+ // Iterators.
+
+ iterator begin() { return iterator(this, begin_); }
+ const_iterator begin() const { return const_iterator(this, begin_); }
+ const_iterator cbegin() const { return const_iterator(this, begin_); }
+
+ iterator end() { return iterator(this, end_); }
+ const_iterator end() const { return const_iterator(this, end_); }
+ const_iterator cend() const { return const_iterator(this, end_); }
+
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator crbegin() const { return rbegin(); }
+
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+ const_reverse_iterator crend() const { return rend(); }
+
+ // ---------------------------------------------------------------------------
+ // Memory management.
+
+ // IMPORTANT NOTE ON reserve(...): This class implements auto-shrinking of
+ // the buffer when elements are deleted and there is "too much" wasted space.
+ // So if you call reserve() with a large size in anticipation of pushing many
+ // elements, but pop an element before the queue is full, the capacity you
+ // reserved may be lost.
+ //
+ // As a result, it's only worthwhile to call reserve() when you're adding
+ // many things at once with no intermediate operations.
+ void reserve(size_type new_capacity) {
+ if (new_capacity > capacity())
+ SetCapacityTo(new_capacity);
+ }
+
+ size_type capacity() const {
+ // One item is wasted to indicate end().
+ return buffer_.capacity() == 0 ? 0 : buffer_.capacity() - 1;
+ }
+
+ void shrink_to_fit() {
+ if (empty()) {
+ // Optimize empty case to really delete everything if there was
+ // something.
+ if (buffer_.capacity())
+ buffer_ = VectorBuffer();
+ } else {
+ SetCapacityTo(size());
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // Size management.
+
+ // This will additionally reset the capacity() to 0.
+ void clear() {
+ // This can't resize(0) because that requires a default constructor to
+ // compile, which not all contained classes may implement.
+ ClearRetainCapacity();
+ buffer_ = VectorBuffer();
+ }
+
+ bool empty() const { return begin_ == end_; }
+
+ size_type size() const {
+ if (begin_ <= end_)
+ return end_ - begin_;
+ return buffer_.capacity() - begin_ + end_;
+ }
+
+ // When reducing size, the elements are deleted from the end. When expanding
+ // size, elements are added to the end with |value| or the default
+ // constructed version. Even when using resize(count) to shrink, a default
+ // constructor is required for the code to compile, even though it will not
+ // be called.
+ //
+ // There are two versions rather than using a default value to avoid
+ // creating a temporary when shrinking (when it's not needed). Plus if
+ // the default constructor is desired when expanding usually just calling it
+ // for each element is faster than making a default-constructed temporary and
+ // copying it.
+ void resize(size_type count) {
+ // SEE BELOW VERSION if you change this. The code is mostly the same.
+ if (count > size()) {
+ // This could be slighly more efficient but expanding a queue with
+ // identical elements is unusual and the extra computations of emplacing
+ // one-by-one will typically be small relative to calling the constructor
+ // for every item.
+ ExpandCapacityIfNecessary(count - size());
+ while (size() < count)
+ emplace_back();
+ } else if (count < size()) {
+ size_t new_end = (begin_ + count) % buffer_.capacity();
+ DestructRange(new_end, end_);
+ end_ = new_end;
+
+ ShrinkCapacityIfNecessary();
+ }
+ IncrementGeneration();
+ }
+ void resize(size_type count, const value_type& value) {
+ // SEE ABOVE VERSION if you change this. The code is mostly the same.
+ if (count > size()) {
+ ExpandCapacityIfNecessary(count - size());
+ while (size() < count)
+ emplace_back(value);
+ } else if (count < size()) {
+ size_t new_end = (begin_ + count) % buffer_.capacity();
+ DestructRange(new_end, end_);
+ end_ = new_end;
+
+ ShrinkCapacityIfNecessary();
+ }
+ IncrementGeneration();
+ }
+
+ // ---------------------------------------------------------------------------
+ // Insert and erase.
+ //
+ // Insertion and deletion in the middle is O(n) and invalidates all existing
+ // iterators.
+ //
+ // The implementation of insert isn't optimized as much as it could be. If
+ // the insertion requires that the buffer be grown, it will first be grown
+ // and everything moved, and then the items will be inserted, potentially
+ // moving some items twice. This simplifies the implemntation substantially
+ // and means less generated templatized code. Since this is an uncommon
+ // operation for deques, and already relatively slow, it doesn't seem worth
+ // the benefit to optimize this.
+
+ void insert(const_iterator pos, size_type count, const T& value) {
+ ValidateIterator(pos);
+
+ // Optimize insert at the beginning.
+ if (pos == begin()) {
+ ExpandCapacityIfNecessary(count);
+ for (size_t i = 0; i < count; i++)
+ push_front(value);
+ return;
+ }
+
+ iterator insert_cur(this, pos.index_);
+ iterator insert_end;
+ MakeRoomFor(count, &insert_cur, &insert_end);
+ while (insert_cur < insert_end) {
+ new (&buffer_[insert_cur.index_]) T(value);
+ ++insert_cur;
+ }
+
+ IncrementGeneration();
+ }
+
+ // This enable_if keeps this call from getting confused with the (pos, count,
+ // value) version when value is an integer.
+ template <class InputIterator>
+ typename std::enable_if<::base::internal::is_iterator<InputIterator>::value,
+ void>::type
+ insert(const_iterator pos, InputIterator first, InputIterator last) {
+ ValidateIterator(pos);
+
+ size_t inserted_items = std::distance(first, last);
+ if (inserted_items == 0)
+ return; // Can divide by 0 when doing modulo below, so return early.
+
+ // Make a hole to copy the items into.
+ iterator insert_cur;
+ iterator insert_end;
+ if (pos == begin()) {
+ // Optimize insert at the beginning, nothing needs to be shifted and the
+ // hole is the |inserted_items| block immediately before |begin_|.
+ ExpandCapacityIfNecessary(inserted_items);
+ insert_end = begin();
+ begin_ =
+ (begin_ + buffer_.capacity() - inserted_items) % buffer_.capacity();
+ insert_cur = begin();
+ } else {
+ insert_cur = iterator(this, pos.index_);
+ MakeRoomFor(inserted_items, &insert_cur, &insert_end);
+ }
+
+ // Copy the items.
+ while (insert_cur < insert_end) {
+ new (&buffer_[insert_cur.index_]) T(*first);
+ ++insert_cur;
+ ++first;
+ }
+
+ IncrementGeneration();
+ }
+
+ // These all return an iterator to the inserted item. Existing iterators will
+ // be invalidated.
+ iterator insert(const_iterator pos, const T& value) {
+ return emplace(pos, value);
+ }
+ iterator insert(const_iterator pos, T&& value) {
+ return emplace(pos, std::move(value));
+ }
+ template <class... Args>
+ iterator emplace(const_iterator pos, Args&&... args) {
+ ValidateIterator(pos);
+
+ // Optimize insert at beginning which doesn't require shifting.
+ if (pos == cbegin()) {
+ emplace_front(std::forward<Args>(args)...);
+ return begin();
+ }
+
+ // Do this before we make the new iterators we return.
+ IncrementGeneration();
+
+ iterator insert_begin(this, pos.index_);
+ iterator insert_end;
+ MakeRoomFor(1, &insert_begin, &insert_end);
+ new (&buffer_[insert_begin.index_]) T(std::forward<Args>(args)...);
+
+ return insert_begin;
+ }
+
+ // Calling erase() won't automatically resize the buffer smaller like resize
+ // or the pop functions. Erase is slow and relatively uncommon, and for
+ // normal deque usage a pop will normally be done on a regular basis that
+ // will prevent excessive buffer usage over long periods of time. It's not
+ // worth having the extra code for every template instantiation of erase()
+ // to resize capacity downward to a new buffer.
+ iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
+ iterator erase(const_iterator first, const_iterator last) {
+ ValidateIterator(first);
+ ValidateIterator(last);
+
+ IncrementGeneration();
+
+ // First, call the destructor on the deleted items.
+ if (first.index_ == last.index_) {
+ // Nothing deleted. Need to return early to avoid falling through to
+ // moving items on top of themselves.
+ return iterator(this, first.index_);
+ } else if (first.index_ < last.index_) {
+ // Contiguous range.
+ buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]);
+ } else {
+ // Deleted range wraps around.
+ buffer_.DestructRange(&buffer_[first.index_],
+ &buffer_[buffer_.capacity()]);
+ buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]);
+ }
+
+ if (first.index_ == begin_) {
+ // This deletion is from the beginning. Nothing needs to be copied, only
+ // begin_ needs to be updated.
+ begin_ = last.index_;
+ return iterator(this, last.index_);
+ }
+
+ // In an erase operation, the shifted items all move logically to the left,
+ // so move them from left-to-right.
+ iterator move_src(this, last.index_);
+ iterator move_src_end = end();
+ iterator move_dest(this, first.index_);
+ for (; move_src < move_src_end; move_src++, move_dest++) {
+ buffer_.MoveRange(&buffer_[move_src.index_],
+ &buffer_[move_src.index_ + 1],
+ &buffer_[move_dest.index_]);
+ }
+
+ end_ = move_dest.index_;
+
+ // Since we did not reallocate and only changed things after the erase
+ // element(s), the input iterator's index points to the thing following the
+ // deletion.
+ return iterator(this, first.index_);
+ }
+
+ // ---------------------------------------------------------------------------
+ // Begin/end operations.
+
+ void push_front(const T& value) { emplace_front(value); }
+ void push_front(T&& value) { emplace_front(std::move(value)); }
+
+ void push_back(const T& value) { emplace_back(value); }
+ void push_back(T&& value) { emplace_back(std::move(value)); }
+
+ template <class... Args>
+ reference emplace_front(Args&&... args) {
+ ExpandCapacityIfNecessary(1);
+ if (begin_ == 0)
+ begin_ = buffer_.capacity() - 1;
+ else
+ begin_--;
+ IncrementGeneration();
+ new (&buffer_[begin_]) T(std::forward<Args>(args)...);
+ return front();
+ }
+
+ template <class... Args>
+ reference emplace_back(Args&&... args) {
+ ExpandCapacityIfNecessary(1);
+ new (&buffer_[end_]) T(std::forward<Args>(args)...);
+ if (end_ == buffer_.capacity() - 1)
+ end_ = 0;
+ else
+ end_++;
+ IncrementGeneration();
+ return back();
+ }
+
+ void pop_front() {
+ DCHECK(size());
+ buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]);
+ begin_++;
+ if (begin_ == buffer_.capacity())
+ begin_ = 0;
+
+ ShrinkCapacityIfNecessary();
+
+ // Technically popping will not invalidate any iterators since the
+ // underlying buffer will be stable. But in the future we may want to add a
+ // feature that resizes the buffer smaller if there is too much wasted
+ // space. This ensures we can make such a change safely.
+ IncrementGeneration();
+ }
+ void pop_back() {
+ DCHECK(size());
+ if (end_ == 0)
+ end_ = buffer_.capacity() - 1;
+ else
+ end_--;
+ buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]);
+
+ ShrinkCapacityIfNecessary();
+
+ // See pop_front comment about why this is here.
+ IncrementGeneration();
+ }
+
+ // ---------------------------------------------------------------------------
+ // General operations.
+
+ void swap(circular_deque& other) {
+ std::swap(buffer_, other.buffer_);
+ std::swap(begin_, other.begin_);
+ std::swap(end_, other.end_);
+ IncrementGeneration();
+ }
+
+ friend void swap(circular_deque& lhs, circular_deque& rhs) { lhs.swap(rhs); }
+
+ private:
+ friend internal::circular_deque_iterator<T>;
+ friend internal::circular_deque_const_iterator<T>;
+
+ // Moves the items in the given circular buffer to the current one. The
+ // source is moved from so will become invalid. The destination buffer must
+ // have already been allocated with enough size.
+ static void MoveBuffer(VectorBuffer& from_buf,
+ size_t from_begin,
+ size_t from_end,
+ VectorBuffer* to_buf,
+ size_t* to_begin,
+ size_t* to_end) {
+ size_t from_capacity = from_buf.capacity();
+
+ *to_begin = 0;
+ if (from_begin < from_end) {
+ // Contiguous.
+ from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end],
+ to_buf->begin());
+ *to_end = from_end - from_begin;
+ } else if (from_begin > from_end) {
+ // Discontiguous, copy the right side to the beginning of the new buffer.
+ from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity],
+ to_buf->begin());
+ size_t right_size = from_capacity - from_begin;
+ // Append the left side.
+ from_buf.MoveRange(&from_buf[0], &from_buf[from_end],
+ &(*to_buf)[right_size]);
+ *to_end = right_size + from_end;
+ } else {
+ // No items.
+ *to_end = 0;
+ }
+ }
+
+ // Expands the buffer size. This assumes the size is larger than the
+ // number of elements in the vector (it won't call delete on anything).
+ void SetCapacityTo(size_t new_capacity) {
+ // Use the capacity + 1 as the internal buffer size to differentiate
+ // empty and full (see definition of buffer_ below).
+ VectorBuffer new_buffer(new_capacity + 1);
+ MoveBuffer(buffer_, begin_, end_, &new_buffer, &begin_, &end_);
+ buffer_ = std::move(new_buffer);
+ }
+ void ExpandCapacityIfNecessary(size_t additional_elts) {
+ size_t min_new_capacity = size() + additional_elts;
+ if (capacity() >= min_new_capacity)
+ return; // Already enough room.
+
+ min_new_capacity =
+ std::max(min_new_capacity, internal::kCircularBufferInitialCapacity);
+
+ // std::vector always grows by at least 50%. WTF::Deque grows by at least
+ // 25%. We expect queue workloads to generally stay at a similar size and
+ // grow less than a vector might, so use 25%.
+ size_t new_capacity =
+ std::max(min_new_capacity, capacity() + capacity() / 4);
+ SetCapacityTo(new_capacity);
+ }
+
+ void ShrinkCapacityIfNecessary() {
+ // Don't auto-shrink below this size.
+ if (capacity() <= internal::kCircularBufferInitialCapacity)
+ return;
+
+ // Shrink when 100% of the size() is wasted.
+ size_t sz = size();
+ size_t empty_spaces = capacity() - sz;
+ if (empty_spaces < sz)
+ return;
+
+ // Leave 1/4 the size as free capacity, not going below the initial
+ // capacity.
+ size_t new_capacity =
+ std::max(internal::kCircularBufferInitialCapacity, sz + sz / 4);
+ if (new_capacity < capacity()) {
+ // Count extra item to convert to internal capacity.
+ SetCapacityTo(new_capacity);
+ }
+ }
+
+ // Backend for clear() but does not resize the internal buffer.
+ void ClearRetainCapacity() {
+ // This can't resize(0) because that requires a default constructor to
+ // compile, which not all contained classes may implement.
+ DestructRange(begin_, end_);
+ begin_ = 0;
+ end_ = 0;
+ IncrementGeneration();
+ }
+
+ // Calls destructors for the given begin->end indices. The indices may wrap
+ // around. The buffer is not resized, and the begin_ and end_ members are
+ // not changed.
+ void DestructRange(size_t begin, size_t end) {
+ if (end == begin) {
+ return;
+ } else if (end > begin) {
+ buffer_.DestructRange(&buffer_[begin], &buffer_[end]);
+ } else {
+ buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]);
+ buffer_.DestructRange(&buffer_[0], &buffer_[end]);
+ }
+ }
+
+ // Makes room for |count| items starting at |*insert_begin|. Since iterators
+ // are not stable across buffer resizes, |*insert_begin| will be updated to
+ // point to the beginning of the newly opened position in the new array (it's
+ // in/out), and the end of the newly opened position (it's out-only).
+ void MakeRoomFor(size_t count, iterator* insert_begin, iterator* insert_end) {
+ if (count == 0) {
+ *insert_end = *insert_begin;
+ return;
+ }
+
+ // The offset from the beginning will be stable across reallocations.
+ size_t begin_offset = insert_begin->OffsetFromBegin();
+ ExpandCapacityIfNecessary(count);
+
+ insert_begin->index_ = (begin_ + begin_offset) % buffer_.capacity();
+ *insert_end =
+ iterator(this, (insert_begin->index_ + count) % buffer_.capacity());
+
+ // Update the new end and prepare the iterators for copying.
+ iterator src = end();
+ end_ = (end_ + count) % buffer_.capacity();
+ iterator dest = end();
+
+ // Move the elements. This will always involve shifting logically to the
+ // right, so move in a right-to-left order.
+ while (true) {
+ if (src == *insert_begin)
+ break;
+ --src;
+ --dest;
+ buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1],
+ &buffer_[dest.index_]);
+ }
+ }
+
+#if DCHECK_IS_ON()
+ // Asserts the given index is dereferencable. The index is an index into the
+ // buffer, not an index used by operator[] or at() which will be offsets from
+ // begin.
+ void CheckValidIndex(size_t i) const {
+ if (begin_ <= end_)
+ DCHECK(i >= begin_ && i < end_);
+ else
+ DCHECK((i >= begin_ && i < buffer_.capacity()) || i < end_);
+ }
+
+ // Asserts the given index is either dereferencable or points to end().
+ void CheckValidIndexOrEnd(size_t i) const {
+ if (i != end_)
+ CheckValidIndex(i);
+ }
+
+ void ValidateIterator(const const_iterator& i) const {
+ DCHECK(i.parent_deque_ == this);
+ i.CheckUnstableUsage();
+ }
+
+ // See generation_ below.
+ void IncrementGeneration() { generation_++; }
+#else
+ // No-op versions of these functions for release builds.
+ void CheckValidIndex(size_t) const {}
+ void CheckValidIndexOrEnd(size_t) const {}
+ void ValidateIterator(const const_iterator& i) const {}
+ void IncrementGeneration() {}
+#endif
+
+ // Danger, the buffer_.capacity() is the "internal capacity" which is
+ // capacity() + 1 since there is an extra item to indicate the end. Otherwise
+ // being completely empty and completely full are indistinguishable (begin ==
+ // end). We could add a separate flag to avoid it, but that adds significant
+ // extra complexity since every computation will have to check for it. Always
+ // keeping one extra unused element in the buffer makes iterator computations
+ // much simpler.
+ //
+ // Container internal code will want to use buffer_.capacity() for offset
+ // computations rather than capacity().
+ VectorBuffer buffer_;
+ size_type begin_ = 0;
+ size_type end_ = 0;
+
+#if DCHECK_IS_ON()
+ // Incremented every time a modification is made that could affect iterator
+ // invalidations.
+ uint64_t generation_ = 0;
+#endif
+};
+
+// Implementations of base::Erase[If] (see base/stl_util.h).
+template <class T, class Value>
+void Erase(circular_deque<T>& container, const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <class T, class Predicate>
+void EraseIf(circular_deque<T>& container, Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_CIRCULAR_DEQUE_H_
diff --git a/gn/base/containers/flat_map.h b/gn/base/containers/flat_map.h
new file mode 100644
index 00000000000..b4fe5196ec8
--- /dev/null
+++ b/gn/base/containers/flat_map.h
@@ -0,0 +1,362 @@
+// 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_MAP_H_
+#define BASE_CONTAINERS_FLAT_MAP_H_
+
+#include <functional>
+#include <tuple>
+#include <utility>
+
+#include "base/containers/flat_tree.h"
+#include "base/logging.h"
+#include "base/template_util.h"
+
+namespace base {
+
+namespace internal {
+
+// An implementation of the flat_tree GetKeyFromValue template parameter that
+// extracts the key as the first element of a pair.
+template <class Key, class Mapped>
+struct GetKeyFromValuePairFirst {
+ const Key& operator()(const std::pair<Key, Mapped>& p) const {
+ return p.first;
+ }
+};
+
+} // namespace internal
+
+// flat_map is a container with a std::map-like interface that stores its
+// contents in a sorted vector.
+//
+// Please see //base/containers/README.md for an overview of which container
+// to select.
+//
+// PROS
+//
+// - Good memory locality.
+// - Low overhead, especially for smaller maps.
+// - Performance is good for more workloads than you might expect (see
+// overview link above).
+// - Supports C++14 map interface.
+//
+// CONS
+//
+// - Inserts and removals are O(n).
+//
+// IMPORTANT NOTES
+//
+// - Iterators are invalidated across mutations.
+// - If possible, construct a flat_map in one operation by inserting into
+// a std::vector and moving that vector into the flat_map constructor.
+//
+// QUICK REFERENCE
+//
+// Most of the core functionality is inherited from flat_tree. Please see
+// flat_tree.h for more details for most of these functions. As a quick
+// reference, the functions available are:
+//
+// Constructors (inputs need not be sorted):
+// flat_map(InputIterator first, InputIterator last,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
+// const Compare& compare = Compare());
+// flat_map(const flat_map&);
+// flat_map(flat_map&&);
+// flat_map(std::vector<value_type>,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
+// const Compare& compare = Compare()); // Re-use storage.
+// flat_map(std::initializer_list<value_type> ilist,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
+// const Compare& comp = Compare());
+//
+// Assignment functions:
+// flat_map& operator=(const flat_map&);
+// flat_map& operator=(flat_map&&);
+// flat_map& operator=(initializer_list<value_type>);
+//
+// Memory management functions:
+// void reserve(size_t);
+// size_t capacity() const;
+// void shrink_to_fit();
+//
+// Size management functions:
+// void clear();
+// size_t size() const;
+// size_t max_size() const;
+// bool empty() const;
+//
+// Iterator functions:
+// 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 and accessor functions:
+// mapped_type& operator[](const key_type&);
+// mapped_type& operator[](key_type&&);
+// pair<iterator, bool> insert(const value_type&);
+// pair<iterator, bool> insert(value_type&&);
+// iterator insert(const_iterator hint, const value_type&);
+// iterator insert(const_iterator hint, value_type&&);
+// void insert(InputIterator first, InputIterator last,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES);
+// pair<iterator, bool> insert_or_assign(K&&, M&&);
+// iterator insert_or_assign(const_iterator hint, K&&, M&&);
+// pair<iterator, bool> emplace(Args&&...);
+// iterator emplace_hint(const_iterator, Args&&...);
+// pair<iterator, bool> try_emplace(K&&, Args&&...);
+// iterator try_emplace(const_iterator hint, K&&, Args&&...);
+//
+// Erase functions:
+// iterator erase(iterator);
+// iterator erase(const_iterator);
+// iterator erase(const_iterator first, const_iterator& last);
+// template <class K> size_t erase(const K& key);
+//
+// Comparators (see std::map documentation).
+// key_compare key_comp() const;
+// value_compare value_comp() const;
+//
+// Search functions:
+// template <typename K> size_t count(const K&) const;
+// template <typename K> iterator find(const K&);
+// template <typename K> const_iterator find(const K&) const;
+// template <typename K> pair<iterator, iterator> equal_range(const K&);
+// template <typename K> iterator lower_bound(const K&);
+// template <typename K> const_iterator lower_bound(const K&) const;
+// template <typename K> iterator upper_bound(const K&);
+// template <typename K> const_iterator upper_bound(const K&) const;
+//
+// General functions:
+// void swap(flat_map&&);
+//
+// Non-member operators:
+// bool operator==(const flat_map&, const flat_map);
+// bool operator!=(const flat_map&, const flat_map);
+// bool operator<(const flat_map&, const flat_map);
+// bool operator>(const flat_map&, const flat_map);
+// bool operator>=(const flat_map&, const flat_map);
+// bool operator<=(const flat_map&, const flat_map);
+//
+template <class Key, class Mapped, class Compare = std::less<>>
+class flat_map : public ::base::internal::flat_tree<
+ Key,
+ std::pair<Key, Mapped>,
+ ::base::internal::GetKeyFromValuePairFirst<Key, Mapped>,
+ Compare> {
+ private:
+ using tree = typename ::base::internal::flat_tree<
+ Key,
+ std::pair<Key, Mapped>,
+ ::base::internal::GetKeyFromValuePairFirst<Key, Mapped>,
+ Compare>;
+
+ public:
+ using key_type = typename tree::key_type;
+ using mapped_type = Mapped;
+ using value_type = typename tree::value_type;
+ using iterator = typename tree::iterator;
+ using const_iterator = typename tree::const_iterator;
+
+ // --------------------------------------------------------------------------
+ // Lifetime and assignments.
+ //
+ // Note: we could do away with these constructors, destructor and assignment
+ // operator overloads by inheriting |tree|'s, but this breaks the GCC build
+ // due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84782 (see
+ // https://crbug.com/837221).
+
+ flat_map() = default;
+ explicit flat_map(const Compare& comp);
+
+ template <class InputIterator>
+ flat_map(InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
+ const Compare& comp = Compare());
+
+ flat_map(const flat_map&) = default;
+ flat_map(flat_map&&) noexcept = default;
+
+ flat_map(std::vector<value_type> items,
+ FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
+ const Compare& comp = Compare());
+
+ flat_map(std::initializer_list<value_type> ilist,
+ FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
+ const Compare& comp = Compare());
+
+ ~flat_map() = default;
+
+ flat_map& operator=(const flat_map&) = default;
+ flat_map& operator=(flat_map&&) = default;
+ // Takes the first if there are duplicates in the initializer list.
+ flat_map& operator=(std::initializer_list<value_type> ilist);
+
+ // --------------------------------------------------------------------------
+ // Map-specific insert operations.
+ //
+ // Normal insert() functions are inherited from flat_tree.
+ //
+ // Assume that every operation invalidates iterators and references.
+ // Insertion of one element can take O(size).
+
+ mapped_type& operator[](const key_type& key);
+ mapped_type& operator[](key_type&& key);
+
+ template <class K, class M>
+ std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj);
+ template <class K, class M>
+ iterator insert_or_assign(const_iterator hint, K&& key, M&& obj);
+
+ template <class K, class... Args>
+ std::enable_if_t<std::is_constructible<key_type, K&&>::value,
+ std::pair<iterator, bool>>
+ try_emplace(K&& key, Args&&... args);
+
+ template <class K, class... Args>
+ std::enable_if_t<std::is_constructible<key_type, K&&>::value, iterator>
+ try_emplace(const_iterator hint, K&& key, Args&&... args);
+
+ // --------------------------------------------------------------------------
+ // General operations.
+ //
+ // Assume that swap invalidates iterators and references.
+
+ void swap(flat_map& other) noexcept;
+
+ friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); }
+};
+
+// ----------------------------------------------------------------------------
+// Lifetime.
+
+template <class Key, class Mapped, class Compare>
+flat_map<Key, Mapped, Compare>::flat_map(const Compare& comp) : tree(comp) {}
+
+template <class Key, class Mapped, class Compare>
+template <class InputIterator>
+flat_map<Key, Mapped, Compare>::flat_map(InputIterator first,
+ InputIterator last,
+ FlatContainerDupes dupe_handling,
+ const Compare& comp)
+ : tree(first, last, dupe_handling, comp) {}
+
+template <class Key, class Mapped, class Compare>
+flat_map<Key, Mapped, Compare>::flat_map(std::vector<value_type> items,
+ FlatContainerDupes dupe_handling,
+ const Compare& comp)
+ : tree(std::move(items), dupe_handling, comp) {}
+
+template <class Key, class Mapped, class Compare>
+flat_map<Key, Mapped, Compare>::flat_map(
+ std::initializer_list<value_type> ilist,
+ FlatContainerDupes dupe_handling,
+ const Compare& comp)
+ : flat_map(std::begin(ilist), std::end(ilist), dupe_handling, comp) {}
+
+// ----------------------------------------------------------------------------
+// Assignments.
+
+template <class Key, class Mapped, class Compare>
+auto flat_map<Key, Mapped, Compare>::operator=(
+ std::initializer_list<value_type> ilist) -> flat_map& {
+ // When https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84782 gets fixed, we
+ // need to remember to inherit tree::operator= to prevent
+ // flat_map<...> x;
+ // x = {...};
+ // from first creating a flat_map and then move assigning it. This most
+ // likely would be optimized away but still affects our debug builds.
+ tree::operator=(ilist);
+ return *this;
+}
+
+// ----------------------------------------------------------------------------
+// Insert operations.
+
+template <class Key, class Mapped, class Compare>
+auto flat_map<Key, Mapped, Compare>::operator[](const key_type& key)
+ -> mapped_type& {
+ iterator found = tree::lower_bound(key);
+ if (found == tree::end() || tree::key_comp()(key, found->first))
+ found = tree::unsafe_emplace(found, key, mapped_type());
+ return found->second;
+}
+
+template <class Key, class Mapped, class Compare>
+auto flat_map<Key, Mapped, Compare>::operator[](key_type&& key)
+ -> mapped_type& {
+ iterator found = tree::lower_bound(key);
+ if (found == tree::end() || tree::key_comp()(key, found->first))
+ found = tree::unsafe_emplace(found, std::move(key), mapped_type());
+ return found->second;
+}
+
+template <class Key, class Mapped, class Compare>
+template <class K, class M>
+auto flat_map<Key, Mapped, Compare>::insert_or_assign(K&& key, M&& obj)
+ -> std::pair<iterator, bool> {
+ auto result =
+ tree::emplace_key_args(key, std::forward<K>(key), std::forward<M>(obj));
+ if (!result.second)
+ result.first->second = std::forward<M>(obj);
+ return result;
+}
+
+template <class Key, class Mapped, class Compare>
+template <class K, class M>
+auto flat_map<Key, Mapped, Compare>::insert_or_assign(const_iterator hint,
+ K&& key,
+ M&& obj) -> iterator {
+ auto result = tree::emplace_hint_key_args(hint, key, std::forward<K>(key),
+ std::forward<M>(obj));
+ if (!result.second)
+ result.first->second = std::forward<M>(obj);
+ return result.first;
+}
+
+template <class Key, class Mapped, class Compare>
+template <class K, class... Args>
+auto flat_map<Key, Mapped, Compare>::try_emplace(K&& key, Args&&... args)
+ -> std::enable_if_t<std::is_constructible<key_type, K&&>::value,
+ std::pair<iterator, bool>> {
+ return tree::emplace_key_args(
+ key, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<K>(key)),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+}
+
+template <class Key, class Mapped, class Compare>
+template <class K, class... Args>
+auto flat_map<Key, Mapped, Compare>::try_emplace(const_iterator hint,
+ K&& key,
+ Args&&... args)
+ -> std::enable_if_t<std::is_constructible<key_type, K&&>::value, iterator> {
+ return tree::emplace_hint_key_args(
+ hint, key, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<K>(key)),
+ std::forward_as_tuple(std::forward<Args>(args)...))
+ .first;
+}
+
+// ----------------------------------------------------------------------------
+// General operations.
+
+template <class Key, class Mapped, class Compare>
+void flat_map<Key, Mapped, Compare>::swap(flat_map& other) noexcept {
+ tree::swap(other);
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_FLAT_MAP_H_
diff --git a/gn/base/containers/flat_tree.h b/gn/base/containers/flat_tree.h
new file mode 100644
index 00000000000..7856e242336
--- /dev/null
+++ b/gn/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, 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/base/containers/queue.h
new file mode 100644
index 00000000000..2d3b480089f
--- /dev/null
+++ b/gn/base/containers/queue.h
@@ -0,0 +1,23 @@
+// 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_QUEUE_H_
+#define BASE_CONTAINERS_QUEUE_H_
+
+#include <queue>
+
+#include "base/containers/circular_deque.h"
+
+namespace base {
+
+// Provides a definition of base::queue that's like std::queue but uses a
+// base::circular_queue instead of std::deque. Since std::queue is just a
+// wrapper for an underlying type, we can just provide a typedef for it that
+// defaults to the base circular_deque.
+template <class T, class Container = circular_deque<T>>
+using queue = std::queue<T, Container>;
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_QUEUE_H_
diff --git a/gn/base/containers/span.h b/gn/base/containers/span.h
new file mode 100644
index 00000000000..c9718619bcc
--- /dev/null
+++ b/gn/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(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/stack.h b/gn/base/containers/stack.h
new file mode 100644
index 00000000000..1aaa8793c7c
--- /dev/null
+++ b/gn/base/containers/stack.h
@@ -0,0 +1,23 @@
+// 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_STACK_H_
+#define BASE_CONTAINERS_STACK_H_
+
+#include <stack>
+
+#include "base/containers/circular_deque.h"
+
+namespace base {
+
+// Provides a definition of base::stack that's like std::stack but uses a
+// base::circular_queue instead of std::deque. Since std::stack is just a
+// wrapper for an underlying type, we can just provide a typedef for it that
+// defaults to the base circular_deque.
+template <class T, class Container = circular_deque<T>>
+using stack = std::stack<T, Container>;
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_STACK_H_
diff --git a/gn/base/containers/vector_buffer.h b/gn/base/containers/vector_buffer.h
new file mode 100644
index 00000000000..a72c1ed95e8
--- /dev/null
+++ b/gn/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__) && !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
new file mode 100644
index 00000000000..7f0e5d9d5d1
--- /dev/null
+++ b/gn/base/environment.cc
@@ -0,0 +1,237 @@
+// 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
new file mode 100644
index 00000000000..82af9878dac
--- /dev/null
+++ b/gn/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 "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
new file mode 100644
index 00000000000..98fa3a6597c
--- /dev/null
+++ b/gn/base/files/file.cc
@@ -0,0 +1,132 @@
+// 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
new file mode 100644
index 00000000000..61239adb8e7
--- /dev/null
+++ b/gn/base/files/file.h
@@ -0,0 +1,356 @@
+// 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.cc b/gn/base/files/file_enumerator.cc
new file mode 100644
index 00000000000..9dfb2ba04b5
--- /dev/null
+++ b/gn/base/files/file_enumerator.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2013 The Chromium Authors. 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_util.h"
+
+namespace base {
+
+FileEnumerator::FileInfo::~FileInfo() = default;
+
+bool FileEnumerator::ShouldSkip(const FilePath& path) {
+ FilePath::StringType basename = path.BaseName().value();
+ return basename == FILE_PATH_LITERAL(".") ||
+ (basename == FILE_PATH_LITERAL("..") &&
+ !(INCLUDE_DOT_DOT & file_type_));
+}
+
+bool FileEnumerator::IsTypeMatched(bool is_dir) const {
+ return (file_type_ &
+ (is_dir ? FileEnumerator::DIRECTORIES : FileEnumerator::FILES)) != 0;
+}
+
+} // namespace base
diff --git a/gn/base/files/file_enumerator.h b/gn/base/files/file_enumerator.h
new file mode 100644
index 00000000000..81f757b445a
--- /dev/null
+++ b/gn/base/files/file_enumerator.h
@@ -0,0 +1,171 @@
+// 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_ENUMERATOR_H_
+#define BASE_FILES_FILE_ENUMERATOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/containers/stack.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "util/build_config.h"
+#include "util/ticks.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+namespace base {
+
+// A class for enumerating the files in a provided path. The order of the
+// results is not guaranteed.
+//
+// This is blocking. Do not use on critical threads.
+//
+// Example:
+//
+// base::FileEnumerator enum(my_dir, false, base::FileEnumerator::FILES,
+// FILE_PATH_LITERAL("*.txt"));
+// for (base::FilePath name = enum.Next(); !name.empty(); name = enum.Next())
+// ...
+class FileEnumerator {
+ public:
+ // Note: copy & assign supported.
+ class FileInfo {
+ public:
+ FileInfo();
+ ~FileInfo();
+
+ bool IsDirectory() const;
+
+ // The name of the file. This will not include any path information. This
+ // is in constrast to the value returned by FileEnumerator.Next() which
+ // includes the |root_path| passed into the FileEnumerator constructor.
+ FilePath GetName() const;
+
+ int64_t GetSize() const;
+ Ticks GetLastModifiedTime() const;
+
+#if defined(OS_WIN)
+ // Note that the cAlternateFileName (used to hold the "short" 8.3 name)
+ // of the WIN32_FIND_DATA will be empty. Since we don't use short file
+ // names, we tell Windows to omit it which speeds up the query slightly.
+ const WIN32_FIND_DATA& find_data() const { return find_data_; }
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ const struct stat& stat() const { return stat_; }
+#endif
+
+ private:
+ friend class FileEnumerator;
+
+#if defined(OS_WIN)
+ WIN32_FIND_DATA find_data_;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ struct stat stat_;
+ FilePath filename_;
+#endif
+ };
+
+ enum FileType {
+ FILES = 1 << 0,
+ DIRECTORIES = 1 << 1,
+ INCLUDE_DOT_DOT = 1 << 2,
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ SHOW_SYM_LINKS = 1 << 4,
+#endif
+ };
+
+ // Search policy for intermediate folders.
+ enum class FolderSearchPolicy {
+ // Recursive search will pass through folders whose names match the
+ // pattern. Inside each one, all files will be returned. Folders with names
+ // that do not match the pattern will be ignored within their interior.
+ MATCH_ONLY,
+ // Recursive search will pass through every folder and perform pattern
+ // matching inside each one.
+ ALL,
+ };
+
+ // |root_path| is the starting directory to search for. It may or may not end
+ // in a slash.
+ //
+ // If |recursive| is true, this will enumerate all matches in any
+ // subdirectories matched as well. It does a breadth-first search, so all
+ // files in one directory will be returned before any files in a
+ // subdirectory.
+ //
+ // |file_type|, a bit mask of FileType, specifies whether the enumerator
+ // should match files, directories, or both.
+ //
+ // |pattern| is an optional pattern for which files to match. This
+ // works like shell globbing. For example, "*.txt" or "Foo???.doc".
+ // However, be careful in specifying patterns that aren't cross platform
+ // since the underlying code uses OS-specific matching routines. In general,
+ // Windows matching is less featureful than others, so test there first.
+ // If unspecified, this will match all files.
+ FileEnumerator(const FilePath& root_path, bool recursive, int file_type);
+ FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern);
+ FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern,
+ FolderSearchPolicy folder_search_policy);
+ ~FileEnumerator();
+
+ // Returns the next file or an empty string if there are no more results.
+ //
+ // The returned path will incorporate the |root_path| passed in the
+ // constructor: "<root_path>/file_name.txt". If the |root_path| is absolute,
+ // then so will be the result of Next().
+ FilePath Next();
+
+ // Write the file info into |info|.
+ FileInfo GetInfo() const;
+
+ private:
+ // Returns true if the given path should be skipped in enumeration.
+ bool ShouldSkip(const FilePath& path);
+
+ bool IsTypeMatched(bool is_dir) const;
+
+ bool IsPatternMatched(const FilePath& src) const;
+
+#if defined(OS_WIN)
+ // True when find_data_ is valid.
+ bool has_find_data_ = false;
+ WIN32_FIND_DATA find_data_;
+ HANDLE find_handle_ = INVALID_HANDLE_VALUE;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // The files in the current directory
+ std::vector<FileInfo> directory_entries_;
+
+ // The next entry to use from the directory_entries_ vector
+ size_t current_directory_entry_;
+#endif
+ FilePath root_path_;
+ const bool recursive_;
+ const int file_type_;
+ FilePath::StringType pattern_;
+ const FolderSearchPolicy folder_search_policy_;
+
+ // A stack that keeps track of which subdirectories we still need to
+ // enumerate in the breadth-first search.
+ base::stack<FilePath> pending_paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileEnumerator);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_ENUMERATOR_H_
diff --git a/gn/base/files/file_enumerator_posix.cc b/gn/base/files/file_enumerator_posix.cc
new file mode 100644
index 00000000000..41d52b8afdd
--- /dev/null
+++ b/gn/base/files/file_enumerator_posix.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 "base/files/file_enumerator.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "util/build_config.h"
+
+namespace base {
+namespace {
+
+void GetStat(const FilePath& path, bool show_links, struct stat* st) {
+ DCHECK(st);
+ const int res = show_links ? lstat(path.value().c_str(), st)
+ : stat(path.value().c_str(), st);
+ if (res < 0) {
+ // Print the stat() error message unless it was ENOENT and we're following
+ // symlinks.
+ if (!(errno == ENOENT && !show_links))
+ DPLOG(ERROR) << "Couldn't stat" << path.value();
+ memset(st, 0, sizeof(*st));
+ }
+}
+
+} // namespace
+
+// FileEnumerator::FileInfo ----------------------------------------------------
+
+FileEnumerator::FileInfo::FileInfo() {
+ memset(&stat_, 0, sizeof(stat_));
+}
+
+bool FileEnumerator::FileInfo::IsDirectory() const {
+ return S_ISDIR(stat_.st_mode);
+}
+
+FilePath FileEnumerator::FileInfo::GetName() const {
+ return filename_;
+}
+
+int64_t FileEnumerator::FileInfo::GetSize() const {
+ return stat_.st_size;
+}
+
+Ticks FileEnumerator::FileInfo::GetLastModifiedTime() const {
+ return stat_.st_mtime;
+}
+
+// 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)
+ : current_directory_entry_(0),
+ root_path_(root_path),
+ recursive_(recursive),
+ file_type_(file_type),
+ pattern_(pattern),
+ folder_search_policy_(folder_search_policy) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() = default;
+
+FilePath FileEnumerator::Next() {
+ ++current_directory_entry_;
+
+ // While we've exhausted the entries in the current directory, do the next
+ while (current_directory_entry_ >= directory_entries_.size()) {
+ if (pending_paths_.empty())
+ return FilePath();
+
+ root_path_ = pending_paths_.top();
+ root_path_ = root_path_.StripTrailingSeparators();
+ pending_paths_.pop();
+
+ DIR* dir = opendir(root_path_.value().c_str());
+ if (!dir)
+ continue;
+
+ directory_entries_.clear();
+
+ current_directory_entry_ = 0;
+ struct dirent* dent;
+ while ((dent = readdir(dir))) {
+ FileInfo info;
+ info.filename_ = FilePath(dent->d_name);
+
+ if (ShouldSkip(info.filename_))
+ continue;
+
+ const bool is_pattern_matched = IsPatternMatched(info.filename_);
+
+ // MATCH_ONLY policy enumerates files and directories which matching
+ // pattern only. So we can early skip further checks.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
+ !is_pattern_matched)
+ continue;
+
+ // Do not call OS stat/lstat if there is no sense to do it. If pattern is
+ // not matched (file will not appear in results) and search is not
+ // recursive (possible directory will not be added to pending paths) -
+ // there is no sense to obtain item below.
+ if (!recursive_ && !is_pattern_matched)
+ continue;
+
+ const FilePath full_path = root_path_.Append(info.filename_);
+ GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_);
+
+ const bool is_dir = info.IsDirectory();
+
+ if (recursive_ && is_dir)
+ pending_paths_.push(full_path);
+
+ if (is_pattern_matched && IsTypeMatched(is_dir))
+ directory_entries_.push_back(std::move(info));
+ }
+ closedir(dir);
+
+ // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
+ // ALL policy enumerates files in all subfolders by origin pattern.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
+ pattern_.clear();
+ }
+
+ return root_path_.Append(
+ directory_entries_[current_directory_entry_].filename_);
+}
+
+FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
+ return directory_entries_[current_directory_entry_];
+}
+
+bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
+ return pattern_.empty() ||
+ !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
+}
+
+} // namespace base
diff --git a/gn/base/files/file_enumerator_win.cc b/gn/base/files/file_enumerator_win.cc
new file mode 100644
index 00000000000..803b7bd4437
--- /dev/null
+++ b/gn/base/files/file_enumerator_win.cc
@@ -0,0 +1,192 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..a66c8cc1ffc
--- /dev/null
+++ b/gn/base/files/file_path.cc
@@ -0,0 +1,690 @@
+// 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/sys_string_conversions.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.
+
+string16 FilePath::LossyDisplayName() const {
+ return WideToUTF16(SysNativeMBToWide(path_));
+}
+
+std::string FilePath::MaybeAsASCII() const {
+ if (base::IsStringASCII(path_))
+ return path_;
+ return std::string();
+}
+
+std::string FilePath::AsUTF8Unsafe() const {
+#if defined(SYSTEM_NATIVE_UTF8)
+ return value();
+#else
+ return WideToUTF8(SysNativeMBToWide(value()));
+#endif
+}
+
+string16 FilePath::AsUTF16Unsafe() const {
+#if defined(SYSTEM_NATIVE_UTF8)
+ return UTF8ToUTF16(value());
+#else
+ return WideToUTF16(SysNativeMBToWide(value()));
+#endif
+}
+
+// static
+FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) {
+#if defined(SYSTEM_NATIVE_UTF8)
+ return FilePath(utf8);
+#else
+ return FilePath(SysWideToNativeMB(UTF8ToWide(utf8)));
+#endif
+}
+
+// static
+FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) {
+#if defined(SYSTEM_NATIVE_UTF8)
+ return FilePath(UTF16ToUTF8(utf16));
+#else
+ return FilePath(SysWideToNativeMB(UTF16ToWide(utf16.as_string())));
+#endif
+}
+
+#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
new file mode 100644
index 00000000000..1717cba713f
--- /dev/null
+++ b/gn/base/files/file_path.h
@@ -0,0 +1,426 @@
+// 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
new file mode 100644
index 00000000000..fc7d6636420
--- /dev/null
+++ b/gn/base/files/file_path_constants.cc
@@ -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.
+
+#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
new file mode 100644
index 00000000000..4faf77b944a
--- /dev/null
+++ b/gn/base/files/file_posix.cc
@@ -0,0 +1,434 @@
+// 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_LINUX)
+ 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;
+#elif 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;
+#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
new file mode 100644
index 00000000000..d087b00f6c9
--- /dev/null
+++ b/gn/base/files/file_util.cc
@@ -0,0 +1,269 @@
+// 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 Move(const FilePath& from_path, const FilePath& to_path) {
+ if (from_path.ReferencesParent() || to_path.ReferencesParent())
+ return false;
+ return internal::MoveUnsafe(from_path, to_path);
+}
+
+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
new file mode 100644
index 00000000000..bfcac0169fc
--- /dev/null
+++ b/gn/base/files/file_util.h
@@ -0,0 +1,468 @@
+// 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
+
+// Moves the given path, whether it's a file or a directory.
+// If a simple rename is not possible, such as in the case where the paths are
+// on different volumes, this will attempt to copy and delete. Returns
+// true for success.
+// This function fails if either path contains traversal components ('..').
+bool Move(const FilePath& from_path, const FilePath& to_path);
+
+// 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);
+
+// Copies a single file. Use CopyDirectory() to copy directories.
+// This function fails if either path contains traversal components ('..').
+// This function also fails if |to_path| is a directory.
+//
+// On POSIX, if |to_path| is a symlink, CopyFile() will follow the symlink. This
+// may have security implications. Use with care.
+//
+// If |to_path| already exists and is a regular file, it will be overwritten,
+// though its permissions will stay the same.
+//
+// If |to_path| does not exist, it will be created. The new file's permissions
+// varies per platform:
+//
+// - This function keeps the metadata on Windows. The read only bit is not kept.
+// - On Mac and iOS, |to_path| retains |from_path|'s permissions, except user
+// read/write permissions are always set.
+// - On Linux and Android, |to_path| has user read/write permissions only. i.e.
+// Always 0600.
+// - On ChromeOS, |to_path| has user read/write permissions and group/others
+// read permissions. i.e. Always 0644.
+bool CopyFile(const FilePath& from_path, const FilePath& to_path);
+
+// Copies the given path, and optionally all subdirectories and their contents
+// as well.
+//
+// If there are files existing under to_path, always overwrite. Returns true
+// if successful, false otherwise. Wildcards on the names are not supported.
+//
+// This function has the same metadata behavior as CopyFile().
+//
+// If you only need to copy a file use CopyFile, it's faster.
+bool CopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive);
+
+// Like CopyDirectory() except trying to overwrite an existing file will not
+// work and will return false.
+bool CopyDirectoryExcl(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive);
+
+// 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);
+
+// Get the home directory. This is more complicated than just getenv("HOME")
+// as it knows to fall back on getpwent() etc.
+//
+// You should not generally call this directly. Instead use DIR_HOME with the
+// path service which will use this function but cache the value.
+// Path service may also override DIR_HOME.
+FilePath GetHomeDir();
+
+// 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
+
+// Internal --------------------------------------------------------------------
+
+namespace internal {
+
+// Same as Move but allows paths with traversal components.
+// Use only with extreme care.
+bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path);
+
+#if defined(OS_WIN)
+// Copy from_path to to_path recursively and then delete from_path recursively.
+// Returns true if all operations succeed.
+// This function simulates Move(), but unlike Move() it works across volumes.
+// This function is not transactional.
+bool CopyAndDeleteDirectory(const FilePath& from_path, const FilePath& to_path);
+#endif // defined(OS_WIN)
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_FILES_FILE_UTIL_H_
diff --git a/gn/base/files/file_util_linux.cc b/gn/base/files/file_util_linux.cc
new file mode 100644
index 00000000000..b230fd96484
--- /dev/null
+++ b/gn/base/files/file_util_linux.cc
@@ -0,0 +1,63 @@
+// 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_util.h"
+
+#include <errno.h>
+#include <linux/magic.h>
+#include <sys/vfs.h>
+
+#include "base/files/file_path.h"
+
+namespace base {
+
+bool GetFileSystemType(const FilePath& path, FileSystemType* type) {
+ struct statfs statfs_buf;
+ if (statfs(path.value().c_str(), &statfs_buf) < 0) {
+ if (errno == ENOENT)
+ return false;
+ *type = FILE_SYSTEM_UNKNOWN;
+ return true;
+ }
+
+ // Not all possible |statfs_buf.f_type| values are in linux/magic.h.
+ // Missing values are copied from the statfs man page.
+ switch (statfs_buf.f_type) {
+ case 0:
+ *type = FILE_SYSTEM_0;
+ break;
+ case EXT2_SUPER_MAGIC: // Also ext3 and ext4
+ case MSDOS_SUPER_MAGIC:
+ case REISERFS_SUPER_MAGIC:
+ case BTRFS_SUPER_MAGIC:
+ case 0x5346544E: // NTFS
+ case 0x58465342: // XFS
+ case 0x3153464A: // JFS
+ *type = FILE_SYSTEM_ORDINARY;
+ break;
+ case NFS_SUPER_MAGIC:
+ *type = FILE_SYSTEM_NFS;
+ break;
+ case SMB_SUPER_MAGIC:
+ case 0xFF534D42: // CIFS
+ *type = FILE_SYSTEM_SMB;
+ break;
+ case CODA_SUPER_MAGIC:
+ *type = FILE_SYSTEM_CODA;
+ break;
+ case HUGETLBFS_MAGIC:
+ case RAMFS_MAGIC:
+ case TMPFS_MAGIC:
+ *type = FILE_SYSTEM_MEMORY;
+ break;
+ case CGROUP_SUPER_MAGIC:
+ *type = FILE_SYSTEM_CGROUP;
+ break;
+ default:
+ *type = FILE_SYSTEM_OTHER;
+ }
+ return true;
+}
+
+} // namespace base
diff --git a/gn/base/files/file_util_mac.mm b/gn/base/files/file_util_mac.mm
new file mode 100644
index 00000000000..35fd27a6699
--- /dev/null
+++ b/gn/base/files/file_util_mac.mm
@@ -0,0 +1,64 @@
+// 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"
+
+#import <Foundation/Foundation.h>
+#include <copyfile.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+
+bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
+ if (from_path.ReferencesParent() || to_path.ReferencesParent())
+ return false;
+ return (copyfile(from_path.value().c_str(), to_path.value().c_str(), NULL,
+ COPYFILE_DATA) == 0);
+}
+
+bool GetTempDir(base::FilePath* path) {
+ // In order to facilitate hermetic runs on macOS, first check
+ // $MAC_CHROMIUM_TMPDIR. We check this instead of $TMPDIR because external
+ // programs currently set $TMPDIR with no effect, but when we respect it
+ // directly it can cause crashes (like crbug.com/698759).
+ const char* env_tmpdir = getenv("MAC_CHROMIUM_TMPDIR");
+ if (env_tmpdir) {
+ DCHECK_LT(strlen(env_tmpdir), 50u)
+ << "too-long TMPDIR causes socket name length issues.";
+ *path = base::FilePath(env_tmpdir);
+ return true;
+ }
+
+ // If we didn't find it, fall back to the native function.
+ NSString* tmp = NSTemporaryDirectory();
+ if (tmp == nil)
+ return false;
+ *path = base::mac::NSStringToFilePath(tmp);
+ return true;
+}
+
+FilePath GetHomeDir() {
+ NSString* tmp = NSHomeDirectory();
+ if (tmp != nil) {
+ FilePath mac_home_dir = base::mac::NSStringToFilePath(tmp);
+ if (!mac_home_dir.empty())
+ return mac_home_dir;
+ }
+
+ // Fall back on temp dir if no home directory is defined.
+ FilePath rv;
+ if (GetTempDir(&rv))
+ return rv;
+
+ // Last resort.
+ return FilePath("/tmp");
+}
+
+} // namespace base
diff --git a/gn/base/files/file_util_posix.cc b/gn/base/files/file_util_posix.cc
new file mode 100644
index 00000000000..23c7cb4abc9
--- /dev/null
+++ b/gn/base/files/file_util_posix.cc
@@ -0,0 +1,977 @@
+// 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/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#include "base/mac/foundation_util.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() {
+#if defined(OS_MACOSX)
+ return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID());
+#endif
+
+#if defined(GOOGLE_CHROME_BUILD)
+ return std::string(".com.google.Chrome.XXXXXX");
+#else
+ return std::string(".org.chromium.Chromium.XXXXXX");
+#endif
+}
+
+#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;
+}
+
+bool DoCopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive,
+ bool open_exclusive) {
+ // Some old callers of CopyDirectory want it to support wildcards.
+ // After some discussion, we decided to fix those callers.
+ // Break loudly here if anyone tries to do this.
+ DCHECK(to_path.value().find('*') == std::string::npos);
+ DCHECK(from_path.value().find('*') == std::string::npos);
+
+ if (from_path.value().size() >= PATH_MAX) {
+ return false;
+ }
+
+ // This function does not properly handle destinations within the source
+ FilePath real_to_path = to_path;
+ if (PathExists(real_to_path))
+ real_to_path = MakeAbsoluteFilePath(real_to_path);
+ else
+ real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
+ if (real_to_path.empty())
+ return false;
+
+ FilePath real_from_path = MakeAbsoluteFilePath(from_path);
+ if (real_from_path.empty())
+ return false;
+ if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))
+ return false;
+
+ int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS;
+ if (recursive)
+ traverse_type |= FileEnumerator::DIRECTORIES;
+ FileEnumerator traversal(from_path, recursive, traverse_type);
+
+ // We have to mimic windows behavior here. |to_path| may not exist yet,
+ // start the loop with |to_path|.
+ struct stat from_stat;
+ FilePath current = from_path;
+ if (stat(from_path.value().c_str(), &from_stat) < 0) {
+ DPLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
+ << from_path.value();
+ return false;
+ }
+ FilePath from_path_base = from_path;
+ if (recursive && DirectoryExists(to_path)) {
+ // If the destination already exists and is a directory, then the
+ // top level of source needs to be copied.
+ from_path_base = from_path.DirName();
+ }
+
+ // The Windows version of this function assumes that non-recursive calls
+ // will always have a directory for from_path.
+ // TODO(maruel): This is not necessary anymore.
+ DCHECK(recursive || S_ISDIR(from_stat.st_mode));
+
+ do {
+ // current is the source path, including from_path, so append
+ // the suffix after from_path to to_path to create the target_path.
+ FilePath target_path(to_path);
+ if (from_path_base != current &&
+ !from_path_base.AppendRelativePath(current, &target_path)) {
+ return false;
+ }
+
+ if (S_ISDIR(from_stat.st_mode)) {
+ mode_t mode = (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR;
+ if (mkdir(target_path.value().c_str(), mode) == 0)
+ continue;
+ if (errno == EEXIST && !open_exclusive)
+ continue;
+
+ DPLOG(ERROR) << "CopyDirectory() couldn't create directory: "
+ << target_path.value();
+ return false;
+ }
+
+ if (!S_ISREG(from_stat.st_mode)) {
+ DLOG(WARNING) << "CopyDirectory() skipping non-regular file: "
+ << current.value();
+ continue;
+ }
+
+ // Add O_NONBLOCK so we can't block opening a pipe.
+ File infile(open(current.value().c_str(), O_RDONLY | O_NONBLOCK));
+ if (!infile.IsValid()) {
+ DPLOG(ERROR) << "CopyDirectory() couldn't open file: " << current.value();
+ return false;
+ }
+
+ struct stat stat_at_use;
+ if (fstat(infile.GetPlatformFile(), &stat_at_use) < 0) {
+ DPLOG(ERROR) << "CopyDirectory() couldn't stat file: " << current.value();
+ return false;
+ }
+
+ if (!S_ISREG(stat_at_use.st_mode)) {
+ DLOG(WARNING) << "CopyDirectory() skipping non-regular file: "
+ << current.value();
+ continue;
+ }
+
+ int open_flags = O_WRONLY | O_CREAT;
+ // If |open_exclusive| is set then we should always create the destination
+ // file, so O_NONBLOCK is not necessary to ensure we don't block on the
+ // open call for the target file below, and since the destination will
+ // always be a regular file it wouldn't affect the behavior of the
+ // subsequent write calls anyway.
+ if (open_exclusive)
+ open_flags |= O_EXCL;
+ else
+ open_flags |= O_TRUNC | O_NONBLOCK;
+// Each platform has different default file opening modes for CopyFile which
+// we want to replicate here. On OS X, we use copyfile(3) which takes the
+// source file's permissions into account. On the other platforms, we just
+// use the base::File constructor. On Chrome OS, base::File uses a different
+// set of permissions than it does on other POSIX platforms.
+#if defined(OS_MACOSX)
+ int mode = 0600 | (stat_at_use.st_mode & 0177);
+#else
+ int mode = 0600;
+#endif
+ File outfile(open(target_path.value().c_str(), open_flags, mode));
+ if (!outfile.IsValid()) {
+ DPLOG(ERROR) << "CopyDirectory() couldn't create file: "
+ << target_path.value();
+ return false;
+ }
+
+ if (!CopyFileContents(&infile, &outfile)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't copy file: " << current.value();
+ return false;
+ }
+ } while (AdvanceEnumeratorWithStat(&traversal, &current, &from_stat));
+
+ return true;
+}
+#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;
+}
+
+bool CopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive) {
+ return DoCopyDirectory(from_path, to_path, recursive, false);
+}
+
+bool CopyDirectoryExcl(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive) {
+ return DoCopyDirectory(from_path, to_path, recursive, true);
+}
+#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
+
+#if !defined(OS_MACOSX)
+// This is implemented in file_util_mac.mm for Mac.
+bool GetTempDir(FilePath* path) {
+ const char* tmp = getenv("TMPDIR");
+ if (tmp) {
+ *path = FilePath(tmp);
+ return true;
+ }
+
+ *path = FilePath("/tmp");
+ return true;
+}
+#endif // !defined(OS_MACOSX)
+
+#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)
+
+// -----------------------------------------------------------------------------
+
+namespace internal {
+
+bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
+ // Windows compatibility: if |to_path| exists, |from_path| and |to_path|
+ // must be the same type, either both files, or both directories.
+ stat_wrapper_t to_file_info;
+ if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
+ stat_wrapper_t from_file_info;
+ if (CallStat(from_path.value().c_str(), &from_file_info) != 0)
+ return false;
+ if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
+ return false;
+ }
+
+ if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
+ return true;
+
+ if (!CopyDirectory(from_path, to_path, true))
+ return false;
+
+ DeleteFile(from_path, true);
+ return true;
+}
+
+} // namespace internal
+
+#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
new file mode 100644
index 00000000000..2a1e9ecf704
--- /dev/null
+++ b/gn/base/files/file_util_win.cc
@@ -0,0 +1,896 @@
+// 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);
+}
+
+bool DoCopyFile(const FilePath& from_path,
+ const FilePath& to_path,
+ bool fail_if_exists) {
+ if (from_path.ReferencesParent() || to_path.ReferencesParent())
+ return false;
+
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.value().length() >= MAX_PATH ||
+ to_path.value().length() >= MAX_PATH) {
+ return false;
+ }
+
+ // Unlike the posix implementation that copies the file manually and discards
+ // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access
+ // bits, which is usually not what we want. We can't do much about the
+ // SECURITY_DESCRIPTOR but at least remove the read only bit.
+ const wchar_t* dest = to_path.value().c_str();
+ if (!::CopyFile(from_path.value().c_str(), dest, fail_if_exists)) {
+ // Copy failed.
+ return false;
+ }
+ DWORD attrs = GetFileAttributes(dest);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ if (attrs & FILE_ATTRIBUTE_READONLY) {
+ SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY);
+ }
+ return true;
+}
+
+bool DoCopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive,
+ bool fail_if_exists) {
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.value().length() >= MAX_PATH ||
+ to_path.value().length() >= MAX_PATH) {
+ return false;
+ }
+
+ // This function does not properly handle destinations within the source.
+ FilePath real_to_path = to_path;
+ if (PathExists(real_to_path)) {
+ real_to_path = MakeAbsoluteFilePath(real_to_path);
+ if (real_to_path.empty())
+ return false;
+ } else {
+ real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
+ if (real_to_path.empty())
+ return false;
+ }
+ FilePath real_from_path = MakeAbsoluteFilePath(from_path);
+ if (real_from_path.empty())
+ return false;
+ if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))
+ return false;
+
+ int traverse_type = FileEnumerator::FILES;
+ if (recursive)
+ traverse_type |= FileEnumerator::DIRECTORIES;
+ FileEnumerator traversal(from_path, recursive, traverse_type);
+
+ if (!PathExists(from_path)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
+ << from_path.value().c_str();
+ return false;
+ }
+ // TODO(maruel): This is not necessary anymore.
+ DCHECK(recursive || DirectoryExists(from_path));
+
+ FilePath current = from_path;
+ bool from_is_dir = DirectoryExists(from_path);
+ bool success = true;
+ FilePath from_path_base = from_path;
+ if (recursive && DirectoryExists(to_path)) {
+ // If the destination already exists and is a directory, then the
+ // top level of source needs to be copied.
+ from_path_base = from_path.DirName();
+ }
+
+ while (success && !current.empty()) {
+ // current is the source path, including from_path, so append
+ // the suffix after from_path to to_path to create the target_path.
+ FilePath target_path(to_path);
+ if (from_path_base != current) {
+ if (!from_path_base.AppendRelativePath(current, &target_path)) {
+ success = false;
+ break;
+ }
+ }
+
+ if (from_is_dir) {
+ if (!DirectoryExists(target_path) &&
+ !::CreateDirectory(target_path.value().c_str(), NULL)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
+ << target_path.value().c_str();
+ success = false;
+ }
+ } else if (!DoCopyFile(current, target_path, fail_if_exists)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't create file: "
+ << target_path.value().c_str();
+ success = false;
+ }
+
+ current = traversal.Next();
+ if (!current.empty())
+ from_is_dir = traversal.GetInfo().IsDirectory();
+ }
+
+ return success;
+}
+
+// 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 CopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive) {
+ return DoCopyDirectory(from_path, to_path, recursive, false);
+}
+
+bool CopyDirectoryExcl(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive) {
+ return DoCopyDirectory(from_path, to_path, recursive, true);
+}
+
+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;
+}
+
+FilePath GetHomeDir() {
+ char16 result[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT,
+ result)) &&
+ result[0]) {
+ return FilePath(result);
+ }
+
+ // Fall back to the temporary directory on failure.
+ FilePath temp;
+ if (GetTempDir(&temp))
+ return temp;
+
+ // Last resort.
+ return FilePath(L"C:\\");
+}
+
+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 CopyFile(const FilePath& from_path, const FilePath& to_path) {
+ return DoCopyFile(from_path, to_path, false);
+}
+
+bool SetNonBlocking(int fd) {
+ unsigned long nonblocking = 1;
+ if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0)
+ return true;
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+namespace internal {
+
+bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.value().length() >= MAX_PATH ||
+ to_path.value().length() >= MAX_PATH) {
+ return false;
+ }
+ if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
+ MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
+ return true;
+
+ // Keep the last error value from MoveFileEx around in case the below
+ // fails.
+ bool ret = false;
+ DWORD last_error = ::GetLastError();
+
+ if (DirectoryExists(from_path)) {
+ // MoveFileEx fails if moving directory across volumes. We will simulate
+ // the move by using Copy and Delete. Ideally we could check whether
+ // from_path and to_path are indeed in different volumes.
+ ret = internal::CopyAndDeleteDirectory(from_path, to_path);
+ }
+
+ if (!ret) {
+ // Leave a clue about what went wrong so that it can be (at least) picked
+ // up by a PLOG entry.
+ ::SetLastError(last_error);
+ }
+
+ return ret;
+}
+
+bool CopyAndDeleteDirectory(const FilePath& from_path,
+ const FilePath& to_path) {
+ if (CopyDirectory(from_path, to_path, true)) {
+ if (DeleteFile(from_path, true))
+ return true;
+
+ // Like Move, this function is not transactional, so we just
+ // leave the copied bits behind if deleting from_path fails.
+ // If to_path exists previously then we have already overwritten
+ // it by now, we don't get better off by deleting the new bits.
+ }
+ return false;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/gn/base/files/file_win.cc b/gn/base/files/file_win.cc
new file mode 100644
index 00000000000..7ca4461e94f
--- /dev/null
+++ b/gn/base/files/file_win.cc
@@ -0,0 +1,381 @@
+// 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
new file mode 100644
index 00000000000..cba2fc8ad6c
--- /dev/null
+++ b/gn/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 "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
new file mode 100644
index 00000000000..11afedd695e
--- /dev/null
+++ b/gn/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)
+ // 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/base/files/scoped_file.h
new file mode 100644
index 00000000000..0d896504300
--- /dev/null
+++ b/gn/base/files/scoped_file.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. 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_SCOPED_FILE_H_
+#define BASE_FILES_SCOPED_FILE_H_
+
+#include <stdio.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/scoped_generic.h"
+#include "util/build_config.h"
+
+namespace base {
+
+namespace internal {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+struct ScopedFDCloseTraits {
+ static int InvalidValue() { return -1; }
+ static void Free(int fd);
+};
+#endif
+
+// Functor for |ScopedFILE| (below).
+struct ScopedFILECloser {
+ inline void operator()(FILE* x) const {
+ if (x)
+ fclose(x);
+ }
+};
+
+} // namespace internal
+
+// -----------------------------------------------------------------------------
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// A low-level Posix file descriptor closer class. Use this when writing
+// platform-specific code, especially that does non-file-like things with the
+// FD (like sockets).
+//
+// If you're writing low-level Windows code, see base/win/scoped_handle.h
+// which provides some additional functionality.
+//
+// If you're writing cross-platform code that deals with actual files, you
+// should generally use base::File instead which can be constructed with a
+// handle, and in addition to handling ownership, has convenient cross-platform
+// file manipulation functions on it.
+typedef ScopedGeneric<int, internal::ScopedFDCloseTraits> ScopedFD;
+#endif
+
+// Automatically closes |FILE*|s.
+typedef std::unique_ptr<FILE, internal::ScopedFILECloser> ScopedFILE;
+
+} // namespace base
+
+#endif // BASE_FILES_SCOPED_FILE_H_
diff --git a/gn/base/files/scoped_temp_dir.cc b/gn/base/files/scoped_temp_dir.cc
new file mode 100644
index 00000000000..01ec0f0caab
--- /dev/null
+++ b/gn/base/files/scoped_temp_dir.cc
@@ -0,0 +1,97 @@
+// 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/scoped_temp_dir.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+
+namespace base {
+
+namespace {
+
+constexpr FilePath::CharType kScopedDirPrefix[] =
+ FILE_PATH_LITERAL("scoped_dir");
+
+} // namespace
+
+ScopedTempDir::ScopedTempDir() = default;
+
+ScopedTempDir::~ScopedTempDir() {
+ if (!path_.empty() && !Delete())
+ DLOG(WARNING) << "Could not delete temp dir in dtor.";
+}
+
+bool ScopedTempDir::CreateUniqueTempDir() {
+ if (!path_.empty())
+ return false;
+
+ // This "scoped_dir" prefix is only used on Windows and serves as a template
+ // for the unique name.
+ if (!base::CreateNewTempDirectory(kScopedDirPrefix, &path_))
+ return false;
+
+ return true;
+}
+
+bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) {
+ if (!path_.empty())
+ return false;
+
+ // If |base_path| does not exist, create it.
+ if (!base::CreateDirectory(base_path))
+ return false;
+
+ // Create a new, uniquely named directory under |base_path|.
+ if (!base::CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_))
+ return false;
+
+ return true;
+}
+
+bool ScopedTempDir::Set(const FilePath& path) {
+ if (!path_.empty())
+ return false;
+
+ if (!DirectoryExists(path) && !base::CreateDirectory(path))
+ return false;
+
+ path_ = path;
+ return true;
+}
+
+bool ScopedTempDir::Delete() {
+ if (path_.empty())
+ return false;
+
+ bool ret = base::DeleteFile(path_, true);
+ if (ret) {
+ // We only clear the path if deleted the directory.
+ path_.clear();
+ }
+
+ return ret;
+}
+
+FilePath ScopedTempDir::Take() {
+ FilePath ret = path_;
+ path_ = FilePath();
+ return ret;
+}
+
+const FilePath& ScopedTempDir::GetPath() const {
+ DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?";
+ return path_;
+}
+
+bool ScopedTempDir::IsValid() const {
+ return !path_.empty() && DirectoryExists(path_);
+}
+
+// static
+const FilePath::CharType* ScopedTempDir::GetTempDirPrefix() {
+ return kScopedDirPrefix;
+}
+
+} // namespace base
diff --git a/gn/base/files/scoped_temp_dir.h b/gn/base/files/scoped_temp_dir.h
new file mode 100644
index 00000000000..4ddb690945c
--- /dev/null
+++ b/gn/base/files/scoped_temp_dir.h
@@ -0,0 +1,70 @@
+// 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_FILES_SCOPED_TEMP_DIR_H_
+#define BASE_FILES_SCOPED_TEMP_DIR_H_
+
+// An object representing a temporary / scratch directory that should be
+// cleaned up (recursively) when this object goes out of scope. Since deletion
+// occurs during the destructor, no further error handling is possible if the
+// directory fails to be deleted. As a result, deletion is not guaranteed by
+// this class. (However note that, whenever possible, by default
+// CreateUniqueTempDir creates the directory in a location that is
+// automatically cleaned up on reboot, or at other appropriate times.)
+//
+// Multiple calls to the methods which establish a temporary directory
+// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have
+// intervening calls to Delete or Take, or the calls will fail.
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace base {
+
+class ScopedTempDir {
+ public:
+ // No directory is owned/created initially.
+ ScopedTempDir();
+
+ // Recursively delete path.
+ ~ScopedTempDir();
+
+ // Creates a unique directory in TempPath, and takes ownership of it.
+ // See file_util::CreateNewTemporaryDirectory.
+ bool CreateUniqueTempDir() WARN_UNUSED_RESULT;
+
+ // Creates a unique directory under a given path, and takes ownership of it.
+ bool CreateUniqueTempDirUnderPath(const FilePath& path) WARN_UNUSED_RESULT;
+
+ // Takes ownership of directory at |path|, creating it if necessary.
+ // Don't call multiple times unless Take() has been called first.
+ bool Set(const FilePath& path) WARN_UNUSED_RESULT;
+
+ // Deletes the temporary directory wrapped by this object.
+ bool Delete() WARN_UNUSED_RESULT;
+
+ // Caller takes ownership of the temporary directory so it won't be destroyed
+ // when this object goes out of scope.
+ FilePath Take();
+
+ // Returns the path to the created directory. Call one of the
+ // CreateUniqueTempDir* methods before getting the path.
+ const FilePath& GetPath() const;
+
+ // Returns true if path_ is non-empty and exists.
+ bool IsValid() const;
+
+ // Returns the prefix used for temp directory names generated by
+ // ScopedTempDirs.
+ static const FilePath::CharType* GetTempDirPrefix();
+
+ private:
+ FilePath path_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTempDir);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_SCOPED_TEMP_DIR_H_
diff --git a/gn/base/gtest_prod_util.h b/gn/base/gtest_prod_util.h
new file mode 100644
index 00000000000..664dded781f
--- /dev/null
+++ b/gn/base/gtest_prod_util.h
@@ -0,0 +1,16 @@
+// 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
new file mode 100644
index 00000000000..713f69223b1
--- /dev/null
+++ b/gn/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 <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
new file mode 100644
index 00000000000..1289560983b
--- /dev/null
+++ b/gn/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 <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
new file mode 100644
index 00000000000..3d5475e8065
--- /dev/null
+++ b/gn/base/json/json_reader.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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
new file mode 100644
index 00000000000..c4f7c8fc738
--- /dev/null
+++ b/gn/base/json/json_reader.h
@@ -0,0 +1,134 @@
+// 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
new file mode 100644
index 00000000000..55506e6b592
--- /dev/null
+++ b/gn/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<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
new file mode 100644
index 00000000000..d91bead803f
--- /dev/null
+++ b/gn/base/json/json_value_converter.h
@@ -0,0 +1,512 @@
+// 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
new file mode 100644
index 00000000000..656a87c22ee
--- /dev/null
+++ b/gn/base/json/json_writer.cc
@@ -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.
+
+#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
new file mode 100644
index 00000000000..7edd3a68680
--- /dev/null
+++ b/gn/base/json/json_writer.h
@@ -0,0 +1,74 @@
+// 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
new file mode 100644
index 00000000000..471a9d30cfa
--- /dev/null
+++ b/gn/base/json/string_escape.cc
@@ -0,0 +1,167 @@
+// 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
new file mode 100644
index 00000000000..d318b6aa1c1
--- /dev/null
+++ b/gn/base/json/string_escape.h
@@ -0,0 +1,55 @@
+// 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
new file mode 100644
index 00000000000..c381a284a89
--- /dev/null
+++ b/gn/base/logging.cc
@@ -0,0 +1,331 @@
+// 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/sys_string_conversions.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
new file mode 100644
index 00000000000..2edad38c4a5
--- /dev/null
+++ b/gn/base/logging.h
@@ -0,0 +1,956 @@
+// 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/mac/bundle_locations.h b/gn/base/mac/bundle_locations.h
new file mode 100644
index 00000000000..2bda76e6411
--- /dev/null
+++ b/gn/base/mac/bundle_locations.h
@@ -0,0 +1,65 @@
+// 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_MAC_BUNDLE_LOCATIONS_H_
+#define BASE_MAC_BUNDLE_LOCATIONS_H_
+
+#include "base/files/file_path.h"
+
+#if defined(__OBJC__)
+#import <Foundation/Foundation.h>
+#else // __OBJC__
+class NSBundle;
+#endif // __OBJC__
+
+namespace base {
+
+class FilePath;
+
+namespace mac {
+
+// This file provides several functions to explicitly request the various
+// component bundles of Chrome. Please use these methods rather than calling
+// +[NSBundle mainBundle] or CFBundleGetMainBundle().
+//
+// Terminology
+// - "Outer Bundle" - This is the main bundle for Chrome; it's what
+// +[NSBundle mainBundle] returns when Chrome is launched normally.
+//
+// - "Main Bundle" - This is the bundle from which Chrome was launched.
+// This will be the same as the outer bundle except when Chrome is launched
+// via an app shortcut, in which case this will return the app shortcut's
+// bundle rather than the main Chrome bundle.
+//
+// - "Framework Bundle" - This is the bundle corresponding to the Chrome
+// framework.
+//
+// Guidelines for use:
+// - To access a resource, the Framework bundle should be used.
+// - If the choice is between the Outer or Main bundles then please choose
+// carefully. Most often the Outer bundle will be the right choice, but for
+// cases such as adding an app to the "launch on startup" list, the Main
+// bundle is probably the one to use.
+
+// Methods for retrieving the various bundles.
+NSBundle* MainBundle();
+FilePath MainBundlePath();
+NSBundle* OuterBundle();
+FilePath OuterBundlePath();
+NSBundle* FrameworkBundle();
+FilePath FrameworkBundlePath();
+
+// Set the bundle that the preceding functions will return, overriding the
+// default values. Restore the default by passing in |nil|.
+void SetOverrideOuterBundle(NSBundle* bundle);
+void SetOverrideFrameworkBundle(NSBundle* bundle);
+
+// Same as above but accepting a FilePath argument.
+void SetOverrideOuterBundlePath(const FilePath& file_path);
+void SetOverrideFrameworkBundlePath(const FilePath& file_path);
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_BUNDLE_LOCATIONS_H_
diff --git a/gn/base/mac/bundle_locations.mm b/gn/base/mac/bundle_locations.mm
new file mode 100644
index 00000000000..54021b85ee0
--- /dev/null
+++ b/gn/base/mac/bundle_locations.mm
@@ -0,0 +1,83 @@
+// 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/mac/bundle_locations.h"
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace base {
+namespace mac {
+
+// NSBundle isn't threadsafe, all functions in this file must be called on the
+// main thread.
+static NSBundle* g_override_framework_bundle = nil;
+static NSBundle* g_override_outer_bundle = nil;
+
+NSBundle* MainBundle() {
+ return [NSBundle mainBundle];
+}
+
+FilePath MainBundlePath() {
+ NSBundle* bundle = MainBundle();
+ return NSStringToFilePath([bundle bundlePath]);
+}
+
+NSBundle* OuterBundle() {
+ if (g_override_outer_bundle)
+ return g_override_outer_bundle;
+ return [NSBundle mainBundle];
+}
+
+FilePath OuterBundlePath() {
+ NSBundle* bundle = OuterBundle();
+ return NSStringToFilePath([bundle bundlePath]);
+}
+
+NSBundle* FrameworkBundle() {
+ if (g_override_framework_bundle)
+ return g_override_framework_bundle;
+ return [NSBundle mainBundle];
+}
+
+FilePath FrameworkBundlePath() {
+ NSBundle* bundle = FrameworkBundle();
+ return NSStringToFilePath([bundle bundlePath]);
+}
+
+static void AssignOverrideBundle(NSBundle* new_bundle,
+ NSBundle** override_bundle) {
+ if (new_bundle != *override_bundle) {
+ [*override_bundle release];
+ *override_bundle = [new_bundle retain];
+ }
+}
+
+static void AssignOverridePath(const FilePath& file_path,
+ NSBundle** override_bundle) {
+ NSString* path = base::SysUTF8ToNSString(file_path.value());
+ NSBundle* new_bundle = [NSBundle bundleWithPath:path];
+ DCHECK(new_bundle) << "Failed to load the bundle at " << file_path.value();
+ AssignOverrideBundle(new_bundle, override_bundle);
+}
+
+void SetOverrideOuterBundle(NSBundle* bundle) {
+ AssignOverrideBundle(bundle, &g_override_outer_bundle);
+}
+
+void SetOverrideFrameworkBundle(NSBundle* bundle) {
+ AssignOverrideBundle(bundle, &g_override_framework_bundle);
+}
+
+void SetOverrideOuterBundlePath(const FilePath& file_path) {
+ AssignOverridePath(file_path, &g_override_outer_bundle);
+}
+
+void SetOverrideFrameworkBundlePath(const FilePath& file_path) {
+ AssignOverridePath(file_path, &g_override_framework_bundle);
+}
+
+} // namespace mac
+} // namespace base
diff --git a/gn/base/mac/foundation_util.h b/gn/base/mac/foundation_util.h
new file mode 100644
index 00000000000..6e1ce55cd2c
--- /dev/null
+++ b/gn/base/mac/foundation_util.h
@@ -0,0 +1,406 @@
+// 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_MAC_FOUNDATION_UTIL_H_
+#define BASE_MAC_FOUNDATION_UTIL_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "util/build_config.h"
+
+#if defined(__OBJC__)
+#import <Foundation/Foundation.h>
+@class NSFont;
+@class UIFont;
+#else // __OBJC__
+#include <CoreFoundation/CoreFoundation.h>
+class NSBundle;
+class NSFont;
+class NSString;
+class UIFont;
+#endif // __OBJC__
+
+#if defined(OS_IOS)
+#include <CoreText/CoreText.h>
+#else
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+// Adapted from NSObjCRuntime.h NS_ENUM definition (used in Foundation starting
+// with the OS X 10.8 SDK and the iOS 6.0 SDK).
+#if __has_extension(cxx_strong_enums) && \
+ (defined(OS_IOS) || \
+ (defined(MAC_OS_X_VERSION_10_8) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8))
+#define CR_FORWARD_ENUM(_type, _name) enum _name : _type _name
+#else
+#define CR_FORWARD_ENUM(_type, _name) _type _name
+#endif
+
+// Adapted from NSPathUtilities.h and NSObjCRuntime.h.
+#if __LP64__ || NS_BUILD_32_LIKE_64
+typedef CR_FORWARD_ENUM(unsigned long, NSSearchPathDirectory);
+typedef unsigned long NSSearchPathDomainMask;
+#else
+typedef CR_FORWARD_ENUM(unsigned int, NSSearchPathDirectory);
+typedef unsigned int NSSearchPathDomainMask;
+#endif
+
+typedef struct OpaqueSecTrustRef* SecACLRef;
+typedef struct OpaqueSecTrustedApplicationRef* SecTrustedApplicationRef;
+
+#if defined(OS_IOS)
+typedef struct CF_BRIDGED_TYPE(id) __SecKey* SecKeyRef;
+typedef struct CF_BRIDGED_TYPE(id) __SecPolicy* SecPolicyRef;
+#else
+typedef struct OpaqueSecKeyRef* SecKeyRef;
+typedef struct OpaqueSecPolicyRef* SecPolicyRef;
+#endif
+
+namespace base {
+
+class FilePath;
+
+namespace mac {
+
+// Returns true if the application is running from a bundle
+bool AmIBundled();
+void SetOverrideAmIBundled(bool value);
+
+#if defined(UNIT_TEST)
+// This is required because instantiating some tests requires checking the
+// directory structure, which sets the AmIBundled cache state. Individual tests
+// may or may not be bundled, and this would trip them up if the cache weren't
+// cleared. This should not be called from individual tests, just from test
+// instantiation code that gets a path from PathService.
+void ClearAmIBundledCache();
+#endif
+
+// Returns true if this process is marked as a "Background only process".
+bool IsBackgroundOnlyProcess();
+
+// Returns the path to a resource within the framework bundle.
+FilePath PathForFrameworkBundleResource(CFStringRef resourceName);
+
+// Returns the creator code associated with the CFBundleRef at bundle.
+OSType CreatorCodeForCFBundleRef(CFBundleRef bundle);
+
+// Returns the creator code associated with this application, by calling
+// CreatorCodeForCFBundleRef for the application's main bundle. If this
+// information cannot be determined, returns kUnknownType ('????'). This
+// does not respect the override app bundle because it's based on CFBundle
+// instead of NSBundle, and because callers probably don't want the override
+// app bundle's creator code anyway.
+OSType CreatorCodeForApplication();
+
+// Searches for directories for the given key in only the given |domain_mask|.
+// If found, fills result (which must always be non-NULL) with the
+// first found directory and returns true. Otherwise, returns false.
+bool GetSearchPathDirectory(NSSearchPathDirectory directory,
+ NSSearchPathDomainMask domain_mask,
+ FilePath* result);
+
+// Searches for directories for the given key in only the local domain.
+// If found, fills result (which must always be non-NULL) with the
+// first found directory and returns true. Otherwise, returns false.
+bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result);
+
+// Searches for directories for the given key in only the user domain.
+// If found, fills result (which must always be non-NULL) with the
+// first found directory and returns true. Otherwise, returns false.
+bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result);
+
+// Returns the ~/Library directory.
+FilePath GetUserLibraryPath();
+
+// Takes a path to an (executable) binary and tries to provide the path to an
+// application bundle containing it. It takes the outermost bundle that it can
+// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
+// |exec_name| - path to the binary
+// returns - path to the application bundle, or empty on error
+FilePath GetAppBundlePath(const FilePath& exec_name);
+
+#define TYPE_NAME_FOR_CF_TYPE_DECL(TypeCF) \
+ std::string TypeNameForCFType(TypeCF##Ref);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(CFArray);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFBag);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFBoolean);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFData);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFDate);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFDictionary);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFNull);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFNumber);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFSet);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFString);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFURL);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFUUID);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(CGColor);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(CTFont);
+TYPE_NAME_FOR_CF_TYPE_DECL(CTRun);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(SecKey);
+TYPE_NAME_FOR_CF_TYPE_DECL(SecPolicy);
+
+#undef TYPE_NAME_FOR_CF_TYPE_DECL
+
+// Retain/release calls for memory management in C++.
+void NSObjectRetain(void* obj);
+void NSObjectRelease(void* obj);
+
+// CFTypeRefToNSObjectAutorelease transfers ownership of a Core Foundation
+// object (one derived from CFTypeRef) to the Foundation memory management
+// system. In a traditional managed-memory environment, cf_object is
+// autoreleased and returned as an NSObject. In a garbage-collected
+// environment, cf_object is marked as eligible for garbage collection.
+//
+// This function should only be used to convert a concrete CFTypeRef type to
+// its equivalent "toll-free bridged" NSObject subclass, for example,
+// converting a CFStringRef to NSString.
+//
+// By calling this function, callers relinquish any ownership claim to
+// cf_object. In a managed-memory environment, the object's ownership will be
+// managed by the innermost NSAutoreleasePool, so after this function returns,
+// callers should not assume that cf_object is valid any longer than the
+// returned NSObject.
+//
+// Returns an id, typed here for C++'s sake as a void*.
+void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object);
+
+// Returns the base bundle ID, which can be set by SetBaseBundleID but
+// defaults to a reasonable string. This never returns NULL. BaseBundleID
+// returns a pointer to static storage that must not be freed.
+const char* BaseBundleID();
+
+// Sets the base bundle ID to override the default. The implementation will
+// make its own copy of new_base_bundle_id.
+void SetBaseBundleID(const char* new_base_bundle_id);
+
+} // namespace mac
+} // namespace base
+
+#if !defined(__OBJC__)
+#define OBJC_CPP_CLASS_DECL(x) class x;
+#else // __OBJC__
+#define OBJC_CPP_CLASS_DECL(x)
+#endif // __OBJC__
+
+// Convert toll-free bridged CFTypes to NSTypes and vice-versa. This does not
+// autorelease |cf_val|. This is useful for the case where there is a CFType in
+// a call that expects an NSType and the compiler is complaining about const
+// casting problems.
+// The calls are used like this:
+// NSString *foo = CFToNSCast(CFSTR("Hello"));
+// CFStringRef foo2 = NSToCFCast(@"Hello");
+// The macro magic below is to enforce safe casting. It could possibly have
+// been done using template function specialization, but template function
+// specialization doesn't always work intuitively,
+// (http://www.gotw.ca/publications/mill17.htm) so the trusty combination
+// of macros and function overloading is used instead.
+
+#define CF_TO_NS_CAST_DECL(TypeCF, TypeNS) \
+ OBJC_CPP_CLASS_DECL(TypeNS) \
+ \
+ namespace base { \
+ namespace mac { \
+ TypeNS* CFToNSCast(TypeCF##Ref cf_val); \
+ TypeCF##Ref NSToCFCast(TypeNS* ns_val); \
+ } \
+ }
+
+#define CF_TO_NS_MUTABLE_CAST_DECL(name) \
+ CF_TO_NS_CAST_DECL(CF##name, NS##name) \
+ OBJC_CPP_CLASS_DECL(NSMutable##name) \
+ \
+ namespace base { \
+ namespace mac { \
+ NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val); \
+ CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val); \
+ } \
+ }
+
+// List of toll-free bridged types taken from:
+// http://www.cocoadev.com/index.pl?TollFreeBridged
+
+CF_TO_NS_MUTABLE_CAST_DECL(Array);
+CF_TO_NS_MUTABLE_CAST_DECL(AttributedString);
+CF_TO_NS_CAST_DECL(CFCalendar, NSCalendar);
+CF_TO_NS_MUTABLE_CAST_DECL(CharacterSet);
+CF_TO_NS_MUTABLE_CAST_DECL(Data);
+CF_TO_NS_CAST_DECL(CFDate, NSDate);
+CF_TO_NS_MUTABLE_CAST_DECL(Dictionary);
+CF_TO_NS_CAST_DECL(CFError, NSError);
+CF_TO_NS_CAST_DECL(CFLocale, NSLocale);
+CF_TO_NS_CAST_DECL(CFNumber, NSNumber);
+CF_TO_NS_CAST_DECL(CFRunLoopTimer, NSTimer);
+CF_TO_NS_CAST_DECL(CFTimeZone, NSTimeZone);
+CF_TO_NS_MUTABLE_CAST_DECL(Set);
+CF_TO_NS_CAST_DECL(CFReadStream, NSInputStream);
+CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream);
+CF_TO_NS_MUTABLE_CAST_DECL(String);
+CF_TO_NS_CAST_DECL(CFURL, NSURL);
+
+#if defined(OS_IOS)
+CF_TO_NS_CAST_DECL(CTFont, UIFont);
+#else
+CF_TO_NS_CAST_DECL(CTFont, NSFont);
+#endif
+
+#undef CF_TO_NS_CAST_DECL
+#undef CF_TO_NS_MUTABLE_CAST_DECL
+#undef OBJC_CPP_CLASS_DECL
+
+namespace base {
+namespace mac {
+
+// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more
+// specific CoreFoundation type. The compatibility of the passed
+// object is found by comparing its opaque type against the
+// requested type identifier. If the supplied object is not
+// compatible with the requested return type, CFCast<>() returns
+// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer
+// to either variant results in NULL being returned without
+// triggering any DCHECK.
+//
+// Example usage:
+// CFNumberRef some_number = base::mac::CFCast<CFNumberRef>(
+// CFArrayGetValueAtIndex(array, index));
+//
+// CFTypeRef hello = CFSTR("hello world");
+// CFStringRef some_string = base::mac::CFCastStrict<CFStringRef>(hello);
+
+template <typename T>
+T CFCast(const CFTypeRef& cf_val);
+
+template <typename T>
+T CFCastStrict(const CFTypeRef& cf_val);
+
+#define CF_CAST_DECL(TypeCF) \
+ template <> \
+ TypeCF##Ref CFCast<TypeCF##Ref>(const CFTypeRef& cf_val); \
+ \
+ template <> \
+ TypeCF##Ref CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val);
+
+CF_CAST_DECL(CFArray);
+CF_CAST_DECL(CFBag);
+CF_CAST_DECL(CFBoolean);
+CF_CAST_DECL(CFData);
+CF_CAST_DECL(CFDate);
+CF_CAST_DECL(CFDictionary);
+CF_CAST_DECL(CFNull);
+CF_CAST_DECL(CFNumber);
+CF_CAST_DECL(CFSet);
+CF_CAST_DECL(CFString);
+CF_CAST_DECL(CFURL);
+CF_CAST_DECL(CFUUID);
+
+CF_CAST_DECL(CGColor);
+
+CF_CAST_DECL(CTFont);
+CF_CAST_DECL(CTFontDescriptor);
+CF_CAST_DECL(CTRun);
+
+CF_CAST_DECL(SecACL);
+CF_CAST_DECL(SecKey);
+CF_CAST_DECL(SecPolicy);
+CF_CAST_DECL(SecTrustedApplication);
+
+#undef CF_CAST_DECL
+
+#if defined(__OBJC__)
+
+// ObjCCast<>() and ObjCCastStrict<>() cast a basic id to a more
+// specific (NSObject-derived) type. The compatibility of the passed
+// object is found by checking if it's a kind of the requested type
+// identifier. If the supplied object is not compatible with the
+// requested return type, ObjCCast<>() returns nil and
+// ObjCCastStrict<>() will DCHECK. Providing a nil pointer to either
+// variant results in nil being returned without triggering any DCHECK.
+//
+// The strict variant is useful when retrieving a value from a
+// collection which only has values of a specific type, e.g. an
+// NSArray of NSStrings. The non-strict variant is useful when
+// retrieving values from data that you can't fully control. For
+// example, a plist read from disk may be beyond your exclusive
+// control, so you'd only want to check that the values you retrieve
+// from it are of the expected types, but not crash if they're not.
+//
+// Example usage:
+// NSString* version = base::mac::ObjCCast<NSString>(
+// [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]);
+//
+// NSString* str = base::mac::ObjCCastStrict<NSString>(
+// [ns_arr_of_ns_strs objectAtIndex:0]);
+template <typename T>
+T* ObjCCast(id objc_val) {
+ if ([objc_val isKindOfClass:[T class]]) {
+ return reinterpret_cast<T*>(objc_val);
+ }
+ return nil;
+}
+
+template <typename T>
+T* ObjCCastStrict(id objc_val) {
+ T* rv = ObjCCast<T>(objc_val);
+ DCHECK(objc_val == nil || rv);
+ return rv;
+}
+
+#endif // defined(__OBJC__)
+
+// Helper function for GetValueFromDictionary to create the error message
+// that appears when a type mismatch is encountered.
+std::string GetValueFromDictionaryErrorMessage(CFStringRef key,
+ const std::string& expected_type,
+ CFTypeRef value);
+
+// Utility function to pull out a value from a dictionary, check its type, and
+// return it. Returns NULL if the key is not present or of the wrong type.
+template <typename T>
+T GetValueFromDictionary(CFDictionaryRef dict, CFStringRef key) {
+ CFTypeRef value = CFDictionaryGetValue(dict, key);
+ T value_specific = CFCast<T>(value);
+
+ if (value && !value_specific) {
+ std::string expected_type = TypeNameForCFType(value_specific);
+ DLOG(WARNING) << GetValueFromDictionaryErrorMessage(key, expected_type,
+ value);
+ }
+
+ return value_specific;
+}
+
+// Converts |path| to an autoreleased NSString. Returns nil if |path| is empty.
+NSString* FilePathToNSString(const FilePath& path);
+
+// Converts |str| to a FilePath. Returns an empty path if |str| is nil.
+FilePath NSStringToFilePath(NSString* str);
+
+#if defined(__OBJC__)
+// Converts |range| to an NSRange, returning the new range in |range_out|.
+// Returns true if conversion was successful, false if the values of |range|
+// could not be converted to NSUIntegers.
+bool CFRangeToNSRange(CFRange range, NSRange* range_out) WARN_UNUSED_RESULT;
+#endif // defined(__OBJC__)
+
+} // namespace mac
+} // namespace base
+
+// Stream operations for CFTypes. They can be used with NSTypes as well
+// by using the NSToCFCast methods above.
+// e.g. LOG(INFO) << base::mac::NSToCFCast(@"foo");
+// Operator << can not be overloaded for ObjectiveC types as the compiler
+// can not distinguish between overloads for id with overloads for void*.
+extern std::ostream& operator<<(std::ostream& o, const CFErrorRef err);
+extern std::ostream& operator<<(std::ostream& o, const CFStringRef str);
+
+#endif // BASE_MAC_FOUNDATION_UTIL_H_
diff --git a/gn/base/mac/foundation_util.mm b/gn/base/mac/foundation_util.mm
new file mode 100644
index 00000000000..d88ada8f395
--- /dev/null
+++ b/gn/base/mac/foundation_util.mm
@@ -0,0 +1,475 @@
+// 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/mac/foundation_util.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/mac_logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "util/build_config.h"
+
+#if !defined(OS_IOS)
+#import <AppKit/AppKit.h>
+#endif
+
+extern "C" {
+CFTypeID SecKeyGetTypeID();
+#if !defined(OS_IOS)
+CFTypeID SecACLGetTypeID();
+CFTypeID SecTrustedApplicationGetTypeID();
+Boolean _CFIsObjC(CFTypeID typeID, CFTypeRef obj);
+#endif
+} // extern "C"
+
+namespace base {
+namespace mac {
+
+namespace {
+
+bool g_cached_am_i_bundled_called = false;
+bool g_cached_am_i_bundled_value = false;
+bool g_override_am_i_bundled = false;
+bool g_override_am_i_bundled_value = false;
+
+bool UncachedAmIBundled() {
+#if defined(OS_IOS)
+ // All apps are bundled on iOS.
+ return true;
+#else
+ if (g_override_am_i_bundled)
+ return g_override_am_i_bundled_value;
+
+ // Yes, this is cheap.
+ return [[base::mac::OuterBundle() bundlePath] hasSuffix:@".app"];
+#endif
+}
+
+} // namespace
+
+bool AmIBundled() {
+ // If the return value is not cached, this function will return different
+ // values depending on when it's called. This confuses some client code, see
+ // http://crbug.com/63183 .
+ if (!g_cached_am_i_bundled_called) {
+ g_cached_am_i_bundled_called = true;
+ g_cached_am_i_bundled_value = UncachedAmIBundled();
+ }
+ DCHECK_EQ(g_cached_am_i_bundled_value, UncachedAmIBundled())
+ << "The return value of AmIBundled() changed. This will confuse tests. "
+ << "Call SetAmIBundled() override manually if your test binary "
+ << "delay-loads the framework.";
+ return g_cached_am_i_bundled_value;
+}
+
+void SetOverrideAmIBundled(bool value) {
+#if defined(OS_IOS)
+ // It doesn't make sense not to be bundled on iOS.
+ if (!value)
+ NOTREACHED();
+#endif
+ g_override_am_i_bundled = true;
+ g_override_am_i_bundled_value = value;
+}
+
+void ClearAmIBundledCache() {
+ g_cached_am_i_bundled_called = false;
+}
+
+bool IsBackgroundOnlyProcess() {
+ // This function really does want to examine NSBundle's idea of the main
+ // bundle dictionary. It needs to look at the actual running .app's
+ // Info.plist to access its LSUIElement property.
+ NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary];
+ return [info_dictionary[@"LSUIElement"] boolValue] != NO;
+}
+
+FilePath PathForFrameworkBundleResource(CFStringRef resourceName) {
+ NSBundle* bundle = base::mac::FrameworkBundle();
+ NSString* resourcePath =
+ [bundle pathForResource:(NSString*)resourceName ofType:nil];
+ return NSStringToFilePath(resourcePath);
+}
+
+OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
+ OSType creator = kUnknownType;
+ CFBundleGetPackageInfo(bundle, NULL, &creator);
+ return creator;
+}
+
+OSType CreatorCodeForApplication() {
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if (!bundle)
+ return kUnknownType;
+
+ return CreatorCodeForCFBundleRef(bundle);
+}
+
+bool GetSearchPathDirectory(NSSearchPathDirectory directory,
+ NSSearchPathDomainMask domain_mask,
+ FilePath* result) {
+ DCHECK(result);
+ NSArray<NSString*>* dirs =
+ NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);
+ if ([dirs count] < 1) {
+ return false;
+ }
+ *result = NSStringToFilePath(dirs[0]);
+ return true;
+}
+
+bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
+ return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
+}
+
+bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
+ return GetSearchPathDirectory(directory, NSUserDomainMask, result);
+}
+
+FilePath GetUserLibraryPath() {
+ FilePath user_library_path;
+ if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {
+ DLOG(WARNING) << "Could not get user library path";
+ }
+ return user_library_path;
+}
+
+// Takes a path to an (executable) binary and tries to provide the path to an
+// application bundle containing it. It takes the outermost bundle that it can
+// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
+// |exec_name| - path to the binary
+// returns - path to the application bundle, or empty on error
+FilePath GetAppBundlePath(const FilePath& exec_name) {
+ const char kExt[] = ".app";
+ const size_t kExtLength = arraysize(kExt) - 1;
+
+ // Split the path into components.
+ std::vector<std::string> components;
+ exec_name.GetComponents(&components);
+
+ // It's an error if we don't get any components.
+ if (components.empty())
+ return FilePath();
+
+ // Don't prepend '/' to the first component.
+ std::vector<std::string>::const_iterator it = components.begin();
+ std::string bundle_name = *it;
+ DCHECK_GT(it->length(), 0U);
+ // If the first component ends in ".app", we're already done.
+ if (it->length() > kExtLength &&
+ !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
+ return FilePath(bundle_name);
+
+ // The first component may be "/" or "//", etc. Only append '/' if it doesn't
+ // already end in '/'.
+ if (bundle_name.back() != '/')
+ bundle_name += '/';
+
+ // Go through the remaining components.
+ for (++it; it != components.end(); ++it) {
+ DCHECK_GT(it->length(), 0U);
+
+ bundle_name += *it;
+
+ // If the current component ends in ".app", we're done.
+ if (it->length() > kExtLength &&
+ !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
+ return FilePath(bundle_name);
+
+ // Separate this component from the next one.
+ bundle_name += '/';
+ }
+
+ return FilePath();
+}
+
+#define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \
+ std::string TypeNameForCFType(TypeCF##Ref) { return #TypeCF; }
+
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFData);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFString);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID);
+
+TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor);
+
+TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun);
+
+#if !defined(OS_IOS)
+TYPE_NAME_FOR_CF_TYPE_DEFN(SecKey);
+TYPE_NAME_FOR_CF_TYPE_DEFN(SecPolicy);
+#endif
+
+#undef TYPE_NAME_FOR_CF_TYPE_DEFN
+
+void NSObjectRetain(void* obj) {
+ id<NSObject> nsobj = static_cast<id<NSObject>>(obj);
+ [nsobj retain];
+}
+
+void NSObjectRelease(void* obj) {
+ id<NSObject> nsobj = static_cast<id<NSObject>>(obj);
+ [nsobj release];
+}
+
+void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) {
+ // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease
+ // is a no-op.
+ //
+ // In the traditional GC-less environment, NSMakeCollectable is a no-op,
+ // and cf_object is autoreleased, balancing out the caller's ownership claim.
+ //
+ // NSMakeCollectable returns nil when used on a NULL object.
+ return [NSMakeCollectable(cf_object) autorelease];
+}
+
+static const char* base_bundle_id;
+
+const char* BaseBundleID() {
+ if (base_bundle_id) {
+ return base_bundle_id;
+ }
+
+#if defined(GOOGLE_CHROME_BUILD)
+ return "com.google.Chrome";
+#else
+ return "org.chromium.Chromium";
+#endif
+}
+
+void SetBaseBundleID(const char* new_base_bundle_id) {
+ if (new_base_bundle_id != base_bundle_id) {
+ free((void*)base_bundle_id);
+ base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL;
+ }
+}
+
+// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in
+// foundation_util.h.
+#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \
+ \
+ TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \
+ DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
+ TypeNS* ns_val = \
+ const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \
+ return ns_val; \
+ } \
+ \
+ TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \
+ TypeCF##Ref cf_val = reinterpret_cast<TypeCF##Ref>(ns_val); \
+ DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
+ return cf_val; \
+ }
+
+#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \
+ CF_TO_NS_CAST_DEFN(CF##name, NS##name) \
+ \
+ NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \
+ DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
+ NSMutable##name* ns_val = reinterpret_cast<NSMutable##name*>(cf_val); \
+ return ns_val; \
+ } \
+ \
+ CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \
+ CFMutable##name##Ref cf_val = \
+ reinterpret_cast<CFMutable##name##Ref>(ns_val); \
+ DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
+ return cf_val; \
+ }
+
+CF_TO_NS_MUTABLE_CAST_DEFN(Array);
+CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString);
+CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar);
+CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet);
+CF_TO_NS_MUTABLE_CAST_DEFN(Data);
+CF_TO_NS_CAST_DEFN(CFDate, NSDate);
+CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary);
+CF_TO_NS_CAST_DEFN(CFError, NSError);
+CF_TO_NS_CAST_DEFN(CFLocale, NSLocale);
+CF_TO_NS_CAST_DEFN(CFNumber, NSNumber);
+CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer);
+CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone);
+CF_TO_NS_MUTABLE_CAST_DEFN(Set);
+CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream);
+CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream);
+CF_TO_NS_MUTABLE_CAST_DEFN(String);
+CF_TO_NS_CAST_DEFN(CFURL, NSURL);
+
+#if defined(OS_IOS)
+CF_TO_NS_CAST_DEFN(CTFont, UIFont);
+#else
+// The NSFont/CTFont toll-free bridging is broken when it comes to type
+// checking, so do some special-casing.
+// http://www.openradar.me/15341349 rdar://15341349
+NSFont* CFToNSCast(CTFontRef cf_val) {
+ NSFont* ns_val = const_cast<NSFont*>(reinterpret_cast<const NSFont*>(cf_val));
+ DCHECK(!cf_val || CTFontGetTypeID() == CFGetTypeID(cf_val) ||
+ (_CFIsObjC(CTFontGetTypeID(), cf_val) &&
+ [ns_val isKindOfClass:[NSFont class]]));
+ return ns_val;
+}
+
+CTFontRef NSToCFCast(NSFont* ns_val) {
+ CTFontRef cf_val = reinterpret_cast<CTFontRef>(ns_val);
+ DCHECK(!cf_val || CTFontGetTypeID() == CFGetTypeID(cf_val) ||
+ [ns_val isKindOfClass:[NSFont class]]);
+ return cf_val;
+}
+#endif
+
+#undef CF_TO_NS_CAST_DEFN
+#undef CF_TO_NS_MUTABLE_CAST_DEFN
+
+#define CF_CAST_DEFN(TypeCF) \
+ template <> \
+ TypeCF##Ref CFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \
+ if (cf_val == NULL) { \
+ return NULL; \
+ } \
+ if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \
+ return (TypeCF##Ref)(cf_val); \
+ } \
+ return NULL; \
+ } \
+ \
+ template <> \
+ TypeCF##Ref CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \
+ TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \
+ DCHECK(cf_val == NULL || rv); \
+ return rv; \
+ }
+
+CF_CAST_DEFN(CFArray);
+CF_CAST_DEFN(CFBag);
+CF_CAST_DEFN(CFBoolean);
+CF_CAST_DEFN(CFData);
+CF_CAST_DEFN(CFDate);
+CF_CAST_DEFN(CFDictionary);
+CF_CAST_DEFN(CFNull);
+CF_CAST_DEFN(CFNumber);
+CF_CAST_DEFN(CFSet);
+CF_CAST_DEFN(CFString);
+CF_CAST_DEFN(CFURL);
+CF_CAST_DEFN(CFUUID);
+
+CF_CAST_DEFN(CGColor);
+
+CF_CAST_DEFN(CTFontDescriptor);
+CF_CAST_DEFN(CTRun);
+
+#if defined(OS_IOS)
+CF_CAST_DEFN(CTFont);
+#else
+// The NSFont/CTFont toll-free bridging is broken when it comes to type
+// checking, so do some special-casing.
+// http://www.openradar.me/15341349 rdar://15341349
+template <>
+CTFontRef CFCast<CTFontRef>(const CFTypeRef& cf_val) {
+ if (cf_val == NULL) {
+ return NULL;
+ }
+ if (CFGetTypeID(cf_val) == CTFontGetTypeID()) {
+ return (CTFontRef)(cf_val);
+ }
+
+ if (!_CFIsObjC(CTFontGetTypeID(), cf_val))
+ return NULL;
+
+ id<NSObject> ns_val = reinterpret_cast<id>(const_cast<void*>(cf_val));
+ if ([ns_val isKindOfClass:[NSFont class]]) {
+ return (CTFontRef)(cf_val);
+ }
+ return NULL;
+}
+
+template <>
+CTFontRef CFCastStrict<CTFontRef>(const CFTypeRef& cf_val) {
+ CTFontRef rv = CFCast<CTFontRef>(cf_val);
+ DCHECK(cf_val == NULL || rv);
+ return rv;
+}
+#endif
+
+#if !defined(OS_IOS)
+CF_CAST_DEFN(SecACL);
+CF_CAST_DEFN(SecKey);
+CF_CAST_DEFN(SecPolicy);
+CF_CAST_DEFN(SecTrustedApplication);
+#endif
+
+#undef CF_CAST_DEFN
+
+std::string GetValueFromDictionaryErrorMessage(CFStringRef key,
+ const std::string& expected_type,
+ CFTypeRef value) {
+ ScopedCFTypeRef<CFStringRef> actual_type_ref(
+ CFCopyTypeIDDescription(CFGetTypeID(value)));
+ return "Expected value for key " + base::SysCFStringRefToUTF8(key) +
+ " to be " + expected_type + " but it was " +
+ base::SysCFStringRefToUTF8(actual_type_ref) + " instead";
+}
+
+NSString* FilePathToNSString(const FilePath& path) {
+ if (path.empty())
+ return nil;
+ return @(path.value().c_str()); // @() does UTF8 conversion.
+}
+
+FilePath NSStringToFilePath(NSString* str) {
+ if (![str length])
+ return FilePath();
+ return FilePath([str fileSystemRepresentation]);
+}
+
+bool CFRangeToNSRange(CFRange range, NSRange* range_out) {
+ if (base::IsValueInRangeForNumericType<decltype(range_out->location)>(
+ range.location) &&
+ base::IsValueInRangeForNumericType<decltype(range_out->length)>(
+ range.length) &&
+ base::IsValueInRangeForNumericType<decltype(range_out->location)>(
+ range.location + range.length)) {
+ *range_out = NSMakeRange(range.location, range.length);
+ return true;
+ }
+ return false;
+}
+
+} // namespace mac
+} // namespace base
+
+std::ostream& operator<<(std::ostream& o, const CFStringRef string) {
+ return o << base::SysCFStringRefToUTF8(string);
+}
+
+std::ostream& operator<<(std::ostream& o, const CFErrorRef err) {
+ base::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err));
+ base::ScopedCFTypeRef<CFDictionaryRef> user_info(CFErrorCopyUserInfo(err));
+ CFStringRef errorDesc = NULL;
+ if (user_info.get()) {
+ errorDesc = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey));
+ }
+ o << "Code: " << CFErrorGetCode(err) << " Domain: " << CFErrorGetDomain(err)
+ << " Desc: " << desc.get();
+ if (errorDesc) {
+ o << "(" << errorDesc << ")";
+ }
+ return o;
+}
diff --git a/gn/base/mac/mac_logging.h b/gn/base/mac/mac_logging.h
new file mode 100644
index 00000000000..5ef75f38323
--- /dev/null
+++ b/gn/base/mac/mac_logging.h
@@ -0,0 +1,74 @@
+// 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_MAC_MAC_LOGGING_H_
+#define BASE_MAC_MAC_LOGGING_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "util/build_config.h"
+
+#if defined(OS_IOS)
+#include <MacTypes.h>
+#else
+#include <libkern/OSTypes.h>
+#endif
+
+// Use the OSSTATUS_LOG family to log messages related to errors in Mac OS X
+// system routines that report status via an OSStatus or OSErr value. It is
+// similar to the PLOG family which operates on errno, but because there is no
+// global (or thread-local) OSStatus or OSErr value, the specific error must
+// be supplied as an argument to the OSSTATUS_LOG macro. The message logged
+// will contain the symbolic constant name corresponding to the status value,
+// along with the value itself.
+//
+// OSErr is just an older 16-bit form of the newer 32-bit OSStatus. Despite
+// the name, OSSTATUS_LOG can be used equally well for OSStatus and OSErr.
+
+namespace logging {
+
+// Returns a UTF8 description from an OS X Status error.
+std::string DescriptionFromOSStatus(OSStatus err);
+
+class OSStatusLogMessage : public logging::LogMessage {
+ public:
+ OSStatusLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ OSStatus status);
+ ~OSStatusLogMessage();
+
+ private:
+ OSStatus status_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSStatusLogMessage);
+};
+
+} // namespace logging
+
+#define OSSTATUS_LOG_STREAM(severity, status) \
+ COMPACT_GOOGLE_LOG_EX_##severity(OSStatusLogMessage, status).stream()
+
+#define OSSTATUS_LOG(severity, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), LOG_IS_ON(severity))
+#define OSSTATUS_LOG_IF(severity, condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \
+ LOG_IS_ON(severity) && (condition))
+
+#define OSSTATUS_CHECK(condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), !(condition)) \
+ << "Check failed: " #condition << ". "
+
+#define OSSTATUS_DLOG(severity, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), DLOG_IS_ON(severity))
+#define OSSTATUS_DLOG_IF(severity, condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \
+ DLOG_IS_ON(severity) && (condition))
+
+#define OSSTATUS_DCHECK(condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \
+ DCHECK_IS_ON() && !(condition)) \
+ << "Check failed: " #condition << ". "
+
+#endif // BASE_MAC_MAC_LOGGING_H_
diff --git a/gn/base/mac/mac_logging.mm b/gn/base/mac/mac_logging.mm
new file mode 100644
index 00000000000..f7c30528bc5
--- /dev/null
+++ b/gn/base/mac/mac_logging.mm
@@ -0,0 +1,42 @@
+// 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/mac/mac_logging.h"
+
+#import <Foundation/Foundation.h>
+
+#include <iomanip>
+
+#include "util/build_config.h"
+
+#if !defined(OS_IOS)
+#include <CoreServices/CoreServices.h>
+#endif
+
+namespace logging {
+
+std::string DescriptionFromOSStatus(OSStatus err) {
+ NSError* error =
+ [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil];
+ return error.description.UTF8String;
+}
+
+OSStatusLogMessage::OSStatusLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ OSStatus status)
+ : LogMessage(file_path, line, severity), status_(status) {}
+
+OSStatusLogMessage::~OSStatusLogMessage() {
+#if defined(OS_IOS)
+ // TODO(crbug.com/546375): Consider using NSError with NSOSStatusErrorDomain
+ // to try to get a description of the failure.
+ stream() << ": " << status_;
+#else
+ stream() << ": " << DescriptionFromOSStatus(status_) << " (" << status_
+ << ")";
+#endif
+}
+
+} // namespace logging
diff --git a/gn/base/mac/scoped_cftyperef.h b/gn/base/mac/scoped_cftyperef.h
new file mode 100644
index 00000000000..a602fd9cbb2
--- /dev/null
+++ b/gn/base/mac/scoped_cftyperef.h
@@ -0,0 +1,48 @@
+// 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_MAC_SCOPED_CFTYPEREF_H_
+#define BASE_MAC_SCOPED_CFTYPEREF_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/mac/scoped_typeref.h"
+
+namespace base {
+
+// ScopedCFTypeRef<> is patterned after std::unique_ptr<>, but maintains
+// ownership of a CoreFoundation object: any object that can be represented
+// as a CFTypeRef. Style deviations here are solely for compatibility with
+// std::unique_ptr<>'s interface, with which everyone is already familiar.
+//
+// By default, ScopedCFTypeRef<> takes ownership of an object (in the
+// constructor or in reset()) by taking over the caller's existing ownership
+// claim. The caller must own the object it gives to ScopedCFTypeRef<>, and
+// relinquishes an ownership claim to that object. ScopedCFTypeRef<> does not
+// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy|
+// enum. If the value |RETAIN| is passed (in the constructor or in reset()),
+// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial
+// ownership is not changed.
+
+namespace internal {
+
+template <typename CFT>
+struct ScopedCFTypeRefTraits {
+ static CFT InvalidValue() { return nullptr; }
+ static CFT Retain(CFT object) {
+ CFRetain(object);
+ return object;
+ }
+ static void Release(CFT object) { CFRelease(object); }
+};
+
+} // namespace internal
+
+template <typename CFT>
+using ScopedCFTypeRef =
+ ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits<CFT>>;
+
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_CFTYPEREF_H_
diff --git a/gn/base/mac/scoped_typeref.h b/gn/base/mac/scoped_typeref.h
new file mode 100644
index 00000000000..659ee3426b0
--- /dev/null
+++ b/gn/base/mac/scoped_typeref.h
@@ -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.
+
+#ifndef BASE_MAC_SCOPED_TYPEREF_H_
+#define BASE_MAC_SCOPED_TYPEREF_H_
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_policy.h"
+
+namespace base {
+
+// ScopedTypeRef<> is patterned after std::unique_ptr<>, but maintains ownership
+// of a reference to any type that is maintained by Retain and Release methods.
+//
+// The Traits structure must provide the Retain and Release methods for type T.
+// A default ScopedTypeRefTraits is used but not defined, and should be defined
+// for each type to use this interface. For example, an appropriate definition
+// of ScopedTypeRefTraits for CGLContextObj would be:
+//
+// template<>
+// struct ScopedTypeRefTraits<CGLContextObj> {
+// static CGLContextObj InvalidValue() { return nullptr; }
+// static CGLContextObj Retain(CGLContextObj object) {
+// CGLContextRetain(object);
+// return object;
+// }
+// static void Release(CGLContextObj object) { CGLContextRelease(object); }
+// };
+//
+// For the many types that have pass-by-pointer create functions, the function
+// InitializeInto() is provided to allow direct initialization and assumption
+// of ownership of the object. For example, continuing to use the above
+// CGLContextObj specialization:
+//
+// base::ScopedTypeRef<CGLContextObj> context;
+// CGLCreateContext(pixel_format, share_group, context.InitializeInto());
+//
+// For initialization with an existing object, the caller may specify whether
+// the ScopedTypeRef<> being initialized is assuming the caller's existing
+// ownership of the object (and should not call Retain in initialization) or if
+// it should not assume this ownership and must create its own (by calling
+// Retain in initialization). This behavior is based on the |policy| parameter,
+// with |ASSUME| for the former and |RETAIN| for the latter. The default policy
+// is to |ASSUME|.
+
+template <typename T>
+struct ScopedTypeRefTraits;
+
+template <typename T, typename Traits = ScopedTypeRefTraits<T>>
+class ScopedTypeRef {
+ public:
+ typedef T element_type;
+
+ explicit constexpr ScopedTypeRef(
+ __unsafe_unretained T object = Traits::InvalidValue(),
+ base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+ : object_(object) {
+ if (object_ && policy == base::scoped_policy::RETAIN)
+ object_ = Traits::Retain(object_);
+ }
+
+ ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.object_) {
+ if (object_)
+ object_ = Traits::Retain(object_);
+ }
+
+ // This allows passing an object to a function that takes its superclass.
+ template <typename R, typename RTraits>
+ explicit ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that_as_subclass)
+ : object_(that_as_subclass.get()) {
+ if (object_)
+ object_ = Traits::Retain(object_);
+ }
+
+ ScopedTypeRef(ScopedTypeRef<T, Traits>&& that) : object_(that.object_) {
+ that.object_ = Traits::InvalidValue();
+ }
+
+ ~ScopedTypeRef() {
+ if (object_)
+ Traits::Release(object_);
+ }
+
+ ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {
+ reset(that.get(), base::scoped_policy::RETAIN);
+ return *this;
+ }
+
+ // This is to be used only to take ownership of objects that are created
+ // by pass-by-pointer create functions. To enforce this, require that the
+ // object be reset to NULL before this may be used.
+ T* InitializeInto() WARN_UNUSED_RESULT {
+ DCHECK(!object_);
+ return &object_;
+ }
+
+ void reset(__unsafe_unretained T object = Traits::InvalidValue(),
+ base::scoped_policy::OwnershipPolicy policy =
+ base::scoped_policy::ASSUME) {
+ if (object && policy == base::scoped_policy::RETAIN)
+ object = Traits::Retain(object);
+ if (object_)
+ Traits::Release(object_);
+ object_ = object;
+ }
+
+ bool operator==(__unsafe_unretained T that) const { return object_ == that; }
+
+ bool operator!=(__unsafe_unretained T that) const { return object_ != that; }
+
+ operator T() const __attribute((ns_returns_not_retained)) { return object_; }
+
+ T get() const __attribute((ns_returns_not_retained)) { return object_; }
+
+ void swap(ScopedTypeRef& that) {
+ __unsafe_unretained T temp = that.object_;
+ that.object_ = object_;
+ object_ = temp;
+ }
+
+ // ScopedTypeRef<>::release() is like std::unique_ptr<>::release. It is NOT
+ // a wrapper for Release(). To force a ScopedTypeRef<> object to call
+ // Release(), use ScopedTypeRef<>::reset().
+ T release() __attribute((ns_returns_not_retained)) WARN_UNUSED_RESULT {
+ __unsafe_unretained T temp = object_;
+ object_ = Traits::InvalidValue();
+ return temp;
+ }
+
+ private:
+ __unsafe_unretained T object_;
+};
+
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_TYPEREF_H_
diff --git a/gn/base/macros.h b/gn/base/macros.h
new file mode 100644
index 00000000000..321f65bc513
--- /dev/null
+++ b/gn/base/macros.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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
new file mode 100644
index 00000000000..c66f7b220c1
--- /dev/null
+++ b/gn/base/md5.cc
@@ -0,0 +1,299 @@
+// 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
new file mode 100644
index 00000000000..16a0a6f90d9
--- /dev/null
+++ b/gn/base/md5.h
@@ -0,0 +1,76 @@
+// 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/free_deleter.h b/gn/base/memory/free_deleter.h
new file mode 100644
index 00000000000..e12795c7a88
--- /dev/null
+++ b/gn/base/memory/free_deleter.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 BASE_MEMORY_FREE_DELETER_H_
+#define BASE_MEMORY_FREE_DELETER_H_
+
+#include <stdlib.h>
+
+namespace base {
+
+// Function object which invokes 'free' on its parameter, which must be
+// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
+//
+// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
+// static_cast<int*>(malloc(sizeof(int))));
+struct FreeDeleter {
+ inline void operator()(void* ptr) const { free(ptr); }
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_FREE_DELETER_H_
diff --git a/gn/base/memory/ptr_util.h b/gn/base/memory/ptr_util.h
new file mode 100644
index 00000000000..42f4f49eebd
--- /dev/null
+++ b/gn/base/memory/ptr_util.h
@@ -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.
+
+#ifndef BASE_MEMORY_PTR_UTIL_H_
+#define BASE_MEMORY_PTR_UTIL_H_
+
+#include <memory>
+#include <utility>
+
+namespace base {
+
+// Helper to transfer ownership of a raw pointer to a std::unique_ptr<T>.
+// Note that std::unique_ptr<T> has very different semantics from
+// std::unique_ptr<T[]>: do not use this helper for array allocations.
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* ptr) {
+ return std::unique_ptr<T>(ptr);
+}
+
+} // namespace base
+
+#endif // BASE_MEMORY_PTR_UTIL_H_
diff --git a/gn/base/memory/raw_scoped_refptr_mismatch_checker.h b/gn/base/memory/raw_scoped_refptr_mismatch_checker.h
new file mode 100644
index 00000000000..ab8b2abcbb9
--- /dev/null
+++ b/gn/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,
+ 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/base/memory/ref_counted.cc
new file mode 100644
index 00000000000..79ab8535a5e
--- /dev/null
+++ b/gn/base/memory/ref_counted.cc
@@ -0,0 +1,35 @@
+// 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/memory/ref_counted.h"
+
+namespace base {
+
+namespace subtle {
+
+bool RefCountedThreadSafeBase::HasOneRef() const {
+ return ref_count_.IsOne();
+}
+
+#if defined(ARCH_CPU_64_BIT)
+void RefCountedBase::AddRefImpl() const {
+ // Check if |ref_count_| overflow only on 64 bit archs since the number of
+ // objects may exceed 2^32.
+ // To avoid the binary size bloat, use non-inline function here.
+ CHECK(++ref_count_ > 0);
+}
+#endif
+
+#if !defined(ARCH_CPU_X86_FAMILY)
+bool RefCountedThreadSafeBase::Release() const {
+ return ReleaseImpl();
+}
+void RefCountedThreadSafeBase::AddRef() const {
+ AddRefImpl();
+}
+#endif
+
+} // namespace subtle
+
+} // namespace base
diff --git a/gn/base/memory/ref_counted.h b/gn/base/memory/ref_counted.h
new file mode 100644
index 00000000000..8ebdcd68b5b
--- /dev/null
+++ b/gn/base/memory/ref_counted.h
@@ -0,0 +1,317 @@
+// 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_MEMORY_REF_COUNTED_H_
+#define BASE_MEMORY_REF_COUNTED_H_
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/atomic_ref_count.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "util/build_config.h"
+
+namespace base {
+namespace subtle {
+
+class RefCountedBase {
+ public:
+ bool HasOneRef() const { return ref_count_ == 1; }
+
+ protected:
+ explicit RefCountedBase(StartRefCountFromZeroTag) {}
+
+ explicit RefCountedBase(StartRefCountFromOneTag) : ref_count_(1) {}
+
+ ~RefCountedBase() {}
+
+ void AddRef() const { AddRefImpl(); }
+
+ // Returns true if the object should self-delete.
+ bool Release() const {
+ --ref_count_;
+
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease"
+ // without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+
+ return ref_count_ == 0;
+ }
+
+ // Returns true if it is safe to read or write the object, from a thread
+ // safety standpoint. Should be DCHECK'd from the methods of RefCounted
+ // classes if there is a danger of objects being shared across threads.
+ //
+ // This produces fewer false positives than adding a separate SequenceChecker
+ // into the subclass, because it automatically detaches from the sequence when
+ // the reference count is 1 (and never fails if there is only one reference).
+ //
+ // This means unlike a separate SequenceChecker, it will permit a singly
+ // referenced object to be passed between threads (not holding a reference on
+ // the sending thread), but will trap if the sending thread holds onto a
+ // reference, or if the object is accessed from multiple threads
+ // simultaneously.
+ bool IsOnValidSequence() const { return true; }
+
+ private:
+ template <typename U>
+ friend scoped_refptr<U> base::AdoptRef(U*);
+
+ void Adopted() const {}
+
+#if defined(ARCH_CPU_64_BIT)
+ void AddRefImpl() const;
+#else
+ void AddRefImpl() const { ++ref_count_; }
+#endif
+
+ mutable uint32_t ref_count_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
+};
+
+class RefCountedThreadSafeBase {
+ public:
+ bool HasOneRef() const;
+
+ protected:
+ explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {}
+ explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag)
+ : ref_count_(1) {}
+
+ ~RefCountedThreadSafeBase() = default;
+
+// Release and AddRef are suitable for inlining on X86 because they generate
+// very small code sequences. On other platforms (ARM), it causes a size
+// regression and is probably not worth it.
+#if defined(ARCH_CPU_X86_FAMILY)
+ // Returns true if the object should self-delete.
+ bool Release() const { return ReleaseImpl(); }
+ void AddRef() const { AddRefImpl(); }
+#else
+ // Returns true if the object should self-delete.
+ bool Release() const;
+ void AddRef() const;
+#endif
+
+ private:
+ template <typename U>
+ friend scoped_refptr<U> base::AdoptRef(U*);
+
+ void Adopted() const {}
+
+ ALWAYS_INLINE void AddRefImpl() const { ref_count_.Increment(); }
+
+ ALWAYS_INLINE bool ReleaseImpl() const {
+ if (!ref_count_.Decrement()) {
+ return true;
+ }
+ return false;
+ }
+
+ mutable AtomicRefCount ref_count_{0};
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase);
+};
+
+} // namespace subtle
+
+// ScopedAllowCrossThreadRefCountAccess disables the check documented on
+// RefCounted below for rare pre-existing use cases where thread-safety was
+// guaranteed through other means (e.g. explicit sequencing of calls across
+// execution sequences when bouncing between threads in order). New callers
+// should refrain from using this (callsites handling thread-safety through
+// locks should use RefCountedThreadSafe per the overhead of its atomics being
+// negligible compared to locks anyways and callsites doing explicit sequencing
+// should properly std::move() the ref to avoid hitting this check).
+// TODO(tzik): Cleanup existing use cases and remove
+// ScopedAllowCrossThreadRefCountAccess.
+class ScopedAllowCrossThreadRefCountAccess final {
+ public:
+ ScopedAllowCrossThreadRefCountAccess() {}
+ ~ScopedAllowCrossThreadRefCountAccess() {}
+};
+
+//
+// A base class for reference counted classes. Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class. To use this, just extend your
+// class from it like so:
+//
+// class MyFoo : public base::RefCounted<MyFoo> {
+// ...
+// private:
+// friend class base::RefCounted<MyFoo>;
+// ~MyFoo();
+// };
+//
+// You should always make your destructor non-public, to avoid any code deleting
+// the object accidently while there are references to it.
+//
+//
+// The ref count manipulation to RefCounted is NOT thread safe and has DCHECKs
+// to trap unsafe cross thread usage. A subclass instance of RefCounted can be
+// passed to another execution sequence only when its ref count is 1. If the ref
+// count is more than 1, the RefCounted class verifies the ref updates are made
+// on the same execution sequence as the previous ones. The subclass can also
+// manually call IsOnValidSequence to trap other non-thread-safe accesses; see
+// the documentation for that method.
+//
+//
+// The reference count starts from zero by default, and we intended to migrate
+// to start-from-one ref count. Put REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() to
+// the ref counted class to opt-in.
+//
+// If an object has start-from-one ref count, the first scoped_refptr need to be
+// created by base::AdoptRef() or base::MakeRefCounted(). We can use
+// base::MakeRefCounted() to create create both type of ref counted object.
+//
+// The motivations to use start-from-one ref count are:
+// - Start-from-one ref count doesn't need the ref count increment for the
+// first reference.
+// - It can detect an invalid object acquisition for a being-deleted object
+// that has zero ref count. That tends to happen on custom deleter that
+// delays the deletion.
+// TODO(tzik): Implement invalid acquisition detection.
+// - Behavior parity to Blink's WTF::RefCounted, whose count starts from one.
+// And start-from-one ref count is a step to merge WTF::RefCounted into
+// base::RefCounted.
+//
+#define REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() \
+ static constexpr ::base::subtle::StartRefCountFromOneTag \
+ kRefCountPreference = ::base::subtle::kStartRefCountFromOneTag
+
+template <class T, typename Traits>
+class RefCounted;
+
+template <typename T>
+struct DefaultRefCountedTraits {
+ static void Destruct(const T* x) {
+ RefCounted<T, DefaultRefCountedTraits>::DeleteInternal(x);
+ }
+};
+
+template <class T, typename Traits = DefaultRefCountedTraits<T>>
+class RefCounted : public subtle::RefCountedBase {
+ public:
+ static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference =
+ subtle::kStartRefCountFromZeroTag;
+
+ RefCounted() : subtle::RefCountedBase(T::kRefCountPreference) {}
+
+ void AddRef() const { subtle::RefCountedBase::AddRef(); }
+
+ void Release() const {
+ if (subtle::RefCountedBase::Release()) {
+ // Prune the code paths which the static analyzer may take to simulate
+ // object destruction. Use-after-free errors aren't possible given the
+ // lifetime guarantees of the refcounting system.
+ ANALYZER_SKIP_THIS_PATH();
+
+ Traits::Destruct(static_cast<const T*>(this));
+ }
+ }
+
+ protected:
+ ~RefCounted() = default;
+
+ private:
+ friend struct DefaultRefCountedTraits<T>;
+ template <typename U>
+ static void DeleteInternal(const U* x) {
+ delete x;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RefCounted);
+};
+
+// Forward declaration.
+template <class T, typename Traits>
+class RefCountedThreadSafe;
+
+// Default traits for RefCountedThreadSafe<T>. Deletes the object when its ref
+// count reaches 0. Overload to delete it on a different thread etc.
+template <typename T>
+struct DefaultRefCountedThreadSafeTraits {
+ static void Destruct(const T* x) {
+ // Delete through RefCountedThreadSafe to make child classes only need to be
+ // friend with RefCountedThreadSafe instead of this struct, which is an
+ // implementation detail.
+ RefCountedThreadSafe<T, DefaultRefCountedThreadSafeTraits>::DeleteInternal(
+ x);
+ }
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+// class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
+// ...
+// };
+//
+// If you're using the default trait, then you should add compile time
+// asserts that no one else is deleting your object. i.e.
+// private:
+// friend class base::RefCountedThreadSafe<MyFoo>;
+// ~MyFoo();
+//
+// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe
+// too. See the comment above the RefCounted definition for details.
+template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T>>
+class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
+ public:
+ static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference =
+ subtle::kStartRefCountFromZeroTag;
+
+ explicit RefCountedThreadSafe()
+ : subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {}
+
+ void AddRef() const { subtle::RefCountedThreadSafeBase::AddRef(); }
+
+ void Release() const {
+ if (subtle::RefCountedThreadSafeBase::Release()) {
+ ANALYZER_SKIP_THIS_PATH();
+ Traits::Destruct(static_cast<const T*>(this));
+ }
+ }
+
+ protected:
+ ~RefCountedThreadSafe() = default;
+
+ private:
+ friend struct DefaultRefCountedThreadSafeTraits<T>;
+ template <typename U>
+ static void DeleteInternal(const U* x) {
+ delete x;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe);
+};
+
+//
+// A thread-safe wrapper for some piece of data so we can place other
+// things in scoped_refptrs<>.
+//
+template <typename T>
+class RefCountedData
+ : public base::RefCountedThreadSafe<base::RefCountedData<T>> {
+ public:
+ RefCountedData() : data() {}
+ RefCountedData(const T& in_value) : data(in_value) {}
+ RefCountedData(T&& in_value) : data(std::move(in_value)) {}
+
+ T data;
+
+ private:
+ friend class base::RefCountedThreadSafe<base::RefCountedData<T>>;
+ ~RefCountedData() = default;
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_REF_COUNTED_H_
diff --git a/gn/base/memory/scoped_policy.h b/gn/base/memory/scoped_policy.h
new file mode 100644
index 00000000000..5dbf2048d64
--- /dev/null
+++ b/gn/base/memory/scoped_policy.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_SCOPED_POLICY_H_
+#define BASE_MEMORY_SCOPED_POLICY_H_
+
+namespace base {
+namespace scoped_policy {
+
+// Defines the ownership policy for a scoped object.
+enum OwnershipPolicy {
+ // The scoped object takes ownership of an object by taking over an existing
+ // ownership claim.
+ ASSUME,
+
+ // The scoped object will retain the the object and any initial ownership is
+ // not changed.
+ RETAIN
+};
+
+} // namespace scoped_policy
+} // namespace base
+
+#endif // BASE_MEMORY_SCOPED_POLICY_H_
diff --git a/gn/base/memory/scoped_refptr.h b/gn/base/memory/scoped_refptr.h
new file mode 100644
index 00000000000..a2576170bf9
--- /dev/null
+++ b/gn/base/memory/scoped_refptr.h
@@ -0,0 +1,333 @@
+// 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_MEMORY_SCOPED_REFPTR_H_
+#define BASE_MEMORY_SCOPED_REFPTR_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <type_traits>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+template <class T>
+class scoped_refptr;
+
+namespace base {
+
+template <class, typename>
+class RefCounted;
+template <class, typename>
+class RefCountedThreadSafe;
+
+template <typename T>
+scoped_refptr<T> AdoptRef(T* t);
+
+namespace subtle {
+
+enum AdoptRefTag { kAdoptRefTag };
+enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag };
+enum StartRefCountFromOneTag { kStartRefCountFromOneTag };
+
+template <typename T, typename U, typename V>
+constexpr bool IsRefCountPreferenceOverridden(const T*,
+ const RefCounted<U, V>*) {
+ return !std::is_same<std::decay_t<decltype(T::kRefCountPreference)>,
+ std::decay_t<decltype(U::kRefCountPreference)>>::value;
+}
+
+template <typename T, typename U, typename V>
+constexpr bool IsRefCountPreferenceOverridden(
+ const T*,
+ const RefCountedThreadSafe<U, V>*) {
+ return !std::is_same<std::decay_t<decltype(T::kRefCountPreference)>,
+ std::decay_t<decltype(U::kRefCountPreference)>>::value;
+}
+
+constexpr bool IsRefCountPreferenceOverridden(...) {
+ return false;
+}
+
+} // namespace subtle
+
+// Creates a scoped_refptr from a raw pointer without incrementing the reference
+// count. Use this only for a newly created object whose reference count starts
+// from 1 instead of 0.
+template <typename T>
+scoped_refptr<T> AdoptRef(T* obj) {
+ using Tag = std::decay_t<decltype(T::kRefCountPreference)>;
+ static_assert(std::is_same<subtle::StartRefCountFromOneTag, Tag>::value,
+ "Use AdoptRef only for the reference count starts from one.");
+
+ DCHECK(obj);
+ DCHECK(obj->HasOneRef());
+ obj->Adopted();
+ return scoped_refptr<T>(obj, subtle::kAdoptRefTag);
+}
+
+namespace subtle {
+
+template <typename T>
+scoped_refptr<T> AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) {
+ return scoped_refptr<T>(obj);
+}
+
+template <typename T>
+scoped_refptr<T> AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) {
+ return AdoptRef(obj);
+}
+
+} // namespace subtle
+
+// Constructs an instance of T, which is a ref counted type, and wraps the
+// object into a scoped_refptr<T>.
+template <typename T, typename... Args>
+scoped_refptr<T> MakeRefCounted(Args&&... args) {
+ T* obj = new T(std::forward<Args>(args)...);
+ return subtle::AdoptRefIfNeeded(obj, T::kRefCountPreference);
+}
+
+// Takes an instance of T, which is a ref counted type, and wraps the object
+// into a scoped_refptr<T>.
+template <typename T>
+scoped_refptr<T> WrapRefCounted(T* t) {
+ return scoped_refptr<T>(t);
+}
+
+} // namespace base
+
+//
+// A smart pointer class for reference counted objects. Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference. Sample usage:
+//
+// class MyFoo : public RefCounted<MyFoo> {
+// ...
+// private:
+// friend class RefCounted<MyFoo>; // Allow destruction by RefCounted<>.
+// ~MyFoo(); // Destructor must be private/protected.
+// };
+//
+// void some_function() {
+// scoped_refptr<MyFoo> foo = MakeRefCounted<MyFoo>();
+// foo->Method(param);
+// // |foo| is released when this function returns
+// }
+//
+// void some_other_function() {
+// scoped_refptr<MyFoo> foo = MakeRefCounted<MyFoo>();
+// ...
+// foo = nullptr; // explicitly releases |foo|
+// ...
+// if (foo)
+// foo->Method(param);
+// }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+// {
+// scoped_refptr<MyFoo> a = MakeRefCounted<MyFoo>();
+// scoped_refptr<MyFoo> b;
+//
+// b.swap(a);
+// // now, |b| references the MyFoo object, and |a| references nullptr.
+// }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+// {
+// scoped_refptr<MyFoo> a = MakeRefCounted<MyFoo>();
+// scoped_refptr<MyFoo> b;
+//
+// b = a;
+// // now, |a| and |b| each own a reference to the same MyFoo object.
+// }
+//
+// Also see Chromium's ownership and calling conventions:
+// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions
+// Specifically:
+// If the function (at least sometimes) takes a ref on a refcounted object,
+// declare the param as scoped_refptr<T>. The caller can decide whether it
+// wishes to transfer ownership (by calling std::move(t) when passing t) or
+// retain its ref (by simply passing t directly).
+// In other words, use scoped_refptr like you would a std::unique_ptr except
+// in the odd case where it's required to hold on to a ref while handing one
+// to another component (if a component merely needs to use t on the stack
+// without keeping a ref: pass t as a raw T*).
+template <class T>
+class scoped_refptr {
+ public:
+ typedef T element_type;
+
+ constexpr scoped_refptr() = default;
+
+ // Constructs from raw pointer. constexpr if |p| is null.
+ constexpr scoped_refptr(T* p) : ptr_(p) {
+ if (ptr_)
+ AddRef(ptr_);
+ }
+
+ // Copy constructor. This is required in addition to the copy conversion
+ // constructor below.
+ scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {}
+
+ // Copy conversion constructor.
+ template <typename U,
+ typename = typename std::enable_if<
+ std::is_convertible<U*, T*>::value>::type>
+ scoped_refptr(const scoped_refptr<U>& r) : scoped_refptr(r.ptr_) {}
+
+ // Move constructor. This is required in addition to the move conversion
+ // constructor below.
+ scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; }
+
+ // Move conversion constructor.
+ template <typename U,
+ typename = typename std::enable_if<
+ std::is_convertible<U*, T*>::value>::type>
+ scoped_refptr(scoped_refptr<U>&& r) noexcept : ptr_(r.ptr_) {
+ r.ptr_ = nullptr;
+ }
+
+ ~scoped_refptr() {
+ static_assert(!base::subtle::IsRefCountPreferenceOverridden(
+ static_cast<T*>(nullptr), static_cast<T*>(nullptr)),
+ "It's unsafe to override the ref count preference."
+ " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE"
+ " from subclasses.");
+ if (ptr_)
+ Release(ptr_);
+ }
+
+ T* get() const { return ptr_; }
+
+ T& operator*() const {
+ DCHECK(ptr_);
+ return *ptr_;
+ }
+
+ T* operator->() const {
+ DCHECK(ptr_);
+ return ptr_;
+ }
+
+ scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
+
+ // Unified assignment operator.
+ scoped_refptr& operator=(scoped_refptr r) noexcept {
+ swap(r);
+ return *this;
+ }
+
+ void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); }
+
+ explicit operator bool() const { return ptr_ != nullptr; }
+
+ template <typename U>
+ bool operator==(const scoped_refptr<U>& rhs) const {
+ return ptr_ == rhs.get();
+ }
+
+ template <typename U>
+ bool operator!=(const scoped_refptr<U>& rhs) const {
+ return !operator==(rhs);
+ }
+
+ template <typename U>
+ bool operator<(const scoped_refptr<U>& rhs) const {
+ return ptr_ < rhs.get();
+ }
+
+ protected:
+ T* ptr_ = nullptr;
+
+ private:
+ template <typename U>
+ friend scoped_refptr<U> base::AdoptRef(U*);
+
+ scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {}
+
+ // Friend required for move constructors that set r.ptr_ to null.
+ template <typename U>
+ friend class scoped_refptr;
+
+ // Non-inline helpers to allow:
+ // class Opaque;
+ // extern template class scoped_refptr<Opaque>;
+ // Otherwise the compiler will complain that Opaque is an incomplete type.
+ static void AddRef(T* ptr);
+ static void Release(T* ptr);
+};
+
+// static
+template <typename T>
+void scoped_refptr<T>::AddRef(T* ptr) {
+ ptr->AddRef();
+}
+
+// static
+template <typename T>
+void scoped_refptr<T>::Release(T* ptr) {
+ ptr->Release();
+}
+
+template <typename T, typename U>
+bool operator==(const scoped_refptr<T>& lhs, const U* rhs) {
+ return lhs.get() == rhs;
+}
+
+template <typename T, typename U>
+bool operator==(const T* lhs, const scoped_refptr<U>& rhs) {
+ return lhs == rhs.get();
+}
+
+template <typename T>
+bool operator==(const scoped_refptr<T>& lhs, std::nullptr_t null) {
+ return !static_cast<bool>(lhs);
+}
+
+template <typename T>
+bool operator==(std::nullptr_t null, const scoped_refptr<T>& rhs) {
+ return !static_cast<bool>(rhs);
+}
+
+template <typename T, typename U>
+bool operator!=(const scoped_refptr<T>& lhs, const U* rhs) {
+ return !operator==(lhs, rhs);
+}
+
+template <typename T, typename U>
+bool operator!=(const T* lhs, const scoped_refptr<U>& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+template <typename T>
+bool operator!=(const scoped_refptr<T>& lhs, std::nullptr_t null) {
+ return !operator==(lhs, null);
+}
+
+template <typename T>
+bool operator!=(std::nullptr_t null, const scoped_refptr<T>& rhs) {
+ return !operator==(null, rhs);
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& out, const scoped_refptr<T>& p) {
+ return out << p.get();
+}
+
+template <typename T>
+void swap(scoped_refptr<T>& lhs, scoped_refptr<T>& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+#endif // BASE_MEMORY_SCOPED_REFPTR_H_
diff --git a/gn/base/memory/weak_ptr.cc b/gn/base/memory/weak_ptr.cc
new file mode 100644
index 00000000000..4abeb7f84ef
--- /dev/null
+++ b/gn/base/memory/weak_ptr.cc
@@ -0,0 +1,75 @@
+// 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/memory/weak_ptr.h"
+
+namespace base {
+namespace internal {
+
+WeakReference::Flag::Flag() : is_valid_(true) {}
+
+void WeakReference::Flag::Invalidate() {
+ is_valid_ = false;
+}
+
+bool WeakReference::Flag::IsValid() const {
+ return is_valid_;
+}
+
+WeakReference::Flag::~Flag() = default;
+
+WeakReference::WeakReference() = default;
+
+WeakReference::WeakReference(const scoped_refptr<Flag>& flag) : flag_(flag) {}
+
+WeakReference::~WeakReference() = default;
+
+WeakReference::WeakReference(WeakReference&& other) = default;
+
+WeakReference::WeakReference(const WeakReference& other) = default;
+
+bool WeakReference::is_valid() const {
+ return flag_ && flag_->IsValid();
+}
+
+WeakReferenceOwner::WeakReferenceOwner() = default;
+
+WeakReferenceOwner::~WeakReferenceOwner() {
+ Invalidate();
+}
+
+WeakReference WeakReferenceOwner::GetRef() const {
+ // If we hold the last reference to the Flag then create a new one.
+ if (!HasRefs())
+ flag_ = new WeakReference::Flag();
+
+ return WeakReference(flag_);
+}
+
+void WeakReferenceOwner::Invalidate() {
+ if (flag_) {
+ flag_->Invalidate();
+ flag_ = nullptr;
+ }
+}
+
+WeakPtrBase::WeakPtrBase() : ptr_(0) {}
+
+WeakPtrBase::~WeakPtrBase() = default;
+
+WeakPtrBase::WeakPtrBase(const WeakReference& ref, uintptr_t ptr)
+ : ref_(ref), ptr_(ptr) {
+ DCHECK(ptr_);
+}
+
+WeakPtrFactoryBase::WeakPtrFactoryBase(uintptr_t ptr) : ptr_(ptr) {
+ DCHECK(ptr_);
+}
+
+WeakPtrFactoryBase::~WeakPtrFactoryBase() {
+ ptr_ = 0;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/gn/base/memory/weak_ptr.h b/gn/base/memory/weak_ptr.h
new file mode 100644
index 00000000000..3b2a0af6fb9
--- /dev/null
+++ b/gn/base/memory/weak_ptr.h
@@ -0,0 +1,378 @@
+// 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.
+
+// Weak pointers are pointers to an object that do not affect its lifetime,
+// and which may be invalidated (i.e. reset to nullptr) by the object, or its
+// owner, at any time, most commonly when the object is about to be deleted.
+
+// Weak pointers are useful when an object needs to be accessed safely by one
+// or more objects other than its owner, and those callers can cope with the
+// object vanishing and e.g. tasks posted to it being silently dropped.
+// Reference-counting such an object would complicate the ownership graph and
+// make it harder to reason about the object's lifetime.
+
+// EXAMPLE:
+//
+// class Controller {
+// public:
+// Controller() : weak_factory_(this) {}
+// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
+// void WorkComplete(const Result& result) { ... }
+// private:
+// // Member variables should appear before the WeakPtrFactory, to ensure
+// // that any WeakPtrs to Controller are invalidated before its members
+// // variable's destructors are executed, rendering them invalid.
+// WeakPtrFactory<Controller> weak_factory_;
+// };
+//
+// class Worker {
+// public:
+// static void StartNew(const WeakPtr<Controller>& controller) {
+// Worker* worker = new Worker(controller);
+// // Kick off asynchronous processing...
+// }
+// private:
+// Worker(const WeakPtr<Controller>& controller)
+// : controller_(controller) {}
+// void DidCompleteAsynchronousProcessing(const Result& result) {
+// if (controller_)
+// controller_->WorkComplete(result);
+// }
+// WeakPtr<Controller> controller_;
+// };
+//
+// With this implementation a caller may use SpawnWorker() to dispatch multiple
+// Workers and subsequently delete the Controller, without waiting for all
+// Workers to have completed.
+
+// ------------------------- IMPORTANT: Thread-safety -------------------------
+
+// Weak pointers may be passed safely between threads, but must always be
+// dereferenced and invalidated on the same SequencedTaskRunner otherwise
+// checking the pointer would be racey.
+//
+// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory
+// is dereferenced, the factory and its WeakPtrs become bound to the calling
+// thread or current SequencedWorkerPool token, and cannot be dereferenced or
+// invalidated on any other task runner. Bound WeakPtrs can still be handed
+// off to other task runners, e.g. to use to post tasks back to object on the
+// bound sequence.
+//
+// If all WeakPtr objects are destroyed or invalidated then the factory is
+// unbound from the SequencedTaskRunner/Thread. The WeakPtrFactory may then be
+// destroyed, or new WeakPtr objects may be used, from a different sequence.
+//
+// Thus, at least one WeakPtr object must exist and have been dereferenced on
+// the correct thread to enforce that other WeakPtr objects will enforce they
+// are used on the desired thread.
+
+#ifndef BASE_MEMORY_WEAK_PTR_H_
+#define BASE_MEMORY_WEAK_PTR_H_
+
+#include <cstddef>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+template <typename T>
+class SupportsWeakPtr;
+template <typename T>
+class WeakPtr;
+
+namespace internal {
+// These classes are part of the WeakPtr implementation.
+// DO NOT USE THESE CLASSES DIRECTLY YOURSELF.
+
+class WeakReference {
+ public:
+ // Although Flag is bound to a specific SequencedTaskRunner, it may be
+ // deleted from another via base::WeakPtr::~WeakPtr().
+ class Flag : public RefCountedThreadSafe<Flag> {
+ public:
+ Flag();
+
+ void Invalidate();
+ bool IsValid() const;
+
+ private:
+ friend class base::RefCountedThreadSafe<Flag>;
+
+ ~Flag();
+
+ bool is_valid_;
+ };
+
+ WeakReference();
+ explicit WeakReference(const scoped_refptr<Flag>& flag);
+ ~WeakReference();
+
+ WeakReference(WeakReference&& other);
+ WeakReference(const WeakReference& other);
+ WeakReference& operator=(WeakReference&& other) = default;
+ WeakReference& operator=(const WeakReference& other) = default;
+
+ bool is_valid() const;
+
+ private:
+ scoped_refptr<const Flag> flag_;
+};
+
+class WeakReferenceOwner {
+ public:
+ WeakReferenceOwner();
+ ~WeakReferenceOwner();
+
+ WeakReference GetRef() const;
+
+ bool HasRefs() const { return flag_ && !flag_->HasOneRef(); }
+
+ void Invalidate();
+
+ private:
+ mutable scoped_refptr<WeakReference::Flag> flag_;
+};
+
+// This class simplifies the implementation of WeakPtr's type conversion
+// constructor by avoiding the need for a public accessor for ref_. A
+// WeakPtr<T> cannot access the private members of WeakPtr<U>, so this
+// base class gives us a way to access ref_ in a protected fashion.
+class WeakPtrBase {
+ public:
+ WeakPtrBase();
+ ~WeakPtrBase();
+
+ WeakPtrBase(const WeakPtrBase& other) = default;
+ WeakPtrBase(WeakPtrBase&& other) = default;
+ WeakPtrBase& operator=(const WeakPtrBase& other) = default;
+ WeakPtrBase& operator=(WeakPtrBase&& other) = default;
+
+ void reset() {
+ ref_ = internal::WeakReference();
+ ptr_ = 0;
+ }
+
+ protected:
+ WeakPtrBase(const WeakReference& ref, uintptr_t ptr);
+
+ WeakReference ref_;
+
+ // This pointer is only valid when ref_.is_valid() is true. Otherwise, its
+ // value is undefined (as opposed to nullptr).
+ uintptr_t ptr_;
+};
+
+// This class provides a common implementation of common functions that would
+// otherwise get instantiated separately for each distinct instantiation of
+// SupportsWeakPtr<>.
+class SupportsWeakPtrBase {
+ public:
+ // A safe static downcast of a WeakPtr<Base> to WeakPtr<Derived>. This
+ // conversion will only compile if there is exists a Base which inherits
+ // from SupportsWeakPtr<Base>. See base::AsWeakPtr() below for a helper
+ // function that makes calling this easier.
+ //
+ // Precondition: t != nullptr
+ template <typename Derived>
+ static WeakPtr<Derived> StaticAsWeakPtr(Derived* t) {
+ static_assert(
+ std::is_base_of<internal::SupportsWeakPtrBase, Derived>::value,
+ "AsWeakPtr argument must inherit from SupportsWeakPtr");
+ return AsWeakPtrImpl<Derived>(t);
+ }
+
+ private:
+ // This template function uses type inference to find a Base of Derived
+ // which is an instance of SupportsWeakPtr<Base>. We can then safely
+ // static_cast the Base* to a Derived*.
+ template <typename Derived, typename Base>
+ static WeakPtr<Derived> AsWeakPtrImpl(SupportsWeakPtr<Base>* t) {
+ WeakPtr<Base> ptr = t->AsWeakPtr();
+ return WeakPtr<Derived>(
+ ptr.ref_, static_cast<Derived*>(reinterpret_cast<Base*>(ptr.ptr_)));
+ }
+};
+
+} // namespace internal
+
+template <typename T>
+class WeakPtrFactory;
+
+// The WeakPtr class holds a weak reference to |T*|.
+//
+// This class is designed to be used like a normal pointer. You should always
+// null-test an object of this class before using it or invoking a method that
+// may result in the underlying object being destroyed.
+//
+// EXAMPLE:
+//
+// class Foo { ... };
+// WeakPtr<Foo> foo;
+// if (foo)
+// foo->method();
+//
+template <typename T>
+class WeakPtr : public internal::WeakPtrBase {
+ public:
+ WeakPtr() = default;
+
+ WeakPtr(std::nullptr_t) {}
+
+ // Allow conversion from U to T provided U "is a" T. Note that this
+ // is separate from the (implicit) copy and move constructors.
+ template <typename U>
+ WeakPtr(const WeakPtr<U>& other) : WeakPtrBase(other) {
+ // Need to cast from U* to T* to do pointer adjustment in case of multiple
+ // inheritance. This also enforces the "U is a T" rule.
+ T* t = reinterpret_cast<U*>(other.ptr_);
+ ptr_ = reinterpret_cast<uintptr_t>(t);
+ }
+ template <typename U>
+ WeakPtr(WeakPtr<U>&& other) : WeakPtrBase(std::move(other)) {
+ // Need to cast from U* to T* to do pointer adjustment in case of multiple
+ // inheritance. This also enforces the "U is a T" rule.
+ T* t = reinterpret_cast<U*>(other.ptr_);
+ ptr_ = reinterpret_cast<uintptr_t>(t);
+ }
+
+ T* get() const {
+ return ref_.is_valid() ? reinterpret_cast<T*>(ptr_) : nullptr;
+ }
+
+ T& operator*() const {
+ DCHECK(get() != nullptr);
+ return *get();
+ }
+ T* operator->() const {
+ DCHECK(get() != nullptr);
+ return get();
+ }
+
+ // Allow conditionals to test validity, e.g. if (weak_ptr) {...};
+ explicit operator bool() const { return get() != nullptr; }
+
+ private:
+ friend class internal::SupportsWeakPtrBase;
+ template <typename U>
+ friend class WeakPtr;
+ friend class SupportsWeakPtr<T>;
+ friend class WeakPtrFactory<T>;
+
+ WeakPtr(const internal::WeakReference& ref, T* ptr)
+ : WeakPtrBase(ref, reinterpret_cast<uintptr_t>(ptr)) {}
+};
+
+// Allow callers to compare WeakPtrs against nullptr to test validity.
+template <class T>
+bool operator!=(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
+ return !(weak_ptr == nullptr);
+}
+template <class T>
+bool operator!=(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
+ return weak_ptr != nullptr;
+}
+template <class T>
+bool operator==(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
+ return weak_ptr.get() == nullptr;
+}
+template <class T>
+bool operator==(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
+ return weak_ptr == nullptr;
+}
+
+namespace internal {
+class WeakPtrFactoryBase {
+ protected:
+ WeakPtrFactoryBase(uintptr_t ptr);
+ ~WeakPtrFactoryBase();
+ internal::WeakReferenceOwner weak_reference_owner_;
+ uintptr_t ptr_;
+};
+} // namespace internal
+
+// A class may be composed of a WeakPtrFactory and thereby
+// control how it exposes weak pointers to itself. This is helpful if you only
+// need weak pointers within the implementation of a class. This class is also
+// useful when working with primitive types. For example, you could have a
+// WeakPtrFactory<bool> that is used to pass around a weak reference to a bool.
+template <class T>
+class WeakPtrFactory : public internal::WeakPtrFactoryBase {
+ public:
+ explicit WeakPtrFactory(T* ptr)
+ : WeakPtrFactoryBase(reinterpret_cast<uintptr_t>(ptr)) {}
+
+ ~WeakPtrFactory() = default;
+
+ WeakPtr<T> GetWeakPtr() {
+ return WeakPtr<T>(weak_reference_owner_.GetRef(),
+ reinterpret_cast<T*>(ptr_));
+ }
+
+ // Call this method to invalidate all existing weak pointers.
+ void InvalidateWeakPtrs() {
+ DCHECK(ptr_);
+ weak_reference_owner_.Invalidate();
+ }
+
+ // Call this method to determine if any weak pointers exist.
+ bool HasWeakPtrs() const {
+ DCHECK(ptr_);
+ return weak_reference_owner_.HasRefs();
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory);
+};
+
+// A class may extend from SupportsWeakPtr to let others take weak pointers to
+// it. This avoids the class itself implementing boilerplate to dispense weak
+// pointers. However, since SupportsWeakPtr's destructor won't invalidate
+// weak pointers to the class until after the derived class' members have been
+// destroyed, its use can lead to subtle use-after-destroy issues.
+template <class T>
+class SupportsWeakPtr : public internal::SupportsWeakPtrBase {
+ public:
+ SupportsWeakPtr() = default;
+
+ WeakPtr<T> AsWeakPtr() {
+ return WeakPtr<T>(weak_reference_owner_.GetRef(), static_cast<T*>(this));
+ }
+
+ protected:
+ ~SupportsWeakPtr() = default;
+
+ private:
+ internal::WeakReferenceOwner weak_reference_owner_;
+ DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr);
+};
+
+// Helper function that uses type deduction to safely return a WeakPtr<Derived>
+// when Derived doesn't directly extend SupportsWeakPtr<Derived>, instead it
+// extends a Base that extends SupportsWeakPtr<Base>.
+//
+// EXAMPLE:
+// class Base : public base::SupportsWeakPtr<Producer> {};
+// class Derived : public Base {};
+//
+// Derived derived;
+// base::WeakPtr<Derived> ptr = base::AsWeakPtr(&derived);
+//
+// Note that the following doesn't work (invalid type conversion) since
+// Derived::AsWeakPtr() is WeakPtr<Base> SupportsWeakPtr<Base>::AsWeakPtr(),
+// and there's no way to safely cast WeakPtr<Base> to WeakPtr<Derived> at
+// the caller.
+//
+// base::WeakPtr<Derived> ptr = derived.AsWeakPtr(); // Fails.
+
+template <typename Derived>
+WeakPtr<Derived> AsWeakPtr(Derived* t) {
+ return internal::SupportsWeakPtrBase::StaticAsWeakPtr<Derived>(t);
+}
+
+} // namespace base
+
+#endif // BASE_MEMORY_WEAK_PTR_H_
diff --git a/gn/base/numerics/checked_math.h b/gn/base/numerics/checked_math.h
new file mode 100644
index 00000000000..433860c58ba
--- /dev/null
+++ b/gn/base/numerics/checked_math.h
@@ -0,0 +1,393 @@
+// 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_CHECKED_MATH_H_
+#define BASE_NUMERICS_CHECKED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/checked_math_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+class CheckedNumeric {
+ static_assert(std::is_arithmetic<T>::value,
+ "CheckedNumeric<T>: T must be a numeric type.");
+
+ public:
+ using type = T;
+
+ constexpr CheckedNumeric() = default;
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
+ : state_(rhs.state_.value(), rhs.IsValid()) {}
+
+ template <typename Src>
+ friend class CheckedNumeric;
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to CheckedNumerics to make them easier to use.
+ template <typename Src>
+ constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
+ : state_(value) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // This is not an explicit constructor because we want a seamless conversion
+ // from StrictNumeric types.
+ template <typename Src>
+ constexpr CheckedNumeric(
+ StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+ : state_(static_cast<Src>(value)) {}
+
+ // IsValid() - The public API to test if a CheckedNumeric is currently valid.
+ // A range checked destination type can be supplied using the Dst template
+ // parameter.
+ template <typename Dst = T>
+ constexpr bool IsValid() const {
+ return state_.is_valid() &&
+ IsValueInRangeForNumericType<Dst>(state_.value());
+ }
+
+ // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
+ // and is within the range supported by the destination type. Returns true if
+ // successful and false otherwise.
+ template <typename Dst>
+#if defined(__clang__) || defined(__GNUC__)
+ __attribute__((warn_unused_result))
+#elif defined(_MSC_VER)
+ _Check_return_
+#endif
+ constexpr bool
+ AssignIfValid(Dst* result) const {
+ return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+ ? ((*result = static_cast<Dst>(state_.value())), true)
+ : false;
+ }
+
+ // ValueOrDie() - The primary accessor for the underlying value. If the
+ // current state is not valid it will CHECK and crash.
+ // A range checked destination type can be supplied using the Dst template
+ // parameter, which will trigger a CHECK if the value is not in bounds for
+ // the destination.
+ // The CHECK behavior can be overridden by supplying a handler as a
+ // template parameter, for test code, etc. However, the handler cannot access
+ // the underlying value, and it is not available through other means.
+ template <typename Dst = T, class CheckHandler = CheckOnFailure>
+ constexpr StrictNumeric<Dst> ValueOrDie() const {
+ return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+ ? static_cast<Dst>(state_.value())
+ : CheckHandler::template HandleFailure<Dst>();
+ }
+
+ // ValueOrDefault(T default_value) - A convenience method that returns the
+ // current value if the state is valid, and the supplied default_value for
+ // any other state.
+ // A range checked destination type can be supplied using the Dst template
+ // parameter. WARNING: This function may fail to compile or CHECK at runtime
+ // if the supplied default_value is not within range of the destination type.
+ template <typename Dst = T, typename Src>
+ constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
+ return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+ ? static_cast<Dst>(state_.value())
+ : checked_cast<Dst>(default_value);
+ }
+
+ // Returns a checked numeric of the specified type, cast from the current
+ // CheckedNumeric. If the current state is invalid or the destination cannot
+ // represent the result then the returned CheckedNumeric will be invalid.
+ template <typename Dst>
+ constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+ return *this;
+ }
+
+ // This friend method is available solely for providing more detailed logging
+ // in the the tests. Do not implement it in production code, because the
+ // underlying values may change at any time.
+ template <typename U>
+ friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
+
+ // Prototypes for the supported arithmetic operator overloads.
+ template <typename Src>
+ constexpr CheckedNumeric& operator+=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator-=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator*=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator/=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator%=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator<<=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator>>=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator&=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator|=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator^=(const Src rhs);
+
+ constexpr CheckedNumeric operator-() const {
+ // The negation of two's complement int min is int min, so we simply
+ // check for that in the constexpr case.
+ // We use an optimized code path for a known run-time variable.
+ return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
+ std::is_floating_point<T>::value
+ ? CheckedNumeric<T>(
+ NegateWrapper(state_.value()),
+ IsValid() && (!std::is_signed<T>::value ||
+ std::is_floating_point<T>::value ||
+ NegateWrapper(state_.value()) !=
+ std::numeric_limits<T>::lowest()))
+ : FastRuntimeNegate();
+ }
+
+ constexpr CheckedNumeric operator~() const {
+ return CheckedNumeric<decltype(InvertWrapper(T()))>(
+ InvertWrapper(state_.value()), IsValid());
+ }
+
+ constexpr CheckedNumeric Abs() const {
+ return !IsValueNegative(state_.value()) ? *this : -*this;
+ }
+
+ template <typename U>
+ constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
+ const U rhs) const {
+ using R = typename UnderlyingType<U>::type;
+ using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
+ // TODO(jschuh): This can be converted to the MathOp version and remain
+ // constexpr once we have C++14 support.
+ return CheckedNumeric<result_type>(
+ static_cast<result_type>(
+ IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
+ ? state_.value()
+ : Wrapper<U>::value(rhs)),
+ state_.is_valid() && Wrapper<U>::is_valid(rhs));
+ }
+
+ template <typename U>
+ constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
+ const U rhs) const {
+ using R = typename UnderlyingType<U>::type;
+ using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
+ // TODO(jschuh): This can be converted to the MathOp version and remain
+ // constexpr once we have C++14 support.
+ return CheckedNumeric<result_type>(
+ static_cast<result_type>(
+ IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
+ ? state_.value()
+ : Wrapper<U>::value(rhs)),
+ state_.is_valid() && Wrapper<U>::is_valid(rhs));
+ }
+
+ // This function is available only for integral types. It returns an unsigned
+ // integer of the same width as the source type, containing the absolute value
+ // of the source, and properly handling signed min.
+ constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
+ UnsignedAbs() const {
+ return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+ SafeUnsignedAbs(state_.value()), state_.is_valid());
+ }
+
+ constexpr CheckedNumeric& operator++() {
+ *this += 1;
+ return *this;
+ }
+
+ constexpr CheckedNumeric operator++(int) {
+ CheckedNumeric value = *this;
+ *this += 1;
+ return value;
+ }
+
+ constexpr CheckedNumeric& operator--() {
+ *this -= 1;
+ return *this;
+ }
+
+ constexpr CheckedNumeric operator--(int) {
+ CheckedNumeric value = *this;
+ *this -= 1;
+ return value;
+ }
+
+ // These perform the actual math operations on the CheckedNumerics.
+ // Binary arithmetic operations.
+ template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ T result = 0;
+ bool is_valid =
+ Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
+ Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
+ return CheckedNumeric<T>(result, is_valid);
+ }
+
+ // Assignment arithmetic operations.
+ template <template <typename, typename, typename> class M, typename R>
+ constexpr CheckedNumeric& MathOp(const R rhs) {
+ using Math = typename MathWrapper<M, T, R>::math;
+ T result = 0; // Using T as the destination saves a range check.
+ bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
+ Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
+ *this = CheckedNumeric<T>(result, is_valid);
+ return *this;
+ }
+
+ private:
+ CheckedNumericState<T> state_;
+
+ CheckedNumeric FastRuntimeNegate() const {
+ T result;
+ bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
+ return CheckedNumeric<T>(result, IsValid() && success);
+ }
+
+ template <typename Src>
+ constexpr CheckedNumeric(Src value, bool is_valid)
+ : state_(value, is_valid) {}
+
+ // These wrappers allow us to handle state the same way for both
+ // CheckedNumeric and POD arithmetic types.
+ template <typename Src>
+ struct Wrapper {
+ static constexpr bool is_valid(Src) { return true; }
+ static constexpr Src value(Src value) { return value; }
+ };
+
+ template <typename Src>
+ struct Wrapper<CheckedNumeric<Src>> {
+ static constexpr bool is_valid(const CheckedNumeric<Src> v) {
+ return v.IsValid();
+ }
+ static constexpr Src value(const CheckedNumeric<Src> v) {
+ return v.state_.value();
+ }
+ };
+
+ template <typename Src>
+ struct Wrapper<StrictNumeric<Src>> {
+ static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
+ static constexpr Src value(const StrictNumeric<Src> v) {
+ return static_cast<Src>(v);
+ }
+ };
+};
+
+// Convenience functions to avoid the ugly template disambiguator syntax.
+template <typename Dst, typename Src>
+constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
+ return value.template IsValid<Dst>();
+}
+
+template <typename Dst, typename Src>
+constexpr StrictNumeric<Dst> ValueOrDieForType(
+ const CheckedNumeric<Src> value) {
+ return value.template ValueOrDie<Dst>();
+}
+
+template <typename Dst, typename Src, typename Default>
+constexpr StrictNumeric<Dst> ValueOrDefaultForType(
+ const CheckedNumeric<Src> value,
+ const Default default_value) {
+ return value.template ValueOrDefault<Dst>(default_value);
+}
+
+// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
+// or CheckedNumericType.
+template <typename T>
+constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
+ const T value) {
+ return value;
+}
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
+ const L lhs,
+ const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+ rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
+CheckMathOp(const L lhs, const R rhs, const Args... args) {
+ return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
+
+// These are some extra StrictNumeric operators to support simple pointer
+// arithmetic with our result types. Since wrapping on a pointer is always
+// bad, we trigger the CHECK condition here.
+template <typename L, typename R>
+L* operator+(L* lhs, const StrictNumeric<R> rhs) {
+ uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
+ CheckMul(sizeof(L), static_cast<R>(rhs)))
+ .template ValueOrDie<uintptr_t>();
+ return reinterpret_cast<L*>(result);
+}
+
+template <typename L, typename R>
+L* operator-(L* lhs, const StrictNumeric<R> rhs) {
+ uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
+ CheckMul(sizeof(L), static_cast<R>(rhs)))
+ .template ValueOrDie<uintptr_t>();
+ return reinterpret_cast<L*>(result);
+}
+
+} // namespace internal
+
+using internal::CheckAdd;
+using internal::CheckAnd;
+using internal::CheckDiv;
+using internal::CheckedNumeric;
+using internal::CheckLsh;
+using internal::CheckMax;
+using internal::CheckMin;
+using internal::CheckMod;
+using internal::CheckMul;
+using internal::CheckOr;
+using internal::CheckRsh;
+using internal::CheckSub;
+using internal::CheckXor;
+using internal::IsValidForType;
+using internal::MakeCheckedNum;
+using internal::ValueOrDefaultForType;
+using internal::ValueOrDieForType;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_CHECKED_MATH_H_
diff --git a/gn/base/numerics/checked_math_impl.h b/gn/base/numerics/checked_math_impl.h
new file mode 100644
index 00000000000..e083389ebf3
--- /dev/null
+++ b/gn/base/numerics/checked_math_impl.h
@@ -0,0 +1,567 @@
+// 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_CHECKED_MATH_IMPL_H_
+#define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math_shared_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+constexpr bool CheckedAddImpl(T x, T y, T* result) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ // Since the value of x+y is undefined if we have a signed type, we compute
+ // it using the unsigned type of the same size.
+ using UnsignedDst = typename std::make_unsigned<T>::type;
+ using SignedDst = typename std::make_signed<T>::type;
+ UnsignedDst ux = static_cast<UnsignedDst>(x);
+ UnsignedDst uy = static_cast<UnsignedDst>(y);
+ UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
+ *result = static_cast<T>(uresult);
+ // Addition is valid if the sign of (x + y) is equal to either that of x or
+ // that of y.
+ return (std::is_signed<T>::value)
+ ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
+ : uresult >= uy; // Unsigned is either valid or underflow.
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedAddOp {};
+
+template <typename T, typename U>
+struct CheckedAddOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (CheckedAddFastOp<T, U>::is_supported)
+ return CheckedAddFastOp<T, U>::Do(x, y, result);
+
+ // Double the underlying type up to a full machine word.
+ using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ using Promotion =
+ typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+ IntegerBitsPlusSign<intptr_t>::value),
+ typename BigEnoughPromotion<T, U>::type,
+ FastPromotion>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y))) {
+ return false;
+ }
+
+ Promotion presult = {};
+ bool is_valid = true;
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+ presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
+ } else {
+ is_valid = CheckedAddImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ }
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T>
+constexpr bool CheckedSubImpl(T x, T y, T* result) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ // Since the value of x+y is undefined if we have a signed type, we compute
+ // it using the unsigned type of the same size.
+ using UnsignedDst = typename std::make_unsigned<T>::type;
+ using SignedDst = typename std::make_signed<T>::type;
+ UnsignedDst ux = static_cast<UnsignedDst>(x);
+ UnsignedDst uy = static_cast<UnsignedDst>(y);
+ UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
+ *result = static_cast<T>(uresult);
+ // Subtraction is valid if either x and y have same sign, or (x-y) and x have
+ // the same sign.
+ return (std::is_signed<T>::value)
+ ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
+ : x >= y;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedSubOp {};
+
+template <typename T, typename U>
+struct CheckedSubOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (CheckedSubFastOp<T, U>::is_supported)
+ return CheckedSubFastOp<T, U>::Do(x, y, result);
+
+ // Double the underlying type up to a full machine word.
+ using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ using Promotion =
+ typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+ IntegerBitsPlusSign<intptr_t>::value),
+ typename BigEnoughPromotion<T, U>::type,
+ FastPromotion>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y))) {
+ return false;
+ }
+
+ Promotion presult = {};
+ bool is_valid = true;
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+ presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
+ } else {
+ is_valid = CheckedSubImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ }
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T>
+constexpr bool CheckedMulImpl(T x, T y, T* result) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ // Since the value of x*y is potentially undefined if we have a signed type,
+ // we compute it using the unsigned type of the same size.
+ using UnsignedDst = typename std::make_unsigned<T>::type;
+ using SignedDst = typename std::make_signed<T>::type;
+ const UnsignedDst ux = SafeUnsignedAbs(x);
+ const UnsignedDst uy = SafeUnsignedAbs(y);
+ UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
+ const bool is_negative =
+ std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
+ *result = is_negative ? 0 - uresult : uresult;
+ // We have a fast out for unsigned identity or zero on the second operand.
+ // After that it's an unsigned overflow check on the absolute value, with
+ // a +1 bound for a negative result.
+ return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
+ ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedMulOp {};
+
+template <typename T, typename U>
+struct CheckedMulOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (CheckedMulFastOp<T, U>::is_supported)
+ return CheckedMulFastOp<T, U>::Do(x, y, result);
+
+ using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ // Verify the destination type can hold the result (always true for 0).
+ if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) &&
+ x && y)) {
+ return false;
+ }
+
+ Promotion presult = {};
+ bool is_valid = true;
+ if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
+ // The fast op may be available with the promoted type.
+ is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult);
+ } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+ presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
+ } else {
+ is_valid = CheckedMulImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ }
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+// Division just requires a check for a zero denominator or an invalid negation
+// on signed min/-1.
+template <typename T, typename U, class Enable = void>
+struct CheckedDivOp {};
+
+template <typename T, typename U>
+struct CheckedDivOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ if (BASE_NUMERICS_UNLIKELY(!y))
+ return false;
+
+ // The overflow check can be compiled away if we don't have the exact
+ // combination of types needed to trigger this case.
+ using Promotion = typename BigEnoughPromotion<T, U>::type;
+ if (BASE_NUMERICS_UNLIKELY(
+ (std::is_signed<T>::value && std::is_signed<U>::value &&
+ IsTypeInRangeForNumericType<T, Promotion>::value &&
+ static_cast<Promotion>(x) ==
+ std::numeric_limits<Promotion>::lowest() &&
+ y == static_cast<U>(-1)))) {
+ return false;
+ }
+
+ // This branch always compiles away if the above branch wasn't removed.
+ if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) &&
+ x)) {
+ return false;
+ }
+
+ Promotion presult = Promotion(x) / Promotion(y);
+ *result = static_cast<V>(presult);
+ return IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedModOp {};
+
+template <typename T, typename U>
+struct CheckedModOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ using Promotion = typename BigEnoughPromotion<T, U>::type;
+ if (BASE_NUMERICS_LIKELY(y)) {
+ Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y);
+ *result = static_cast<Promotion>(presult);
+ return IsValueInRangeForNumericType<V>(presult);
+ }
+ return false;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedLshOp {};
+
+// Left shift. Shifts less than 0 or greater than or equal to the number
+// of bits in the promoted type are undefined. Shifts of negative values
+// are undefined. Otherwise it is defined when the result fits.
+template <typename T, typename U>
+struct CheckedLshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V>
+ static constexpr bool Do(T x, U shift, V* result) {
+ // Disallow negative numbers and verify the shift is in bounds.
+ if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
+ as_unsigned(shift) <
+ as_unsigned(std::numeric_limits<T>::digits))) {
+ // Shift as unsigned to avoid undefined behavior.
+ *result = static_cast<V>(as_unsigned(x) << shift);
+ // If the shift can be reversed, we know it was valid.
+ return *result >> shift == x;
+ }
+
+ // Handle the legal corner-case of a full-width signed shift of zero.
+ return std::is_signed<T>::value && !x &&
+ as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedRshOp {};
+
+// Right shift. Shifts less than 0 or greater than or equal to the number
+// of bits in the promoted type are undefined. Otherwise, it is always defined,
+// but a right shift of a negative value is implementation-dependent.
+template <typename T, typename U>
+struct CheckedRshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V>
+ static bool Do(T x, U shift, V* result) {
+ // Use the type conversion push negative values out of range.
+ if (BASE_NUMERICS_LIKELY(as_unsigned(shift) <
+ IntegerBitsPlusSign<T>::value)) {
+ T tmp = x >> shift;
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+ return false;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedAndOp {};
+
+// For simplicity we support only unsigned integer results.
+template <typename T, typename U>
+struct CheckedAndOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedOrOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct CheckedOrOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct CheckedXorOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+// Max doesn't really need to be implemented this way because it can't fail,
+// but it makes the code much cleaner to use the MathOp wrappers.
+template <typename T, typename U, class Enable = void>
+struct CheckedMaxOp {};
+
+template <typename T, typename U>
+struct CheckedMaxOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
+ : static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+// Min doesn't really need to be implemented this way because it can't fail,
+// but it makes the code much cleaner to use the MathOp wrappers.
+template <typename T, typename U, class Enable = void>
+struct CheckedMinOp {};
+
+template <typename T, typename U>
+struct CheckedMinOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename LowestValuePromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
+ : static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
+ template <typename T, typename U> \
+ struct Checked##NAME##Op< \
+ T, U, \
+ typename std::enable_if<std::is_floating_point<T>::value || \
+ std::is_floating_point<U>::value>::type> { \
+ using result_type = typename MaxExponentPromotion<T, U>::type; \
+ template <typename V> \
+ static constexpr bool Do(T x, U y, V* result) { \
+ using Promotion = typename MaxExponentPromotion<T, U>::type; \
+ Promotion presult = x OP y; \
+ *result = static_cast<V>(presult); \
+ return IsValueInRangeForNumericType<V>(presult); \
+ } \
+ };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+// Floats carry around their validity state with them, but integers do not. So,
+// we wrap the underlying value in a specialization in order to hide that detail
+// and expose an interface via accessors.
+enum NumericRepresentation {
+ NUMERIC_INTEGER,
+ NUMERIC_FLOATING,
+ NUMERIC_UNKNOWN
+};
+
+template <typename NumericType>
+struct GetNumericRepresentation {
+ static const NumericRepresentation value =
+ std::is_integral<NumericType>::value
+ ? NUMERIC_INTEGER
+ : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
+ : NUMERIC_UNKNOWN);
+};
+
+template <typename T,
+ NumericRepresentation type = GetNumericRepresentation<T>::value>
+class CheckedNumericState {};
+
+// Integrals require quite a bit of additional housekeeping to manage state.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_INTEGER> {
+ private:
+ // is_valid_ precedes value_ because member intializers in the constructors
+ // are evaluated in field order, and is_valid_ must be read when initializing
+ // value_.
+ bool is_valid_;
+ T value_;
+
+ // Ensures that a type conversion does not trigger undefined behavior.
+ template <typename Src>
+ static constexpr T WellDefinedConversionOrZero(const Src value,
+ const bool is_valid) {
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return (std::is_integral<SrcType>::value || is_valid)
+ ? static_cast<T>(value)
+ : static_cast<T>(0);
+ }
+
+ public:
+ template <typename Src, NumericRepresentation type>
+ friend class CheckedNumericState;
+
+ constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
+
+ template <typename Src>
+ constexpr CheckedNumericState(Src value, bool is_valid)
+ : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
+ value_(WellDefinedConversionOrZero(value, is_valid_)) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : is_valid_(rhs.IsValid()),
+ value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
+
+ template <typename Src>
+ constexpr explicit CheckedNumericState(Src value)
+ : is_valid_(IsValueInRangeForNumericType<T>(value)),
+ value_(WellDefinedConversionOrZero(value, is_valid_)) {}
+
+ constexpr bool is_valid() const { return is_valid_; }
+ constexpr T value() const { return value_; }
+};
+
+// Floating points maintain their own validity, but need translation wrappers.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_FLOATING> {
+ private:
+ T value_;
+
+ // Ensures that a type conversion does not trigger undefined behavior.
+ template <typename Src>
+ static constexpr T WellDefinedConversionOrNaN(const Src value,
+ const bool is_valid) {
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
+ NUMERIC_RANGE_CONTAINED ||
+ is_valid)
+ ? static_cast<T>(value)
+ : std::numeric_limits<T>::quiet_NaN();
+ }
+
+ public:
+ template <typename Src, NumericRepresentation type>
+ friend class CheckedNumericState;
+
+ constexpr CheckedNumericState() : value_(0.0) {}
+
+ template <typename Src>
+ constexpr CheckedNumericState(Src value, bool is_valid)
+ : value_(WellDefinedConversionOrNaN(value, is_valid)) {}
+
+ template <typename Src>
+ constexpr explicit CheckedNumericState(Src value)
+ : value_(WellDefinedConversionOrNaN(
+ value,
+ IsValueInRangeForNumericType<T>(value))) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : value_(WellDefinedConversionOrNaN(
+ rhs.value(),
+ rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
+
+ constexpr bool is_valid() const {
+ // Written this way because std::isfinite is not reliably constexpr.
+ return MustTreatAsConstexpr(value_)
+ ? value_ <= std::numeric_limits<T>::max() &&
+ value_ >= std::numeric_limits<T>::lowest()
+ : std::isfinite(value_);
+ }
+ constexpr T value() const { return value_; }
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
diff --git a/gn/base/numerics/clamped_math.h b/gn/base/numerics/clamped_math.h
new file mode 100644
index 00000000000..9e8354353a6
--- /dev/null
+++ b/gn/base/numerics/clamped_math.h
@@ -0,0 +1,262 @@
+// 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_CLAMPED_MATH_H_
+#define BASE_NUMERICS_CLAMPED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/clamped_math_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+class ClampedNumeric {
+ static_assert(std::is_arithmetic<T>::value,
+ "ClampedNumeric<T>: T must be a numeric type.");
+
+ public:
+ using type = T;
+
+ constexpr ClampedNumeric() : value_(0) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
+ : value_(saturated_cast<T>(rhs.value_)) {}
+
+ template <typename Src>
+ friend class ClampedNumeric;
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to ClampedNumerics to make them easier to use.
+ template <typename Src>
+ constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit)
+ : value_(saturated_cast<T>(value)) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // This is not an explicit constructor because we want a seamless conversion
+ // from StrictNumeric types.
+ template <typename Src>
+ constexpr ClampedNumeric(
+ StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+ : value_(saturated_cast<T>(static_cast<Src>(value))) {}
+
+ // Returns a ClampedNumeric of the specified type, cast from the current
+ // ClampedNumeric, and saturated to the destination type.
+ template <typename Dst>
+ constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+ return *this;
+ }
+
+ // Prototypes for the supported arithmetic operator overloads.
+ template <typename Src>
+ constexpr ClampedNumeric& operator+=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator-=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator*=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator/=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator%=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator<<=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator>>=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator&=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator|=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator^=(const Src rhs);
+
+ constexpr ClampedNumeric operator-() const {
+ // The negation of two's complement int min is int min, so that's the
+ // only overflow case where we will saturate.
+ return ClampedNumeric<T>(SaturatedNegWrapper(value_));
+ }
+
+ constexpr ClampedNumeric operator~() const {
+ return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
+ }
+
+ constexpr ClampedNumeric Abs() const {
+ // The negation of two's complement int min is int min, so that's the
+ // only overflow case where we will saturate.
+ return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
+ }
+
+ template <typename U>
+ constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
+ const U rhs) const {
+ using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
+ return ClampedNumeric<result_type>(
+ ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
+ }
+
+ template <typename U>
+ constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
+ const U rhs) const {
+ using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
+ return ClampedNumeric<result_type>(
+ ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
+ }
+
+ // This function is available only for integral types. It returns an unsigned
+ // integer of the same width as the source type, containing the absolute value
+ // of the source, and properly handling signed min.
+ constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
+ UnsignedAbs() const {
+ return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+ SafeUnsignedAbs(value_));
+ }
+
+ constexpr ClampedNumeric& operator++() {
+ *this += 1;
+ return *this;
+ }
+
+ constexpr ClampedNumeric operator++(int) {
+ ClampedNumeric value = *this;
+ *this += 1;
+ return value;
+ }
+
+ constexpr ClampedNumeric& operator--() {
+ *this -= 1;
+ return *this;
+ }
+
+ constexpr ClampedNumeric operator--(int) {
+ ClampedNumeric value = *this;
+ *this -= 1;
+ return value;
+ }
+
+ // These perform the actual math operations on the ClampedNumerics.
+ // Binary arithmetic operations.
+ template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return ClampedNumeric<T>(
+ Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
+ }
+
+ // Assignment arithmetic operations.
+ template <template <typename, typename, typename> class M, typename R>
+ constexpr ClampedNumeric& MathOp(const R rhs) {
+ using Math = typename MathWrapper<M, T, R>::math;
+ *this =
+ ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
+ return *this;
+ }
+
+ template <typename Dst>
+ constexpr operator Dst() const {
+ return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
+ value_);
+ }
+
+ // This method extracts the raw integer value without saturating it to the
+ // destination type as the conversion operator does. This is useful when
+ // e.g. assigning to an auto type or passing as a deduced template parameter.
+ constexpr T RawValue() const { return value_; }
+
+ private:
+ T value_;
+
+ // These wrappers allow us to handle state the same way for both
+ // ClampedNumeric and POD arithmetic types.
+ template <typename Src>
+ struct Wrapper {
+ static constexpr Src value(Src value) {
+ return static_cast<typename UnderlyingType<Src>::type>(value);
+ }
+ };
+};
+
+// Convience wrapper to return a new ClampedNumeric from the provided arithmetic
+// or ClampedNumericType.
+template <typename T>
+constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
+ const T value) {
+ return value;
+}
+
+// Overload the ostream output operator to make logging work nicely.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
+ const L lhs,
+ const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+ rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+constexpr ClampedNumeric<typename ResultType<M, L, R, Args...>::type>
+ClampMathOp(const L lhs, const R rhs, const Args... args) {
+ return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=);
+
+} // namespace internal
+
+using internal::ClampAdd;
+using internal::ClampAnd;
+using internal::ClampDiv;
+using internal::ClampedNumeric;
+using internal::ClampLsh;
+using internal::ClampMax;
+using internal::ClampMin;
+using internal::ClampMod;
+using internal::ClampMul;
+using internal::ClampOr;
+using internal::ClampRsh;
+using internal::ClampSub;
+using internal::ClampXor;
+using internal::MakeClampedNum;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_CLAMPED_MATH_H_
diff --git a/gn/base/numerics/clamped_math_impl.h b/gn/base/numerics/clamped_math_impl.h
new file mode 100644
index 00000000000..303a7e945a1
--- /dev/null
+++ b/gn/base/numerics/clamped_math_impl.h
@@ -0,0 +1,341 @@
+// 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_CLAMPED_MATH_IMPL_H_
+#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math_shared_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_signed<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+ return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
+ ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
+ ? NegateWrapper(value)
+ : std::numeric_limits<T>::max())
+ : ClampedNegFastOp<T>::Do(value);
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ !std::is_signed<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+ return T(0);
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+ return -value;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+ // The calculation below is a static identity for unsigned types, but for
+ // signed integer types it provides a non-branching, saturated absolute value.
+ // This works because SafeUnsignedAbs() returns an unsigned type, which can
+ // represent the absolute value of all negative numbers of an equal-width
+ // integer type. The call to IsValueNegative() then detects overflow in the
+ // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
+ // a signed integer value. If it is the overflow case, we end up subtracting
+ // one from the unsigned result, thus saturating to numeric_limits<T>::max().
+ return static_cast<T>(SafeUnsignedAbs(value) -
+ IsValueNegative<T>(SafeUnsignedAbs(value)));
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+ return value < 0 ? -value : value;
+}
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAddOp {};
+
+template <typename T, typename U>
+struct ClampedAddOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ if (ClampedAddFastOp<T, U>::is_supported)
+ return ClampedAddFastOp<T, U>::template Do<V>(x, y);
+
+ static_assert(std::is_same<V, result_type>::value ||
+ IsTypeInRangeForNumericType<U, V>::value,
+ "The saturation result cannot be determined from the "
+ "provided types.");
+ const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
+ V result = {};
+ return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
+ ? result
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedSubOp {};
+
+template <typename T, typename U>
+struct ClampedSubOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (ClampedSubFastOp<T, U>::is_supported)
+ return ClampedSubFastOp<T, U>::template Do<V>(x, y);
+
+ static_assert(std::is_same<V, result_type>::value ||
+ IsTypeInRangeForNumericType<U, V>::value,
+ "The saturation result cannot be determined from the "
+ "provided types.");
+ const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
+ V result = {};
+ return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
+ ? result
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMulOp {};
+
+template <typename T, typename U>
+struct ClampedMulOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (ClampedMulFastOp<T, U>::is_supported)
+ return ClampedMulFastOp<T, U>::template Do<V>(x, y);
+
+ V result = {};
+ const V saturated =
+ CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+ return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
+ ? result
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedDivOp {};
+
+template <typename T, typename U>
+struct ClampedDivOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ V result = {};
+ if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
+ return result;
+ // Saturation goes to max, min, or NaN (if x is zero).
+ return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
+ : SaturationDefaultLimits<V>::NaN();
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedModOp {};
+
+template <typename T, typename U>
+struct ClampedModOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ V result = {};
+ return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
+ ? result
+ : x;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedLshOp {};
+
+// Left shift. Non-zero values saturate in the direction of the sign. A zero
+// shifted by any value always results in zero.
+template <typename T, typename U>
+struct ClampedLshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U shift) {
+ static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+ if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
+ // Shift as unsigned to avoid undefined behavior.
+ V result = static_cast<V>(as_unsigned(x) << shift);
+ // If the shift can be reversed, we know it was valid.
+ if (BASE_NUMERICS_LIKELY(result >> shift == x))
+ return result;
+ }
+ return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedRshOp {};
+
+// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
+template <typename T, typename U>
+struct ClampedRshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U shift) {
+ static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+ // Signed right shift is odd, because it saturates to -1 or 0.
+ const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
+ return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
+ ? saturated_cast<V>(x >> shift)
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAndOp {};
+
+template <typename T, typename U>
+struct ClampedAndOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) & static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedOrOp {};
+
+// For simplicity we promote to unsigned integers.
+template <typename T, typename U>
+struct ClampedOrOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) | static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct ClampedXorOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) ^ static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMaxOp {};
+
+template <typename T, typename U>
+struct ClampedMaxOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
+ : saturated_cast<V>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMinOp {};
+
+template <typename T, typename U>
+struct ClampedMinOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename LowestValuePromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
+ : saturated_cast<V>(y);
+ }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
+ template <typename T, typename U> \
+ struct Clamped##NAME##Op< \
+ T, U, \
+ typename std::enable_if<std::is_floating_point<T>::value || \
+ std::is_floating_point<U>::value>::type> { \
+ using result_type = typename MaxExponentPromotion<T, U>::type; \
+ template <typename V = result_type> \
+ static constexpr V Do(T x, U y) { \
+ return saturated_cast<V>(x OP y); \
+ } \
+ };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
diff --git a/gn/base/numerics/math_constants.h b/gn/base/numerics/math_constants.h
new file mode 100644
index 00000000000..9a5b8efdbcc
--- /dev/null
+++ b/gn/base/numerics/math_constants.h
@@ -0,0 +1,15 @@
+// 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_MATH_CONSTANTS_H_
+#define BASE_NUMERICS_MATH_CONSTANTS_H_
+
+namespace base {
+
+constexpr double kPiDouble = 3.14159265358979323846;
+constexpr float kPiFloat = 3.14159265358979323846f;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_MATH_CONSTANTS_H_
diff --git a/gn/base/numerics/ranges.h b/gn/base/numerics/ranges.h
new file mode 100644
index 00000000000..f19320cedda
--- /dev/null
+++ b/gn/base/numerics/ranges.h
@@ -0,0 +1,27 @@
+// 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_RANGES_H_
+#define BASE_NUMERICS_RANGES_H_
+
+#include <algorithm>
+#include <cmath>
+
+namespace base {
+
+// To be replaced with std::clamp() from C++17, someday.
+template <class T>
+constexpr const T& ClampToRange(const T& value, const T& min, const T& max) {
+ return std::min(std::max(value, min), max);
+}
+
+template <typename T>
+constexpr bool IsApproximatelyEqual(T lhs, T rhs, T tolerance) {
+ static_assert(std::is_arithmetic<T>::value, "Argument must be arithmetic");
+ return std::abs(rhs - lhs) <= tolerance;
+}
+
+} // namespace base
+
+#endif // BASE_NUMERICS_RANGES_H_
diff --git a/gn/base/numerics/safe_conversions.h b/gn/base/numerics/safe_conversions.h
new file mode 100644
index 00000000000..a4fe387e8a7
--- /dev/null
+++ b/gn/base/numerics/safe_conversions.h
@@ -0,0 +1,344 @@
+// Copyright 2014 The Chromium Authors. 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_CONVERSIONS_H_
+#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <ostream>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions_impl.h"
+
+#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
+#include "base/numerics/safe_conversions_arm_impl.h"
+#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
+#else
+#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
+#endif
+
+namespace base {
+namespace internal {
+
+#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+template <typename Dst, typename Src>
+struct SaturateFastAsmOp {
+ static const bool is_supported = false;
+ static constexpr Dst Do(Src) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<Dst>();
+ }
+};
+#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+
+// The following special case a few specific integer conversions where we can
+// eke out better performance than range checking.
+template <typename Dst, typename Src, typename Enable = void>
+struct IsValueInRangeFastOp {
+ static const bool is_supported = false;
+ static constexpr bool Do(Src value) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+// Signed to signed range comparison.
+template <typename Dst, typename Src>
+struct IsValueInRangeFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<
+ std::is_integral<Dst>::value && std::is_integral<Src>::value &&
+ std::is_signed<Dst>::value && std::is_signed<Src>::value &&
+ !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+ static const bool is_supported = true;
+
+ static constexpr bool Do(Src value) {
+ // Just downcast to the smaller type, sign extend it back to the original
+ // type, and then see if it matches the original value.
+ return value == static_cast<Dst>(value);
+ }
+};
+
+// Signed to unsigned range comparison.
+template <typename Dst, typename Src>
+struct IsValueInRangeFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<
+ std::is_integral<Dst>::value && std::is_integral<Src>::value &&
+ !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
+ !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+ static const bool is_supported = true;
+
+ static constexpr bool Do(Src value) {
+ // We cast a signed as unsigned to overflow negative values to the top,
+ // then compare against whichever maximum is smaller, as our upper bound.
+ return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
+ }
+};
+
+// Convenience function that returns true if the supplied value is in range
+// for the destination type.
+template <typename Dst, typename Src>
+constexpr bool IsValueInRangeForNumericType(Src value) {
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
+ ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
+ static_cast<SrcType>(value))
+ : internal::DstRangeRelationToSrcRange<Dst>(
+ static_cast<SrcType>(value))
+ .IsValid();
+}
+
+// checked_cast<> is analogous to static_cast<> for numeric types,
+// except that it CHECKs that the specified numeric conversion will not
+// overflow or underflow. NaN source will always trigger a CHECK.
+template <typename Dst,
+ class CheckHandler = internal::CheckOnFailure,
+ typename Src>
+constexpr Dst checked_cast(Src value) {
+ // This throws a compile-time error on evaluating the constexpr if it can be
+ // determined at compile-time as failing, otherwise it will CHECK at runtime.
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
+ ? static_cast<Dst>(static_cast<SrcType>(value))
+ : CheckHandler::template HandleFailure<Dst>();
+}
+
+// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
+// You may provide your own limits (e.g. to saturated_cast) so long as you
+// implement all of the static constexpr member functions in the class below.
+template <typename T>
+struct SaturationDefaultLimits : public std::numeric_limits<T> {
+ static constexpr T NaN() {
+ return std::numeric_limits<T>::has_quiet_NaN
+ ? std::numeric_limits<T>::quiet_NaN()
+ : T();
+ }
+ using std::numeric_limits<T>::max;
+ static constexpr T Overflow() {
+ return std::numeric_limits<T>::has_infinity
+ ? std::numeric_limits<T>::infinity()
+ : std::numeric_limits<T>::max();
+ }
+ using std::numeric_limits<T>::lowest;
+ static constexpr T Underflow() {
+ return std::numeric_limits<T>::has_infinity
+ ? std::numeric_limits<T>::infinity() * -1
+ : std::numeric_limits<T>::lowest();
+ }
+};
+
+template <typename Dst, template <typename> class S, typename Src>
+constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
+ // For some reason clang generates much better code when the branch is
+ // structured exactly this way, rather than a sequence of checks.
+ return !constraint.IsOverflowFlagSet()
+ ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
+ : S<Dst>::Underflow())
+ // Skip this check for integral Src, which cannot be NaN.
+ : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
+ ? S<Dst>::Overflow()
+ : S<Dst>::NaN());
+}
+
+// We can reduce the number of conditions and get slightly better performance
+// for normal signed and unsigned integer ranges. And in the specific case of
+// Arm, we can use the optimized saturation instructions.
+template <typename Dst, typename Src, typename Enable = void>
+struct SaturateFastOp {
+ static const bool is_supported = false;
+ static constexpr Dst Do(Src value) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<Dst>();
+ }
+};
+
+template <typename Dst, typename Src>
+struct SaturateFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<std::is_integral<Src>::value &&
+ std::is_integral<Dst>::value>::type> {
+ static const bool is_supported = true;
+ static Dst Do(Src value) {
+ if (SaturateFastAsmOp<Dst, Src>::is_supported)
+ return SaturateFastAsmOp<Dst, Src>::Do(value);
+
+ // The exact order of the following is structured to hit the correct
+ // optimization heuristics across compilers. Do not change without
+ // checking the emitted code.
+ Dst saturated = CommonMaxOrMin<Dst, Src>(
+ IsMaxInRangeForNumericType<Dst, Src>() ||
+ (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
+ return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
+ ? static_cast<Dst>(value)
+ : saturated;
+ }
+};
+
+// saturated_cast<> is analogous to static_cast<> for numeric types, except
+// that the specified numeric conversion will saturate by default rather than
+// overflow or underflow, and NaN assignment to an integral will return 0.
+// All boundary condition behaviors can be overriden with a custom handler.
+template <typename Dst,
+ template <typename> class SaturationHandler = SaturationDefaultLimits,
+ typename Src>
+constexpr Dst saturated_cast(Src value) {
+ using SrcType = typename UnderlyingType<Src>::type;
+ return !IsCompileTimeConstant(value) &&
+ SaturateFastOp<Dst, SrcType>::is_supported &&
+ std::is_same<SaturationHandler<Dst>,
+ SaturationDefaultLimits<Dst>>::value
+ ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
+ : saturated_cast_impl<Dst, SaturationHandler, SrcType>(
+ static_cast<SrcType>(value),
+ DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
+ static_cast<SrcType>(value)));
+}
+
+// strict_cast<> is analogous to static_cast<> for numeric types, except that
+// it will cause a compile failure if the destination type is not large enough
+// to contain any value in the source type. It performs no runtime checking.
+template <typename Dst, typename Src>
+constexpr Dst strict_cast(Src value) {
+ using SrcType = typename UnderlyingType<Src>::type;
+ static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
+ static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
+
+ // If you got here from a compiler error, it's because you tried to assign
+ // from a source type to a destination type that has insufficient range.
+ // The solution may be to change the destination type you're assigning to,
+ // and use one large enough to represent the source.
+ // Alternatively, you may be better served with the checked_cast<> or
+ // saturated_cast<> template functions for your particular use case.
+ static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
+ NUMERIC_RANGE_CONTAINED,
+ "The source type is out of range for the destination type. "
+ "Please see strict_cast<> comments for more information.");
+
+ return static_cast<Dst>(static_cast<SrcType>(value));
+}
+
+// Some wrappers to statically check that a type is in range.
+template <typename Dst, typename Src, class Enable = void>
+struct IsNumericRangeContained {
+ static const bool value = false;
+};
+
+template <typename Dst, typename Src>
+struct IsNumericRangeContained<
+ Dst,
+ Src,
+ typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
+ ArithmeticOrUnderlyingEnum<Src>::value>::type> {
+ static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ NUMERIC_RANGE_CONTAINED;
+};
+
+// StrictNumeric implements compile time range checking between numeric types by
+// wrapping assignment operations in a strict_cast. This class is intended to be
+// used for function arguments and return types, to ensure the destination type
+// can always contain the source type. This is essentially the same as enforcing
+// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
+// incrementally at API boundaries, making it easier to convert code so that it
+// compiles cleanly with truncation warnings enabled.
+// This template should introduce no runtime overhead, but it also provides no
+// runtime checking of any of the associated mathematical operations. Use
+// CheckedNumeric for runtime range checks of the actual value being assigned.
+template <typename T>
+class StrictNumeric {
+ public:
+ using type = T;
+
+ constexpr StrictNumeric() : value_(0) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
+ : value_(strict_cast<T>(rhs.value_)) {}
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to StrictNumerics to make them easier to use.
+ template <typename Src>
+ constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
+ : value_(strict_cast<T>(value)) {}
+
+ // If you got here from a compiler error, it's because you tried to assign
+ // from a source type to a destination type that has insufficient range.
+ // The solution may be to change the destination type you're assigning to,
+ // and use one large enough to represent the source.
+ // If you're assigning from a CheckedNumeric<> class, you may be able to use
+ // the AssignIfValid() member function, specify a narrower destination type to
+ // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
+ // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
+ // If you've encountered an _ambiguous overload_ you can use a static_cast<>
+ // to explicitly cast the result to the destination type.
+ // If none of that works, you may be better served with the checked_cast<> or
+ // saturated_cast<> template functions for your particular use case.
+ template <typename Dst,
+ typename std::enable_if<
+ IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
+ constexpr operator Dst() const {
+ return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
+ }
+
+ private:
+ const T value_;
+};
+
+// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
+template <typename T>
+constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
+ const T value) {
+ return value;
+}
+
+// Overload the ostream output operator to make logging work nicely.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+
+#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
+ template <typename L, typename R, \
+ typename std::enable_if< \
+ internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
+ constexpr bool operator OP(const L lhs, const R rhs) { \
+ return SafeCompare<NAME, typename UnderlyingType<L>::type, \
+ typename UnderlyingType<R>::type>(lhs, rhs); \
+ }
+
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=);
+
+}; // namespace internal
+
+using internal::as_signed;
+using internal::as_unsigned;
+using internal::checked_cast;
+using internal::IsTypeInRangeForNumericType;
+using internal::IsValueInRangeForNumericType;
+using internal::IsValueNegative;
+using internal::MakeStrictNum;
+using internal::SafeUnsignedAbs;
+using internal::saturated_cast;
+using internal::strict_cast;
+using internal::StrictNumeric;
+
+// Explicitly make a shorter size_t alias for convenience.
+using SizeT = StrictNumeric<size_t>;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
diff --git a/gn/base/numerics/safe_conversions_impl.h b/gn/base/numerics/safe_conversions_impl.h
new file mode 100644
index 00000000000..8ae6a547316
--- /dev/null
+++ b/gn/base/numerics/safe_conversions_impl.h
@@ -0,0 +1,848 @@
+// Copyright 2014 The Chromium Authors. 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_CONVERSIONS_IMPL_H_
+#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
+
+#include <stdint.h>
+
+#include <limits>
+#include <type_traits>
+
+#if defined(__GNUC__) || defined(__clang__)
+#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
+#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define BASE_NUMERICS_LIKELY(x) (x)
+#define BASE_NUMERICS_UNLIKELY(x) (x)
+#endif
+
+namespace base {
+namespace internal {
+
+// The std library doesn't provide a binary max_exponent for integers, however
+// we can compute an analog using std::numeric_limits<>::digits.
+template <typename NumericType>
+struct MaxExponent {
+ static const int value = std::is_floating_point<NumericType>::value
+ ? std::numeric_limits<NumericType>::max_exponent
+ : std::numeric_limits<NumericType>::digits + 1;
+};
+
+// The number of bits (including the sign) in an integer. Eliminates sizeof
+// hacks.
+template <typename NumericType>
+struct IntegerBitsPlusSign {
+ static const int value = std::numeric_limits<NumericType>::digits +
+ std::is_signed<NumericType>::value;
+};
+
+// Helper templates for integer manipulations.
+
+template <typename Integer>
+struct PositionOfSignBit {
+ static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
+};
+
+// Determines if a numeric value is negative without throwing compiler
+// warnings on: unsigned(value) < 0.
+template <typename T,
+ typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
+constexpr bool IsValueNegative(T value) {
+ static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
+ return value < 0;
+}
+
+template <typename T,
+ typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
+constexpr bool IsValueNegative(T) {
+ static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
+ return false;
+}
+
+// This performs a fast negation, returning a signed value. It works on unsigned
+// arguments, but probably doesn't do what you want for any unsigned value
+// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
+template <typename T>
+constexpr typename std::make_signed<T>::type ConditionalNegate(
+ T x,
+ bool is_negative) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ using SignedT = typename std::make_signed<T>::type;
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ return static_cast<SignedT>(
+ (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
+}
+
+// This performs a safe, absolute value via unsigned overflow.
+template <typename T>
+constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
+ : static_cast<UnsignedT>(value);
+}
+
+// This allows us to switch paths on known compile-time constants.
+#if defined(__clang__) || defined(__GNUC__)
+constexpr bool CanDetectCompileTimeConstant() {
+ return true;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T v) {
+ return __builtin_constant_p(v);
+}
+#else
+constexpr bool CanDetectCompileTimeConstant() {
+ return false;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T) {
+ return false;
+}
+#endif
+template <typename T>
+constexpr bool MustTreatAsConstexpr(const T v) {
+ // Either we can't detect a compile-time constant, and must always use the
+ // constexpr path, or we know we have a compile-time constant.
+ return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
+}
+
+// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
+// Also used in a constexpr template to trigger a compilation failure on
+// an error condition.
+struct CheckOnFailure {
+ template <typename T>
+ static T HandleFailure() {
+#if defined(_MSC_VER)
+ __debugbreak();
+#elif defined(__GNUC__) || defined(__clang__)
+ __builtin_trap();
+#else
+ ((void)(*(volatile char*)0 = 0));
+#endif
+ return T();
+ }
+};
+
+enum IntegerRepresentation {
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED
+};
+
+// A range for a given nunmeric Src type is contained for a given numeric Dst
+// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
+// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
+// We implement this as template specializations rather than simple static
+// comparisons to ensure type correctness in our comparisons.
+enum NumericRangeRepresentation {
+ NUMERIC_RANGE_NOT_CONTAINED,
+ NUMERIC_RANGE_CONTAINED
+};
+
+// Helper templates to statically determine if our destination type can contain
+// maximum and minimum values represented by the source type.
+
+template <typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign = std::is_signed<Dst>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign = std::is_signed<Src>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED>
+struct StaticDstRangeRelationToSrcRange;
+
+// Same sign: Dst is guaranteed to contain Src only if its range is equal or
+// larger.
+template <typename Dst, typename Src, IntegerRepresentation Sign>
+struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
+ static const NumericRangeRepresentation value =
+ MaxExponent<Dst>::value >= MaxExponent<Src>::value
+ ? NUMERIC_RANGE_CONTAINED
+ : NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// Unsigned to signed: Dst is guaranteed to contain source only if its range is
+// larger.
+template <typename Dst, typename Src>
+struct StaticDstRangeRelationToSrcRange<Dst,
+ Src,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED> {
+ static const NumericRangeRepresentation value =
+ MaxExponent<Dst>::value > MaxExponent<Src>::value
+ ? NUMERIC_RANGE_CONTAINED
+ : NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// Signed to unsigned: Dst cannot be statically determined to contain Src.
+template <typename Dst, typename Src>
+struct StaticDstRangeRelationToSrcRange<Dst,
+ Src,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED> {
+ static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// This class wraps the range constraints as separate booleans so the compiler
+// can identify constants and eliminate unused code paths.
+class RangeCheck {
+ public:
+ constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
+ : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
+ constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
+ constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
+ constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
+ constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
+ constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
+ constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
+ constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
+ constexpr bool operator==(const RangeCheck rhs) const {
+ return is_underflow_ == rhs.is_underflow_ &&
+ is_overflow_ == rhs.is_overflow_;
+ }
+ constexpr bool operator!=(const RangeCheck rhs) const {
+ return !(*this == rhs);
+ }
+
+ private:
+ // Do not change the order of these member variables. The integral conversion
+ // optimization depends on this exact order.
+ const bool is_underflow_;
+ const bool is_overflow_;
+};
+
+// The following helper template addresses a corner case in range checks for
+// conversion from a floating-point type to an integral type of smaller range
+// but larger precision (e.g. float -> unsigned). The problem is as follows:
+// 1. Integral maximum is always one less than a power of two, so it must be
+// truncated to fit the mantissa of the floating point. The direction of
+// rounding is implementation defined, but by default it's always IEEE
+// floats, which round to nearest and thus result in a value of larger
+// magnitude than the integral value.
+// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
+// // is 4294967295u.
+// 2. If the floating point value is equal to the promoted integral maximum
+// value, a range check will erroneously pass.
+// Example: (4294967296f <= 4294967295u) // This is true due to a precision
+// // loss in rounding up to float.
+// 3. When the floating point value is then converted to an integral, the
+// resulting value is out of range for the target integral type and
+// thus is implementation defined.
+// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
+// To fix this bug we manually truncate the maximum value when the destination
+// type is an integral of larger precision than the source floating-point type,
+// such that the resulting maximum is represented exactly as a floating point.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct NarrowingRange {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = typename std::numeric_limits<Dst>;
+
+ // Computes the mask required to make an accurate comparison between types.
+ static const int kShift =
+ (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
+ SrcLimits::digits < DstLimits::digits)
+ ? (DstLimits::digits - SrcLimits::digits)
+ : 0;
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+
+ // Masks out the integer bits that are beyond the precision of the
+ // intermediate type used for comparison.
+ static constexpr T Adjust(T value) {
+ static_assert(std::is_same<T, Dst>::value, "");
+ static_assert(kShift < DstLimits::digits, "");
+ return static_cast<T>(
+ ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
+ IsValueNegative(value)));
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static constexpr T Adjust(T value) {
+ static_assert(std::is_same<T, Dst>::value, "");
+ static_assert(kShift == 0, "");
+ return value;
+ }
+
+ static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
+ static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
+};
+
+template <typename Dst,
+ typename Src,
+ template <typename>
+ class Bounds,
+ IntegerRepresentation DstSign = std::is_signed<Dst>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign = std::is_signed<Src>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ NumericRangeRepresentation DstRange =
+ StaticDstRangeRelationToSrcRange<Dst, Src>::value>
+struct DstRangeRelationToSrcRangeImpl;
+
+// The following templates are for ranges that must be verified at runtime. We
+// split it into checks based on signedness to avoid confusing casts and
+// compiler warnings on signed an unsigned comparisons.
+
+// Same sign narrowing: The range is contained for normal limits.
+template <typename Dst,
+ typename Src,
+ template <typename>
+ class Bounds,
+ IntegerRepresentation DstSign,
+ IntegerRepresentation SrcSign>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ DstSign,
+ SrcSign,
+ NUMERIC_RANGE_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeCheck(
+ static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
+ static_cast<Dst>(value) >= DstLimits::lowest(),
+ static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
+ static_cast<Dst>(value) <= DstLimits::max());
+ }
+};
+
+// Signed to signed narrowing: Both the upper and lower boundaries may be
+// exceeded for standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_SIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
+ }
+};
+
+// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
+// standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeCheck(
+ DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
+ value <= DstLimits::max());
+ }
+};
+
+// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ using Promotion = decltype(Src() + Dst());
+ return RangeCheck(DstLimits::lowest() <= Dst(0) ||
+ static_cast<Promotion>(value) >=
+ static_cast<Promotion>(DstLimits::lowest()),
+ static_cast<Promotion>(value) <=
+ static_cast<Promotion>(DstLimits::max()));
+ }
+};
+
+// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
+// and any negative value exceeds the lower boundary for standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ using Promotion = decltype(Src() + Dst());
+ return RangeCheck(
+ value >= Src(0) && (DstLimits::lowest() == 0 ||
+ static_cast<Dst>(value) >= DstLimits::lowest()),
+ static_cast<Promotion>(SrcLimits::max()) <=
+ static_cast<Promotion>(DstLimits::max()) ||
+ static_cast<Promotion>(value) <=
+ static_cast<Promotion>(DstLimits::max()));
+ }
+};
+
+// Simple wrapper for statically checking if a type's range is contained.
+template <typename Dst, typename Src>
+struct IsTypeInRangeForNumericType {
+ static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ NUMERIC_RANGE_CONTAINED;
+};
+
+template <typename Dst,
+ template <typename> class Bounds = std::numeric_limits,
+ typename Src>
+constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
+ static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
+ return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
+}
+
+// Integer promotion templates used by the portable checked integer arithmetic.
+template <size_t Size, bool IsSigned>
+struct IntegerForDigitsAndSign;
+
+#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
+ template <> \
+ struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
+ std::is_signed<I>::value> { \
+ using type = I; \
+ }
+
+INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
+#undef INTEGER_FOR_DIGITS_AND_SIGN
+
+// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
+// support 128-bit math, then the ArithmeticPromotion template below will need
+// to be updated (or more likely replaced with a decltype expression).
+static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
+ "Max integer size not supported for this toolchain.");
+
+template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
+struct TwiceWiderInteger {
+ using type =
+ typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
+ IsSigned>::type;
+};
+
+enum ArithmeticPromotionCategory {
+ LEFT_PROMOTION, // Use the type of the left-hand argument.
+ RIGHT_PROMOTION // Use the type of the right-hand argument.
+};
+
+// Determines the type that can represent the largest positive value.
+template <typename Lhs,
+ typename Rhs,
+ ArithmeticPromotionCategory Promotion =
+ (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION>
+struct MaxExponentPromotion;
+
+template <typename Lhs, typename Rhs>
+struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ using type = Lhs;
+};
+
+template <typename Lhs, typename Rhs>
+struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ using type = Rhs;
+};
+
+// Determines the type that can represent the lowest arithmetic value.
+template <typename Lhs,
+ typename Rhs,
+ ArithmeticPromotionCategory Promotion =
+ std::is_signed<Lhs>::value
+ ? (std::is_signed<Rhs>::value
+ ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION)
+ : LEFT_PROMOTION)
+ : (std::is_signed<Rhs>::value
+ ? RIGHT_PROMOTION
+ : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION))>
+struct LowestValuePromotion;
+
+template <typename Lhs, typename Rhs>
+struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ using type = Lhs;
+};
+
+template <typename Lhs, typename Rhs>
+struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ using type = Rhs;
+};
+
+// Determines the type that is best able to represent an arithmetic result.
+template <
+ typename Lhs,
+ typename Rhs = Lhs,
+ bool is_intmax_type =
+ std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
+ IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
+ value == IntegerBitsPlusSign<intmax_t>::value,
+ bool is_max_exponent =
+ StaticDstRangeRelationToSrcRange<
+ typename MaxExponentPromotion<Lhs, Rhs>::type,
+ Lhs>::value ==
+ NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
+ typename MaxExponentPromotion<Lhs, Rhs>::type,
+ Rhs>::value == NUMERIC_RANGE_CONTAINED>
+struct BigEnoughPromotion;
+
+// The side with the max exponent is big enough.
+template <typename Lhs, typename Rhs, bool is_intmax_type>
+struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
+ static const bool is_contained = true;
+};
+
+// We can use a twice wider type to fit.
+template <typename Lhs, typename Rhs>
+struct BigEnoughPromotion<Lhs, Rhs, false, false> {
+ using type =
+ typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
+ std::is_signed<Lhs>::value ||
+ std::is_signed<Rhs>::value>::type;
+ static const bool is_contained = true;
+};
+
+// No type is large enough.
+template <typename Lhs, typename Rhs>
+struct BigEnoughPromotion<Lhs, Rhs, true, false> {
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
+ static const bool is_contained = false;
+};
+
+// We can statically check if operations on the provided types can wrap, so we
+// can skip the checked operations if they're not needed. So, for an integer we
+// care if the destination type preserves the sign and is twice the width of
+// the source.
+template <typename T, typename Lhs, typename Rhs = Lhs>
+struct IsIntegerArithmeticSafe {
+ static const bool value =
+ !std::is_floating_point<T>::value &&
+ !std::is_floating_point<Lhs>::value &&
+ !std::is_floating_point<Rhs>::value &&
+ std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
+ IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
+ std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
+ IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
+};
+
+// Promotes to a type that can represent any possible result of a binary
+// arithmetic operation with the source types.
+template <typename Lhs,
+ typename Rhs,
+ bool is_promotion_possible = IsIntegerArithmeticSafe<
+ typename std::conditional<std::is_signed<Lhs>::value ||
+ std::is_signed<Rhs>::value,
+ intmax_t,
+ uintmax_t>::type,
+ typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
+struct FastIntegerArithmeticPromotion;
+
+template <typename Lhs, typename Rhs>
+struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
+ using type =
+ typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
+ std::is_signed<Lhs>::value ||
+ std::is_signed<Rhs>::value>::type;
+ static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
+ static const bool is_contained = true;
+};
+
+template <typename Lhs, typename Rhs>
+struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
+ using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
+ static const bool is_contained = false;
+};
+
+// Extracts the underlying type from an enum.
+template <typename T, bool is_enum = std::is_enum<T>::value>
+struct ArithmeticOrUnderlyingEnum;
+
+template <typename T>
+struct ArithmeticOrUnderlyingEnum<T, true> {
+ using type = typename std::underlying_type<T>::type;
+ static const bool value = std::is_arithmetic<type>::value;
+};
+
+template <typename T>
+struct ArithmeticOrUnderlyingEnum<T, false> {
+ using type = T;
+ static const bool value = std::is_arithmetic<type>::value;
+};
+
+// The following are helper templates used in the CheckedNumeric class.
+template <typename T>
+class CheckedNumeric;
+
+template <typename T>
+class ClampedNumeric;
+
+template <typename T>
+class StrictNumeric;
+
+// Used to treat CheckedNumeric and arithmetic underlying types the same.
+template <typename T>
+struct UnderlyingType {
+ using type = typename ArithmeticOrUnderlyingEnum<T>::type;
+ static const bool is_numeric = std::is_arithmetic<type>::value;
+ static const bool is_checked = false;
+ static const bool is_clamped = false;
+ static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<CheckedNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = true;
+ static const bool is_clamped = false;
+ static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<ClampedNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = false;
+ static const bool is_clamped = true;
+ static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<StrictNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = false;
+ static const bool is_clamped = false;
+ static const bool is_strict = true;
+};
+
+template <typename L, typename R>
+struct IsCheckedOp {
+ static const bool value =
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+ (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
+};
+
+template <typename L, typename R>
+struct IsClampedOp {
+ static const bool value =
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+ (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
+ !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
+};
+
+template <typename L, typename R>
+struct IsStrictOp {
+ static const bool value =
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+ (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
+ !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
+ !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
+};
+
+// as_signed<> returns the supplied integral value (or integral castable
+// Numeric template) cast as a signed integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_signed<
+ typename base::internal::UnderlyingType<Src>::type>::type
+as_signed(const Src value) {
+ static_assert(std::is_integral<decltype(as_signed(value))>::value,
+ "Argument must be a signed or unsigned integer type.");
+ return static_cast<decltype(as_signed(value))>(value);
+}
+
+// as_unsigned<> returns the supplied integral value (or integral castable
+// Numeric template) cast as an unsigned integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_unsigned<
+ typename base::internal::UnderlyingType<Src>::type>::type
+as_unsigned(const Src value) {
+ static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
+ "Argument must be a signed or unsigned integer type.");
+ return static_cast<decltype(as_unsigned(value))>(value);
+}
+
+template <typename L, typename R>
+constexpr bool IsLessImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsUnderflow() || r_range.IsOverflow() ||
+ (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsLess {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+constexpr bool IsLessOrEqualImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsUnderflow() || r_range.IsOverflow() ||
+ (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsLessOrEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+constexpr bool IsGreaterImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsOverflow() || r_range.IsUnderflow() ||
+ (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsGreater {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+constexpr bool IsGreaterOrEqualImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsOverflow() || r_range.IsUnderflow() ||
+ (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsGreaterOrEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+struct IsEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return DstRangeRelationToSrcRange<R>(lhs) ==
+ DstRangeRelationToSrcRange<L>(rhs) &&
+ static_cast<decltype(lhs + rhs)>(lhs) ==
+ static_cast<decltype(lhs + rhs)>(rhs);
+ }
+};
+
+template <typename L, typename R>
+struct IsNotEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return DstRangeRelationToSrcRange<R>(lhs) !=
+ DstRangeRelationToSrcRange<L>(rhs) ||
+ static_cast<decltype(lhs + rhs)>(lhs) !=
+ static_cast<decltype(lhs + rhs)>(rhs);
+ }
+};
+
+// These perform the actual math operations on the CheckedNumerics.
+// Binary arithmetic operations.
+template <template <typename, typename> class C, typename L, typename R>
+constexpr bool SafeCompare(const L lhs, const R rhs) {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ using Promotion = BigEnoughPromotion<L, R>;
+ using BigType = typename Promotion::type;
+ return Promotion::is_contained
+ // Force to a larger type for speed if both are contained.
+ ? C<BigType, BigType>::Test(
+ static_cast<BigType>(static_cast<L>(lhs)),
+ static_cast<BigType>(static_cast<R>(rhs)))
+ // Let the template functions figure it out for mixed types.
+ : C<L, R>::Test(lhs, rhs);
+}
+
+template <typename Dst, typename Src>
+constexpr bool IsMaxInRangeForNumericType() {
+ return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
+ std::numeric_limits<Src>::max());
+}
+
+template <typename Dst, typename Src>
+constexpr bool IsMinInRangeForNumericType() {
+ return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
+ std::numeric_limits<Src>::lowest());
+}
+
+template <typename Dst, typename Src>
+constexpr Dst CommonMax() {
+ return !IsMaxInRangeForNumericType<Dst, Src>()
+ ? Dst(std::numeric_limits<Dst>::max())
+ : Dst(std::numeric_limits<Src>::max());
+}
+
+template <typename Dst, typename Src>
+constexpr Dst CommonMin() {
+ return !IsMinInRangeForNumericType<Dst, Src>()
+ ? Dst(std::numeric_limits<Dst>::lowest())
+ : Dst(std::numeric_limits<Src>::lowest());
+}
+
+// This is a wrapper to generate return the max or min for a supplied type.
+// If the argument is false, the returned value is the maximum. If true the
+// returned value is the minimum.
+template <typename Dst, typename Src = Dst>
+constexpr Dst CommonMaxOrMin(bool is_min) {
+ return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
+}
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
diff --git a/gn/base/numerics/safe_math.h b/gn/base/numerics/safe_math.h
new file mode 100644
index 00000000000..e30be901f99
--- /dev/null
+++ b/gn/base/numerics/safe_math.h
@@ -0,0 +1,12 @@
+// 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_H_
+#define BASE_NUMERICS_SAFE_MATH_H_
+
+#include "base/numerics/checked_math.h"
+#include "base/numerics/clamped_math.h"
+#include "base/numerics/safe_conversions.h"
+
+#endif // BASE_NUMERICS_SAFE_MATH_H_
diff --git a/gn/base/numerics/safe_math_clang_gcc_impl.h b/gn/base/numerics/safe_math_clang_gcc_impl.h
new file mode 100644
index 00000000000..1760338b089
--- /dev/null
+++ b/gn/base/numerics/safe_math_clang_gcc_impl.h
@@ -0,0 +1,157 @@
+// 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_CLANG_GCC_IMPL_H_
+#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+
+#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
+#include "base/numerics/safe_math_arm_impl.h"
+#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
+#else
+#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
+#endif
+
+namespace base {
+namespace internal {
+
+// These are the non-functioning boilerplate implementations of the optimized
+// safe math routines.
+#if !BASE_HAS_ASSEMBLER_SAFE_MATH
+template <typename T, typename U>
+struct CheckedMulFastAsmOp {
+ 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 ClampedAddFastAsmOp {
+ 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 ClampedSubFastAsmOp {
+ 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 ClampedMulFastAsmOp {
+ 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>();
+ }
+};
+#endif // BASE_HAS_ASSEMBLER_SAFE_MATH
+#undef BASE_HAS_ASSEMBLER_SAFE_MATH
+
+template <typename T, typename U>
+struct CheckedAddFastOp {
+ static const bool is_supported = true;
+ template <typename V>
+ __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+ return !__builtin_add_overflow(x, y, result);
+ }
+};
+
+template <typename T, typename U>
+struct CheckedSubFastOp {
+ static const bool is_supported = true;
+ template <typename V>
+ __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+ return !__builtin_sub_overflow(x, y, result);
+ }
+};
+
+template <typename T, typename U>
+struct CheckedMulFastOp {
+#if defined(__clang__)
+ // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
+ // support full-width, mixed-sign multiply builtins.
+ // https://crbug.com/613003
+ // We can support intptr_t, uintptr_t, or a smaller common type.
+ static const bool is_supported =
+ (IsTypeInRangeForNumericType<intptr_t, T>::value &&
+ IsTypeInRangeForNumericType<intptr_t, U>::value) ||
+ (IsTypeInRangeForNumericType<uintptr_t, T>::value &&
+ IsTypeInRangeForNumericType<uintptr_t, U>::value);
+#else
+ static const bool is_supported = true;
+#endif
+ template <typename V>
+ __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+ return CheckedMulFastAsmOp<T, U>::is_supported
+ ? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
+ : !__builtin_mul_overflow(x, y, result);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastOp {
+ static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastOp {
+ static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastOp {
+ static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
+ }
+};
+
+template <typename T>
+struct ClampedNegFastOp {
+ static const bool is_supported = std::is_signed<T>::value;
+ __attribute__((always_inline)) static T Do(T value) {
+ // Use this when there is no assembler path available.
+ if (!ClampedSubFastAsmOp<T, T>::is_supported) {
+ T result;
+ return !__builtin_sub_overflow(T(0), value, &result)
+ ? result
+ : std::numeric_limits<T>::max();
+ }
+
+ // Fallback to the normal subtraction path.
+ return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
+ }
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
diff --git a/gn/base/numerics/safe_math_shared_impl.h b/gn/base/numerics/safe_math_shared_impl.h
new file mode 100644
index 00000000000..583c487a42e
--- /dev/null
+++ b/gn/base/numerics/safe_math_shared_impl.h
@@ -0,0 +1,237 @@
+// 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
new file mode 100644
index 00000000000..931305ccc6b
--- /dev/null
+++ b/gn/base/optional.h
@@ -0,0 +1,922 @@
+// 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/eintr_wrapper.h b/gn/base/posix/eintr_wrapper.h
new file mode 100644
index 00000000000..e18372e8684
--- /dev/null
+++ b/gn/base/posix/eintr_wrapper.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This provides a wrapper around system calls which may be interrupted by a
+// signal and return EINTR. See man 7 signal.
+// To prevent long-lasting loops (which would likely be a bug, such as a signal
+// that should be masked) to go unnoticed, there is a limit after which the
+// caller will nonetheless see an EINTR in Debug builds.
+//
+// On Windows and Fuchsia, this wrapper macro does nothing because there are no
+// signals.
+//
+// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return
+// value of close is significant. See http://crbug.com/269623.
+
+#ifndef BASE_POSIX_EINTR_WRAPPER_H_
+#define BASE_POSIX_EINTR_WRAPPER_H_
+
+#include "util/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
+
+#include <errno.h>
+
+#if defined(NDEBUG)
+
+#define HANDLE_EINTR(x) \
+ ({ \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR); \
+ eintr_wrapper_result; \
+ })
+
+#else
+
+#define HANDLE_EINTR(x) \
+ ({ \
+ int eintr_wrapper_counter = 0; \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR && \
+ eintr_wrapper_counter++ < 100); \
+ eintr_wrapper_result; \
+ })
+
+#endif // NDEBUG
+
+#define IGNORE_EINTR(x) \
+ ({ \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ if (eintr_wrapper_result == -1 && errno == EINTR) { \
+ eintr_wrapper_result = 0; \
+ } \
+ } while (0); \
+ eintr_wrapper_result; \
+ })
+
+#else // !OS_POSIX || OS_FUCHSIA
+
+#define HANDLE_EINTR(x) (x)
+#define IGNORE_EINTR(x) (x)
+
+#endif // !OS_POSIX || OS_FUCHSIA
+
+#endif // BASE_POSIX_EINTR_WRAPPER_H_
diff --git a/gn/base/posix/file_descriptor_shuffle.cc b/gn/base/posix/file_descriptor_shuffle.cc
new file mode 100644
index 00000000000..f628bec411c
--- /dev/null
+++ b/gn/base/posix/file_descriptor_shuffle.cc
@@ -0,0 +1,101 @@
+// 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/posix/file_descriptor_shuffle.h"
+
+#include <stddef.h>
+#include <unistd.h>
+#include <ostream>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace base {
+
+bool PerformInjectiveMultimapDestructive(InjectiveMultimap* m,
+ InjectionDelegate* delegate) {
+ static const size_t kMaxExtraFDs = 16;
+ int extra_fds[kMaxExtraFDs];
+ unsigned next_extra_fd = 0;
+
+ // DANGER: this function must not allocate or lock.
+ // Cannot use STL iterators here, since debug iterators use locks.
+
+ for (size_t i_index = 0; i_index < m->size(); ++i_index) {
+ InjectiveMultimap::value_type* i = &(*m)[i_index];
+ int temp_fd = -1;
+
+ // We DCHECK the injectiveness of the mapping.
+ for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
+ InjectiveMultimap::value_type* j = &(*m)[j_index];
+ DCHECK(i->dest != j->dest) << "Both fd " << i->source << " and "
+ << j->source << " map to " << i->dest;
+ }
+
+ const bool is_identity = i->source == i->dest;
+
+ for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
+ InjectiveMultimap::value_type* j = &(*m)[j_index];
+ if (!is_identity && i->dest == j->source) {
+ if (temp_fd == -1) {
+ if (!delegate->Duplicate(&temp_fd, i->dest))
+ return false;
+ if (next_extra_fd < kMaxExtraFDs) {
+ extra_fds[next_extra_fd++] = temp_fd;
+ } else {
+ RAW_LOG(ERROR,
+ "PerformInjectiveMultimapDestructive overflowed "
+ "extra_fds. Leaking file descriptors!");
+ }
+ }
+
+ j->source = temp_fd;
+ j->close = false;
+ }
+
+ if (i->close && i->source == j->dest)
+ i->close = false;
+
+ if (i->close && i->source == j->source) {
+ i->close = false;
+ j->close = true;
+ }
+ }
+
+ if (!is_identity) {
+ if (!delegate->Move(i->source, i->dest))
+ return false;
+ }
+
+ if (!is_identity && i->close)
+ delegate->Close(i->source);
+ }
+
+ for (unsigned i = 0; i < next_extra_fd; i++)
+ delegate->Close(extra_fds[i]);
+
+ return true;
+}
+
+bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
+ InjectionDelegate* delegate) {
+ InjectiveMultimap m(m_in);
+ return PerformInjectiveMultimapDestructive(&m, delegate);
+}
+
+bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
+ *result = HANDLE_EINTR(dup(fd));
+ return *result >= 0;
+}
+
+bool FileDescriptorTableInjection::Move(int src, int dest) {
+ return HANDLE_EINTR(dup2(src, dest)) != -1;
+}
+
+void FileDescriptorTableInjection::Close(int fd) {
+ int ret = IGNORE_EINTR(close(fd));
+ DPCHECK(ret == 0);
+}
+
+} // namespace base
diff --git a/gn/base/posix/file_descriptor_shuffle.h b/gn/base/posix/file_descriptor_shuffle.h
new file mode 100644
index 00000000000..68434988991
--- /dev/null
+++ b/gn/base/posix/file_descriptor_shuffle.h
@@ -0,0 +1,82 @@
+// 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_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
+#define BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
+
+// This code exists to shuffle file descriptors, which is commonly needed when
+// forking subprocesses. The naive approach (just call dup2 to set up the
+// desired descriptors) is very simple, but wrong: it won't handle edge cases
+// (like mapping 0 -> 1, 1 -> 0) correctly.
+//
+// In order to unittest this code, it's broken into the abstract action (an
+// injective multimap) and the concrete code for dealing with file descriptors.
+// Users should use the code like this:
+// base::InjectiveMultimap file_descriptor_map;
+// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true));
+// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true));
+// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true));
+// base::ShuffleFileDescriptors(file_descriptor_map);
+//
+// and trust the the Right Thing will get done.
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+
+namespace base {
+
+// A Delegate which performs the actions required to perform an injective
+// multimapping in place.
+class InjectionDelegate {
+ public:
+ // Duplicate |fd|, an element of the domain, and write a fresh element of the
+ // domain into |result|. Returns true iff successful.
+ virtual bool Duplicate(int* result, int fd) = 0;
+ // Destructively move |src| to |dest|, overwriting |dest|. Returns true iff
+ // successful.
+ virtual bool Move(int src, int dest) = 0;
+ // Delete an element of the domain.
+ virtual void Close(int fd) = 0;
+
+ protected:
+ virtual ~InjectionDelegate() = default;
+};
+
+// An implementation of the InjectionDelegate interface using the file
+// descriptor table of the current process as the domain.
+class FileDescriptorTableInjection : public InjectionDelegate {
+ bool Duplicate(int* result, int fd) override;
+ bool Move(int src, int dest) override;
+ void Close(int fd) override;
+};
+
+// A single arc of the directed graph which describes an injective multimapping.
+struct InjectionArc {
+ InjectionArc(int in_source, int in_dest, bool in_close)
+ : source(in_source), dest(in_dest), close(in_close) {}
+
+ int source;
+ int dest;
+ bool close; // if true, delete the source element after performing the
+ // mapping.
+};
+
+typedef std::vector<InjectionArc> InjectiveMultimap;
+
+bool PerformInjectiveMultimap(const InjectiveMultimap& map,
+ InjectionDelegate* delegate);
+
+bool PerformInjectiveMultimapDestructive(InjectiveMultimap* map,
+ InjectionDelegate* delegate);
+
+// This function will not call malloc but will mutate |map|
+static inline bool ShuffleFileDescriptors(InjectiveMultimap* map) {
+ FileDescriptorTableInjection delegate;
+ return PerformInjectiveMultimapDestructive(map, &delegate);
+}
+
+} // namespace base
+
+#endif // BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
diff --git a/gn/base/posix/safe_strerror.cc b/gn/base/posix/safe_strerror.cc
new file mode 100644
index 00000000000..3e3bb15810b
--- /dev/null
+++ b/gn/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__) || 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/posix/safe_strerror.h b/gn/base/posix/safe_strerror.h
new file mode 100644
index 00000000000..48e9db97ec0
--- /dev/null
+++ b/gn/base/posix/safe_strerror.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_POSIX_SAFE_STRERROR_H_
+#define BASE_POSIX_SAFE_STRERROR_H_
+
+#include <stddef.h>
+
+#include <string>
+
+namespace base {
+
+// BEFORE using anything from this file, first look at PLOG and friends in
+// logging.h and use them instead if applicable.
+//
+// This file declares safe, portable alternatives to the POSIX strerror()
+// function. strerror() is inherently unsafe in multi-threaded apps and should
+// never be used. Doing so can cause crashes. Additionally, the thread-safe
+// alternative strerror_r varies in semantics across platforms. Use these
+// functions instead.
+
+// Thread-safe strerror function with dependable semantics that never fails.
+// It will write the string form of error "err" to buffer buf of length len.
+// If there is an error calling the OS's strerror_r() function then a message to
+// that effect will be printed into buf, truncating if necessary. The final
+// result is always null-terminated. The value of errno is never changed.
+//
+// Use this instead of strerror_r().
+void safe_strerror_r(int err, char* buf, size_t len);
+
+// Calls safe_strerror_r with a buffer of suitable size and returns the result
+// in a C++ string.
+//
+// Use this instead of strerror(). Note though that safe_strerror_r will be
+// more robust in the case of heap corruption errors, since it doesn't need to
+// allocate a string.
+std::string safe_strerror(int err);
+
+} // namespace base
+
+#endif // BASE_POSIX_SAFE_STRERROR_H_
diff --git a/gn/base/scoped_clear_errno.h b/gn/base/scoped_clear_errno.h
new file mode 100644
index 00000000000..44a0c6274ba
--- /dev/null
+++ b/gn/base/scoped_clear_errno.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. 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_SCOPED_CLEAR_ERRNO_H_
+#define BASE_SCOPED_CLEAR_ERRNO_H_
+
+#include <errno.h>
+
+#include "base/macros.h"
+
+namespace base {
+
+// Simple scoper that saves the current value of errno, resets it to 0, and on
+// destruction puts the old value back.
+class ScopedClearErrno {
+ public:
+ ScopedClearErrno() : old_errno_(errno) { errno = 0; }
+ ~ScopedClearErrno() {
+ if (errno == 0)
+ errno = old_errno_;
+ }
+
+ private:
+ const int old_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedClearErrno);
+};
+
+} // namespace base
+
+#endif // BASE_SCOPED_CLEAR_ERRNO_H_
diff --git a/gn/base/scoped_generic.h b/gn/base/scoped_generic.h
new file mode 100644
index 00000000000..6a56f645fdc
--- /dev/null
+++ b/gn/base/scoped_generic.h
@@ -0,0 +1,185 @@
+// Copyright 2014 The Chromium Authors. 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_SCOPED_GENERIC_H_
+#define BASE_SCOPED_GENERIC_H_
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+
+namespace base {
+
+// This class acts like unique_ptr with a custom deleter (although is slightly
+// less fancy in some of the more escoteric respects) except that it keeps a
+// copy of the object rather than a pointer, and we require that the contained
+// object has some kind of "invalid" value.
+//
+// Defining a scoper based on this class allows you to get a scoper for
+// non-pointer types without having to write custom code for set, reset, and
+// move, etc. and get almost identical semantics that people are used to from
+// unique_ptr.
+//
+// It is intended that you will typedef this class with an appropriate deleter
+// to implement clean up tasks for objects that act like pointers from a
+// resource management standpoint but aren't, such as file descriptors and
+// various types of operating system handles. Using unique_ptr for these
+// things requires that you keep a pointer to the handle valid for the lifetime
+// of the scoper (which is easy to mess up).
+//
+// For an object to be able to be put into a ScopedGeneric, it must support
+// standard copyable semantics and have a specific "invalid" value. The traits
+// must define a free function and also the invalid value to assign for
+// default-constructed and released objects.
+//
+// struct FooScopedTraits {
+// // It's assumed that this is a fast inline function with little-to-no
+// // penalty for duplicate calls. This must be a static function even
+// // for stateful traits.
+// static int InvalidValue() {
+// return 0;
+// }
+//
+// // This free function will not be called if f == InvalidValue()!
+// static void Free(int f) {
+// ::FreeFoo(f);
+// }
+// };
+//
+// typedef ScopedGeneric<int, FooScopedTraits> ScopedFoo;
+template <typename T, typename Traits>
+class ScopedGeneric {
+ private:
+ // This must be first since it's used inline below.
+ //
+ // Use the empty base class optimization to allow us to have a D
+ // member, while avoiding any space overhead for it when D is an
+ // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good
+ // discussion of this technique.
+ struct Data : public Traits {
+ explicit Data(const T& in) : generic(in) {}
+ Data(const T& in, const Traits& other) : Traits(other), generic(in) {}
+ T generic;
+ };
+
+ public:
+ typedef T element_type;
+ typedef Traits traits_type;
+
+ ScopedGeneric() : data_(traits_type::InvalidValue()) {}
+
+ // Constructor. Takes responsibility for freeing the resource associated with
+ // the object T.
+ explicit ScopedGeneric(const element_type& value) : data_(value) {}
+
+ // Constructor. Allows initialization of a stateful traits object.
+ ScopedGeneric(const element_type& value, const traits_type& traits)
+ : data_(value, traits) {}
+
+ // Move constructor. Allows initialization from a ScopedGeneric rvalue.
+ ScopedGeneric(ScopedGeneric<T, Traits>&& rvalue)
+ : data_(rvalue.release(), rvalue.get_traits()) {}
+
+ ~ScopedGeneric() { FreeIfNecessary(); }
+
+ // operator=. Allows assignment from a ScopedGeneric rvalue.
+ ScopedGeneric& operator=(ScopedGeneric<T, Traits>&& rvalue) {
+ reset(rvalue.release());
+ return *this;
+ }
+
+ // Frees the currently owned object, if any. Then takes ownership of a new
+ // object, if given. Self-resets are not allowd as on unique_ptr. See
+ // http://crbug.com/162971
+ void reset(const element_type& value = traits_type::InvalidValue()) {
+ if (data_.generic != traits_type::InvalidValue() && data_.generic == value)
+ abort();
+ FreeIfNecessary();
+ data_.generic = value;
+ }
+
+ void swap(ScopedGeneric& other) {
+ // Standard swap idiom: 'using std::swap' ensures that std::swap is
+ // present in the overload set, but we call swap unqualified so that
+ // any more-specific overloads can be used, if available.
+ using std::swap;
+ swap(static_cast<Traits&>(data_), static_cast<Traits&>(other.data_));
+ swap(data_.generic, other.data_.generic);
+ }
+
+ // Release the object. The return value is the current object held by this
+ // object. After this operation, this object will hold a null value, and
+ // will not own the object any more.
+ element_type release() WARN_UNUSED_RESULT {
+ element_type old_generic = data_.generic;
+ data_.generic = traits_type::InvalidValue();
+ return old_generic;
+ }
+
+ // Returns a raw pointer to the object storage, to allow the scoper to be used
+ // to receive and manage out-parameter values. Implies reset().
+ element_type* receive() WARN_UNUSED_RESULT {
+ reset();
+ return &data_.generic;
+ }
+
+ const element_type& get() const { return data_.generic; }
+
+ // Returns true if this object doesn't hold the special null value for the
+ // associated data type.
+ bool is_valid() const { return data_.generic != traits_type::InvalidValue(); }
+
+ bool operator==(const element_type& value) const {
+ return data_.generic == value;
+ }
+ bool operator!=(const element_type& value) const {
+ return data_.generic != value;
+ }
+
+ Traits& get_traits() { return data_; }
+ const Traits& get_traits() const { return data_; }
+
+ private:
+ void FreeIfNecessary() {
+ if (data_.generic != traits_type::InvalidValue()) {
+ data_.Free(data_.generic);
+ data_.generic = traits_type::InvalidValue();
+ }
+ }
+
+ // Forbid comparison. If U != T, it totally doesn't make sense, and if U ==
+ // T, it still doesn't make sense because you should never have the same
+ // object owned by two different ScopedGenerics.
+ template <typename T2, typename Traits2>
+ bool operator==(const ScopedGeneric<T2, Traits2>& p2) const;
+ template <typename T2, typename Traits2>
+ bool operator!=(const ScopedGeneric<T2, Traits2>& p2) const;
+
+ Data data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedGeneric);
+};
+
+template <class T, class Traits>
+void swap(const ScopedGeneric<T, Traits>& a,
+ const ScopedGeneric<T, Traits>& b) {
+ a.swap(b);
+}
+
+template <class T, class Traits>
+bool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) {
+ return value == scoped.get();
+}
+
+template <class T, class Traits>
+bool operator!=(const T& value, const ScopedGeneric<T, Traits>& scoped) {
+ return value != scoped.get();
+}
+
+} // namespace base
+
+#endif // BASE_SCOPED_GENERIC_H_
diff --git a/gn/base/sha1.cc b/gn/base/sha1.cc
new file mode 100644
index 00000000000..9c22be1983a
--- /dev/null
+++ b/gn/base/sha1.cc
@@ -0,0 +1,213 @@
+// 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/sha1.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "base/sys_byteorder.h"
+
+namespace base {
+
+// Implementation of SHA-1. Only handles data in byte-sized blocks,
+// which simplifies the code a fair bit.
+
+// Identifier names follow notation in FIPS PUB 180-3, where you'll
+// also find a description of the algorithm:
+// http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf
+
+// Usage example:
+//
+// SecureHashAlgorithm sha;
+// while(there is data to hash)
+// sha.Update(moredata, size of data);
+// sha.Final();
+// memcpy(somewhere, sha.Digest(), 20);
+//
+// to reuse the instance of sha, call sha.Init();
+
+// TODO(jhawkins): Replace this implementation with a per-platform
+// implementation using each platform's crypto library. See
+// http://crbug.com/47218
+
+class SecureHashAlgorithm {
+ public:
+ SecureHashAlgorithm() { Init(); }
+
+ static const int kDigestSizeBytes;
+
+ void Init();
+ void Update(const void* data, size_t nbytes);
+ void Final();
+
+ // 20 bytes of message digest.
+ const unsigned char* Digest() const {
+ return reinterpret_cast<const unsigned char*>(H);
+ }
+
+ private:
+ void Pad();
+ void Process();
+
+ uint32_t A, B, C, D, E;
+
+ uint32_t H[5];
+
+ union {
+ uint32_t W[80];
+ uint8_t M[64];
+ };
+
+ uint32_t cursor;
+ uint64_t l;
+};
+
+static inline uint32_t f(uint32_t t, uint32_t B, uint32_t C, uint32_t D) {
+ if (t < 20) {
+ return (B & C) | ((~B) & D);
+ } else if (t < 40) {
+ return B ^ C ^ D;
+ } else if (t < 60) {
+ return (B & C) | (B & D) | (C & D);
+ } else {
+ return B ^ C ^ D;
+ }
+}
+
+static inline uint32_t S(uint32_t n, uint32_t X) {
+ return (X << n) | (X >> (32 - n));
+}
+
+static inline uint32_t K(uint32_t t) {
+ if (t < 20) {
+ return 0x5a827999;
+ } else if (t < 40) {
+ return 0x6ed9eba1;
+ } else if (t < 60) {
+ return 0x8f1bbcdc;
+ } else {
+ return 0xca62c1d6;
+ }
+}
+
+const int SecureHashAlgorithm::kDigestSizeBytes = 20;
+
+void SecureHashAlgorithm::Init() {
+ A = 0;
+ B = 0;
+ C = 0;
+ D = 0;
+ E = 0;
+ cursor = 0;
+ l = 0;
+ H[0] = 0x67452301;
+ H[1] = 0xefcdab89;
+ H[2] = 0x98badcfe;
+ H[3] = 0x10325476;
+ H[4] = 0xc3d2e1f0;
+}
+
+void SecureHashAlgorithm::Final() {
+ Pad();
+ Process();
+
+ for (int t = 0; t < 5; ++t)
+ H[t] = ByteSwap(H[t]);
+}
+
+void SecureHashAlgorithm::Update(const void* data, size_t nbytes) {
+ const uint8_t* d = reinterpret_cast<const uint8_t*>(data);
+ while (nbytes--) {
+ M[cursor++] = *d++;
+ if (cursor >= 64)
+ Process();
+ l += 8;
+ }
+}
+
+void SecureHashAlgorithm::Pad() {
+ M[cursor++] = 0x80;
+
+ if (cursor > 64 - 8) {
+ // pad out to next block
+ while (cursor < 64)
+ M[cursor++] = 0;
+
+ Process();
+ }
+
+ while (cursor < 64 - 8)
+ M[cursor++] = 0;
+
+ M[cursor++] = (l >> 56) & 0xff;
+ M[cursor++] = (l >> 48) & 0xff;
+ M[cursor++] = (l >> 40) & 0xff;
+ M[cursor++] = (l >> 32) & 0xff;
+ M[cursor++] = (l >> 24) & 0xff;
+ M[cursor++] = (l >> 16) & 0xff;
+ M[cursor++] = (l >> 8) & 0xff;
+ M[cursor++] = l & 0xff;
+}
+
+void SecureHashAlgorithm::Process() {
+ uint32_t t;
+
+ // Each a...e corresponds to a section in the FIPS 180-3 algorithm.
+
+ // a.
+ //
+ // W and M are in a union, so no need to memcpy.
+ // memcpy(W, M, sizeof(M));
+ for (t = 0; t < 16; ++t)
+ W[t] = ByteSwap(W[t]);
+
+ // b.
+ for (t = 16; t < 80; ++t)
+ W[t] = S(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
+
+ // c.
+ A = H[0];
+ B = H[1];
+ C = H[2];
+ D = H[3];
+ E = H[4];
+
+ // d.
+ for (t = 0; t < 80; ++t) {
+ uint32_t TEMP = S(5, A) + f(t, B, C, D) + E + W[t] + K(t);
+ E = D;
+ D = C;
+ C = S(30, B);
+ B = A;
+ A = TEMP;
+ }
+
+ // e.
+ H[0] += A;
+ H[1] += B;
+ H[2] += C;
+ H[3] += D;
+ H[4] += E;
+
+ cursor = 0;
+}
+
+std::string SHA1HashString(const std::string& str) {
+ char hash[SecureHashAlgorithm::kDigestSizeBytes];
+ SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()),
+ str.length(), reinterpret_cast<unsigned char*>(hash));
+ return std::string(hash, SecureHashAlgorithm::kDigestSizeBytes);
+}
+
+void SHA1HashBytes(const unsigned char* data, size_t len, unsigned char* hash) {
+ SecureHashAlgorithm sha;
+ sha.Update(data, len);
+ sha.Final();
+
+ memcpy(hash, sha.Digest(), SecureHashAlgorithm::kDigestSizeBytes);
+}
+
+} // namespace base
diff --git a/gn/base/sha1.h b/gn/base/sha1.h
new file mode 100644
index 00000000000..482ebe60fac
--- /dev/null
+++ b/gn/base/sha1.h
@@ -0,0 +1,28 @@
+// 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_SHA1_H_
+#define BASE_SHA1_H_
+
+#include <stddef.h>
+
+#include <string>
+
+namespace base {
+
+// These functions perform SHA-1 operations.
+
+static const size_t kSHA1Length = 20; // Length in bytes of a SHA-1 hash.
+
+// Computes the SHA-1 hash of the input string |str| and returns the full
+// hash.
+std::string SHA1HashString(const std::string& str);
+
+// Computes the SHA-1 hash of the |len| bytes in |data| and puts the hash
+// in |hash|. |hash| must be kSHA1Length bytes long.
+void SHA1HashBytes(const unsigned char* data, size_t len, unsigned char* hash);
+
+} // namespace base
+
+#endif // BASE_SHA1_H_
diff --git a/gn/base/stl_util.h b/gn/base/stl_util.h
new file mode 100644
index 00000000000..d9e3484163b
--- /dev/null
+++ b/gn/base/stl_util.h
@@ -0,0 +1,406 @@
+// 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
new file mode 100644
index 00000000000..b193e216ccc
--- /dev/null
+++ b/gn/base/strings/char_traits.h
@@ -0,0 +1,92 @@
+// 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
new file mode 100644
index 00000000000..997ab20efac
--- /dev/null
+++ b/gn/base/strings/string16.cc
@@ -0,0 +1,87 @@
+// 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
new file mode 100644
index 00000000000..59bf6d70c5b
--- /dev/null
+++ b/gn/base/strings/string16.h
@@ -0,0 +1,203 @@
+// 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
new file mode 100644
index 00000000000..01375f5de22
--- /dev/null
+++ b/gn/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> {
+ 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
new file mode 100644
index 00000000000..ecf950e72eb
--- /dev/null
+++ b/gn/base/strings/string_number_conversions.h
@@ -0,0 +1,152 @@
+// 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
new file mode 100644
index 00000000000..612298a060e
--- /dev/null
+++ b/gn/base/strings/string_piece.cc
@@ -0,0 +1,440 @@
+// 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
new file mode 100644
index 00000000000..8e4deaed652
--- /dev/null
+++ b/gn/base/strings/string_piece.h
@@ -0,0 +1,439 @@
+// 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
new file mode 100644
index 00000000000..b50b9806c9b
--- /dev/null
+++ b/gn/base/strings/string_piece_forward.h
@@ -0,0 +1,24 @@
+// 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
new file mode 100644
index 00000000000..e9b5c96c9b9
--- /dev/null
+++ b/gn/base/strings/string_split.cc
@@ -0,0 +1,268 @@
+// 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
new file mode 100644
index 00000000000..6f8fc7ca1a8
--- /dev/null
+++ b/gn/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 <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
new file mode 100644
index 00000000000..451a0110523
--- /dev/null
+++ b/gn/base/strings/string_tokenizer.h
@@ -0,0 +1,251 @@
+// 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
new file mode 100644
index 00000000000..9b03c265e48
--- /dev/null
+++ b/gn/base/strings/string_util.cc
@@ -0,0 +1,1101 @@
+// 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
new file mode 100644
index 00000000000..2d5d01cb727
--- /dev/null
+++ b/gn/base/strings/string_util.h
@@ -0,0 +1,455 @@
+// 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
new file mode 100644
index 00000000000..37ff95f6472
--- /dev/null
+++ b/gn/base/strings/string_util_constants.cc
@@ -0,0 +1,59 @@
+// 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
new file mode 100644
index 00000000000..6868a658a08
--- /dev/null
+++ b/gn/base/strings/string_util_posix.h
@@ -0,0 +1,41 @@
+// 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
new file mode 100644
index 00000000000..8f9fa81631a
--- /dev/null
+++ b/gn/base/strings/string_util_win.h
@@ -0,0 +1,48 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef 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/stringize_macros.h b/gn/base/strings/stringize_macros.h
new file mode 100644
index 00000000000..251c443e95a
--- /dev/null
+++ b/gn/base/strings/stringize_macros.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file defines preprocessor macros for stringizing preprocessor
+// symbols (or their output) and manipulating preprocessor symbols
+// that define strings.
+
+#ifndef BASE_STRINGS_STRINGIZE_MACROS_H_
+#define BASE_STRINGS_STRINGIZE_MACROS_H_
+
+#include "util/build_config.h"
+
+// This is not very useful as it does not expand defined symbols if
+// called directly. Use its counterpart without the _NO_EXPANSION
+// suffix, below.
+#define STRINGIZE_NO_EXPANSION(x) #x
+
+// Use this to quote the provided parameter, first expanding it if it
+// is a preprocessor symbol.
+//
+// For example, if:
+// #define A FOO
+// #define B(x) myobj->FunctionCall(x)
+//
+// Then:
+// STRINGIZE(A) produces "FOO"
+// STRINGIZE(B(y)) produces "myobj->FunctionCall(y)"
+#define STRINGIZE(x) STRINGIZE_NO_EXPANSION(x)
+
+#endif // BASE_STRINGS_STRINGIZE_MACROS_H_
diff --git a/gn/base/strings/stringprintf.cc b/gn/base/strings/stringprintf.cc
new file mode 100644
index 00000000000..c7d141b4366
--- /dev/null
+++ b/gn/base/strings/stringprintf.cc
@@ -0,0 +1,190 @@
+// 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
new file mode 100644
index 00000000000..8c12acadb81
--- /dev/null
+++ b/gn/base/strings/stringprintf.h
@@ -0,0 +1,60 @@
+// 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/sys_string_conversions.h b/gn/base/strings/sys_string_conversions.h
new file mode 100644
index 00000000000..9150c05814a
--- /dev/null
+++ b/gn/base/strings/sys_string_conversions.h
@@ -0,0 +1,82 @@
+// 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_SYS_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_SYS_STRING_CONVERSIONS_H_
+
+// Provides system-dependent string type conversions for cases where it's
+// necessary to not use ICU. Generally, you should not need this in Chrome,
+// but it is used in some shared code. Dependencies should be minimal.
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "util/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#ifdef __OBJC__
+@class NSString;
+#else
+class NSString;
+#endif
+#endif // OS_MACOSX
+
+namespace base {
+
+// Converts between wide and UTF-8 representations of a string. On error, the
+// result is system-dependent.
+std::string SysWideToUTF8(const std::wstring& wide);
+std::wstring SysUTF8ToWide(StringPiece utf8);
+
+// Converts between wide and the system multi-byte representations of a string.
+// DANGER: This will lose information and can change (on Windows, this can
+// change between reboots).
+std::string SysWideToNativeMB(const std::wstring& wide);
+std::wstring SysNativeMBToWide(StringPiece native_mb);
+
+// Windows-specific ------------------------------------------------------------
+
+#if defined(OS_WIN)
+
+// Converts between 8-bit and wide strings, using the given code page. The
+// code page identifier is one accepted by the Windows function
+// MultiByteToWideChar().
+std::wstring SysMultiByteToWide(StringPiece mb, uint32_t code_page);
+std::string SysWideToMultiByte(const std::wstring& wide, uint32_t code_page);
+
+#endif // defined(OS_WIN)
+
+// Mac-specific ----------------------------------------------------------------
+
+#if defined(OS_MACOSX)
+
+// Converts between STL strings and CFStringRefs/NSStrings.
+
+// Creates a string, and returns it with a refcount of 1. You are responsible
+// for releasing it. Returns NULL on failure.
+CFStringRef SysUTF8ToCFStringRef(const std::string& utf8);
+CFStringRef SysUTF16ToCFStringRef(const string16& utf16);
+
+// Same, but returns an autoreleased NSString.
+NSString* SysUTF8ToNSString(const std::string& utf8);
+NSString* SysUTF16ToNSString(const string16& utf16);
+
+// Converts a CFStringRef to an STL string. Returns an empty string on failure.
+std::string SysCFStringRefToUTF8(CFStringRef ref);
+string16 SysCFStringRefToUTF16(CFStringRef ref);
+
+// Same, but accepts NSString input. Converts nil NSString* to the appropriate
+// string type of length 0.
+std::string SysNSStringToUTF8(NSString* ref);
+string16 SysNSStringToUTF16(NSString* ref);
+
+#endif // defined(OS_MACOSX)
+
+} // namespace base
+
+#endif // BASE_STRINGS_SYS_STRING_CONVERSIONS_H_
diff --git a/gn/base/strings/sys_string_conversions_mac.mm b/gn/base/strings/sys_string_conversions_mac.mm
new file mode 100644
index 00000000000..3b78777e5a8
--- /dev/null
+++ b/gn/base/strings/sys_string_conversions_mac.mm
@@ -0,0 +1,176 @@
+// 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/sys_string_conversions.h"
+
+#import <Foundation/Foundation.h>
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+namespace {
+
+// Convert the supplied CFString into the specified encoding, and return it as
+// an STL string of the template type. Returns an empty string on failure.
+//
+// Do not assert in this function since it is used by the asssertion code!
+template <typename StringType>
+static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring,
+ CFStringEncoding encoding) {
+ CFIndex length = CFStringGetLength(cfstring);
+ if (length == 0)
+ return StringType();
+
+ CFRange whole_string = CFRangeMake(0, length);
+ CFIndex out_size;
+ CFIndex converted = CFStringGetBytes(cfstring, whole_string, encoding,
+ 0, // lossByte
+ false, // isExternalRepresentation
+ NULL, // buffer
+ 0, // maxBufLen
+ &out_size);
+ if (converted == 0 || out_size == 0)
+ return StringType();
+
+ // out_size is the number of UInt8-sized units needed in the destination.
+ // A buffer allocated as UInt8 units might not be properly aligned to
+ // contain elements of StringType::value_type. Use a container for the
+ // proper value_type, and convert out_size by figuring the number of
+ // value_type elements per UInt8. Leave room for a NUL terminator.
+ typename StringType::size_type elements =
+ out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1;
+
+ std::vector<typename StringType::value_type> out_buffer(elements);
+ converted =
+ CFStringGetBytes(cfstring, whole_string, encoding,
+ 0, // lossByte
+ false, // isExternalRepresentation
+ reinterpret_cast<UInt8*>(&out_buffer[0]), out_size,
+ NULL); // usedBufLen
+ if (converted == 0)
+ return StringType();
+
+ out_buffer[elements - 1] = '\0';
+ return StringType(&out_buffer[0], elements - 1);
+}
+
+// Given an STL string |in| with an encoding specified by |in_encoding|,
+// convert it to |out_encoding| and return it as an STL string of the
+// |OutStringType| template type. Returns an empty string on failure.
+//
+// Do not assert in this function since it is used by the asssertion code!
+template <typename InStringType, typename OutStringType>
+static OutStringType STLStringToSTLStringWithEncodingsT(
+ const InStringType& in,
+ CFStringEncoding in_encoding,
+ CFStringEncoding out_encoding) {
+ typename InStringType::size_type in_length = in.length();
+ if (in_length == 0)
+ return OutStringType();
+
+ base::ScopedCFTypeRef<CFStringRef> cfstring(CFStringCreateWithBytesNoCopy(
+ NULL, reinterpret_cast<const UInt8*>(in.data()),
+ in_length * sizeof(typename InStringType::value_type), in_encoding, false,
+ kCFAllocatorNull));
+ if (!cfstring)
+ return OutStringType();
+
+ return CFStringToSTLStringWithEncodingT<OutStringType>(cfstring,
+ out_encoding);
+}
+
+// Given an STL string |in| with an encoding specified by |in_encoding|,
+// return it as a CFStringRef. Returns NULL on failure.
+template <typename StringType>
+static CFStringRef STLStringToCFStringWithEncodingsT(
+ const StringType& in,
+ CFStringEncoding in_encoding) {
+ typename StringType::size_type in_length = in.length();
+ if (in_length == 0)
+ return CFSTR("");
+
+ return CFStringCreateWithBytes(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),
+ in_length * sizeof(typename StringType::value_type), in_encoding, false);
+}
+
+// Specify the byte ordering explicitly, otherwise CFString will be confused
+// when strings don't carry BOMs, as they typically won't.
+static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
+#ifdef __BIG_ENDIAN__
+static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16BE;
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE;
+#elif defined(__LITTLE_ENDIAN__)
+static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16LE;
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE;
+#endif // __LITTLE_ENDIAN__
+
+} // namespace
+
+// Do not assert in this function since it is used by the asssertion code!
+std::string SysWideToUTF8(const std::wstring& wide) {
+ return STLStringToSTLStringWithEncodingsT<std::wstring, std::string>(
+ wide, kWideStringEncoding, kNarrowStringEncoding);
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::wstring SysUTF8ToWide(StringPiece utf8) {
+ return STLStringToSTLStringWithEncodingsT<StringPiece, std::wstring>(
+ utf8, kNarrowStringEncoding, kWideStringEncoding);
+}
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ return SysWideToUTF8(wide);
+}
+
+std::wstring SysNativeMBToWide(StringPiece native_mb) {
+ return SysUTF8ToWide(native_mb);
+}
+
+CFStringRef SysUTF8ToCFStringRef(const std::string& utf8) {
+ return STLStringToCFStringWithEncodingsT(utf8, kNarrowStringEncoding);
+}
+
+CFStringRef SysUTF16ToCFStringRef(const string16& utf16) {
+ return STLStringToCFStringWithEncodingsT(utf16, kMediumStringEncoding);
+}
+
+NSString* SysUTF8ToNSString(const std::string& utf8) {
+ return (NSString*)base::mac::CFTypeRefToNSObjectAutorelease(
+ SysUTF8ToCFStringRef(utf8));
+}
+
+NSString* SysUTF16ToNSString(const string16& utf16) {
+ return (NSString*)base::mac::CFTypeRefToNSObjectAutorelease(
+ SysUTF16ToCFStringRef(utf16));
+}
+
+std::string SysCFStringRefToUTF8(CFStringRef ref) {
+ return CFStringToSTLStringWithEncodingT<std::string>(ref,
+ kNarrowStringEncoding);
+}
+
+string16 SysCFStringRefToUTF16(CFStringRef ref) {
+ return CFStringToSTLStringWithEncodingT<string16>(ref, kMediumStringEncoding);
+}
+
+std::string SysNSStringToUTF8(NSString* nsstring) {
+ if (!nsstring)
+ return std::string();
+ return SysCFStringRefToUTF8(reinterpret_cast<CFStringRef>(nsstring));
+}
+
+string16 SysNSStringToUTF16(NSString* nsstring) {
+ if (!nsstring)
+ return string16();
+ return SysCFStringRefToUTF16(reinterpret_cast<CFStringRef>(nsstring));
+}
+
+} // namespace base
diff --git a/gn/base/strings/sys_string_conversions_posix.cc b/gn/base/strings/sys_string_conversions_posix.cc
new file mode 100644
index 00000000000..0e1442829a2
--- /dev/null
+++ b/gn/base/strings/sys_string_conversions_posix.cc
@@ -0,0 +1,163 @@
+// 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/sys_string_conversions.h"
+
+#include <stddef.h>
+#include <wchar.h>
+
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/build_config.h"
+
+namespace base {
+
+std::string SysWideToUTF8(const std::wstring& wide) {
+ // In theory this should be using the system-provided conversion rather
+ // than our ICU, but this will do for now.
+ return WideToUTF8(wide);
+}
+std::wstring SysUTF8ToWide(StringPiece utf8) {
+ // In theory this should be using the system-provided conversion rather
+ // than our ICU, but this will do for now.
+ std::wstring out;
+ UTF8ToWide(utf8.data(), utf8.size(), &out);
+ return out;
+}
+
+#if defined(SYSTEM_NATIVE_UTF8) || defined(OS_ANDROID)
+// TODO(port): Consider reverting the OS_ANDROID when we have wcrtomb()
+// support and a better understanding of what calls these routines.
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ return WideToUTF8(wide);
+}
+
+std::wstring SysNativeMBToWide(StringPiece native_mb) {
+ return SysUTF8ToWide(native_mb);
+}
+
+#else
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ mbstate_t ps;
+
+ // Calculate the number of multi-byte characters. We walk through the string
+ // without writing the output, counting the number of multi-byte characters.
+ size_t num_out_chars = 0;
+ memset(&ps, 0, sizeof(ps));
+ for (size_t i = 0; i < wide.size(); ++i) {
+ const wchar_t src = wide[i];
+ // Use a temp buffer since calling wcrtomb with an output of NULL does not
+ // calculate the output length.
+ char buf[16];
+ // Skip NULLs to avoid wcrtomb's special handling of them.
+ size_t res = src ? wcrtomb(buf, src, &ps) : 0;
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-1):
+ return std::string();
+ break;
+ case 0:
+ // We hit an embedded null byte, keep going.
+ ++num_out_chars;
+ break;
+ default:
+ num_out_chars += res;
+ break;
+ }
+ }
+
+ if (num_out_chars == 0)
+ return std::string();
+
+ std::string out;
+ out.resize(num_out_chars);
+
+ // We walk the input string again, with |i| tracking the index of the
+ // wide input, and |j| tracking the multi-byte output.
+ memset(&ps, 0, sizeof(ps));
+ for (size_t i = 0, j = 0; i < wide.size(); ++i) {
+ const wchar_t src = wide[i];
+ // We don't want wcrtomb to do its funkiness for embedded NULLs.
+ size_t res = src ? wcrtomb(&out[j], src, &ps) : 0;
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-1):
+ return std::string();
+ break;
+ case 0:
+ // We hit an embedded null byte, keep going.
+ ++j; // Output is already zeroed.
+ break;
+ default:
+ j += res;
+ break;
+ }
+ }
+
+ return out;
+}
+
+std::wstring SysNativeMBToWide(StringPiece native_mb) {
+ mbstate_t ps;
+
+ // Calculate the number of wide characters. We walk through the string
+ // without writing the output, counting the number of wide characters.
+ size_t num_out_chars = 0;
+ memset(&ps, 0, sizeof(ps));
+ for (size_t i = 0; i < native_mb.size();) {
+ const char* src = native_mb.data() + i;
+ size_t res = mbrtowc(nullptr, src, native_mb.size() - i, &ps);
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-2):
+ case static_cast<size_t>(-1):
+ return std::wstring();
+ break;
+ case 0:
+ // We hit an embedded null byte, keep going.
+ i += 1;
+ FALLTHROUGH;
+ default:
+ i += res;
+ ++num_out_chars;
+ break;
+ }
+ }
+
+ if (num_out_chars == 0)
+ return std::wstring();
+
+ std::wstring out;
+ out.resize(num_out_chars);
+
+ memset(&ps, 0, sizeof(ps)); // Clear the shift state.
+ // We walk the input string again, with |i| tracking the index of the
+ // multi-byte input, and |j| tracking the wide output.
+ for (size_t i = 0, j = 0; i < native_mb.size(); ++j) {
+ const char* src = native_mb.data() + i;
+ wchar_t* dst = &out[j];
+ size_t res = mbrtowc(dst, src, native_mb.size() - i, &ps);
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-2):
+ case static_cast<size_t>(-1):
+ return std::wstring();
+ break;
+ case 0:
+ i += 1; // Skip null byte.
+ break;
+ default:
+ i += res;
+ break;
+ }
+ }
+
+ return out;
+}
+
+#endif // defined(SYSTEM_NATIVE_UTF8) || defined(OS_ANDROID)
+
+} // namespace base
diff --git a/gn/base/strings/sys_string_conversions_win.cc b/gn/base/strings/sys_string_conversions_win.cc
new file mode 100644
index 00000000000..232dd782200
--- /dev/null
+++ b/gn/base/strings/sys_string_conversions_win.cc
@@ -0,0 +1,71 @@
+// 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/strings/sys_string_conversions.h"
+
+#include <stdint.h>
+#include <windows.h>
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// Do not assert in this function since it is used by the asssertion code!
+std::string SysWideToUTF8(const std::wstring& wide) {
+ return SysWideToMultiByte(wide, CP_UTF8);
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::wstring SysUTF8ToWide(StringPiece utf8) {
+ return SysMultiByteToWide(utf8, CP_UTF8);
+}
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ return SysWideToMultiByte(wide, CP_ACP);
+}
+
+std::wstring SysNativeMBToWide(StringPiece native_mb) {
+ return SysMultiByteToWide(native_mb, CP_ACP);
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::wstring SysMultiByteToWide(StringPiece mb, uint32_t code_page) {
+ if (mb.empty())
+ return std::wstring();
+
+ int mb_length = static_cast<int>(mb.length());
+ // Compute the length of the buffer.
+ int charcount =
+ MultiByteToWideChar(code_page, 0, mb.data(), mb_length, NULL, 0);
+ if (charcount == 0)
+ return std::wstring();
+
+ std::wstring wide;
+ wide.resize(charcount);
+ MultiByteToWideChar(code_page, 0, mb.data(), mb_length, &wide[0], charcount);
+
+ return wide;
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::string SysWideToMultiByte(const std::wstring& wide, uint32_t code_page) {
+ int wide_length = static_cast<int>(wide.length());
+ if (wide_length == 0)
+ return std::string();
+
+ // Compute the length of the buffer we'll need.
+ int charcount = WideCharToMultiByte(code_page, 0, wide.data(), wide_length,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0)
+ return std::string();
+
+ std::string mb;
+ mb.resize(charcount);
+ WideCharToMultiByte(code_page, 0, wide.data(), wide_length, &mb[0], charcount,
+ NULL, NULL);
+
+ return mb;
+}
+
+} // namespace base
diff --git a/gn/base/strings/utf_offset_string_conversions.cc b/gn/base/strings/utf_offset_string_conversions.cc
new file mode 100644
index 00000000000..177839a8e71
--- /dev/null
+++ b/gn/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 "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
new file mode 100644
index 00000000000..b7c4a1b8086
--- /dev/null
+++ b/gn/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 <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
new file mode 100644
index 00000000000..c962411b81d
--- /dev/null
+++ b/gn/base/strings/utf_string_conversion_utils.cc
@@ -0,0 +1,153 @@
+// 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
new file mode 100644
index 00000000000..6f89652ee87
--- /dev/null
+++ b/gn/base/strings/utf_string_conversion_utils.h
@@ -0,0 +1,99 @@
+// 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
new file mode 100644
index 00000000000..b04d54d215e
--- /dev/null
+++ b/gn/base/strings/utf_string_conversions.cc
@@ -0,0 +1,333 @@
+// 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
new file mode 100644
index 00000000000..704943eddaf
--- /dev/null
+++ b/gn/base/strings/utf_string_conversions.h
@@ -0,0 +1,48 @@
+// 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/sys_byteorder.h b/gn/base/sys_byteorder.h
new file mode 100644
index 00000000000..bb7b4c3f95c
--- /dev/null
+++ b/gn/base/sys_byteorder.h
@@ -0,0 +1,139 @@
+// 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 header defines cross-platform ByteSwap() implementations for 16, 32 and
+// 64-bit values, and NetToHostXX() / HostToNextXX() functions equivalent to
+// the traditional ntohX() and htonX() functions.
+// Use the functions defined here rather than using the platform-specific
+// functions directly.
+
+#ifndef BASE_SYS_BYTEORDER_H_
+#define BASE_SYS_BYTEORDER_H_
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "util/build_config.h"
+
+#if defined(COMPILER_MSVC)
+#include <stdlib.h>
+#endif
+
+namespace base {
+
+// Returns a value with all bytes in |x| swapped, i.e. reverses the endianness.
+inline uint16_t ByteSwap(uint16_t x) {
+#if defined(COMPILER_MSVC)
+ return _byteswap_ushort(x);
+#else
+ return __builtin_bswap16(x);
+#endif
+}
+
+inline uint32_t ByteSwap(uint32_t x) {
+#if defined(COMPILER_MSVC)
+ return _byteswap_ulong(x);
+#else
+ return __builtin_bswap32(x);
+#endif
+}
+
+inline uint64_t ByteSwap(uint64_t x) {
+#if defined(COMPILER_MSVC)
+ return _byteswap_uint64(x);
+#else
+ return __builtin_bswap64(x);
+#endif
+}
+
+inline uintptr_t ByteSwapUintPtrT(uintptr_t x) {
+ // We do it this way because some build configurations are ILP32 even when
+ // defined(ARCH_CPU_64_BITS). Unfortunately, we can't use sizeof in #ifs. But,
+ // because these conditionals are constexprs, the irrelevant branches will
+ // likely be optimized away, so this construction should not result in code
+ // bloat.
+ if (sizeof(uintptr_t) == 4) {
+ return ByteSwap(static_cast<uint32_t>(x));
+ } else if (sizeof(uintptr_t) == 8) {
+ return ByteSwap(static_cast<uint64_t>(x));
+ } else {
+ NOTREACHED();
+ }
+}
+
+// Converts the bytes in |x| from host order (endianness) to little endian, and
+// returns the result.
+inline uint16_t ByteSwapToLE16(uint16_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return x;
+#else
+ return ByteSwap(x);
+#endif
+}
+inline uint32_t ByteSwapToLE32(uint32_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return x;
+#else
+ return ByteSwap(x);
+#endif
+}
+inline uint64_t ByteSwapToLE64(uint64_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return x;
+#else
+ return ByteSwap(x);
+#endif
+}
+
+// Converts the bytes in |x| from network to host order (endianness), and
+// returns the result.
+inline uint16_t NetToHost16(uint16_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint32_t NetToHost32(uint32_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint64_t NetToHost64(uint64_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+
+// Converts the bytes in |x| from host to network order (endianness), and
+// returns the result.
+inline uint16_t HostToNet16(uint16_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint32_t HostToNet32(uint32_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint64_t HostToNet64(uint64_t x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+
+} // namespace base
+
+#endif // BASE_SYS_BYTEORDER_H_
diff --git a/gn/base/template_util.h b/gn/base/template_util.h
new file mode 100644
index 00000000000..dda9587ff7c
--- /dev/null
+++ b/gn/base/template_util.h
@@ -0,0 +1,156 @@
+// 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/third_party/icu/LICENSE b/gn/base/third_party/icu/LICENSE
new file mode 100644
index 00000000000..2882e4ebda9
--- /dev/null
+++ b/gn/base/third_party/icu/LICENSE
@@ -0,0 +1,76 @@
+COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+
+Copyright © 1991-2017 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in http://www.unicode.org/copyright.html
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+
+---------------------
+
+Third-Party Software Licenses
+
+This section contains third-party software notices and/or additional
+terms for licensed third-party software components included within ICU
+libraries.
+
+1. ICU License - ICU 1.8.1 to ICU 57.1
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2016 International Business Machines Corporation and others
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the
+property of their respective owners.
diff --git a/gn/base/third_party/icu/README.chromium b/gn/base/third_party/icu/README.chromium
new file mode 100644
index 00000000000..297e89a2edd
--- /dev/null
+++ b/gn/base/third_party/icu/README.chromium
@@ -0,0 +1,17 @@
+Name: ICU
+URL: http://site.icu-project.org/
+Version: 60
+License: Unicode
+License File: NOT_SHIPPED
+
+This file has the relevant components from ICU copied to handle basic UTF8/16/32
+conversions. Components are copied from umachine.h, utf.h, utf8.h, and utf16.h
+into icu_utf.h, and from utf_impl.cpp into icu_utf.cc.
+
+The main change is that U_/U8_/U16_ prefixes have been replaced with
+CBU_/CBU8_/CBU16_ (for "Chrome Base") to avoid confusion with the "real" ICU
+macros should ICU be in use on the system. For the same reason, the functions
+and types have been put in the "base_icu" namespace.
+
+Note that this license file is marked as NOT_SHIPPED, since a more complete
+ICU license is included from //third_party/icu/README.chromium
diff --git a/gn/base/third_party/icu/icu_utf.cc b/gn/base/third_party/icu/icu_utf.cc
new file mode 100644
index 00000000000..8dcb401fd9a
--- /dev/null
+++ b/gn/base/third_party/icu/icu_utf.cc
@@ -0,0 +1,129 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+******************************************************************************
+*
+* Copyright (C) 1999-2012, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+******************************************************************************
+* file name: utf_impl.cpp
+* encoding: UTF-8
+* tab size: 8 (not used)
+* indentation:4
+*
+* created on: 1999sep13
+* created by: Markus W. Scherer
+*
+* This file provides implementation functions for macros in the utfXX.h
+* that would otherwise be too long as macros.
+*/
+
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base_icu {
+
+// source/common/utf_impl.cpp
+
+static const UChar32 utf8_errorValue[6] = {
+ // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE,
+ // but without relying on the obsolete unicode/utf_old.h.
+ 0x15, 0x9f, 0xffff, 0x10ffff};
+
+static UChar32 errorValue(int32_t count, int8_t strict) {
+ if (strict >= 0) {
+ return utf8_errorValue[count];
+ } else if (strict == -3) {
+ return 0xfffd;
+ } else {
+ return CBU_SENTINEL;
+ }
+}
+
+/*
+ * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros
+ * and their obsolete sibling UTF8_NEXT_CHAR_SAFE().
+ *
+ * U8_NEXT() supports NUL-terminated strings indicated via length<0.
+ *
+ * The "strict" parameter controls the error behavior:
+ * <0 "Safe" behavior of U8_NEXT():
+ * -1: All illegal byte sequences yield U_SENTINEL=-1.
+ * -2: Same as -1, except for lenient treatment of surrogate code points as
+ * legal. Some implementations use this for roundtripping of Unicode 16-bit
+ * strings that are not well-formed UTF-16, that is, they contain
+ * unpaired surrogates. -3: All illegal byte sequences yield U+FFFD. 0
+ * Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE): All illegal
+ * byte sequences yield a positive code point such that this result code
+ * point would be encoded with the same number of bytes as the illegal
+ * sequence. >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(...,
+ * TRUE): Same as the obsolete "safe" behavior, but non-characters are also
+ * treated like illegal sequences.
+ *
+ * Note that a UBool is the same as an int8_t.
+ */
+UChar32 utf8_nextCharSafeBody(const uint8_t* s,
+ int32_t* pi,
+ int32_t length,
+ UChar32 c,
+ UBool strict) {
+ // *pi is one after byte c.
+ int32_t i = *pi;
+ // length can be negative for NUL-terminated strings: Read and validate one
+ // byte at a time.
+ if (i == length || c > 0xf4) {
+ // end of string, or not a lead byte
+ } else if (c >= 0xf0) {
+ // Test for 4-byte sequences first because
+ // U8_NEXT() handles shorter valid sequences inline.
+ uint8_t t1 = s[i], t2, t3;
+ c &= 7;
+ if (CBU8_IS_VALID_LEAD4_AND_T1(c, t1) && ++i != length &&
+ (t2 = s[i] - 0x80) <= 0x3f && ++i != length &&
+ (t3 = s[i] - 0x80) <= 0x3f) {
+ ++i;
+ c = (c << 18) | ((t1 & 0x3f) << 12) | (t2 << 6) | t3;
+ // strict: forbid non-characters like U+fffe
+ if (strict <= 0 || !CBU_IS_UNICODE_NONCHAR(c)) {
+ *pi = i;
+ return c;
+ }
+ }
+ } else if (c >= 0xe0) {
+ c &= 0xf;
+ if (strict != -2) {
+ uint8_t t1 = s[i], t2;
+ if (CBU8_IS_VALID_LEAD3_AND_T1(c, t1) && ++i != length &&
+ (t2 = s[i] - 0x80) <= 0x3f) {
+ ++i;
+ c = (c << 12) | ((t1 & 0x3f) << 6) | t2;
+ // strict: forbid non-characters like U+fffe
+ if (strict <= 0 || !CBU_IS_UNICODE_NONCHAR(c)) {
+ *pi = i;
+ return c;
+ }
+ }
+ } else {
+ // strict=-2 -> lenient: allow surrogates
+ uint8_t t1 = s[i] - 0x80, t2;
+ if (t1 <= 0x3f && (c > 0 || t1 >= 0x20) && ++i != length &&
+ (t2 = s[i] - 0x80) <= 0x3f) {
+ *pi = i + 1;
+ return (c << 12) | (t1 << 6) | t2;
+ }
+ }
+ } else if (c >= 0xc2) {
+ uint8_t t1 = s[i] - 0x80;
+ if (t1 <= 0x3f) {
+ *pi = i + 1;
+ return ((c - 0xc0) << 6) | t1;
+ }
+ } // else 0x80<=c<0xc2 is not a lead byte
+
+ /* error handling */
+ c = errorValue(i - *pi, strict);
+ *pi = i;
+ return c;
+}
+
+} // namespace base_icu
diff --git a/gn/base/third_party/icu/icu_utf.h b/gn/base/third_party/icu/icu_utf.h
new file mode 100644
index 00000000000..b626b398404
--- /dev/null
+++ b/gn/base/third_party/icu/icu_utf.h
@@ -0,0 +1,464 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+******************************************************************************
+*
+* Copyright (C) 1999-2015, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+******************************************************************************
+*/
+
+#ifndef BASE_THIRD_PARTY_ICU_ICU_UTF_H_
+#define BASE_THIRD_PARTY_ICU_ICU_UTF_H_
+
+#include <stdint.h>
+
+namespace base_icu {
+
+// source/common/unicode/umachine.h
+
+/** The ICU boolean type @stable ICU 2.0 */
+typedef int8_t UBool;
+
+/**
+ * Define UChar32 as a type for single Unicode code points.
+ * UChar32 is a signed 32-bit integer (same as int32_t).
+ *
+ * The Unicode code point range is 0..0x10ffff.
+ * All other values (negative or >=0x110000) are illegal as Unicode code points.
+ * They may be used as sentinel values to indicate "done", "error"
+ * or similar non-code point conditions.
+ *
+ * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined
+ * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned)
+ * or else to be uint32_t.
+ * That is, the definition of UChar32 was platform-dependent.
+ *
+ * @see U_SENTINEL
+ * @stable ICU 2.4
+ */
+typedef int32_t UChar32;
+
+/**
+ * This value is intended for sentinel values for APIs that
+ * (take or) return single code points (UChar32).
+ * It is outside of the Unicode code point range 0..0x10ffff.
+ *
+ * For example, a "done" or "error" value in a new API
+ * could be indicated with U_SENTINEL.
+ *
+ * ICU APIs designed before ICU 2.4 usually define service-specific "done"
+ * values, mostly 0xffff.
+ * Those may need to be distinguished from
+ * actual U+ffff text contents by calling functions like
+ * CharacterIterator::hasNext() or UnicodeString::length().
+ *
+ * @return -1
+ * @see UChar32
+ * @stable ICU 2.4
+ */
+#define CBU_SENTINEL (-1)
+
+// source/common/unicode/utf.h
+
+/**
+ * Is this code point a Unicode noncharacter?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_UNICODE_NONCHAR(c) \
+ ((c) >= 0xfdd0 && ((c) <= 0xfdef || ((c)&0xfffe) == 0xfffe) && \
+ (c) <= 0x10ffff)
+
+/**
+ * Is c a Unicode code point value (0..U+10ffff)
+ * that can be assigned a character?
+ *
+ * Code points that are not characters include:
+ * - single surrogate code points (U+d800..U+dfff, 2048 code points)
+ * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code
+ * points) - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points) - the highest
+ * Unicode code point value is U+10ffff
+ *
+ * This means that all code points below U+d800 are character code points,
+ * and that boundary is tested first for performance.
+ *
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_UNICODE_CHAR(c) \
+ ((uint32_t)(c) < 0xd800 || \
+ (0xdfff < (c) && (c) <= 0x10ffff && !CBU_IS_UNICODE_NONCHAR(c)))
+
+/**
+ * Is this code point a surrogate (U+d800..U+dfff)?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_SURROGATE(c) (((c)&0xfffff800) == 0xd800)
+
+/**
+ * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_SURROGATE_LEAD(c) (((c)&0x400) == 0)
+
+// source/common/unicode/utf8.h
+
+/**
+ * Internal bit vector for 3-byte UTF-8 validity check, for use in
+ * U8_IS_VALID_LEAD3_AND_T1. Each bit indicates whether one lead byte + first
+ * trail byte pair starts a valid sequence. Lead byte E0..EF bits 3..0 are used
+ * as byte index, first trail byte bits 7..5 are used as bit index into that
+ * byte.
+ * @see U8_IS_VALID_LEAD3_AND_T1
+ * @internal
+ */
+#define CBU8_LEAD3_T1_BITS \
+ "\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x10\x30\x30"
+
+/**
+ * Internal 3-byte UTF-8 validity check.
+ * Non-zero if lead byte E0..EF and first trail byte 00..FF start a valid
+ * sequence.
+ * @internal
+ */
+#define CBU8_IS_VALID_LEAD3_AND_T1(lead, t1) \
+ (CBU8_LEAD3_T1_BITS[(lead)&0xf] & (1 << ((uint8_t)(t1) >> 5)))
+
+/**
+ * Internal bit vector for 4-byte UTF-8 validity check, for use in
+ * U8_IS_VALID_LEAD4_AND_T1. Each bit indicates whether one lead byte + first
+ * trail byte pair starts a valid sequence. First trail byte bits 7..4 are used
+ * as byte index, lead byte F0..F4 bits 2..0 are used as bit index into that
+ * byte.
+ * @see U8_IS_VALID_LEAD4_AND_T1
+ * @internal
+ */
+#define CBU8_LEAD4_T1_BITS \
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x0F\x0F\x0F\x00\x00\x00\x00"
+
+/**
+ * Internal 4-byte UTF-8 validity check.
+ * Non-zero if lead byte F0..F4 and first trail byte 00..FF start a valid
+ * sequence.
+ * @internal
+ */
+#define CBU8_IS_VALID_LEAD4_AND_T1(lead, t1) \
+ (CBU8_LEAD4_T1_BITS[(uint8_t)(t1) >> 4] & (1 << ((lead)&7)))
+
+/**
+ * Function for handling "next code point" with error-checking.
+ *
+ * This is internal since it is not meant to be called directly by external clie
+nts;
+ * however it is U_STABLE (not U_INTERNAL) since it is called by public macros i
+n this
+ * file and thus must remain stable, and should not be hidden when other interna
+l
+ * functions are hidden (otherwise public macros would fail to compile).
+ * @internal
+ */
+UChar32 utf8_nextCharSafeBody(const uint8_t* s,
+ int32_t* pi,
+ int32_t length,
+ ::base_icu::UChar32 c,
+ ::base_icu::UBool strict);
+
+/**
+ * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_SINGLE(c) (((c)&0x80) == 0)
+
+/**
+ * Is this code unit (byte) a UTF-8 lead byte? (0xC2..0xF4)
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_LEAD(c) ((uint8_t)((c)-0xc2) <= 0x32)
+
+/**
+ * Is this code unit (byte) a UTF-8 trail byte? (0x80..0xBF)
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_TRAIL(c) ((int8_t)(c) < -0x40)
+
+/**
+ * How many code units (bytes) are used for the UTF-8 encoding
+ * of this Unicode code point?
+ * @param c 32-bit code point
+ * @return 1..4, or 0 if c is a surrogate or not a Unicode code point
+ * @stable ICU 2.4
+ */
+#define CBU8_LENGTH(c) \
+ ((uint32_t)(c) <= 0x7f \
+ ? 1 \
+ : ((uint32_t)(c) <= 0x7ff \
+ ? 2 \
+ : ((uint32_t)(c) <= 0xd7ff \
+ ? 3 \
+ : ((uint32_t)(c) <= 0xdfff || (uint32_t)(c) > 0x10ffff \
+ ? 0 \
+ : ((uint32_t)(c) <= 0xffff ? 3 : 4)))))
+
+/**
+ * The maximum number of UTF-8 code units (bytes) per Unicode code point
+ * (U+0000..U+10ffff).
+ * @return 4
+ * @stable ICU 2.4
+ */
+#define CBU8_MAX_LENGTH 4
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * The offset may point to the lead byte of a multi-byte sequence,
+ * in which case the macro will read the whole sequence.
+ * If the offset points to a trail byte or an illegal UTF-8 sequence, then
+ * c is set to a negative value.
+ *
+ * @param s const uint8_t * string
+ * @param i int32_t string offset, must be i<length
+ * @param length int32_t string length
+ * @param c output UChar32 variable, set to <0 in case of an error
+ * @see U8_NEXT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU8_NEXT(s, i, length, c) \
+ { \
+ (c) = (uint8_t)(s)[(i)++]; \
+ if (!CBU8_IS_SINGLE(c)) { \
+ uint8_t __t1, __t2; \
+ if (/* handle U+0800..U+FFFF inline */ \
+ (0xe0 <= (c) && (c) < 0xf0) && \
+ (((i) + 1) < (length) || (length) < 0) && \
+ CBU8_IS_VALID_LEAD3_AND_T1((c), __t1 = (s)[i]) && \
+ (__t2 = (s)[(i) + 1] - 0x80) <= 0x3f) { \
+ (c) = (((c)&0xf) << 12) | ((__t1 & 0x3f) << 6) | __t2; \
+ (i) += 2; \
+ } else if (/* handle U+0080..U+07FF inline */ \
+ ((c) < 0xe0 && (c) >= 0xc2) && ((i) != (length)) && \
+ (__t1 = (s)[i] - 0x80) <= 0x3f) { \
+ (c) = (((c)&0x1f) << 6) | __t1; \
+ ++(i); \
+ } else { \
+ /* function call for "complicated" and error cases */ \
+ (c) = ::base_icu::utf8_nextCharSafeBody((const uint8_t*)s, &(i), \
+ (length), c, -1); \
+ } \
+ } \
+ }
+
+/**
+ * Append a code point to a string, overwriting 1 to 4 bytes.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the
+ * string. Otherwise, the result is undefined.
+ *
+ * @param s const uint8_t * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see U8_APPEND
+ * @stable ICU 2.4
+ */
+#define CBU8_APPEND_UNSAFE(s, i, c) \
+ { \
+ if ((uint32_t)(c) <= 0x7f) { \
+ (s)[(i)++] = (uint8_t)(c); \
+ } else { \
+ if ((uint32_t)(c) <= 0x7ff) { \
+ (s)[(i)++] = (uint8_t)(((c) >> 6) | 0xc0); \
+ } else { \
+ if ((uint32_t)(c) <= 0xffff) { \
+ (s)[(i)++] = (uint8_t)(((c) >> 12) | 0xe0); \
+ } else { \
+ (s)[(i)++] = (uint8_t)(((c) >> 18) | 0xf0); \
+ (s)[(i)++] = (uint8_t)((((c) >> 12) & 0x3f) | 0x80); \
+ } \
+ (s)[(i)++] = (uint8_t)((((c) >> 6) & 0x3f) | 0x80); \
+ } \
+ (s)[(i)++] = (uint8_t)(((c)&0x3f) | 0x80); \
+ } \
+ }
+
+// source/common/unicode/utf16.h
+
+/**
+ * Does this code unit alone encode a code point (BMP, not a surrogate)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SINGLE(c) !CBU_IS_SURROGATE(c)
+
+/**
+ * Is this code unit a lead surrogate (U+d800..U+dbff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_LEAD(c) (((c)&0xfffffc00) == 0xd800)
+
+/**
+ * Is this code unit a trail surrogate (U+dc00..U+dfff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00) == 0xdc00)
+
+/**
+ * Is this code unit a surrogate (U+d800..U+dfff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SURROGATE(c) CBU_IS_SURROGATE(c)
+
+/**
+ * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SURROGATE_LEAD(c) (((c)&0x400) == 0)
+
+/**
+ * Helper constant for U16_GET_SUPPLEMENTARY.
+ * @internal
+ */
+#define CBU16_SURROGATE_OFFSET ((0xd800 << 10UL) + 0xdc00 - 0x10000)
+
+/**
+ * Get a supplementary code point value (U+10000..U+10ffff)
+ * from its lead and trail surrogates.
+ * The result is undefined if the input values are not
+ * lead and trail surrogates.
+ *
+ * @param lead lead surrogate (U+d800..U+dbff)
+ * @param trail trail surrogate (U+dc00..U+dfff)
+ * @return supplementary code point (U+10000..U+10ffff)
+ * @stable ICU 2.4
+ */
+#define CBU16_GET_SUPPLEMENTARY(lead, trail) \
+ (((::base_icu::UChar32)(lead) << 10UL) + \
+ (::base_icu::UChar32)(trail)-CBU16_SURROGATE_OFFSET)
+
+/**
+ * Get the lead surrogate (0xd800..0xdbff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return lead surrogate (U+d800..U+dbff) for supplementary
+ * @stable ICU 2.4
+ */
+#define CBU16_LEAD(supplementary) \
+ (::base_icu::UChar)(((supplementary) >> 10) + 0xd7c0)
+
+/**
+ * Get the trail surrogate (0xdc00..0xdfff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return trail surrogate (U+dc00..U+dfff) for supplementary
+ * @stable ICU 2.4
+ */
+#define CBU16_TRAIL(supplementary) \
+ (::base_icu::UChar)(((supplementary)&0x3ff) | 0xdc00)
+
+/**
+ * How many 16-bit code units are used to encode this Unicode code point? (1 or
+ * 2) The result is not defined if c is not a Unicode code point
+ * (U+0000..U+10ffff).
+ * @param c 32-bit code point
+ * @return 1 or 2
+ * @stable ICU 2.4
+ */
+#define CBU16_LENGTH(c) ((uint32_t)(c) <= 0xffff ? 1 : 2)
+
+/**
+ * The maximum number of 16-bit code units per Unicode code point
+ * (U+0000..U+10ffff).
+ * @return 2
+ * @stable ICU 2.4
+ */
+#define CBU16_MAX_LENGTH 2
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * The offset may point to the lead surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the following trail surrogate as well.
+ * If the offset points to a trail surrogate or
+ * to a single, unpaired lead surrogate, then c is set to that unpaired
+ * surrogate.
+ *
+ * @param s const UChar * string
+ * @param i string offset, must be i<length
+ * @param length string length
+ * @param c output UChar32 variable
+ * @see U16_NEXT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_NEXT(s, i, length, c) \
+ { \
+ (c) = (s)[(i)++]; \
+ if (CBU16_IS_LEAD(c)) { \
+ uint16_t __c2; \
+ if ((i) != (length) && CBU16_IS_TRAIL(__c2 = (s)[(i)])) { \
+ ++(i); \
+ (c) = CBU16_GET_SUPPLEMENTARY((c), __c2); \
+ } \
+ } \
+ }
+
+/**
+ * Append a code point to a string, overwriting 1 or 2 code units.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the
+ * string. Otherwise, the result is undefined.
+ *
+ * @param s const UChar * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see U16_APPEND
+ * @stable ICU 2.4
+ */
+#define CBU16_APPEND_UNSAFE(s, i, c) \
+ { \
+ if ((uint32_t)(c) <= 0xffff) { \
+ (s)[(i)++] = (uint16_t)(c); \
+ } else { \
+ (s)[(i)++] = (uint16_t)(((c) >> 10) + 0xd7c0); \
+ (s)[(i)++] = (uint16_t)(((c)&0x3ff) | 0xdc00); \
+ } \
+ }
+
+} // namespace base_icu
+
+#endif // BASE_THIRD_PARTY_ICU_ICU_UTF_H_
diff --git a/gn/base/timer/elapsed_timer.cc b/gn/base/timer/elapsed_timer.cc
new file mode 100644
index 00000000000..68992f4e0a2
--- /dev/null
+++ b/gn/base/timer/elapsed_timer.cc
@@ -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.
+
+#include "base/timer/elapsed_timer.h"
+
+namespace base {
+
+ElapsedTimer::ElapsedTimer() {
+ begin_ = TicksNow();
+}
+
+ElapsedTimer::ElapsedTimer(ElapsedTimer&& other) {
+ begin_ = other.begin_;
+}
+
+void ElapsedTimer::operator=(ElapsedTimer&& other) {
+ begin_ = other.begin_;
+}
+
+TickDelta ElapsedTimer::Elapsed() const {
+ return TicksDelta(TicksNow(), begin_);
+}
+
+} // namespace base
diff --git a/gn/base/timer/elapsed_timer.h b/gn/base/timer/elapsed_timer.h
new file mode 100644
index 00000000000..fbbc83d970a
--- /dev/null
+++ b/gn/base/timer/elapsed_timer.h
@@ -0,0 +1,32 @@
+// 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_TIMER_ELAPSED_TIMER_H_
+#define BASE_TIMER_ELAPSED_TIMER_H_
+
+#include "base/macros.h"
+#include "util/ticks.h"
+
+namespace base {
+
+// A simple wrapper around TicksNow().
+class ElapsedTimer {
+ public:
+ ElapsedTimer();
+ ElapsedTimer(ElapsedTimer&& other);
+
+ void operator=(ElapsedTimer&& other);
+
+ // Returns the time elapsed since object construction.
+ TickDelta Elapsed() const;
+
+ private:
+ Ticks begin_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElapsedTimer);
+};
+
+} // namespace base
+
+#endif // BASE_TIMER_ELAPSED_TIMER_H_
diff --git a/gn/base/value_iterators.cc b/gn/base/value_iterators.cc
new file mode 100644
index 00000000000..ba9c73072f2
--- /dev/null
+++ b/gn/base/value_iterators.cc
@@ -0,0 +1,228 @@
+// 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 "base/value_iterators.h"
+
+namespace base {
+
+namespace detail {
+
+// ----------------------------------------------------------------------------
+// dict_iterator.
+
+dict_iterator::pointer::pointer(const reference& ref) : ref_(ref) {}
+
+dict_iterator::pointer::pointer(const pointer& ptr) = default;
+
+dict_iterator::dict_iterator(DictStorage::iterator dict_iter)
+ : dict_iter_(dict_iter) {}
+
+dict_iterator::dict_iterator(const dict_iterator& dict_iter) = default;
+
+dict_iterator& dict_iterator::operator=(const dict_iterator& dict_iter) =
+ default;
+
+dict_iterator::~dict_iterator() = default;
+
+dict_iterator::reference dict_iterator::operator*() {
+ return {dict_iter_->first, *dict_iter_->second};
+}
+
+dict_iterator::pointer dict_iterator::operator->() {
+ return pointer(operator*());
+}
+
+dict_iterator& dict_iterator::operator++() {
+ ++dict_iter_;
+ return *this;
+}
+
+dict_iterator dict_iterator::operator++(int) {
+ dict_iterator tmp(*this);
+ ++dict_iter_;
+ return tmp;
+}
+
+dict_iterator& dict_iterator::operator--() {
+ --dict_iter_;
+ return *this;
+}
+
+dict_iterator dict_iterator::operator--(int) {
+ dict_iterator tmp(*this);
+ --dict_iter_;
+ return tmp;
+}
+
+bool operator==(const dict_iterator& lhs, const dict_iterator& rhs) {
+ return lhs.dict_iter_ == rhs.dict_iter_;
+}
+
+bool operator!=(const dict_iterator& lhs, const dict_iterator& rhs) {
+ return !(lhs == rhs);
+}
+
+// ----------------------------------------------------------------------------
+// const_dict_iterator.
+
+const_dict_iterator::pointer::pointer(const reference& ref) : ref_(ref) {}
+
+const_dict_iterator::pointer::pointer(const pointer& ptr) = default;
+
+const_dict_iterator::const_dict_iterator(DictStorage::const_iterator dict_iter)
+ : dict_iter_(dict_iter) {}
+
+const_dict_iterator::const_dict_iterator(const const_dict_iterator& dict_iter) =
+ default;
+
+const_dict_iterator& const_dict_iterator::operator=(
+ const const_dict_iterator& dict_iter) = default;
+
+const_dict_iterator::~const_dict_iterator() = default;
+
+const_dict_iterator::reference const_dict_iterator::operator*() const {
+ return {dict_iter_->first, *dict_iter_->second};
+}
+
+const_dict_iterator::pointer const_dict_iterator::operator->() const {
+ return pointer(operator*());
+}
+
+const_dict_iterator& const_dict_iterator::operator++() {
+ ++dict_iter_;
+ return *this;
+}
+
+const_dict_iterator const_dict_iterator::operator++(int) {
+ const_dict_iterator tmp(*this);
+ ++dict_iter_;
+ return tmp;
+}
+
+const_dict_iterator& const_dict_iterator::operator--() {
+ --dict_iter_;
+ return *this;
+}
+
+const_dict_iterator const_dict_iterator::operator--(int) {
+ const_dict_iterator tmp(*this);
+ --dict_iter_;
+ return tmp;
+}
+
+bool operator==(const const_dict_iterator& lhs,
+ const const_dict_iterator& rhs) {
+ return lhs.dict_iter_ == rhs.dict_iter_;
+}
+
+bool operator!=(const const_dict_iterator& lhs,
+ const const_dict_iterator& rhs) {
+ return !(lhs == rhs);
+}
+
+// ----------------------------------------------------------------------------
+// dict_iterator_proxy.
+
+dict_iterator_proxy::dict_iterator_proxy(DictStorage* storage)
+ : storage_(storage) {}
+
+dict_iterator_proxy::iterator dict_iterator_proxy::begin() {
+ return iterator(storage_->begin());
+}
+
+dict_iterator_proxy::const_iterator dict_iterator_proxy::begin() const {
+ return const_iterator(storage_->begin());
+}
+
+dict_iterator_proxy::iterator dict_iterator_proxy::end() {
+ return iterator(storage_->end());
+}
+
+dict_iterator_proxy::const_iterator dict_iterator_proxy::end() const {
+ return const_iterator(storage_->end());
+}
+
+dict_iterator_proxy::reverse_iterator dict_iterator_proxy::rbegin() {
+ return reverse_iterator(end());
+}
+
+dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::rbegin()
+ const {
+ return const_reverse_iterator(end());
+}
+
+dict_iterator_proxy::reverse_iterator dict_iterator_proxy::rend() {
+ return reverse_iterator(begin());
+}
+
+dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::rend() const {
+ return const_reverse_iterator(begin());
+}
+
+dict_iterator_proxy::const_iterator dict_iterator_proxy::cbegin() const {
+ return const_iterator(begin());
+}
+
+dict_iterator_proxy::const_iterator dict_iterator_proxy::cend() const {
+ return const_iterator(end());
+}
+
+dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::crbegin()
+ const {
+ return const_reverse_iterator(rbegin());
+}
+
+dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::crend() const {
+ return const_reverse_iterator(rend());
+}
+
+// ----------------------------------------------------------------------------
+// const_dict_iterator_proxy.
+
+const_dict_iterator_proxy::const_dict_iterator_proxy(const DictStorage* storage)
+ : storage_(storage) {}
+
+const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::begin()
+ const {
+ return const_iterator(storage_->begin());
+}
+
+const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::end()
+ const {
+ return const_iterator(storage_->end());
+}
+
+const_dict_iterator_proxy::const_reverse_iterator
+const_dict_iterator_proxy::rbegin() const {
+ return const_reverse_iterator(end());
+}
+
+const_dict_iterator_proxy::const_reverse_iterator
+const_dict_iterator_proxy::rend() const {
+ return const_reverse_iterator(begin());
+}
+
+const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::cbegin()
+ const {
+ return const_iterator(begin());
+}
+
+const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::cend()
+ const {
+ return const_iterator(end());
+}
+
+const_dict_iterator_proxy::const_reverse_iterator
+const_dict_iterator_proxy::crbegin() const {
+ return const_reverse_iterator(rbegin());
+}
+
+const_dict_iterator_proxy::const_reverse_iterator
+const_dict_iterator_proxy::crend() const {
+ return const_reverse_iterator(rend());
+}
+
+} // namespace detail
+
+} // namespace base
diff --git a/gn/base/value_iterators.h b/gn/base/value_iterators.h
new file mode 100644
index 00000000000..4c814c5b1b2
--- /dev/null
+++ b/gn/base/value_iterators.h
@@ -0,0 +1,191 @@
+// 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_VALUE_ITERATORS_H_
+#define BASE_VALUE_ITERATORS_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+
+namespace base {
+
+class Value;
+
+namespace detail {
+
+using DictStorage = base::flat_map<std::string, std::unique_ptr<Value>>;
+
+// This iterator closely resembles DictStorage::iterator, with one
+// important exception. It abstracts the underlying unique_ptr away, meaning its
+// value_type is std::pair<const std::string, Value>. It's reference type is a
+// std::pair<const std::string&, Value&>, so that callers have read-write
+// access without incurring a copy.
+class dict_iterator {
+ public:
+ using difference_type = DictStorage::iterator::difference_type;
+ using value_type = std::pair<const std::string, Value>;
+ using reference = std::pair<const std::string&, Value&>;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ class pointer {
+ public:
+ explicit pointer(const reference& ref);
+ pointer(const pointer& ptr);
+ pointer& operator=(const pointer& ptr) = delete;
+
+ reference* operator->() { return &ref_; }
+
+ private:
+ reference ref_;
+ };
+
+ explicit dict_iterator(DictStorage::iterator dict_iter);
+ dict_iterator(const dict_iterator& dict_iter);
+ dict_iterator& operator=(const dict_iterator& dict_iter);
+ ~dict_iterator();
+
+ reference operator*();
+ pointer operator->();
+
+ dict_iterator& operator++();
+ dict_iterator operator++(int);
+ dict_iterator& operator--();
+ dict_iterator operator--(int);
+
+ friend bool operator==(const dict_iterator& lhs, const dict_iterator& rhs);
+ friend bool operator!=(const dict_iterator& lhs, const dict_iterator& rhs);
+
+ private:
+ DictStorage::iterator dict_iter_;
+};
+
+// This iterator closely resembles DictStorage::const_iterator, with one
+// important exception. It abstracts the underlying unique_ptr away, meaning its
+// value_type is std::pair<const std::string, Value>. It's reference type is a
+// std::pair<const std::string&, const Value&>, so that callers have read-only
+// access without incurring a copy.
+class const_dict_iterator {
+ public:
+ using difference_type = DictStorage::const_iterator::difference_type;
+ using value_type = std::pair<const std::string, Value>;
+ using reference = std::pair<const std::string&, const Value&>;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ class pointer {
+ public:
+ explicit pointer(const reference& ref);
+ pointer(const pointer& ptr);
+ pointer& operator=(const pointer& ptr) = delete;
+
+ const reference* operator->() const { return &ref_; }
+
+ private:
+ const reference ref_;
+ };
+
+ explicit const_dict_iterator(DictStorage::const_iterator dict_iter);
+ const_dict_iterator(const const_dict_iterator& dict_iter);
+ const_dict_iterator& operator=(const const_dict_iterator& dict_iter);
+ ~const_dict_iterator();
+
+ reference operator*() const;
+ pointer operator->() const;
+
+ const_dict_iterator& operator++();
+ const_dict_iterator operator++(int);
+ const_dict_iterator& operator--();
+ const_dict_iterator operator--(int);
+
+ friend bool operator==(const const_dict_iterator& lhs,
+ const const_dict_iterator& rhs);
+ friend bool operator!=(const const_dict_iterator& lhs,
+ const const_dict_iterator& rhs);
+
+ private:
+ DictStorage::const_iterator dict_iter_;
+};
+
+// This class wraps the various |begin| and |end| methods of the underlying
+// DictStorage in dict_iterators and const_dict_iterators. This allows callers
+// to use this class for easy iteration over the underlying values, granting
+// them either read-only or read-write access, depending on the
+// const-qualification.
+class dict_iterator_proxy {
+ public:
+ using key_type = DictStorage::key_type;
+ using mapped_type = DictStorage::mapped_type::element_type;
+ using value_type = std::pair<key_type, mapped_type>;
+ using key_compare = DictStorage::key_compare;
+ using size_type = DictStorage::size_type;
+ using difference_type = DictStorage::difference_type;
+
+ using iterator = dict_iterator;
+ using const_iterator = const_dict_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ explicit dict_iterator_proxy(DictStorage* storage);
+
+ iterator begin();
+ const_iterator begin() const;
+ iterator end();
+ const_iterator end() const;
+
+ reverse_iterator rbegin();
+ const_reverse_iterator rbegin() const;
+ reverse_iterator rend();
+ const_reverse_iterator rend() const;
+
+ const_dict_iterator cbegin() const;
+ const_dict_iterator cend() const;
+ const_reverse_iterator crbegin() const;
+ const_reverse_iterator crend() const;
+
+ private:
+ DictStorage* storage_;
+};
+
+// This class wraps the various const |begin| and |end| methods of the
+// underlying DictStorage in const_dict_iterators. This allows callers to use
+// this class for easy iteration over the underlying values, granting them
+// either read-only access.
+class const_dict_iterator_proxy {
+ public:
+ using key_type = const DictStorage::key_type;
+ using mapped_type = const DictStorage::mapped_type::element_type;
+ using value_type = std::pair<key_type, mapped_type>;
+ using key_compare = DictStorage::key_compare;
+ using size_type = DictStorage::size_type;
+ using difference_type = DictStorage::difference_type;
+
+ using iterator = const_dict_iterator;
+ using const_iterator = const_dict_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ explicit const_dict_iterator_proxy(const DictStorage* storage);
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ const_reverse_iterator rbegin() const;
+ const_reverse_iterator rend() const;
+
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+ const_reverse_iterator crbegin() const;
+ const_reverse_iterator crend() const;
+
+ private:
+ const DictStorage* storage_;
+};
+} // namespace detail
+
+} // namespace base
+
+#endif // BASE_VALUE_ITERATORS_H_
diff --git a/gn/base/values.cc b/gn/base/values.cc
new file mode 100644
index 00000000000..0b348b725f9
--- /dev/null
+++ b/gn/base/values.cc
@@ -0,0 +1,1297 @@
+// 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
new file mode 100644
index 00000000000..c3ec875fa4b
--- /dev/null
+++ b/gn/base/values.h
@@ -0,0 +1,757 @@
+// 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
new file mode 100644
index 00000000000..e07820baaab
--- /dev/null
+++ b/gn/base/win/registry.cc
@@ -0,0 +1,613 @@
+// 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
new file mode 100644
index 00000000000..e2e0d305cec
--- /dev/null
+++ b/gn/base/win/registry.h
@@ -0,0 +1,248 @@
+// 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
new file mode 100644
index 00000000000..ddc744fbc05
--- /dev/null
+++ b/gn/base/win/scoped_handle.cc
@@ -0,0 +1,35 @@
+// 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
new file mode 100644
index 00000000000..e67e44717fd
--- /dev/null
+++ b/gn/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 "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/scoped_process_information.cc b/gn/base/win/scoped_process_information.cc
new file mode 100644
index 00000000000..93fb9e63449
--- /dev/null
+++ b/gn/base/win/scoped_process_information.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_process_information.h"
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+// Duplicates source into target, returning true upon success. |target| is
+// guaranteed to be untouched in case of failure. Succeeds with no side-effects
+// if source is NULL.
+bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) {
+ if (!source)
+ return true;
+
+ HANDLE temp = NULL;
+ if (!::DuplicateHandle(::GetCurrentProcess(), source, ::GetCurrentProcess(),
+ &temp, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ DWORD last_error = ::GetLastError();
+ DPLOG(ERROR) << "Failed to duplicate a handle " << last_error;
+ ::SetLastError(last_error);
+ return false;
+ }
+ target->Set(temp);
+ return true;
+}
+
+} // namespace
+
+ScopedProcessInformation::ScopedProcessInformation()
+ : process_id_(0), thread_id_(0) {}
+
+ScopedProcessInformation::ScopedProcessInformation(
+ const PROCESS_INFORMATION& process_info)
+ : process_id_(0), thread_id_(0) {
+ Set(process_info);
+}
+
+ScopedProcessInformation::~ScopedProcessInformation() {
+ Close();
+}
+
+bool ScopedProcessInformation::IsValid() const {
+ return process_id_ || process_handle_.Get() || thread_id_ ||
+ thread_handle_.Get();
+}
+
+void ScopedProcessInformation::Close() {
+ process_handle_.Close();
+ thread_handle_.Close();
+ process_id_ = 0;
+ thread_id_ = 0;
+}
+
+void ScopedProcessInformation::Set(const PROCESS_INFORMATION& process_info) {
+ if (IsValid())
+ Close();
+
+ process_handle_.Set(process_info.hProcess);
+ thread_handle_.Set(process_info.hThread);
+ process_id_ = process_info.dwProcessId;
+ thread_id_ = process_info.dwThreadId;
+}
+
+bool ScopedProcessInformation::DuplicateFrom(
+ const ScopedProcessInformation& other) {
+ DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL";
+ DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid";
+
+ if (CheckAndDuplicateHandle(other.process_handle(), &process_handle_) &&
+ CheckAndDuplicateHandle(other.thread_handle(), &thread_handle_)) {
+ process_id_ = other.process_id();
+ thread_id_ = other.thread_id();
+ return true;
+ }
+
+ return false;
+}
+
+PROCESS_INFORMATION ScopedProcessInformation::Take() {
+ PROCESS_INFORMATION process_information = {};
+ process_information.hProcess = process_handle_.Take();
+ process_information.hThread = thread_handle_.Take();
+ process_information.dwProcessId = process_id();
+ process_information.dwThreadId = thread_id();
+ process_id_ = 0;
+ thread_id_ = 0;
+
+ return process_information;
+}
+
+HANDLE ScopedProcessInformation::TakeProcessHandle() {
+ process_id_ = 0;
+ return process_handle_.Take();
+}
+
+HANDLE ScopedProcessInformation::TakeThreadHandle() {
+ thread_id_ = 0;
+ return thread_handle_.Take();
+}
+
+} // namespace win
+} // namespace base
diff --git a/gn/base/win/scoped_process_information.h b/gn/base/win/scoped_process_information.h
new file mode 100644
index 00000000000..557555deba4
--- /dev/null
+++ b/gn/base/win/scoped_process_information.h
@@ -0,0 +1,74 @@
+// 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_PROCESS_INFORMATION_H_
+#define BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+// Manages the closing of process and thread handles from PROCESS_INFORMATION
+// structures. Allows clients to take ownership of either handle independently.
+class ScopedProcessInformation {
+ public:
+ ScopedProcessInformation();
+ explicit ScopedProcessInformation(const PROCESS_INFORMATION& process_info);
+ ~ScopedProcessInformation();
+
+ // Returns true iff this instance is holding a thread and/or process handle.
+ bool IsValid() const;
+
+ // Closes the held thread and process handles, if any.
+ void Close();
+
+ // Populates this instance with the provided |process_info|.
+ void Set(const PROCESS_INFORMATION& process_info);
+
+ // Populates this instance with duplicate handles and the thread/process IDs
+ // from |other|. Returns false in case of failure, in which case this instance
+ // will be completely unpopulated.
+ bool DuplicateFrom(const ScopedProcessInformation& other);
+
+ // Transfers ownership of the held PROCESS_INFORMATION, if any, away from this
+ // instance.
+ PROCESS_INFORMATION Take();
+
+ // Transfers ownership of the held process handle, if any, away from this
+ // instance. Note that the related process_id will also be cleared.
+ HANDLE TakeProcessHandle();
+
+ // Transfers ownership of the held thread handle, if any, away from this
+ // instance. Note that the related thread_id will also be cleared.
+ HANDLE TakeThreadHandle();
+
+ // Returns the held process handle, if any, while retaining ownership.
+ HANDLE process_handle() const { return process_handle_.Get(); }
+
+ // Returns the held thread handle, if any, while retaining ownership.
+ HANDLE thread_handle() const { return thread_handle_.Get(); }
+
+ // Returns the held process id, if any.
+ DWORD process_id() const { return process_id_; }
+
+ // Returns the held thread id, if any.
+ DWORD thread_id() const { return thread_id_; }
+
+ private:
+ ScopedHandle process_handle_;
+ ScopedHandle thread_handle_;
+ DWORD process_id_;
+ DWORD thread_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcessInformation);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
diff --git a/gn/base/win/windows_types.h b/gn/base/win/windows_types.h
new file mode 100644
index 00000000000..d076c446b87
--- /dev/null
+++ b/gn/base/win/windows_types.h
@@ -0,0 +1,254 @@
+// 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
new file mode 100644
index 00000000000..f393e18be38
--- /dev/null
+++ b/gn/build/build_aix.ninja.template
@@ -0,0 +1,16 @@
+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
+ 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_linux.ninja.template b/gn/build/build_linux.ninja.template
new file mode 100644
index 00000000000..e59854b189e
--- /dev/null
+++ b/gn/build/build_linux.ninja.template
@@ -0,0 +1,19 @@
+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
+ 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_mac.ninja.template b/gn/build/build_mac.ninja.template
new file mode 100644
index 00000000000..ff82eb88c1d
--- /dev/null
+++ b/gn/build/build_mac.ninja.template
@@ -0,0 +1,19 @@
+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
+ description = CXX $out
+ depfile = $out.d
+ deps = gcc
+
+rule alink_thin
+ command = rm -f $out && libtool -static -o $out $in
+ description = AR $out
+
+rule link
+ command = $ld $ldflags -o $out $in $solibs $libs
+ description = LINK $out
diff --git a/gn/build/build_win.ninja.template b/gn/build/build_win.ninja.template
new file mode 100644
index 00000000000..3edfb6a4611
--- /dev/null
+++ b/gn/build/build_win.ninja.template
@@ -0,0 +1,25 @@
+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}
+ 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}
+
+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}
diff --git a/gn/build/full_test.py b/gn/build/full_test.py
new file mode 100755
index 00000000000..2095dddf6da
--- /dev/null
+++ b/gn/build/full_test.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# 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.
+
+import os
+import shutil
+import subprocess
+import sys
+import timeit
+
+
+IS_WIN = sys.platform.startswith('win')
+
+
+def RemoveDir(d):
+ if os.path.isdir(d):
+ shutil.rmtree(d)
+
+
+def Trial(gn_path_to_use, save_out_dir=None):
+ bin_path = os.path.join('out', 'gntrial')
+ if not os.path.isdir(bin_path):
+ os.makedirs(bin_path)
+ gn_to_run = os.path.join(bin_path, 'gn' + ('.exe' if IS_WIN else ''))
+ shutil.copy2(gn_path_to_use, gn_to_run)
+ comp_dir = os.path.join('out', 'COMP')
+ subprocess.check_call([gn_to_run, 'gen', comp_dir, '-q', '--check'])
+ if save_out_dir:
+ RemoveDir(save_out_dir)
+ shutil.move(comp_dir, save_out_dir)
+
+
+def main():
+ if len(sys.argv) < 3 or len(sys.argv) > 4:
+ print 'Usage: full_test.py /chrome/tree/at/762a25542878 rel_gn_path [clean]'
+ return 1
+
+ if len(sys.argv) == 4:
+ RemoveDir('out')
+
+ subprocess.check_call([sys.executable, os.path.join('build', 'gen.py')])
+ subprocess.check_call(['ninja', '-C', 'out'])
+ subprocess.check_call([os.path.join('out', 'gn_unittests')])
+ orig_dir = os.getcwd()
+
+ in_chrome_tree_gn = sys.argv[2]
+ our_gn = os.path.join(orig_dir, 'out', 'gn' + ('.exe' if IS_WIN else ''))
+
+ os.chdir(sys.argv[1])
+
+ # Check in-tree vs. ours. Uses:
+ # - Chromium tree at 762a25542878 in argv[1] (this can be off by a bit, but
+ # is roughly when GN was moved out of the Chrome tree, so matches in case GN
+ # semantics/ordering change after that.)
+ # - relative path to argv[1] built gn binary in argv[2]
+
+ # First, do a comparison to make sure the output between the two gn binaries
+ # actually matches.
+ print 'Confirming output matches...'
+ dir_a = os.path.join('out', 'a')
+ dir_b = os.path.join('out', 'b')
+ Trial(in_chrome_tree_gn, dir_a)
+ Trial(our_gn, dir_b)
+ subprocess.check_call(['diff', '-r', dir_a, dir_b])
+
+ # Then, some time trials.
+ TRIALS = 5
+ print 'Comparing performance... (takes a while)'
+ time_a = timeit.timeit('Trial("%s")' % in_chrome_tree_gn, number=TRIALS,
+ setup='from __main__ import Trial')
+ time_b = timeit.timeit('Trial("%s")' % our_gn, number=TRIALS,
+ setup='from __main__ import Trial')
+ print 'In-tree gn avg: %.3fs' % (time_a / TRIALS)
+ print 'Our gn avg: %.3fs' % (time_b / TRIALS)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/gn/build/gen.py b/gn/build/gen.py
new file mode 100755
index 00000000000..76f88e84224
--- /dev/null
+++ b/gn/build/gen.py
@@ -0,0 +1,732 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Generates build.ninja that will build GN."""
+
+import contextlib
+import errno
+import optparse
+import os
+import platform
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import urllib2
+
+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):
+ """Represents a host/target platform."""
+ def __init__(self, platform):
+ self._platform = platform
+ if self._platform is not None:
+ return
+ self._platform = sys.platform
+ if self._platform.startswith('linux'):
+ self._platform = 'linux'
+ elif self._platform.startswith('darwin'):
+ self._platform = 'darwin'
+ elif self._platform.startswith('mingw'):
+ self._platform = 'mingw'
+ elif self._platform.startswith('win'):
+ self._platform = 'msvc'
+ elif self._platform.startswith('aix'):
+ self._platform = 'aix'
+ elif self._platform.startswith('fuchsia'):
+ self._platform = 'fuchsia'
+
+ @staticmethod
+ def known_platforms():
+ return ['linux', 'darwin', 'msvc', 'aix', 'fuchsia']
+
+ def platform(self):
+ return self._platform
+
+ def is_linux(self):
+ return self._platform == 'linux'
+
+ def is_mingw(self):
+ return self._platform == 'mingw'
+
+ def is_msvc(self):
+ return self._platform == 'msvc'
+
+ def is_windows(self):
+ return self.is_mingw() or self.is_msvc()
+
+ def is_darwin(self):
+ return self._platform == 'darwin'
+
+ def is_aix(self):
+ return self._platform == 'aix'
+
+ def is_posix(self):
+ return self._platform in ['linux', 'darwin', 'aix']
+
+
+def main(argv):
+ parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
+ parser.add_option('-d', '--debug', action='store_true',
+ help='Do a debug build. Defaults to release build.')
+ parser.add_option('--platform',
+ help='target platform (' +
+ '/'.join(Platform.known_platforms()) + ')',
+ choices=Platform.known_platforms())
+ parser.add_option('--host',
+ help='host platform (' +
+ '/'.join(Platform.known_platforms()) + ')',
+ choices=Platform.known_platforms())
+ parser.add_option('--use-lto', action='store_true',
+ help='Enable the use of LTO')
+ parser.add_option('--use-icf', action='store_true',
+ help='Enable the use of Identical Code Folding')
+ parser.add_option('--no-sysroot', action='store_true',
+ help='(Linux only) Do not build with the Debian sysroot.')
+ parser.add_option('--no-last-commit-position', action='store_true',
+ help='Do not generate last_commit_position.h.')
+ parser.add_option('--out-path',
+ help='The path to generate the build files in.')
+ options, args = parser.parse_args(argv)
+
+ if args:
+ parser.error('Unrecognized command line arguments: %s.' % ', '.join(args))
+
+ platform = Platform(options.platform)
+ if options.host:
+ host = Platform(options.host)
+ else:
+ host = platform
+
+ linux_sysroot = None
+ if platform.is_linux() and not options.no_sysroot:
+ linux_sysroot = UpdateLinuxSysroot()
+
+ out_dir = options.out_path or os.path.join(REPO_ROOT, 'out')
+ if not os.path.isdir(out_dir):
+ os.makedirs(out_dir)
+ if not options.no_last_commit_position:
+ GenerateLastCommitPosition(host,
+ os.path.join(out_dir, 'last_commit_position.h'))
+ WriteGNNinja(os.path.join(out_dir, 'build.ninja'), platform, host, options,
+ linux_sysroot)
+ return 0
+
+
+def GenerateLastCommitPosition(host, header):
+ ROOT_TAG = 'initial-commit'
+ describe_output = subprocess.check_output(
+ ['git', 'describe', 'HEAD', '--match', ROOT_TAG], shell=host.is_windows(),
+ cwd=REPO_ROOT)
+ mo = re.match(ROOT_TAG + '-(\d+)-g([0-9a-f]+)', describe_output)
+ if not mo:
+ raise ValueError(
+ 'Unexpected output from git describe when generating version header')
+
+ contents = '''// Generated by build/gen.py.
+
+#ifndef OUT_LAST_COMMIT_POSITION_H_
+#define OUT_LAST_COMMIT_POSITION_H_
+
+#define LAST_COMMIT_POSITION "%s (%s)"
+
+#endif // OUT_LAST_COMMIT_POSITION_H_
+''' % (mo.group(1), mo.group(2))
+
+ # Only write/touch this file if the commit position has changed.
+ old_contents = ''
+ if os.path.isfile(header):
+ with open(header, 'rb') as f:
+ old_contents = f.read()
+
+ if old_contents != contents:
+ with open(header, 'wb') as f:
+ f.write(contents)
+
+
+def UpdateLinuxSysroot():
+ # Sysroot revision from:
+ # https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/sysroots.json
+ server = 'https://commondatastorage.googleapis.com'
+ path = 'chrome-linux-sysroot/toolchain'
+ revision = '1015a998c2adf188813cca60b558b0ea1a0b6ced'
+ filename = 'debian_sid_amd64_sysroot.tar.xz'
+
+ url = '%s/%s/%s/%s' % (server, path, revision, filename)
+
+ sysroot = os.path.join(SCRIPT_DIR, os.pardir, '.linux-sysroot')
+
+ stamp = os.path.join(sysroot, '.stamp')
+ if os.path.exists(stamp):
+ with open(stamp) as s:
+ if s.read() == url:
+ return sysroot
+
+ print 'Installing Debian root image from %s' % url
+
+ if os.path.isdir(sysroot):
+ shutil.rmtree(sysroot)
+ os.mkdir(sysroot)
+ tarball = os.path.join(sysroot, filename)
+ print 'Downloading %s' % url
+
+ for _ in range(3):
+ response = urllib2.urlopen(url)
+ with open(tarball, 'wb') as f:
+ f.write(response.read())
+ break
+ else:
+ raise Exception('Failed to download %s' % url)
+
+ subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot])
+
+ os.remove(tarball)
+
+ with open(stamp, 'w') as s:
+ s.write(url)
+
+ return sysroot
+
+
+def WriteGenericNinja(path, static_libraries, executables,
+ cc, cxx, ar, ld, platform, host, options,
+ cflags=[], cflags_cc=[], ldflags=[], libflags=[],
+ include_dirs=[], solibs=[]):
+ 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 ''),
+ ' description = Regenerating ninja files',
+ '',
+ 'build build.ninja: regen',
+ ' generator = 1',
+ ' depfile = build.ninja.d',
+ '',
+ ]
+
+
+ template_filename = os.path.join(SCRIPT_DIR, {
+ 'msvc': 'build_win.ninja.template',
+ 'darwin': 'build_mac.ninja.template',
+ 'linux': 'build_linux.ninja.template',
+ 'aix': 'build_aix.ninja.template',
+ }[platform.platform()])
+
+ with open(template_filename) as f:
+ ninja_template = f.read()
+
+ if platform.is_windows():
+ executable_ext = '.exe'
+ library_ext = '.lib'
+ object_ext = '.obj'
+ else:
+ executable_ext = ''
+ library_ext = '.a'
+ object_ext = '.o'
+
+ def escape_path_ninja(path):
+ return path.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')
+
+ def src_to_obj(path):
+ return escape_path_ninja('%s' % os.path.splitext(path)[0] + object_ext)
+
+ def library_to_a(library):
+ return '%s%s' % (library, library_ext)
+
+ 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.join(REPO_ROOT, src_file))),
+ ' 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', [])),
+ ])
+
+ for library, settings in static_libraries.iteritems():
+ for src_file in settings['sources']:
+ build_source(src_file, settings)
+
+ ninja_lines.append('build %s: alink_thin %s' % (
+ library_to_a(library),
+ ' '.join([src_to_obj(src_file) for src_file in settings['sources']])))
+ ninja_lines.append(' libflags = %s' % ' '.join(libflags))
+
+
+ for executable, settings in executables.iteritems():
+ for src_file in settings['sources']:
+ build_source(src_file, settings)
+
+ ninja_lines.extend([
+ 'build %s%s: link %s | %s' % (
+ executable, executable_ext,
+ ' '.join([src_to_obj(src_file) for src_file in settings['sources']]),
+ ' '.join([library_to_a(library) for library in settings['libs']])),
+ ' ldflags = %s' % ' '.join(ldflags),
+ ' solibs = %s' % ' '.join(solibs),
+ ' libs = %s' % ' '.join(
+ [library_to_a(library) for library in settings['libs']]),
+ ])
+
+ ninja_lines.append('') # Make sure the file ends with a newline.
+
+ with open(path, 'w') as f:
+ f.write('\n'.join(ninja_header_lines))
+ f.write(ninja_template)
+ f.write('\n'.join(ninja_lines))
+
+ with open(path + '.d', 'w') as f:
+ f.write('build.ninja: ' +
+ os.path.relpath(os.path.join(SCRIPT_DIR, 'gen.py'),
+ os.path.dirname(path)) + ' ' +
+ os.path.relpath(template_filename, os.path.dirname(path)) + '\n')
+
+
+def WriteGNNinja(path, platform, host, options, linux_sysroot):
+ 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')
+ 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()
+ ldflags = os.environ.get('LDFLAGS', '').split()
+ libflags = os.environ.get('LIBFLAGS', '').split()
+ include_dirs = [REPO_ROOT, os.path.dirname(path)]
+ libs = []
+
+ if not platform.is_msvc():
+ if options.debug:
+ cflags.extend(['-O0', '-g'])
+ else:
+ cflags.append('-DNDEBUG')
+ cflags.append('-O3')
+ ldflags.append('-O3')
+ # Use -fdata-sections and -ffunction-sections to place each function
+ # or data item into its own section so --gc-sections can eliminate any
+ # unused functions and data items.
+ cflags.extend(['-fdata-sections', '-ffunction-sections'])
+ ldflags.extend(['-fdata-sections', '-ffunction-sections'])
+ if platform.is_darwin():
+ ldflags.append('-Wl,-dead_strip')
+ elif not platform.is_aix():
+ # Garbage collection is done by default on aix.
+ ldflags.append('-Wl,--gc-sections')
+
+ # Omit all symbol information from the output file.
+ if platform.is_darwin():
+ ldflags.append('-Wl,-S')
+ elif platform.is_aix():
+ ldflags.append('-Wl,-s')
+ else:
+ ldflags.append('-Wl,-strip-all')
+
+ # Enable identical code-folding.
+ if options.use_icf:
+ ldflags.append('-Wl,--icf=all')
+
+ cflags.extend([
+ '-D_FILE_OFFSET_BITS=64',
+ '-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS',
+ '-pthread',
+ '-pipe',
+ '-fno-exceptions',
+ '-fno-rtti',
+ ])
+ cflags_cc.extend(['-std=c++14', '-Wno-c++11-narrowing'])
+
+ if platform.is_linux():
+ if linux_sysroot:
+ # Use the sid sysroot that UpdateLinuxSysroot() downloads.
+ cflags.append('--sysroot=' + linux_sysroot)
+ ldflags.append('--sysroot=' + linux_sysroot)
+ ldflags.extend([
+ '-static-libstdc++',
+ '-Wl,--as-needed',
+ ])
+ libs.extend([
+ # These are needed by libc++.
+ '-ldl',
+ '-lpthread',
+ ])
+ 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')
+ ldflags.extend(['-maix64', '-pthread'])
+
+ if options.use_lto:
+ cflags.extend(['-flto', '-fwhole-program-vtables'])
+ ldflags.extend(['-flto', '-fwhole-program-vtables'])
+
+ elif platform.is_msvc():
+ if not options.debug:
+ cflags.extend(['/Ox', '/DNDEBUG', '/GL'])
+ libflags.extend(['/LTCG'])
+ ldflags.extend(['/LTCG', '/OPT:REF', '/OPT:ICF'])
+
+ cflags.extend([
+ '/DNOMINMAX',
+ '/DUNICODE',
+ '/DWIN32_LEAN_AND_MEAN',
+ '/DWINVER=0x0A00',
+ '/D_CRT_SECURE_NO_DEPRECATE',
+ '/D_SCL_SECURE_NO_DEPRECATE',
+ '/D_UNICODE',
+ '/D_WIN32_WINNT=0x0A00',
+ '/FS',
+ '/Gy',
+ '/W4',
+ '/WX',
+ '/Zi',
+ '/wd4099',
+ '/wd4100',
+ '/wd4127',
+ '/wd4244',
+ '/wd4267',
+ '/wd4838',
+ '/wd4996',
+ ])
+ cflags_cc.extend([
+ '/GR-',
+ '/D_HAS_EXCEPTIONS=0',
+ ])
+
+ ldflags.extend(['/DEBUG', '/MACHINE:x64'])
+
+ 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': []},
+ '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/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_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/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/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_copy_target_writer.cc',
+ 'tools/gn/ninja_create_bundle_target_writer.cc',
+ 'tools/gn/ninja_group_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/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/source_file_type.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': []},
+ }
+
+ executables = {
+ 'gn': {'sources': [ 'tools/gn/gn_main.cc' ],
+ 'tool': 'cxx', 'include_dirs': [], '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_unittest.cc',
+ 'tools/gn/header_checker_unittest.cc',
+ 'tools/gn/inherited_libraries_unittest.cc',
+ 'tools/gn/input_conversion_unittest.cc',
+ 'tools/gn/label_pattern_unittest.cc',
+ 'tools/gn/label_unittest.cc',
+ 'tools/gn/loader_unittest.cc',
+ 'tools/gn/ninja_action_target_writer_unittest.cc',
+ 'tools/gn/ninja_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_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/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': []},
+ }
+
+ 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',
+ ])
+
+ if platform.is_linux() or platform.is_aix():
+ static_libraries['base']['sources'].extend([
+ 'base/strings/sys_string_conversions_posix.cc',
+ ])
+
+ if platform.is_darwin():
+ static_libraries['base']['sources'].extend([
+ 'base/files/file_util_mac.mm',
+ 'base/mac/bundle_locations.mm',
+ 'base/mac/foundation_util.mm',
+ 'base/strings/sys_string_conversions_mac.mm',
+ ])
+
+ libs.extend([
+ '-framework', 'AppKit',
+ '-framework', 'CoreFoundation',
+ '-framework', 'Foundation',
+ '-framework', 'Security',
+ ])
+
+ 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/strings/sys_string_conversions_win.cc',
+ 'base/win/registry.cc',
+ 'base/win/scoped_handle.cc',
+ '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',
+ ])
+
+ # 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,
+ libflags, include_dirs, libs)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/gn/docs/cross_compiles.md b/gn/docs/cross_compiles.md
new file mode 100644
index 00000000000..6da66543102
--- /dev/null
+++ b/gn/docs/cross_compiles.md
@@ -0,0 +1,125 @@
+# How GN handles cross-compiling
+
+## As a GN user
+
+GN has robust support for doing cross compiles and building things for
+multiple architectures in a single build (e.g., to build some things to
+run locally and some things to run on an embedded device). In fact,
+there is no limit on the number of different architectures you can build
+at once; the Chromium build uses at least four in some configurations.
+
+To start, GN has the concepts of a _host_ and a _target_. The host is
+the platform that the build is run on, and the target is the platform
+where the code will actually run (This is different from
+[autotools](http://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html)'
+terminology, but uses the more common terminology for cross
+compiling**).**
+
+(Confusingly, GN also refers to each build artifact -- an executable,
+library, etc. -- as a target. On this page, we will use "target" only to
+refer to the system you want to run your code on, and use "rule" or some
+other synonym to refer to a specific build artifact).
+
+When GN starts up, the `host_os` and `host_cpu` variables are set
+automatically to match the operating system (they can be overridden in
+args files, which can be useful in weird corner cases). The user can
+specify that they want to do a cross-compile by setting either or both
+of `target_os` and `target_cpu`; if they are not set, the build config
+files will usually set them to the host's values, though the Chromium
+build will set target\_cpu to "arm" if target\_os is set to "android").
+
+So, for example, running on an x64 Linux machine:
+
+```
+gn gen out/Default
+```
+
+is equivalent to:
+
+```
+gn gen out/Default --args='target_os="linux" target_cpu="x64"'
+```
+
+To do an 32-bit ARM Android cross-compile, do:
+
+```
+gn gen out/Default --args='target_os="android"'
+```
+
+(We don't have to specify target\_cpu because of the conditionals
+mentioned above).
+
+And, to do a 64-bit MIPS Chrome OS cross-compile:
+
+```
+gn gen out/Default --args='target_os="chromeos" target_cpu="mips64el"'
+```
+
+## As a BUILD.gn author
+
+If you are editing build files outside of the //build directory (i.e.,
+not directly working on toolchains, compiler configs, etc.), generally
+you only need to worry about a few things:
+
+The `current_toolchain`, `current_cpu`, and `current_os` variables
+reflect the settings that are **currently** in effect in a given rule.
+The `is_linux`, `is_win` etc. variables are updated to reflect the
+current settings, and changes to `cflags`, `ldflags` and so forth also
+only apply to the current toolchain and the current thing being built.
+
+You can also refer to the `target_cpu` and `target_os` variables. This
+is useful if you need to do something different on the host depending on
+which target\_arch is requested; the values are constant across all
+toolchains. You can do similar things for the `host_cpu` and `host_os`
+variables, but should generally never need to.
+
+For the default toolchain, `target_cpu` and `current_cpu` are the same. For a
+secondary toolchain, `current_cpu` is set based on the toolchain definition
+and `target_cpu` remains the same. When writing rules, **`current_cpu` should
+be used rather than `target_cpu` most of the time**.
+
+By default, dependencies listed in the `deps` variable of a rule use the
+same (currently active) toolchain. You may specify a different toolchain
+using the `foo(bar)` label notation as described in [the label section
+of the reference doc](reference.md#Toolchains).
+
+Here's an example of when to use `target_cpu` vs `current_cpu`:
+
+```
+declare_args() {
+ # Applies only to toolchains targeting target_cpu.
+ sysroot = ""
+}
+
+config("my_config") {
+ # Uses current_cpu because compile flags are toolchain-dependent.
+ if (current_cpu == "arm") {
+ defines = [ "CPU_IS_32_BIT" ]
+ } else {
+ defines = [ "CPU_IS_64_BIT" ]
+ }
+ # Compares current_cpu with target_cpu to see whether current_toolchain
+ # has the same architecture as target_toolchain.
+ if (sysroot != "" && current_cpu == target_cpu) {
+ cflags = [
+ "-isysroot",
+ sysroot,
+ ]
+ }
+}
+```
+
+## 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.
+
+Be sure you understand the differences between `host_cpu`, `target_cpu`,
+`current_cpu`, and `toolchain_cpu` (and the os equivalents). The first
+two are set as described above. You are responsible for making sure that
+`current_cpu` is set appropriately in your toolchain definitions; if you
+are using the stock templates like `gcc_toolchain` and `msvc_toolchain`,
+that means you are responsible for making sure that `toolchain_cpu` and
+`toolchain_os` are set as appropriate in the template invocations.
diff --git a/gn/docs/faq.md b/gn/docs/faq.md
new file mode 100644
index 00000000000..55d8aeffead
--- /dev/null
+++ b/gn/docs/faq.md
@@ -0,0 +1,52 @@
+# GN Frequently Asked Questions
+
+[TOC]
+
+## Where is the GN documentation?
+
+GN has extensive built-in help, so you can run `gn help`, but you can
+also see all of the help on [the reference page](reference.md). See
+also the [quick start](quick_start.md) guide and the [language and
+operation details](language.md).
+
+## Can I generate XCode or Visual Studio projects?
+
+You can generate skeleton (or wrapper) projects for Xcode, Visual Studio,
+QTCreator, and Eclipse that will list the files and targets in the
+build, but use Ninja to do the actual build. You cannot generate "real"
+projects that look like native ones like GYP could.
+
+Run `gn help gen` for more details.
+
+## How do I generate common build variants?
+
+In GN, args go with a build directory rather than being global in the
+environment. To edit the args for your `out/Default` build directory:
+
+```
+gn args out/Default
+```
+
+You can set variables in that file:
+
+ * The default is a debug build. To do a release build add
+ `is_debug = false`
+ * The default is a static build. To do a component build add
+ `is_component_build = true`
+ * The default is a developer build. To do an official build, set
+ `is_official_build = true`
+ * The default is Chromium branding. To do Chrome branding, set
+ `is_chrome_branded = true`
+
+## How do I do cross-compiles?
+
+GN has robust support for doing cross compiles and building things for
+multiple architectures in a single build.
+
+See [GNCrossCompiles](cross_compiles.md) for more info.
+
+## Can I control what targets are built by default?
+
+Yes! If you create a group target called "default" in the top-level (root)
+build file, i.e., "//:default", GN will tell Ninja to build that by
+default, rather than building everything.
diff --git a/gn/docs/hacking.md b/gn/docs/hacking.md
new file mode 100644
index 00000000000..be3f1328c30
--- /dev/null
+++ b/gn/docs/hacking.md
@@ -0,0 +1,23 @@
+# Hacking on the GN binary itself
+
+## Building GN itself
+
+GN is part of the Chromium tree, in [//tools/gn/](../). If you have a
+Chromium checkout, you already have the source and you can do `ninja -C
+out/Debug gn` to build it.
+
+To build gn using gn, run (in the root `src` directory):
+
+```
+gn gen out/Default
+ninja -C out/Default gn
+```
+
+Change `out/Default` as necessary to put the build directory where you
+want.
+
+## Running GN's unit tests
+
+```
+ninja -C out/Default gn_unittests && out/Default/gn_unittests
+```
diff --git a/gn/docs/language.md b/gn/docs/language.md
new file mode 100644
index 00000000000..dcc6c9ef4e9
--- /dev/null
+++ b/gn/docs/language.md
@@ -0,0 +1,540 @@
+# GN Language and Operation
+
+[TOC]
+
+## Introduction
+
+This page describes many of the language details and behaviors.
+
+### Use the built-in help!
+
+GN has an extensive built-in help system which provides a reference for
+every function and built-in variable. This page is more high-level.
+
+```
+gn help
+```
+
+You can also see the
+[slides](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing)
+from a March, 2016 introduction to GN. The speaker notes contain the full
+content.
+
+### Design philosophy
+
+ * Writing build files should not be a creative endeavour. Ideally two
+ people should produce the same buildfile given the same
+ requirements. There should be no flexibility unless it's absolutely
+ needed. As many things should be fatal errors as possible.
+
+ * The definition should read more like code than rules. I don't want
+ to write or debug Prolog. But everybody on our team can write and
+ debug C++ and Python.
+
+ * The build language should be opinionated as to how the build should
+ work. It should not necessarily be easy or even possible to express
+ arbitrary things. We should be changing source and tooling to make
+ the build simpler rather than making everything more complicated to
+ conform to external requirements (within reason).
+
+ * Be like Blaze when it makes sense (see "Differences and similarities
+ to Blaze" below).
+
+## Language
+
+GN uses an extremely simple, dynamically typed language. The types are:
+
+ * Boolean (`true`, `false`).
+ * 64-bit signed integers.
+ * Strings.
+ * Lists (of any other types).
+ * Scopes (sort of like a dictionary, only for built-in stuff).
+
+There are some built-in variables whose values depend on the current
+environment. See `gn help` for more.
+
+There are purposefully many omissions in the language. There are no
+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
+
+Strings are enclosed in double-quotes and use backslash as the escape
+character. The only escape sequences supported are:
+
+ * `\"` (for literal quote)
+ * `\$` (for literal dollars sign)
+ * `\\` (for literal backslash)
+
+Any other use of a backslash is treated as a literal backslash. So, for
+example, `\b` used in patterns does not need to be escaped, nor do most Windows
+paths like `"C:\foo\bar.h"`.
+
+Simple variable substitution is supported via `$`, where the word
+following the dollars sign is replaced with the value of the variable.
+You can optionally surround the name with `{}` if there is not a
+non-variable-name character to terminate the variable name. More complex
+expressions are not supported, only variable name substitution.
+
+```
+a = "mypath"
+b = "$a/foo.cc" # b -> "mypath/foo.cc"
+c = "foo${a}bar.cc" # c -> "foomypathbar.cc"
+```
+
+You can encode 8-bit characters using "$0xFF" syntax, so a string with newlines
+(hex 0A) would `"look$0x0Alike$0x0Athis"`.
+
+### Lists
+
+There is no way to get the length of a list. If you find yourself
+wanting to do this kind of thing, you're trying to do too much work in
+the build.
+
+Lists support appending:
+
+```
+a = [ "first" ]
+a += [ "second" ] # [ "first", "second" ]
+a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]
+b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
+```
+
+Appending a list to another list appends the items in the second list
+rather than appending the list as a nested member.
+
+You can remove items from a list:
+
+```
+a = [ "first", "second", "third", "first" ]
+b = a - [ "first" ] # [ "second", "third" ]
+a -= [ "second" ] # [ "first", "third", "fourth" ]
+```
+
+The - operator on a list searches for matches and removes all matching
+items. Subtracting a list from another list will remove each item in the
+second list.
+
+If no matching items are found, an error will be thrown, so you need to
+know in advance that the item is there before removing it. Given that
+there is no way to test for inclusion, the main use-case is to set up a
+master list of files or flags, and to remove ones that don't apply to
+the current build based on various conditions.
+
+Stylistically, prefer to only add to lists and have each source file or
+dependency appear once. This is the opposite of the advice Chrome-team used to
+give for GYP (GYP would prefer to list all files, and then remove the ones you
+didn't want in conditionals).
+
+Lists support zero-based subscripting to extract values:
+
+```
+a = [ "first", "second", "third" ]
+b = a[1] # -> "second"
+```
+
+The \[\] operator is read-only and can not be used to mutate the
+list. The primary use-case of this is when an external script returns
+several known values and you want to extract them.
+
+There are some cases where it's easy to overwrite a list when you mean
+to append to it instead. To help catch this case, it is an error to
+assign a nonempty list to a variable containing an existing nonempty
+list. If you want to get around this restriction, first assign the
+destination variable to the empty list.
+
+```
+a = [ "one" ]
+a = [ "two" ] # Error: overwriting nonempty list with a nonempty list.
+a = [] # OK
+a = [ "two" ] # OK
+```
+
+Note that execution of the build script is done without intrinsic
+knowledge of the meaning of the underlying data. This means that it
+doesn't know that `sources` is a list of file names, for example. So if
+you remove an item, it must match the literal string rather than
+specifying a different name that will resolve to the same file name.
+
+### Conditionals
+
+Conditionals look like C:
+
+```
+ if (is_linux || (is_win && target_cpu == "x86")) {
+ sources -= [ "something.cc" ]
+ } else if (...) {
+ ...
+ } else {
+ ...
+ }
+```
+
+You can use them in most places, even around entire targets if the
+target should only be declared in certain circumstances.
+
+### Looping
+
+You can iterate over a list with `foreach`. This is discouraged. Most things
+the build should do can normally be expressed without doing this, and if you
+find it necessary it may be an indication you're doing too much work in the
+metabuild.
+
+```
+foreach(i, mylist) {
+ print(i) # Note: i is a copy of each element, not a reference to it.
+}
+```
+
+### Function calls
+
+Simple function calls look like most other languages:
+
+```
+print("hello, world")
+assert(is_win, "This should only be executed on Windows")
+```
+
+Such functions are built-in and the user can not define new ones.
+
+Some functions take a block of code enclosed by `{ }` following them:
+
+```
+static_library("mylibrary") {
+ sources = [ "a.cc" ]
+}
+```
+
+Most of these define targets. The user can define new functions like this
+with the template mechanism discussed below.
+
+Precisely, this expression means that the block becomes an argument to the
+function for the function to execute. Most of the block-style functions execute
+the block and treat the resulting scope as a dictionary of variables to read.
+
+### Scoping and execution
+
+Files and function calls followed by `{ }` blocks introduce new scopes. Scopes
+are nested. When you read a variable, the containing scopes will be searched in
+reverse order until a matching name is found. Variable writes always go to the
+innermost scope.
+
+There is no way to modify any enclosing scope other than the innermost
+one. This means that when you define a target, for example, nothing you
+do inside of the block will "leak out" into the rest of the file.
+
+`if`/`else`/`foreach` statements, even though they use `{ }`, do not introduce
+a new scope so changes will persist outside of the statement.
+
+## Naming things
+
+### File and directory names
+
+File and directory names are strings and are interpreted as relative to
+the current build file's directory. There are three possible forms:
+
+Relative names:
+
+```
+"foo.cc"
+"src/foo.cc"
+"../src/foo.cc"
+```
+
+Source-tree absolute names:
+
+```
+"//net/foo.cc"
+"//base/test/foo.cc"
+```
+
+System absolute names (rare, normally used for include directories):
+
+```
+"/usr/local/include/"
+"/C:/Program Files/Windows Kits/Include"
+```
+
+## Build configuration
+
+## Targets
+
+A target is a node in the build graph. It usually represents some kind
+of executable or library file that will be generated. Targets depend on
+other targets. The built-in target types (see `gn help <targettype>` for
+more help) are:
+
+ * `action`: Run a script to generate a file.
+ * `action_foreach`: Run a script once for each source file.
+ * `bundle_data`: Declare data to go into a Mac/iOS bundle.
+ * `create_bundle`: Creates a Mac/iOS bundle.
+ * `executable`: Generates an executable file.
+ * `group`: A virtual dependency node that refers to one or more other
+ targets.
+ * `shared_library`: A .dll or .so.
+ * `loadable_module`: A .dll or .so loadable only at runtime.
+ * `source_set`: A lightweight virtual static library (usually
+ preferrable over a real static library since it will build faster).
+ * `static_library`: A .lib or .a file (normally you'll want a
+ `source_set` instead).
+
+You can extend this to make custom target types using templates (see below). In
+Chrome some of the more commonly-used templates are:
+
+ * `component`: Either a source set or shared library, depending on the
+ build type.
+ * `test`: A test executable. On mobile this will create the appropriate
+ native app type for tests.
+ * `app`: Executable or Mac/iOS application.
+ * `android_apk`: Make an APK. There are a _lot_ of other Android ones, see
+ `//build/config/android/rules.gni`.
+
+## Configs
+
+Configs are named objects that specify sets of flags, include
+directories, and defines. They can be applied to a target and pushed to
+dependent targets.
+
+To define a config:
+
+```
+config("myconfig") {
+ includes = [ "src/include" ]
+ defines = [ "ENABLE_DOOM_MELON" ]
+}
+```
+
+To apply a config to a target:
+
+```
+executable("doom_melon") {
+ configs = [ ":myconfig" ]
+}
+```
+
+It is common for the build config file to specify target defaults that
+set a default list of configs. Targets can add or remove to this list as
+needed. So in practice you would usually use `configs += ":myconfig"` to
+append to the list of defaults.
+
+See `gn help config` for more information about how configs are declared
+and applied.
+
+### Public configs
+
+A target can apply settings to other targets that depend on it. The most
+common example is a third party target that requires some defines or
+include directories for its headers to compile properly. You want these
+settings to apply both to the compile of the third party library itself,
+as well as all targets that use the library.
+
+To do this, you write a config with the settings you want to apply:
+
+```
+config("my_external_library_config") {
+ includes = "."
+ defines = [ "DISABLE_JANK" ]
+}
+```
+
+Then this config is added to the target as a "public" config. It will
+apply both to the target as well as targets that directly depend on it.
+
+```
+shared_library("my_external_library") {
+ ...
+ # Targets that depend on this get this config applied.
+ public_configs = [ ":my_external_library_config" ]
+}
+```
+
+Dependent targets can in turn forward this up the dependency tree
+another level by adding your target as a "public" dependency.
+
+```
+static_library("intermediate_library") {
+ ...
+ # Targets that depend on this one also get the configs from "my external library".
+ public_deps = [ ":my_external_library" ]
+}
+```
+
+A target can forward a config to all dependents until a link boundary is
+reached by setting it as an `all_dependent_config`. This is strongly
+discouraged as it can spray flags and defines over more of the build than
+necessary. Instead, use public_deps to control which flags apply where.
+
+In Chrome, prefer the build flag header system (`build/buildflag_header.gni`)
+for defines which prevents most screw-ups with compiler defines.
+
+## Templates
+
+Templates are GN's primary way to re-use code. Typically, a template
+would expand to one or more other target types.
+
+```
+# Declares a script that compiles IDL files to source, and then compiles those
+# source files.
+template("idl") {
+ # Always base helper targets on target_name so they're unique. Target name
+ # will be the string passed as the name when the template is invoked.
+ idl_target_name = "${target_name}_generate"
+ action_foreach(idl_target_name) {
+ ...
+ }
+
+ # Your template should always define a target with the name target_name.
+ # When other targets depend on your template invocation, this will be the
+ # destination of that dependency.
+ source_set(target_name) {
+ ...
+ deps = [ ":$idl_target_name" ] # Require the sources to be compiled.
+ }
+}
+```
+
+Typically your template definition would go in a `.gni` file and users
+would import that file to see the template definition:
+
+```
+import("//tools/idl_compiler.gni")
+
+idl("my_interfaces") {
+ sources = [ "a.idl", "b.idl" ]
+}
+```
+
+Declaring a template creates a closure around the variables in scope at
+that time. When the template is invoked, the magic variable `invoker` is
+used to read variables out of the invoking scope. The template would
+generally copy the values its interested in into its own scope:
+
+```
+template("idl") {
+ source_set(target_name) {
+ sources = invoker.sources
+ }
+}
+```
+
+The current directory when a template executes will be that of the
+invoking build file rather than the template source file. This is so
+files passed in from the template invoker will be correct (this
+generally accounts for most file handling in a template). However, if
+the template has files itself (perhaps it generates an action that runs
+a script), you will want to use absolute paths ("//foo/...") to refer to
+these files to account for the fact that the current directory will be
+unpredictable during invocation. See `gn help template` for more
+information and more complete examples.
+
+## Other features
+
+### Imports
+
+You can import `.gni` files into the current scope with the `import`
+function. This is _not_ an include in the C++ sense. The imported file is
+executed independently and the resulting scope is copied into the current file
+(C++ executes the included file in the current context of when the
+include directive appeared). This allows the results of the import to be
+cached, and also prevents some of the more "creative" uses of includes like
+multiply-included files.
+
+Typically, a `.gni` would define build arguments and templates. See `gn
+help import` for more.
+
+Your `.gni` file can define temporary variables that are not exported files
+that include it by using a preceding underscore in the name like `_this`.
+
+### Path processing
+
+Often you will want to make a file name or a list of file names relative
+to a different directory. This is especially common when running
+scripts, which are executed with the build output directory as the
+current directory, while build files usually refer to files relative to
+their containing directory.
+
+You can use `rebase_path` to convert directories. See `gn help
+rebase_path` for more help and examples. Typical usage to convert a file
+name relative to the current directory to be relative to the root build
+directory would be: ``` new_paths = rebase_path("myfile.c",
+root_build_dir) ```
+
+### Patterns
+
+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`).
+
+They are like simple regular expressions. See `gn help label_pattern`
+for more.
+
+### Executing scripts
+
+There are two ways to execute scripts. All external scripts in GN are in
+Python. The first way is as a build step. Such a script would take some
+input and generate some output as part of the build. Targets that invoke
+scripts are declared with the "action" target type (see `gn help
+action`).
+
+The second way to execute scripts is synchronously during build file
+execution. This is necessary in some cases to determine the set of files
+to compile, or to get certain system configurations that the build file
+might depend on. The build file can read the stdout of the script and
+act on it in different ways.
+
+Synchronous script execution is done by the `exec_script` function (see
+`gn help exec_script` for details and examples). Because synchronously
+executing a script requires that the current buildfile execution be
+suspended until a Python process completes execution, relying on
+external scripts is slow and should be minimized.
+
+To prevent abuse, files permitted to call `exec_script` can be whitelisted in
+the toplevel `.gn` file. Chrome does this to require additional code review
+for such additions. See `gn help dotfile`.
+
+You can synchronously read and write files which is discouraged but
+occasionally necessary when synchronously running scripts. The typical use-case
+would be to pass a list of file names longer than the command-line limits of
+the current platform. See `gn help read_file` and `gn help write_file` for how
+to read and write files. These functions should be avoided if at all possible.
+
+Actions that exceed command-line length limits can use response files to
+get around this limitation without synchronously writing files. See
+`gn help response_file_contents`.
+
+# Differences and similarities to Blaze
+
+Blaze is Google's internal build system, now publicly released as
+[Bazel](http://bazel.io/). It has inspired a number of other systems such as
+[Pants](http://www.pantsbuild.org/) and [Buck](http://facebook.github.io/buck/).
+
+In Google's homogeneous environment, the need for conditionals is very
+low and they can get by with a few hacks (`abi_deps`). Chrome uses
+conditionals all over the place and the need to add these is the main
+reason for the files looking different.
+
+GN also adds the concept of "configs" to manage some of the trickier
+dependency and configuration problems which likewise don't arise on the
+server. Blaze has a concept of a "configuration" which is like a GN
+toolchain, but built into the tool itself. The way that toolchains work
+in GN is a result of trying to separate this concept out into the build
+files in a clean way.
+
+GN keeps some GYP concept like "all dependent" settings which work a bit
+differently in Blaze. This is partially to make conversion from the existing
+GYP code easier, and the GYP constructs generally offer more fine-grained
+control (which is either good or bad, depending on the situation).
+
+GN also uses GYP names like "sources" instead of "srcs" since
+abbreviating this seems needlessly obscure, although it uses Blaze's
+"deps" since "dependencies" is so hard to type. Chromium also compiles
+multiple languages in one target so specifying the language type on the
+target name prefix was dropped (e.g. from `cc_library`).
diff --git a/gn/docs/quick_start.md b/gn/docs/quick_start.md
new file mode 100644
index 00000000000..629b36d62c9
--- /dev/null
+++ b/gn/docs/quick_start.md
@@ -0,0 +1,365 @@
+# GN Quick Start guide
+
+[TOC]
+
+## 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.
+
+## 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.
+
+To make a build directory:
+
+```
+gn gen out/my_build
+```
+
+## Passing build arguments
+
+Set build arguments on your build directory by running:
+
+```
+gn args out/my_build
+```
+
+This will bring up an editor. Type build args into that file like this:
+
+```
+is_component_build = true
+is_debug = false
+```
+
+You can see the list of available arguments and their default values by
+typing
+
+```
+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.
+
+Chrome developers can also read the [Chrome-specific build
+configuration](http://www.chromium.org/developers/gn-build-configuration)
+instructions for more information.
+
+## Cross-compiling to a target OS or architecture
+
+Run `gn args out/Default` (substituting your build directory as needed) and
+add one or more of the following lines for common cross-compiling options.
+
+```
+target_os = "chromeos"
+target_os = "android"
+
+target_cpu = "arm"
+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
+```
+
+## Step-by-step
+
+### Adding a build file
+
+Create a `tools/gn/tutorial/BUILD.gn` file and enter the following:
+
+```
+executable("hello_world") {
+ sources = [
+ "hello_world.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):
+
+```
+group("root") {
+ deps = [
+ ...
+ "//url",
+ "//tools/gn/tutorial:hello_world",
+ ]
+}
+```
+
+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:
+
+```
+gn gen out/Default
+ninja -C out/Default hello_world
+out/Default/hello_world
+```
+
+GN encourages target names for static libraries that aren't globally
+unique. To build one of these, you can pass the label with no leading
+"//" to ninja:
+
+```
+ninja -C out/Default tools/gn/tutorial:hello_world
+```
+
+### 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:
+
+```
+static_library("hello") {
+ sources = [
+ "hello.cc",
+ ]
+}
+```
+
+Now let's add an executable that depends on this library:
+
+```
+executable("say_hello") {
+ sources = [
+ "say_hello.cc",
+ ]
+ deps = [
+ ":hello",
+ ]
+}
+```
+
+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`.
+
+### Test the static library version
+
+From the command line in the source root directory:
+
+```
+ninja -C out/Default say_hello
+out/Default/say_hello
+```
+
+Note that you **didn't** need to re-run GN. GN will automatically rebuild
+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
+
+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:
+
+```
+static_library("hello") {
+ sources = [
+ "hello.cc",
+ ]
+ defines = [
+ "TWO_PEOPLE",
+ ]
+}
+```
+
+### 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:
+
+```
+config("hello_config") {
+ defines = [
+ "TWO_PEOPLE",
+ ]
+}
+```
+
+To apply these settings to your target, you only need to add the
+config's label to the list of configs in the target:
+
+```
+static_library("hello") {
+ ...
+ configs += [
+ ":hello_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).
+
+### Dependent 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:
+
+```
+static_library("hello") {
+ sources = [
+ "hello.cc",
+ ]
+ all_dependent_configs = [
+ ":hello_config"
+ ]
+}
+```
+
+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).
+
+Now if you compile and run, you'll see the new version with two people:
+
+```
+> 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.
+```
+
+## Add a new build argument
+
+You declare which arguments you accept and specify default values via
+`declare_args`.
+
+```
+declare_args() {
+ enable_teleporter = true
+ enable_doom_melon = false
+}
+```
+
+See `gn help buildargs` for an overview of how this works.
+See `gn help declare_args` for specifics on declaring them.
+
+It is an error to declare a given argument more than once in a given scope, so
+care should be used in scoping and naming arguments.
+
+## Don't know what's going on?
+
+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
+```
+
+will print out lots of exciting information. You can also print just one
+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
+...lots of other stuff omitted...
+ From //tools/gn/tutorial:hello_config
+ (Added by //tools/gn/tutorial/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:
+
+```
+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
new file mode 100644
index 00000000000..f2b84f97a19
--- /dev/null
+++ b/gn/docs/reference.md
@@ -0,0 +1,6653 @@
+# GN Reference
+
+*This page is automatically generated from* `gn help --markdown all`.
+
+## Contents
+
+* [Commands](#commands)
+ * [analyze: Analyze which targets are affected by a list of files.](#analyze)
+ * [args: Display or configure arguments declared by the build.](#args)
+ * [check: Check header dependencies.](#check)
+ * [clean: Cleans the output directory.](#clean)
+ * [desc: Show lots of insightful information about a target or config.](#desc)
+ * [format: Format .gn file.](#format)
+ * [gen: Generate ninja files.](#gen)
+ * [help: Does what you think.](#help)
+ * [ls: List matching targets.](#ls)
+ * [path: Find paths between two targets.](#path)
+ * [refs: Find stuff referencing a target or file.](#refs)
+* [Target declarations](#targets)
+ * [action: Declare a target that runs a script a single time.](#action)
+ * [action_foreach: Declare a target that runs a script over a set of files.](#action_foreach)
+ * [bundle_data: [iOS/macOS] Declare a target without output.](#bundle_data)
+ * [copy: Declare a target that copies files.](#copy)
+ * [create_bundle: [iOS/macOS] Build an iOS or macOS bundle.](#create_bundle)
+ * [executable: Declare an executable target.](#executable)
+ * [group: Declare a named group of targets.](#group)
+ * [loadable_module: Declare a loadable module target.](#loadable_module)
+ * [shared_library: Declare a shared library target.](#shared_library)
+ * [source_set: Declare a source set target.](#source_set)
+ * [static_library: Declare a static library target.](#static_library)
+ * [target: Declare an target with the given programmatic type.](#target)
+* [Buildfile functions](#functions)
+ * [assert: Assert an expression is true at generation time.](#assert)
+ * [config: Defines a configuration object.](#config)
+ * [declare_args: Declare build arguments.](#declare_args)
+ * [defined: Returns whether an identifier is defined.](#defined)
+ * [exec_script: Synchronously run a script and return the output.](#exec_script)
+ * [foreach: Iterate over a list.](#foreach)
+ * [forward_variables_from: Copies variables from a different scope.](#forward_variables_from)
+ * [get_label_info: Get an attribute from a target's label.](#get_label_info)
+ * [get_path_info: Extract parts of a file or directory name.](#get_path_info)
+ * [get_target_outputs: [file list] Get the list of outputs from a target.](#get_target_outputs)
+ * [getenv: Get an environment variable.](#getenv)
+ * [import: Import a file into the current scope.](#import)
+ * [not_needed: Mark variables from scope as not needed.](#not_needed)
+ * [pool: Defines a pool object.](#pool)
+ * [print: Prints to the console.](#print)
+ * [process_file_template: Do template expansion over a list of files.](#process_file_template)
+ * [read_file: Read a file into a variable.](#read_file)
+ * [rebase_path: Rebase a file or directory to another location.](#rebase_path)
+ * [set_default_toolchain: Sets the default toolchain name.](#set_default_toolchain)
+ * [set_defaults: Set default values for a target type.](#set_defaults)
+ * [set_sources_assignment_filter: Set a pattern to filter source files.](#set_sources_assignment_filter)
+ * [split_list: Splits a list into N different sub-lists.](#split_list)
+ * [string_replace: Replaces substring in the given string.](#string_replace)
+ * [template: Define a template rule.](#template)
+ * [tool: Specify arguments to a toolchain tool.](#tool)
+ * [toolchain: Defines a toolchain.](#toolchain)
+ * [write_file: Write a file to disk.](#write_file)
+* [Built-in predefined variables](#predefined_variables)
+ * [current_cpu: [string] The processor architecture of the current toolchain.](#current_cpu)
+ * [current_os: [string] The operating system of the current toolchain.](#current_os)
+ * [current_toolchain: [string] Label of the current toolchain.](#current_toolchain)
+ * [default_toolchain: [string] Label of the default toolchain.](#default_toolchain)
+ * [host_cpu: [string] The processor architecture that GN is running on.](#host_cpu)
+ * [host_os: [string] The operating system that GN is running on.](#host_os)
+ * [invoker: [string] The invoking scope inside a template.](#invoker)
+ * [python_path: [string] Absolute path of Python.](#python_path)
+ * [root_build_dir: [string] Directory where build commands are run.](#root_build_dir)
+ * [root_gen_dir: [string] Directory for the toolchain's generated files.](#root_gen_dir)
+ * [root_out_dir: [string] Root directory for toolchain output files.](#root_out_dir)
+ * [target_cpu: [string] The desired cpu architecture for the build.](#target_cpu)
+ * [target_gen_dir: [string] Directory for a target's generated files.](#target_gen_dir)
+ * [target_name: [string] The name of the current target.](#target_name)
+ * [target_os: [string] The desired operating system for the build.](#target_os)
+ * [target_out_dir: [string] Directory for target output files.](#target_out_dir)
+* [Variables you set in targets](#target_variables)
+ * [all_dependent_configs: [label list] Configs to be forced on dependents.](#all_dependent_configs)
+ * [allow_circular_includes_from: [label list] Permit includes from deps.](#allow_circular_includes_from)
+ * [arflags: [string list] Arguments passed to static_library archiver.](#arflags)
+ * [args: [string list] Arguments passed to an action.](#args)
+ * [asmflags: [string list] Flags passed to the assembler.](#asmflags)
+ * [assert_no_deps: [label pattern list] Ensure no deps on these targets.](#assert_no_deps)
+ * [bundle_contents_dir: Expansion of {{bundle_contents_dir}} in create_bundle.](#bundle_contents_dir)
+ * [bundle_deps_filter: [label list] A list of labels that are filtered out.](#bundle_deps_filter)
+ * [bundle_executable_dir: Expansion of {{bundle_executable_dir}} in create_bundle](#bundle_executable_dir)
+ * [bundle_plugins_dir: Expansion of {{bundle_plugins_dir}} in create_bundle.](#bundle_plugins_dir)
+ * [bundle_resources_dir: Expansion of {{bundle_resources_dir}} in create_bundle.](#bundle_resources_dir)
+ * [bundle_root_dir: Expansion of {{bundle_root_dir}} in create_bundle.](#bundle_root_dir)
+ * [cflags: [string list] Flags passed to all C compiler variants.](#cflags)
+ * [cflags_c: [string list] Flags passed to the C compiler.](#cflags_c)
+ * [cflags_cc: [string list] Flags passed to the C++ compiler.](#cflags_cc)
+ * [cflags_objc: [string list] Flags passed to the Objective C compiler.](#cflags_objc)
+ * [cflags_objcc: [string list] Flags passed to the Objective C++ compiler.](#cflags_objcc)
+ * [check_includes: [boolean] Controls whether a target's files are checked.](#check_includes)
+ * [code_signing_args: [string list] Arguments passed to code signing script.](#code_signing_args)
+ * [code_signing_outputs: [file list] Output files for code signing step.](#code_signing_outputs)
+ * [code_signing_script: [file name] Script for code signing.](#code_signing_script)
+ * [code_signing_sources: [file list] Sources for code signing step.](#code_signing_sources)
+ * [complete_static_lib: [boolean] Links all deps into a static library.](#complete_static_lib)
+ * [configs: [label list] Configs applying to this target or config.](#configs)
+ * [data: [file list] Runtime data file dependencies.](#data)
+ * [data_deps: [label list] Non-linked dependencies.](#data_deps)
+ * [defines: [string list] C preprocessor defines.](#defines)
+ * [depfile: [string] File name for input dependencies for actions.](#depfile)
+ * [deps: [label list] Private linked dependencies.](#deps)
+ * [friend: [label pattern list] Allow targets to include private headers.](#friend)
+ * [include_dirs: [directory list] Additional include directories.](#include_dirs)
+ * [inputs: [file list] Additional compile-time dependencies.](#inputs)
+ * [ldflags: [string list] Flags passed to the linker.](#ldflags)
+ * [lib_dirs: [directory list] Additional library directories.](#lib_dirs)
+ * [libs: [string list] Additional libraries to link.](#libs)
+ * [output_dir: [directory] Directory to put output file in.](#output_dir)
+ * [output_extension: [string] Value to use for the output's file extension.](#output_extension)
+ * [output_name: [string] Name for the output file other than the default.](#output_name)
+ * [output_prefix_override: [boolean] Don't use prefix for output name.](#output_prefix_override)
+ * [outputs: [file list] Output files for actions and copy targets.](#outputs)
+ * [partial_info_plist: [filename] Path plist from asset catalog compiler.](#partial_info_plist)
+ * [pool: [string] Label of the pool used by the action.](#pool)
+ * [precompiled_header: [string] Header file to precompile.](#precompiled_header)
+ * [precompiled_header_type: [string] "gcc" or "msvc".](#precompiled_header_type)
+ * [precompiled_source: [file name] Source file to precompile.](#precompiled_source)
+ * [product_type: [string] Product type for Xcode projects.](#product_type)
+ * [public: [file list] Declare public header files for a target.](#public)
+ * [public_configs: [label list] Configs applied to dependents.](#public_configs)
+ * [public_deps: [label list] Declare public dependencies.](#public_deps)
+ * [response_file_contents: [string list] Contents of .rsp file for actions.](#response_file_contents)
+ * [script: [file name] Script file for actions.](#script)
+ * [sources: [file list] Source files for a target.](#sources)
+ * [testonly: [boolean] Declares a target must only be used for testing.](#testonly)
+ * [visibility: [label list] A list of labels that can depend on a target.](#visibility)
+ * [write_runtime_deps: Writes the target's runtime_deps to the given path.](#write_runtime_deps)
+ * [xcode_extra_attributes: [scope] Extra attributes for Xcode projects.](#xcode_extra_attributes)
+ * [test_application_name: [string] Test application name for unit or ui test target.](#test_application_name)
+* [Other help topics](#other)
+ * [all: Print all the help at once](#all)
+ * [buildargs: How build arguments work.](#buildargs)
+ * [dotfile: Info about the toplevel .gn file.](#dotfile)
+ * [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.](#input_conversion)
+ * [label_pattern: Matching more than one label.](#label_pattern)
+ * [labels: About labels.](#labels)
+ * [ninja_rules: How Ninja build rules are named.](#ninja_rules)
+ * [nogncheck: Annotating includes for checking.](#nogncheck)
+ * [output_conversion: Specifies how to transform a value to output.](#output_conversion)
+ * [runtime_deps: How runtime dependency computation works.](#runtime_deps)
+ * [source_expansion: Map sources to outputs for scripts.](#source_expansion)
+ * [switches: Show available command-line switches.](#switches)
+
+## <a name="commands"></a>Commands
+
+### <a name="analyze"></a>**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.
+```
+### <a name="args"></a>**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).
+```
+### <a name="check"></a>**gn check <out_dir> [<label_pattern>] [\--force]**
+
+```
+ 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.
+```
+
+#### **What gets checked**
+
+```
+ The .gn file may specify a list of targets to be checked. Only these targets
+ will be checked if no label_pattern is specified on the command line.
+ Otherwise, the command-line list is used instead. See "gn help dotfile".
+
+ 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.
+
+ - 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.
+```
+### <a name="clean"></a>**gn clean <out_dir>**
+
+```
+ Deletes the contents of the output directory except for args.gn and
+ creates a Ninja build environment sufficient to regenerate the build.
+```
+### <a name="desc"></a>**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)
+ defines [--blame]
+ depfile
+ deps [--all] [--tree] (see below)
+ include_dirs [--blame]
+ inputs
+ ldflags [--blame]
+ lib_dirs
+ libs
+ outputs
+ public_configs
+ public
+ script
+ sources
+ testonly
+ visibility
+
+ 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
+ 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.
+
+ --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).
+ --as=(buildfile|label|output)
+ How to print targets.
+
+ buildfile
+ Prints the build files where the given target was declared as
+ file names.
+ label (default)
+ Prints the label of the target.
+ output
+ Prints the first output file for the target relative to the
+ root build directory.
+
+ --testonly=(true|false)
+ Restrict outputs to targets with the testonly flag set
+ accordingly. When unspecified, the target's testonly flags are
+ ignored.
+
+ --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.
+ --type=(action|copy|executable|group|loadable_module|shared_library|
+ source_set|static_library)
+ Restrict outputs to targets matching the given type. If
+ unspecified, no filtering will be performed.
+```
+
+#### **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.
+```
+### <a name="format"></a>**gn format [\--dump-tree] (\--stdin | <build_file>)**
+
+```
+ 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
+ For debugging, dumps the parse tree to stdout and does not update the
+ file or print formatted output.
+
+ --stdin
+ Read input from stdin and write to stdout rather than update a file
+ in-place.
+```
+
+#### **Examples**
+```
+ gn format //some/BUILD.gn
+ gn format some\\BUILD.gn
+ gn format /abspath/some/BUILD.gn
+ gn format --stdin
+```
+### <a name="gen"></a>**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.
+ "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
+ 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. This is
+ used for various Clang-based tooling, allowing for the replay of individual
+ compilations independent of the build system.
+```
+### <a name="help"></a>**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.
+```
+### <a name="ls"></a>**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**
+
+```
+ --as=(buildfile|label|output)
+ How to print targets.
+
+ buildfile
+ Prints the build files where the given target was declared as
+ file names.
+ label (default)
+ Prints the label of the target.
+ output
+ 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.
+
+ 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.
+
+ --testonly=(true|false)
+ Restrict outputs to targets with the testonly flag set
+ accordingly. When unspecified, the target's testonly flags are
+ ignored.
+
+ --type=(action|copy|executable|group|loadable_module|shared_library|
+ source_set|static_library)
+ Restrict outputs to targets matching the given type. If
+ unspecified, no filtering will be performed.
+```
+
+#### **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).
+```
+### <a name="path"></a>**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
+```
+### <a name="refs"></a>**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
+ 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.
+
+ buildfile
+ Prints the build files where the given target was declared as
+ file names.
+ label (default)
+ Prints the label of the target.
+ output
+ Prints the first output file for the target relative to the
+ root build directory.
+
+ -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
+ ignored.
+
+ --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.
+ --type=(action|copy|executable|group|loadable_module|shared_library|
+ source_set|static_library)
+ Restrict outputs to targets matching the given type. If
+ unspecified, no filtering will be performed.
+```
+
+#### **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.
+```
+## <a name="targets"></a>Target declarations
+
+### <a name="action"></a>**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.
+
+ The "deps" and "public_deps" for an action will always be
+ completed before any part of the action is run so it can depend on
+ the output of previous steps. The "data_deps" will be built if the
+ action is built, but may not have completed before all steps of the
+ action are started. This can give additional parallelism in the build
+ for runtime-only dependencies.
+```
+
+#### **Outputs**
+
+```
+ You should specify files created by your script by specifying them in the
+ "outputs".
+
+ The script will be executed with the given arguments with the current
+ directory being that of the root build directory. If you pass files
+ to your script, see "gn help rebase_path" for how to convert
+ 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).
+```
+
+#### **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.
+```
+
+#### **Variables**
+
+```
+ args, data, data_deps, depfile, deps, inputs, 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)
+ }
+```
+### <a name="action_foreach"></a>**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".
+
+ The "deps" and "public_deps" for an action will always be
+ completed before any part of the action is run so it can depend on
+ the output of previous steps. The "data_deps" will be built if the
+ action is built, but may not have completed before all steps of the
+ action are started. This can give additional parallelism in the build
+ for runtime-only dependencies.
+```
+
+#### **Outputs**
+
+```
+ The script will be executed with the given arguments with the current
+ directory being that of the root build directory. If you pass files
+ to your script, see "gn help rebase_path" for how to convert
+ 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).
+```
+
+#### **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.
+```
+
+#### **Variables**
+
+```
+ args, data, data_deps, depfile, deps, inputs, 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 is 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" ]
+ }
+```
+### <a name="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
+ 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, 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}}"
+ ]
+ }
+```
+### <a name="copy"></a>**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}}" ]
+ }
+```
+### <a name="create_bundle"></a>**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" properties must be defined. They will be used for the
+ expansion of {{bundle_*_dir}} rules in "bundle_data" outputs.
+
+ 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_plugins_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
+ * = required
+```
+
+#### **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") {
+ 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") {
+ 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
+ bundle_plugins_dir = "${bundle_contents_dir}/Plugins"
+
+ 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"
+ bundle_plugins_dir = "${bundle_contents_dir}/Plugins"
+ }
+ 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" ]
+ }
+ }
+ }
+ }
+```
+### <a name="executable"></a>**executable**: Declare an executable target.
+
+#### **Variables**
+
+```
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, output_name,
+ output_extension, public, sources, testonly, visibility
+```
+### <a name="group"></a>**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: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+```
+
+#### **Example**
+
+```
+ group("all") {
+ deps = [
+ "//project:runner",
+ "//project:unit_tests",
+ ]
+ }
+```
+### <a name="loadable_module"></a>**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.
+```
+
+#### **Variables**
+
+```
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, output_name,
+ output_extension, public, sources, testonly, visibility
+```
+### <a name="shared_library"></a>**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.
+```
+
+#### **Variables**
+
+```
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, output_name,
+ output_extension, public, sources, testonly, visibility
+```
+### <a name="source_set"></a>**source_set**: Declare a source set target.
+
+```
+ 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**
+
+```
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, output_name,
+ output_extension, public, sources, testonly, visibility
+```
+### <a name="static_library"></a>**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
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, output_name,
+ output_extension, public, sources, testonly, visibility
+```
+### <a name="target"></a>**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") {
+ ...
+ }
+```
+## <a name="functions"></a>Buildfile functions
+
+### <a name="assert"></a>**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");
+```
+### <a name="config"></a>**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".
+```
+
+#### **Variables valid in a config definition**
+```
+ Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
+ asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
+ libs, precompiled_header, precompiled_source
+ Nested configs: configs
+```
+
+#### **Variables on a target used to apply configs**
+
+```
+ all_dependent_configs, configs, public_configs
+```
+
+#### **Example**
+
+```
+ config("myconfig") {
+ includes = [ "include/common" ]
+ defines = [ "ENABLE_DOOM_MELON" ]
+ }
+
+ executable("mything") {
+ configs = [ ":myconfig" ]
+ }
+```
+### <a name="declare_args"></a>**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.
+```
+### <a name="defined"></a>**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"
+ }
+ }
+```
+### <a name="exec_script"></a>**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").
+```
+
+#### **Arguments**:
+
+```
+ filename:
+ File name of python 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 input_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")
+```
+### <a name="foreach"></a>**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
+```
+### <a name="forward_variables_from"></a>**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**
+
+```
+ # 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" ]
+ }
+ }
+```
+### <a name="get_label_info"></a>**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".
+```
+### <a name="get_path_info"></a>**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"
+```
+### <a name="get_target_outputs"></a>**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 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 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").
+
+ 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.
+```
+
+#### **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")
+ }
+```
+### <a name="getenv"></a>**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")
+```
+### <a name="import"></a>**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")
+```
+### <a name="not_needed"></a>**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" ])
+```
+### <a name="pool"></a>**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)")
+ }
+ }
+```
+### <a name="print"></a>**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)
+```
+### <a name="process_file_template"></a>**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" ]
+```
+### <a name="read_file"></a>**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 input_conversion".
+```
+
+#### **Example**
+
+```
+ lines = read_file("foo.txt", "list lines")
+```
+### <a name="rebase_path"></a>**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)
+ }
+```
+### <a name="set_default_toolchain"></a>**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")
+ }
+```
+### <a name="set_defaults"></a>**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" ]
+ }
+```
+### <a name="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="split_list"></a>**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]
+```
+### <a name="string_replace"></a>**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!
+```
+### <a name="template"></a>**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" ]
+ }
+```
+### <a name="tool"></a>**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.
+```
+
+#### **Tool variables**
+
+```
+ command [string with substitutions]
+ Valid for: all tools except "action" (required)
+
+ The command to run.
+
+ 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}}"
+
+ 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.
+```
+
+#### **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"
+
+ 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.
+```
+
+#### **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}}"
+ }
+ };
+```
+### <a name="toolchain"></a>**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") {
+ ...
+ }
+ }
+```
+### <a name="write_file"></a>**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 output_conversion".
+```
+## <a name="predefined_variables"></a>Built-in predefined variables
+
+### <a name="current_cpu"></a>**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.
+```
+### <a name="current_os"></a>**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.
+```
+### <a name="current_toolchain"></a>**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") {
+ ...
+```
+### <a name="default_toolchain"></a>**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").
+```
+### <a name="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
+ 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"
+```
+### <a name="host_os"></a>**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"
+```
+### <a name="invoker"></a>**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
+ }
+```
+### <a name="python_path"></a>**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.
+```
+### <a name="root_build_dir"></a>**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.
+```
+### <a name="root_gen_dir"></a>**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.
+```
+### <a name="root_out_dir"></a>**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) ]
+ }
+```
+### <a name="target_cpu"></a>**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"
+```
+### <a name="target_gen_dir"></a>**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) ]"
+ }
+```
+### <a name="target_name"></a>**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") {
+ }
+```
+### <a name="target_os"></a>**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"
+```
+### <a name="target_out_dir"></a>**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) ]"
+
+ }
+```
+## <a name="target_variables"></a>Variables you set in targets
+
+### <a name="all_dependent_configs"></a>**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".
+```
+
+#### **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 occurence 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="allow_circular_includes_from"></a>**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" ]
+ }
+```
+### <a name="arflags"></a>**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.
+```
+
+#### **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 occurence 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="args"></a>**args**: 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".
+```
+### <a name="asmflags"></a>**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.
+```
+
+#### **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 occurence 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="assert_no_deps"></a>**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.
+ ]
+ }
+```
+### <a name="bundle_contents_dir"></a>**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.
+```
+### <a name="bundle_deps_filter"></a>**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",
+ ]
+ }
+```
+### <a name="bundle_executable_dir"></a>**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.
+```
+### <a name="bundle_plugins_dir"></a>**bundle_plugins_dir**: Expansion of {{bundle_plugins_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_plugins_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.
+```
+### <a name="bundle_resources_dir"></a>**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.
+```
+### <a name="bundle_root_dir"></a>**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"
+ bundle_plugins_dir = "${bundle_contents_dir}/PlugIns"
+ }
+```
+### <a name="cflags*"></a>**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.
+```
+
+#### **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 occurence 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="cflags*"></a>**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.
+```
+
+#### **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 occurence 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="cflags*"></a>**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.
+```
+
+#### **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 occurence 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="cflags*"></a>**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.
+```
+
+#### **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 occurence 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="cflags*"></a>**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.
+```
+
+#### **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 occurence 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="check_includes"></a>**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
+ ...
+ }
+```
+### <a name="code_signing_args"></a>**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".
+```
+### <a name="code_signing_outputs"></a>**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".
+```
+### <a name="code_signing_script"></a>**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".
+```
+### <a name="code_signing_sources"></a>**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".
+```
+### <a name="complete_static_lib"></a>**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" ]
+ }
+```
+### <a name="configs"></a>**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.)
+```
+
+#### **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 occurence 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**
+
+```
+ # 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" ]
+ }
+ }
+```
+### <a name="data"></a>**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.
+```
+### <a name="data_deps"></a>**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" ]
+ }
+```
+### <a name="defines"></a>**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.
+```
+
+#### **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 occurence 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**
+
+```
+ defines = [ "AWESOME_FEATURE", "LOG_LEVEL=3" ]
+```
+### <a name="depfile"></a>**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 ]
+ }
+```
+### <a name="deps"></a>**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".
+```
+### <a name="friend"></a>**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.
+ ]
+ }
+```
+### <a name="include_dirs"></a>**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.
+```
+
+#### **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 occurence 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**
+
+```
+ include_dirs = [ "src/include", "//third_party/foo" ]
+```
+### <a name="inputs"></a>**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" ]
+ }
+```
+### <a name="ldflags"></a>**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.
+```
+
+#### **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 occurence 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="lib_dirs"></a>**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.
+
+ libs and lib_dirs work differently than other flags in two respects.
+ First, then are inherited across static library boundaries until a
+ shared library or executable target is reached. Second, they are
+ uniquified so each one is only passed once (the first instance of it
+ will be the one used).
+```
+
+#### **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 occurence 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.
+
+ For "libs" and "lib_dirs" only, the values propagated from
+ dependencies (as described above) are applied last assuming they
+ are not already in the list.
+```
+
+#### **Example**
+
+```
+ lib_dirs = [ "/usr/lib/foo", "lib/doom_melon" ]
+```
+### <a name="libs"></a>**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.
+
+ libs and lib_dirs work differently than other flags in two respects.
+ First, then are inherited across static library boundaries until a
+ shared library or executable target is reached. Second, they are
+ uniquified so each one is only passed once (the first instance of it
+ will be the one used).
+```
+
+#### **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.
+```
+
+#### **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 occurence 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.
+
+ For "libs" and "lib_dirs" only, the values propagated from
+ dependencies (as described above) are applied last assuming they
+ are not already in the list.
+```
+
+#### **Examples**
+
+```
+ On Windows:
+ libs = [ "ctl3d.lib" ]
+
+ On Linux:
+ libs = [ "ld" ]
+```
+### <a name="output_dir"></a>**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"
+ ...
+ }
+```
+### <a name="output_extension"></a>**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"
+ ...
+ }
+ }
+```
+### <a name="output_name"></a>**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"
+ }
+```
+### <a name="output_prefix_override"></a>**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
+ ...
+ }
+```
+### <a name="outputs"></a>**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".
+```
+### <a name="partial_info_plist"></a>**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".
+```
+### <a name="pool"></a>**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"
+ ...
+ }
+```
+### <a name="precompiled_header"></a>**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" ]
+ ...
+```
+### <a name="precompiled_header_type"></a>**precompiled_header_type**: [string] "gcc" or "msvc".
+
+```
+ See "gn help precompiled_header".
+```
+### <a name="precompiled_source"></a>**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".
+```
+### <a name="product_type"></a>**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.
+```
+### <a name="public"></a>**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 = []
+```
+### <a name="public_configs"></a>**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" ]
+ ...
+ }
+```
+
+#### **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 occurence 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="public_deps"></a>**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" ]
+ }
+```
+### <a name="response_file_contents"></a>**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}}",
+ ]
+ }
+```
+### <a name="script"></a>**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").
+```
+### <a name="sources"></a>**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).
+```
+
+#### **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.
+```
+### <a name="testonly"></a>**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
+ ...
+ }
+```
+### <a name="visibility"></a>**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/*" ]
+```
+### <a name="write_runtime_deps"></a>**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").
+```
+### <a name="xcode_extra_attributes"></a>**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.
+```
+### <a name="test_application_name"></a>**test_application_name**: Test application name for unit or ui 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"
+ ...
+ }
+```
+## <a name="other"></a>Other help topics
+
+### <a name="buildargs"></a>**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.
+```
+### <a name="dotfile"></a>**.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.
+
+ 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
+ }
+```
+### <a name="execution"></a>**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.
+```
+### <a name="grammar"></a>**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 ]
+```
+### <a name="input_conversion"></a>**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.
+```
+### <a name="label_pattern"></a>**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.
+```
+### <a name="labels"></a>**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
+```
+### <a name="ninja_rules"></a>**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.
+```
+### <a name="nogncheck"></a>**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".
+```
+### <a name="output_conversion"></a>**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.
+```
+### <a name="runtime_deps"></a>**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.
+```
+### <a name="source_expansion"></a>**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 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
+```
+## <a name="switches"></a>Command Line Switches
+
+**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.
+
+```
+ * [--args: Specifies build arguments overrides.](#--args)
+ * [--color: Force colored output.](#--color)
+ * [--dotfile: Override the name of the ".gn" file.](#--dotfile)
+ * [--fail-on-unused-args: Treat unused build args as fatal errors.](#--fail-on-unused-args)
+ * [--markdown: Write help output in the Markdown format.](#--markdown)
+ * [--nocolor: Force non-colored output.](#--nocolor)
+ * [-q: Quiet mode. Don't print output on success.](#-q)
+ * [--root: Explicitly specify source root.](#--root)
+ * [--runtime-deps-list-file: Save runtime dependencies for targets in file.](#--runtime-deps-list-file)
+ * [--script-executable: Set the executable used to execute scripts.](#--script-executable)
+ * [--threads: Specify number of worker threads.](#--threads)
+ * [--time: Outputs a summary of how long everything took.](#--time)
+ * [--tracelog: Writes a Chrome-compatible trace log to the given file.](#--tracelog)
+ * [-v: Verbose logging.](#-v)
+ * [--version: Prints the GN version number and exits.](#--version)
+```
+
diff --git a/gn/docs/standalone.md b/gn/docs/standalone.md
new file mode 100644
index 00000000000..29d62360f7f
--- /dev/null
+++ b/gn/docs/standalone.md
@@ -0,0 +1,44 @@
+# Introduction
+
+This page is about how to design a project that can build independently
+with GN but also be brought into the Chrome build.
+
+GN is in principle no different than GYP in that there is some core
+configuration that must be the same between both the standalone build
+and the Chrome build. However, GN is much more explicit in its naming
+and configuration, so the similarities between the two builds are also
+much more explicit and there is less flexibility in how things are
+configured.
+
+# What you need for a minimal GN build
+
+Requirements:
+
+ * A master build config file. Chrome's is `//build/config/BUILDCONFIG.gn`
+ * A separate build file for the toolchain definition. It's not a good idea
+ to put these in a BUILD.gn file shared with any target definitions for
+ complex reasons. Chrome's are in `//build/toolchain/<platform>/BUILD.gn`.
+ * A `BUILD.gn` file in the root directory. This will be loaded after the
+ build config file to start the build.
+
+You may want a `.gn` file in the root directory. When you run GN it
+recursively looks up the directory tree until it finds this file, and it
+treats the containing directory as the "source root". This file also
+defines the location of the master build config file:
+
+ * See Chrome's `src/.gn` file.
+ * Unlike Chrome, you probably don't need to define a secondary root.
+ * see `gn help dotfile` for more.
+
+Adding a `.gn` file in a repository that is pulled into Chrome means
+that then running GN in your subdirectory will configure a build for
+your subproject rather than for all of Chrome. This could be an
+advantage or a disadvantage.
+
+If you are in a directory with such a file and you want to not use it
+(e.g., to do the full Chrome build instead), you can use the command-line
+flags `--root` and `--dotfile` to set the values you want.
+
+If you want a completely standalone build that has nothing to do with Chrome
+and doesn't use Chrome's `//build` files, you can look at an example in
+[//tools/gn/example](../example).
diff --git a/gn/docs/style_guide.md b/gn/docs/style_guide.md
new file mode 100644
index 00000000000..466f8727e0d
--- /dev/null
+++ b/gn/docs/style_guide.md
@@ -0,0 +1,286 @@
+# GN Style Guide
+
+[TOC]
+
+## Naming and ordering within the file
+
+### Location of build files
+
+It usually makes sense to have more build files closer to the code than
+fewer ones at the top level; this is in contrast with what we did with
+GYP. This makes things easier to find, and also makes the set of owners
+required for reviews smaller since changes are more focused to particular
+subdirectories.
+
+### Targets
+
+ * Most BUILD files should have a target with the same name as the
+ directory. This target should be the first target.
+ * Other targets should be in some logical order -- usually
+ more important targets will be first, and unit tests will follow the
+ corresponding target. If there's no clear ordering, consider
+ alphabetical order.
+ * Test support libraries should be static libraries named "test\_support".
+ For example, "//ui/compositor:test\_support". Test support libraries should
+ include as public deps the non-test-support version of the library
+ so tests need only depend on the test\_support target (rather than
+ both).
+
+Naming advice
+
+ * Targets and configs should be named using lowercase with underscores
+ separating words, unless there is a strong reason to do otherwise.
+ * Source sets, groups, and static libraries do not need globally unique names.
+ Prefer to give such targets short, non-redundant names without worrying
+ about global uniqueness. For example, it looks much better to write a
+ dependency as `"//mojo/public/bindings"` rather than
+ `"//mojo/public/bindings:mojo_bindings"
+ * Shared libraries (and by extension, components) must have globally unique
+ output names. Give such targets short non-unique names above, and then
+ provide a globally unique `output_name` for that target.
+ * Executables and tests should be given a globally unique name. Technically
+ only the output names must be unique, but since only the output names
+ appear in the shell and on bots, it's much less confusing if the name
+ matches the other places the executable appears.
+
+### Configs
+
+ * A config associated with a single target should be named the same as
+ the target with `_config` following it.
+ * A config should appear immediately before the corresponding target
+ that uses it.
+
+### Example
+
+Example for the `src/foo/BUILD.gn` file:
+
+```
+# 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.
+
+# Config for foo is named foo_config and immediately precedes it in the file.
+config("foo_config") {
+}
+
+# Target matching path name is the first target.
+executable("foo") {
+}
+
+# Test for foo follows it.
+test("foo_unittests") {
+}
+
+config("bar_config") {
+}
+
+source_set("bar") {
+}
+```
+
+## Ordering within a target
+
+ 1. `output_name` / `visibility` / `testonly`
+ 2. `sources`
+ 3. `cflags`, `include_dirs`, `defines`, `configs` etc. in whatever
+ order makes sense to you.
+ 4. `public_deps`
+ 5. `deps`
+
+### Conditions
+
+Simple conditions affecting just one variable (e.g. adding a single
+source or adding a flag for one particular OS) can go beneath the
+variable they affect. More complicated conditions affecting more than
+one thing should go at the bottom.
+
+Conditions should be written to minimize the number of conditional blocks.
+
+## Formatting and indenting
+
+GN contains a built-in code formatter which defines the formatting style.
+Some additional notes:
+
+ * Variables are `lower_case_with_underscores`.
+ * Comments should be complete sentences with periods at the end.
+ * Compiler flags and such should always be commented with what they do
+ and why the flag is needed.
+
+### Sources
+
+Prefer to list sources only once. It is OK to conditionally include sources
+rather than listing them all at the top and then conditionally excluding them
+when they don't apply. Conditional inclusion is often clearer since a file is
+only listed once and it's easier to reason about when reading.
+
+```
+ sources = [
+ "main.cc",
+ ]
+ if (use_aura) {
+ sources += [ "thing_aura.cc" ]
+ }
+ if (use_gtk) {
+ sources += [ "thing_gtk.cc" ]
+ }
+```
+
+### Deps
+
+ * Deps should be in alphabetical order.
+ * Deps within the current file should be written first and not
+ qualified with the file name (just `:foo`).
+ * Other deps should always use fully-qualified path names unless
+ relative ones are required for some reason.
+
+```
+ deps = [
+ ":a_thing",
+ ":mystatic",
+ "//foo/bar:other_thing",
+ "//foo/baz:that_thing",
+ ]
+```
+
+### Import
+
+Use fully-qualified paths for imports:
+
+```
+import("//foo/bar/baz.gni") # Even if this file is in the foo/bar directory
+```
+
+## Usage
+
+### Source sets versus static libraries
+
+Source sets and static libraries can be used interchangeably in most cases. If
+you're unsure what to use, a source set is almost never wrong and is less likely
+to cause problems.
+
+Static libraries follow different linking rules. When a static library is
+included in a link, only the object files that contain unresolved symbols will
+be brought into the build. Source sets result in every object file being added
+to the link line of the final binary.
+
+ * If you're eventually linking code into a component, shared library, or
+ loadable module, you normally need to use source sets. This is because
+ object files with no symbols referenced from within the shared library will
+ not be linked into the final library at all. This omission will happen even
+ if that object file has a symbol marked for export that targets dependent
+ on that shared library need. This will result in undefined symbols when
+ linking later targets.
+
+ * Unit tests (and anything else with static initializers with side effects)
+ must use source sets. The gtest TEST macros create static initializers
+ that register the test. But since no code references symbols in the object
+ file, linking a test into a static library and then into a test executable
+ means the tests will get stripped.
+
+ * Static libraries involve duplicating all of the data in the object files
+ that comprise it. This takes more disk space and for certain very large
+ libraries in configurations with very large object files can cause
+ internal limits on the size of static libraries to be exceeded. Source
+ sets do not have this limitation. Some targets switch between source sets
+ and static libraries depending on the build configuration to avoid this
+ problem.
+
+ * Source sets can have no sources, while static libraries will give strange
+ platform-specific errors if they have no sources. If a target has only
+ headers (for include checking purposes) or conditionally has no sources on
+ sone platforms, use a source set.
+
+ * In cases where a lot of the symbols are not needed for a particular link
+ (this especially happens when linking test binaries), putting that code in
+ a static library can dramatically increase linking performance. This is
+ because the object files not needed for the link are never considered in
+ the first place, rather than forcing the linker to strip the unused code
+ in a later pass when nothing references it.
+
+### Loadable modules versus shared libraries versus components
+
+A component is a Chrome primitive (rather than a built-in GN concept) that
+expands either to a shared library or a static library / source set depending
+on the value of the `is_component_build` variable. This allows release builds
+to be linked statically in a large binary, but for developers to use shared
+libraries for most operations.
+
+A shared library will be listed on the link line of dependent targets and will
+be loaded automatically by the operating system when the application starts
+and symbols automatically resolved. A loadable module will not be linked
+directly and the application must manually load it.
+
+On Windows and Linux shared libraries and loadable modules result in the same
+type of file (`.dll` and `.so`, respectively). The only difference is in how
+they are linked to dependent targets. On these platforms, having a `deps`
+dependency on a loadable module is the same as having a `data_deps`
+(non-linked) dependency on a shared library.
+
+On Mac, these targets have different formats: a shared library will generate a
+`.dylib` file and a loadable module will generate a `.so` file.
+
+Use loadable modules for things like plugins. Shared libraries should be
+seldom-used outside of components because most Chrome code is shipped to the
+end-user as a small number of large binaries. In the case of plugin-like
+libraries, it's good practice to use both a loadable module for the target type
+(even for platforms where it doesn't matter) and data deps for targets that
+depend on it so it's clear from both places that how the library will be linked
+and loaded.
+
+## Build arguments
+
+### Scope
+
+Build arguments should be scoped to a unit of behavior, e.g. enabling a feature.
+Typically an argument would be declared in an imported file to share it with
+the subset of the build that could make use of it.
+
+Chrome has many legacy flags in `//build/config/features.gni`,
+`//build/config/ui.gni`. These locations are deprecated. Feature flags should
+go along with the code for the feature. Many browser-level features can go
+somewhere in `//chrome/` without lower-level code knowing about it. Some
+UI environment flags can go into `//ui/`, and many flags can also go with
+the corresponding code in `//components/`. You can write a `.gni` file in
+components and have build files in chrome or content import it if necessary.
+
+The way to think about things in the `//build` directory is that this is
+DEPSed into various projects like V8 and WebRTC. Build flags specific to
+code outside of the build directory shouldn't be in the build directory, and
+V8 shouldn't get feature defines for Chrome features.
+
+New feature defines should use the buildflag system. See
+`//build/buildflag_header.gni` which allows preprocessor defines to be
+modularized without many of the disadvantages that made us use global defines
+in the past.
+
+### Type
+
+Arguments support all the [GN language types](language.md#Language).
+
+In the vast majority of cases `boolean` is the preferred type, since most
+arguments are enabling or disabling features or includes.
+
+`String`s are typically used for filepaths. They are also used for enumerated
+types, though `integer`s are sometimes used as well.
+
+### Naming conventions
+
+While there are no hard and fast rules around argument naming there are
+many common conventions. If you ever want to see the current list of argument
+names and default values for your current checkout use
+`gn args out/Debug --list --short`.
+
+`use_foo` - indicates dependencies or major codepaths to include (e.g.
+`use_open_ssl`, `use_ozone`, `use_cups`)
+
+`enable_foo` - indicates feature or tools to be enabled (e.g.
+`enable_google_now`, `enable_nacl`, `enable_remoting`, `enable_pdf`)
+
+`disable_foo` - _NOT_ recommended, use `enable_foo` instead with swapped default
+value
+
+`is_foo` - usually a global state descriptor (e.g. `is_chrome_branded`,
+`is_desktop_linux`); poor choice for non-globals
+
+`foo_use_bar` - prefixes can be used to indicate a limited scope for an argument
+(e.g. `rtc_use_h264`, `v8_use_snapshot`)
diff --git a/gn/docs/update_binaries.md b/gn/docs/update_binaries.md
new file mode 100644
index 00000000000..d3e3bbce7cc
--- /dev/null
+++ b/gn/docs/update_binaries.md
@@ -0,0 +1,5 @@
+# How to update the GN binaries that Chromium uses.
+
+Any committer should be able to do a roll by running
+[//tools/gn/bin/roll_gn.py](../bin/roll_gn.py) on Linux or Mac.
+
diff --git a/gn/infra/README.recipes.md b/gn/infra/README.recipes.md
new file mode 100644
index 00000000000..46036eee22d
--- /dev/null
+++ b/gn/infra/README.recipes.md
@@ -0,0 +1,60 @@
+<!--- AUTOGENERATED BY `./recipes.py test train` -->
+# Package documentation for [gn]()
+## Table of Contents
+
+**[Recipe Modules](#Recipe-Modules)**
+ * [windows_sdk](#recipe_modules-windows_sdk)
+
+**[Recipes](#Recipes)**
+ * [gn](#recipes-gn) &mdash; Recipe for building GN.
+ * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
+## Recipe Modules
+
+### *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]
+
+#### **class [WindowsSDKApi](/infra/recipe_modules/windows_sdk/api.py#10)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+
+API for using Windows SDK distributed via CIPD.
+
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/windows_sdk/api.py#18)(self, path=None, version=None, enabled=True):**
+
+Setups the SDK environment when enabled.
+
+Args:
+ path (path): Path to a directory where to install the SDK
+ (default is '[start_dir]/cipd/windows_sdk')
+ version (str): CIPD instance ID, tag or ref of the SDK
+ (default is set via $infra/windows_sdk.version property)
+ enabled (bool): Whether the SDK should be used or not.
+
+Raises:
+ StepFailure or InfraFailure.
+## Recipes
+
+### *recipes* / [gn](/infra/recipes/gn.py)
+
+[DEPS](/infra/recipes/gn.py#8): [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#28)(api, repository):**
+### *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]
+
+&mdash; **def [RunSteps](/infra/recipe_modules/windows_sdk/examples/full.py#13)(api):**
+
+[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-buildbucket
+[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-cipd
+[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-context
+[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-file
+[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-json
+[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-path
+[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-platform
+[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-properties
+[recipe_engine/recipe_modules/python]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-python
+[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-raw_io
+[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-step
+[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/recipe_engine/recipe_api.py#1006
diff --git a/gn/infra/config/cq.cfg b/gn/infra/config/cq.cfg
new file mode 100644
index 00000000000..bb3a274fd1d
--- /dev/null
+++ b/gn/infra/config/cq.cfg
@@ -0,0 +1,27 @@
+# See http://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
+# documentation of this file format.
+
+version: 1
+cq_name: "gn"
+git_repo_url: "https://gn.googlesource.com/gn"
+gerrit {}
+verifiers {
+ try_job {
+ buckets {
+ name: "luci.gn.try",
+ builders {
+ name: "linux"
+ }
+ builders {
+ name: "mac"
+ }
+ builders {
+ name: "win"
+ }
+ }
+ }
+ gerrit_cq_ability {
+ committer_list: "project-gn-committers"
+ dry_run_access_list: "project-gn-tryjob-access"
+ }
+}
diff --git a/gn/infra/config/recipes.cfg b/gn/infra/config/recipes.cfg
new file mode 100644
index 00000000000..70e68920ff2
--- /dev/null
+++ b/gn/infra/config/recipes.cfg
@@ -0,0 +1,12 @@
+{
+ "api_version": 2,
+ "deps": {
+ "recipe_engine": {
+ "branch": "master",
+ "revision": "82e233fa4e6fed0a5dd99501e0d52d3dce25c994",
+ "url": "https://chromium.googlesource.com/infra/luci/recipes-py"
+ }
+ },
+ "project_id": "gn",
+ "recipes_path": "infra"
+}
diff --git a/gn/infra/config/refs.cfg b/gn/infra/config/refs.cfg
new file mode 100644
index 00000000000..465672fcb15
--- /dev/null
+++ b/gn/infra/config/refs.cfg
@@ -0,0 +1,8 @@
+# See RefsCfg message in
+# http://luci-config.appspot.com/schemas/projects:refs.cfg for the
+# documentation of this file format.
+
+refs {
+ name: "refs/heads/master"
+ config_path: "infra/config"
+}
diff --git a/gn/infra/recipe_modules/windows_sdk/__init__.py b/gn/infra/recipe_modules/windows_sdk/__init__.py
new file mode 100644
index 00000000000..6183f2b5b7d
--- /dev/null
+++ b/gn/infra/recipe_modules/windows_sdk/__init__.py
@@ -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.
+
+DEPS = [
+ 'recipe_engine/cipd',
+ 'recipe_engine/context',
+ 'recipe_engine/json',
+ 'recipe_engine/path',
+ 'recipe_engine/platform',
+ 'recipe_engine/step',
+]
+
+from recipe_engine.recipe_api import Property
+from recipe_engine.config import ConfigGroup, Single
+
+PROPERTIES = {
+ '$gn/windows_sdk':
+ Property(
+ help='Properties specifically for the infra windows_sdk module.',
+ param_name='sdk_properties',
+ kind=ConfigGroup(
+ # CIPD instance ID, tag or ref for the Windows SDK version.
+ version=Single(str),),
+ default={'version': 'uploaded:2018-06-13'},
+ )
+}
diff --git a/gn/infra/recipe_modules/windows_sdk/api.py b/gn/infra/recipe_modules/windows_sdk/api.py
new file mode 100644
index 00000000000..7c4067deff7
--- /dev/null
+++ b/gn/infra/recipe_modules/windows_sdk/api.py
@@ -0,0 +1,113 @@
+# 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 contextlib import contextmanager
+
+from recipe_engine import recipe_api
+
+
+class WindowsSDKApi(recipe_api.RecipeApi):
+ """API for using Windows SDK distributed via CIPD."""
+
+ def __init__(self, sdk_properties, *args, **kwargs):
+ super(WindowsSDKApi, self).__init__(*args, **kwargs)
+
+ self._sdk_properties = sdk_properties
+
+ @contextmanager
+ def __call__(self, path=None, version=None, enabled=True):
+ """Setups the SDK environment when enabled.
+
+ Args:
+ path (path): Path to a directory where to install the SDK
+ (default is '[start_dir]/cipd/windows_sdk')
+ version (str): CIPD instance ID, tag or ref of the SDK
+ (default is set via $infra/windows_sdk.version property)
+ enabled (bool): Whether the SDK should be used or not.
+
+ Raises:
+ StepFailure or InfraFailure.
+ """
+ if enabled:
+ sdk_dir = self._ensure_sdk(
+ path or self.m.path['start_dir'].join('cipd', 'windows_sdk'),
+ version or self._sdk_properties['version'])
+ try:
+ with self.m.context(**self._sdk_env(sdk_dir)):
+ yield
+ finally:
+ if self.m.platform.is_win:
+ # cl.exe automatically starts background mspdbsrv.exe daemon which
+ # needs to be manually stopped so Swarming can tidy up after itself.
+ self.m.step('taskkill mspdbsrv',
+ ['taskkill.exe', '/f', '/t', '/im', 'mspdbsrv.exe'])
+ else:
+ yield
+
+ def _ensure_sdk(self, sdk_dir, sdk_version):
+ """Ensures the Windows SDK CIPD package is installed.
+
+ Returns the directory where the SDK package has been installed.
+
+ Args:
+ path (path): Path to a directory.
+ version (str): CIPD instance ID, tag or ref.
+ """
+ with self.m.context(infra_steps=True):
+ pkgs = self.m.cipd.EnsureFile()
+ pkgs.add_package('chrome_internal/third_party/sdk/windows', sdk_version)
+ self.m.cipd.ensure(sdk_dir, pkgs)
+ return sdk_dir
+
+ def _sdk_env(self, sdk_dir):
+ """Constructs the environment for the SDK.
+
+ Returns environment and environment prefixes.
+
+ Args:
+ sdk_dir (path): Path to a directory containing the SDK.
+ """
+ env = {}
+ env_prefixes = {}
+
+ # Load .../win_sdk/bin/SetEnv.${arch}.json to extract the required
+ # environment. It contains a dict that looks like this:
+ # {
+ # "env": {
+ # "VAR": [["..", "..", "x"], ["..", "..", "y"]],
+ # ...
+ # }
+ # }
+ # All these environment variables need to be added to the environment
+ # for the compiler and linker to work.
+ filename = 'SetEnv.%s.json' % {32: 'x86', 64: 'x64'}[self.m.platform.bits]
+ step_result = self.m.json.read(
+ 'read %s' % filename,
+ sdk_dir.join('win_sdk', 'bin', filename),
+ step_test_data=lambda: self.m.json.test_api.output({
+ 'env': {
+ 'PATH': [['..', '..', 'win_sdk', 'bin', 'x64']],
+ 'VSINSTALLDIR': [['..', '..\\']],},}))
+ data = step_result.json.output.get('env')
+ for key in data:
+ # recipes' Path() does not like .., ., \, or /, so this is cumbersome.
+ # What we want to do is:
+ # [sdk_bin_dir.join(*e) for e in env[k]]
+ # Instead do that badly, and rely (but verify) on the fact that the paths
+ # are all specified relative to the root, but specified relative to
+ # win_sdk/bin (i.e. everything starts with "../../".)
+ results = []
+ for value in data[key]:
+ assert value[0] == '..' and (value[1] == '..' or value[1] == '..\\')
+ results.append('%s' % sdk_dir.join(*value[2:]))
+
+ # PATH is special-cased because we don't want to overwrite other things
+ # like C:\Windows\System32. Others are replacements because prepending
+ # doesn't necessarily makes sense, like VSINSTALLDIR.
+ if key.lower() == 'path':
+ env_prefixes[key] = results
+ else:
+ env[key] = ';'.join(results)
+
+ return {'env': env, 'env_prefixes': env_prefixes}
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
new file mode 100644
index 00000000000..51d94309ff2
--- /dev/null
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
@@ -0,0 +1,23 @@
+[
+ {
+ "cmd": [
+ "gn",
+ "gen",
+ "out/Release"
+ ],
+ "name": "gn"
+ },
+ {
+ "cmd": [
+ "ninja",
+ "-C",
+ "out/Release"
+ ],
+ "name": "ninja"
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
new file mode 100644
index 00000000000..51d94309ff2
--- /dev/null
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
@@ -0,0 +1,23 @@
+[
+ {
+ "cmd": [
+ "gn",
+ "gen",
+ "out/Release"
+ ],
+ "name": "gn"
+ },
+ {
+ "cmd": [
+ "ninja",
+ "-C",
+ "out/Release"
+ ],
+ "name": "ninja"
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
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
new file mode 100644
index 00000000000..d9a7a658e79
--- /dev/null
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -0,0 +1,108 @@
+[
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd\\windows_sdk",
+ "-ensure-file",
+ "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+ "/path/to/tmp/json"
+ ],
+ "name": "read SetEnv.x64.json",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"env\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"PATH\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"win_sdk\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"bin\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"x64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"VSINSTALLDIR\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\\\\\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "gn",
+ "gen",
+ "out/Release"
+ ],
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "gn"
+ },
+ {
+ "cmd": [
+ "ninja",
+ "-C",
+ "out/Release"
+ ],
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "ninja"
+ },
+ {
+ "cmd": [
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
+ ],
+ "name": "taskkill mspdbsrv"
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.py b/gn/infra/recipe_modules/windows_sdk/examples/full.py
new file mode 100644
index 00000000000..5e8e503eb1a
--- /dev/null
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.py
@@ -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.
+
+DEPS = [
+ 'windows_sdk',
+ 'recipe_engine/platform',
+ 'recipe_engine/properties',
+ 'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+ with api.windows_sdk(enabled=api.platform.is_win):
+ api.step('gn', ['gn', 'gen', 'out/Release'])
+ api.step('ninja', ['ninja', '-C', 'out/Release'])
+
+
+def GenTests(api):
+ for platform in ('linux', 'mac', 'win'):
+ properties = {
+ 'buildername': 'test_builder',
+ }
+ yield (api.test(platform) + api.platform.name(platform) +
+ api.properties.generic(**properties))
diff --git a/gn/infra/recipes.py b/gn/infra/recipes.py
new file mode 100755
index 00000000000..d726c689795
--- /dev/null
+++ b/gn/infra/recipes.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+
+# Copyright 2017 The LUCI Authors. All rights reserved.
+# Use of this source code is governed under the Apache License, Version 2.0
+# that can be found in the LICENSE file.
+
+"""Bootstrap script to clone and forward to the recipe engine tool.
+
+*******************
+** DO NOT MODIFY **
+*******************
+
+This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/master/doc/recipes.py.
+To fix bugs, fix in the googlesource repo then run the autoroller.
+"""
+
+import argparse
+import json
+import logging
+import os
+import random
+import subprocess
+import sys
+import time
+import urlparse
+
+from collections import namedtuple
+
+from cStringIO import StringIO
+
+# The dependency entry for the recipe_engine in the client repo's recipes.cfg
+#
+# url (str) - the url to the engine repo we want to use.
+# revision (str) - the git revision for the engine to get.
+# path_override (str) - the subdirectory in the engine repo we should use to
+# find it's recipes.py entrypoint. This is here for completeness, but will
+# essentially always be empty. It would be used if the recipes-py repo was
+# merged as a subdirectory of some other repo and you depended on that
+# subdirectory.
+# branch (str) - the branch to fetch for the engine as an absolute ref (e.g.
+# refs/heads/master)
+# repo_type ("GIT"|"GITILES") - An ignored enum which will be removed soon.
+EngineDep = namedtuple('EngineDep',
+ 'url revision path_override branch repo_type')
+
+
+class MalformedRecipesCfg(Exception):
+ def __init__(self, msg, path):
+ super(MalformedRecipesCfg, self).__init__('malformed recipes.cfg: %s: %r'
+ % (msg, path))
+
+
+def parse(repo_root, recipes_cfg_path):
+ """Parse is a lightweight a recipes.cfg file parser.
+
+ Args:
+ repo_root (str) - native path to the root of the repo we're trying to run
+ recipes for.
+ recipes_cfg_path (str) - native path to the recipes.cfg file to process.
+
+ Returns (as tuple):
+ engine_dep (EngineDep|None): The recipe_engine dependency, or None, if the
+ current repo IS the recipe_engine.
+ recipes_path (str) - native path to where the recipes live inside of the
+ current repo (i.e. the folder containing `recipes/` and/or
+ `recipe_modules`)
+ """
+ with open(recipes_cfg_path, 'rU') as fh:
+ pb = json.load(fh)
+
+ try:
+ if pb['api_version'] != 2:
+ raise MalformedRecipesCfg('unknown version %d' % pb['api_version'],
+ recipes_cfg_path)
+
+ # If we're running ./doc/recipes.py from the recipe_engine repo itself, then
+ # return None to signal that there's no EngineDep.
+ if pb['project_id'] == 'recipe_engine':
+ return None, pb.get('recipes_path', '')
+
+ engine = pb['deps']['recipe_engine']
+
+ if 'url' not in engine:
+ raise MalformedRecipesCfg(
+ 'Required field "url" in dependency "recipe_engine" not found',
+ recipes_cfg_path)
+
+ engine.setdefault('revision', '')
+ engine.setdefault('path_override', '')
+ engine.setdefault('branch', 'refs/heads/master')
+ recipes_path = pb.get('recipes_path', '')
+
+ # TODO(iannucci): only support absolute refs
+ if not engine['branch'].startswith('refs/'):
+ engine['branch'] = 'refs/heads/' + engine['branch']
+
+ engine.setdefault('repo_type', 'GIT')
+ if engine['repo_type'] not in ('GIT', 'GITILES'):
+ raise MalformedRecipesCfg(
+ 'Unsupported "repo_type" value in dependency "recipe_engine"',
+ recipes_cfg_path)
+
+ recipes_path = os.path.join(
+ repo_root, recipes_path.replace('/', os.path.sep))
+ return EngineDep(**engine), recipes_path
+ except KeyError as ex:
+ raise MalformedRecipesCfg(ex.message, recipes_cfg_path)
+
+
+_BAT = '.bat' if sys.platform.startswith(('win', 'cygwin')) else ''
+GIT = 'git' + _BAT
+VPYTHON = 'vpython' + _BAT
+
+
+def _subprocess_call(argv, **kwargs):
+ logging.info('Running %r', argv)
+ return subprocess.call(argv, **kwargs)
+
+
+def _git_check_call(argv, **kwargs):
+ argv = [GIT]+argv
+ logging.info('Running %r', argv)
+ subprocess.check_call(argv, **kwargs)
+
+
+def _git_output(argv, **kwargs):
+ argv = [GIT]+argv
+ logging.info('Running %r', argv)
+ return subprocess.check_output(argv, **kwargs)
+
+
+def parse_args(argv):
+ """This extracts a subset of the arguments that this bootstrap script cares
+ about. Currently this consists of:
+ * an override for the recipe engine in the form of `-O recipe_engin=/path`
+ * the --package option.
+ """
+ PREFIX = 'recipe_engine='
+
+ p = argparse.ArgumentParser(add_help=False)
+ p.add_argument('-O', '--project-override', action='append')
+ p.add_argument('--package', type=os.path.abspath)
+ args, _ = p.parse_known_args(argv)
+ for override in args.project_override or ():
+ if override.startswith(PREFIX):
+ return override[len(PREFIX):], args.package
+ return None, args.package
+
+
+def checkout_engine(engine_path, repo_root, recipes_cfg_path):
+ dep, recipes_path = parse(repo_root, recipes_cfg_path)
+ if dep is None:
+ # we're running from the engine repo already!
+ return os.path.join(repo_root, recipes_path)
+
+ url = dep.url
+
+ if not engine_path and url.startswith('file://'):
+ engine_path = urlparse.urlparse(url).path
+
+ if not engine_path:
+ revision = dep.revision
+ subpath = dep.path_override
+ branch = dep.branch
+
+ # Ensure that we have the recipe engine cloned.
+ engine = os.path.join(recipes_path, '.recipe_deps', 'recipe_engine')
+ engine_path = os.path.join(engine, subpath)
+
+ with open(os.devnull, 'w') as NUL:
+ # Note: this logic mirrors the logic in recipe_engine/fetch.py
+ _git_check_call(['init', engine], stdout=NUL)
+
+ try:
+ _git_check_call(['rev-parse', '--verify', '%s^{commit}' % revision],
+ cwd=engine, stdout=NUL, stderr=NUL)
+ except subprocess.CalledProcessError:
+ _git_check_call(['fetch', url, branch], cwd=engine, stdout=NUL,
+ stderr=NUL)
+
+ try:
+ _git_check_call(['diff', '--quiet', revision], cwd=engine)
+ except subprocess.CalledProcessError:
+ _git_check_call(['reset', '-q', '--hard', revision], cwd=engine)
+
+ return engine_path
+
+
+def main():
+ if '--verbose' in sys.argv:
+ logging.getLogger().setLevel(logging.INFO)
+
+ args = sys.argv[1:]
+ engine_override, recipes_cfg_path = parse_args(args)
+
+ if recipes_cfg_path:
+ # calculate repo_root from recipes_cfg_path
+ repo_root = os.path.dirname(
+ os.path.dirname(
+ os.path.dirname(recipes_cfg_path)))
+ else:
+ # find repo_root with git and calculate recipes_cfg_path
+ repo_root = (_git_output(
+ ['rev-parse', '--show-toplevel'],
+ cwd=os.path.abspath(os.path.dirname(__file__))).strip())
+ repo_root = os.path.abspath(repo_root)
+ recipes_cfg_path = os.path.join(repo_root, 'infra', 'config', 'recipes.cfg')
+ args = ['--package', recipes_cfg_path] + args
+
+ engine_path = checkout_engine(engine_override, repo_root, recipes_cfg_path)
+
+ return _subprocess_call([
+ VPYTHON, '-u',
+ os.path.join(engine_path, 'recipes.py')] + args)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/gn/infra/recipes/gn.expected/ci.json b/gn/infra/recipes/gn.expected/ci.json
new file mode 100644
index 00000000000..e2e89338efa
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/ci.json
@@ -0,0 +1,375 @@
+[
+ {
+ "cmd": [],
+ "name": "git"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]\\gn"
+ ],
+ "infra_step": true,
+ "name": "git.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://gn.googlesource.com/gn",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.checkout",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd",
+ "-ensure-file",
+ "infra/ninja/${platform} version:1.8.2",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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-version:1.8.2---\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "debug"
+ },
+ {
+ "cmd": [],
+ "name": "debug.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd\\windows_sdk",
+ "-ensure-file",
+ "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "debug.build.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "debug.build.read SetEnv.x64.json",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"env\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"PATH\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"win_sdk\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"bin\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"x64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"VSINSTALLDIR\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\\\\\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]\\gn\\build\\gen.py",
+ "-d"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug.build.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd\\ninja",
+ "-C",
+ "[START_DIR]\\gn\\out"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "debug.build.taskkill mspdbsrv",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\gn\\out\\gn_unittests"
+ ],
+ "name": "debug.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release"
+ },
+ {
+ "cmd": [],
+ "name": "release.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd\\windows_sdk",
+ "-ensure-file",
+ "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "release.build.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "release.build.read SetEnv.x64.json",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"env\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"PATH\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"win_sdk\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"bin\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"x64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"VSINSTALLDIR\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\\\\\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]\\gn\\build\\gen.py"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.build.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd\\ninja",
+ "-C",
+ "[START_DIR]\\gn\\out"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "release.build.taskkill mspdbsrv",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\gn\\out\\gn_unittests"
+ ],
+ "name": "release.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "pkg-build",
+ "-pkg-def",
+ "{\"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",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "build gn/gn/windows-amd64",
+ "~followup_annotations": [
+ "@@@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/windows-amd64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cipd_exists.json b/gn/infra/recipes/gn.expected/cipd_exists.json
new file mode 100644
index 00000000000..5a7f14a7e13
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/cipd_exists.json
@@ -0,0 +1,254 @@
+[
+ {
+ "cmd": [],
+ "name": "git"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]/gn"
+ ],
+ "infra_step": true,
+ "name": "git.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://gn.googlesource.com/gn",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.checkout",
+ "~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} goma",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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-goma------------\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${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---\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "debug"
+ },
+ {
+ "cmd": [],
+ "name": "debug.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "-d"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "debug.build.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",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "debug.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/gn/out/gn_unittests"
+ ],
+ "name": "debug.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release"
+ },
+ {
+ "cmd": [],
+ "name": "release.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "release.build.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",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "release.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/gn/out/gn_unittests"
+ ],
+ "name": "release.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "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",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "build gn/gn/linux-amd64",
+ "~followup_annotations": [
+ "@@@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": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "name": "rev-parse",
+ "stdout": "/path/to/tmp/"
+ },
+ {
+ "cmd": [
+ "cipd",
+ "search",
+ "gn/gn/linux-amd64",
+ "-tag",
+ "git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "~followup_annotations": [
+ "@@@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": "Package is up-to-date"
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cipd_register.json b/gn/infra/recipes/gn.expected/cipd_register.json
new file mode 100644
index 00000000000..5a31ae9b355
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/cipd_register.json
@@ -0,0 +1,270 @@
+[
+ {
+ "cmd": [],
+ "name": "git"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]/gn"
+ ],
+ "infra_step": true,
+ "name": "git.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://gn.googlesource.com/gn",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "infra_step": true,
+ "name": "git.checkout",
+ "~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} goma",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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-goma------------\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${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---\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "debug"
+ },
+ {
+ "cmd": [],
+ "name": "debug.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py",
+ "-d"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "debug.build.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",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "debug.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/gn/out/gn_unittests"
+ ],
+ "name": "debug.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release"
+ },
+ {
+ "cmd": [],
+ "name": "release.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]/gn/build/gen.py"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "release.build.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",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ },
+ "name": "release.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/gn/out/gn_unittests"
+ ],
+ "name": "release.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "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",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "build gn/gn/linux-amd64",
+ "~followup_annotations": [
+ "@@@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": [
+ "git",
+ "rev-parse",
+ "HEAD"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "name": "rev-parse",
+ "stdout": "/path/to/tmp/"
+ },
+ {
+ "cmd": [
+ "cipd",
+ "search",
+ "gn/gn/linux-amd64",
+ "-tag",
+ "git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": []@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "pkg-register",
+ "[CLEANUP]/gn.cipd",
+ "-ref",
+ "latest",
+ "-tag",
+ "git_repository:https://gn.googlesource.com/gn",
+ "-tag",
+ "git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "register gn/gn/linux-amd64",
+ "~followup_annotations": [
+ "@@@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@@@"
+ ]
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cq.json b/gn/infra/recipes/gn.expected/cq.json
new file mode 100644
index 00000000000..e9b8b251db6
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/cq.json
@@ -0,0 +1,380 @@
+[
+ {
+ "cmd": [],
+ "name": "git"
+ },
+ {
+ "cmd": [
+ "git",
+ "init",
+ "[START_DIR]\\gn"
+ ],
+ "infra_step": true,
+ "name": "git.init",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "--tags",
+ "https://gn.googlesource.com/gn",
+ "refs/heads/master"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.fetch",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "checkout",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.checkout",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "fetch",
+ "https://gn.googlesource.com/gn",
+ "refs/changes/00/1000/1"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.fetch 1000/1",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "git",
+ "cherry-pick",
+ "FETCH_HEAD"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "git.cherry-pick 1000/1",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd",
+ "-ensure-file",
+ "infra/ninja/${platform} version:1.8.2",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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-version:1.8.2---\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "debug"
+ },
+ {
+ "cmd": [],
+ "name": "debug.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd\\windows_sdk",
+ "-ensure-file",
+ "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "debug.build.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "debug.build.read SetEnv.x64.json",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"env\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"PATH\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"win_sdk\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"bin\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"x64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"VSINSTALLDIR\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\\\\\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]\\gn\\build\\gen.py",
+ "-d"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug.build.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd\\ninja",
+ "-C",
+ "[START_DIR]\\gn\\out"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "debug.build.taskkill mspdbsrv",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\gn\\out\\gn_unittests"
+ ],
+ "name": "debug.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release"
+ },
+ {
+ "cmd": [],
+ "name": "release.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd\\windows_sdk",
+ "-ensure-file",
+ "chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "infra_step": true,
+ "name": "release.build.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@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@ \"package\": \"chrome_internal/third_party/sdk/windows\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "release.build.read SetEnv.x64.json",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"env\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"PATH\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"win_sdk\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"bin\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"x64\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"VSINSTALLDIR\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"..\\\\\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "[START_DIR]\\gn\\build\\gen.py"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.build.generate",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd\\ninja",
+ "-C",
+ "[START_DIR]\\gn\\out"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.build.ninja",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "name": "release.build.taskkill mspdbsrv",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\gn\\out\\gn_unittests"
+ ],
+ "name": "release.test",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.py b/gn/infra/recipes/gn.py
new file mode 100644
index 00000000000..0a88862d994
--- /dev/null
+++ b/gn/infra/recipes/gn.py
@@ -0,0 +1,178 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed under the Apache License, Version 2.0
+# that can be found in the LICENSE file.
+"""Recipe for building GN."""
+
+from recipe_engine.recipe_api import Property
+
+DEPS = [
+ 'recipe_engine/buildbucket',
+ 'recipe_engine/cipd',
+ 'recipe_engine/context',
+ 'recipe_engine/file',
+ 'recipe_engine/json',
+ 'recipe_engine/path',
+ 'recipe_engine/platform',
+ 'recipe_engine/properties',
+ 'recipe_engine/python',
+ 'recipe_engine/raw_io',
+ 'recipe_engine/step',
+ 'windows_sdk',
+]
+
+PROPERTIES = {
+ 'repository': Property(kind=str, default='https://gn.googlesource.com/gn'),
+}
+
+
+def RunSteps(api, repository):
+ src_dir = api.path['start_dir'].join('gn')
+
+ with api.step.nest('git'), api.context(infra_steps=True):
+ api.step('init', ['git', 'init', src_dir])
+
+ with api.context(cwd=src_dir):
+ build_input = api.buildbucket.build_input
+ ref = (
+ build_input.gitiles_commit.id
+ if build_input.gitiles_commit else 'refs/heads/master')
+ # Fetch tags so `git describe` works.
+ api.step('fetch', ['git', 'fetch', '--tags', repository, ref])
+ api.step('checkout', ['git', 'checkout', 'FETCH_HEAD'])
+ 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'])
+
+ 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:
+ pkgs.add_package('fuchsia/clang/${platform}', 'goma')
+ api.cipd.ensure(cipd_dir, pkgs)
+
+ env = {
+ 'linux': {
+ 'CC': cipd_dir.join('bin', 'clang'),
+ 'CXX': cipd_dir.join('bin', 'clang++'),
+ 'AR': cipd_dir.join('bin', 'llvm-ar'),
+ 'LDFLAGS': '-static-libstdc++ -ldl -lpthread',
+ },
+ 'mac': {},
+ 'win': {},
+ }[api.platform.name]
+
+ # The order is important since release build will get uploaded to CIPD.
+ configs = [
+ {
+ 'name': 'debug',
+ 'args': ['-d']
+ },
+ {
+ 'name': 'release',
+ 'args': []
+ },
+ ]
+
+ for config in configs:
+ with api.step.nest(config['name']):
+ with api.step.nest('build'):
+ with api.context(
+ env=env, cwd=src_dir), api.windows_sdk(enabled=api.platform.is_win):
+ api.python(
+ 'generate', src_dir.join('build', 'gen.py'), args=config['args'])
+
+ # Windows requires the environment modifications when building too.
+ api.step('ninja', [cipd_dir.join('ninja'), '-C', src_dir.join('out')])
+
+ api.step('test', [src_dir.join('out', 'gn_unittests')])
+
+ if build_input.gerrit_changes:
+ return
+
+ # TODO: Use ${platform} after crbug.com/855703 is fixed and deployed.
+ platform = '%s-%s' % (api.platform.name.replace('win', 'windows'), {
+ 'intel': {
+ 32: '386',
+ 64: 'amd64',
+ },
+ 'arm': {
+ 32: 'armv6',
+ 64: 'arm64',
+ },
+ }[api.platform.arch][api.platform.bits])
+
+ cipd_pkg_name = 'gn/gn/' + platform
+ gn = 'gn' + ('.exe' if api.platform.is_win else '')
+
+ 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)
+
+ cipd_pkg_file = api.path['cleanup'].join('gn.cipd')
+
+ api.cipd.build_from_pkg(
+ pkg_def=pkg_def,
+ output_package=cipd_pkg_file,
+ )
+
+ 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_pin = api.cipd.search(cipd_pkg_name, 'git_revision:' + revision)
+ if cipd_pin:
+ api.step('Package is up-to-date', cmd=None)
+ return
+
+ 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):
+ REVISION = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+
+ yield (api.test('ci') + api.platform.name('win') + api.buildbucket.ci_build(
+ git_repo='gn.googlesource.com/gn',
+ revision=REVISION,
+ ))
+
+ yield (api.test('cq') + api.platform.name('win') + api.buildbucket.try_build(
+ gerrit_host='gn-review.googlesource.com',
+ change_number=1000,
+ patch_set=1,
+ ))
+
+ yield (api.test('cipd_exists') + api.buildbucket.ci_build(
+ project='infra-internal',
+ git_repo='gn.googlesource.com/gn',
+ revision=REVISION,
+ ) + api.step_data('rev-parse', api.raw_io.stream_output(REVISION)) +
+ api.step_data('cipd search gn/gn/linux-amd64 git_revision:' + REVISION,
+ api.cipd.example_search('gn/gn/linux-amd64',
+ ['git_revision:' + REVISION])))
+
+ yield (api.test('cipd_register') + api.buildbucket.ci_build(
+ project='infra-internal',
+ git_repo='gn.googlesource.com/gn',
+ revision=REVISION,
+ ) + api.step_data('rev-parse', api.raw_io.stream_output(REVISION)) +
+ api.step_data('cipd search gn/gn/linux-amd64 git_revision:' + REVISION,
+ api.cipd.example_search('gn/gn/linux-amd64', [])))
diff --git a/gn/tools/gn/action_target_generator.cc b/gn/tools/gn/action_target_generator.cc
new file mode 100644
index 00000000000..c0ba6c8058f
--- /dev/null
+++ b/gn/tools/gn/action_target_generator.cc
@@ -0,0 +1,221 @@
+// Copyright (c) 2013 The Chromium Authors. 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,
+ SUBSTITUTION_RSP_FILE_NAME);
+ 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
new file mode 100644
index 00000000000..0ea3cbbab87
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..b4c2b99c0e6
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..f73d869cafb
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..806a39f8c64
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..312c22aab01
--- /dev/null
+++ b/gn/tools/gn/analyzer.cc
@@ -0,0 +1,488 @@
+// 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.push_back(SourceFile(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
new file mode 100644
index 00000000000..01be0e45552
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..a6d73a28d27
--- /dev/null
+++ b/gn/tools/gn/analyzer_unittest.cc
@@ -0,0 +1,594 @@
+// 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/build_settings.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/config.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(new Tool());
+ fake_tool->set_outputs(
+ SubstitutionList::MakeForTest("//out/debug/output.txt"));
+ toolchain->SetTool(Toolchain::TYPE_LINK, 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
new file mode 100644
index 00000000000..aa42ff3dab9
--- /dev/null
+++ b/gn/tools/gn/args.cc
@@ -0,0 +1,404 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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_);
+
+ // Default values.
+ for (const auto& map_pair : declared_arguments_per_toolchain_) {
+ for (const auto& arg : map_pair.second)
+ 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_AIX)
+ os = "aix";
+#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
new file mode 100644
index 00000000000..4a4bc7c8b36
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..49952295c91
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..60af24237e4
--- /dev/null
+++ b/gn/tools/gn/binary_target_generator.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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;
+
+ // 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::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::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 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;
+}
diff --git a/gn/tools/gn/binary_target_generator.h b/gn/tools/gn/binary_target_generator.h
new file mode 100644
index 00000000000..40fc3141e26
--- /dev/null
+++ b/gn/tools/gn/binary_target_generator.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+
+ private:
+ bool FillCompleteStaticLib();
+ bool FillFriends();
+ bool FillOutputName();
+ bool FillOutputPrefixOverride();
+ bool FillOutputDir();
+ bool FillOutputExtension();
+ bool FillAllowCircularIncludesFrom();
+
+ 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
new file mode 100644
index 00000000000..84f2a3cb5d7
--- /dev/null
+++ b/gn/tools/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 "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)
+ : 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_) {}
+
+BuildSettings::~BuildSettings() = default;
+
+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
new file mode 100644
index 00000000000..52f3625a4be
--- /dev/null
+++ b/gn/tools/gn/build_settings.h
@@ -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.
+
+#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);
+ ~BuildSettings();
+
+ // 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 std::string& root_path_utf8() const { return root_path_utf8_; }
+ void SetRootPath(const base::FilePath& r);
+
+ // 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 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
new file mode 100644
index 00000000000..1d04db01331
--- /dev/null
+++ b/gn/tools/gn/builder.cc
@@ -0,0 +1,605 @@
+// Copyright (c) 2013 The Chromium Authors. 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 (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
+ Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
+ Tool* tool = toolchain->GetTool(tool_type);
+ if (!tool || tool->pool().label.is_null())
+ continue;
+
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ tool->pool().label, tool->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 (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
+ Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
+ Tool* tool = toolchain->GetTool(tool_type);
+ if (!tool || tool->pool().label.is_null())
+ continue;
+
+ BuilderRecord* record =
+ GetResolvedRecordOfType(tool->pool().label, toolchain->defined_from(),
+ BuilderRecord::ITEM_POOL, err);
+ if (!record) {
+ *err = Err(tool->pool().origin, "Pool for tool not defined.",
+ "I was hoping to find a pool " +
+ tool->pool().label.GetUserVisibleName(false));
+ return false;
+ }
+
+ tool->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
new file mode 100644
index 00000000000..973ee6a39df
--- /dev/null
+++ b/gn/tools/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 <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
new file mode 100644
index 00000000000..36fd465c484
--- /dev/null
+++ b/gn/tools/gn/builder_record.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 The Chromium Authors. 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),
+ originally_referenced_from_(nullptr),
+ should_generate_(false),
+ resolved_(false) {}
+
+BuilderRecord::~BuilderRecord() = default;
+
+// 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
new file mode 100644
index 00000000000..dc96f7f56d4
--- /dev/null
+++ b/gn/tools/gn/builder_record.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2013 The Chromium Authors. 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);
+ ~BuilderRecord();
+
+ 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_;
+ bool should_generate_;
+ bool resolved_;
+
+ 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
new file mode 100644
index 00000000000..08d8d34af05
--- /dev/null
+++ b/gn/tools/gn/builder_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..9f2cf2b84f4
--- /dev/null
+++ b/gn/tools/gn/bundle_data.cc
@@ -0,0 +1,169 @@
+// 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 = 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(SourceFile::SWAP_IN, &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());
+ }
+}
+
+void BundleData::GetOutputFiles(const Settings* settings,
+ OutputFiles* outputs) const {
+ SourceFiles outputs_as_sources;
+ GetOutputsAsSourceFiles(settings, &outputs_as_sources);
+ for (const SourceFile& source_file : outputs_as_sources)
+ outputs->push_back(OutputFile(settings->build_settings(), source_file));
+}
+
+void BundleData::GetOutputsAsSourceFiles(const Settings* settings,
+ SourceFiles* outputs_as_source) const {
+ for (const BundleFileRule& file_rule : file_rules_) {
+ for (const SourceFile& source : file_rule.sources()) {
+ outputs_as_source->push_back(
+ file_rule.ApplyPatternToSource(settings, *this, source));
+ }
+ }
+
+ 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));
+}
+
+SourceFile BundleData::GetCompiledAssetCatalogPath() const {
+ DCHECK(!assets_catalog_sources_.empty());
+ std::string assets_car_path = resources_dir_.value() + "/Assets.car";
+ return SourceFile(SourceFile::SWAP_IN, &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(SourceFile::SWAP_IN, &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
new file mode 100644
index 00000000000..dc63bbd56a8
--- /dev/null
+++ b/gn/tools/gn/bundle_data.h
@@ -0,0 +1,197 @@
+// 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.
+ void GetOutputFiles(const Settings* settings, OutputFiles* outputs) const;
+
+ // Returns the list of outputs as SourceFile.
+ void GetOutputsAsSourceFiles(const Settings* settings,
+ SourceFiles* outputs_as_source) 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_; }
+
+ SourceDir& plugins_dir() { return plugins_dir_; }
+ const SourceDir& plugins_dir() const { return plugins_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_;
+ SourceDir plugins_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
new file mode 100644
index 00000000000..23f26fd679d
--- /dev/null
+++ b/gn/tools/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 "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 (SubstitutionType type : outputs.required_types()) {
+ if (!IsValidBundleDataSubstitution(type)) {
+ *err_ = Err(value->origin(), "Invalid substitution type.",
+ "The substitution " + std::string(kSubstitutionNames[type]) +
+ " 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
new file mode 100644
index 00000000000..e1788efd7b8
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..4d4ca3a34eb
--- /dev/null
+++ b/gn/tools/gn/bundle_file_rule.cc
@@ -0,0 +1,66 @@
+// 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 "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"
+
+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;
+
+SourceFile BundleFileRule::ApplyPatternToSource(
+ const Settings* settings,
+ const BundleData& bundle_data,
+ const SourceFile& source_file) const {
+ std::string output_path;
+ for (const auto& subrange : pattern_.ranges()) {
+ switch (subrange.type) {
+ case SUBSTITUTION_LITERAL:
+ output_path.append(subrange.literal);
+ break;
+ case SUBSTITUTION_BUNDLE_ROOT_DIR:
+ output_path.append(bundle_data.root_dir().value());
+ break;
+ case SUBSTITUTION_BUNDLE_CONTENTS_DIR:
+ output_path.append(bundle_data.contents_dir().value());
+ break;
+ case SUBSTITUTION_BUNDLE_RESOURCES_DIR:
+ output_path.append(bundle_data.resources_dir().value());
+ break;
+ case SUBSTITUTION_BUNDLE_EXECUTABLE_DIR:
+ output_path.append(bundle_data.executable_dir().value());
+ break;
+ case SUBSTITUTION_BUNDLE_PLUGINS_DIR:
+ output_path.append(bundle_data.plugins_dir().value());
+ break;
+ default:
+ output_path.append(SubstitutionWriter::GetSourceSubstitution(
+ target_, target_->settings(), source_file, subrange.type,
+ SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir()));
+ break;
+ }
+ }
+ return SourceFile(SourceFile::SWAP_IN, &output_path);
+}
+
+OutputFile BundleFileRule::ApplyPatternToSourceAsOutputFile(
+ const Settings* settings,
+ const BundleData& bundle_data,
+ const SourceFile& source_file) const {
+ return OutputFile(settings->build_settings(),
+ ApplyPatternToSource(settings, bundle_data, source_file));
+}
diff --git a/gn/tools/gn/bundle_file_rule.h b/gn/tools/gn/bundle_file_rule.h
new file mode 100644
index 00000000000..372e62802c7
--- /dev/null
+++ b/gn/tools/gn/bundle_file_rule.h
@@ -0,0 +1,51 @@
+// 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.
+ SourceFile ApplyPatternToSource(const Settings* settings,
+ const BundleData& bundle_data,
+ const SourceFile& source_file) const;
+ OutputFile ApplyPatternToSourceAsOutputFile(
+ const Settings* settings,
+ const BundleData& bundle_data,
+ const SourceFile& source_file) 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
new file mode 100644
index 00000000000..9f4f0703e63
--- /dev/null
+++ b/gn/tools/gn/c_include_iterator.cc
@@ -0,0 +1,171 @@
+// Copyright 2014 The Chromium Authors. 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;
+
+ 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()),
+ offset_(0),
+ line_number_(0),
+ lines_since_last_include_(0) {}
+
+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
new file mode 100644
index 00000000000..e81a60c9383
--- /dev/null
+++ b/gn/tools/gn/c_include_iterator.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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_;
+
+ int line_number_; // 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_;
+
+ 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
new file mode 100644
index 00000000000..ef442635390
--- /dev/null
+++ b/gn/tools/gn/c_include_iterator_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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);
+}
diff --git a/gn/tools/gn/command_analyze.cc b/gn/tools/gn/command_analyze.cc
new file mode 100644
index 00000000000..9aa698b3c15
--- /dev/null
+++ b/gn/tools/gn/command_analyze.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+ }
+
+ 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
new file mode 100644
index 00000000000..9980333c67d
--- /dev/null
+++ b/gn/tools/gn/command_args.cc
@@ -0,0 +1,506 @@
+// Copyright (c) 2013 The Chromium Authors. 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) {
+ 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 <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
new file mode 100644
index 00000000000..99fbff502f3
--- /dev/null
+++ b/gn/tools/gn/command_check.cc
@@ -0,0 +1,256 @@
+// Copyright 2014 The Chromium Authors. 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]
+
+ 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.
+
+What gets checked
+
+ The .gn file may specify a list of targets to be checked. Only these targets
+ will be checked if no label_pattern is specified on the command line.
+ Otherwise, the command-line list is used instead. See "gn help dotfile".
+
+ 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.
+
+ - 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");
+
+ if (!CheckPublicHeaders(&setup->build_settings(), all_targets,
+ targets_to_check, force))
+ 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) {
+ ScopedTrace trace(TraceItem::TRACE_CHECK_HEADERS, "Check headers");
+
+ scoped_refptr<HeaderChecker> header_checker(
+ new HeaderChecker(build_settings, all_targets));
+
+ 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
new file mode 100644
index 00000000000..b138864f61a
--- /dev/null
+++ b/gn/tools/gn/command_clean.cc
@@ -0,0 +1,129 @@
+// 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;
+ }
+
+ 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
new file mode 100644
index 00000000000..2531c7c5507
--- /dev/null
+++ b/gn/tools/gn/command_desc.cc
@@ -0,0 +1,516 @@
+// Copyright (c) 2013 The Chromium Authors. 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_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";
+
+// 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;
+ 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->is_none()) {
+ OutputString(indent + "<null>\n");
+ }
+}
+
+// Default handler for property
+void DefaultHandler(const std::string& name, const base::Value* value) {
+ OutputString("\n");
+ OutputString(name);
+ OutputString("\n");
+ PrintValue(value, 1);
+}
+
+// Specific handler for properties that need different treatment
+
+// Prints label and property value on one line, capitalizing the label.
+void LabelHandler(std::string name, const base::Value* value) {
+ name[0] = base::ToUpperASCII(name[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) {
+ const base::ListValue* list;
+ if (value->GetAsList(&list)) {
+ if (list->empty()) {
+ base::Value str("(no visibility)");
+ DefaultHandler(name, &str);
+ } else {
+ DefaultHandler(name, value);
+ }
+ }
+}
+
+void PublicHandler(const std::string& name, const base::Value* value) {
+ std::string p;
+ if (value->GetAsString(&p)) {
+ if (p == "*") {
+ base::Value str("[All headers listed in the sources are public.]");
+ DefaultHandler(name, &str);
+ return;
+ }
+ }
+ DefaultHandler(name, value);
+}
+
+void ConfigsHandler(const std::string& name, const base::Value* value) {
+ bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ if (tree)
+ DefaultHandler(name + " tree (in order applying)", value);
+ else
+ DefaultHandler(name + " (in order applying, try also --tree)", value);
+}
+
+void DepsHandler(const std::string& name, const base::Value* value) {
+ bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
+ if (tree) {
+ DefaultHandler("Dependency tree", value);
+ } else {
+ if (!all) {
+ DefaultHandler(
+ "Direct dependencies "
+ "(try also \"--all\", \"--tree\", or even \"--all --tree\")",
+ value);
+ } else {
+ DefaultHandler("All recursive dependencies", value);
+ }
+ }
+}
+
+// Outputs need special processing when output patterns are present.
+void ProcessOutputs(base::DictionaryValue* target) {
+ base::ListValue* patterns = nullptr;
+ base::ListValue* outputs = nullptr;
+ target->GetList("output_patterns", &patterns);
+ target->GetList(variables::kOutputs, &outputs);
+
+ if (outputs || patterns) {
+ OutputString("\noutputs\n");
+ int indent = 1;
+ if (patterns) {
+ OutputString(" Output patterns\n");
+ indent = 2;
+ PrintValue(patterns, indent);
+ OutputString("\n Resolved output file list\n");
+ }
+ if (outputs)
+ PrintValue(outputs, indent);
+
+ target->Remove("output_patterns", nullptr);
+ target->Remove(variables::kOutputs, nullptr);
+ }
+}
+
+bool PrintTarget(const Target* target,
+ const std::string& what,
+ bool single_target,
+ 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, without any headers
+ if (!what.empty() && dict->size() == 1 && single_target) {
+ base::DictionaryValue::Iterator iter(*dict);
+ PrintValue(&iter.value(), 0);
+ return true;
+ }
+
+ OutputString("Target ", DECORATION_YELLOW);
+ OutputString(target->label().GetUserVisibleName(false));
+ OutputString("\n");
+
+ std::unique_ptr<base::Value> v;
+#define HANDLER(property, handler_name) \
+ if (dict->Remove(property, &v)) { \
+ handler_name(property, v.get()); \
+ }
+
+ // Entries with DefaultHandler are present to enforce order
+ HANDLER("type", LabelHandler);
+ HANDLER("toolchain", LabelHandler);
+ HANDLER(variables::kVisibility, VisibilityHandler);
+ HANDLER(variables::kTestonly, DefaultHandler);
+ HANDLER(variables::kCheckIncludes, DefaultHandler);
+ HANDLER(variables::kAllowCircularIncludesFrom, DefaultHandler);
+ HANDLER(variables::kSources, DefaultHandler);
+ HANDLER(variables::kPublic, PublicHandler);
+ HANDLER(variables::kInputs, DefaultHandler);
+ HANDLER(variables::kConfigs, ConfigsHandler);
+ HANDLER(variables::kPublicConfigs, ConfigsHandler);
+ HANDLER(variables::kAllDependentConfigs, ConfigsHandler);
+ HANDLER(variables::kScript, DefaultHandler);
+ HANDLER(variables::kArgs, DefaultHandler);
+ HANDLER(variables::kDepfile, DefaultHandler);
+ ProcessOutputs(dict.get());
+ HANDLER("bundle_data", DefaultHandler);
+ HANDLER(variables::kArflags, DefaultHandler);
+ HANDLER(variables::kAsmflags, DefaultHandler);
+ HANDLER(variables::kCflags, DefaultHandler);
+ HANDLER(variables::kCflagsC, DefaultHandler);
+ HANDLER(variables::kCflagsCC, DefaultHandler);
+ HANDLER(variables::kCflagsObjC, DefaultHandler);
+ HANDLER(variables::kCflagsObjCC, DefaultHandler);
+ HANDLER(variables::kDefines, DefaultHandler);
+ HANDLER(variables::kIncludeDirs, DefaultHandler);
+ HANDLER(variables::kLdflags, DefaultHandler);
+ HANDLER(variables::kPrecompiledHeader, DefaultHandler);
+ HANDLER(variables::kPrecompiledSource, DefaultHandler);
+ HANDLER(variables::kDeps, DepsHandler);
+ HANDLER(variables::kLibs, DefaultHandler);
+ HANDLER(variables::kLibDirs, DefaultHandler);
+
+#undef HANDLER
+
+ // Process the rest (if any)
+ base::DictionaryValue::Iterator iter(*dict);
+ while (!iter.IsAtEnd()) {
+ DefaultHandler(iter.key(), &iter.value());
+ iter.Advance();
+ }
+
+ return true;
+}
+
+bool PrintConfig(const Config* config,
+ const std::string& what,
+ bool single_config) {
+ 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, without any headers
+ if (!what.empty() && dict->size() == 1 && single_config) {
+ base::DictionaryValue::Iterator iter(*dict);
+ PrintValue(&iter.value(), 0);
+ return true;
+ }
+
+ OutputString("Config: ", DECORATION_YELLOW);
+ OutputString(config->label().GetUserVisibleName(false));
+ OutputString("\n");
+
+ std::unique_ptr<base::Value> v;
+#define HANDLER(property, handler_name) \
+ if (dict->Remove(property, &v)) { \
+ handler_name(property, v.get()); \
+ }
+
+ HANDLER("toolchain", LabelHandler);
+ if (!config->configs().empty()) {
+ OutputString(
+ "(This is a composite config, the values below are after the\n"
+ "expansion of the child configs.)\n");
+ }
+ HANDLER(variables::kArflags, DefaultHandler);
+ HANDLER(variables::kAsmflags, DefaultHandler);
+ HANDLER(variables::kCflags, DefaultHandler);
+ HANDLER(variables::kCflagsC, DefaultHandler);
+ HANDLER(variables::kCflagsCC, DefaultHandler);
+ HANDLER(variables::kCflagsObjC, DefaultHandler);
+ HANDLER(variables::kCflagsObjCC, DefaultHandler);
+ HANDLER(variables::kDefines, DefaultHandler);
+ HANDLER(variables::kIncludeDirs, DefaultHandler);
+ HANDLER(variables::kInputs, DefaultHandler);
+ HANDLER(variables::kLdflags, DefaultHandler);
+ HANDLER(variables::kLibs, DefaultHandler);
+ HANDLER(variables::kLibDirs, DefaultHandler);
+ HANDLER(variables::kPrecompiledHeader, DefaultHandler);
+ HANDLER(variables::kPrecompiledSource, DefaultHandler);
+
+#undef HANDLER
+
+ 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 <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)
+ defines [--blame]
+ depfile
+ deps [--all] [--tree] (see below)
+ include_dirs [--blame]
+ inputs
+ ldflags [--blame]
+ lib_dirs
+ libs
+ outputs
+ public_configs
+ public
+ script
+ sources
+ testonly
+ visibility
+
+ 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;
+
+ 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,
+ 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))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/tools/gn/command_format.cc b/gn/tools/gn/command_format.cc
new file mode 100644
index 00000000000..2d6cea4bdc5
--- /dev/null
+++ b/gn/tools/gn/command_format.cc
@@ -0,0 +1,1109 @@
+// Copyright 2014 The Chromium Authors. 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/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/tokenizer.h"
+
+namespace commands {
+
+const char kSwitchDryRun[] = "dry-run";
+const char kSwitchDumpTree[] = "dump-tree";
+const char kSwitchStdin[] = "stdin";
+
+const char kFormat[] = "format";
+const char kFormat_HelpShort[] = "format: Format .gn file.";
+const char kFormat_Help[] =
+ R"(gn format [--dump-tree] (--stdin | <build_file>)
+
+ 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
+ For debugging, dumps the parse tree to stdout and does not update the
+ file or print formatted output.
+
+ --stdin
+ Read input from stdin and write to stdout rather than update a file
+ in-place.
+
+Examples
+ gn format //some/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,
+ kSequenceStyleBlock,
+ kSequenceStyleBracedBlock,
+ };
+
+ 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);
+
+ // 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 (binop->comments() && !binop->comments()->before().empty() &&
+ binop->comments()->before()[0].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();
+ }
+}
+
+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();
+ }
+ }
+
+ 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 (");
+ // TODO(scottmg): The { needs to be included in the suffix here.
+ Expr(condition->condition(), kPrecedenceLowest, ") ");
+ Sequence(kSequenceStyleBracedBlock, 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("{");
+
+ if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
+ force_multiline = true;
+
+ 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, bool dump_tree, std::string* output) {
+ if (dump_tree) {
+ std::ostringstream os;
+ root->Print(os, 0);
+ fprintf(stderr, "%s", os.str().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,
+ bool 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,
+ bool 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);
+ bool dump_tree =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
+ 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;
+ }
+
+ 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;
+ }
+
+ // TODO(scottmg): Eventually, this should be a list/spec of files, and they
+ // should all be done in parallel.
+ if (args.size() != 1) {
+ Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
+ .PrintToStdout();
+ return 1;
+ }
+
+ Setup setup;
+ SourceDir source_dir =
+ SourceDirForCurrentDirectory(setup.build_settings().root_path());
+
+ Err err;
+ SourceFile file =
+ source_dir.ResolveRelativeFile(Value(nullptr, args[0]), &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ std::string output_string;
+ if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
+ if (!dump_tree) {
+ // 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 \"") +
+ to_write.AsUTF8Unsafe() +
+ 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 \"") +
+ to_write.AsUTF8Unsafe() + std::string("\"."))
+ .PrintToStdout();
+ return 1;
+ }
+ printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
+ }
+ }
+ }
+
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/tools/gn/command_format.h b/gn/tools/gn/command_format.h
new file mode 100644
index 00000000000..413937e9ff9
--- /dev/null
+++ b/gn/tools/gn/command_format.h
@@ -0,0 +1,26 @@
+// 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 {
+
+bool FormatFileToString(Setup* setup,
+ const SourceFile& file,
+ bool dump_tree,
+ std::string* output);
+
+bool FormatStringToString(const std::string& input,
+ bool 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
new file mode 100644
index 00000000000..cd69e5d44ad
--- /dev/null
+++ b/gn/tools/gn/command_format_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. 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"), false, \
+ &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); \
+ }
+
+// 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)
diff --git a/gn/tools/gn/command_gen.cc b/gn/tools/gn/command_gen.cc
new file mode 100644
index 00000000000..82046d2e583
--- /dev/null
+++ b/gn/tools/gn/command_gen.cc
@@ -0,0 +1,517 @@
+// Copyright (c) 2013 The Chromium Authors. 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 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) {
+ VisualStudioWriter::Version version = VisualStudioWriter::Version::Vs2017;
+ if (ide == kSwitchIdeValueVs2013)
+ version = VisualStudioWriter::Version::Vs2013;
+ else if (ide == kSwitchIdeValueVs2015)
+ version = VisualStudioWriter::Version::Vs2015;
+
+ 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";
+
+ bool res = CompileCommandsWriter::RunAndWriteFiles(build_settings, builder,
+ file_name, 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.
+ "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
+ 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. 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();
+ 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
new file mode 100644
index 00000000000..cff20522a6d
--- /dev/null
+++ b/gn/tools/gn/command_help.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 <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/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 {
+
+void PrintToplevelHelp() {
+ PrintSectionHelp("Commands", "<command>", "commands");
+ for (const auto& cmd : commands::GetCommands())
+ PrintShortHelp(cmd.second.help_short);
+
+ // Target declarations.
+ PrintSectionHelp("Target declarations", "<function>", "targets");
+ for (const auto& func : functions::GetFunctions()) {
+ if (func.second.is_target)
+ PrintShortHelp(func.second.help_short);
+ }
+
+ // Functions.
+ PrintSectionHelp("Buildfile functions", "<function>", "functions");
+ for (const auto& func : functions::GetFunctions()) {
+ if (!func.second.is_target)
+ PrintShortHelp(func.second.help_short);
+ }
+
+ // Built-in variables.
+ PrintSectionHelp("Built-in predefined variables", "<variable>",
+ "predefined_variables");
+ for (const auto& builtin : variables::GetBuiltinVariables())
+ PrintShortHelp(builtin.second.help_short);
+
+ // Target variables.
+ PrintSectionHelp("Variables you set in targets", "<variable>",
+ "target_variables");
+ for (const auto& target : variables::GetTargetVariables())
+ PrintShortHelp(target.second.help_short);
+
+ PrintSectionHelp("Other help topics", "", "other");
+ PrintShortHelp("all: Print all the help at once");
+ PrintShortHelp("buildargs: How build arguments work.");
+ PrintShortHelp("dotfile: Info about the toplevel .gn file.");
+ PrintShortHelp("execution: Build graph and execution overview.");
+ PrintShortHelp("grammar: Language and grammar for GN build files.");
+ PrintShortHelp(
+ "input_conversion: Processing input from exec_script and read_file.");
+ PrintShortHelp("label_pattern: Matching more than one label.");
+ PrintShortHelp("labels: About labels.");
+ PrintShortHelp("ninja_rules: How Ninja build rules are named.");
+ PrintShortHelp("nogncheck: Annotating includes for checking.");
+ PrintShortHelp(
+ "output_conversion: Specifies how to transform a value to output.");
+ PrintShortHelp("runtime_deps: How runtime dependency computation works.");
+ PrintShortHelp("source_expansion: Map sources to outputs for scripts.");
+ PrintShortHelp("switches: Show available command-line switches.");
+}
+
+void PrintSwitchHelp() {
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
+
+ OutputString("Available global switches\n", DECORATION_YELLOW);
+ OutputString(
+ " Do \"gn help --the_switch_you_want_help_on\" for more. Individual\n"
+ " commands may take command-specific switches not listed here. See the\n"
+ " help on your specific command for more.\n\n");
+
+ 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");
+ for (const auto& c : commands::GetCommands())
+ PrintLongHelp(c.second.help);
+
+ if (is_markdown)
+ OutputString("## <a name=\"targets\"></a>Target declarations\n\n");
+ for (const auto& f : functions::GetFunctions()) {
+ if (f.second.is_target)
+ PrintLongHelp(f.second.help);
+ }
+
+ if (is_markdown)
+ OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n");
+ for (const auto& f : functions::GetFunctions()) {
+ if (!f.second.is_target)
+ PrintLongHelp(f.second.help);
+ }
+
+ if (is_markdown) {
+ OutputString(
+ "## <a name=\"predefined_variables\"></a>"
+ "Built-in predefined variables\n\n");
+ }
+ for (const auto& v : variables::GetBuiltinVariables())
+ PrintLongHelp(v.second.help);
+
+ if (is_markdown) {
+ OutputString(
+ "## <a name=\"target_variables\"></a>"
+ "Variables you set in targets\n\n");
+ }
+ for (const auto& v : variables::GetTargetVariables())
+ PrintLongHelp(v.second.help);
+
+ if (is_markdown)
+ OutputString("## <a name=\"other\"></a>Other help topics\n\n");
+ PrintLongHelp(kBuildArgs_Help, "buildargs");
+ PrintLongHelp(kDotfile_Help, "dotfile");
+ PrintLongHelp(kExecution_Help, "execution");
+ PrintLongHelp(kGrammar_Help, "grammar");
+ PrintLongHelp(kInputOutputConversion_Help, "input_conversion");
+ PrintLongHelp(kLabelPattern_Help, "label_pattern");
+ PrintLongHelp(kLabels_Help, "labels");
+ PrintLongHelp(kNinjaRules_Help, "ninja_rules");
+ PrintLongHelp(kNoGnCheck_Help, "nogncheck");
+ PrintLongHelp(kInputOutputConversion_Help, "output_conversion");
+ PrintLongHelp(kRuntimeDeps_Help, "runtime_deps");
+ PrintLongHelp(kSourceExpansion_Help, "source_expansion");
+
+ if (is_markdown)
+ OutputString("## <a name=\"switches\"></a>Command Line Switches\n\n");
+ 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;
+}
+
+} // 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;
+
+ // 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["input_conversion"] = []() {
+ PrintLongHelp(kInputOutputConversion_Help);
+ };
+ random_topics["label_pattern"] = []() { PrintLongHelp(kLabelPattern_Help); };
+ random_topics["labels"] = []() { PrintLongHelp(kLabels_Help); };
+ random_topics["ninja_rules"] = []() { PrintLongHelp(kNinjaRules_Help); };
+ random_topics["nogncheck"] = []() { PrintLongHelp(kNoGnCheck_Help); };
+ random_topics["output_conversion"] = []() {
+ PrintLongHelp(kInputOutputConversion_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
new file mode 100644
index 00000000000..65ec1a99cba
--- /dev/null
+++ b/gn/tools/gn/command_ls.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 <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;
+ }
+
+ 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_path.cc b/gn/tools/gn/command_path.cc
new file mode 100644
index 00000000000..333a78ea3f3
--- /dev/null
+++ b/gn/tools/gn/command_path.cc
@@ -0,0 +1,412 @@
+// 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;
+ }
+
+ 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
new file mode 100644
index 00000000000..92ecf10376b
--- /dev/null
+++ b/gn/tools/gn/command_refs.cc
@@ -0,0 +1,498 @@
+// Copyright (c) 2013 The Chromium Authors. 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 <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);
+
+ 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
new file mode 100644
index 00000000000..7fb496d0e44
--- /dev/null
+++ b/gn/tools/gn/commands.cc
@@ -0,0 +1,558 @@
+// Copyright (c) 2013 The Chromium Authors. 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 swap to output.
+ std::vector<const Target*> result;
+ result.reserve(targets->size());
+
+ for (const Target* target : *targets) {
+ if (target->testonly() == testonly)
+ result.push_back(target);
+ }
+
+ targets->swap(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 swap to 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->swap(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(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
new file mode 100644
index 00000000000..7d6017edb35
--- /dev/null
+++ b/gn/tools/gn/commands.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2013 The Chromium Authors. 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 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.
+//
+// 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);
+
+// 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
new file mode 100644
index 00000000000..0a545e8f1f4
--- /dev/null
+++ b/gn/tools/gn/compile_commands_writer.cc
@@ -0,0 +1,286 @@
+// 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/stringprintf.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/config_values_extractors.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, SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
+ &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, SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
+ Toolchain::TYPE_CC, &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, SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
+ Toolchain::TYPE_CXX, &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, SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
+ Toolchain::TYPE_OBJC, &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, SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
+ Toolchain::TYPE_OBJCXX, &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,
+ SourceFileType source_type,
+ Toolchain::ToolType tool_type,
+ EscapeOptions opts,
+ std::string* compile_commands) {
+ EscapeOptions no_quoting(opts);
+ no_quoting.inhibit_quoting = true;
+ const Tool* tool = target->toolchain()->GetTool(tool_type);
+ std::ostringstream command_out;
+
+ for (const auto& range : tool->command().ranges()) {
+ // TODO: this is emitting a bonus space prior to each substitution.
+ switch (range.type) {
+ case SUBSTITUTION_LITERAL:
+ EscapeStringToStream(command_out, range.literal, no_quoting);
+ break;
+ case SUBSTITUTION_OUTPUT:
+ path_output.WriteFiles(command_out, tool_outputs);
+ break;
+ case SUBSTITUTION_DEFINES:
+ command_out << flags.defines;
+ break;
+ case SUBSTITUTION_INCLUDE_DIRS:
+ command_out << flags.includes;
+ break;
+ case SUBSTITUTION_CFLAGS:
+ command_out << flags.cflags;
+ break;
+ case SUBSTITUTION_CFLAGS_C:
+ if (source_type == SOURCE_C)
+ command_out << flags.cflags_c;
+ break;
+ case SUBSTITUTION_CFLAGS_CC:
+ if (source_type == SOURCE_CPP)
+ command_out << flags.cflags_cc;
+ break;
+ case SUBSTITUTION_CFLAGS_OBJC:
+ if (source_type == SOURCE_M)
+ command_out << flags.cflags_objc;
+ break;
+ case SUBSTITUTION_CFLAGS_OBJCC:
+ if (source_type == SOURCE_MM)
+ command_out << flags.cflags_objcc;
+ break;
+ case SUBSTITUTION_LABEL:
+ case SUBSTITUTION_LABEL_NAME:
+ case SUBSTITUTION_ROOT_GEN_DIR:
+ case SUBSTITUTION_ROOT_OUT_DIR:
+ case SUBSTITUTION_TARGET_GEN_DIR:
+ case SUBSTITUTION_TARGET_OUT_DIR:
+ case SUBSTITUTION_TARGET_OUTPUT_NAME:
+ case SUBSTITUTION_SOURCE:
+ case SUBSTITUTION_SOURCE_NAME_PART:
+ case SUBSTITUTION_SOURCE_FILE_PART:
+ case SUBSTITUTION_SOURCE_DIR:
+ case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
+ case SUBSTITUTION_SOURCE_GEN_DIR:
+ case SUBSTITUTION_SOURCE_OUT_DIR:
+ case SUBSTITUTION_SOURCE_TARGET_RELATIVE:
+ EscapeStringToStream(command_out,
+ SubstitutionWriter::GetCompilerSubstitution(
+ target, source, range.type),
+ opts);
+ break;
+
+ // Other flags shouldn't be relevant to compiling C/C++/ObjC/ObjC++
+ // source files.
+ default:
+ NOTREACHED() << "Unsupported substitution for this type of target : "
+ << kSubstitutionNames[range.type];
+ 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.
+ SourceFileType source_type = GetSourceFileType(source);
+ if (source_type != SOURCE_CPP && source_type != SOURCE_C &&
+ source_type != SOURCE_M && source_type != SOURCE_MM)
+ continue;
+
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (!target->GetOutputFilesForSource(source, &tool_type, &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_type, 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,
+ 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::string json;
+ RenderJSON(build_settings, all_targets, &json);
+ if (!WriteFileIfChanged(output_path, json, err))
+ return false;
+ return true;
+}
diff --git a/gn/tools/gn/compile_commands_writer.h b/gn/tools/gn/compile_commands_writer.h
new file mode 100644
index 00000000000..2f1ec834877
--- /dev/null
+++ b/gn/tools/gn/compile_commands_writer.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_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:
+ 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::string* compile_commands);
+};
+
+#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
new file mode 100644
index 00000000000..b162ba5de83
--- /dev/null
+++ b/gn/tools/gn/compile_commands_writer_unittest.cc
@@ -0,0 +1,590 @@
+// 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 = std::make_unique<Tool>();
+ TestWithScope::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_precompiled_header_type(Tool::PCH_MSVC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ TestWithScope::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"));
+ cc_tool->set_precompiled_header_type(Tool::PCH_MSVC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ 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 = std::make_unique<Tool>();
+ TestWithScope::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_precompiled_header_type(Tool::PCH_GCC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ TestWithScope::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"));
+ cc_tool->set_precompiled_header_type(Tool::PCH_GCC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ 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);
+}
diff --git a/gn/tools/gn/config.cc b/gn/tools/gn/config.cc
new file mode 100644
index 00000000000..e021fe40b9d
--- /dev/null
+++ b/gn/tools/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 "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), resolved_(false) {}
+
+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
new file mode 100644
index 00000000000..ad049737330
--- /dev/null
+++ b/gn/tools/gn/config.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 The Chromium Authors. 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_;
+ 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
new file mode 100644
index 00000000000..4d8a41f8c5d
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..5cdfa7a8175
--- /dev/null
+++ b/gn/tools/gn/config_values.cc
@@ -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.
+
+#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_);
+
+ // 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
new file mode 100644
index 00000000000..bcab53c5245
--- /dev/null
+++ b/gn/tools/gn/config_values.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_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)
+ // =================================================================
+ // 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_;
+ // 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
new file mode 100644
index 00000000000..248d8c47c88
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..5bb42296b1e
--- /dev/null
+++ b/gn/tools/gn/config_values_extractors.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2013 The Chromium Authors. 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), cur_index_(-1) {}
+
+ 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_;
+};
+
+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
new file mode 100644
index 00000000000..5cfca79bced
--- /dev/null
+++ b/gn/tools/gn/config_values_extractors_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium Authors. 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
new file mode 100644
index 00000000000..ff4e4dfe252
--- /dev/null
+++ b/gn/tools/gn/config_values_generator.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2013 The Chromium Authors. 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)
+
+#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
new file mode 100644
index 00000000000..165f41a9532
--- /dev/null
+++ b/gn/tools/gn/config_values_generator.h
@@ -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.
+
+#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\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
new file mode 100644
index 00000000000..4e40efb22fd
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..89751f98e83
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..79edd78b3a8
--- /dev/null
+++ b/gn/tools/gn/create_bundle_target_generator.cc
@@ -0,0 +1,303 @@
+// 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 (!FillBundleDir(bundle_data.root_dir(), variables::kBundlePlugInsDir,
+ &bundle_data.plugins_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) {
+ 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->SwapValue(&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().swap(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().swap(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
new file mode 100644
index 00000000000..a7f1edca148
--- /dev/null
+++ b/gn/tools/gn/create_bundle_target_generator.h
@@ -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.
+
+#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
new file mode 100644
index 00000000000..497ef04ffb5
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..413e83bb69e
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..b2fd7162bd3
--- /dev/null
+++ b/gn/tools/gn/desc_builder.cc
@@ -0,0 +1,729 @@
+// 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/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/settings.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 ]
+// }
+//
+// 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 <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::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::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());
+ }
+
+ 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(),
+ LabelPtrLabelLess<Target>());
+
+ 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;
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (target_->GetOutputFilesForSource(source, &tool_type, &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->SetWithoutPathExpansion("plugins_dir",
+ RenderValue(bundle_data.plugins_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) {
+ std::vector<SourceFile> output_files;
+ target_->bundle_data().GetOutputsAsSourceFiles(target_->settings(),
+ &output_files);
+ 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
new file mode 100644
index 00000000000..1f0c395923f
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..328d5dacfe8
--- /dev/null
+++ b/gn/tools/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 "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/eclipse_writer.h b/gn/tools/gn/eclipse_writer.h
new file mode 100644
index 00000000000..e15254fbadb
--- /dev/null
+++ b/gn/tools/gn/eclipse_writer.h
@@ -0,0 +1,67 @@
+// 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_ECLIPSE_WRITER_H_
+#define TOOLS_GN_ECLIPSE_WRITER_H_
+
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+class BuildSettings;
+class Builder;
+class Err;
+class Target;
+
+class EclipseWriter {
+ public:
+ static bool RunAndWriteFile(const BuildSettings* build_settings,
+ const Builder& builder,
+ Err* err);
+
+ private:
+ EclipseWriter(const BuildSettings* build_settings,
+ const Builder& builder,
+ std::ostream& out);
+ ~EclipseWriter();
+
+ void Run();
+
+ // Populates |include_dirs_| with the include dirs of all the targets for the
+ // default toolchain.
+ void GetAllIncludeDirs();
+
+ // Populates |defines_| with the defines of all the targets for the default
+ // toolchain.
+ void GetAllDefines();
+
+ // Returns true if |target| uses the default toolchain.
+ bool UsesDefaultToolchain(const Target* target) const;
+
+ // Writes the XML settings file.
+ void WriteCDTSettings();
+
+ const BuildSettings* build_settings_;
+ const Builder& builder_;
+
+ // The output stream for the settings file.
+ std::ostream& out_;
+
+ // Eclipse languages for which the include dirs and defines apply.
+ std::vector<std::string> languages_;
+
+ // The include dirs of all the targets which use the default toolchain.
+ std::set<std::string> include_dirs_;
+
+ // The defines of all the targets which use the default toolchain.
+ std::map<std::string, std::string> defines_;
+
+ DISALLOW_COPY_AND_ASSIGN(EclipseWriter);
+};
+
+#endif // TOOLS_GN_ECLIPSE_WRITER_H_
diff --git a/gn/tools/gn/err.cc b/gn/tools/gn/err.cc
new file mode 100644
index 00000000000..fe4db20c969
--- /dev/null
+++ b/gn/tools/gn/err.cc
@@ -0,0 +1,192 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..90f31509310
--- /dev/null
+++ b/gn/tools/gn/err.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..53c8d898c5b
--- /dev/null
+++ b/gn/tools/gn/escape.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2013 The Chromium Authors. 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 "base/logging.h"
+#include "util/build_config.h"
+
+namespace {
+
+// A "1" in this lookup table means that char is valid in the Posix shell.
+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};
+
+// Append one character to the given string, escaping it for Ninja.
+//
+// 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 void NinjaEscapeChar(char ch, std::string* dest) {
+ if (ch == '$' || ch == ' ' || ch == ':')
+ dest->push_back('$');
+ dest->push_back(ch);
+}
+
+void EscapeStringToString_Ninja(const base::StringPiece& str,
+ const EscapeOptions& options,
+ std::string* dest,
+ bool* needed_quoting) {
+ for (const auto& elem : str)
+ NinjaEscapeChar(elem, dest);
+}
+
+void EscapeStringToString_NinjaPreformatted(const base::StringPiece& str,
+ std::string* dest) {
+ // Only Ninja-escape $.
+ for (const auto& elem : str) {
+ if (elem == '$')
+ dest->push_back('$');
+ dest->push_back(elem);
+ }
+}
+
+// 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
+void EscapeStringToString_WindowsNinjaFork(const base::StringPiece& str,
+ const EscapeOptions& options,
+ std::string* 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);
+
+ if (str.find_first_of(" \"") == std::string::npos) {
+ // Simple case, don't quote.
+ EscapeStringToString_Ninja(str, options, dest, needed_quoting);
+ } else {
+ if (!options.inhibit_quoting)
+ dest->push_back('"');
+
+ for (size_t i = 0; i < str.size(); i++) {
+ // Count backslashes in case they're followed by a quote.
+ size_t backslash_count = 0;
+ while (i < str.size() && str[i] == '\\') {
+ i++;
+ backslash_count++;
+ }
+ if (i == str.size()) {
+ // Backslashes at end of string. Backslash-escape all of them since
+ // they'll be followed by a quote.
+ dest->append(backslash_count * 2, '\\');
+ } else if (str[i] == '"') {
+ // 0 or more backslashes followed by a quote. Backslash-escape the
+ // backslashes, then backslash-escape the quote.
+ dest->append(backslash_count * 2 + 1, '\\');
+ dest->push_back('"');
+ } else {
+ // Non-special Windows character, just escape for Ninja. Also, add any
+ // backslashes we read previously, these are literals.
+ dest->append(backslash_count, '\\');
+ NinjaEscapeChar(str[i], dest);
+ }
+ }
+
+ if (!options.inhibit_quoting)
+ dest->push_back('"');
+ if (needed_quoting)
+ *needed_quoting = true;
+ }
+}
+
+void EscapeStringToString_PosixNinjaFork(const base::StringPiece& str,
+ const EscapeOptions& options,
+ std::string* dest,
+ bool* needed_quoting) {
+ 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->push_back('\\');
+ dest->push_back('$');
+ dest->push_back(elem);
+ } else if (elem == ':') {
+ // Colon is the only other Ninja special char, which is not special to
+ // the shell.
+ dest->push_back('$');
+ dest->push_back(':');
+ } else if (static_cast<unsigned>(elem) >= 0x80 ||
+ !kShellValid[static_cast<int>(elem)]) {
+ // All other invalid shell chars get backslash-escaped.
+ dest->push_back('\\');
+ dest->push_back(elem);
+ } else {
+ // Everything else is a literal.
+ dest->push_back(elem);
+ }
+ }
+}
+
+void EscapeStringToString(const base::StringPiece& str,
+ const EscapeOptions& options,
+ std::string* dest,
+ bool* needed_quoting) {
+ switch (options.mode) {
+ case ESCAPE_NONE:
+ dest->append(str.data(), str.size());
+ break;
+ case ESCAPE_NINJA:
+ EscapeStringToString_Ninja(str, options, dest, needed_quoting);
+ break;
+ case ESCAPE_NINJA_COMMAND:
+ switch (options.platform) {
+ case ESCAPE_PLATFORM_CURRENT:
+#if defined(OS_WIN)
+ EscapeStringToString_WindowsNinjaFork(str, options, dest,
+ needed_quoting);
+#else
+ EscapeStringToString_PosixNinjaFork(str, options, dest,
+ needed_quoting);
+#endif
+ break;
+ case ESCAPE_PLATFORM_WIN:
+ EscapeStringToString_WindowsNinjaFork(str, options, dest,
+ needed_quoting);
+ break;
+ case ESCAPE_PLATFORM_POSIX:
+ EscapeStringToString_PosixNinjaFork(str, options, dest,
+ needed_quoting);
+ break;
+ default:
+ NOTREACHED();
+ }
+ break;
+ case ESCAPE_NINJA_PREFORMATTED_COMMAND:
+ EscapeStringToString_NinjaPreformatted(str, dest);
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+} // namespace
+
+std::string EscapeString(const base::StringPiece& str,
+ const EscapeOptions& options,
+ bool* needed_quoting) {
+ std::string result;
+ result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
+ EscapeStringToString(str, options, &result, needed_quoting);
+ return result;
+}
+
+void EscapeStringToStream(std::ostream& out,
+ const base::StringPiece& str,
+ const EscapeOptions& options) {
+ std::string escaped;
+ EscapeStringToString(str, options, &escaped, nullptr);
+ if (!escaped.empty())
+ out.write(escaped.data(), escaped.size());
+}
diff --git a/gn/tools/gn/escape.h b/gn/tools/gn/escape.h
new file mode 100644
index 00000000000..d59a8eb2620
--- /dev/null
+++ b/gn/tools/gn/escape.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_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,
+
+ // 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
+ // 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 {
+ EscapeOptions()
+ : mode(ESCAPE_NONE),
+ platform(ESCAPE_PLATFORM_CURRENT),
+ inhibit_quoting(false) {}
+
+ EscapingMode mode;
+
+ // Controls how "fork" escaping is done. You will generally want to keep the
+ // default "current" platform.
+ EscapingPlatform platform;
+
+ // 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;
+};
+
+// 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
new file mode 100644
index 00000000000..0aa8adc45d3
--- /dev/null
+++ b/gn/tools/gn/escape_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2013 The Chromium Authors. 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, 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/example/.gn b/gn/tools/gn/example/.gn
new file mode 100644
index 00000000000..e5b6d4a3db7
--- /dev/null
+++ b/gn/tools/gn/example/.gn
@@ -0,0 +1,2 @@
+# The location of the build configuration file.
+buildconfig = "//build/BUILDCONFIG.gn"
diff --git a/gn/tools/gn/example/BUILD.gn b/gn/tools/gn/example/BUILD.gn
new file mode 100644
index 00000000000..a18390e6489
--- /dev/null
+++ b/gn/tools/gn/example/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+executable("hello") {
+ sources = [
+ "hello.cc",
+ ]
+
+ deps = [
+ ":hello_shared",
+ ":hello_static",
+ ]
+}
+
+shared_library("hello_shared") {
+ sources = [
+ "hello_shared.cc",
+ "hello_shared.h",
+ ]
+
+ defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
+}
+
+static_library("hello_static") {
+ sources = [
+ "hello_static.cc",
+ "hello_static.h",
+ ]
+}
diff --git a/gn/tools/gn/example/README.txt b/gn/tools/gn/example/README.txt
new file mode 100644
index 00000000000..d0ddeed8b47
--- /dev/null
+++ b/gn/tools/gn/example/README.txt
@@ -0,0 +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 GN build.
+
+Don't miss the ".gn" file in this directory!
diff --git a/gn/tools/gn/example/build/BUILD.gn b/gn/tools/gn/example/build/BUILD.gn
new file mode 100644
index 00000000000..8eae46a1aad
--- /dev/null
+++ b/gn/tools/gn/example/build/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("compiler_defaults") {
+ if (current_os == "linux") {
+ cflags = [
+ "-fPIC",
+ "-pthread",
+ ]
+ }
+}
+
+config("executable_ldconfig") {
+ ldflags = [
+ "-Wl,-rpath=\$ORIGIN/",
+ "-Wl,-rpath-link=",
+ ]
+}
diff --git a/gn/tools/gn/example/build/BUILDCONFIG.gn b/gn/tools/gn/example/build/BUILDCONFIG.gn
new file mode 100644
index 00000000000..e419fd96a8a
--- /dev/null
+++ b/gn/tools/gn/example/build/BUILDCONFIG.gn
@@ -0,0 +1,38 @@
+# Copyright 2014 The Chromium 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 = host_os
+}
+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_defaults" ]
+
+# Apply that default list to the binary target types.
+set_defaults("executable") {
+ configs = _shared_binary_target_configs
+
+ # Executables get this additional configuration.
+ configs += [ "//build:executable_ldconfig" ]
+}
+set_defaults("static_library") {
+ configs = _shared_binary_target_configs
+}
+set_defaults("shared_library") {
+ configs = _shared_binary_target_configs
+}
+set_defaults("source_set") {
+ configs = _shared_binary_target_configs
+}
+
+set_default_toolchain("//build/toolchain:gcc")
diff --git a/gn/tools/gn/example/build/toolchain/BUILD.gn b/gn/tools/gn/example/build/toolchain/BUILD.gn
new file mode 100644
index 00000000000..d9457d7eae4
--- /dev/null
+++ b/gn/tools/gn/example/build/toolchain/BUILD.gn
@@ -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.
+
+toolchain("gcc") {
+ tool("cc") {
+ depfile = "{{output}}.d"
+ 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",
+ ]
+ }
+
+ tool("cxx") {
+ depfile = "{{output}}.d"
+ 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",
+ ]
+ }
+
+ tool("alink") {
+ rspfile = "{{output}}.rsp"
+ command = "rm -f {{output}} && ar rcs {{output}} @$rspfile"
+ description = "AR {{target_output_name}}{{output_extension}}"
+ rspfile_content = "{{inputs}}"
+ outputs = [
+ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}",
+ ]
+ default_output_extension = ".a"
+ output_prefix = "lib"
+ }
+
+ tool("solink") {
+ soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so".
+ sofile = "{{output_dir}}/$soname"
+ rspfile = soname + ".rsp"
+
+ command = "g++ -shared {{ldflags}} -o $sofile -Wl,-soname=$soname @$rspfile"
+ rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
+
+ description = "SOLINK $soname"
+
+ # Use this for {{output_extension}} expansions unless a target manually
+ # overrides it (in which case {{output_extension}} will be what the target
+ # specifies).
+ default_output_extension = ".so"
+
+ # Use this for {{output_dir}} expansions unless a target manually overrides
+ # it (in which case {{output_dir}} will be what the target specifies).
+ default_output_dir = "{{root_out_dir}}"
+
+ outputs = [
+ sofile,
+ ]
+ link_output = sofile
+ depend_output = sofile
+ output_prefix = "lib"
+ }
+
+ 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}}"
+ description = "LINK $outfile"
+ default_output_dir = "{{root_out_dir}}"
+ rspfile_content = "{{inputs}}"
+ outputs = [
+ outfile,
+ ]
+ }
+
+ tool("stamp") {
+ command = "touch {{output}}"
+ description = "STAMP {{output}}"
+ }
+
+ tool("copy") {
+ command = "cp -af {{source}} {{output}}"
+ description = "COPY {{source}} {{output}}"
+ }
+}
diff --git a/gn/tools/gn/example/hello.cc b/gn/tools/gn/example/hello.cc
new file mode 100644
index 00000000000..c4aa4482949
--- /dev/null
+++ b/gn/tools/gn/example/hello.cc
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "hello_shared.h"
+#include "hello_static.h"
+
+int main(int argc, char* argv[]) {
+ printf("%s, %s\n", GetStaticText(), GetSharedText());
+ return 0;
+}
diff --git a/gn/tools/gn/example/hello_shared.cc b/gn/tools/gn/example/hello_shared.cc
new file mode 100644
index 00000000000..58be84c27b4
--- /dev/null
+++ b/gn/tools/gn/example/hello_shared.cc
@@ -0,0 +1,9 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hello_shared.h"
+
+const char* GetSharedText() {
+ return "world";
+}
diff --git a/gn/tools/gn/example/hello_shared.h b/gn/tools/gn/example/hello_shared.h
new file mode 100644
index 00000000000..7af804b3be4
--- /dev/null
+++ b/gn/tools/gn/example/hello_shared.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. 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_EXAMPLE_HELLO_SHARED_H_
+#define TOOLS_GN_EXAMPLE_HELLO_SHARED_H_
+
+#if defined(WIN32)
+
+#if defined(HELLO_SHARED_IMPLEMENTATION)
+#define HELLO_EXPORT __declspec(dllexport)
+#define HELLO_EXPORT_PRIVATE __declspec(dllexport)
+#else
+#define HELLO_EXPORT __declspec(dllimport)
+#define HELLO_EXPORT_PRIVATE __declspec(dllimport)
+#endif // defined(HELLO_SHARED_IMPLEMENTATION)
+
+#else
+
+#if defined(HELLO_SHARED_IMPLEMENTATION)
+#define HELLO_EXPORT __attribute__((visibility("default")))
+#define HELLO_EXPORT_PRIVATE __attribute__((visibility("default")))
+#else
+#define HELLO_EXPORT
+#define HELLO_EXPORT_PRIVATE
+#endif // defined(HELLO_SHARED_IMPLEMENTATION)
+
+#endif
+
+HELLO_EXPORT const char* GetSharedText();
+
+#endif // TOOLS_GN_EXAMPLE_HELLO_SHARED_H_
diff --git a/gn/tools/gn/example/hello_static.cc b/gn/tools/gn/example/hello_static.cc
new file mode 100644
index 00000000000..cdf4e67b05c
--- /dev/null
+++ b/gn/tools/gn/example/hello_static.cc
@@ -0,0 +1,9 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hello_static.h"
+
+const char* GetStaticText() {
+ return "Hello";
+}
diff --git a/gn/tools/gn/example/hello_static.h b/gn/tools/gn/example/hello_static.h
new file mode 100644
index 00000000000..f15a6336d2a
--- /dev/null
+++ b/gn/tools/gn/example/hello_static.h
@@ -0,0 +1,10 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_EXAMPLE_HELLO_STATIC_H_
+#define TOOLS_GN_EXAMPLE_HELLO_STATIC_H_
+
+const char* GetStaticText();
+
+#endif // TOOLS_GN_EXAMPLE_HELLO_STATIC_H_
diff --git a/gn/tools/gn/exec_process.cc b/gn/tools/gn/exec_process.cc
new file mode 100644
index 00000000000..5c0619ccb71
--- /dev/null
+++ b/gn/tools/gn/exec_process.cc
@@ -0,0 +1,280 @@
+// Copyright 2014 The Chromium Authors. 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/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
new file mode 100644
index 00000000000..278d53928ed
--- /dev/null
+++ b/gn/tools/gn/exec_process.h
@@ -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.
+
+#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
new file mode 100644
index 00000000000..323a07048ab
--- /dev/null
+++ b/gn/tools/gn/exec_process_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 "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) {
+ base::ScopedTempDir temp_dir;
+ 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) {
+ base::ScopedTempDir temp_dir;
+ std::string std_out, std_err;
+ int exit_code;
+
+ ASSERT_TRUE(ExecPython(
+ "import sys; print 'o' * 10000; print >>sys.stderr, 'e' * 10000",
+ &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(
+ "import sys; print >>sys.stderr, 'e' * 10000; 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(
+ "import sys; sys.stdout.close(); print >>sys.stderr, 'e' * 10000",
+ &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
new file mode 100644
index 00000000000..e8f06965f95
--- /dev/null
+++ b/gn/tools/gn/filesystem_utils.cc
@@ -0,0 +1,1059 @@
+// Copyright (c) 2013 The Chromium Authors. 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);
+ }
+}
+
+} // 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();
+
+ if (source_root.size() > path.size())
+ 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,
+ path.substr(0, source_root.size())))
+ after_common_index = source_root.size();
+ else
+ return false;
+ } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
+ DoesBeginWindowsDriveLetter(path.substr(1))) {
+ // Handle "/C:/foo"
+ if (AreAbsoluteWindowsPathsEqual(source_root,
+ path.substr(1, source_root.size())))
+ after_common_index = source_root.size() + 1;
+ else
+ return false;
+ } else {
+ return false;
+ }
+
+ // If we get here, there's a match and after_common_index identifies the
+ // part after it.
+
+ // The base may or may not have a trailing slash, so skip all slashes from
+ // the path after our prefix match.
+ size_t first_after_slash = after_common_index;
+ while (first_after_slash < path.size() && 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.size()) == source_root) {
+ // The base may or may not have a trailing slash, so skip all slashes from
+ // the path after our prefix match.
+ size_t first_after_slash = source_root.size();
+ while (first_after_slash < path.size() && 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 aboslute path instead.
+ if (base_components[0] != target_components[0])
+ return target;
+#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(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
new file mode 100644
index 00000000000..220c4f83ada
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..87e37f80d5e
--- /dev/null
+++ b/gn/tools/gn/filesystem_utils_unittest.cc
@@ -0,0 +1,846 @@
+// Copyright (c) 2013 The Chromium Authors. 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"),
+ 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());
+}
diff --git a/gn/tools/gn/format_test_data/001.gn b/gn/tools/gn/format_test_data/001.gn
new file mode 100644
index 00000000000..c35c27951fd
--- /dev/null
+++ b/gn/tools/gn/format_test_data/001.gn
@@ -0,0 +1,2 @@
+# Test.
+executable("test"){}
diff --git a/gn/tools/gn/format_test_data/001.golden b/gn/tools/gn/format_test_data/001.golden
new file mode 100644
index 00000000000..31c9069d2b2
--- /dev/null
+++ b/gn/tools/gn/format_test_data/001.golden
@@ -0,0 +1,3 @@
+# Test.
+executable("test") {
+}
diff --git a/gn/tools/gn/format_test_data/002.gn b/gn/tools/gn/format_test_data/002.gn
new file mode 100644
index 00000000000..34043b5ed30
--- /dev/null
+++ b/gn/tools/gn/format_test_data/002.gn
@@ -0,0 +1,6 @@
+executable("test") {
+ sources = [
+ "stuff.cc",
+ "things.cc"
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/002.golden b/gn/tools/gn/format_test_data/002.golden
new file mode 100644
index 00000000000..cd8f38810c4
--- /dev/null
+++ b/gn/tools/gn/format_test_data/002.golden
@@ -0,0 +1,6 @@
+executable("test") {
+ sources = [
+ "stuff.cc",
+ "things.cc",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/003.gn b/gn/tools/gn/format_test_data/003.gn
new file mode 100644
index 00000000000..429165a285c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/003.gn
@@ -0,0 +1,10 @@
+executable("test") {
+ sources = [
+ "stuff.cc",
+ "things.cc"
+ ]
+
+ deps = [
+ "//base",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/003.golden b/gn/tools/gn/format_test_data/003.golden
new file mode 100644
index 00000000000..2fdcb9e38ba
--- /dev/null
+++ b/gn/tools/gn/format_test_data/003.golden
@@ -0,0 +1,10 @@
+executable("test") {
+ sources = [
+ "stuff.cc",
+ "things.cc",
+ ]
+
+ deps = [
+ "//base",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/004.gn b/gn/tools/gn/format_test_data/004.gn
new file mode 100644
index 00000000000..f36d039cf4c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/004.gn
@@ -0,0 +1,10 @@
+# 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/004.golden b/gn/tools/gn/format_test_data/004.golden
new file mode 100644
index 00000000000..68373d10144
--- /dev/null
+++ b/gn/tools/gn/format_test_data/004.golden
@@ -0,0 +1,13 @@
+# 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/tools/gn/format_test_data/005.gn
new file mode 100644
index 00000000000..07b4ef372c0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/005.gn
@@ -0,0 +1,5 @@
+# This is a separated block comment that mustn't be attached the to target
+# below, and should be separated by a single blank line.
+
+executable("test") {
+}
diff --git a/gn/tools/gn/format_test_data/005.golden b/gn/tools/gn/format_test_data/005.golden
new file mode 100644
index 00000000000..07b4ef372c0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/005.golden
@@ -0,0 +1,5 @@
+# This is a separated block comment that mustn't be attached the to target
+# below, and should be separated by a single blank line.
+
+executable("test") {
+}
diff --git a/gn/tools/gn/format_test_data/006.gn b/gn/tools/gn/format_test_data/006.gn
new file mode 100644
index 00000000000..737fceaae0e
--- /dev/null
+++ b/gn/tools/gn/format_test_data/006.gn
@@ -0,0 +1,9 @@
+# This is a separated block comment that mustn't be attached the to target
+# below, and should be separated by a single blank line.
+
+
+
+
+
+executable("test") {
+}
diff --git a/gn/tools/gn/format_test_data/006.golden b/gn/tools/gn/format_test_data/006.golden
new file mode 100644
index 00000000000..07b4ef372c0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/006.golden
@@ -0,0 +1,5 @@
+# This is a separated block comment that mustn't be attached the to target
+# below, and should be separated by a single blank line.
+
+executable("test") {
+}
diff --git a/gn/tools/gn/format_test_data/007.gn b/gn/tools/gn/format_test_data/007.gn
new file mode 100644
index 00000000000..3cd002d8606
--- /dev/null
+++ b/gn/tools/gn/format_test_data/007.gn
@@ -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/007.golden b/gn/tools/gn/format_test_data/007.golden
new file mode 100644
index 00000000000..610fa3fead5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/007.golden
@@ -0,0 +1,11 @@
+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/tools/gn/format_test_data/008.gn
new file mode 100644
index 00000000000..16b4a8e32fb
--- /dev/null
+++ b/gn/tools/gn/format_test_data/008.gn
@@ -0,0 +1 @@
+if (is_win) { sources = ["win.cc"] }
diff --git a/gn/tools/gn/format_test_data/008.golden b/gn/tools/gn/format_test_data/008.golden
new file mode 100644
index 00000000000..aec281ab952
--- /dev/null
+++ b/gn/tools/gn/format_test_data/008.golden
@@ -0,0 +1,5 @@
+if (is_win) {
+ sources = [
+ "win.cc",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/009.gn b/gn/tools/gn/format_test_data/009.gn
new file mode 100644
index 00000000000..e47b62175ae
--- /dev/null
+++ b/gn/tools/gn/format_test_data/009.gn
@@ -0,0 +1,2 @@
+if (is_win) { sources = ["win.cc"] }
+else { sources = ["linux.cc"] }
diff --git a/gn/tools/gn/format_test_data/009.golden b/gn/tools/gn/format_test_data/009.golden
new file mode 100644
index 00000000000..af27d797005
--- /dev/null
+++ b/gn/tools/gn/format_test_data/009.golden
@@ -0,0 +1,9 @@
+if (is_win) {
+ sources = [
+ "win.cc",
+ ]
+} else {
+ sources = [
+ "linux.cc",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/010.gn b/gn/tools/gn/format_test_data/010.gn
new file mode 100644
index 00000000000..70004a75158
--- /dev/null
+++ b/gn/tools/gn/format_test_data/010.gn
@@ -0,0 +1,2 @@
+if (is_win) { sources = ["win.cc"] }
+else if (is_linux) { sources = ["linux.cc"] }
diff --git a/gn/tools/gn/format_test_data/010.golden b/gn/tools/gn/format_test_data/010.golden
new file mode 100644
index 00000000000..64fabb9ebc5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/010.golden
@@ -0,0 +1,9 @@
+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/tools/gn/format_test_data/011.gn
new file mode 100644
index 00000000000..c2c5bf755ea
--- /dev/null
+++ b/gn/tools/gn/format_test_data/011.gn
@@ -0,0 +1,4 @@
+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/011.golden b/gn/tools/gn/format_test_data/011.golden
new file mode 100644
index 00000000000..4ce009ea1dc
--- /dev/null
+++ b/gn/tools/gn/format_test_data/011.golden
@@ -0,0 +1,13 @@
+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/tools/gn/format_test_data/012.gn
new file mode 100644
index 00000000000..1da385c00e8
--- /dev/null
+++ b/gn/tools/gn/format_test_data/012.gn
@@ -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/012.golden b/gn/tools/gn/format_test_data/012.golden
new file mode 100644
index 00000000000..a0049b2d55d
--- /dev/null
+++ b/gn/tools/gn/format_test_data/012.golden
@@ -0,0 +1,22 @@
+# (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/tools/gn/format_test_data/013.gn
new file mode 100644
index 00000000000..06183a82d48
--- /dev/null
+++ b/gn/tools/gn/format_test_data/013.gn
@@ -0,0 +1,7 @@
+defines = [
+ # Separate comment inside expression.
+
+ # Connected comment.
+ "WEE",
+ "BLORPY",
+]
diff --git a/gn/tools/gn/format_test_data/013.golden b/gn/tools/gn/format_test_data/013.golden
new file mode 100644
index 00000000000..d84b7d20f8e
--- /dev/null
+++ b/gn/tools/gn/format_test_data/013.golden
@@ -0,0 +1,7 @@
+defines = [
+ # Separate comment inside expression.
+
+ # Connected comment.
+ "WEE",
+ "BLORPY",
+]
diff --git a/gn/tools/gn/format_test_data/014.gn b/gn/tools/gn/format_test_data/014.gn
new file mode 100644
index 00000000000..2d0170d862a
--- /dev/null
+++ b/gn/tools/gn/format_test_data/014.gn
@@ -0,0 +1,6 @@
+defines = [
+
+ # Connected comment.
+ "WEE",
+ "BLORPY",
+]
diff --git a/gn/tools/gn/format_test_data/014.golden b/gn/tools/gn/format_test_data/014.golden
new file mode 100644
index 00000000000..c0ba5bb3981
--- /dev/null
+++ b/gn/tools/gn/format_test_data/014.golden
@@ -0,0 +1,5 @@
+defines = [
+ # Connected comment.
+ "WEE",
+ "BLORPY",
+]
diff --git a/gn/tools/gn/format_test_data/015.gn b/gn/tools/gn/format_test_data/015.gn
new file mode 100644
index 00000000000..f065095f656
--- /dev/null
+++ b/gn/tools/gn/format_test_data/015.gn
@@ -0,0 +1,4 @@
+if (is_win) {
+ sources = ["a.cc"]
+ # Some comment at end.
+}
diff --git a/gn/tools/gn/format_test_data/015.golden b/gn/tools/gn/format_test_data/015.golden
new file mode 100644
index 00000000000..553f01ce105
--- /dev/null
+++ b/gn/tools/gn/format_test_data/015.golden
@@ -0,0 +1,6 @@
+if (is_win) {
+ sources = [
+ "a.cc",
+ ]
+ # Some comment at end.
+}
diff --git a/gn/tools/gn/format_test_data/016.gn b/gn/tools/gn/format_test_data/016.gn
new file mode 100644
index 00000000000..00a79922828
--- /dev/null
+++ b/gn/tools/gn/format_test_data/016.gn
@@ -0,0 +1 @@
+something = !is_win && is_linux || is_mac && !(is_freebsd || is_ios)
diff --git a/gn/tools/gn/format_test_data/016.golden b/gn/tools/gn/format_test_data/016.golden
new file mode 100644
index 00000000000..3f4f15bc507
--- /dev/null
+++ b/gn/tools/gn/format_test_data/016.golden
@@ -0,0 +1 @@
+something = (!is_win && is_linux) || (is_mac && !(is_freebsd || is_ios))
diff --git a/gn/tools/gn/format_test_data/017.gn b/gn/tools/gn/format_test_data/017.gn
new file mode 100644
index 00000000000..225ae2fe30c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/017.gn
@@ -0,0 +1,15 @@
+executable("win" # Suffix comment on arg.
+ # And a strangely positioned line comment for some reason
+ ) {
+ defines = [ # Defines comment, suffix at end position.
+ ]
+
+ deps = [
+ # Deps comment, should be forced multiline.
+ ]
+ sources = [
+ "a.cc"
+ # End of single sources comment, should be forced multiline.
+ ]
+ # End of block comment.
+}
diff --git a/gn/tools/gn/format_test_data/017.golden b/gn/tools/gn/format_test_data/017.golden
new file mode 100644
index 00000000000..d9d7ad9a472
--- /dev/null
+++ b/gn/tools/gn/format_test_data/017.golden
@@ -0,0 +1,16 @@
+executable("win" # Suffix comment on arg.
+
+ # And a strangely positioned line comment for some reason
+ ) {
+ defines = [] # Defines comment, suffix at end position.
+
+ deps = [
+ # Deps comment, should be forced multiline.
+ ]
+ sources = [
+ "a.cc",
+ # End of single sources comment, should be forced multiline.
+ ]
+
+ # End of block comment.
+}
diff --git a/gn/tools/gn/format_test_data/018.gn b/gn/tools/gn/format_test_data/018.gn
new file mode 100644
index 00000000000..e63ef9def15
--- /dev/null
+++ b/gn/tools/gn/format_test_data/018.gn
@@ -0,0 +1,3 @@
+# Don't crash when no block on a function call.
+
+import("wee.gni")
diff --git a/gn/tools/gn/format_test_data/018.golden b/gn/tools/gn/format_test_data/018.golden
new file mode 100644
index 00000000000..e63ef9def15
--- /dev/null
+++ b/gn/tools/gn/format_test_data/018.golden
@@ -0,0 +1,3 @@
+# Don't crash when no block on a function call.
+
+import("wee.gni")
diff --git a/gn/tools/gn/format_test_data/019.gn b/gn/tools/gn/format_test_data/019.gn
new file mode 100644
index 00000000000..48772b9ded8
--- /dev/null
+++ b/gn/tools/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",
+ "//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
new file mode 100644
index 00000000000..c800ed1fe42
--- /dev/null
+++ b/gn/tools/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",
+ "//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/020.gn b/gn/tools/gn/format_test_data/020.gn
new file mode 100644
index 00000000000..96de16448bc
--- /dev/null
+++ b/gn/tools/gn/format_test_data/020.gn
@@ -0,0 +1,5 @@
+cflags = [
+ "/wd4267", # size_t -> int.
+ "/wd4324", # Structure was padded due to __declspec(align()), which is
+ # uninteresting.
+]
diff --git a/gn/tools/gn/format_test_data/020.golden b/gn/tools/gn/format_test_data/020.golden
new file mode 100644
index 00000000000..96de16448bc
--- /dev/null
+++ b/gn/tools/gn/format_test_data/020.golden
@@ -0,0 +1,5 @@
+cflags = [
+ "/wd4267", # size_t -> int.
+ "/wd4324", # Structure was padded due to __declspec(align()), which is
+ # uninteresting.
+]
diff --git a/gn/tools/gn/format_test_data/021.gn b/gn/tools/gn/format_test_data/021.gn
new file mode 100644
index 00000000000..355735db135
--- /dev/null
+++ b/gn/tools/gn/format_test_data/021.gn
@@ -0,0 +1,33 @@
+f(aaaaaaaaaaaaaaaaaaa)
+
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa)
+
+# Exactly 80 wide.
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaa)
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaa)
+
+aaaaaaaaaa(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaa + aaaaaaaaaa.aaaaaaaaaaaaaaa)
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaa(aaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa))
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa, aaaaaaaaaaaa)
+
+# 80 ---------------------------------------------------------------------------
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa, aaaaaaaaaaaa)
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa)
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa)
+
+somefunction(someotherFunction(ddddddddddddddddddddddddddddddddddd, ddddddddddddddddddddddddddddd), test)
diff --git a/gn/tools/gn/format_test_data/021.golden b/gn/tools/gn/format_test_data/021.golden
new file mode 100644
index 00000000000..f17c5fd83c6
--- /dev/null
+++ b/gn/tools/gn/format_test_data/021.golden
@@ -0,0 +1,61 @@
+f(aaaaaaaaaaaaaaaaaaa)
+
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa)
+
+# Exactly 80 wide.
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaa)
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaa)
+
+aaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaa + aaaaaaaaaa.aaaaaaaaaaaaaaa)
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaa(aaaaaaaaaaaaa,
+ aaaaaaaaaaaaa,
+ aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa))
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaa +
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa,
+ aaaaaaaaaaaa)
+
+# 80 ---------------------------------------------------------------------------
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa,
+ aaaaaaaaaaaa)
+
+aaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa)
+
+aaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa)
+
+somefunction(someotherFunction(ddddddddddddddddddddddddddddddddddd,
+ ddddddddddddddddddddddddddddd),
+ test)
diff --git a/gn/tools/gn/format_test_data/022.gn b/gn/tools/gn/format_test_data/022.gn
new file mode 100644
index 00000000000..a67ed24d6bb
--- /dev/null
+++ b/gn/tools/gn/format_test_data/022.gn
@@ -0,0 +1,6 @@
+executable(something[0]) {
+ if (weeeeee.stuff) {
+ x = a.b
+ y = a[8]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/022.golden b/gn/tools/gn/format_test_data/022.golden
new file mode 100644
index 00000000000..a67ed24d6bb
--- /dev/null
+++ b/gn/tools/gn/format_test_data/022.golden
@@ -0,0 +1,6 @@
+executable(something[0]) {
+ if (weeeeee.stuff) {
+ x = a.b
+ y = a[8]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/023.gn b/gn/tools/gn/format_test_data/023.gn
new file mode 100644
index 00000000000..8a1b51926b7
--- /dev/null
+++ b/gn/tools/gn/format_test_data/023.gn
@@ -0,0 +1,38 @@
+f(aaaaaaaaaaaaaaaaaaa) {}
+
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa) {}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaa) {}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaa) {}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa, aaaaaaaaa) {}
+
+aaaaaaaaaa(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaa + aaaaaaaaaa.aaaaaaaaaaaaaaa) {}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaa) {}
+
+aaaaaaa(aaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa)) {}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa, aaaaaaaaaaaa) {}
+
+# 80 ---------------------------------------------------------------------------
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa, aaaaaaaaaaaa) {}
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa) {}
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaa) {}
+
+somefunction(someotherFunction(ddddddddddddddddddddddddddddddddddd, ddddddddddddddddddddddddddddd), test) {}
diff --git a/gn/tools/gn/format_test_data/023.golden b/gn/tools/gn/format_test_data/023.golden
new file mode 100644
index 00000000000..bab6e45eda3
--- /dev/null
+++ b/gn/tools/gn/format_test_data/023.golden
@@ -0,0 +1,88 @@
+f(aaaaaaaaaaaaaaaaaaa) {
+}
+
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa) {
+}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaa, aaaaaa) {
+}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaa,
+ aaaaa) {
+}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {
+}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {
+}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {
+}
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa,
+ aaaaaaaaa) {
+}
+
+aaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaa + aaaaaaaaaa.aaaaaaaaaaaaaaa) {
+}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaa) {
+}
+
+aaaaaaa(aaaaaaaaaaaaa,
+ aaaaaaaaaaaaa,
+ aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa)) {
+}
+
+# 80 ---------------------------------------------------------------------------
+f(aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaaaaaaaaaaaa +
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {
+}
+
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa,
+ aaaaaaaaaaaa) {
+}
+
+# 80 ---------------------------------------------------------------------------
+aaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa,
+ aaaaaaaaaaaa) {
+}
+
+aaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa) {
+}
+
+aaaaaaaaaaaa(
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+ aaaaaaaaaaaa) {
+}
+
+somefunction(someotherFunction(ddddddddddddddddddddddddddddddddddd,
+ ddddddddddddddddddddddddddddd),
+ test) {
+}
diff --git a/gn/tools/gn/format_test_data/024.gn b/gn/tools/gn/format_test_data/024.gn
new file mode 100644
index 00000000000..5034cdc84e7
--- /dev/null
+++ b/gn/tools/gn/format_test_data/024.gn
@@ -0,0 +1 @@
+somefunc(){}
diff --git a/gn/tools/gn/format_test_data/024.golden b/gn/tools/gn/format_test_data/024.golden
new file mode 100644
index 00000000000..f2c755dcb8b
--- /dev/null
+++ b/gn/tools/gn/format_test_data/024.golden
@@ -0,0 +1,2 @@
+somefunc() {
+}
diff --git a/gn/tools/gn/format_test_data/025.gn b/gn/tools/gn/format_test_data/025.gn
new file mode 100644
index 00000000000..959ec8a3c52
--- /dev/null
+++ b/gn/tools/gn/format_test_data/025.gn
@@ -0,0 +1,5 @@
+# Various parenthesis maintenance/trimming.
+if ((a.b && c[d] + ((x < 4 || ((z + b)))))) {
+ y = z - (y - (x - !(b-d)))
+ a += ["a", "b", "c"] - (["x"] - ["y"])
+}
diff --git a/gn/tools/gn/format_test_data/025.golden b/gn/tools/gn/format_test_data/025.golden
new file mode 100644
index 00000000000..e6d08692a4f
--- /dev/null
+++ b/gn/tools/gn/format_test_data/025.golden
@@ -0,0 +1,9 @@
+# Various parenthesis maintenance/trimming.
+if (a.b && c[d] + (x < 4 || z + b)) {
+ y = z - (y - (x - !(b - d)))
+ a += [
+ "a",
+ "b",
+ "c",
+ ] - ([ "x" ] - [ "y" ])
+}
diff --git a/gn/tools/gn/format_test_data/026.gn b/gn/tools/gn/format_test_data/026.gn
new file mode 100644
index 00000000000..8cf2028a72a
--- /dev/null
+++ b/gn/tools/gn/format_test_data/026.gn
@@ -0,0 +1,6 @@
+# 80 ---------------------------------------------------------------------------
+args = [
+ rebase_path("$target_gen_dir/experimental-libraries.cc", root_build_dir),
+ "EXPERIMENTAL",
+ v8_compress_startup_data
+] + rebase_path(sources, root_build_dir)
diff --git a/gn/tools/gn/format_test_data/026.golden b/gn/tools/gn/format_test_data/026.golden
new file mode 100644
index 00000000000..a1d1d3f936d
--- /dev/null
+++ b/gn/tools/gn/format_test_data/026.golden
@@ -0,0 +1,7 @@
+# 80 ---------------------------------------------------------------------------
+args =
+ [
+ rebase_path("$target_gen_dir/experimental-libraries.cc", root_build_dir),
+ "EXPERIMENTAL",
+ v8_compress_startup_data,
+ ] + rebase_path(sources, root_build_dir)
diff --git a/gn/tools/gn/format_test_data/027.gn b/gn/tools/gn/format_test_data/027.gn
new file mode 100644
index 00000000000..cc5fe5fa942
--- /dev/null
+++ b/gn/tools/gn/format_test_data/027.gn
@@ -0,0 +1,3 @@
+# 80 ---------------------------------------------------------------------------
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = [
+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaa"]
diff --git a/gn/tools/gn/format_test_data/027.golden b/gn/tools/gn/format_test_data/027.golden
new file mode 100644
index 00000000000..05f0eb57931
--- /dev/null
+++ b/gn/tools/gn/format_test_data/027.golden
@@ -0,0 +1,5 @@
+# 80 ---------------------------------------------------------------------------
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = [
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaa",
+]
diff --git a/gn/tools/gn/format_test_data/028.gn b/gn/tools/gn/format_test_data/028.gn
new file mode 100644
index 00000000000..d84e1f8c957
--- /dev/null
+++ b/gn/tools/gn/format_test_data/028.gn
@@ -0,0 +1,9 @@
+# Don't separate these.
+import("wee.gni")
+import("waa.gni")
+
+import("woo.gni")
+
+
+
+import("blah.gni")
diff --git a/gn/tools/gn/format_test_data/028.golden b/gn/tools/gn/format_test_data/028.golden
new file mode 100644
index 00000000000..a1d54c550a2
--- /dev/null
+++ b/gn/tools/gn/format_test_data/028.golden
@@ -0,0 +1,7 @@
+# Don't separate these.
+import("wee.gni")
+import("waa.gni")
+
+import("woo.gni")
+
+import("blah.gni")
diff --git a/gn/tools/gn/format_test_data/029.gn b/gn/tools/gn/format_test_data/029.gn
new file mode 100644
index 00000000000..ac67830164e
--- /dev/null
+++ b/gn/tools/gn/format_test_data/029.gn
@@ -0,0 +1,9 @@
+# Don't separate small simple statements.
+is_android = false
+is_chromeos = false
+is_ios = false
+is_linux -= false
+is_mac = true
+is_nacl = false
+is_posix += true
+is_win = false
diff --git a/gn/tools/gn/format_test_data/029.golden b/gn/tools/gn/format_test_data/029.golden
new file mode 100644
index 00000000000..ac67830164e
--- /dev/null
+++ b/gn/tools/gn/format_test_data/029.golden
@@ -0,0 +1,9 @@
+# Don't separate small simple statements.
+is_android = false
+is_chromeos = false
+is_ios = false
+is_linux -= false
+is_mac = true
+is_nacl = false
+is_posix += true
+is_win = false
diff --git a/gn/tools/gn/format_test_data/030.gn b/gn/tools/gn/format_test_data/030.gn
new file mode 100644
index 00000000000..adac9a82416
--- /dev/null
+++ b/gn/tools/gn/format_test_data/030.gn
@@ -0,0 +1,12 @@
+# Don't separate simple statements in a scope.
+
+import("//testing/test.gni")
+
+test("something") {
+ if (is_linux) {
+ sources -= [ "file_version_info_unittest.cc" ]
+ sources += [ "nix/xdg_util_unittest.cc" ]
+ defines = [ "USE_SYMBOLIZE" ]
+ configs += [ "//build/config/linux:glib" ]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/030.golden b/gn/tools/gn/format_test_data/030.golden
new file mode 100644
index 00000000000..adac9a82416
--- /dev/null
+++ b/gn/tools/gn/format_test_data/030.golden
@@ -0,0 +1,12 @@
+# Don't separate simple statements in a scope.
+
+import("//testing/test.gni")
+
+test("something") {
+ if (is_linux) {
+ sources -= [ "file_version_info_unittest.cc" ]
+ sources += [ "nix/xdg_util_unittest.cc" ]
+ defines = [ "USE_SYMBOLIZE" ]
+ configs += [ "//build/config/linux:glib" ]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/031.gn b/gn/tools/gn/format_test_data/031.gn
new file mode 100644
index 00000000000..d83d4234a70
--- /dev/null
+++ b/gn/tools/gn/format_test_data/031.gn
@@ -0,0 +1,8 @@
+deps += [
+ ":packed_extra_resources",
+ ":packed_resources",
+
+ # This shouldn't crash.
+
+ # This shouldn't crash 2.
+ ]
diff --git a/gn/tools/gn/format_test_data/031.golden b/gn/tools/gn/format_test_data/031.golden
new file mode 100644
index 00000000000..6653951f16a
--- /dev/null
+++ b/gn/tools/gn/format_test_data/031.golden
@@ -0,0 +1,8 @@
+deps += [
+ ":packed_extra_resources",
+ ":packed_resources",
+
+ # This shouldn't crash.
+
+ # This shouldn't crash 2.
+]
diff --git a/gn/tools/gn/format_test_data/032.gn b/gn/tools/gn/format_test_data/032.gn
new file mode 100644
index 00000000000..d7ea7e5f8f4
--- /dev/null
+++ b/gn/tools/gn/format_test_data/032.gn
@@ -0,0 +1,6 @@
+# Make sure continued conditions are aligned.
+if (something) {
+ if (false) {
+ } else if (is_linux && !is_android && current_cpu == "x64" && !disable_iterator_debugging) {
+ }
+}
diff --git a/gn/tools/gn/format_test_data/032.golden b/gn/tools/gn/format_test_data/032.golden
new file mode 100644
index 00000000000..aeca8963933
--- /dev/null
+++ b/gn/tools/gn/format_test_data/032.golden
@@ -0,0 +1,7 @@
+# Make sure continued conditions are aligned.
+if (something) {
+ if (false) {
+ } else if (is_linux && !is_android && current_cpu == "x64" &&
+ !disable_iterator_debugging) {
+ }
+}
diff --git a/gn/tools/gn/format_test_data/033.gn b/gn/tools/gn/format_test_data/033.gn
new file mode 100644
index 00000000000..6767acd6b93
--- /dev/null
+++ b/gn/tools/gn/format_test_data/033.gn
@@ -0,0 +1,8 @@
+# Don't attach trailing comments too far back.
+if (!is_android) {
+ source_set("tcmalloc") {
+ if (is_win) {
+ ldflags = [ "/ignore:4006:4221" ]
+ } # is_win
+ } # source_set
+} # !is_android
diff --git a/gn/tools/gn/format_test_data/033.golden b/gn/tools/gn/format_test_data/033.golden
new file mode 100644
index 00000000000..6767acd6b93
--- /dev/null
+++ b/gn/tools/gn/format_test_data/033.golden
@@ -0,0 +1,8 @@
+# Don't attach trailing comments too far back.
+if (!is_android) {
+ source_set("tcmalloc") {
+ if (is_win) {
+ ldflags = [ "/ignore:4006:4221" ]
+ } # is_win
+ } # source_set
+} # !is_android
diff --git a/gn/tools/gn/format_test_data/034.gn b/gn/tools/gn/format_test_data/034.gn
new file mode 100644
index 00000000000..33f5eadceb5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/034.gn
@@ -0,0 +1,13 @@
+# Special case for 'args': If args[N] starts with '-' and args[N+1] is a call to
+# rebase_path, keep them as a pair, rather than breaking into individual items.
+action("wee") {
+ if (something) {
+ args = [
+ "--depfile", rebase_path(depfile, root_build_dir),
+ "--android-sdk", rebase_path(android_sdk, root_build_dir),
+ "--android-sdk-tools",
+ rebase_path(android_sdk_build_tools, root_build_dir),
+ "--android-manifest", rebase_path(android_manifest, root_build_dir),
+ ]
+ }
+}
diff --git a/gn/tools/gn/format_test_data/035.gn b/gn/tools/gn/format_test_data/035.gn
new file mode 100644
index 00000000000..70bc1a9eca0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/035.gn
@@ -0,0 +1 @@
+import("//build/config/sysroot.gni") # Imports android/config.gni.
diff --git a/gn/tools/gn/format_test_data/035.golden b/gn/tools/gn/format_test_data/035.golden
new file mode 100644
index 00000000000..70bc1a9eca0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/035.golden
@@ -0,0 +1 @@
+import("//build/config/sysroot.gni") # Imports android/config.gni.
diff --git a/gn/tools/gn/format_test_data/036.gn b/gn/tools/gn/format_test_data/036.gn
new file mode 100644
index 00000000000..5a5eca8455c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/036.gn
@@ -0,0 +1,9 @@
+import("a")
+import("b")
+
+assert(x)
+assert(y)
+assert(z)
+
+source_set("stuff") {
+}
diff --git a/gn/tools/gn/format_test_data/036.golden b/gn/tools/gn/format_test_data/036.golden
new file mode 100644
index 00000000000..5a5eca8455c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/036.golden
@@ -0,0 +1,9 @@
+import("a")
+import("b")
+
+assert(x)
+assert(y)
+assert(z)
+
+source_set("stuff") {
+}
diff --git a/gn/tools/gn/format_test_data/037.gn b/gn/tools/gn/format_test_data/037.gn
new file mode 100644
index 00000000000..ebbf0f8fa9a
--- /dev/null
+++ b/gn/tools/gn/format_test_data/037.gn
@@ -0,0 +1,5 @@
+if (true) {
+ if (true) {
+ args = rebase_path(sources, root_build_dir) + rebase_path(outputs, root_build_dir)
+ }
+}
diff --git a/gn/tools/gn/format_test_data/037.golden b/gn/tools/gn/format_test_data/037.golden
new file mode 100644
index 00000000000..71e95a4b581
--- /dev/null
+++ b/gn/tools/gn/format_test_data/037.golden
@@ -0,0 +1,6 @@
+if (true) {
+ if (true) {
+ args = rebase_path(sources, root_build_dir) +
+ rebase_path(outputs, root_build_dir)
+ }
+}
diff --git a/gn/tools/gn/format_test_data/038.gn b/gn/tools/gn/format_test_data/038.gn
new file mode 100644
index 00000000000..ea06d93da70
--- /dev/null
+++ b/gn/tools/gn/format_test_data/038.gn
@@ -0,0 +1,4 @@
+if (stuff) {
+ # Blank line at EOF.
+}
+
diff --git a/gn/tools/gn/format_test_data/038.golden b/gn/tools/gn/format_test_data/038.golden
new file mode 100644
index 00000000000..f8c9a1b92b0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/038.golden
@@ -0,0 +1,3 @@
+if (stuff) {
+ # Blank line at EOF.
+}
diff --git a/gn/tools/gn/format_test_data/039.gn b/gn/tools/gn/format_test_data/039.gn
new file mode 100644
index 00000000000..662b8cdb108
--- /dev/null
+++ b/gn/tools/gn/format_test_data/039.gn
@@ -0,0 +1,6 @@
+if (true) {
+ assert(arm_float_abi == "" ||
+ arm_float_abi == "hard" ||
+ arm_float_abi == "soft" ||
+ arm_float_abi == "softfp")
+}
diff --git a/gn/tools/gn/format_test_data/039.golden b/gn/tools/gn/format_test_data/039.golden
new file mode 100644
index 00000000000..b92b1ae838b
--- /dev/null
+++ b/gn/tools/gn/format_test_data/039.golden
@@ -0,0 +1,4 @@
+if (true) {
+ assert(arm_float_abi == "" || arm_float_abi == "hard" ||
+ arm_float_abi == "soft" || arm_float_abi == "softfp")
+}
diff --git a/gn/tools/gn/format_test_data/040.gn b/gn/tools/gn/format_test_data/040.gn
new file mode 100644
index 00000000000..1f4754e2794
--- /dev/null
+++ b/gn/tools/gn/format_test_data/040.gn
@@ -0,0 +1,9 @@
+# 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/041.gn b/gn/tools/gn/format_test_data/041.gn
new file mode 100644
index 00000000000..fa39b4923ff
--- /dev/null
+++ b/gn/tools/gn/format_test_data/041.gn
@@ -0,0 +1,12 @@
+if (true) {
+ a = [ "wee" ]
+
+ b = [
+ "x",
+ "y",
+ "z",
+ ]
+ c = [ "x" ]
+
+ d = [ "x" ]
+}
diff --git a/gn/tools/gn/format_test_data/041.golden b/gn/tools/gn/format_test_data/041.golden
new file mode 100644
index 00000000000..fa39b4923ff
--- /dev/null
+++ b/gn/tools/gn/format_test_data/041.golden
@@ -0,0 +1,12 @@
+if (true) {
+ a = [ "wee" ]
+
+ b = [
+ "x",
+ "y",
+ "z",
+ ]
+ c = [ "x" ]
+
+ d = [ "x" ]
+}
diff --git a/gn/tools/gn/format_test_data/042.gn b/gn/tools/gn/format_test_data/042.gn
new file mode 100644
index 00000000000..b827f29e070
--- /dev/null
+++ b/gn/tools/gn/format_test_data/042.gn
@@ -0,0 +1,44 @@
+# 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/042.golden b/gn/tools/gn/format_test_data/042.golden
new file mode 100644
index 00000000000..1968d0acd94
--- /dev/null
+++ b/gn/tools/gn/format_test_data/042.golden
@@ -0,0 +1,110 @@
+# 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/tools/gn/format_test_data/043.gn
new file mode 100644
index 00000000000..b95c6a52593
--- /dev/null
+++ b/gn/tools/gn/format_test_data/043.gn
@@ -0,0 +1,6 @@
+# Don't break and indent when it's hopeless.
+# 80 ---------------------------------------------------------------------------
+android_java_prebuilt("android_support_v13_java") {
+ jar_path = "$android_sdk_root/extras/android/support/v7/appcompat/libs/android-support-v7-appcompat.jar"
+ jar_path = "$android_sdk_root/extras/android/support/v13/android-support-v13.jar"
+}
diff --git a/gn/tools/gn/format_test_data/043.golden b/gn/tools/gn/format_test_data/043.golden
new file mode 100644
index 00000000000..336ec2fff1e
--- /dev/null
+++ b/gn/tools/gn/format_test_data/043.golden
@@ -0,0 +1,7 @@
+# Don't break and indent when it's hopeless.
+# 80 ---------------------------------------------------------------------------
+android_java_prebuilt("android_support_v13_java") {
+ jar_path = "$android_sdk_root/extras/android/support/v7/appcompat/libs/android-support-v7-appcompat.jar"
+ jar_path =
+ "$android_sdk_root/extras/android/support/v13/android-support-v13.jar"
+}
diff --git a/gn/tools/gn/format_test_data/044.gn b/gn/tools/gn/format_test_data/044.gn
new file mode 100644
index 00000000000..fe09617912d
--- /dev/null
+++ b/gn/tools/gn/format_test_data/044.gn
@@ -0,0 +1,10 @@
+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/044.golden b/gn/tools/gn/format_test_data/044.golden
new file mode 100644
index 00000000000..030c5dd5dc7
--- /dev/null
+++ b/gn/tools/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/tools/gn/format_test_data/045.gn
new file mode 100644
index 00000000000..28a7280d3ba
--- /dev/null
+++ b/gn/tools/gn/format_test_data/045.gn
@@ -0,0 +1,10 @@
+static_library("browser") {
+ if (!is_ios) {
+ sources += rebase_path(gypi_values.chrome_browser_predictor_sources,
+ ".", "//chrome")
+ sources += rebase_path(gypi_values.chrome_browser_predictor_sourcesaaaaaaaa,
+ ".", "//chrome")
+ sources += rebase_path(gypi_values.chrome_browser_predictor_sourcesaaaaaaaaa,
+ ".", "//chrome")
+ }
+}
diff --git a/gn/tools/gn/format_test_data/045.golden b/gn/tools/gn/format_test_data/045.golden
new file mode 100644
index 00000000000..21c560a80f4
--- /dev/null
+++ b/gn/tools/gn/format_test_data/045.golden
@@ -0,0 +1,14 @@
+static_library("browser") {
+ if (!is_ios) {
+ sources += rebase_path(gypi_values.chrome_browser_predictor_sources,
+ ".",
+ "//chrome")
+ sources += rebase_path(gypi_values.chrome_browser_predictor_sourcesaaaaaaaa,
+ ".",
+ "//chrome")
+ sources +=
+ rebase_path(gypi_values.chrome_browser_predictor_sourcesaaaaaaaaa,
+ ".",
+ "//chrome")
+ }
+}
diff --git a/gn/tools/gn/format_test_data/046.gn b/gn/tools/gn/format_test_data/046.gn
new file mode 100644
index 00000000000..28df74be66c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/046.gn
@@ -0,0 +1,22 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ # The JavaScript files required by main.html.
+ remoting_webapp_main_html_js_files =
+ # Include the core files first as it is required by the other files.
+ # Otherwise, Jscompile will complain.
+ remoting_webapp_js_core_files +
+ remoting_webapp_js_auth_client2host_files +
+ remoting_webapp_js_auth_google_files +
+ remoting_webapp_js_client_files +
+ remoting_webapp_js_gnubby_auth_files +
+ remoting_webapp_js_cast_extension_files +
+ remoting_webapp_js_host_files +
+ remoting_webapp_js_logging_files +
+ remoting_webapp_js_ui_files +
+ remoting_webapp_js_ui_host_control_files +
+ remoting_webapp_js_ui_host_display_files +
+ remoting_webapp_js_wcs_container_files
+ # Uncomment this line to include browser test files in the web app
+ # to expedite debugging or local development.
+ #+ remoting_webapp_js_browser_test_files
+}
diff --git a/gn/tools/gn/format_test_data/046.golden b/gn/tools/gn/format_test_data/046.golden
new file mode 100644
index 00000000000..7d2a679c18d
--- /dev/null
+++ b/gn/tools/gn/format_test_data/046.golden
@@ -0,0 +1,19 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ # The JavaScript files required by main.html.
+ remoting_webapp_main_html_js_files =
+ # Include the core files first as it is required by the other files.
+ # Otherwise, Jscompile will complain.
+ remoting_webapp_js_core_files +
+ remoting_webapp_js_auth_client2host_files +
+ remoting_webapp_js_auth_google_files + remoting_webapp_js_client_files +
+ remoting_webapp_js_gnubby_auth_files +
+ remoting_webapp_js_cast_extension_files + remoting_webapp_js_host_files +
+ remoting_webapp_js_logging_files + remoting_webapp_js_ui_files +
+ remoting_webapp_js_ui_host_control_files +
+ remoting_webapp_js_ui_host_display_files +
+ remoting_webapp_js_wcs_container_files
+ # Uncomment this line to include browser test files in the web app
+ # to expedite debugging or local development.
+ #+ remoting_webapp_js_browser_test_files
+}
diff --git a/gn/tools/gn/format_test_data/047.gn b/gn/tools/gn/format_test_data/047.gn
new file mode 100644
index 00000000000..f1fdbeca8b5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/047.gn
@@ -0,0 +1,7 @@
+if (true) {
+ args += [ "--template" ] +
+ rebase_path(remoting_webapp_template_files, template_rel_dir)
+ args += [ "--dir-for-templates",
+ rebase_path(template_rel_dir, root_build_dir) ]
+ args += ["--js"] + rebase_path(remoting_webapp_main_html_js_files, template_rel_dir_and_some_more)
+}
diff --git a/gn/tools/gn/format_test_data/047.golden b/gn/tools/gn/format_test_data/047.golden
new file mode 100644
index 00000000000..5217e762580
--- /dev/null
+++ b/gn/tools/gn/format_test_data/047.golden
@@ -0,0 +1,10 @@
+if (true) {
+ args += [ "--template" ] +
+ rebase_path(remoting_webapp_template_files, template_rel_dir)
+ args += [
+ "--dir-for-templates",
+ rebase_path(template_rel_dir, root_build_dir),
+ ]
+ args += [ "--js" ] + rebase_path(remoting_webapp_main_html_js_files,
+ template_rel_dir_and_some_more)
+}
diff --git a/gn/tools/gn/format_test_data/048.gn b/gn/tools/gn/format_test_data/048.gn
new file mode 100644
index 00000000000..7d39efb68c4
--- /dev/null
+++ b/gn/tools/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",
+ ]
+
+ 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
new file mode 100644
index 00000000000..7d39efb68c4
--- /dev/null
+++ b/gn/tools/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",
+ ]
+
+ 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/049.gn b/gn/tools/gn/format_test_data/049.gn
new file mode 100644
index 00000000000..fe793d2898a
--- /dev/null
+++ b/gn/tools/gn/format_test_data/049.gn
@@ -0,0 +1,14 @@
+func(aaaaaaaaaaaaaaaaaaaaa,
+ bbbbbbbbbbbbbbbbbbbbbbbbbb,
+ # Comment about function arg.
+ ccccccccccccccccccccccccccccc,
+ dddddddddddddddddddd)
+
+func(aaaaaaaaaaaaaaaaaaaaa,
+ bbbbbbbbbbbbbbbbbbbbbbbbbb,
+
+ # Block comment
+ # Comment about function arg.
+
+ ccccccccccccccccccccccccccccc,
+ dddddddddddddddddddd)
diff --git a/gn/tools/gn/format_test_data/050.gn b/gn/tools/gn/format_test_data/050.gn
new file mode 100644
index 00000000000..92a6d6d7d4b
--- /dev/null
+++ b/gn/tools/gn/format_test_data/050.gn
@@ -0,0 +1,10 @@
+# 80 ---------------------------------------------------------------------------
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - cccccccccccccccccccccccc)
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccc
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccc + ddddddddddddddddddddd + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + ffffffffffffff(aaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccc, ddddddddddddddddddd)
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccc + ddddddddddddddddddddd + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + ffffffffffffff(aaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccc + ddddddddddddddddddd)
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccc + ddddddddddddddddddddd + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + ffffffffffffff(aaaaaaaaaaaaa - (bbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccc + ddddddddddddddddddd))
diff --git a/gn/tools/gn/format_test_data/050.golden b/gn/tools/gn/format_test_data/050.golden
new file mode 100644
index 00000000000..2645600c738
--- /dev/null
+++ b/gn/tools/gn/format_test_data/050.golden
@@ -0,0 +1,27 @@
+# 80 ---------------------------------------------------------------------------
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -
+ (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - cccccccccccccccccccccccc)
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +
+ cccccccccccccccccccccccc
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +
+ cccccccccccccccccccccccc + ddddddddddddddddddddd +
+ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +
+ ffffffffffffff(aaaaaaaaaaaaa,
+ bbbbbbbbbbbbbbbbbbbbbbbb,
+ ccccccccccccccccccc,
+ ddddddddddddddddddd)
+
+zippy = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +
+ cccccccccccccccccccccccc + ddddddddddddddddddddd +
+ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +
+ ffffffffffffff(aaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbb +
+ ccccccccccccccccccc + ddddddddddddddddddd)
+
+zippy =
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +
+ cccccccccccccccccccccccc + ddddddddddddddddddddd +
+ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +
+ ffffffffffffff(aaaaaaaaaaaaa - (bbbbbbbbbbbbbbbbbbbbbbbb +
+ ccccccccccccccccccc + ddddddddddddddddddd))
diff --git a/gn/tools/gn/format_test_data/051.gn b/gn/tools/gn/format_test_data/051.gn
new file mode 100644
index 00000000000..8076ec696d4
--- /dev/null
+++ b/gn/tools/gn/format_test_data/051.gn
@@ -0,0 +1,6 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa =
+ bbbbbbbbbbbbbbbbbbbb - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +
+ cccccccccccccccccccccccccccccccccccc
+}
diff --git a/gn/tools/gn/format_test_data/051.golden b/gn/tools/gn/format_test_data/051.golden
new file mode 100644
index 00000000000..37a12124013
--- /dev/null
+++ b/gn/tools/gn/format_test_data/051.golden
@@ -0,0 +1,7 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa =
+ bbbbbbbbbbbbbbbbbbbb -
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +
+ cccccccccccccccccccccccccccccccccccc
+}
diff --git a/gn/tools/gn/format_test_data/052.gn b/gn/tools/gn/format_test_data/052.gn
new file mode 100644
index 00000000000..1f6a10f5159
--- /dev/null
+++ b/gn/tools/gn/format_test_data/052.gn
@@ -0,0 +1,11 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ if (true) {
+ sources += rebase_path(
+ gypi_values.browser_chromeos_non_athena_sources,
+ ".", "//chrome") +
+ rebase_path(gypi_values.browser_chromeos_extension_non_athena_sources,
+ ".", "//chrome")
+ }
+}
+
diff --git a/gn/tools/gn/format_test_data/052.golden b/gn/tools/gn/format_test_data/052.golden
new file mode 100644
index 00000000000..880c5639f2c
--- /dev/null
+++ b/gn/tools/gn/format_test_data/052.golden
@@ -0,0 +1,12 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ if (true) {
+ sources +=
+ rebase_path(gypi_values.browser_chromeos_non_athena_sources,
+ ".",
+ "//chrome") +
+ rebase_path(gypi_values.browser_chromeos_extension_non_athena_sources,
+ ".",
+ "//chrome")
+ }
+}
diff --git a/gn/tools/gn/format_test_data/053.gn b/gn/tools/gn/format_test_data/053.gn
new file mode 100644
index 00000000000..42a3e0899ba
--- /dev/null
+++ b/gn/tools/gn/format_test_data/053.gn
@@ -0,0 +1,7 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ check_internal_result = exec_script(
+ "build/check_internal.py",
+ [ rebase_path("internal/google_chrome_api_keys.h", root_build_dir) ],
+ "value")
+}
diff --git a/gn/tools/gn/format_test_data/053.golden b/gn/tools/gn/format_test_data/053.golden
new file mode 100644
index 00000000000..76179cc1953
--- /dev/null
+++ b/gn/tools/gn/format_test_data/053.golden
@@ -0,0 +1,8 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ check_internal_result =
+ exec_script("build/check_internal.py",
+ [ rebase_path("internal/google_chrome_api_keys.h",
+ root_build_dir) ],
+ "value")
+}
diff --git a/gn/tools/gn/format_test_data/054.gn b/gn/tools/gn/format_test_data/054.gn
new file mode 100644
index 00000000000..dca2ace3b32
--- /dev/null
+++ b/gn/tools/gn/format_test_data/054.gn
@@ -0,0 +1,7 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ args = [
+ "{{source}}",
+ rebase_path("${target_gen_dir}/{{source_name_part}}-inc.cc", root_build_dir)
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/054.golden b/gn/tools/gn/format_test_data/054.golden
new file mode 100644
index 00000000000..7dfe5be7f26
--- /dev/null
+++ b/gn/tools/gn/format_test_data/054.golden
@@ -0,0 +1,8 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ args = [
+ "{{source}}",
+ rebase_path("${target_gen_dir}/{{source_name_part}}-inc.cc",
+ root_build_dir),
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/055.gn b/gn/tools/gn/format_test_data/055.gn
new file mode 100644
index 00000000000..7e467247694
--- /dev/null
+++ b/gn/tools/gn/format_test_data/055.gn
@@ -0,0 +1,10 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ if (true) {
+ use_system_harfbuzz = exec_script(
+ pkg_config_script,
+ pkg_config_args + [ "--atleast-version=1.31.0", "pangoft2" ],
+ "value")
+ }
+}
+
diff --git a/gn/tools/gn/format_test_data/055.golden b/gn/tools/gn/format_test_data/055.golden
new file mode 100644
index 00000000000..b2bfdf28654
--- /dev/null
+++ b/gn/tools/gn/format_test_data/055.golden
@@ -0,0 +1,11 @@
+# 80 ---------------------------------------------------------------------------
+if (true) {
+ if (true) {
+ use_system_harfbuzz = exec_script(pkg_config_script,
+ pkg_config_args + [
+ "--atleast-version=1.31.0",
+ "pangoft2",
+ ],
+ "value")
+ }
+}
diff --git a/gn/tools/gn/format_test_data/056.gn b/gn/tools/gn/format_test_data/056.gn
new file mode 100644
index 00000000000..2082a770d87
--- /dev/null
+++ b/gn/tools/gn/format_test_data/056.gn
@@ -0,0 +1,45 @@
+# 80 ---------------------------------------------------------------------------
+java_files = [
+ "test/android/java/src/org/chromium/base/ContentUriTestUtils.java"
+]
+
+defines = [
+ "test/android/java/src/org/chromium/base/ContentUriTestUtils.java"
+]
+
+defines = [
+ "abc/test/android/java/src/org/chromium/base/ContentUriTestUtils.java"
+]
+
+cflags += [
+ # WEE
+ "/a",
+ "/b",
+ "/c",
+]
+
+sources = [ "/a", "/b", "/c" ]
+
+sources = [
+ # WEE
+ "/a",
+ "/b",
+ "/c",
+]
+
+sources += [
+ # WEE
+ "/a",
+ "/b",
+ "/c",
+]
+
+configs -= [
+ # Something!
+ "//build/config/win:nominmax",
+]
+
+cflags = [
+ "/wd4267", # size_t -> int
+ "/wd4324", # structure was padded
+]
diff --git a/gn/tools/gn/format_test_data/056.golden b/gn/tools/gn/format_test_data/056.golden
new file mode 100644
index 00000000000..df088fc06b5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/056.golden
@@ -0,0 +1,45 @@
+# 80 ---------------------------------------------------------------------------
+java_files =
+ [ "test/android/java/src/org/chromium/base/ContentUriTestUtils.java" ]
+
+defines = [ "test/android/java/src/org/chromium/base/ContentUriTestUtils.java" ]
+
+defines =
+ [ "abc/test/android/java/src/org/chromium/base/ContentUriTestUtils.java" ]
+
+cflags += [
+ # WEE
+ "/a",
+ "/b",
+ "/c",
+]
+
+sources = [
+ "/a",
+ "/b",
+ "/c",
+]
+
+sources = [
+ # WEE
+ "/a",
+ "/b",
+ "/c",
+]
+
+sources += [
+ # WEE
+ "/a",
+ "/b",
+ "/c",
+]
+
+configs -= [
+ # Something!
+ "//build/config/win:nominmax",
+]
+
+cflags = [
+ "/wd4267", # size_t -> int
+ "/wd4324", # structure was padded
+]
diff --git a/gn/tools/gn/format_test_data/057.gn b/gn/tools/gn/format_test_data/057.gn
new file mode 100644
index 00000000000..858e3115b11
--- /dev/null
+++ b/gn/tools/gn/format_test_data/057.gn
@@ -0,0 +1,24 @@
+# 80 ---------------------------------------------------------------------------
+# Because there is a difference in precedence level between || and &&
+# a || b || c && d
+# is equivalent to
+# a || b || (c && d)
+# Because parens are not stored in the parse tree, the formatter recreates the
+# minimally required set to maintain meaning. However, this particular case can
+# be confusing for human readers, so we special case these ones and add
+# strictly-unnecessary parens.
+
+supports_android = (is_apk || is_android_resources ||
+ (is_java_library && defined(invoker.supports_android) &&
+ invoker.supports_android))
+
+enable_one_click_signin = is_win || is_mac || (is_linux && !is_chromeos)
+enable_one_click_signin = (is_linux && !is_chromeos) || is_win || is_mac
+
+x = c || (a&&b)
+x = (a&&b) || c
+x = a&&b || c
+
+x = c && (a||b)
+x = (a||b) && c
+x = a||b && c
diff --git a/gn/tools/gn/format_test_data/057.golden b/gn/tools/gn/format_test_data/057.golden
new file mode 100644
index 00000000000..d0daa6c8e72
--- /dev/null
+++ b/gn/tools/gn/format_test_data/057.golden
@@ -0,0 +1,24 @@
+# 80 ---------------------------------------------------------------------------
+# Because there is a difference in precedence level between || and &&
+# a || b || c && d
+# is equivalent to
+# a || b || (c && d)
+# Because parens are not stored in the parse tree, the formatter recreates the
+# minimally required set to maintain meaning. However, this particular case can
+# be confusing for human readers, so we special case these ones and add
+# strictly-unnecessary parens.
+
+supports_android = is_apk || is_android_resources ||
+ (is_java_library && defined(invoker.supports_android) &&
+ invoker.supports_android)
+
+enable_one_click_signin = is_win || is_mac || (is_linux && !is_chromeos)
+enable_one_click_signin = (is_linux && !is_chromeos) || is_win || is_mac
+
+x = c || (a && b)
+x = (a && b) || c
+x = (a && b) || c
+
+x = c && (a || b)
+x = (a || b) && c
+x = a || (b && c)
diff --git a/gn/tools/gn/format_test_data/058.gn b/gn/tools/gn/format_test_data/058.gn
new file mode 100644
index 00000000000..568074a634b
--- /dev/null
+++ b/gn/tools/gn/format_test_data/058.gn
@@ -0,0 +1,2 @@
+if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) {
+}
diff --git a/gn/tools/gn/format_test_data/058.golden b/gn/tools/gn/format_test_data/058.golden
new file mode 100644
index 00000000000..568074a634b
--- /dev/null
+++ b/gn/tools/gn/format_test_data/058.golden
@@ -0,0 +1,2 @@
+if (!defined(invoker.ignore_libs) || !invoker.ignore_libs) {
+}
diff --git a/gn/tools/gn/format_test_data/059.gn b/gn/tools/gn/format_test_data/059.gn
new file mode 100644
index 00000000000..ea6fb8e3dc8
--- /dev/null
+++ b/gn/tools/gn/format_test_data/059.gn
@@ -0,0 +1,10 @@
+assert(type == "android_apk" || type == "java_library" ||
+ type == "android_resources" || things == stuff && stuff != 432)
+
+assert(type == "android_apk" || type == "java_library" ||
+ type == "android_resources",
+ type == "android_apk" || type == "java_library" ||
+ type == "android_resources")
+
+
+if (type == "android_apk" || type == "java_library" || type == "android_resources" || things == stuff && stuff != 432) {}
diff --git a/gn/tools/gn/format_test_data/059.golden b/gn/tools/gn/format_test_data/059.golden
new file mode 100644
index 00000000000..423e88882e1
--- /dev/null
+++ b/gn/tools/gn/format_test_data/059.golden
@@ -0,0 +1,11 @@
+assert(type == "android_apk" || type == "java_library" ||
+ type == "android_resources" || (things == stuff && stuff != 432))
+
+assert(type == "android_apk" || type == "java_library" ||
+ type == "android_resources",
+ type == "android_apk" || type == "java_library" ||
+ type == "android_resources")
+
+if (type == "android_apk" || type == "java_library" ||
+ type == "android_resources" || (things == stuff && stuff != 432)) {
+}
diff --git a/gn/tools/gn/format_test_data/060.gn b/gn/tools/gn/format_test_data/060.gn
new file mode 100644
index 00000000000..2b0da79f7cb
--- /dev/null
+++ b/gn/tools/gn/format_test_data/060.gn
@@ -0,0 +1,2 @@
+some_variable = "this is a very long string that is going to exceed 80 col and will never under any circumstance fit"
+another_variable = [ "this is a very long string that is going to exceed 80 col and will never under any circumstance fit" ]
diff --git a/gn/tools/gn/format_test_data/060.golden b/gn/tools/gn/format_test_data/060.golden
new file mode 100644
index 00000000000..2b0da79f7cb
--- /dev/null
+++ b/gn/tools/gn/format_test_data/060.golden
@@ -0,0 +1,2 @@
+some_variable = "this is a very long string that is going to exceed 80 col and will never under any circumstance fit"
+another_variable = [ "this is a very long string that is going to exceed 80 col and will never under any circumstance fit" ]
diff --git a/gn/tools/gn/format_test_data/061.gn b/gn/tools/gn/format_test_data/061.gn
new file mode 100644
index 00000000000..5948037fe56
--- /dev/null
+++ b/gn/tools/gn/format_test_data/061.gn
@@ -0,0 +1,9 @@
+action("generate_gl_bindings") {
+ args = [
+ "--header-paths=" + rebase_path("//third_party/khronos", root_build_dir) +
+ ":" + rebase_path("//third_party/mesa/src/include", root_build_dir) + ":" +
+ rebase_path("//ui/gl", root_build_dir) + ":" +
+ rebase_path("//gpu", root_build_dir),
+ rebase_path(gl_binding_output_dir, root_build_dir),
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/061.golden b/gn/tools/gn/format_test_data/061.golden
new file mode 100644
index 00000000000..edbf43dc6ad
--- /dev/null
+++ b/gn/tools/gn/format_test_data/061.golden
@@ -0,0 +1,9 @@
+action("generate_gl_bindings") {
+ args = [
+ "--header-paths=" + rebase_path("//third_party/khronos", root_build_dir) +
+ ":" + rebase_path("//third_party/mesa/src/include", root_build_dir) +
+ ":" + rebase_path("//ui/gl", root_build_dir) + ":" +
+ rebase_path("//gpu", root_build_dir),
+ rebase_path(gl_binding_output_dir, root_build_dir),
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/062.gn b/gn/tools/gn/format_test_data/062.gn
new file mode 100644
index 00000000000..8c4cc7d8423
--- /dev/null
+++ b/gn/tools/gn/format_test_data/062.gn
@@ -0,0 +1,122 @@
+# 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
new file mode 100644
index 00000000000..b55451011f7
--- /dev/null
+++ b/gn/tools/gn/format_test_data/062.golden
@@ -0,0 +1,132 @@
+# 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
new file mode 100644
index 00000000000..9fd8211b692
--- /dev/null
+++ b/gn/tools/gn/format_test_data/063.gn
@@ -0,0 +1,36 @@
+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
new file mode 100644
index 00000000000..3dc4bedfae7
--- /dev/null
+++ b/gn/tools/gn/format_test_data/063.golden
@@ -0,0 +1,36 @@
+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.gn b/gn/tools/gn/format_test_data/064.gn
new file mode 100644
index 00000000000..c1867250fe5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/064.gn
@@ -0,0 +1,3 @@
+source_set("test") {
+ deps = [ rebase_path(sdk_dep, ".", mojo_root) ]
+}
diff --git a/gn/tools/gn/format_test_data/064.golden b/gn/tools/gn/format_test_data/064.golden
new file mode 100644
index 00000000000..3ff56f6490f
--- /dev/null
+++ b/gn/tools/gn/format_test_data/064.golden
@@ -0,0 +1,5 @@
+source_set("test") {
+ deps = [
+ rebase_path(sdk_dep, ".", mojo_root),
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/065.gn b/gn/tools/gn/format_test_data/065.gn
new file mode 100644
index 00000000000..a4489092232
--- /dev/null
+++ b/gn/tools/gn/format_test_data/065.gn
@@ -0,0 +1,4 @@
+source_set("test") {
+ some_target_name = ":some_target"
+ deps = [ some_target_name, "//last_target", ":another_target" ]
+}
diff --git a/gn/tools/gn/format_test_data/065.golden b/gn/tools/gn/format_test_data/065.golden
new file mode 100644
index 00000000000..5df85fda4df
--- /dev/null
+++ b/gn/tools/gn/format_test_data/065.golden
@@ -0,0 +1,8 @@
+source_set("test") {
+ some_target_name = ":some_target"
+ deps = [
+ ":another_target",
+ "//last_target",
+ some_target_name,
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/066.gn b/gn/tools/gn/format_test_data/066.gn
new file mode 100644
index 00000000000..c62eb2ae6ec
--- /dev/null
+++ b/gn/tools/gn/format_test_data/066.gn
@@ -0,0 +1,30 @@
+# Suppress sorting based on comment.
+
+# NOSORT
+sources = []
+
+# NOSORT
+sources = [
+ "a",
+]
+
+# NOSORT
+sources += [
+ "a",
+]
+
+# NOSORT
+sources = [
+ "z",
+ "z2",
+ "a",
+ "y.cc",
+]
+
+# NOSORT
+sources += [
+ "z",
+ "z2",
+ "a",
+ "y.cc",
+]
diff --git a/gn/tools/gn/format_test_data/066.golden b/gn/tools/gn/format_test_data/066.golden
new file mode 100644
index 00000000000..45467b880f5
--- /dev/null
+++ b/gn/tools/gn/format_test_data/066.golden
@@ -0,0 +1,28 @@
+# Suppress sorting based on comment.
+
+# NOSORT
+sources = []
+
+# NOSORT
+sources = [
+ "a",
+]
+
+# NOSORT
+sources += [ "a" ]
+
+# NOSORT
+sources = [
+ "z",
+ "z2",
+ "a",
+ "y.cc",
+]
+
+# NOSORT
+sources += [
+ "z",
+ "z2",
+ "a",
+ "y.cc",
+]
diff --git a/gn/tools/gn/format_test_data/067.gn b/gn/tools/gn/format_test_data/067.gn
new file mode 100644
index 00000000000..b3d5eaa6ce0
--- /dev/null
+++ b/gn/tools/gn/format_test_data/067.gn
@@ -0,0 +1,8 @@
+# Scope wrapping.
+
+myscope = {
+}
+myscope = { a = 1 }
+myscope = { a = 1 b = 2}
+# Comment
+SomeFunction({a=1}, "foo")
diff --git a/gn/tools/gn/format_test_data/067.golden b/gn/tools/gn/format_test_data/067.golden
new file mode 100644
index 00000000000..9ce2fa77276
--- /dev/null
+++ b/gn/tools/gn/format_test_data/067.golden
@@ -0,0 +1,17 @@
+# Scope wrapping.
+
+myscope = {
+}
+myscope = {
+ a = 1
+}
+myscope = {
+ a = 1
+ b = 2
+}
+
+# Comment
+SomeFunction({
+ a = 1
+ },
+ "foo")
diff --git a/gn/tools/gn/format_test_data/068.gn b/gn/tools/gn/format_test_data/068.gn
new file mode 100644
index 00000000000..7fb8e1b00d4
--- /dev/null
+++ b/gn/tools/gn/format_test_data/068.gn
@@ -0,0 +1,3 @@
+# Initial comment
+
+# Comment that should be separate, no subsequent content in file.
diff --git a/gn/tools/gn/format_test_data/068.golden b/gn/tools/gn/format_test_data/068.golden
new file mode 100644
index 00000000000..7fb8e1b00d4
--- /dev/null
+++ b/gn/tools/gn/format_test_data/068.golden
@@ -0,0 +1,3 @@
+# Initial comment
+
+# Comment that should be separate, no subsequent content in file.
diff --git a/gn/tools/gn/format_test_data/069.gn b/gn/tools/gn/format_test_data/069.gn
new file mode 100644
index 00000000000..200b3131272
--- /dev/null
+++ b/gn/tools/gn/format_test_data/069.gn
@@ -0,0 +1,3 @@
+if (true) {
+ configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", ]
+}
diff --git a/gn/tools/gn/format_test_data/069.golden b/gn/tools/gn/format_test_data/069.golden
new file mode 100644
index 00000000000..64d4acce040
--- /dev/null
+++ b/gn/tools/gn/format_test_data/069.golden
@@ -0,0 +1,5 @@
+if (true) {
+ configs -= [
+ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
+ ]
+}
diff --git a/gn/tools/gn/format_test_data/070.gn b/gn/tools/gn/format_test_data/070.gn
new file mode 100644
index 00000000000..09d80d2b02e
--- /dev/null
+++ b/gn/tools/gn/format_test_data/070.gn
@@ -0,0 +1,15 @@
+multiple = [
+ {
+ name = "elements_test"
+ },
+ {
+ name = "eapol_crypto_test"
+ },
+ ]
+
+
+single = [
+ {
+ name = "elements_test"
+ },
+ ]
diff --git a/gn/tools/gn/format_test_data/070.golden b/gn/tools/gn/format_test_data/070.golden
new file mode 100644
index 00000000000..0997aa47743
--- /dev/null
+++ b/gn/tools/gn/format_test_data/070.golden
@@ -0,0 +1,14 @@
+multiple = [
+ {
+ name = "elements_test"
+ },
+ {
+ name = "eapol_crypto_test"
+ },
+]
+
+single = [
+ {
+ name = "elements_test"
+ },
+]
diff --git a/gn/tools/gn/function_exec_script.cc b/gn/tools/gn/function_exec_script.cc
new file mode 100644
index 00000000000..15621b7790c
--- /dev/null
+++ b/gn/tools/gn/function_exec_script.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2013 The Chromium Authors. 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").
+
+Arguments:
+
+ filename:
+ File name of python 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 input_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 python 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& python_path = build_settings->python_path();
+ base::CommandLine cmdline(python_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("Pythoning",
+ base::UTF16ToUTF8(cmdline.GetCommandLineString()));
+#else
+ g_scheduler->Log("Pythoning", 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 python.",
+ "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
+ return Value();
+ }
+ }
+ if (g_scheduler->verbose_logging()) {
+ g_scheduler->Log(
+ "Pythoning",
+ 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
new file mode 100644
index 00000000000..24ffaaba617
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..e4c289d887d
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..de08072c939
--- /dev/null
+++ b/gn/tools/gn/function_forward_variables_from.cc
@@ -0,0 +1,242 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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
+
+ # 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
new file mode 100644
index 00000000000..20baea7566c
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..0d0c77a3c0c
--- /dev/null
+++ b/gn/tools/gn/function_get_label_info.cc
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium Authors. 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
new file mode 100644
index 00000000000..46eb7f2c940
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..905092cd50a
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..6e4a8da6e55
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..f5d4ef6cbd3
--- /dev/null
+++ b/gn/tools/gn/function_get_target_outputs.cc
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium Authors. 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 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 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").
+
+ 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.
+
+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->action_values().GetOutputsAsSourceFiles(target, &files);
+ } else {
+ // Other types of targets are not supported.
+ *err = Err(args[0], "Target is not an action, action_foreach, 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
new file mode 100644
index 00000000000..e94477bae67
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..47de6407f1a
--- /dev/null
+++ b/gn/tools/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 "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, SUBSTITUTION_SOURCE_TARGET_RELATIVE)) {
+ *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
new file mode 100644
index 00000000000..1f7bd76dd5c
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..1cc29f5b7bc
--- /dev/null
+++ b/gn/tools/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 "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 input_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
new file mode 100644
index 00000000000..945d6413681
--- /dev/null
+++ b/gn/tools/gn/function_rebase_path.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..718ecb1867d
--- /dev/null
+++ b/gn/tools/gn/function_rebase_path_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright (c) 2013 The Chromium Authors. 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("/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
new file mode 100644
index 00000000000..e8b1885d27e
--- /dev/null
+++ b/gn/tools/gn/function_set_default_toolchain.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..a2a14afcc7d
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..aa23dc6d63a
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..0525974c373
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..cf45c677e24
--- /dev/null
+++ b/gn/tools/gn/function_toolchain.cc
@@ -0,0 +1,1121 @@
+// Copyright (c) 2013 The Chromium Authors. 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/err.h"
+#include "tools/gn/functions.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;
+
+bool ReadBool(Scope* scope,
+ const char* var,
+ Tool* tool,
+ void (Tool::*set)(bool),
+ Err* err) {
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+ if (!v->VerifyTypeIs(Value::BOOLEAN, err))
+ return false;
+
+ (tool->*set)(v->boolean_value());
+ return true;
+}
+
+// Reads the given string from the scope (if present) and puts the result into
+// dest. If the value is not a string, sets the error and returns false.
+bool ReadString(Scope* scope,
+ const char* var,
+ Tool* tool,
+ void (Tool::*set)(std::string),
+ Err* err) {
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+ if (!v->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ (tool->*set)(v->string_value());
+ return true;
+}
+
+// Reads the given label from the scope (if present) and puts the result into
+// dest. If the value is not a label, sets the error and returns false.
+bool ReadLabel(Scope* scope,
+ const char* var,
+ Tool* tool,
+ const Label& current_toolchain,
+ void (Tool::*set)(LabelPtrPair<Pool>),
+ Err* err) {
+ 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 = tool->defined_from();
+
+ (tool->*set)(std::move(pair));
+ return true;
+}
+
+// Calls the given validate function on each type in the list. On failure,
+// sets the error, blame the value, and return false.
+bool ValidateSubstitutionList(const std::vector<SubstitutionType>& list,
+ bool (*validate)(SubstitutionType),
+ const Value* origin,
+ Err* err) {
+ for (const auto& cur_type : list) {
+ if (!validate(cur_type)) {
+ *err = Err(*origin, "Pattern not valid here.",
+ "You used the pattern " +
+ std::string(kSubstitutionNames[cur_type]) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ReadPattern(Scope* scope,
+ const char* name,
+ bool (*validate)(SubstitutionType),
+ Tool* tool,
+ void (Tool::*set)(SubstitutionPattern),
+ Err* err) {
+ const Value* value = scope->GetValue(name, 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(), validate, value, err))
+ return false;
+
+ (tool->*set)(std::move(pattern));
+ return true;
+}
+
+bool ReadPatternList(Scope* scope,
+ const char* name,
+ bool (*validate)(SubstitutionType),
+ Tool* tool,
+ void (Tool::*set)(SubstitutionList),
+ Err* err) {
+ const Value* value = scope->GetValue(name, 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(), validate, value, err))
+ return false;
+
+ (tool->*set)(std::move(list));
+ return true;
+}
+
+bool ReadOutputExtension(Scope* scope, Tool* tool, Err* err) {
+ 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;
+ }
+
+ tool->set_default_output_extension(value->string_value());
+ return true;
+}
+
+bool ReadPrecompiledHeaderType(Scope* scope, Tool* tool, 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") {
+ tool->set_precompiled_header_type(Tool::PCH_GCC);
+ return true;
+ } else if (value->string_value() == "msvc") {
+ tool->set_precompiled_header_type(Tool::PCH_MSVC);
+ return true;
+ }
+ *err = Err(*value, "Invalid precompiled_header_type",
+ "Must either be empty, \"gcc\", or \"msvc\".");
+ return false;
+}
+
+bool ReadDepsFormat(Scope* scope, Tool* tool, 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") {
+ tool->set_depsformat(Tool::DEPS_GCC);
+ } else if (value->string_value() == "msvc") {
+ tool->set_depsformat(Tool::DEPS_MSVC);
+ } else {
+ *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
+ return false;
+ }
+ return true;
+}
+
+bool IsCompilerTool(Toolchain::ToolType type) {
+ return type == Toolchain::TYPE_CC || type == Toolchain::TYPE_CXX ||
+ type == Toolchain::TYPE_OBJC || type == Toolchain::TYPE_OBJCXX ||
+ type == Toolchain::TYPE_RC || type == Toolchain::TYPE_ASM;
+}
+
+bool IsLinkerTool(Toolchain::ToolType type) {
+ // "alink" is not counted as in the generic "linker" tool list.
+ return type == Toolchain::TYPE_SOLINK ||
+ type == Toolchain::TYPE_SOLINK_MODULE || type == Toolchain::TYPE_LINK;
+}
+
+bool IsPatternInOutputList(const SubstitutionList& output_list,
+ const SubstitutionPattern& pattern) {
+ 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 ValidateOutputs(const Tool* tool, Err* err) {
+ if (tool->outputs().list().empty()) {
+ *err = Err(tool->defined_from(),
+ "\"outputs\" must be specified for this tool.");
+ 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 ValidateLinkAndDependOutput(const Tool* tool,
+ Toolchain::ToolType tool_type,
+ 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 (tool_type != Toolchain::TYPE_SOLINK &&
+ tool_type != Toolchain::TYPE_SOLINK_MODULE) {
+ *err = Err(tool->defined_from(),
+ "This tool specifies a " + std::string(variable_name) + ".",
+ "This is only valid for solink and solink_module tools.");
+ return false;
+ }
+
+ if (!IsPatternInOutputList(tool->outputs(), pattern)) {
+ *err = Err(tool->defined_from(), "This tool's link_output is bad.",
+ "It must match one of the outputs.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateRuntimeOutputs(const Tool* tool,
+ Toolchain::ToolType tool_type,
+ Err* err) {
+ if (tool->runtime_outputs().list().empty())
+ return true; // Empty is always OK.
+
+ if (!IsLinkerTool(tool_type)) {
+ *err = Err(tool->defined_from(), "This tool specifies runtime_outputs.",
+ "This is only valid for linker tools (alink doesn't count).");
+ return false;
+ }
+
+ for (const SubstitutionPattern& pattern : tool->runtime_outputs().list()) {
+ if (!IsPatternInOutputList(tool->outputs(), pattern)) {
+ *err = Err(tool->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;
+}
+
+} // 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.
+
+Tool variables
+
+ command [string with substitutions]
+ Valid for: all tools except "action" (required)
+
+ The command to run.
+
+ 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}}"
+
+ 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.
+
+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".
+
+)" // String break to prevent overflowing the 16K max VC string length.
+ R"( 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.
+
+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();
+ Toolchain::ToolType tool_type = Toolchain::ToolNameToType(tool_name);
+ if (tool_type == Toolchain::TYPE_NONE) {
+ *err = Err(args[0], "Unknown tool type");
+ return Value();
+ }
+
+ // Run the tool block.
+ Scope block_scope(scope);
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Figure out which validator to use for the substitution pattern for this
+ // tool type. There are different validators for the "outputs" than for the
+ // rest of the strings.
+ bool (*subst_validator)(SubstitutionType) = nullptr;
+ bool (*subst_output_validator)(SubstitutionType) = nullptr;
+ if (IsCompilerTool(tool_type)) {
+ subst_validator = &IsValidCompilerSubstitution;
+ subst_output_validator = &IsValidCompilerOutputsSubstitution;
+ } else if (IsLinkerTool(tool_type)) {
+ subst_validator = &IsValidLinkerSubstitution;
+ subst_output_validator = &IsValidLinkerOutputsSubstitution;
+ } else if (tool_type == Toolchain::TYPE_ALINK) {
+ subst_validator = &IsValidALinkSubstitution;
+ // ALink uses the standard output file patterns as other linker tools.
+ subst_output_validator = &IsValidLinkerOutputsSubstitution;
+ } else if (tool_type == Toolchain::TYPE_COPY ||
+ tool_type == Toolchain::TYPE_COPY_BUNDLE_DATA) {
+ subst_validator = &IsValidCopySubstitution;
+ subst_output_validator = &IsValidCopySubstitution;
+ } else if (tool_type == Toolchain::TYPE_COMPILE_XCASSETS) {
+ subst_validator = &IsValidCompileXCassetsSubstitution;
+ subst_output_validator = &IsValidCompileXCassetsSubstitution;
+ } else {
+ subst_validator = &IsValidToolSubstitution;
+ subst_output_validator = &IsValidToolSubstitution;
+ }
+
+ std::unique_ptr<Tool> tool = std::make_unique<Tool>();
+ tool->set_defined_from(function);
+
+ if (!ReadPattern(&block_scope, "command", subst_validator, tool.get(),
+ &Tool::set_command, err) ||
+ !ReadOutputExtension(&block_scope, tool.get(), err) ||
+ !ReadPattern(&block_scope, "depfile", subst_validator, tool.get(),
+ &Tool::set_depfile, err) ||
+ !ReadDepsFormat(&block_scope, tool.get(), err) ||
+ !ReadPattern(&block_scope, "description", subst_validator, tool.get(),
+ &Tool::set_description, err) ||
+ !ReadString(&block_scope, "lib_switch", tool.get(), &Tool::set_lib_switch,
+ err) ||
+ !ReadString(&block_scope, "lib_dir_switch", tool.get(),
+ &Tool::set_lib_dir_switch, err) ||
+ !ReadPattern(&block_scope, "link_output", subst_validator, tool.get(),
+ &Tool::set_link_output, err) ||
+ !ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(),
+ &Tool::set_depend_output, err) ||
+ !ReadPatternList(&block_scope, "runtime_outputs", subst_validator,
+ tool.get(), &Tool::set_runtime_outputs, err) ||
+ !ReadString(&block_scope, "output_prefix", tool.get(),
+ &Tool::set_output_prefix, err) ||
+ !ReadPattern(&block_scope, "default_output_dir", subst_validator,
+ tool.get(), &Tool::set_default_output_dir, err) ||
+ !ReadPrecompiledHeaderType(&block_scope, tool.get(), err) ||
+ !ReadBool(&block_scope, "restat", tool.get(), &Tool::set_restat, err) ||
+ !ReadPattern(&block_scope, "rspfile", subst_validator, tool.get(),
+ &Tool::set_rspfile, err) ||
+ !ReadPattern(&block_scope, "rspfile_content", subst_validator, tool.get(),
+ &Tool::set_rspfile_content, err) ||
+ !ReadLabel(&block_scope, "pool", tool.get(), toolchain->label(),
+ &Tool::set_pool, err)) {
+ return Value();
+ }
+
+ if (tool_type != Toolchain::TYPE_COPY && tool_type != Toolchain::TYPE_STAMP &&
+ tool_type != Toolchain::TYPE_COPY_BUNDLE_DATA &&
+ tool_type != Toolchain::TYPE_COMPILE_XCASSETS &&
+ tool_type != Toolchain::TYPE_ACTION) {
+ // All tools should have outputs, except the copy, stamp, copy_bundle_data
+ // compile_xcassets and action tools that generate their outputs internally.
+ if (!ReadPatternList(&block_scope, "outputs", subst_output_validator,
+ tool.get(), &Tool::set_outputs, err) ||
+ !ValidateOutputs(tool.get(), err))
+ return Value();
+ }
+ if (!ValidateRuntimeOutputs(tool.get(), tool_type, err))
+ return Value();
+
+ // Validate link_output and depend_output.
+ if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->link_output(),
+ "link_output", err))
+ return Value();
+ if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->depend_output(),
+ "depend_output", err))
+ return Value();
+ if ((!tool->link_output().empty() && tool->depend_output().empty()) ||
+ (tool->link_output().empty() && !tool->depend_output().empty())) {
+ *err = Err(function,
+ "Both link_output and depend_output should either "
+ "be specified or they should both be empty.");
+ return Value();
+ }
+
+ // Make sure there weren't any vars set in this tool that were unused.
+ if (!block_scope.CheckForUnusedVars(err))
+ return Value();
+
+ toolchain->SetTool(tool_type, std::move(tool));
+ return Value();
+}
+
+} // namespace functions
diff --git a/gn/tools/gn/function_toolchain_unittest.cc b/gn/tools/gn/function_toolchain_unittest.cc
new file mode 100644
index 00000000000..599d7a1b67d
--- /dev/null
+++ b/gn/tools/gn/function_toolchain_unittest.cc
@@ -0,0 +1,60 @@
+// 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/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(Toolchain::TYPE_LINK);
+ 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();
+ }
+}
diff --git a/gn/tools/gn/function_write_file.cc b/gn/tools/gn/function_write_file.cc
new file mode 100644
index 00000000000..44e6b163d80
--- /dev/null
+++ b/gn/tools/gn/function_write_file.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 The Chromium Authors. 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 output_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
new file mode 100644
index 00000000000..035fabae962
--- /dev/null
+++ b/gn/tools/gn/function_write_file_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 <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.AsUTF8Unsafe().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
new file mode 100644
index 00000000000..7fc8b62b3db
--- /dev/null
+++ b/gn/tools/gn/functions.cc
@@ -0,0 +1,1354 @@
+// Copyright (c) 2013 The Chromium Authors. 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".
+
+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") {
+ includes = [ "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.
+ 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) {
+ source = value->scope_value();
+ result_value = (*args_cur)->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
+ args_cur++;
+ }
+
+ // 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(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
new file mode 100644
index 00000000000..5c707c425ad
--- /dev/null
+++ b/gn/tools/gn/functions.h
@@ -0,0 +1,520 @@
+// Copyright (c) 2013 The Chromium Authors. 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 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 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
new file mode 100644
index 00000000000..2339e174131
--- /dev/null
+++ b/gn/tools/gn/functions_target.cc
@@ -0,0 +1,773 @@
+// Copyright (c) 2013 The Chromium Authors. 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, output_name,\n" \
+ " output_extension, public, sources, testonly, visibility\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"
+
+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, 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, 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 is 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, 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" properties must be defined. They will be used for the
+ expansion of {{bundle_*_dir}} rules in "bundle_data" outputs.
+
+ 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_plugins_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
+ * = required
+
+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") {
+ 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") {
+ 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
+ bundle_plugins_dir = "${bundle_contents_dir}/Plugins"
+
+ 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"
+ bundle_plugins_dir = "${bundle_contents_dir}/Plugins"
+ }
+ 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.
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_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.
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_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);
+}
+
+// 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.
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_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.
+
+ 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;
+
+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);
+}
+
+} // namespace functions
diff --git a/gn/tools/gn/functions_target_unittest.cc b/gn/tools/gn/functions_target_unittest.cc
new file mode 100644
index 00000000000..708c06076d4
--- /dev/null
+++ b/gn/tools/gn/functions_target_unittest.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 "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());
+}
+
+// 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();
+}
diff --git a/gn/tools/gn/functions_unittest.cc b/gn/tools/gn/functions_unittest.cc
new file mode 100644
index 00000000000..589986cc20f
--- /dev/null
+++ b/gn/tools/gn/functions_unittest.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 "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());
+}
diff --git a/gn/tools/gn/gn_main.cc b/gn/tools/gn/gn_main.cc
new file mode 100644
index 00000000000..a61a7527701
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..2219e5e4e6f
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..502f40b8db4
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..03afc463e01
--- /dev/null
+++ b/gn/tools/gn/header_checker.cc
@@ -0,0 +1,602 @@
+// Copyright 2014 The Chromium Authors. 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/source_file_type.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)
+ : build_settings_(build_settings), 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).
+ SourceFileType type = GetSourceFileType(file.first);
+ if (type != SOURCE_CPP && type != SOURCE_H && type != SOURCE_C &&
+ type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
+ continue;
+
+ // 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 (IsFileInOuputDir(file))
+ return true;
+
+ base::FilePath path = build_settings_->GetFullPath(file);
+ std::string contents;
+ if (!base::ReadFileToString(path, &contents)) {
+ 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());
+ }
+
+ bool has_errors = false;
+ CIncludeIterator iter(&input_file);
+ base::StringPiece current_include;
+ LocationRange range;
+ while (iter.GetNextIncludeString(&current_include, &range)) {
+ Err err;
+ SourceFile include = SourceFileForInclude(current_include, include_dirs,
+ input_file, range, &err);
+ if (!include.is_null()) {
+ if (!CheckInclude(from_target, input_file, include, range, &err)) {
+ errors->emplace_back(std::move(err));
+ has_errors = true;
+ }
+ }
+ }
+
+ return !has_errors;
+}
+
+// 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.
+bool HeaderChecker::CheckInclude(const Target* from_target,
+ const InputFile& source_file,
+ const SourceFile& include_file,
+ const LocationRange& range,
+ Err* err) 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 true;
+
+ 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 true;
+
+ // 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 true;
+
+ bool is_permitted_chain = false;
+ if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
+ 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 (!found_dependency) {
+ DCHECK(!last_error.has_error());
+ *err = MakeUnreachableError(source_file, range, from_target, targets);
+ return false;
+ }
+ if (last_error.has_error()) {
+ // Found at least one dependency chain above, but it had an error.
+ *err = last_error;
+ return false;
+ }
+
+ // 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.
+
+ return true;
+}
+
+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
new file mode 100644
index 00000000000..ef14729fd13
--- /dev/null
+++ b/gn/tools/gn/header_checker.h
@@ -0,0 +1,195 @@
+// Copyright 2014 The Chromium Authors. 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 <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;
+
+ HeaderChecker(const BuildSettings* build_settings,
+ const std::vector<const Target*>& targets);
+
+ // 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, returns false and sets the error. The
+ // range indicates the location of the include in the file for error
+ // reporting.
+ bool CheckInclude(const Target* from_target,
+ const InputFile& source_file,
+ const SourceFile& include_file,
+ const LocationRange& range,
+ Err* err) 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_;
+
+ // 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
new file mode 100644
index 00000000000..65198715469
--- /dev/null
+++ b/gn/tools/gn/header_checker_unittest.cc
@@ -0,0 +1,382 @@
+// Copyright 2014 The Chromium Authors. 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_));
+
+ // A file in target A can't include a header from D because A has no
+ // dependency on D.
+ EXPECT_FALSE(checker->CheckInclude(&a_, input_file, d_header, range, &err));
+ EXPECT_TRUE(err.has_error());
+
+ // A can include the public header in B.
+ err = Err();
+ EXPECT_TRUE(checker->CheckInclude(&a_, input_file, b_public, range, &err));
+ EXPECT_FALSE(err.has_error());
+
+ // Check A depending on the public and private headers in C.
+ err = Err();
+ EXPECT_TRUE(checker->CheckInclude(&a_, input_file, c_public, range, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_FALSE(checker->CheckInclude(&a_, input_file, c_private, range, &err));
+ EXPECT_TRUE(err.has_error());
+
+ // A can depend on a random file unknown to the build.
+ err = Err();
+ EXPECT_TRUE(checker->CheckInclude(&a_, input_file, SourceFile("//random.h"),
+ range, &err));
+ EXPECT_FALSE(err.has_error());
+
+ // A can depend on a file present only in another toolchain even with no
+ // dependency path.
+ err = Err();
+ EXPECT_TRUE(checker->CheckInclude(&a_, input_file, otc_header, range, &err));
+ EXPECT_FALSE(err.has_error());
+}
+
+// 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.
+ Err err;
+ EXPECT_FALSE(checker->CheckInclude(&b_, input_file, a_public, range, &err));
+ EXPECT_TRUE(err.has_error());
+
+ // 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.
+ err = Err();
+ EXPECT_TRUE(checker->CheckInclude(&b_, input_file, a_public, range, &err));
+ EXPECT_FALSE(err.has_error());
+}
+
+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.
+ err = Err();
+ EXPECT_FALSE(checker->CheckInclude(&b_, input_file, c_private, range, &err));
+ EXPECT_TRUE(err.has_error());
+
+ // A should be able to because of the friend declaration.
+ err = Err();
+ EXPECT_TRUE(checker->CheckInclude(&a_, input_file, c_private, range, &err));
+ EXPECT_FALSE(err.has_error()) << err.message();
+}
diff --git a/gn/tools/gn/import_manager.cc b/gn/tools/gn/import_manager.cc
new file mode 100644
index 00000000000..36c70881a80
--- /dev/null
+++ b/gn/tools/gn/import_manager.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 "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());
+
+ 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);
+ 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
new file mode 100644
index 00000000000..d72d5a79185
--- /dev/null
+++ b/gn/tools/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.
+ 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
new file mode 100644
index 00000000000..482256f48e0
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..e8568b26766
--- /dev/null
+++ b/gn/tools/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;
+ };
+
+ 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
new file mode 100644
index 00000000000..185b60bebac
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..df58d9d676a
--- /dev/null
+++ b/gn/tools/gn/input_conversion.cc
@@ -0,0 +1,357 @@
+// Copyright (c) 2013 The Chromium Authors. 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 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/tools/gn/input_conversion.h
new file mode 100644
index 00000000000..6e09eb4b0e9
--- /dev/null
+++ b/gn/tools/gn/input_conversion.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 The Chromium Authors. 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_CONVERSION_H_
+#define TOOLS_GN_INPUT_CONVERSION_H_
+
+#include <string>
+
+class Err;
+class ParseNode;
+class Settings;
+class Value;
+
+extern const char kInputOutputConversion_Help[];
+
+// Converts the given input string (is read from a file or output from a
+// script) to a Value. Conversions as specified in the input_conversion string
+// will be performed. The given origin will be used for constructing the
+// resulting Value.
+//
+// If the conversion string is invalid, the error will be set and an empty
+// value will be returned.
+Value ConvertInputToValue(const Settings* settings,
+ const std::string& input,
+ const ParseNode* origin,
+ const Value& input_conversion_value,
+ Err* err);
+
+#endif // TOOLS_GN_INPUT_CONVERSION_H_
diff --git a/gn/tools/gn/input_conversion_unittest.cc b/gn/tools/gn/input_conversion_unittest.cc
new file mode 100644
index 00000000000..434d952d064
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..438ab388d34
--- /dev/null
+++ b/gn/tools/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 "tools/gn/input_file.h"
+
+#include "base/files/file_util.h"
+
+InputFile::InputFile(const SourceFile& name)
+ : name_(name), dir_(name_.GetDir()), contents_loaded_(false) {}
+
+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
new file mode 100644
index 00000000000..49e5f855741
--- /dev/null
+++ b/gn/tools/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 "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_;
+ 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
new file mode 100644
index 00000000000..2290dc70447
--- /dev/null
+++ b/gn/tools/gn/input_file_manager.cc
@@ -0,0 +1,332 @@
+// Copyright (c) 2013 The Chromium Authors. 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.swap(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.swap(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
new file mode 100644
index 00000000000..20874d1d9d2
--- /dev/null
+++ b/gn/tools/gn/input_file_manager.h
@@ -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.
+
+#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.
+ typedef base::Callback<void(const ParseNode*)> FileLoadCallback;
+
+ 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.
+ typedef std::unordered_map<SourceFile, std::unique_ptr<InputFileData>>
+ InputFileMap;
+ 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
new file mode 100644
index 00000000000..f36ff023b36
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..f2e7c0658b8
--- /dev/null
+++ b/gn/tools/gn/item.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..4f4b0e7c41d
--- /dev/null
+++ b/gn/tools/gn/json_project_writer.cc
@@ -0,0 +1,220 @@
+// 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;
+}
+
+std::string RenderJSON(const BuildSettings* build_settings,
+ const Builder& builder,
+ 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;
+}
+
+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, builder, 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;
+}
diff --git a/gn/tools/gn/json_project_writer.h b/gn/tools/gn/json_project_writer.h
new file mode 100644
index 00000000000..8c293bfd93d
--- /dev/null
+++ b/gn/tools/gn/json_project_writer.h
@@ -0,0 +1,26 @@
+// 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);
+};
+
+#endif
diff --git a/gn/tools/gn/label.cc b/gn/tools/gn/label.cc
new file mode 100644
index 00000000000..e8494bee82e
--- /dev/null
+++ b/gn/tools/gn/label.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2013 The Chromium Authors. 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() = default;
+
+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());
+}
+
+Label::Label(const Label& other) = default;
+
+Label::~Label() = default;
+
+// 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
new file mode 100644
index 00000000000..3c3d85a4802
--- /dev/null
+++ b/gn/tools/gn/label.h
@@ -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.
+
+#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();
+
+ // 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);
+ Label(const Label& other);
+ ~Label();
+
+ // 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 {
+ if (int c = dir_.value().compare(other.dir_.value()))
+ return c < 0;
+ if (int c = name_.compare(other.name_))
+ return c < 0;
+ if (int c = toolchain_dir_.value().compare(other.toolchain_dir_.value()))
+ return c < 0;
+ return toolchain_name_ < other.toolchain_name_;
+ }
+
+ void swap(Label& other) {
+ dir_.swap(other.dir_);
+ name_.swap(other.name_);
+ toolchain_dir_.swap(other.toolchain_dir_);
+ toolchain_name_.swap(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
+
+inline void swap(Label& lhs, Label& rhs) {
+ lhs.swap(rhs);
+}
+
+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
new file mode 100644
index 00000000000..f5e6b9c1a08
--- /dev/null
+++ b/gn/tools/gn/label_pattern.cc
@@ -0,0 +1,276 @@
+// Copyright 2014 The Chromium Authors. 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
new file mode 100644
index 00000000000..231fd6d8957
--- /dev/null
+++ b/gn/tools/gn/label_pattern.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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
new file mode 100644
index 00000000000..7457a9b55a9
--- /dev/null
+++ b/gn/tools/gn/label_pattern_unittest.cc
@@ -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.
+
+#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
new file mode 100644
index 00000000000..5aaa02d5c91
--- /dev/null
+++ b/gn/tools/gn/label_ptr.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_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() : label(), ptr(nullptr), origin(nullptr) {}
+
+ explicit LabelPtrPair(const Label& l)
+ : label(l), ptr(nullptr), origin(nullptr) {}
+
+ // 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), origin(nullptr) {}
+
+ ~LabelPtrPair() {}
+
+ Label label;
+ const T* ptr; // May be NULL.
+
+ // 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;
+};
+
+typedef LabelPtrPair<Config> LabelConfigPair;
+typedef LabelPtrPair<Target> LabelTargetPair;
+
+typedef std::vector<LabelConfigPair> LabelConfigVector;
+typedef std::vector<LabelTargetPair> LabelTargetVector;
+
+// Comparison and search functions ---------------------------------------------
+
+// To do a brute-force search by label:
+// std::find_if(vect.begin(), vect.end(), LabelPtrLabelEquals<Config>(label));
+template <typename T>
+struct LabelPtrLabelEquals {
+ explicit LabelPtrLabelEquals(const Label& l) : label(l) {}
+
+ bool operator()(const LabelPtrPair<T>& arg) const {
+ return arg.label == label;
+ }
+
+ const Label& label;
+};
+
+// To do a brute-force search by object pointer:
+// std::find_if(vect.begin(), vect.end(), LabelPtrPtrEquals<Config>(config));
+template <typename T>
+struct LabelPtrPtrEquals {
+ explicit LabelPtrPtrEquals(const T* p) : ptr(p) {}
+
+ bool operator()(const LabelPtrPair<T>& arg) const { return arg.ptr == ptr; }
+
+ const T* ptr;
+};
+
+// To sort by label:
+// std::sort(vect.begin(), vect.end(), LabelPtrLabelLess<Config>());
+template <typename T>
+struct LabelPtrLabelLess {
+ bool operator()(const LabelPtrPair<T>& a, const LabelPtrPair<T>& b) const {
+ return a.label < b.label;
+ }
+};
+
+// 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
new file mode 100644
index 00000000000..26a276c844f
--- /dev/null
+++ b/gn/tools/gn/label_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..9c55aaa8747
--- /dev/null
+++ b/gn/tools/gn/lib_file.cc
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/lib_file.h"
+
+#include "base/logging.h"
+
+LibFile::LibFile() = default;
+
+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());
+}
+
+void LibFile::Swap(LibFile* other) {
+ name_.swap(other->name_);
+ source_file_.swap(other->source_file_);
+}
+
+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
new file mode 100644
index 00000000000..f1f073ffc71
--- /dev/null
+++ b/gn/tools/gn/lib_file.h
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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();
+ explicit LibFile(const base::StringPiece& lib_name);
+ explicit LibFile(const SourceFile& source_file);
+
+ void Swap(LibFile* other);
+ 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
+
+inline void swap(LibFile& lhs, LibFile& rhs) {
+ lhs.Swap(&rhs);
+}
+
+#endif // TOOLS_GN_LIB_FILE_H_
diff --git a/gn/tools/gn/loader.cc b/gn/tools/gn/loader.cc
new file mode 100644
index 00000000000..a069fb83d2b
--- /dev/null
+++ b/gn/tools/gn/loader.cc
@@ -0,0 +1,426 @@
+// Copyright (c) 2013 The Chromium Authors. 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();
+ toolchain_records_[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());
+
+ Err err;
+ root->Execute(base_config, &err);
+
+ // 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_.insert(LoadID(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
new file mode 100644
index 00000000000..71b22b8d889
--- /dev/null
+++ b/gn/tools/gn/loader.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..dc8b0df82ad
--- /dev/null
+++ b/gn/tools/gn/loader_unittest.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 <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
new file mode 100644
index 00000000000..b9e4c221f0d
--- /dev/null
+++ b/gn/tools/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 "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() : file_(nullptr), line_number_(-1), column_number_(-1) {}
+
+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/tools/gn/location.h
new file mode 100644
index 00000000000..de9b2fdbe6b
--- /dev/null
+++ b/gn/tools/gn/location.h
@@ -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.
+
+#ifndef TOOLS_GN_LOCATION_H_
+#define TOOLS_GN_LOCATION_H_
+
+#include <string>
+
+class InputFile;
+
+// Represents a place in a source file. Used for error reporting.
+class Location {
+ public:
+ Location();
+ Location(const InputFile* file, int line_number, int column_number, int byte);
+
+ const InputFile* file() const { return file_; }
+ int line_number() const { return line_number_; }
+ int column_number() const { return column_number_; }
+ int byte() const { return byte_; }
+ bool is_null() const { return *this == Location(); }
+
+ bool operator==(const Location& other) const;
+ bool operator!=(const Location& other) const;
+ bool operator<(const Location& other) const;
+
+ // Returns a string with the file, line, and (optionally) the character
+ // offset for this location. If this location is null, returns an empty
+ // string.
+ std::string Describe(bool include_column_number) const;
+
+ private:
+ const InputFile* file_; // Null when unset.
+ int line_number_; // -1 when unset. 1-based.
+ int column_number_; // -1 when unset. 1-based.
+ int byte_; // Index into the buffer, 0-based.
+};
+
+// Represents a range in a source file. Used for error reporting.
+// The end is exclusive i.e. [begin, end)
+class LocationRange {
+ public:
+ LocationRange();
+ LocationRange(const Location& begin, const Location& end);
+
+ const Location& begin() const { return begin_; }
+ const Location& end() const { return end_; }
+ bool is_null() const {
+ return begin_.is_null(); // No need to check both for the null case.
+ }
+
+ LocationRange Union(const LocationRange& other) const;
+
+ private:
+ Location begin_;
+ Location end_;
+};
+
+#endif // TOOLS_GN_LOCATION_H_
diff --git a/gn/tools/gn/misc/emacs/gn-mode.el b/gn/tools/gn/misc/emacs/gn-mode.el
new file mode 100644
index 00000000000..38564c6dd1e
--- /dev/null
+++ b/gn/tools/gn/misc/emacs/gn-mode.el
@@ -0,0 +1,191 @@
+;;; 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"
+ "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_plugins_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"))
+
+(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/tools/gn/misc/help_as_html.py
new file mode 100755
index 00000000000..f8f1c1bc271
--- /dev/null
+++ b/gn/tools/gn/misc/help_as_html.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Runs 'gn help' and various subhelps, and spits out html.
+# TODO:
+# - Handle numbered and dashed lists -> <ol> <ul>. (See "os" and "toolchain").
+# - Handle "Arguments:" blocks a bit better (the argument names could be
+# distinguished).
+# - Convert "|blahblah|" to <code>.
+# - Spit out other similar formats like wiki, markdown, whatever.
+
+import cgi
+import subprocess
+import sys
+
+
+def GetOutput(*args):
+ try:
+ return subprocess.check_output([sys.argv[1]] + list(args))
+ except subprocess.CalledProcessError:
+ return ''
+
+
+def ParseTopLevel(out):
+ commands = []
+ output = []
+ for line in out.splitlines():
+ if line.startswith(' '):
+ command, sep, rest = line.partition(':')
+ command = command.strip()
+ is_option = command.startswith('-')
+ output_line = ['<li>']
+ if not is_option:
+ commands.append(command)
+ output_line.append('<a href="#' + cgi.escape(command) + '">')
+ output_line.append(cgi.escape(command))
+ if not is_option:
+ output_line.append('</a>')
+ output_line.extend([sep + cgi.escape(rest) + '</li>'])
+ output.append(''.join(output_line))
+ else:
+ output.append('<h2>' + cgi.escape(line) + '</h2>')
+ return commands, output
+
+
+def ParseCommand(command, out):
+ first_line = True
+ got_example = False
+ output = []
+ for line in out.splitlines():
+ if first_line:
+ name, sep, rest = line.partition(':')
+ name = name.strip()
+ output.append('<h3><a name="' + cgi.escape(command) + '">' +
+ cgi.escape(name + sep + rest) + '</a></h3>')
+ first_line = False
+ else:
+ if line.startswith('Example'):
+ # Special subsection that's pre-formatted.
+ if got_example:
+ output.append('</pre>')
+ got_example = True
+ output.append('<h4>Example</h4>')
+ output.append('<pre>')
+ elif not line.strip():
+ output.append('<p>')
+ elif not line.startswith(' ') and line.endswith(':'):
+ # Subsection.
+ output.append('<h4>' + cgi.escape(line[:-1]) + '</h4>')
+ else:
+ output.append(cgi.escape(line))
+ if got_example:
+ output.append('</pre>')
+ return output
+
+
+def main():
+ if len(sys.argv) < 2:
+ print 'usage: help_as_html.py <gn_binary>'
+ return 1
+ header = '''<!DOCTYPE html>
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <style>
+ body { font-family: Arial, sans-serif; font-size: small; }
+ pre { font-family: Consolas, monospace; font-size: small; }
+ #container { margin: 0 auto; max-width: 48rem; width: 90%; }
+ </style>
+ </head>
+ <body>
+ <div id="container"><h1>GN</h1>
+'''
+ footer = '</div></body></html>'
+ commands, output = ParseTopLevel(GetOutput('help'))
+ for command in commands:
+ output += ParseCommand(command, GetOutput('help', command))
+ print header + '\n'.join(output) + footer
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/gn/tools/gn/misc/tm/GN.tmLanguage b/gn/tools/gn/misc/tm/GN.tmLanguage
new file mode 100644
index 00000000000..9c244ff86d4
--- /dev/null
+++ b/gn/tools/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)\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)\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)\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/tools/gn/misc/tm/GN.tmPreferences
new file mode 100644
index 00000000000..2706d51344a
--- /dev/null
+++ b/gn/tools/gn/misc/tm/GN.tmPreferences
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>name</key>
+ <string>Comments</string>
+ <key>scope</key>
+ <string>source.gn</string>
+ <key>settings</key>
+ <dict>
+ <key>shellVariables</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>TM_COMMENT_START</string>
+ <key>value</key>
+ <string># </string>
+ </dict>
+ </array>
+ </dict>
+</dict>
+</plist> \ No newline at end of file
diff --git a/gn/tools/gn/misc/vim/README.chromium b/gn/tools/gn/misc/vim/README.chromium
new file mode 100644
index 00000000000..7b73cfc2675
--- /dev/null
+++ b/gn/tools/gn/misc/vim/README.chromium
@@ -0,0 +1,5 @@
+You can use this by adding
+
+ set runtimepath+=/path/to/src/tools/gn/misc/vim
+
+to your .vimrc.
diff --git a/gn/tools/gn/misc/vim/autoload/gn.vim b/gn/tools/gn/misc/vim/autoload/gn.vim
new file mode 100644
index 00000000000..5573efc0af3
--- /dev/null
+++ b/gn/tools/gn/misc/vim/autoload/gn.vim
@@ -0,0 +1,26 @@
+" 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.
+
+function! gn#TranslateToBuildFile(name) abort
+ " Strip '//' prefix
+ let l:new_path = substitute(a:name, '\v^//', '', '')
+
+ " Strip the build target name (necessary if 'isfname' contains ':')
+ let l:new_path = substitute(l:new_path, '\v:.*$', '', '')
+
+ " Append 'BUILD.gn', only if this is a directory and not a file
+ " Prefer using maktaba if it's available, but fallback to an alternative
+ if exists('*maktaba#path#Basename')
+ " Check if the last part of the path appears to be a file
+ if maktaba#path#Basename(l:new_path) !~# '\V.'
+ let l:new_path = maktaba#path#Join([l:new_path, 'BUILD.gn'])
+ endif
+ else
+ " This will break if 'autochdir' is enabled
+ if isdirectory(l:new_path)
+ let l:new_path = substitute(l:new_path, '\v/?$', '/BUILD.gn', '')
+ endif
+ endif
+ return l:new_path
+endfunction
diff --git a/gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim b/gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim
new file mode 100644
index 00000000000..20448c15b11
--- /dev/null
+++ b/gn/tools/gn/misc/vim/ftdetect/gnfiletype.vim
@@ -0,0 +1,27 @@
+" Copyright 2014 The Chromium Authors. All rights reserved.
+" Use of this source code is governed by a BSD-style license that can be
+" found in the LICENSE file.
+
+" We take care to preserve the user's fileencodings and fileformats,
+" because those settings are global (not buffer local), yet we want
+" to override them for loading GN files, which should be UTF-8.
+let s:current_fileformats = ''
+let s:current_fileencodings = ''
+
+" define fileencodings to open as utf-8 encoding even if it's ascii.
+function! s:gnfiletype_pre()
+ let s:current_fileformats = &g:fileformats
+ let s:current_fileencodings = &g:fileencodings
+ set fileencodings=utf-8 fileformats=unix
+ setlocal filetype=gn
+endfunction
+
+" restore fileencodings as others
+function! s:gnfiletype_post()
+ let &g:fileformats = s:current_fileformats
+ let &g:fileencodings = s:current_fileencodings
+endfunction
+
+au BufNewFile *.gn,*.gni setlocal filetype=gn fileencoding=utf-8 fileformat=unix
+au BufRead *.gn,*.gni call s:gnfiletype_pre()
+au BufReadPost *.gn,*.gni call s:gnfiletype_post()
diff --git a/gn/tools/gn/misc/vim/ftplugin/gn.vim b/gn/tools/gn/misc/vim/ftplugin/gn.vim
new file mode 100644
index 00000000000..ede251dfe99
--- /dev/null
+++ b/gn/tools/gn/misc/vim/ftplugin/gn.vim
@@ -0,0 +1,12 @@
+" 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.
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal includeexpr=gn#TranslateToBuildFile(v:fname)
+
+setlocal commentstring=#\ %s
diff --git a/gn/tools/gn/misc/vim/gn-format.py b/gn/tools/gn/misc/vim/gn-format.py
new file mode 100644
index 00000000000..561c76c828a
--- /dev/null
+++ b/gn/tools/gn/misc/vim/gn-format.py
@@ -0,0 +1,59 @@
+# Copyright 2014 The Chromium 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] is not '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
new file mode 100644
index 00000000000..9dee605f305
--- /dev/null
+++ b/gn/tools/gn/misc/vim/syntax/gn.vim
@@ -0,0 +1,84 @@
+" Copyright 2014 The Chromium 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
+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
+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
new file mode 100644
index 00000000000..c2016dcf5e3
--- /dev/null
+++ b/gn/tools/gn/ninja_action_target_writer.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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(Toolchain::TYPE_ACTION);
+ 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
new file mode 100644
index 00000000000..36d7cd96d9f
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..19f81aefc03
--- /dev/null
+++ b/gn/tools/gn/ninja_action_target_writer_unittest.cc
@@ -0,0 +1,477 @@
+// Copyright (c) 2013 The Chromium Authors. 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 = 1 /* skip initial newline */ + 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());
+}
+
+// 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 = 1 /* skip initial newline */ + 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
new file mode 100644
index 00000000000..9e5632e7b25
--- /dev/null
+++ b/gn/tools/gn/ninja_binary_target_writer.cc
@@ -0,0 +1,890 @@
+// Copyright (c) 2013 The Chromium Authors. 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 <stddef.h>
+#include <string.h>
+
+#include <cstring>
+#include <set>
+#include <sstream>
+#include <unordered_set>
+
+#include "base/strings/string_util.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/ninja_utils.h"
+#include "tools/gn/ninja_target_command_util.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/source_file_type.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/substitution_writer.h"
+#include "tools/gn/target.h"
+
+// Represents a set of tool types. Must be first since it is also shared by
+// some helper functions in the anonymous namespace below.
+class NinjaBinaryTargetWriter::SourceFileTypeSet {
+ public:
+ SourceFileTypeSet() {
+ memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES));
+ }
+
+ void Set(SourceFileType type) { flags_[static_cast<int>(type)] = true; }
+ bool Get(SourceFileType type) const { return flags_[static_cast<int>(type)]; }
+
+ private:
+ bool flags_[static_cast<int>(SOURCE_NUMTYPES)];
+};
+
+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(Toolchain::ToolType type) {
+ switch (type) {
+ case Toolchain::TYPE_CC:
+ return "c-header";
+ case Toolchain::TYPE_CXX:
+ return "c++-header";
+ case Toolchain::TYPE_OBJC:
+ return "objective-c-header";
+ case Toolchain::TYPE_OBJCXX:
+ return "objective-c++-header";
+ default:
+ NOTREACHED() << "Not a valid PCH tool type: " << type;
+ return "";
+ }
+}
+
+// Appends the object files generated by the given source set to the given
+// output vector.
+void AddSourceSetObjectFiles(const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) {
+ std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
+ NinjaBinaryTargetWriter::SourceFileTypeSet used_types;
+
+ // 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()) {
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (source_set->GetOutputFilesForSource(source, &tool_type, &tool_outputs))
+ obj_files->push_back(tool_outputs[0]);
+
+ used_types.Set(GetSourceFileType(source));
+ }
+
+ // 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 (used_types.Get(SOURCE_C)) {
+ const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CC);
+ if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, Toolchain::TYPE_CC, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (used_types.Get(SOURCE_CPP)) {
+ const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CXX);
+ if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (used_types.Get(SOURCE_M)) {
+ const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_OBJC);
+ if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (used_types.Get(SOURCE_MM)) {
+ const Tool* tool =
+ source_set->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
+ if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJCXX, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ }
+}
+
+} // namespace
+
+NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out),
+ tool_(target->toolchain()->GetToolForTargetFinalOutput(target)),
+ rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
+
+NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default;
+
+void NinjaBinaryTargetWriter::Run() {
+ // Figure out what source types are needed.
+ SourceFileTypeSet used_types;
+ for (const auto& source : target_->sources())
+ used_types.Set(GetSourceFileType(source));
+
+ WriteCompilerVars(used_types);
+
+ 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(used_types, 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;
+ AddSourceSetObjectFiles(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 NinjaBinaryTargetWriter::WriteCompilerVars(
+ const SourceFileTypeSet& used_types) {
+ const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
+ // Defines.
+ if (subst.used[SUBSTITUTION_DEFINES]) {
+ out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " =";
+ RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
+ DefineWriter(), out_);
+ out_ << std::endl;
+ }
+
+ // Include directories.
+ if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
+ out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
+ 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 (used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) {
+ WriteOneFlag(target_, SUBSTITUTION_ASMFLAGS, false, Toolchain::TYPE_NONE,
+ &ConfigValues::asmflags, opts, path_output_, out_);
+ }
+ if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) ||
+ used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM)) {
+ WriteOneFlag(target_, SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
+ &ConfigValues::cflags, opts, path_output_, out_);
+ }
+ if (used_types.Get(SOURCE_C)) {
+ WriteOneFlag(target_, SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
+ Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts, path_output_, out_);
+ }
+ if (used_types.Get(SOURCE_CPP)) {
+ WriteOneFlag(target_, SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
+ Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts, path_output_, out_);
+ }
+ if (used_types.Get(SOURCE_M)) {
+ WriteOneFlag(target_, SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
+ Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts, path_output_, out_);
+ }
+ if (used_types.Get(SOURCE_MM)) {
+ WriteOneFlag(target_, SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
+ Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts, path_output_, out_);
+ }
+
+ WriteSharedVars(subst);
+}
+
+OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const {
+ CHECK(target_->toolchain()) << "Toolchain not set on target "
+ << target_->label().GetUserVisibleName(true);
+
+ 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.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 input_stamp_file =
+ GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
+ input_stamp_file.value().append(target_->label().name());
+ input_stamp_file.value().append(".inputs.stamp");
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, input_stamp_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+
+ // File inputs.
+ for (const auto* input : inputs) {
+ out_ << " ";
+ path_output_.WriteFile(out_, *input);
+ }
+
+ out_ << "\n";
+ return input_stamp_file;
+}
+
+void NinjaBinaryTargetWriter::WritePCHCommands(
+ const SourceFileTypeSet& used_types,
+ 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 Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC);
+ if (tool_c && tool_c->precompiled_header_type() != Tool::PCH_NONE &&
+ used_types.Get(SOURCE_C)) {
+ WritePCHCommand(SUBSTITUTION_CFLAGS_C, Toolchain::TYPE_CC,
+ tool_c->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+ const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX);
+ if (tool_cxx && tool_cxx->precompiled_header_type() != Tool::PCH_NONE &&
+ used_types.Get(SOURCE_CPP)) {
+ WritePCHCommand(SUBSTITUTION_CFLAGS_CC, Toolchain::TYPE_CXX,
+ tool_cxx->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+
+ const Tool* tool_objc = target_->toolchain()->GetTool(Toolchain::TYPE_OBJC);
+ if (tool_objc && tool_objc->precompiled_header_type() == Tool::PCH_GCC &&
+ used_types.Get(SOURCE_M)) {
+ WritePCHCommand(SUBSTITUTION_CFLAGS_OBJC, Toolchain::TYPE_OBJC,
+ tool_objc->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+
+ const Tool* tool_objcxx =
+ target_->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
+ if (tool_objcxx && tool_objcxx->precompiled_header_type() == Tool::PCH_GCC &&
+ used_types.Get(SOURCE_MM)) {
+ WritePCHCommand(SUBSTITUTION_CFLAGS_OBJCC, Toolchain::TYPE_OBJCXX,
+ tool_objcxx->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+}
+
+void NinjaBinaryTargetWriter::WritePCHCommand(
+ SubstitutionType flag_type,
+ Toolchain::ToolType tool_type,
+ Tool::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 Tool::PCH_MSVC:
+ WriteWindowsPCHCommand(flag_type, tool_type, input_dep, order_only_deps,
+ object_files);
+ break;
+ case Tool::PCH_GCC:
+ WriteGCCPCHCommand(flag_type, tool_type, input_dep, order_only_deps,
+ other_files);
+ break;
+ case Tool::PCH_NONE:
+ NOTREACHED() << "Cannot write a PCH command with no PCH header type";
+ break;
+ }
+}
+
+void NinjaBinaryTargetWriter::WriteGCCPCHCommand(
+ SubstitutionType flag_type,
+ Toolchain::ToolType tool_type,
+ 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_type, &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_type, outputs);
+
+ // This build line needs a custom language-specific flags value. Rule-specific
+ // variables are just indented underneath the rule line.
+ out_ << " " << kSubstitutionNinjaNames[flag_type] << " =";
+
+ // 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_type == Toolchain::TYPE_CC) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts,
+ out_);
+ } else if (tool_type == Toolchain::TYPE_CXX) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc,
+ opts, out_);
+ } else if (tool_type == Toolchain::TYPE_OBJC) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc,
+ opts, out_);
+ } else if (tool_type == Toolchain::TYPE_OBJCXX) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc,
+ opts, out_);
+ }
+
+ // Append the command to specify the language of the .gch file.
+ out_ << " -x " << GetPCHLangForToolType(tool_type);
+
+ // Write two blank lines to help separate the PCH build lines from the
+ // regular source build lines.
+ out_ << std::endl << std::endl;
+}
+
+void NinjaBinaryTargetWriter::WriteWindowsPCHCommand(
+ SubstitutionType flag_type,
+ Toolchain::ToolType tool_type,
+ 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_type, &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_type, outputs);
+
+ // This build line needs a custom language-specific flags value. Rule-specific
+ // variables are just indented underneath the rule line.
+ out_ << " " << kSubstitutionNinjaNames[flag_type] << " =";
+
+ // Append the command to generate the .pch file.
+ // This adds the value to the existing flag instead of overwriting it.
+ out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}";
+ 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 NinjaBinaryTargetWriter::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);
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (!target_->GetOutputFilesForSource(source, &tool_type, &tool_outputs)) {
+ if (GetSourceFileType(source) == 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_type != Toolchain::TYPE_NONE) {
+ // 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 Tool* tool = target_->toolchain()->GetTool(tool_type);
+ if (tool->precompiled_header_type() != Tool::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() == Tool::PCH_MSVC) {
+ output_extension = GetWindowsPCHObjectExtension(
+ tool_type, output_value.substr(extension_offset - 1));
+ } else if (tool->precompiled_header_type() == Tool::PCH_GCC) {
+ output_extension = GetGCCPCHOutputExtension(tool_type);
+ }
+ 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_type,
+ 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 NinjaBinaryTargetWriter::WriteCompilerBuildLine(
+ const SourceFile& source,
+ const std::vector<OutputFile>& extra_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ Toolchain::ToolType tool_type,
+ const std::vector<OutputFile>& outputs) {
+ out_ << "build";
+ path_output_.WriteFiles(out_, outputs);
+
+ out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type);
+ 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;
+}
+
+void NinjaBinaryTargetWriter::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_
+ << Toolchain::ToolTypeToName(
+ target_->toolchain()->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 (GetSourceFileType(src_file) == 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 NinjaBinaryTargetWriter::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 NinjaBinaryTargetWriter::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 NinjaBinaryTargetWriter::WriteOutputSubstitutions() {
+ out_ << " output_extension = "
+ << SubstitutionWriter::GetLinkerSubstitution(
+ target_, tool_, SUBSTITUTION_OUTPUT_EXTENSION);
+ out_ << std::endl;
+ out_ << " output_dir = "
+ << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
+ SUBSTITUTION_OUTPUT_DIR);
+ out_ << std::endl;
+}
+
+void NinjaBinaryTargetWriter::WriteSolibs(
+ const std::vector<OutputFile>& solibs) {
+ if (solibs.empty())
+ return;
+
+ out_ << " solibs =";
+ path_output_.WriteFiles(out_, solibs);
+ out_ << std::endl;
+}
+
+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 set:
+ // 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)
+ AddSourceSetObjectFiles(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_->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::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 NinjaBinaryTargetWriter::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/tools/gn/ninja_binary_target_writer.h b/gn/tools/gn/ninja_binary_target_writer.h
new file mode 100644
index 00000000000..5cb7ab5d889
--- /dev/null
+++ b/gn/tools/gn/ninja_binary_target_writer.h
@@ -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.
+
+#ifndef TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
+
+#include "base/macros.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;
+class SourceFileTypeSet;
+
+// Writes a .ninja file for a binary target type (an executable, a shared
+// library, or a static library).
+class NinjaBinaryTargetWriter : public NinjaTargetWriter {
+ public:
+ class SourceFileTypeSet;
+
+ NinjaBinaryTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaBinaryTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ typedef std::set<OutputFile> OutputFileSet;
+
+ // Writes all flags for the compiler: includes, defines, cflags, etc.
+ void WriteCompilerVars(const SourceFileTypeSet& used_types);
+
+ // 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 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 SourceFileTypeSet& used_types,
+ 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(SubstitutionType flag_type,
+ Toolchain::ToolType tool_type,
+ Tool::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(SubstitutionType flag_type,
+ Toolchain::ToolType tool_type,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* gch_files);
+
+ void WriteWindowsPCHCommand(SubstitutionType flag_type,
+ Toolchain::ToolType tool_type,
+ 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);
+
+ // Writes a build line.
+ void WriteCompilerBuildLine(const SourceFile& source,
+ const std::vector<OutputFile>& extra_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ Toolchain::ToolType tool_type,
+ const std::vector<OutputFile>& outputs);
+
+ 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 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;
+
+ // 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 Tool* tool_;
+
+ // Cached version of the prefix used for rule types for this toolchain.
+ std::string rule_prefix_;
+
+ 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
new file mode 100644
index 00000000000..a0a7ce8fdca
--- /dev/null
+++ b/gn/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -0,0 +1,1155 @@
+// Copyright (c) 2013 The Chromium Authors. 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 <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 NinjaBinaryTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaBinaryTargetWriterTest, 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.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Source set itself.
+ {
+ 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);
+ }
+
+ // 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;
+ NinjaBinaryTargetWriter 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;
+ NinjaBinaryTargetWriter 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;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, StaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.config_values().arflags().push_back("--asdf");
+ 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 = 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(NinjaBinaryTargetWriterTest, CompleteStaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ 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"));
+
+ 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;
+ 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 = 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;
+ 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 = 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(NinjaBinaryTargetWriterTest, 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.public_deps().push_back(LabelTargetPair(&action));
+ 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 = 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(NinjaBinaryTargetWriterTest, 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.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;
+ NinjaBinaryTargetWriter 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.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;
+ NinjaBinaryTargetWriter 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.private_deps().push_back(LabelTargetPair(&gen_lib));
+ executable.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
+
+ std::ostringstream final_out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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;
+ 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 = 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(NinjaBinaryTargetWriterTest, 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.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 = 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(NinjaBinaryTargetWriterTest, 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"));
+ ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
+
+ // Write out the intermediate target.
+ std::ostringstream inter_out;
+ NinjaBinaryTargetWriter 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"));
+ ASSERT_TRUE(exe.OnResolved(&err));
+
+ std::ostringstream final_out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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"));
+ ASSERT_TRUE(shared_lib.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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"));
+ ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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"));
+ ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
+
+ std::ostringstream final_out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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_tool = std::make_unique<Tool>();
+ TestWithScope::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_precompiled_header_type(Tool::PCH_MSVC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ TestWithScope::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"));
+ cc_tool->set_precompiled_header_type(Tool::PCH_MSVC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ 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.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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_tool = std::make_unique<Tool>();
+ TestWithScope::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_precompiled_header_type(Tool::PCH_GCC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ TestWithScope::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"));
+ cc_tool->set_precompiled_header_type(Tool::PCH_GCC);
+ pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ 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.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, DupeObjFileError) {
+ TestWithScope setup;
+ TestTarget target(setup, "//foo:bar", Target::EXECUTABLE);
+ target.sources().push_back(SourceFile("//a.cc"));
+ target.sources().push_back(SourceFile("//a.cc"));
+
+ EXPECT_FALSE(scheduler().is_failed());
+
+ scheduler().SuppressOutputForTesting(true);
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter 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(NinjaBinaryTargetWriterTest, 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.config_values().inputs().push_back(SourceFile("//foo/input.data"));
+ 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"
+ " | ../../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;
+ 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 ./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.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;
+ 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.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.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;
+ 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.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_build_writer.cc b/gn/tools/gn/ninja_build_writer.cc
new file mode 100644
index 00000000000..f26c9a7fd64
--- /dev/null
+++ b/gn/tools/gn/ninja_build_writer.cc
@@ -0,0 +1,596 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+};
+
+std::string GetSelfInvocationCommand(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
+
+ 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::kArgs) {
+ std::string escaped_value =
+ EscapeString(FilePathToUTF8(i->second), escape_shell, nullptr);
+ cmdline.AppendSwitchASCII(i->first, escaped_value);
+ }
+ }
+
+#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 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),
+ 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, 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());
+ for (const auto& other_file : fileset) {
+ const base::FilePath file =
+ MakeAbsoluteFilePathRelativeIfPossible(build_path, other_file);
+ dep_out_ << " " << FilePathToUTF8(file.NormalizePathSeparatorsTo('/'));
+ }
+
+ 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 (int j = Toolchain::TYPE_NONE + 1; j < Toolchain::TYPE_NUMTYPES; j++) {
+ Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(j);
+ const Tool* tool = pair.second->GetTool(tool_type);
+ if (tool && tool->pool().ptr)
+ used_pools.insert(tool->pool().ptr);
+ }
+ }
+
+ for (const Target* target : default_toolchain_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
new file mode 100644
index 00000000000..eee3a6e1f84
--- /dev/null
+++ b/gn/tools/gn/ninja_build_writer.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+
+// 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 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 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[];
+
+#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
new file mode 100644
index 00000000000..bd93d36a8e2
--- /dev/null
+++ b/gn/tools/gn/ninja_build_writer_unittest.cc
@@ -0,0 +1,152 @@
+// 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 "tools/gn/ninja_build_writer.h"
+#include "tools/gn/pool.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"
+
+using NinjaBuildWriterTest = TestWithScheduler;
+
+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(Toolchain::TYPE_LINK)
+ ->set_pool(LabelPtrPair<Pool>(&other_regular_pool));
+
+ // 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(Toolchain::TYPE_STAMP)
+ ->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};
+
+ std::ostringstream ninja_out;
+ std::ostringstream depfile_out;
+
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains,
+ 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_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 foo$:bar: phony obj/foo/bar.stamp\n"
+ "build bar$:bar: phony obj/bar/bar.stamp\n";
+ const char expected_root_target[] =
+ "build all: phony $\n"
+ " obj/foo/bar.stamp $\n"
+ " obj/bar/bar.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, 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,
+ 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
new file mode 100644
index 00000000000..ec3cf74c207
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..c097f67b591
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..5b1a4bae48a
--- /dev/null
+++ b/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc
@@ -0,0 +1,53 @@
+// 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.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.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_copy_target_writer.cc b/gn/tools/gn/ninja_copy_target_writer.cc
new file mode 100644
index 00000000000..2a6f0018c66
--- /dev/null
+++ b/gn/tools/gn/ninja_copy_target_writer.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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(Toolchain::TYPE_COPY);
+ 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(Toolchain::TYPE_STAMP);
+ 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_) +
+ Toolchain::ToolTypeToName(Toolchain::TYPE_COPY);
+
+ 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
new file mode 100644
index 00000000000..a45a470a3d4
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..be17a11556b
--- /dev/null
+++ b/gn/tools/gn/ninja_copy_target_writer_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..9834aba1c66
--- /dev/null
+++ b/gn/tools/gn/ninja_create_bundle_target_writer.cc
@@ -0,0 +1,337 @@
+// 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/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 {
+
+void FailWithMissingToolError(Toolchain::ToolType tool, const Target* target) {
+ const std::string& tool_name = Toolchain::ToolTypeToName(tool);
+ g_scheduler->FailWithError(
+ Err(nullptr, 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 Toolchain::ToolType kRequiredTools[] = {
+ Toolchain::TYPE_COPY_BUNDLE_DATA,
+ Toolchain::TYPE_COMPILE_XCASSETS,
+ Toolchain::TYPE_STAMP,
+ };
+
+ for (size_t i = 0; i < arraysize(kRequiredTools); ++i) {
+ if (!target->toolchain()->GetTool(kRequiredTools[i])) {
+ FailWithMissingToolError(kRequiredTools[i], 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()) {
+ OutputFile output_file = file_rule.ApplyPatternToSourceAsOutputFile(
+ settings_, target_->bundle_data(), source_file);
+ output_files->push_back(output_file);
+
+ out_ << "build ";
+ path_output_.WriteFile(out_, output_file);
+ out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_COPY_BUNDLE_DATA) << " ";
+ 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 (target_->bundle_data().assets_catalog_sources().empty() &&
+ target_->bundle_data().partial_info_plist().is_null())
+ 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_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ 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_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_COMPILE_XCASSETS);
+
+ 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_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+
+ 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->swap(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_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+
+ 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
new file mode 100644
index 00000000000..072e02c2a73
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..3c62c59d133
--- /dev/null
+++ b/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -0,0 +1,456 @@
+// 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");
+ bundle_data->plugins_dir() =
+ SourceDir(bundle_data->contents_dir().value() + "/Plug Ins");
+}
+
+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.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.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 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(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/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\n"
+ "build bar.bundle/Contents/Resources/Assets.car | "
+ "baz/bar/bar_partial_info.plist: compile_xcassets "
+ "../../foo/Foo.xcassets "
+ "../../quz/Quz.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_group_target_writer.cc b/gn/tools/gn/ninja_group_target_writer.cc
new file mode 100644
index 00000000000..85906bb1514
--- /dev/null
+++ b/gn/tools/gn/ninja_group_target_writer.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..66e5f043155
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..ab5f5978710
--- /dev/null
+++ b/gn/tools/gn/ninja_group_target_writer_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. 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_target_command_util.cc b/gn/tools/gn/ninja_target_command_util.cc
new file mode 100644
index 00000000000..29662cddb83
--- /dev/null
+++ b/gn/tools/gn/ninja_target_command_util.cc
@@ -0,0 +1,179 @@
+// 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/substitution_writer.h"
+
+namespace {
+
+// Returns the language-specific suffix for precompiled header files.
+const char* GetPCHLangSuffixForToolType(Toolchain::ToolType type) {
+ switch (type) {
+ case Toolchain::TYPE_CC:
+ return "c";
+ case Toolchain::TYPE_CXX:
+ return "cc";
+ case Toolchain::TYPE_OBJC:
+ return "m";
+ case Toolchain::TYPE_OBJCXX:
+ return "mm";
+ default:
+ NOTREACHED() << "Not a valid PCH tool type: " << type;
+ 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,
+ Toolchain::ToolType tool_type) {
+ // 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_type));
+ ret.value().append(".pch");
+
+ return ret;
+}
+
+void WriteOneFlag(const Target* target,
+ SubstitutionType subst_enum,
+ bool has_precompiled_headers,
+ Toolchain::ToolType tool_type,
+ 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[subst_enum])
+ return;
+
+ if (write_substitution)
+ out << kSubstitutionNinjaNames[subst_enum] << " =";
+
+ if (has_precompiled_headers) {
+ const Tool* tool = target->toolchain()->GetTool(tool_type);
+ if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+ // Name the .pch file.
+ out << " /Fp";
+ path_output.WriteFile(out, GetWindowsPCHFile(target, tool_type));
+
+ // 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() == Tool::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_type, &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,
+ Toolchain::ToolType tool_type,
+ 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 Tool* tool = target->toolchain()->GetTool(tool_type);
+ 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;
+ Tool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
+ switch (header_type) {
+ case Tool::PCH_MSVC:
+ output_extension = GetWindowsPCHObjectExtension(
+ tool_type, output_value.substr(extension_offset - 1));
+ break;
+ case Tool::PCH_GCC:
+ output_extension = GetGCCPCHOutputExtension(tool_type);
+ break;
+ case Tool::PCH_NONE:
+ NOTREACHED() << "No outputs for no PCH type.";
+ break;
+ }
+ output_value.replace(extension_offset - 1, std::string::npos,
+ output_extension);
+}
+
+std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type) {
+ const char* lang_suffix = GetPCHLangSuffixForToolType(tool_type);
+ 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(Toolchain::ToolType tool_type,
+ const std::string& obj_extension) {
+ const char* lang_suffix = GetPCHLangSuffixForToolType(tool_type);
+ 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
new file mode 100644
index 00000000000..619add3d20c
--- /dev/null
+++ b/gn/tools/gn/ninja_target_command_util.h
@@ -0,0 +1,84 @@
+// 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,
+ SubstitutionType subst_enum,
+ bool has_precompiled_headers,
+ Toolchain::ToolType tool_type,
+ 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,
+ Toolchain::ToolType tool_type,
+ std::vector<OutputFile>* outputs);
+
+std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type);
+std::string GetWindowsPCHObjectExtension(Toolchain::ToolType tool_type,
+ 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
new file mode 100644
index 00000000000..de87e8f9a51
--- /dev/null
+++ b/gn/tools/gn/ninja_target_writer.cc
@@ -0,0 +1,324 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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_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->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(SubstitutionType type) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA;
+
+ out_ << kSubstitutionNinjaNames[type] << " = ";
+ 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[SUBSTITUTION_LABEL]) {
+ WriteEscapedSubstitution(SUBSTITUTION_LABEL);
+ written_anything = true;
+ }
+
+ // Target label name
+ if (bits.used[SUBSTITUTION_LABEL_NAME]) {
+ WriteEscapedSubstitution(SUBSTITUTION_LABEL_NAME);
+ written_anything = true;
+ }
+
+ // Root gen dir.
+ if (bits.used[SUBSTITUTION_ROOT_GEN_DIR]) {
+ WriteEscapedSubstitution(SUBSTITUTION_ROOT_GEN_DIR);
+ written_anything = true;
+ }
+
+ // Root out dir.
+ if (bits.used[SUBSTITUTION_ROOT_OUT_DIR]) {
+ WriteEscapedSubstitution(SUBSTITUTION_ROOT_OUT_DIR);
+ written_anything = true;
+ }
+
+ // Target gen dir.
+ if (bits.used[SUBSTITUTION_TARGET_GEN_DIR]) {
+ WriteEscapedSubstitution(SUBSTITUTION_TARGET_GEN_DIR);
+ written_anything = true;
+ }
+
+ // Target out dir.
+ if (bits.used[SUBSTITUTION_TARGET_OUT_DIR]) {
+ WriteEscapedSubstitution(SUBSTITUTION_TARGET_OUT_DIR);
+ written_anything = true;
+ }
+
+ // Target output name.
+ if (bits.used[SUBSTITUTION_TARGET_OUTPUT_NAME]) {
+ WriteEscapedSubstitution(SUBSTITUTION_TARGET_OUTPUT_NAME);
+ 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_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ 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_)
+ << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ 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
new file mode 100644
index 00000000000..a6db06038f2
--- /dev/null
+++ b/gn/tools/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 "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(SubstitutionType 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
new file mode 100644
index 00000000000..3a33192363a
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..ee7c5cdc021
--- /dev/null
+++ b/gn/tools/gn/ninja_toolchain_writer.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 "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/filesystem_utils.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 (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
+ Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
+ const Tool* tool = toolchain_->GetTool(tool_type);
+ if (tool_type == Toolchain::TYPE_ACTION)
+ continue;
+ if (tool)
+ WriteToolRule(tool_type, tool, 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(const Toolchain::ToolType type,
+ const Tool* tool,
+ const std::string& rule_prefix) {
+ out_ << "rule " << rule_prefix << Toolchain::ToolTypeToName(type)
+ << std::endl;
+
+ // Rules explicitly include shell commands, so don't try to escape.
+ EscapeOptions options;
+ options.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
+
+ CHECK(!tool->command().empty()) << "Command should not be empty";
+ WriteRulePattern("command", tool->command(), options);
+
+ WriteRulePattern("description", tool->description(), options);
+ WriteRulePattern("rspfile", tool->rspfile(), options);
+ WriteRulePattern("rspfile_content", tool->rspfile_content(), options);
+
+ if (tool->depsformat() == Tool::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 (tool->depsformat() == Tool::DEPS_MSVC) {
+ // MSVC deps don't have a depfile.
+ out_ << kIndent << "deps = msvc" << 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;
+}
diff --git a/gn/tools/gn/ninja_toolchain_writer.h b/gn/tools/gn/ninja_toolchain_writer.h
new file mode 100644
index 00000000000..9c6ad24b96b
--- /dev/null
+++ b/gn/tools/gn/ninja_toolchain_writer.h
@@ -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.
+
+#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);
+
+ NinjaToolchainWriter(const Settings* settings,
+ const Toolchain* toolchain,
+ std::ostream& out);
+ ~NinjaToolchainWriter();
+
+ void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
+
+ void WriteRules();
+ void WriteToolRule(Toolchain::ToolType type,
+ const Tool* tool,
+ const std::string& rule_prefix);
+ void WriteRulePattern(const char* name,
+ const SubstitutionPattern& pattern,
+ 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
new file mode 100644
index 00000000000..a1963fd4b0b
--- /dev/null
+++ b/gn/tools/gn/ninja_toolchain_writer_unittest.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. 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(Toolchain::TYPE_CC,
+ setup.toolchain()->GetTool(Toolchain::TYPE_CC),
+ std::string("prefix_"));
+
+ EXPECT_EQ(
+ "rule prefix_cc\n"
+ " command = cc ${in} ${cflags} ${cflags_c} ${defines} ${include_dirs} "
+ "-o ${out}\n",
+ stream.str());
+}
diff --git a/gn/tools/gn/ninja_utils.cc b/gn/tools/gn/ninja_utils.cc
new file mode 100644
index 00000000000..98f320517f9
--- /dev/null
+++ b/gn/tools/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 "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_utils.h b/gn/tools/gn/ninja_utils.h
new file mode 100644
index 00000000000..60ae6b27a66
--- /dev/null
+++ b/gn/tools/gn/ninja_utils.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. 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_UTILS_H_
+#define TOOLS_GN_NINJA_UTILS_H_
+
+#include <string>
+
+class Settings;
+class SourceFile;
+class Target;
+
+// Example: "base/base.ninja". The string version will not be escaped, and
+// will always have slashes for path separators.
+SourceFile GetNinjaFileForTarget(const Target* target);
+
+// Returns the name of the root .ninja file for the given toolchain.
+SourceFile GetNinjaFileForToolchain(const Settings* settings);
+
+// Returns the prefix applied to the Ninja rules in a given toolchain so they
+// don't collide with rules from other toolchains.
+std::string GetNinjaRulePrefixForToolchain(const Settings* settings);
+
+#endif // TOOLS_GN_NINJA_UTILS_H_
diff --git a/gn/tools/gn/ninja_writer.cc b/gn/tools/gn/ninja_writer.cc
new file mode 100644
index 00000000000..c6d65ac2945
--- /dev/null
+++ b/gn/tools/gn/ninja_writer.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 "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/ninja_writer.h b/gn/tools/gn/ninja_writer.h
new file mode 100644
index 00000000000..b9b93a4ef2c
--- /dev/null
+++ b/gn/tools/gn/ninja_writer.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 The Chromium Authors. 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_WRITER_H_
+#define TOOLS_GN_NINJA_WRITER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+class Builder;
+class BuildSettings;
+class Err;
+class Target;
+class Toolchain;
+
+class NinjaWriter {
+ public:
+ // Combines a target and the computed build rule for it.
+ using TargetRulePair = std::pair<const Target*, std::string>;
+
+ // Associates the build rules with each toolchain.
+ using PerToolchainRules =
+ std::map<const Toolchain*, std::vector<TargetRulePair>>;
+
+ // On failure will populate |err| and will return false. The map contains
+ // the per-toolchain set of rules collected to write to the toolchain build
+ // files.
+ static bool RunAndWriteFiles(const BuildSettings* build_settings,
+ const Builder& builder,
+ const PerToolchainRules& per_toolchain_rules,
+ Err* err);
+
+ private:
+ NinjaWriter(const Builder& builder);
+ ~NinjaWriter();
+
+ bool WriteToolchains(const PerToolchainRules& per_toolchain_rules, Err* err);
+
+ const Builder& builder_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaWriter);
+};
+
+#endif // TOOLS_GN_NINJA_WRITER_H_
diff --git a/gn/tools/gn/operators.cc b/gn/tools/gn/operators.cc
new file mode 100644
index 00000000000..b5227f4666a
--- /dev/null
+++ b/gn/tools/gn/operators.cc
@@ -0,0 +1,784 @@
+// Copyright (c) 2013 The Chromium Authors. 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: {
+ 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;
+
+ default:
+ 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) {
+ 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.h b/gn/tools/gn/operators.h
new file mode 100644
index 00000000000..82ff68e7578
--- /dev/null
+++ b/gn/tools/gn/operators.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2013 The Chromium Authors. 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_OPERATORS_H_
+#define TOOLS_GN_OPERATORS_H_
+
+class BinaryOpNode;
+class Err;
+class ParseNode;
+class Scope;
+class UnaryOpNode;
+class Value;
+
+Value ExecuteUnaryOperator(Scope* scope,
+ const UnaryOpNode* op_node,
+ const Value& value,
+ Err* err);
+Value ExecuteBinaryOperator(Scope* scope,
+ const BinaryOpNode* op_node,
+ const ParseNode* left,
+ const ParseNode* right,
+ Err* err);
+
+#endif // TOOLS_GN_OPERATORS_H_
diff --git a/gn/tools/gn/operators_unittest.cc b/gn/tools/gn/operators_unittest.cc
new file mode 100644
index 00000000000..7e27fad9bda
--- /dev/null
+++ b/gn/tools/gn/operators_unittest.cc
@@ -0,0 +1,383 @@
+// Copyright (c) 2013 The Chromium Authors. 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);
+ }
+ void Print(std::ostream& out, int indent) const override {}
+
+ 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, 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/tools/gn/ordered_set.h
new file mode 100644
index 00000000000..5081112884b
--- /dev/null
+++ b/gn/tools/gn/ordered_set.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_ORDERED_SET_H_
+#define TOOLS_GN_ORDERED_SET_H_
+
+#include <stddef.h>
+
+#include <set>
+
+// An ordered set of items. Only appending is supported. Iteration is designed
+// to be by index.
+template <typename T>
+class OrderedSet {
+ private:
+ typedef std::set<T> set_type;
+ typedef typename set_type::const_iterator set_iterator;
+ typedef std::vector<set_iterator> vector_type;
+
+ public:
+ static const size_t npos = static_cast<size_t>(-1);
+
+ OrderedSet() {}
+ ~OrderedSet() {}
+
+ const T& operator[](size_t index) const { return *ordering_[index]; }
+ size_t size() const { return ordering_.size(); }
+ bool empty() const { return ordering_.empty(); }
+
+ bool has_item(const T& t) const { return set_.find(t) != set_.end(); }
+
+ // Returns true if the item was inserted. False if it was already in the
+ // set.
+ bool push_back(const T& t) {
+ std::pair<set_iterator, bool> result = set_.insert(t);
+ if (result.second)
+ ordering_.push_back(result.first);
+ return true;
+ }
+
+ // Appends a range of items, skipping ones that already exist.
+ template <class InputIterator>
+ void append(const InputIterator& insert_begin,
+ const InputIterator& insert_end) {
+ for (InputIterator i = insert_begin; i != insert_end; ++i) {
+ const T& t = *i;
+ push_back(t);
+ }
+ }
+
+ // Appends all items from the given other set.
+ void append(const OrderedSet<T>& other) {
+ for (size_t i = 0; i < other.size(); i++)
+ push_back(other[i]);
+ }
+
+ private:
+ set_type set_;
+ vector_type ordering_;
+};
+
+#endif // TOOLS_GN_ORDERED_SET_H_
diff --git a/gn/tools/gn/output_conversion.cc b/gn/tools/gn/output_conversion.cc
new file mode 100644
index 00000000000..70c21fa5f88
--- /dev/null
+++ b/gn/tools/gn/output_conversion.cc
@@ -0,0 +1,174 @@
+// 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") {
+ 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
new file mode 100644
index 00000000000..127a6ca7ffb
--- /dev/null
+++ b/gn/tools/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 <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
new file mode 100644
index 00000000000..2e4146d9764
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..f92e0976b43
--- /dev/null
+++ b/gn/tools/gn/output_file.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/output_file.h"
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/source_file.h"
+
+OutputFile::OutputFile() : value_() {}
+
+OutputFile::OutputFile(std::string&& v) : value_(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())) {}
+
+OutputFile::~OutputFile() = default;
+
+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_);
+ NormalizePath(&path);
+ return SourceFile(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(path);
+}
diff --git a/gn/tools/gn/output_file.h b/gn/tools/gn/output_file.h
new file mode 100644
index 00000000000..a3a64e0d5d6
--- /dev/null
+++ b/gn/tools/gn/output_file.h
@@ -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.
+
+#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();
+ explicit OutputFile(std::string&& v);
+ explicit OutputFile(const std::string& v);
+ OutputFile(const BuildSettings* build_settings,
+ const SourceFile& source_file);
+ ~OutputFile();
+
+ 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
+
+inline void swap(OutputFile& lhs, OutputFile& rhs) {
+ lhs.value().swap(rhs.value());
+}
+
+#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
new file mode 100644
index 00000000000..bf3ad66ca63
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..db34e24b12d
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..20f272b6efd
--- /dev/null
+++ b/gn/tools/gn/parse_tree.cc
@@ -0,0 +1,873 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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"
+
+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());
+}
+
+std::string IndentFor(int value) {
+ return std::string(value, ' ');
+}
+
+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();
+}
+
+void ParseNode::PrintComments(std::ostream& out, int indent) const {
+ if (comments_) {
+ std::string ind = IndentFor(indent + 1);
+ for (const auto& token : comments_->before())
+ out << ind << "+BEFORE_COMMENT(\"" << token.value() << "\")\n";
+ for (const auto& token : comments_->suffix())
+ out << ind << "+SUFFIX_COMMENT(\"" << token.value() << "\")\n";
+ for (const auto& token : comments_->after())
+ out << ind << "+AFTER_COMMENT(\"" << token.value() << "\")\n";
+ }
+}
+
+// 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);
+}
+
+void AccessorNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "ACCESSOR\n";
+ PrintComments(out, indent);
+ out << IndentFor(indent + 1) << base_.value() << "\n";
+ if (index_)
+ index_->Print(out, indent + 1);
+ else if (member_)
+ member_->Print(out, indent + 1);
+}
+
+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;
+ }
+ 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) + ", 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);
+}
+
+void BinaryOpNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "BINARY(" << op_.value() << ")\n";
+ PrintComments(out, indent);
+ left_->Print(out, indent + 1);
+ right_->Print(out, indent + 1);
+}
+
+// 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);
+}
+
+void BlockNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "BLOCK\n";
+ PrintComments(out, indent);
+ for (const auto& statement : statements_)
+ statement->Print(out, indent + 1);
+ if (end_ && end_->comments())
+ end_->Print(out, indent + 1);
+}
+
+// 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);
+}
+
+void ConditionNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "CONDITION\n";
+ PrintComments(out, indent);
+ condition_->Print(out, indent + 1);
+ if_true_->Print(out, indent + 1);
+ if (if_false_)
+ if_false_->Print(out, indent + 1);
+}
+
+// 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);
+}
+
+void FunctionCallNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "FUNCTION(" << function_.value() << ")\n";
+ PrintComments(out, indent);
+ args_->Print(out, indent + 1);
+ if (block_)
+ block_->Print(out, indent + 1);
+}
+
+// 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);
+}
+
+void IdentifierNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "IDENTIFIER(" << value_.value() << ")\n";
+ PrintComments(out, indent);
+}
+
+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);
+}
+
+void ListNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "LIST" << (prefer_multiline_ ? " multiline" : "")
+ << "\n";
+ PrintComments(out, indent);
+ for (const auto& cur : contents_)
+ cur->Print(out, indent + 1);
+ if (end_ && end_->comments())
+ end_->Print(out, indent + 1);
+}
+
+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);
+}
+
+void LiteralNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "LITERAL(" << value_.value() << ")\n";
+ PrintComments(out, indent);
+}
+
+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);
+}
+
+void UnaryOpNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "UNARY(" << op_.value() << ")\n";
+ PrintComments(out, indent);
+ operand_->Print(out, indent + 1);
+}
+
+// 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);
+}
+
+void BlockCommentNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "BLOCK_COMMENT(" << comment_.value() << ")\n";
+ PrintComments(out, indent);
+}
+
+// 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);
+}
+
+void EndNode::Print(std::ostream& out, int indent) const {
+ out << IndentFor(indent) << "END(" << value_.value() << ")\n";
+ PrintComments(out, indent);
+}
diff --git a/gn/tools/gn/parse_tree.h b/gn/tools/gn/parse_tree.h
new file mode 100644
index 00000000000..415041eebca
--- /dev/null
+++ b/gn/tools/gn/parse_tree.h
@@ -0,0 +1,546 @@
+// Copyright 2014 The Chromium Authors. 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 "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;
+
+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;
+
+ // Prints a representation of this node to the given string, indenting
+ // by the given number of spaces.
+ virtual void Print(std::ostream& out, int indent) const = 0;
+
+ const Comments* comments() const { return comments_.get(); }
+ Comments* comments_mutable();
+ void PrintComments(std::ostream& out, int indent) const;
+
+ private:
+ 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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); }
+
+ 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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(); }
+
+ 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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;
+ void Print(std::ostream& out, int indent) 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
new file mode 100644
index 00000000000..e9f03dd3793
--- /dev/null
+++ b/gn/tools/gn/parse_tree_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2014 The Chromium Authors. 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
new file mode 100644
index 00000000000..df57c9ec7f8
--- /dev/null
+++ b/gn/tools/gn/parser.cc
@@ -0,0 +1,886 @@
+// Copyright (c) 2013 The Chromium Authors. 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 ]
+)*";
+
+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.
+//
+// Refs:
+// - 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();
+ }
+}
diff --git a/gn/tools/gn/parser.h b/gn/tools/gn/parser.h
new file mode 100644
index 00000000000..c1930b91f58
--- /dev/null
+++ b/gn/tools/gn/parser.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+ int precedence;
+};
+
+#endif // TOOLS_GN_PARSER_H_
diff --git a/gn/tools/gn/parser_unittest.cc b/gn/tools/gn/parser_unittest.cc
new file mode 100644
index 00000000000..e2bbf39cdfe
--- /dev/null
+++ b/gn/tools/gn/parser_unittest.cc
@@ -0,0 +1,735 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+ result->Print(collector, 0);
+
+ 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;
+ result->Print(collector, 0);
+
+ 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
new file mode 100644
index 00000000000..0f5799774c8
--- /dev/null
+++ b/gn/tools/gn/path_output.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..1d98a2c78e5
--- /dev/null
+++ b/gn/tools/gn/path_output.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_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
new file mode 100644
index 00000000000..8857fd9f9e8
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..aff2ad952e6
--- /dev/null
+++ b/gn/tools/gn/pattern.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..ef29c629668
--- /dev/null
+++ b/gn/tools/gn/pattern.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_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
new file mode 100644
index 00000000000..9e14ca2a28e
--- /dev/null
+++ b/gn/tools/gn/pattern_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..4b9629bbe7f
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..42a80789ec8
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..b43b7d68650
--- /dev/null
+++ b/gn/tools/gn/qt_creator_writer.cc
@@ -0,0 +1,176 @@
+// 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 "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);
+ }
+}
+
+void QtCreatorWriter::HandleTarget(const Target* target) {
+ 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)));
+ }
+
+ 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 ");
+ defines_.insert(define);
+ }
+ }
+}
+
+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
new file mode 100644
index 00000000000..cb374049f40
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..3477ce4d2ed
--- /dev/null
+++ b/gn/tools/gn/runtime_deps.cc
@@ -0,0 +1,314 @@
+// 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
new file mode 100644
index 00000000000..8592677b6dd
--- /dev/null
+++ b/gn/tools/gn/runtime_deps.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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
new file mode 100644
index 00000000000..1adfdd41744
--- /dev/null
+++ b/gn/tools/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 "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/scheduler.cc b/gn/tools/gn/scheduler.cc
new file mode 100644
index 00000000000..bca14724614
--- /dev/null
+++ b/gn/tools/gn/scheduler.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2013 The Chromium Authors. 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),
+ verbose_logging_(false),
+ pool_work_count_lock_(),
+ pool_work_count_cv_(),
+ worker_pool_(),
+ is_failed_(false),
+ suppress_output_for_testing_(false),
+ has_been_shutdown_(false) {
+ 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;
+}
+
+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
new file mode 100644
index 00000000000..b6f55329c4d
--- /dev/null
+++ b/gn/tools/gn/scheduler.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2013 The Chromium Authors. 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.
+ //
+ // 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;
+
+ // 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_;
+
+ 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_;
+
+ bool suppress_output_for_testing_;
+
+ // 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_;
+
+ // 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_;
+
+ 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
new file mode 100644
index 00000000000..236835629ec
--- /dev/null
+++ b/gn/tools/gn/scope.cc
@@ -0,0 +1,576 @@
+// Copyright (c) 2013 The Chromium Authors. 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::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
new file mode 100644
index 00000000000..5e9745a58b0
--- /dev/null
+++ b/gn/tools/gn/scope.h
@@ -0,0 +1,392 @@
+// Copyright (c) 2013 The Chromium Authors. 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:
+ typedef std::map<base::StringPiece, Value> KeyValueMap;
+ // Holds an owning list of Items.
+ typedef std::vector<std::unique_ptr<Item>> ItemVector;
+
+ // 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;
+
+ // 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
new file mode 100644
index 00000000000..d7d11afc707
--- /dev/null
+++ b/gn/tools/gn/scope_per_file_provider.cc
@@ -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.
+
+#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
new file mode 100644
index 00000000000..ac0d8720615
--- /dev/null
+++ b/gn/tools/gn/scope_per_file_provider.h
@@ -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.
+
+#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
new file mode 100644
index 00000000000..1a6ef1a3e56
--- /dev/null
+++ b/gn/tools/gn/scope_per_file_provider_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..681d6c88ca5
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..737e72a57d5
--- /dev/null
+++ b/gn/tools/gn/settings.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 "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),
+ import_manager_(),
+ base_config_(this),
+ greedy_target_generation_(false) {
+ 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/");
+}
+
+Settings::~Settings() = default;
diff --git a/gn/tools/gn/settings.h b/gn/tools/gn/settings.h
new file mode 100644
index 00000000000..f0a66915489
--- /dev/null
+++ b/gn/tools/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 "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);
+ ~Settings();
+
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(Settings);
+};
+
+#endif // TOOLS_GN_SETTINGS_H_
diff --git a/gn/tools/gn/setup.cc b/gn/tools/gn/setup.cc
new file mode 100644
index 00000000000..94f5d798bff
--- /dev/null
+++ b/gn/tools/gn/setup.cc
@@ -0,0 +1,823 @@
+// Copyright (c) 2013 The Chromium Authors. 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/sys_string_conversions.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.
+
+ 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");
+
+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)
+
+// 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(base::SysNativeMBToWide(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"),
+ check_public_headers_(false),
+ dotfile_settings_(&build_settings_, std::string()),
+ dotfile_scope_(&dotfile_settings_),
+ default_args_(nullptr),
+ fill_arguments_(true) {
+ 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());
+}
+
+Setup::~Setup() = default;
+
+bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
+ base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+
+ 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() {
+ RunPreMessageLoop();
+ if (!scheduler_.Run())
+ return false;
+ return RunPostMessageLoop();
+}
+
+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() {
+ Err err;
+ if (!builder_.CheckForBadItems(&err)) {
+ err.PrintToStdout();
+ return false;
+ }
+
+ if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kFailOnUnusedArgs)) {
+ err.PrintToStdout();
+ return false;
+ }
+ err.PrintNonfatalToStdout();
+ OutputString(
+ "\nThe build continued as if that argument was "
+ "unspecified.\n\n");
+ return true;
+ }
+
+ 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)) {
+ return false;
+ }
+ }
+
+ // Write out tracing and timing if requested.
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ 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 (cmdline.HasSwitch(switches::kArgs)) {
+ if (!FillArgsFromCommandLine(cmdline.GetSwitchValueASCII(switches::kArgs)))
+ 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, false, &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 dot_file_path =
+ cmdline.GetSwitchValuePath(switches::kDotfile);
+ if (dot_file_path.empty()) {
+ dotfile_name_ = root_path.Append(kGnFile);
+ } else {
+ dotfile_name_ = base::MakeAbsoluteFilePath(dot_file_path);
+ if (dotfile_name_.empty()) {
+ Err(Location(), "Could not load dotfile.",
+ "The file \"" + FilePathToUTF8(dot_file_path) +
+ "\" couldn't be loaded.")
+ .PrintToStdout();
+ return false;
+ }
+ }
+ } 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;
+ }
+
+ 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
new file mode 100644
index 00000000000..13d0da5f9dc
--- /dev/null
+++ b/gn/tools/gn/setup.h
@@ -0,0 +1,171 @@
+// Copyright (c) 2013 The Chromium Authors. 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 setup the build settings and environment for the various
+// commands to run.
+class Setup {
+ public:
+ Setup();
+ ~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).
+ bool DoSetup(const std::string& build_dir, bool force_create);
+
+ // Runs the load, returning true on success. On failure, prints the error
+ // and returns false. This includes both RunPreMessageLoop() and
+ // RunPostMessageLoop().
+ bool Run();
+
+ 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; }
+
+ // 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();
+
+ // 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_;
+
+ // 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_;
+
+ // Set to true when we should populate the build arguments from the command
+ // line or build argument file. See setter above.
+ bool fill_arguments_;
+
+ // 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/source_dir.cc b/gn/tools/gn/source_dir.cc
new file mode 100644
index 00000000000..0fd5c75a88d
--- /dev/null
+++ b/gn/tools/gn/source_dir.cc
@@ -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.
+
+#include "tools/gn/source_dir.h"
+
+#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() = default;
+
+SourceDir::SourceDir(const base::StringPiece& p) : value_(p.data(), p.size()) {
+ if (!EndsWithSlash(value_))
+ value_.push_back('/');
+ AssertValueSourceDirString(value_);
+}
+
+SourceDir::SourceDir(SwapIn, std::string* s) {
+ value_.swap(*s);
+ if (!EndsWithSlash(value_))
+ value_.push_back('/');
+ AssertValueSourceDirString(value_);
+}
+
+SourceDir::~SourceDir() = default;
+
+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.value_ = 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);
+}
+
+void SourceDir::SwapValue(std::string* v) {
+ value_.swap(*v);
+ AssertValueSourceDirString(value_);
+}
+
+// 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
new file mode 100644
index 00000000000..5c1621b542f
--- /dev/null
+++ b/gn/tools/gn/source_dir.h
@@ -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.
+
+#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:
+ enum SwapIn { SWAP_IN };
+
+ SourceDir();
+ explicit SourceDir(const base::StringPiece& p);
+ // Swaps the given string in without copies. The given string will be empty
+ // after this call.
+ SourceDir(SwapIn, std::string* s);
+ ~SourceDir();
+
+ // 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);
+ }
+
+ 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_; }
+
+ void swap(SourceDir& other) { value_.swap(other.value_); }
+
+ private:
+ friend class SourceFile;
+ std::string value_;
+
+ // Copy & assign supported.
+};
+
+namespace std {
+
+template <>
+struct hash<SourceDir> {
+ std::size_t operator()(const SourceDir& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+
+} // namespace std
+
+inline void swap(SourceDir& lhs, SourceDir& rhs) {
+ lhs.swap(rhs);
+}
+
+#endif // TOOLS_GN_SOURCE_DIR_H_
diff --git a/gn/tools/gn/source_dir_unittest.cc b/gn/tools/gn/source_dir_unittest.cc
new file mode 100644
index 00000000000..adf865bee90
--- /dev/null
+++ b/gn/tools/gn/source_dir_unittest.cc
@@ -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.
+
+#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
+}
diff --git a/gn/tools/gn/source_file.cc b/gn/tools/gn/source_file.cc
new file mode 100644
index 00000000000..79c6eb03913
--- /dev/null
+++ b/gn/tools/gn/source_file.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+}
+
+} // namespace
+
+SourceFile::SourceFile() = default;
+
+SourceFile::SourceFile(const base::StringPiece& p)
+ : value_(p.data(), p.size()) {
+ DCHECK(!value_.empty());
+ AssertValueSourceFileString(value_);
+ NormalizePath(&value_);
+}
+
+SourceFile::SourceFile(SwapIn, std::string* value) {
+ value_.swap(*value);
+ DCHECK(!value_.empty());
+ AssertValueSourceFileString(value_);
+ NormalizePath(&value_);
+}
+
+SourceFile::~SourceFile() = default;
+
+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(base::StringPiece(&value_[0], last_slash + 1));
+}
+
+base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const {
+ return ResolvePath(value_, true, source_root);
+}
diff --git a/gn/tools/gn/source_file.h b/gn/tools/gn/source_file.h
new file mode 100644
index 00000000000..f033987560b
--- /dev/null
+++ b/gn/tools/gn/source_file.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2013 The Chromium Authors. 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:
+ enum SwapIn { SWAP_IN };
+
+ SourceFile();
+
+ // Takes a known absolute source file. Always begins in a slash.
+ explicit SourceFile(const base::StringPiece& p);
+ SourceFile(const SourceFile& other) = default;
+
+ // Constructs from the given string by swapping in the contents of the given
+ // value. The value will be the empty string after this call.
+ SourceFile(SwapIn, std::string* value);
+
+ ~SourceFile();
+
+ bool is_null() const { return value_.empty(); }
+ const std::string& value() const { return value_; }
+
+ // 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_;
+ }
+
+ void swap(SourceFile& other) { value_.swap(other.value_); }
+
+ private:
+ friend class SourceDir;
+
+ std::string value_;
+
+ // Copy & assign supported.
+};
+
+namespace std {
+
+template <>
+struct hash<SourceFile> {
+ std::size_t operator()(const SourceFile& v) const {
+ hash<std::string> h;
+ return h(v.value());
+ }
+};
+
+} // namespace std
+
+inline void swap(SourceFile& lhs, SourceFile& rhs) {
+ lhs.swap(rhs);
+}
+
+#endif // TOOLS_GN_SOURCE_FILE_H_
diff --git a/gn/tools/gn/source_file_type.cc b/gn/tools/gn/source_file_type.cc
new file mode 100644
index 00000000000..48df7f84e6c
--- /dev/null
+++ b/gn/tools/gn/source_file_type.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/source_file_type.h"
+
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/source_file.h"
+
+SourceFileType GetSourceFileType(const SourceFile& file) {
+ base::StringPiece extension = FindExtension(&file.value());
+ if (extension == "cc" || extension == "cpp" || extension == "cxx")
+ return SOURCE_CPP;
+ if (extension == "h" || extension == "hpp" || extension == "hxx" ||
+ extension == "hh")
+ return SOURCE_H;
+ if (extension == "c")
+ return SOURCE_C;
+ if (extension == "m")
+ return SOURCE_M;
+ if (extension == "mm")
+ return SOURCE_MM;
+ if (extension == "rc")
+ return SOURCE_RC;
+ if (extension == "S" || extension == "s" || extension == "asm")
+ return SOURCE_S;
+ if (extension == "o" || extension == "obj")
+ return SOURCE_O;
+ if (extension == "def")
+ return SOURCE_DEF;
+
+ return SOURCE_UNKNOWN;
+}
diff --git a/gn/tools/gn/source_file_type.h b/gn/tools/gn/source_file_type.h
new file mode 100644
index 00000000000..c43b4324443
--- /dev/null
+++ b/gn/tools/gn/source_file_type.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. 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_TYPE_H_
+#define TOOLS_GN_SOURCE_FILE_TYPE_H_
+
+class SourceFile;
+
+// This should be sequential integers starting from 0 so they can be used as
+// array indices.
+enum SourceFileType {
+ 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,
+
+ // Must be last.
+ SOURCE_NUMTYPES,
+};
+
+SourceFileType GetSourceFileType(const SourceFile& file);
+
+#endif // TOOLS_GN_SOURCE_FILE_TYPE_H_
diff --git a/gn/tools/gn/source_file_unittest.cc b/gn/tools/gn/source_file_unittest.cc
new file mode 100644
index 00000000000..110707adea3
--- /dev/null
+++ b/gn/tools/gn/source_file_unittest.cc
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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(SourceFile::SwapIn(), &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
new file mode 100644
index 00000000000..6dd6a6a888c
--- /dev/null
+++ b/gn/tools/gn/standard_out.cc
@@ -0,0 +1,332 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+
+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) {
+ 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, "--", "\\--");
+ }
+ ::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) {
+ 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\e[1m");
+ 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, "--", "\\--");
+ }
+ 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) {
+ EnsureInitialized();
+
+ size_t colon_offset = line.find(':');
+ size_t first_normal = 0;
+ if (colon_offset != std::string::npos) {
+ if (is_markdown) {
+ OutputString(" * [" + line + "](#" + line.substr(0, colon_offset) +
+ ")\n");
+ } else {
+ OutputString(" " + line.substr(0, colon_offset), DECORATION_YELLOW);
+ first_normal = colon_offset;
+ }
+ } else if (is_markdown) {
+ OutputString(" * [" + line + "](" + line + ")\n");
+ }
+
+ if (is_markdown)
+ return;
+
+ // 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;
+ bool 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) {
+ std::string the_tag = tag;
+ if (the_tag.size() == 0) {
+ if (line.substr(0, 2) == "gn") {
+ the_tag = line.substr(3, line.substr(3).find(' '));
+ } else {
+ the_tag = line.substr(0, line.find(':'));
+ }
+ }
+ OutputString("### <a name=\"" + the_tag + "\"></a>", DECORATION_NONE);
+ 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/tools/gn/standard_out.h
new file mode 100644
index 00000000000..f2bb733b6bd
--- /dev/null
+++ b/gn/tools/gn/standard_out.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_STANDARD_OUT_H_
+#define TOOLS_GN_STANDARD_OUT_H_
+
+#include <string>
+
+enum TextDecoration {
+ DECORATION_NONE = 0,
+ DECORATION_DIM,
+ DECORATION_RED,
+ DECORATION_GREEN,
+ DECORATION_BLUE,
+ DECORATION_YELLOW
+};
+
+void OutputString(const std::string& output,
+ TextDecoration dec = DECORATION_NONE);
+
+// If printing markdown, this generates table-of-contents entries with
+// links to the actual help; otherwise, prints a one-line description.
+void PrintSectionHelp(const std::string& line,
+ const std::string& topic,
+ const std::string& tag);
+
+// Prints a line for a command, assuming there is a colon. Everything before
+// the colon is the command (and is highlighted). After the colon if there is
+// a square bracket, the contents of the bracket is dimmed.
+//
+// The line is indented 2 spaces.
+void PrintShortHelp(const std::string& line);
+
+// Rules:
+// - Lines beginning with non-whitespace are highlighted up to the first
+// colon (or the whole line if not).
+// - Lines whose first non-whitespace character is a # are dimmed.
+void PrintLongHelp(const std::string& text, const std::string& tag = "");
+
+#endif // TOOLS_GN_STANDARD_OUT_H_
diff --git a/gn/tools/gn/string_utils.cc b/gn/tools/gn/string_utils.cc
new file mode 100644
index 00000000000..89fc4b0c9fb
--- /dev/null
+++ b/gn/tools/gn/string_utils.cc
@@ -0,0 +1,347 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..744714a754a
--- /dev/null
+++ b/gn/tools/gn/string_utils.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_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
new file mode 100644
index 00000000000..bf10dd49603
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..b588dad84be
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..45123cbe502
--- /dev/null
+++ b/gn/tools/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 "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<SubstitutionType>& required_types() const {
+ return required_types_;
+ }
+
+ void FillRequiredTypes(SubstitutionBits* bits) const;
+
+ private:
+ std::vector<SubstitutionPattern> list_;
+
+ std::vector<SubstitutionType> required_types_;
+};
+
+#endif // TOOLS_GN_SUBSTITUTION_LIST_H_
diff --git a/gn/tools/gn/substitution_pattern.cc b/gn/tools/gn/substitution_pattern.cc
new file mode 100644
index 00000000000..9b5e815f940
--- /dev/null
+++ b/gn/tools/gn/substitution_pattern.cc
@@ -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.
+
+#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(SUBSTITUTION_LITERAL) {}
+
+SubstitutionPattern::Subrange::Subrange(SubstitutionType 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(SUBSTITUTION_LITERAL, str.substr(cur)));
+ break;
+ } else if (next > cur) {
+ ranges_.push_back(
+ Subrange(SUBSTITUTION_LITERAL, str.substr(cur, next - cur)));
+ }
+
+ // Find which specific pattern this corresponds to.
+ bool found_match = false;
+ for (size_t i = SUBSTITUTION_FIRST_PATTERN; i < SUBSTITUTION_NUM_TYPES;
+ i++) {
+ const char* cur_pattern = kSubstitutionNames[i];
+ size_t cur_len = strlen(cur_pattern);
+ if (str.compare(next, cur_len, cur_pattern) == 0) {
+ ranges_.push_back(Subrange(static_cast<SubstitutionType>(i)));
+ 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 == SUBSTITUTION_LITERAL)
+ result.append(elem.literal);
+ else
+ result.append(kSubstitutionNames[elem.type]);
+ }
+ return result;
+}
+
+void SubstitutionPattern::FillRequiredTypes(SubstitutionBits* bits) const {
+ for (const auto& elem : ranges_) {
+ if (elem.type != SUBSTITUTION_LITERAL)
+ bits->used[static_cast<size_t>(elem.type)] = true;
+ }
+}
+
+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 == SUBSTITUTION_LITERAL) {
+ // 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
new file mode 100644
index 00000000000..2f01e6bf277
--- /dev/null
+++ b/gn/tools/gn/substitution_pattern.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. 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(SubstitutionType t, const std::string& l = std::string());
+ ~Subrange();
+
+ inline bool operator==(const Subrange& other) const {
+ return type == other.type && literal == other.literal;
+ }
+
+ SubstitutionType 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. SUBSTITUTION_LITERAL 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 SUBSTITUTION_LITERAL.
+ const std::vector<SubstitutionType>& required_types() const {
+ return required_types_;
+ }
+
+ const std::vector<Subrange>& ranges() const { return ranges_; }
+ bool empty() const { return ranges_.empty(); }
+
+ private:
+ std::vector<Subrange> ranges_;
+ const ParseNode* origin_;
+
+ std::vector<SubstitutionType> 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
new file mode 100644
index 00000000000..c8c739670de
--- /dev/null
+++ b/gn/tools/gn/substitution_pattern_unittest.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 "tools/gn/substitution_pattern.h"
+#include "tools/gn/err.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(SUBSTITUTION_LITERAL, 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(SUBSTITUTION_LITERAL, pattern.ranges()[0].type);
+ EXPECT_EQ("AA", pattern.ranges()[0].literal);
+ EXPECT_EQ(SUBSTITUTION_SOURCE, pattern.ranges()[1].type);
+ EXPECT_EQ(SUBSTITUTION_SOURCE_NAME_PART, pattern.ranges()[2].type);
+ EXPECT_EQ(SUBSTITUTION_LITERAL, pattern.ranges()[3].type);
+ EXPECT_EQ("BB", pattern.ranges()[3].literal);
+ EXPECT_EQ(SUBSTITUTION_SOURCE_FILE_PART, 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());
+}
diff --git a/gn/tools/gn/substitution_type.cc b/gn/tools/gn/substitution_type.cc
new file mode 100644
index 00000000000..8c0fbb4c30c
--- /dev/null
+++ b/gn/tools/gn/substitution_type.cc
@@ -0,0 +1,248 @@
+// Copyright 2014 The Chromium Authors. 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/err.h"
+
+const char* kSubstitutionNames[SUBSTITUTION_NUM_TYPES] = {
+ "<<literal>>", // SUBSTITUTION_LITERAL
+
+ "{{source}}", // SUBSTITUTION_SOURCE
+ "{{output}}", // SUBSTITUTION_OUTPUT
+
+ "{{source_name_part}}", // SUBSTITUTION_NAME_PART
+ "{{source_file_part}}", // SUBSTITUTION_FILE_PART
+ "{{source_dir}}", // SUBSTITUTION_SOURCE_DIR
+ "{{source_root_relative_dir}}", // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR
+ "{{source_gen_dir}}", // SUBSTITUTION_SOURCE_GEN_DIR
+ "{{source_out_dir}}", // SUBSTITUTION_SOURCE_OUT_DIR
+ "{{source_target_relative}}", // SUBSTITUTION_SOURCE_TARGET_RELATIVE
+
+ "{{label}}", // SUBSTITUTION_LABEL
+ "{{label_name}}", // SUBSTITUTION_LABEL_NAME
+ "{{root_gen_dir}}", // SUBSTITUTION_ROOT_GEN_DIR
+ "{{root_out_dir}}", // SUBSTITUTION_ROOT_OUT_DIR
+ "{{target_gen_dir}}", // SUBSTITUTION_TARGET_GEN_DIR
+ "{{target_out_dir}}", // SUBSTITUTION_TARGET_OUT_DIR
+ "{{target_output_name}}", // SUBSTITUTION_TARGET_OUTPUT_NAME
+
+ "{{asmflags}}", // SUBSTITUTION_ASMFLAGS
+ "{{cflags}}", // SUBSTITUTION_CFLAGS
+ "{{cflags_c}}", // SUBSTITUTION_CFLAGS_C
+ "{{cflags_cc}}", // SUBSTITUTION_CFLAGS_CC
+ "{{cflags_objc}}", // SUBSTITUTION_CFLAGS_OBJC
+ "{{cflags_objcc}}", // SUBSTITUTION_CFLAGS_OBJCC
+ "{{defines}}", // SUBSTITUTION_DEFINES
+ "{{include_dirs}}", // SUBSTITUTION_INCLUDE_DIRS
+
+ "{{inputs}}", // SUBSTITUTION_LINKER_INPUTS
+ "{{inputs_newline}}", // SUBSTITUTION_LINKER_INPUTS_NEWLINE
+ "{{ldflags}}", // SUBSTITUTION_LDFLAGS
+ "{{libs}}", // SUBSTITUTION_LIBS
+ "{{output_dir}}", // SUBSTITUTION_OUTPUT_DIR
+ "{{output_extension}}", // SUBSTITUTION_OUTPUT_EXTENSION
+ "{{solibs}}", // SUBSTITUTION_SOLIBS
+
+ "{{arflags}}", // SUBSTITUTION_ARFLAGS
+
+ "{{bundle_root_dir}}", // SUBSTITUTION_BUNDLE_ROOT_DIR
+ "{{bundle_contents_dir}}", // SUBSTITUTION_BUNDLE_CONTENTS_DIR
+ "{{bundle_resources_dir}}", // SUBSTITUTION_BUNDLE_RESOURCES_DIR
+ "{{bundle_executable_dir}}", // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
+ "{{bundle_plugins_dir}}", // SUBSTITUTION_BUNDLE_PLUGINS_DIR
+ "{{bundle_product_type}}", // SUBSTITUTION_BUNDLE_PRODUCT_TYPE
+ "{{bundle_partial_info_plist}}", // SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST,
+
+ "{{response_file_name}}", // SUBSTITUTION_RSP_FILE_NAME
+};
+
+const char* kSubstitutionNinjaNames[SUBSTITUTION_NUM_TYPES] = {
+ nullptr, // SUBSTITUTION_LITERAL
+
+ "in", // SUBSTITUTION_SOURCE
+ "out", // SUBSTITUTION_OUTPUT
+
+ "source_name_part", // SUBSTITUTION_NAME_PART
+ "source_file_part", // SUBSTITUTION_FILE_PART
+ "source_dir", // SUBSTITUTION_SOURCE_DIR
+ "source_root_relative_dir", // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR
+ "source_gen_dir", // SUBSTITUTION_SOURCE_GEN_DIR
+ "source_out_dir", // SUBSTITUTION_SOURCE_OUT_DIR
+ "source_target_relative", // SUBSTITUTION_SOURCE_TARGET_RELATIVE
+
+ "label", // SUBSTITUTION_LABEL
+ "label_name", // SUBSTITUTION_LABEL_NAME
+ "root_gen_dir", // SUBSTITUTION_ROOT_GEN_DIR
+ "root_out_dir", // SUBSTITUTION_ROOT_OUT_DIR
+ "target_gen_dir", // SUBSTITUTION_TARGET_GEN_DIR
+ "target_out_dir", // SUBSTITUTION_TARGET_OUT_DIR
+ "target_output_name", // SUBSTITUTION_TARGET_OUTPUT_NAME
+
+ "asmflags", // SUBSTITUTION_ASMFLAGS
+ "cflags", // SUBSTITUTION_CFLAGS
+ "cflags_c", // SUBSTITUTION_CFLAGS_C
+ "cflags_cc", // SUBSTITUTION_CFLAGS_CC
+ "cflags_objc", // SUBSTITUTION_CFLAGS_OBJC
+ "cflags_objcc", // SUBSTITUTION_CFLAGS_OBJCC
+ "defines", // SUBSTITUTION_DEFINES
+ "include_dirs", // SUBSTITUTION_INCLUDE_DIRS
+
+ // LINKER_INPUTS expands to the same Ninja var as SUBSTITUTION_SOURCE. These
+ // are used in different contexts and are named differently to keep things
+ // clear, but they both expand to the "set of input files" for a build rule.
+ "in", // SUBSTITUTION_LINKER_INPUTS
+ "in_newline", // SUBSTITUTION_LINKER_INPUTS_NEWLINE
+ "ldflags", // SUBSTITUTION_LDFLAGS
+ "libs", // SUBSTITUTION_LIBS
+ "output_dir", // SUBSTITUTION_OUTPUT_DIR
+ "output_extension", // SUBSTITUTION_OUTPUT_EXTENSION
+ "solibs", // SUBSTITUTION_SOLIBS
+
+ "arflags", // SUBSTITUTION_ARFLAGS
+
+ "bundle_root_dir", // SUBSTITUTION_BUNDLE_ROOT_DIR
+ "bundle_contents_dir", // SUBSTITUTION_BUNDLE_CONTENTS_DIR
+ "bundle_resources_dir", // SUBSTITUTION_BUNDLE_RESOURCES_DIR
+ "bundle_executable_dir", // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
+ "bundle_plugins_dir", // SUBSTITUTION_BUNDLE_PLUGINS_DIR
+ "product_type", // SUBSTITUTION_BUNDLE_PRODUCT_TYPE
+ "partial_info_plist", // SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST
+
+ "rspfile", // SUBSTITUTION_RSP_FILE_NAME
+};
+
+SubstitutionBits::SubstitutionBits() : used() {}
+
+void SubstitutionBits::MergeFrom(const SubstitutionBits& other) {
+ for (size_t i = 0; i < SUBSTITUTION_NUM_TYPES; i++)
+ used[i] |= other.used[i];
+}
+
+void SubstitutionBits::FillVector(std::vector<SubstitutionType>* vect) const {
+ for (size_t i = SUBSTITUTION_FIRST_PATTERN; i < SUBSTITUTION_NUM_TYPES; i++) {
+ if (used[i])
+ vect->push_back(static_cast<SubstitutionType>(i));
+ }
+}
+
+bool SubstitutionIsInOutputDir(SubstitutionType type) {
+ return type == SUBSTITUTION_SOURCE_GEN_DIR ||
+ type == SUBSTITUTION_SOURCE_OUT_DIR ||
+ type == SUBSTITUTION_ROOT_GEN_DIR ||
+ type == SUBSTITUTION_ROOT_OUT_DIR ||
+ type == SUBSTITUTION_TARGET_GEN_DIR ||
+ type == SUBSTITUTION_TARGET_OUT_DIR;
+}
+
+bool SubstitutionIsInBundleDir(SubstitutionType type) {
+ return type == SUBSTITUTION_BUNDLE_ROOT_DIR ||
+ type == SUBSTITUTION_BUNDLE_CONTENTS_DIR ||
+ type == SUBSTITUTION_BUNDLE_RESOURCES_DIR ||
+ type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR ||
+ type == SUBSTITUTION_BUNDLE_PLUGINS_DIR;
+}
+
+bool IsValidBundleDataSubstitution(SubstitutionType type) {
+ return type == SUBSTITUTION_LITERAL ||
+ type == SUBSTITUTION_SOURCE_NAME_PART ||
+ type == SUBSTITUTION_SOURCE_FILE_PART ||
+ type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR ||
+ type == SUBSTITUTION_BUNDLE_ROOT_DIR ||
+ type == SUBSTITUTION_BUNDLE_CONTENTS_DIR ||
+ type == SUBSTITUTION_BUNDLE_RESOURCES_DIR ||
+ type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR ||
+ type == SUBSTITUTION_BUNDLE_PLUGINS_DIR;
+}
+
+bool IsValidSourceSubstitution(SubstitutionType type) {
+ return type == SUBSTITUTION_LITERAL || type == SUBSTITUTION_SOURCE ||
+ type == SUBSTITUTION_SOURCE_NAME_PART ||
+ type == SUBSTITUTION_SOURCE_FILE_PART ||
+ type == SUBSTITUTION_SOURCE_DIR ||
+ type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR ||
+ type == SUBSTITUTION_SOURCE_GEN_DIR ||
+ type == SUBSTITUTION_SOURCE_OUT_DIR ||
+ type == SUBSTITUTION_SOURCE_TARGET_RELATIVE;
+}
+
+bool IsValidScriptArgsSubstitution(SubstitutionType type) {
+ return IsValidSourceSubstitution(type) || type == SUBSTITUTION_RSP_FILE_NAME;
+}
+
+bool IsValidToolSubstitution(SubstitutionType type) {
+ return type == SUBSTITUTION_LITERAL || type == SUBSTITUTION_OUTPUT ||
+ type == SUBSTITUTION_LABEL || type == SUBSTITUTION_LABEL_NAME ||
+ type == SUBSTITUTION_ROOT_GEN_DIR ||
+ type == SUBSTITUTION_ROOT_OUT_DIR ||
+ type == SUBSTITUTION_TARGET_GEN_DIR ||
+ type == SUBSTITUTION_TARGET_OUT_DIR ||
+ type == SUBSTITUTION_TARGET_OUTPUT_NAME;
+}
+
+bool IsValidCompilerSubstitution(SubstitutionType type) {
+ return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
+ type == SUBSTITUTION_SOURCE || type == SUBSTITUTION_ASMFLAGS ||
+ type == SUBSTITUTION_CFLAGS || type == SUBSTITUTION_CFLAGS_C ||
+ type == SUBSTITUTION_CFLAGS_CC || type == SUBSTITUTION_CFLAGS_OBJC ||
+ type == SUBSTITUTION_CFLAGS_OBJCC || type == SUBSTITUTION_DEFINES ||
+ type == SUBSTITUTION_INCLUDE_DIRS;
+}
+
+bool IsValidCompilerOutputsSubstitution(SubstitutionType type) {
+ // All tool types except "output" (which would be infinitely recursive).
+ return (IsValidToolSubstitution(type) && type != SUBSTITUTION_OUTPUT) ||
+ IsValidSourceSubstitution(type);
+}
+
+bool IsValidLinkerSubstitution(SubstitutionType type) {
+ return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
+ type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
+ type == SUBSTITUTION_LDFLAGS || type == SUBSTITUTION_LIBS ||
+ type == SUBSTITUTION_OUTPUT_DIR ||
+ type == SUBSTITUTION_OUTPUT_EXTENSION || type == SUBSTITUTION_SOLIBS;
+}
+
+bool IsValidLinkerOutputsSubstitution(SubstitutionType type) {
+ // All valid compiler outputs plus the output extension.
+ return IsValidCompilerOutputsSubstitution(type) ||
+ type == SUBSTITUTION_OUTPUT_DIR ||
+ type == SUBSTITUTION_OUTPUT_EXTENSION;
+}
+
+bool IsValidALinkSubstitution(SubstitutionType type) {
+ return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
+ type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
+ type == SUBSTITUTION_ARFLAGS || type == SUBSTITUTION_OUTPUT_DIR ||
+ type == SUBSTITUTION_OUTPUT_EXTENSION;
+}
+
+bool IsValidCopySubstitution(SubstitutionType type) {
+ return IsValidToolSubstitution(type) || type == SUBSTITUTION_SOURCE;
+}
+
+bool IsValidCompileXCassetsSubstitution(SubstitutionType type) {
+ return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
+ type == SUBSTITUTION_BUNDLE_PRODUCT_TYPE ||
+ type == SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST;
+}
+
+bool EnsureValidSubstitutions(const std::vector<SubstitutionType>& types,
+ bool (*is_valid_subst)(SubstitutionType),
+ const ParseNode* origin,
+ Err* err) {
+ for (SubstitutionType type : types) {
+ if (!is_valid_subst(type)) {
+ *err = Err(origin, "Invalid substitution type.",
+ "The substitution " + std::string(kSubstitutionNames[type]) +
+ " 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
new file mode 100644
index 00000000000..7363e187246
--- /dev/null
+++ b/gn/tools/gn/substitution_type.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_SUBSTITUTION_TYPE_H_
+#define TOOLS_GN_SUBSTITUTION_TYPE_H_
+
+#include <vector>
+
+class Err;
+class ParseNode;
+
+// Keep kSubstitutionNames, kSubstitutionNinjaNames and the
+// IsValid*Substitution functions in sync if you change anything here.
+enum SubstitutionType {
+ SUBSTITUTION_LITERAL = 0,
+
+ // The index of the first pattern. To loop overal all patterns, go from here
+ // until NUM_TYPES.
+ SUBSTITUTION_FIRST_PATTERN,
+
+ // These map to Ninja's {in} and {out} variables.
+ SUBSTITUTION_SOURCE = SUBSTITUTION_FIRST_PATTERN, // {{source}}
+ SUBSTITUTION_OUTPUT, // {{output}}
+
+ // Valid for all compiler tools.
+ SUBSTITUTION_SOURCE_NAME_PART, // {{source_name_part}}
+ SUBSTITUTION_SOURCE_FILE_PART, // {{source_file_part}}
+ SUBSTITUTION_SOURCE_DIR, // {{source_dir}}
+ SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR, // {{root_relative_dir}}
+ SUBSTITUTION_SOURCE_GEN_DIR, // {{source_gen_dir}}
+ SUBSTITUTION_SOURCE_OUT_DIR, // {{source_out_dir}}
+ SUBSTITUTION_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.
+ SUBSTITUTION_LABEL, // {{label}}
+ SUBSTITUTION_LABEL_NAME, // {{label_name}}
+ SUBSTITUTION_ROOT_GEN_DIR, // {{root_gen_dir}}
+ SUBSTITUTION_ROOT_OUT_DIR, // {{root_out_dir}}
+ SUBSTITUTION_TARGET_GEN_DIR, // {{target_gen_dir}}
+ SUBSTITUTION_TARGET_OUT_DIR, // {{target_out_dir}}
+ SUBSTITUTION_TARGET_OUTPUT_NAME, // {{target_output_name}}
+
+ // Valid for compiler tools.
+ SUBSTITUTION_ASMFLAGS, // {{asmflags}}
+ SUBSTITUTION_CFLAGS, // {{cflags}}
+ SUBSTITUTION_CFLAGS_C, // {{cflags_c}}
+ SUBSTITUTION_CFLAGS_CC, // {{cflags_cc}}
+ SUBSTITUTION_CFLAGS_OBJC, // {{cflags_objc}}
+ SUBSTITUTION_CFLAGS_OBJCC, // {{cflags_objcc}}
+ SUBSTITUTION_DEFINES, // {{defines}}
+ SUBSTITUTION_INCLUDE_DIRS, // {{include_dirs}}
+
+ // Valid for linker tools.
+ SUBSTITUTION_LINKER_INPUTS, // {{inputs}}
+ SUBSTITUTION_LINKER_INPUTS_NEWLINE, // {{inputs_newline}}
+ SUBSTITUTION_LDFLAGS, // {{ldflags}}
+ SUBSTITUTION_LIBS, // {{libs}}
+ SUBSTITUTION_OUTPUT_DIR, // {{output_dir}}
+ SUBSTITUTION_OUTPUT_EXTENSION, // {{output_extension}}
+ SUBSTITUTION_SOLIBS, // {{solibs}}
+
+ // Valid for alink only.
+ SUBSTITUTION_ARFLAGS, // {{arflags}}
+
+ // Valid for bundle_data targets.
+ SUBSTITUTION_BUNDLE_ROOT_DIR, // {{bundle_root_dir}}
+ SUBSTITUTION_BUNDLE_CONTENTS_DIR, // {{bundle_contents_dir}}
+ SUBSTITUTION_BUNDLE_RESOURCES_DIR, // {{bundle_resources_dir}}
+ SUBSTITUTION_BUNDLE_EXECUTABLE_DIR, // {{bundle_executable_dir}}
+ SUBSTITUTION_BUNDLE_PLUGINS_DIR, // {{bundle_plugins_dir}}
+
+ // Valid for compile_xcassets tool.
+ SUBSTITUTION_BUNDLE_PRODUCT_TYPE, // {{bundle_product_type}}
+ SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST, // {{bundle_partial_info_plist}}
+
+ // Used only for the args of actions.
+ SUBSTITUTION_RSP_FILE_NAME, // {{response_file_name}}
+
+ SUBSTITUTION_NUM_TYPES // Must be last.
+};
+
+// An array of size SUBSTITUTION_NUM_TYPES that lists the names of the
+// substitution patterns, including the curly braces. So, for example,
+// kSubstitutionNames[SUBSTITUTION_SOURCE] == "{{source}}".
+extern const char* kSubstitutionNames[SUBSTITUTION_NUM_TYPES];
+
+// Ninja variables corresponding to each substitution. These do not include
+// the dollar sign.
+extern const char* kSubstitutionNinjaNames[SUBSTITUTION_NUM_TYPES];
+
+// 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 bitfield (with a true set for each required
+ // item) to a vector of the types listed. Does not include LITERAL.
+ void FillVector(std::vector<SubstitutionType>* vect) const;
+
+ bool used[SUBSTITUTION_NUM_TYPES];
+};
+
+// 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(SubstitutionType 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(SubstitutionType type);
+
+// Returns true if the given substitution is valid for the named purpose.
+bool IsValidBundleDataSubstitution(SubstitutionType type);
+bool IsValidSourceSubstitution(SubstitutionType type);
+bool IsValidScriptArgsSubstitution(SubstitutionType type);
+
+// Both compiler and linker tools.
+bool IsValidToolSubstitution(SubstitutionType type);
+bool IsValidCompilerSubstitution(SubstitutionType type);
+bool IsValidCompilerOutputsSubstitution(SubstitutionType type);
+bool IsValidLinkerSubstitution(SubstitutionType type);
+bool IsValidLinkerOutputsSubstitution(SubstitutionType type);
+bool IsValidALinkSubstitution(SubstitutionType type);
+bool IsValidCopySubstitution(SubstitutionType type);
+bool IsValidCompileXCassetsSubstitution(SubstitutionType 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<SubstitutionType>& types,
+ bool (*is_valid_subst)(SubstitutionType),
+ 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
new file mode 100644
index 00000000000..96a12b1db6c
--- /dev/null
+++ b/gn/tools/gn/substitution_writer.cc
@@ -0,0 +1,596 @@
+// Copyright 2014 The Chromium Authors. 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/escape.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/output_file.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 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 == SUBSTITUTION_LITERAL) {
+ result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
+ } else {
+ result.append("${");
+ result.append(kSubstitutionNinjaNames[range.type]);
+ 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 == SUBSTITUTION_LITERAL)
+ << "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(SourceFile::SWAP_IN, &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 == SUBSTITUTION_LITERAL) {
+ 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<SubstitutionType>& 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 != SUBSTITUTION_SOURCE && type != SUBSTITUTION_RSP_FILE_NAME) {
+ out << " " << kSubstitutionNinjaNames[type] << " = ";
+ 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,
+ SubstitutionType type,
+ OutputStyle output_style,
+ const SourceDir& relative_to) {
+ std::string to_rebase;
+ switch (type) {
+ case SUBSTITUTION_SOURCE:
+ if (source.is_system_absolute())
+ return source.value();
+ to_rebase = source.value();
+ break;
+
+ case SUBSTITUTION_SOURCE_NAME_PART:
+ return FindFilenameNoExtension(&source.value()).as_string();
+
+ case SUBSTITUTION_SOURCE_FILE_PART:
+ return source.GetName();
+
+ case SUBSTITUTION_SOURCE_DIR:
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ to_rebase = DirectoryWithNoLastSlash(source.GetDir());
+ break;
+
+ case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ return RebasePath(DirectoryWithNoLastSlash(source.GetDir()),
+ SourceDir("//"),
+ settings->build_settings()->root_path_utf8());
+
+ case SUBSTITUTION_SOURCE_GEN_DIR:
+ to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings), source.GetDir(), BuildDirType::GEN));
+ break;
+
+ case SUBSTITUTION_SOURCE_OUT_DIR:
+ to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ));
+ break;
+
+ case SUBSTITUTION_SOURCE_TARGET_RELATIVE:
+ if (target) {
+ return RebasePath(source.value(), target->label().dir(),
+ settings->build_settings()->root_path_utf8());
+ }
+ NOTREACHED() << "Cannot use substitution " << kSubstitutionNames[type]
+ << " without target";
+ return std::string();
+
+ default:
+ NOTREACHED() << "Unsupported substitution for this function: "
+ << kSubstitutionNames[type];
+ 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 == SUBSTITUTION_LITERAL) {
+ 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,
+ SubstitutionType type,
+ std::string* result) {
+ switch (type) {
+ case SUBSTITUTION_LABEL:
+ // Only include the toolchain for non-default toolchains.
+ *result =
+ target->label().GetUserVisibleName(!target->settings()->is_default());
+ break;
+ case SUBSTITUTION_LABEL_NAME:
+ *result = target->label().name();
+ break;
+ case SUBSTITUTION_ROOT_GEN_DIR:
+ SetDirOrDotWithNoSlash(
+ GetBuildDirAsOutputFile(BuildDirContext(target), BuildDirType::GEN)
+ .value(),
+ result);
+ break;
+ case SUBSTITUTION_ROOT_OUT_DIR:
+ SetDirOrDotWithNoSlash(
+ target->settings()->toolchain_output_subdir().value(), result);
+ break;
+ case SUBSTITUTION_TARGET_GEN_DIR:
+ SetDirOrDotWithNoSlash(
+ GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN).value(),
+ result);
+ break;
+ case SUBSTITUTION_TARGET_OUT_DIR:
+ SetDirOrDotWithNoSlash(
+ GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ).value(),
+ result);
+ break;
+ case SUBSTITUTION_TARGET_OUTPUT_NAME:
+ *result = target->GetComputedOutputName();
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+// static
+std::string SubstitutionWriter::GetTargetSubstitution(const Target* target,
+ SubstitutionType 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 == SUBSTITUTION_LITERAL) {
+ 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,
+ SubstitutionType 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 == SUBSTITUTION_LITERAL) {
+ 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,
+ SubstitutionType type) {
+ // First try the common tool ones.
+ std::string result;
+ if (GetTargetSubstitution(target, type, &result))
+ return result;
+
+ // Fall-through to the linker-specific ones.
+ switch (type) {
+ case SUBSTITUTION_OUTPUT_DIR:
+ // 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;
+
+ case SUBSTITUTION_OUTPUT_EXTENSION:
+ // 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();
+
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+}
diff --git a/gn/tools/gn/substitution_writer.h b/gn/tools/gn/substitution_writer.h
new file mode 100644
index 00000000000..d5cb1c31bd1
--- /dev/null
+++ b/gn/tools/gn/substitution_writer.h
@@ -0,0 +1,237 @@
+// Copyright 2014 The Chromium Authors. 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 <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<SubstitutionType>& 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,
+ SubstitutionType 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,
+ SubstitutionType type,
+ std::string* result);
+ static std::string GetTargetSubstitution(const Target* target,
+ SubstitutionType 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,
+ SubstitutionType 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,
+ SubstitutionType 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
new file mode 100644
index 00000000000..8093e4177e8
--- /dev/null
+++ b/gn/tools/gn/substitution_writer_unittest.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 The Chromium Authors. 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/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<SubstitutionType> types;
+ types.push_back(SUBSTITUTION_SOURCE);
+ types.push_back(SUBSTITUTION_SOURCE_NAME_PART);
+ types.push_back(SUBSTITUTION_SOURCE_DIR);
+
+ 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", SUBSTITUTION_SOURCE));
+ EXPECT_EQ("//foo/bar/baz.txt",
+ GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE));
+
+ EXPECT_EQ("baz",
+ GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_NAME_PART));
+ EXPECT_EQ("baz",
+ GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_NAME_PART));
+
+ EXPECT_EQ("baz.txt",
+ GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_FILE_PART));
+ EXPECT_EQ("baz.txt",
+ GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_FILE_PART));
+
+ EXPECT_EQ("../../foo/bar",
+ GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_DIR));
+ EXPECT_EQ("//foo/bar",
+ GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_DIR));
+
+ EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt",
+ SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR));
+ EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt",
+ SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR));
+
+ EXPECT_EQ("gen/foo/bar",
+ GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+ EXPECT_EQ("//out/Debug/gen/foo/bar",
+ GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+
+ EXPECT_EQ("obj/foo/bar",
+ GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+ EXPECT_EQ("//out/Debug/obj/foo/bar",
+ GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+
+ // Operations on an absolute path.
+ EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE));
+ EXPECT_EQ("/.", GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_DIR));
+ EXPECT_EQ("gen/ABS_PATH",
+ GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+ EXPECT_EQ("obj/ABS_PATH",
+ GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+#if defined(OS_WIN)
+ EXPECT_EQ("gen/ABS_PATH/C",
+ GetRelSubst("/C:/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+ EXPECT_EQ("obj/ABS_PATH/C",
+ GetRelSubst("/C:/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+#endif
+
+ EXPECT_EQ(".",
+ GetRelSubst("//baz.txt", SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR));
+
+ EXPECT_EQ("baz.txt", GetRelSubst("//foo/bar/baz.txt",
+ SUBSTITUTION_SOURCE_TARGET_RELATIVE));
+ EXPECT_EQ("baz.txt", GetAbsSubst("//foo/bar/baz.txt",
+ SUBSTITUTION_SOURCE_TARGET_RELATIVE));
+
+#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, SUBSTITUTION_LABEL, &result));
+ EXPECT_EQ("//foo/bar:baz", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, SUBSTITUTION_LABEL_NAME, &result));
+ EXPECT_EQ("baz", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, SUBSTITUTION_ROOT_GEN_DIR, &result));
+ EXPECT_EQ("gen", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, SUBSTITUTION_ROOT_OUT_DIR, &result));
+ EXPECT_EQ(".", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, SUBSTITUTION_TARGET_GEN_DIR, &result));
+ EXPECT_EQ("gen/foo/bar", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, SUBSTITUTION_TARGET_OUT_DIR, &result));
+ EXPECT_EQ("obj/foo/bar", result);
+
+ EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
+ &target, SUBSTITUTION_TARGET_OUTPUT_NAME, &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"),
+ SUBSTITUTION_SOURCE_NAME_PART));
+ EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetCompilerSubstitution(
+ &target, SourceFile("//foo/bar/file.txt"),
+ SUBSTITUTION_TARGET_GEN_DIR));
+}
+
+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, SUBSTITUTION_OUTPUT_EXTENSION));
+ EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetLinkerSubstitution(
+ &target, tool, SUBSTITUTION_TARGET_GEN_DIR));
+
+ // 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, SUBSTITUTION_OUTPUT_EXTENSION));
+ target.set_output_extension("");
+ EXPECT_EQ("", SubstitutionWriter::GetLinkerSubstitution(
+ &target, tool, SUBSTITUTION_OUTPUT_EXTENSION));
+
+ // 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.
+ Tool tool;
+ 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,
+ 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, 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,
+ output_name)
+ .value());
+}
diff --git a/gn/tools/gn/switches.cc b/gn/tools/gn/switches.cc
new file mode 100644
index 00000000000..3d77642016a
--- /dev/null
+++ b/gn/tools/gn/switches.cc
@@ -0,0 +1,280 @@
+// Copyright 2014 The Chromium Authors. 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.
+
+ Note that this interacts with "--root" in a possibly incorrect way.
+ It would be nice to test the edge cases and document or fix.
+)";
+
+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 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
new file mode 100644
index 00000000000..1dc98285ff3
--- /dev/null
+++ b/gn/tools/gn/switches.h
@@ -0,0 +1,106 @@
+// Copyright 2014 The Chromium Authors. 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 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
new file mode 100644
index 00000000000..fbbfd50a111
--- /dev/null
+++ b/gn/tools/gn/target.cc
@@ -0,0 +1,875 @@
+// Copyright (c) 2013 The Chromium Authors. 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/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/source_file_type.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()) {
+ Toolchain::ToolType tool_type;
+ if (!target->GetOutputFilesForSource(source, &tool_type, &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),
+ output_type_(UNKNOWN),
+ output_prefix_override_(false),
+ output_extension_set_(false),
+ all_headers_public_(true),
+ check_includes_(true),
+ complete_static_lib_(false),
+ testonly_(false),
+ toolchain_(nullptr) {}
+
+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;
+ 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;
+
+ FillOutputFiles();
+
+ 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);
+
+ 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;
+}
+
+bool Target::IsLinkable() const {
+ return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_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(),
+ Toolchain::ToolTypeToName(
+ toolchain->GetToolTypeForTargetFinalOutput(this))
+ .c_str()));
+ }
+ return false;
+}
+
+bool Target::GetOutputFilesForSource(const SourceFile& source,
+ Toolchain::ToolType* computed_tool_type,
+ std::vector<OutputFile>* outputs) const {
+ outputs->clear();
+ *computed_tool_type = Toolchain::TYPE_NONE;
+
+ SourceFileType file_type = GetSourceFileType(source);
+ if (file_type == SOURCE_UNKNOWN)
+ return false;
+ if (file_type == 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 = toolchain_->GetToolTypeForSourceType(file_type);
+ if (*computed_tool_type == Toolchain::TYPE_NONE)
+ 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)
+ 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);
+}
+
+void Target::FillOutputFiles() {
+ 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: {
+ // 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 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 (tool->link_output().empty() && tool->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 (!tool->link_output().empty()) {
+ link_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->link_output());
+ }
+ if (!tool->depend_output().empty()) {
+ dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->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_);
+ }
+ break;
+ case UNKNOWN:
+ default:
+ NOTREACHED();
+ }
+
+ // Count anything generated from bundle_data dependencies.
+ if (output_type_ == CREATE_BUNDLE)
+ bundle_data_.GetOutputFiles(settings(), &computed_outputs_);
+
+ // 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));
+}
+
+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);
+ // 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);
+ }
+}
diff --git a/gn/tools/gn/target.h b/gn/tools/gn/target.h
new file mode 100644
index 00000000000..8754b6184d7
--- /dev/null
+++ b/gn/tools/gn/target.h
@@ -0,0 +1,396 @@
+// Copyright (c) 2013 The Chromium Authors. 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/ordered_set.h"
+#include "tools/gn/output_file.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,
+ };
+
+ 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_; }
+
+ // 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;
+ }
+
+ 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;
+ }
+
+ // 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_; }
+
+ 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,
+ Toolchain::ToolType* 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.
+ void FillOutputFiles();
+
+ // 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_;
+ std::string output_name_;
+ bool output_prefix_override_;
+ SourceDir output_dir_;
+ std::string output_extension_;
+ bool output_extension_set_;
+
+ FileList sources_;
+ bool all_headers_public_;
+ FileList public_headers_;
+ bool check_includes_;
+ bool complete_static_lib_;
+ bool testonly_;
+ 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_;
+
+ // Toolchain used by this target. Null until target is resolved.
+ const Toolchain* toolchain_;
+
+ // 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_;
+
+ 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
new file mode 100644
index 00000000000..4bc693c0c4f
--- /dev/null
+++ b/gn/tools/gn/target_generator.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 "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/group_target_generator.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 (!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 {
+ *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().swap(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().swap(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::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::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 == SUBSTITUTION_LITERAL) {
+ // 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
new file mode 100644
index 00000000000..549f2cd14c7
--- /dev/null
+++ b/gn/tools/gn/target_generator.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2013 The Chromium Authors. 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;
+
+ bool FillSources();
+ bool FillPublic();
+ bool FillConfigs();
+ bool FillOutputs(bool allow_substitutions);
+ bool FillCheckIncludes();
+
+ // 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 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
new file mode 100644
index 00000000000..62dad5263a0
--- /dev/null
+++ b/gn/tools/gn/target_unittest.cc
@@ -0,0 +1,1094 @@
+// Copyright (c) 2013 The Chromium Authors. 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 = std::make_unique<Tool>();
+ 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(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+
+ 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 = std::make_unique<Tool>();
+ 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(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+
+ 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));
+
+ 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));
+
+ 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);
+}
diff --git a/gn/tools/gn/template.cc b/gn/tools/gn/template.cc
new file mode 100644
index 00000000000..2b40fb3c3b1
--- /dev/null
+++ b/gn/tools/gn/template.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 "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.h b/gn/tools/gn/template.h
new file mode 100644
index 00000000000..6386ba9f2ba
--- /dev/null
+++ b/gn/tools/gn/template.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. 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_TEMPLATE_H_
+#define TOOLS_GN_TEMPLATE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+
+class BlockNode;
+class Err;
+class FunctionCallNode;
+class LocationRange;
+class Scope;
+class Value;
+
+// Represents the information associated with a template() call in GN, which
+// includes a closure and the code to run when the template is invoked.
+//
+// This class is immutable so we can reference it from multiple threads without
+// locking. Normally, this will be associated with a .gni file and then a
+// reference will be taken by each .gn file that imports it. These files might
+// execute the template in parallel.
+class Template : public base::RefCountedThreadSafe<Template> {
+ public:
+ // Makes a new closure based on the given scope.
+ Template(const Scope* scope, const FunctionCallNode* def);
+
+ // Takes ownership of a previously-constructed closure.
+ Template(std::unique_ptr<Scope> closure, const FunctionCallNode* def);
+
+ // Invoke the template. The values correspond to the state of the code
+ // invoking the template. The template name needs to be supplied since the
+ // template object itself doesn't know what name the calling code is using
+ // to refer to it (this is used to set defaults).
+ Value Invoke(Scope* scope,
+ const FunctionCallNode* invocation,
+ const std::string& template_name,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) const;
+
+ // Returns the location range where this template was defined.
+ LocationRange GetDefinitionRange() const;
+
+ private:
+ friend class base::RefCountedThreadSafe<Template>;
+
+ Template();
+ ~Template();
+
+ // It's important that this Scope is const. A template can be referenced by
+ // the root BUILDCONFIG file and then duplicated to all threads. Therefore,
+ // this scope must be usable from multiple threads at the same time.
+ //
+ // When executing a template, a new scope will be created as a child of this
+ // one, which will reference it as mutable or not according to the mutability
+ // of this value.
+ std::unique_ptr<const Scope> closure_;
+
+ const FunctionCallNode* definition_;
+};
+
+#endif // TOOLS_GN_TEMPLATE_H_
diff --git a/gn/tools/gn/template_unittest.cc b/gn/tools/gn/template_unittest.cc
new file mode 100644
index 00000000000..a7a7aee4a9e
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..ce7a90c7110
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..f380bbc0549
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..7063acdebb5
--- /dev/null
+++ b/gn/tools/gn/test_with_scope.cc
@@ -0,0 +1,228 @@
+// Copyright (c) 2013 The Chromium Authors. 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 = std::make_unique<Tool>();
+ 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(Toolchain::TYPE_CC, std::move(cc_tool));
+
+ // CXX
+ std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+ 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"));
+ toolchain->SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+
+ // OBJC
+ std::unique_ptr<Tool> objc_tool = std::make_unique<Tool>();
+ 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(Toolchain::TYPE_OBJC, std::move(objc_tool));
+
+ // OBJC
+ std::unique_ptr<Tool> objcxx_tool = std::make_unique<Tool>();
+ 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(Toolchain::TYPE_OBJCXX, 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 = std::make_unique<Tool>();
+ SetCommandForTool("ar {{output}} {{source}}", alink_tool.get());
+ 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(Toolchain::TYPE_ALINK, std::move(alink_tool));
+
+ // SOLINK
+ std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+ SetCommandForTool(
+ "ld -shared -o {{target_output_name}}.so {{inputs}} "
+ "{{ldflags}} {{libs}}",
+ solink_tool.get());
+ 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(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+
+ // SOLINK_MODULE
+ std::unique_ptr<Tool> solink_module_tool = std::make_unique<Tool>();
+ SetCommandForTool(
+ "ld -bundle -o {{target_output_name}}.so {{inputs}} "
+ "{{ldflags}} {{libs}}",
+ solink_module_tool.get());
+ 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(Toolchain::TYPE_SOLINK_MODULE,
+ std::move(solink_module_tool));
+
+ // LINK
+ std::unique_ptr<Tool> link_tool = std::make_unique<Tool>();
+ SetCommandForTool(
+ "ld -o {{target_output_name}} {{source}} "
+ "{{ldflags}} {{libs}}",
+ link_tool.get());
+ 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(Toolchain::TYPE_LINK, std::move(link_tool));
+
+ // STAMP
+ std::unique_ptr<Tool> stamp_tool = std::make_unique<Tool>();
+ SetCommandForTool("touch {{output}}", stamp_tool.get());
+ toolchain->SetTool(Toolchain::TYPE_STAMP, std::move(stamp_tool));
+
+ // COPY
+ std::unique_ptr<Tool> copy_tool = std::make_unique<Tool>();
+ SetCommandForTool("cp {{source}} {{output}}", copy_tool.get());
+ toolchain->SetTool(Toolchain::TYPE_COPY, std::move(copy_tool));
+
+ // COPY_BUNDLE_DATA
+ std::unique_ptr<Tool> copy_bundle_data_tool = std::make_unique<Tool>();
+ SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get());
+ toolchain->SetTool(Toolchain::TYPE_COPY_BUNDLE_DATA,
+ std::move(copy_bundle_data_tool));
+
+ // COMPILE_XCASSETS
+ std::unique_ptr<Tool> compile_xcassets_tool = std::make_unique<Tool>();
+ SetCommandForTool("touch {{output}}", compile_xcassets_tool.get());
+ toolchain->SetTool(Toolchain::TYPE_COMPILE_XCASSETS,
+ std::move(compile_xcassets_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
new file mode 100644
index 00000000000..88532438213
--- /dev/null
+++ b/gn/tools/gn/test_with_scope.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2013 The Chromium Authors. 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/err.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parse_tree.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
new file mode 100644
index 00000000000..3c016fdb135
--- /dev/null
+++ b/gn/tools/gn/token.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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
new file mode 100644
index 00000000000..126201ca772
--- /dev/null
+++ b/gn/tools/gn/token.h
@@ -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.
+
+#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
new file mode 100644
index 00000000000..9625203dcd4
--- /dev/null
+++ b/gn/tools/gn/tokenizer.cc
@@ -0,0 +1,407 @@
+// Copyright (c) 2013 The Chromium Authors. 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),
+ cur_(0),
+ line_number_(1),
+ column_number_(1) {}
+
+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
new file mode 100644
index 00000000000..67590df55ef
--- /dev/null
+++ b/gn/tools/gn/tokenizer.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_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_; // Byte offset into input buffer.
+
+ int line_number_;
+ int column_number_;
+
+ 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
new file mode 100644
index 00000000000..bc6d4b18ef3
--- /dev/null
+++ b/gn/tools/gn/tokenizer_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..b066fba400c
--- /dev/null
+++ b/gn/tools/gn/tool.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/tool.h"
+
+Tool::Tool()
+ : defined_from_(nullptr),
+ depsformat_(DEPS_GCC),
+ precompiled_header_type_(PCH_NONE),
+ restat_(false),
+ complete_(false) {}
+
+Tool::~Tool() = default;
+
+void Tool::SetComplete() {
+ DCHECK(!complete_);
+ complete_ = true;
+
+ command_.FillRequiredTypes(&substitution_bits_);
+ depfile_.FillRequiredTypes(&substitution_bits_);
+ description_.FillRequiredTypes(&substitution_bits_);
+ outputs_.FillRequiredTypes(&substitution_bits_);
+ link_output_.FillRequiredTypes(&substitution_bits_);
+ depend_output_.FillRequiredTypes(&substitution_bits_);
+ rspfile_.FillRequiredTypes(&substitution_bits_);
+ rspfile_content_.FillRequiredTypes(&substitution_bits_);
+}
diff --git a/gn/tools/gn/tool.h b/gn/tools/gn/tool.h
new file mode 100644
index 00000000000..d2d0b7ceacb
--- /dev/null
+++ b/gn/tools/gn/tool.h
@@ -0,0 +1,203 @@
+// Copyright 2014 The Chromium Authors. 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/substitution_list.h"
+#include "tools/gn/substitution_pattern.h"
+
+class ParseNode;
+class Pool;
+
+class Tool {
+ public:
+ enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
+
+ enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
+
+ Tool();
+ ~Tool();
+
+ const ParseNode* defined_from() const { return defined_from_; }
+ void set_defined_from(const ParseNode* df) { defined_from_ = df; }
+
+ // 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);
+ }
+
+ // 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);
+ }
+
+ 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) {
+ precompiled_header_type_ = pch_type;
+ }
+
+ const SubstitutionPattern& description() const { return description_; }
+ void set_description(SubstitutionPattern desc) {
+ DCHECK(!complete_);
+ description_ = std::move(desc);
+ }
+
+ 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 SubstitutionList& outputs() const { return outputs_; }
+ void set_outputs(SubstitutionList out) {
+ DCHECK(!complete_);
+ outputs_ = std::move(out);
+ }
+
+ // 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);
+ }
+
+ 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 ----------------------------------------------------------
+
+ // Called when the toolchain is saving this tool, after everything is filled
+ // in.
+ void SetComplete();
+
+ // 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();
+ }
+
+ // Substitutions required by this tool.
+ const SubstitutionBits& substitution_bits() const {
+ DCHECK(complete_);
+ return substitution_bits_;
+ }
+
+ bool OnResolved(Err* err);
+
+ private:
+ const ParseNode* defined_from_;
+
+ SubstitutionPattern command_;
+ std::string default_output_extension_;
+ SubstitutionPattern default_output_dir_;
+ SubstitutionPattern depfile_;
+ DepsFormat depsformat_;
+ PrecompiledHeaderType precompiled_header_type_;
+ SubstitutionPattern description_;
+ std::string lib_switch_;
+ std::string lib_dir_switch_;
+ SubstitutionList outputs_;
+ SubstitutionPattern link_output_;
+ SubstitutionPattern depend_output_;
+ SubstitutionList runtime_outputs_;
+ std::string output_prefix_;
+ bool restat_;
+ SubstitutionPattern rspfile_;
+ SubstitutionPattern rspfile_content_;
+ LabelPtrPair<Pool> pool_;
+
+ bool complete_;
+
+ 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
new file mode 100644
index 00000000000..e34acb5228c
--- /dev/null
+++ b/gn/tools/gn/toolchain.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2013 The Chromium Authors. 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"
+
+const char* Toolchain::kToolCc = "cc";
+const char* Toolchain::kToolCxx = "cxx";
+const char* Toolchain::kToolObjC = "objc";
+const char* Toolchain::kToolObjCxx = "objcxx";
+const char* Toolchain::kToolRc = "rc";
+const char* Toolchain::kToolAsm = "asm";
+const char* Toolchain::kToolAlink = "alink";
+const char* Toolchain::kToolSolink = "solink";
+const char* Toolchain::kToolSolinkModule = "solink_module";
+const char* Toolchain::kToolLink = "link";
+const char* Toolchain::kToolStamp = "stamp";
+const char* Toolchain::kToolCopy = "copy";
+const char* Toolchain::kToolCopyBundleData = "copy_bundle_data";
+const char* Toolchain::kToolCompileXCAssets = "compile_xcassets";
+const char* Toolchain::kToolAction = "action";
+
+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;
+}
+
+// static
+Toolchain::ToolType Toolchain::ToolNameToType(const base::StringPiece& str) {
+ if (str == kToolCc)
+ return TYPE_CC;
+ if (str == kToolCxx)
+ return TYPE_CXX;
+ if (str == kToolObjC)
+ return TYPE_OBJC;
+ if (str == kToolObjCxx)
+ return TYPE_OBJCXX;
+ if (str == kToolRc)
+ return TYPE_RC;
+ if (str == kToolAsm)
+ return TYPE_ASM;
+ if (str == kToolAlink)
+ return TYPE_ALINK;
+ if (str == kToolSolink)
+ return TYPE_SOLINK;
+ if (str == kToolSolinkModule)
+ return TYPE_SOLINK_MODULE;
+ if (str == kToolLink)
+ return TYPE_LINK;
+ if (str == kToolStamp)
+ return TYPE_STAMP;
+ if (str == kToolCopy)
+ return TYPE_COPY;
+ if (str == kToolCopyBundleData)
+ return TYPE_COPY_BUNDLE_DATA;
+ if (str == kToolCompileXCAssets)
+ return TYPE_COMPILE_XCASSETS;
+ if (str == kToolAction)
+ return TYPE_ACTION;
+ return TYPE_NONE;
+}
+
+// static
+std::string Toolchain::ToolTypeToName(ToolType type) {
+ switch (type) {
+ case TYPE_CC:
+ return kToolCc;
+ case TYPE_CXX:
+ return kToolCxx;
+ case TYPE_OBJC:
+ return kToolObjC;
+ case TYPE_OBJCXX:
+ return kToolObjCxx;
+ case TYPE_RC:
+ return kToolRc;
+ case TYPE_ASM:
+ return kToolAsm;
+ case TYPE_ALINK:
+ return kToolAlink;
+ case TYPE_SOLINK:
+ return kToolSolink;
+ case TYPE_SOLINK_MODULE:
+ return kToolSolinkModule;
+ case TYPE_LINK:
+ return kToolLink;
+ case TYPE_STAMP:
+ return kToolStamp;
+ case TYPE_COPY:
+ return kToolCopy;
+ case TYPE_COPY_BUNDLE_DATA:
+ return kToolCopyBundleData;
+ case TYPE_COMPILE_XCASSETS:
+ return kToolCompileXCAssets;
+ case TYPE_ACTION:
+ return kToolAction;
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+}
+
+Tool* Toolchain::GetTool(ToolType type) {
+ DCHECK(type != TYPE_NONE);
+ return tools_[static_cast<size_t>(type)].get();
+}
+
+const Tool* Toolchain::GetTool(ToolType type) const {
+ DCHECK(type != TYPE_NONE);
+ return tools_[static_cast<size_t>(type)].get();
+}
+
+void Toolchain::SetTool(ToolType type, std::unique_ptr<Tool> t) {
+ DCHECK(type != TYPE_NONE);
+ DCHECK(!tools_[type].get());
+ t->SetComplete();
+ tools_[type] = std::move(t);
+}
+
+void Toolchain::ToolchainSetupComplete() {
+ // Collect required bits from all tools.
+ for (const auto& tool : tools_) {
+ if (tool)
+ substitution_bits_.MergeFrom(tool->substitution_bits());
+ }
+
+ setup_complete_ = true;
+}
+
+// static
+Toolchain::ToolType Toolchain::GetToolTypeForSourceType(SourceFileType type) {
+ switch (type) {
+ case SOURCE_C:
+ return TYPE_CC;
+ case SOURCE_CPP:
+ return TYPE_CXX;
+ case SOURCE_M:
+ return TYPE_OBJC;
+ case SOURCE_MM:
+ return TYPE_OBJCXX;
+ case SOURCE_ASM:
+ case SOURCE_S:
+ return TYPE_ASM;
+ case SOURCE_RC:
+ return TYPE_RC;
+ case SOURCE_UNKNOWN:
+ case SOURCE_H:
+ case SOURCE_O:
+ case SOURCE_DEF:
+ return TYPE_NONE;
+ default:
+ NOTREACHED();
+ return TYPE_NONE;
+ }
+}
+
+const Tool* Toolchain::GetToolForSourceType(SourceFileType type) {
+ return tools_[GetToolTypeForSourceType(type)].get();
+}
+
+// static
+Toolchain::ToolType Toolchain::GetToolTypeForTargetFinalOutput(
+ const Target* target) {
+ // The contents of this list might be suprising (i.e. stamp tool for copy
+ // rules). See the header for why.
+ switch (target->output_type()) {
+ case Target::GROUP:
+ return TYPE_STAMP;
+ case Target::EXECUTABLE:
+ return Toolchain::TYPE_LINK;
+ case Target::SHARED_LIBRARY:
+ return Toolchain::TYPE_SOLINK;
+ case Target::LOADABLE_MODULE:
+ return Toolchain::TYPE_SOLINK_MODULE;
+ case Target::STATIC_LIBRARY:
+ return Toolchain::TYPE_ALINK;
+ case Target::SOURCE_SET:
+ return TYPE_STAMP;
+ case Target::ACTION:
+ case Target::ACTION_FOREACH:
+ case Target::BUNDLE_DATA:
+ case Target::CREATE_BUNDLE:
+ case Target::COPY_FILES:
+ return TYPE_STAMP;
+ default:
+ NOTREACHED();
+ return Toolchain::TYPE_NONE;
+ }
+}
+
+const Tool* Toolchain::GetToolForTargetFinalOutput(const Target* target) const {
+ return tools_[GetToolTypeForTargetFinalOutput(target)].get();
+}
diff --git a/gn/tools/gn/toolchain.h b/gn/tools/gn/toolchain.h
new file mode 100644
index 00000000000..dd8637cf5ea
--- /dev/null
+++ b/gn/tools/gn/toolchain.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_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/source_file_type.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:
+ enum ToolType {
+ TYPE_NONE = 0,
+ TYPE_CC,
+ TYPE_CXX,
+ TYPE_OBJC,
+ TYPE_OBJCXX,
+ TYPE_RC,
+ TYPE_ASM,
+ TYPE_ALINK,
+ TYPE_SOLINK,
+ TYPE_SOLINK_MODULE,
+ TYPE_LINK,
+ TYPE_STAMP,
+ TYPE_COPY,
+ TYPE_COPY_BUNDLE_DATA,
+ TYPE_COMPILE_XCASSETS,
+ TYPE_ACTION,
+
+ TYPE_NUMTYPES // Must be last.
+ };
+
+ static const char* kToolCc;
+ static const char* kToolCxx;
+ static const char* kToolObjC;
+ static const char* kToolObjCxx;
+ static const char* kToolRc;
+ static const char* kToolAsm;
+ static const char* kToolAlink;
+ static const char* kToolSolink;
+ static const char* kToolSolinkModule;
+ static const char* kToolLink;
+ static const char* kToolStamp;
+ static const char* kToolCopy;
+ static const char* kToolCopyBundleData;
+ static const char* kToolCompileXCAssets;
+ static const char* kToolAction;
+
+ // 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 TYPE_NONE on failure.
+ static ToolType ToolNameToType(const base::StringPiece& str);
+ static std::string ToolTypeToName(ToolType type);
+
+ // Returns null if the tool hasn't been defined.
+ Tool* GetTool(ToolType type);
+ const Tool* GetTool(ToolType type) const;
+
+ // Set a tool. When all tools are configured, you should call
+ // ToolchainSetupComplete().
+ void SetTool(ToolType type, 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.
+ static ToolType GetToolTypeForSourceType(SourceFileType type);
+ const Tool* GetToolForSourceType(SourceFileType type);
+
+ // 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.
+ static ToolType GetToolTypeForTargetFinalOutput(const Target* target);
+ const Tool* GetToolForTargetFinalOutput(const Target* target) const;
+
+ const SubstitutionBits& substitution_bits() const {
+ DCHECK(setup_complete_);
+ return substitution_bits_;
+ }
+
+ private:
+ std::unique_ptr<Tool> tools_[TYPE_NUMTYPES];
+
+ 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
new file mode 100644
index 00000000000..184c4505a8f
--- /dev/null
+++ b/gn/tools/gn/trace.cc
@@ -0,0 +1,332 @@
+// Copyright (c) 2013 The Chromium Authors. 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 {
+
+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();
+ 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/tools/gn/trace.h
new file mode 100644
index 00000000000..f4f06dfa279
--- /dev/null
+++ b/gn/tools/gn/trace.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 The Chromium Authors. 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_TRACE_H_
+#define TOOLS_GN_TRACE_H_
+
+#include <string>
+#include <thread>
+
+#include "base/macros.h"
+#include "util/ticks.h"
+
+class Label;
+
+namespace base {
+class CommandLine;
+class FilePath;
+} // namespace base
+
+class TraceItem {
+ public:
+ enum Type {
+ TRACE_SETUP,
+ TRACE_FILE_LOAD,
+ TRACE_FILE_PARSE,
+ TRACE_FILE_EXECUTE,
+ TRACE_FILE_WRITE,
+ TRACE_IMPORT_LOAD,
+ TRACE_IMPORT_BLOCK,
+ TRACE_SCRIPT_EXECUTE,
+ TRACE_DEFINE_TARGET,
+ TRACE_ON_RESOLVED,
+ TRACE_CHECK_HEADER, // One file.
+ TRACE_CHECK_HEADERS, // All files.
+ };
+
+ TraceItem(Type type, const std::string& name, std::thread::id thread_id);
+ ~TraceItem();
+
+ Type type() const { return type_; }
+ const std::string& name() const { return name_; }
+ std::thread::id thread_id() const { return thread_id_; }
+
+ Ticks begin() const { return begin_; }
+ void set_begin(Ticks b) { begin_ = b; }
+ Ticks end() const { return end_; }
+ void set_end(Ticks e) { end_ = e; }
+
+ TickDelta delta() const { return TicksDelta(end_, begin_); }
+
+ // Optional toolchain label.
+ const std::string& toolchain() const { return toolchain_; }
+ void set_toolchain(const std::string& t) { toolchain_ = t; }
+
+ // Optional command line.
+ const std::string& cmdline() const { return cmdline_; }
+ void set_cmdline(const std::string& c) { cmdline_ = c; }
+
+ private:
+ Type type_;
+ std::string name_;
+ std::thread::id thread_id_;
+
+ Ticks begin_;
+ Ticks end_;
+
+ std::string toolchain_;
+ std::string cmdline_;
+};
+
+class ScopedTrace {
+ public:
+ ScopedTrace(TraceItem::Type t, const std::string& name);
+ ScopedTrace(TraceItem::Type t, const Label& label);
+ ~ScopedTrace();
+
+ void SetToolchain(const Label& label);
+ void SetCommandLine(const base::CommandLine& cmdline);
+
+ void Done();
+
+ private:
+ TraceItem* item_;
+ bool done_;
+};
+
+// Call to turn tracing on. It's off by default.
+void EnableTracing();
+
+// Returns whether tracing is enabled.
+bool TracingEnabled();
+
+// Adds a trace event to the log. Takes ownership of the pointer.
+void AddTrace(TraceItem* item);
+
+// Returns a summary of the current traces, or the empty string if tracing is
+// not enabled.
+std::string SummarizeTraces();
+
+// Saves the current traces to the given filename in JSON format.
+void SaveTraces(const base::FilePath& file_name);
+
+#endif // TOOLS_GN_TRACE_H_
diff --git a/gn/tools/gn/unique_vector.h b/gn/tools/gn/unique_vector.h
new file mode 100644
index 00000000000..732b66344d6
--- /dev/null
+++ b/gn/tools/gn/unique_vector.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. 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()
+ : value_(nullptr),
+ vect_(nullptr),
+ index_(static_cast<size_t>(-1)),
+ hash_val_(0) {}
+
+ // Initialize with a pointer to a value.
+ explicit UniquifyRef(const T* v)
+ : value_(v), vect_(nullptr), index_(static_cast<size_t>(-1)) {
+ FillHashValue();
+ }
+
+ // Initialize with an array + index.
+ UniquifyRef(const std::vector<T>* v, size_t i)
+ : value_(nullptr), 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)
+ : value_(nullptr), 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_;
+
+ // When value is null these are used.
+ const std::vector<T>* vect_;
+ size_t index_;
+
+ size_t hash_val_;
+};
+
+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;
+ }
+
+ // Like push_back but swaps in the type to avoid a copy.
+ bool PushBackViaSwap(T* t) {
+ using std::swap;
+
+ Ref ref(t);
+ if (set_.find(ref) != set_.end())
+ return false; // Already have this one.
+
+ size_t new_index = vector_.size();
+ vector_.resize(new_index + 1);
+ swap(vector_[new_index], *t);
+ 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
new file mode 100644
index 00000000000..afea1e24a00
--- /dev/null
+++ b/gn/tools/gn/unique_vector_unittest.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 <stddef.h>
+
+#include <algorithm>
+
+#include "tools/gn/unique_vector.h"
+#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, PushBackViaSwap) {
+ UniqueVector<std::string> vect;
+ std::string a("a");
+ EXPECT_TRUE(vect.PushBackViaSwap(&a));
+ EXPECT_EQ("", a);
+
+ a = "a";
+ EXPECT_FALSE(vect.PushBackViaSwap(&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
new file mode 100644
index 00000000000..04db7eebee4
--- /dev/null
+++ b/gn/tools/gn/value.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2013 The Chromium Authors. 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"
+
+Value::Value()
+ : type_(NONE), boolean_value_(false), int_value_(0), origin_(nullptr) {}
+
+Value::Value(const ParseNode* origin, Type t)
+ : type_(t), boolean_value_(false), int_value_(0), origin_(origin) {}
+
+Value::Value(const ParseNode* origin, bool bool_val)
+ : type_(BOOLEAN),
+ boolean_value_(bool_val),
+ int_value_(0),
+ origin_(origin) {}
+
+Value::Value(const ParseNode* origin, int64_t int_val)
+ : type_(INTEGER),
+ boolean_value_(false),
+ int_value_(int_val),
+ origin_(origin) {}
+
+Value::Value(const ParseNode* origin, std::string str_val)
+ : type_(STRING),
+ string_value_(std::move(str_val)),
+ boolean_value_(false),
+ int_value_(0),
+ origin_(origin) {}
+
+Value::Value(const ParseNode* origin, const char* str_val)
+ : type_(STRING),
+ string_value_(str_val),
+ boolean_value_(false),
+ int_value_(0),
+ origin_(origin) {}
+
+Value::Value(const ParseNode* origin, std::unique_ptr<Scope> scope)
+ : type_(SCOPE),
+ string_value_(),
+ boolean_value_(false),
+ int_value_(0),
+ scope_value_(std::move(scope)),
+ origin_(origin) {}
+
+Value::Value(const Value& other)
+ : type_(other.type_),
+ string_value_(other.string_value_),
+ boolean_value_(other.boolean_value_),
+ int_value_(other.int_value_),
+ list_value_(other.list_value_),
+ origin_(other.origin_) {
+ if (type() == SCOPE && other.scope_value_.get())
+ scope_value_ = other.scope_value_->MakeClosure();
+}
+
+Value::Value(Value&& other) noexcept = default;
+
+Value::~Value() = default;
+
+Value& Value::operator=(const Value& other) {
+ type_ = other.type_;
+ string_value_ = other.string_value_;
+ boolean_value_ = other.boolean_value_;
+ int_value_ = other.int_value_;
+ list_value_ = other.list_value_;
+ if (type() == SCOPE && other.scope_value_.get())
+ scope_value_ = other.scope_value_->MakeClosure();
+ origin_ = other.origin_;
+ return *this;
+}
+
+// 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:
+ // Scopes are always considered not equal because there's currently
+ // no use case for comparing them, and it requires a bunch of complex
+ // iteration code.
+ return false;
+ default:
+ 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
new file mode 100644
index 00000000000..13beb6fdcf2
--- /dev/null
+++ b/gn/tools/gn/value.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2013 The Chromium Authors. 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) = default;
+
+ 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.
+ bool operator==(const Value& other) const;
+ bool operator!=(const Value& other) const;
+
+ private:
+ // This are a lot of objects associated with every Value that need
+ // initialization and tear down every time. It might be more efficient to
+ // create a union of objects (see small_map) and only use the one we care
+ // about.
+ Type type_;
+ std::string string_value_;
+ bool boolean_value_;
+ int64_t int_value_;
+ std::vector<Value> list_value_;
+ std::unique_ptr<Scope> scope_value_;
+
+ const ParseNode* origin_;
+};
+
+#endif // TOOLS_GN_VALUE_H_
diff --git a/gn/tools/gn/value_extractors.cc b/gn/tools/gn/value_extractors.cc
new file mode 100644
index 00000000000..6eb09accd59
--- /dev/null
+++ b/gn/tools/gn/value_extractors.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..1e426502036
--- /dev/null
+++ b/gn/tools/gn/value_extractors.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2013 The Chromium Authors. 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
new file mode 100644
index 00000000000..40ddf38ecbc
--- /dev/null
+++ b/gn/tools/gn/value_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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.scope());
+ Value scopeval(nullptr, std::unique_ptr<Scope>(scope));
+ EXPECT_EQ("{ }", scopeval.ToString(false));
+
+ 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));
+}
diff --git a/gn/tools/gn/variables.cc b/gn/tools/gn/variables.cc
new file mode 100644
index 00000000000..67975ce52dd
--- /dev/null
+++ b/gn/tools/gn/variables.cc
@@ -0,0 +1,2124 @@
+// Copyright (c) 2013 The Chromium Authors. 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"
+
+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: 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"
+ bundle_plugins_dir = "${bundle_contents_dir}/PlugIns"
+ }
+)";
+
+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: 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: 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 kBundlePlugInsDir[] = "bundle_plugins_dir";
+const char kBundlePlugInsDir_HelpShort[] =
+ "bundle_plugins_dir: "
+ "Expansion of {{bundle_plugins_dir}} in create_bundle.";
+const char kBundlePlugInsDir_Help[] =
+ R"(bundle_plugins_dir: Expansion of {{bundle_plugins_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_plugins_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 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, then 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 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 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).
+
+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[] =
+ "test_application_name: [string] Test application name for unit or ui test "
+ "target.";
+const char kXcodeTestApplicationName_Help[] =
+ R"(test_application_name: Test application name for unit or ui 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 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(BundlePlugInsDir)
+ 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(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(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(ResponseFileContents)
+ INSERT_VARIABLE(Script)
+ INSERT_VARIABLE(Sources)
+ INSERT_VARIABLE(XcodeTestApplicationName)
+ INSERT_VARIABLE(Testonly)
+ INSERT_VARIABLE(Visibility)
+ INSERT_VARIABLE(WriteRuntimeDeps)
+ INSERT_VARIABLE(XcodeExtraAttributes)
+ }
+ return info_map;
+}
+
+#undef INSERT_VARIABLE
+
+} // namespace variables
diff --git a/gn/tools/gn/variables.h b/gn/tools/gn/variables.h
new file mode 100644
index 00000000000..080989b4f5b
--- /dev/null
+++ b/gn/tools/gn/variables.h
@@ -0,0 +1,332 @@
+// Copyright (c) 2013 The Chromium Authors. 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 kBundlePlugInsDir[];
+extern const char kBundlePlugInsDir_HelpShort[];
+extern const char kBundlePlugInsDir_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 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 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 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 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
new file mode 100644
index 00000000000..75039e9e237
--- /dev/null
+++ b/gn/tools/gn/visibility.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. 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
new file mode 100644
index 00000000000..4228f9dae77
--- /dev/null
+++ b/gn/tools/gn/visibility.h
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. 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
new file mode 100644
index 00000000000..2f6febc5a16
--- /dev/null
+++ b/gn/tools/gn/visibility_unittest.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 "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
new file mode 100644
index 00000000000..00cd2a32e54
--- /dev/null
+++ b/gn/tools/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 "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.h b/gn/tools/gn/visual_studio_utils.h
new file mode 100644
index 00000000000..319428fbc13
--- /dev/null
+++ b/gn/tools/gn/visual_studio_utils.h
@@ -0,0 +1,50 @@
+// 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_UTILS_H_
+#define TOOLS_GN_VISUAL_STUDIO_UTILS_H_
+
+#include <string>
+
+// Some compiler options which will be written to project file. We don't need to
+// specify all options because generated project file is going to be used only
+// for compilation of single file. For real build ninja files are used.
+struct CompilerOptions {
+ CompilerOptions();
+ ~CompilerOptions();
+
+ std::string additional_options;
+ std::string buffer_security_check;
+ std::string forced_include_files;
+ std::string disable_specific_warnings;
+ std::string optimization;
+ std::string runtime_library;
+ std::string treat_warning_as_error;
+ std::string warning_level;
+};
+
+// Some linker options which will be written to project file. We don't need to
+// specify all options because generated project file is going to be used only
+// for compilation of single file. For real build ninja files are used.
+struct LinkerOptions {
+ LinkerOptions();
+ ~LinkerOptions();
+
+ std::string subsystem;
+};
+
+// Generates something which looks like a GUID, but depends only on the name and
+// seed. This means the same name / seed will always generate the same GUID, so
+// that projects and solutions which refer to each other can explicitly
+// determine the GUID to refer to explicitly. It also means that the GUID will
+// not change when the project for a target is rebuilt.
+std::string MakeGuid(const std::string& entry_path, const std::string& seed);
+
+// Parses |cflag| value and stores it in |options|.
+void ParseCompilerOption(const std::string& cflag, CompilerOptions* options);
+
+// Parses |ldflags| value and stores it in |options|.
+void ParseLinkerOption(const std::string& ldflag, LinkerOptions* options);
+
+#endif // TOOLS_GN_VISUAL_STUDIO_UTILS_H_
diff --git a/gn/tools/gn/visual_studio_utils_unittest.cc b/gn/tools/gn/visual_studio_utils_unittest.cc
new file mode 100644
index 00000000000..d77781407fa
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..c4fd906bbdc
--- /dev/null
+++ b/gn/tools/gn/visual_studio_writer.cc
@@ -0,0 +1,911 @@
+// 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/source_file_type.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 kProjectVersionVs2013[] = "12.0"; // Visual Studio 2013
+const char kProjectVersionVs2015[] = "14.0"; // Visual Studio 2015
+const char kProjectVersionVs2017[] = "15.0"; // Visual Studio 2017
+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 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;
+ 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");
+ {
+ std::unique_ptr<XmlElementWriter> out_dir =
+ properties->SubElement("OutDir");
+ path_output.WriteDir(out_dir->StartContent(false),
+ build_settings_->build_dir(),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ }
+ 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;
+ Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
+ if (target->GetOutputFilesForSource(file, &tool_type, &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(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(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
new file mode 100644
index 00000000000..3193646b7fc
--- /dev/null
+++ b/gn/tools/gn/visual_studio_writer.h
@@ -0,0 +1,167 @@
+// 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
+ };
+
+ // 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
new file mode 100644
index 00000000000..165bbb47d54
--- /dev/null
+++ b/gn/tools/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 "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 = std::make_unique<Tool>();
+ tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}", ""));
+
+ Toolchain toolchain(setup_.settings(), Label(SourceDir("//tc/"), "tc"));
+ toolchain.SetTool(Toolchain::TYPE_ALINK, 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
new file mode 100644
index 00000000000..bd5c440b984
--- /dev/null
+++ b/gn/tools/gn/xcode_object.cc
@@ -0,0 +1,990 @@
+// 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
new file mode 100644
index 00000000000..715090b652b
--- /dev/null
+++ b/gn/tools/gn/xcode_object.h
@@ -0,0 +1,465 @@
+// 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
new file mode 100644
index 00000000000..70ac2c76706
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..e41c7a0037a
--- /dev/null
+++ b/gn/tools/gn/xcode_writer.cc
@@ -0,0 +1,667 @@
+// 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 =
+ base::FilePath::FromUTF8Unsafe(
+ RebasePath("//", build_settings->build_dir()))
+ .StripTrailingSeparators()
+ .AsUTF8Unsafe();
+
+ std::string config_name = build_settings->build_dir()
+ .Resolve(base::FilePath())
+ .StripTrailingSeparators()
+ .BaseName()
+ .AsUTF8Unsafe();
+ 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
new file mode 100644
index 00000000000..24953dcb9cd
--- /dev/null
+++ b/gn/tools/gn/xcode_writer.h
@@ -0,0 +1,89 @@
+// 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
new file mode 100644
index 00000000000..360d8af18d5
--- /dev/null
+++ b/gn/tools/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 "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
new file mode 100644
index 00000000000..4aa5f7e34a4
--- /dev/null
+++ b/gn/tools/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 <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
new file mode 100644
index 00000000000..1b9eb13aba4
--- /dev/null
+++ b/gn/tools/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 "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/auto_reset_event.h b/gn/util/auto_reset_event.h
new file mode 100644
index 00000000000..5e040a8984f
--- /dev/null
+++ b/gn/util/auto_reset_event.h
@@ -0,0 +1,53 @@
+// 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_AUTO_RESET_EVENT_H_
+#define UTIL_AUTO_RESET_EVENT_H_
+
+#include <atomic>
+
+#include "base/logging.h"
+#include "util/semaphore.h"
+
+// From http://preshing.com/20150316/semaphores-are-surprisingly-versatile/,
+// but using V8's Semaphore.
+class AutoResetEvent {
+ private:
+ // status_ == 1: Event object is signaled.
+ // status_ == 0: Event object is reset and no threads are waiting.
+ // status_ == -N: Event object is reset and N threads are waiting.
+ std::atomic<int> status_;
+ Semaphore semaphore_;
+
+ public:
+ AutoResetEvent() : status_(0), semaphore_(0) {}
+
+ void Signal() {
+ int old_status = status_.load(std::memory_order_relaxed);
+ // Increment status_ atomically via CAS loop.
+ for (;;) {
+ DCHECK_LE(old_status, 1);
+ int new_status = old_status < 1 ? old_status + 1 : 1;
+ if (status_.compare_exchange_weak(old_status, new_status,
+ std::memory_order_release,
+ std::memory_order_relaxed)) {
+ break;
+ }
+ // The compare-exchange failed, likely because another thread changed
+ // status_. old_status has been updated. Retry the CAS loop.
+ }
+ if (old_status < 0)
+ semaphore_.Signal(); // Release one waiting thread.
+ }
+
+ void Wait() {
+ int old_status = status_.fetch_sub(1, std::memory_order_acquire);
+ DCHECK_LE(old_status, 1);
+ if (old_status < 1) {
+ semaphore_.Wait();
+ }
+ }
+};
+
+#endif // UTIL_AUTO_RESET_EVENT_H_
diff --git a/gn/util/build_config.h b/gn/util/build_config.h
new file mode 100644
index 00000000000..addd7cfb081
--- /dev/null
+++ b/gn/util/build_config.h
@@ -0,0 +1,196 @@
+// 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
new file mode 100644
index 00000000000..f2adab52bcb
--- /dev/null
+++ b/gn/util/exe_path.cc
@@ -0,0 +1,61 @@
+// 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>
+#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);
+}
+
+#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/util/exe_path.h
new file mode 100644
index 00000000000..0e1b8cbed38
--- /dev/null
+++ b/gn/util/exe_path.h
@@ -0,0 +1,12 @@
+// 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_EXE_PATH_H_
+#define UTIL_EXE_PATH_H_
+
+#include "base/files/file_path.h"
+
+base::FilePath GetExePath();
+
+#endif // UTIL_EXE_PATH_H_
diff --git a/gn/util/msg_loop.cc b/gn/util/msg_loop.cc
new file mode 100644
index 00000000000..dbb92cf4485
--- /dev/null
+++ b/gn/util/msg_loop.cc
@@ -0,0 +1,76 @@
+// 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
new file mode 100644
index 00000000000..267d2a9f681
--- /dev/null
+++ b/gn/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 "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
new file mode 100644
index 00000000000..2ae7b7b69c0
--- /dev/null
+++ b/gn/util/semaphore.cc
@@ -0,0 +1,95 @@
+// 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_LINUX) || defined(OS_AIX)
+
+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
new file mode 100644
index 00000000000..2de27594311
--- /dev/null
+++ b/gn/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 <mach/mach.h>
+#elif defined(OS_LINUX) || defined(OS_AIX)
+#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_LINUX) || defined(OS_AIX)
+ 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
new file mode 100644
index 00000000000..a1ce3e92a22
--- /dev/null
+++ b/gn/util/sys_info.cc
@@ -0,0 +1,81 @@
+// 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)
+ SYSTEM_INFO system_info = {};
+ ::GetNativeSystemInfo(&system_info);
+ return system_info.dwNumberOfProcessors;
+#else
+#error
+#endif
+}
diff --git a/gn/util/sys_info.h b/gn/util/sys_info.h
new file mode 100644
index 00000000000..68133e1088f
--- /dev/null
+++ b/gn/util/sys_info.h
@@ -0,0 +1,13 @@
+// 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_SYS_INFO_H_
+#define UTIL_SYS_INFO_H_
+
+#include <string>
+
+std::string OperatingSystemArchitecture();
+int NumberOfProcessors();
+
+#endif // UTIL_SYS_INFO_H_
diff --git a/gn/util/task.h b/gn/util/task.h
new file mode 100644
index 00000000000..278ff133985
--- /dev/null
+++ b/gn/util/task.h
@@ -0,0 +1,13 @@
+// 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
new file mode 100644
index 00000000000..6b2e26d6b96
--- /dev/null
+++ b/gn/util/test/gn_test.cc
@@ -0,0 +1,162 @@
+// 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
new file mode 100644
index 00000000000..b5539d7fa4d
--- /dev/null
+++ b/gn/util/test/test.h
@@ -0,0 +1,195 @@
+// 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
new file mode 100644
index 00000000000..2a90c90e310
--- /dev/null
+++ b/gn/util/ticks.cc
@@ -0,0 +1,93 @@
+// 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_LINUX) || defined(OS_AIX)
+#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_LINUX) || defined(OS_AIX)
+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_LINUX) || defined(OS_AIX)
+ 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_LINUX) || defined(OS_AIX)
+ 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/util/ticks.h
new file mode 100644
index 00000000000..fcbde0509e8
--- /dev/null
+++ b/gn/util/ticks.h
@@ -0,0 +1,45 @@
+// 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_TICKS_H_
+#define UTIL_TICKS_H_
+
+#include <stdint.h>
+
+using Ticks = uint64_t;
+
+class TickDelta {
+ public:
+ explicit TickDelta(uint64_t delta) : delta_(delta) {}
+
+ double InSecondsF() const { return delta_ / 1000000000.0; }
+ double InMillisecondsF() const { return delta_ / 1000000.0; }
+ double InMicrosecondsF() const { return delta_ / 1000.0; }
+ double InNanosecondsF() const { return delta_; }
+
+ uint64_t InSeconds() const { return delta_ / 1000000000; }
+ uint64_t InMilliseconds() const { return delta_ / 1000000; }
+ uint64_t InMicroseconds() const { return delta_ / 1000; }
+ uint64_t InNanoseconds() const { return delta_; }
+
+ uint64_t raw() const { return delta_; }
+
+ private:
+ uint64_t delta_;
+};
+
+Ticks TicksNow();
+
+TickDelta TicksDelta(Ticks new_ticks, Ticks old_ticks);
+
+class ElapsedTimer {
+ public:
+ ElapsedTimer() : start_(TicksNow()) {}
+ TickDelta Elapsed() { return TicksDelta(TicksNow(), start_); }
+
+ private:
+ Ticks start_;
+};
+
+#endif // UTIL_TICKS_H_
diff --git a/gn/util/worker_pool.cc b/gn/util/worker_pool.cc
new file mode 100644
index 00000000000..92fadd44eea
--- /dev/null
+++ b/gn/util/worker_pool.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 "util/worker_pool.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "tools/gn/switches.h"
+#include "util/sys_info.h"
+
+namespace {
+
+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) {
+ threads_.reserve(thread_count);
+ for (size_t i = 0; i < thread_count; ++i)
+ threads_.emplace_back([this]() { Worker(); });
+}
+
+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
new file mode 100644
index 00000000000..d0616449097
--- /dev/null
+++ b/gn/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 <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_