summaryrefslogtreecommitdiffstats
path: root/gn
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-04-09 17:53:07 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-04-17 10:44:35 +0000
commit3ffaed019d0772e59d6cdb2d0d32fe4834c31f72 (patch)
tree6d5ce3a8656c327c767b7b5fc112fb45ad3851f2 /gn
parentf72214e77a7392dd062812c1eea4e7339b3ffa30 (diff)
BASELINE: Update GN
Change-Id: Ie794eb9f8e5c17c6caea72b89ce954c7ad772eaa Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'gn')
-rw-r--r--gn/AUTHORS1
-rw-r--r--gn/OWNERS1
-rw-r--r--gn/README.md18
-rw-r--r--gn/base/files/file_path.cc21
-rw-r--r--gn/base/files/file_posix.cc16
-rw-r--r--gn/base/files/file_util.cc6
-rw-r--r--gn/base/files/file_util.h73
-rw-r--r--gn/base/files/file_util_mac.mm64
-rw-r--r--gn/base/files/file_util_posix.cc190
-rw-r--r--gn/base/files/file_util_win.cc199
-rw-r--r--gn/base/logging.cc1
-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/numerics/safe_conversions.h5
-rw-r--r--gn/base/numerics/safe_math_clang_gcc_impl.h5
-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
-rwxr-xr-xgn/build/gen.py142
-rw-r--r--gn/docs/faq.md6
-rw-r--r--gn/docs/hacking.md23
-rw-r--r--gn/docs/reference.md1113
-rw-r--r--gn/docs/standalone.md2
-rw-r--r--gn/docs/style_guide.md50
-rw-r--r--gn/docs/update_binaries.md5
-rw-r--r--gn/infra/README.recipes.md68
-rw-r--r--gn/infra/recipe_modules/macos_sdk/__init__.py44
-rw-r--r--gn/infra/recipe_modules/macos_sdk/api.py92
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json23
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json83
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json23
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.py22
-rw-r--r--gn/infra/recipe_modules/windows_sdk/__init__.py12
-rw-r--r--gn/infra/recipe_modules/windows_sdk/api.py55
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json12
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.py2
-rw-r--r--gn/infra/recipes/gn.expected/ci_linux.json229
-rw-r--r--gn/infra/recipes/gn.expected/ci_mac.json295
-rw-r--r--gn/infra/recipes/gn.expected/ci_win.json (renamed from gn/infra/recipes/gn.expected/ci.json)207
-rw-r--r--gn/infra/recipes/gn.expected/cipd_exists.json34
-rw-r--r--gn/infra/recipes/gn.expected/cipd_register.json38
-rw-r--r--gn/infra/recipes/gn.expected/cq_linux.json234
-rw-r--r--gn/infra/recipes/gn.expected/cq_mac.json300
-rw-r--r--gn/infra/recipes/gn.expected/cq_win.json (renamed from gn/infra/recipes/gn.expected/cq.json)201
-rw-r--r--gn/infra/recipes/gn.py99
-rw-r--r--gn/tools/gn/args.cc2
-rw-r--r--gn/tools/gn/build_settings.cc3
-rw-r--r--gn/tools/gn/build_settings.h3
-rw-r--r--gn/tools/gn/bundle_data.cc25
-rw-r--r--gn/tools/gn/bundle_data.h15
-rw-r--r--gn/tools/gn/bundle_file_rule.cc75
-rw-r--r--gn/tools/gn/bundle_file_rule.h19
-rw-r--r--gn/tools/gn/c_include_iterator.cc9
-rw-r--r--gn/tools/gn/c_include_iterator_unittest.cc17
-rw-r--r--gn/tools/gn/command_analyze.cc1
-rw-r--r--gn/tools/gn/command_args.cc7
-rw-r--r--gn/tools/gn/command_check.cc23
-rw-r--r--gn/tools/gn/command_clean.cc1
-rw-r--r--gn/tools/gn/command_desc.cc335
-rw-r--r--gn/tools/gn/command_format.cc248
-rw-r--r--gn/tools/gn/command_format.h16
-rw-r--r--gn/tools/gn/command_format_unittest.cc14
-rw-r--r--gn/tools/gn/command_gen.cc11
-rw-r--r--gn/tools/gn/command_help.cc139
-rw-r--r--gn/tools/gn/command_ls.cc1
-rw-r--r--gn/tools/gn/command_meta.cc163
-rw-r--r--gn/tools/gn/command_path.cc1
-rw-r--r--gn/tools/gn/command_refs.cc7
-rw-r--r--gn/tools/gn/commands.cc1
-rw-r--r--gn/tools/gn/commands.h11
-rw-r--r--gn/tools/gn/create_bundle_target_generator.cc5
-rw-r--r--gn/tools/gn/desc_builder.cc91
-rw-r--r--gn/tools/gn/exec_process.cc1
-rw-r--r--gn/tools/gn/exec_process_unittest.cc2
-rw-r--r--gn/tools/gn/filesystem_utils.cc7
-rw-r--r--gn/tools/gn/filesystem_utils_unittest.cc4
-rw-r--r--gn/tools/gn/format_test_data/028.golden2
-rw-r--r--gn/tools/gn/format_test_data/066.gn2
-rw-r--r--gn/tools/gn/format_test_data/066.golden2
-rw-r--r--gn/tools/gn/format_test_data/071.gn26
-rw-r--r--gn/tools/gn/format_test_data/071.golden25
-rw-r--r--gn/tools/gn/format_test_data/072.gn8
-rw-r--r--gn/tools/gn/format_test_data/072.golden21
-rw-r--r--gn/tools/gn/format_test_data/073.gn23
-rw-r--r--gn/tools/gn/format_test_data/073.golden34
-rw-r--r--gn/tools/gn/format_test_data/074.gn7
-rw-r--r--gn/tools/gn/format_test_data/074.golden7
-rw-r--r--gn/tools/gn/format_test_data/075.gn11
-rw-r--r--gn/tools/gn/format_test_data/075.golden12
-rw-r--r--gn/tools/gn/function_exec_script.cc25
-rw-r--r--gn/tools/gn/function_forward_variables_from.cc7
-rw-r--r--gn/tools/gn/function_get_path_info.cc2
-rw-r--r--gn/tools/gn/function_set_defaults.cc2
-rw-r--r--gn/tools/gn/function_write_file_unittest.cc2
-rw-r--r--gn/tools/gn/functions.cc38
-rw-r--r--gn/tools/gn/functions.h9
-rw-r--r--gn/tools/gn/functions_target.cc190
-rw-r--r--gn/tools/gn/functions_target_unittest.cc52
-rw-r--r--gn/tools/gn/functions_unittest.cc12
-rw-r--r--gn/tools/gn/generated_file_target_generator.cc168
-rw-r--r--gn/tools/gn/generated_file_target_generator.h49
-rw-r--r--gn/tools/gn/header_checker.cc101
-rw-r--r--gn/tools/gn/header_checker.h31
-rw-r--r--gn/tools/gn/header_checker_unittest.cc79
-rw-r--r--gn/tools/gn/input_conversion.cc24
-rw-r--r--gn/tools/gn/label_ptr.h49
-rw-r--r--gn/tools/gn/metadata.cc69
-rw-r--r--gn/tools/gn/metadata.h69
-rw-r--r--gn/tools/gn/metadata_unittest.cc204
-rw-r--r--gn/tools/gn/metadata_walk.cc24
-rw-r--r--gn/tools/gn/metadata_walk.h26
-rw-r--r--gn/tools/gn/metadata_walk_unittest.cc211
-rw-r--r--gn/tools/gn/misc/emacs/gn-mode.el23
-rw-r--r--gn/tools/gn/misc/tm/GN.tmLanguage4
-rw-r--r--gn/tools/gn/misc/vim/README.chromium5
-rw-r--r--gn/tools/gn/misc/vim/README.md29
-rw-r--r--gn/tools/gn/misc/vim/syntax/gn.vim5
-rw-r--r--gn/tools/gn/ninja_action_target_writer_unittest.cc8
-rw-r--r--gn/tools/gn/ninja_build_writer.cc36
-rw-r--r--gn/tools/gn/ninja_build_writer.h10
-rw-r--r--gn/tools/gn/ninja_build_writer_unittest.cc86
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer.cc32
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc2
-rw-r--r--gn/tools/gn/ninja_generated_file_target_writer.cc92
-rw-r--r--gn/tools/gn/ninja_generated_file_target_writer.h25
-rw-r--r--gn/tools/gn/ninja_generated_file_target_writer_unittest.cc60
-rw-r--r--gn/tools/gn/ninja_target_writer.cc4
-rw-r--r--gn/tools/gn/operators.cc5
-rw-r--r--gn/tools/gn/operators_unittest.cc43
-rw-r--r--gn/tools/gn/output_conversion.cc4
-rw-r--r--gn/tools/gn/parse_tree.cc200
-rw-r--r--gn/tools/gn/parse_tree.h51
-rw-r--r--gn/tools/gn/parser.cc51
-rw-r--r--gn/tools/gn/parser.h5
-rw-r--r--gn/tools/gn/parser_unittest.cc4
-rw-r--r--gn/tools/gn/qt_creator_writer.cc128
-rw-r--r--gn/tools/gn/scheduler.cc10
-rw-r--r--gn/tools/gn/scheduler.h5
-rw-r--r--gn/tools/gn/scope.cc17
-rw-r--r--gn/tools/gn/scope.h5
-rw-r--r--gn/tools/gn/setup.cc100
-rw-r--r--gn/tools/gn/setup.h23
-rw-r--r--gn/tools/gn/setup_unittest.cc45
-rw-r--r--gn/tools/gn/source_dir.h10
-rw-r--r--gn/tools/gn/source_dir_unittest.cc21
-rw-r--r--gn/tools/gn/standard_out.cc68
-rw-r--r--gn/tools/gn/standard_out.h24
-rw-r--r--gn/tools/gn/substitution_pattern.h2
-rw-r--r--gn/tools/gn/substitution_type.cc8
-rw-r--r--gn/tools/gn/substitution_type.h1
-rw-r--r--gn/tools/gn/substitution_writer.h1
-rw-r--r--gn/tools/gn/switches.cc36
-rw-r--r--gn/tools/gn/switches.h12
-rw-r--r--gn/tools/gn/target.cc112
-rw-r--r--gn/tools/gn/target.h47
-rw-r--r--gn/tools/gn/target_generator.cc39
-rw-r--r--gn/tools/gn/target_generator.h1
-rw-r--r--gn/tools/gn/target_unittest.cc214
-rw-r--r--gn/tools/gn/toolchain.cc2
-rw-r--r--gn/tools/gn/value.cc134
-rw-r--r--gn/tools/gn/value.h30
-rw-r--r--gn/tools/gn/value_unittest.cc19
-rw-r--r--gn/tools/gn/variables.cc142
-rw-r--r--gn/tools/gn/variables.h28
-rw-r--r--gn/tools/gn/visual_studio_writer.cc16
-rw-r--r--gn/tools/gn/visual_studio_writer.h3
-rw-r--r--gn/tools/gn/xcode_writer.cc19
-rw-r--r--gn/util/exe_path.cc15
-rw-r--r--gn/util/semaphore.cc2
-rw-r--r--gn/util/semaphore.h4
-rw-r--r--gn/util/ticks.cc8
173 files changed, 6760 insertions, 3725 deletions
diff --git a/gn/AUTHORS b/gn/AUTHORS
index ca2667131c3..fe152fd3877 100644
--- a/gn/AUTHORS
+++ b/gn/AUTHORS
@@ -25,6 +25,7 @@ DanCraft99 <simputest@gmail.com>
Gergely Nagy <ngg@ngg.hu>
Ilia K <ki.stfu@gmail.com>
Ivan Naydonov <samogot@gmail.com>
+Joe Armstrong <joearmstrong334@gmail.com>
Julien Brianceau <jbriance@cisco.com>
Kal Conley <kcconley@gmail.com>
Kamil Rytarowski <krytarowski@gmail.com>
diff --git a/gn/OWNERS b/gn/OWNERS
index 0200965cc3c..d78697cf06f 100644
--- a/gn/OWNERS
+++ b/gn/OWNERS
@@ -1,4 +1,3 @@
brettw@chromium.org
-dpranke@chromium.org
phosek@chromium.org
scottmg@chromium.org
diff --git a/gn/README.md b/gn/README.md
index 5c98d1f4879..a370e008839 100644
--- a/gn/README.md
+++ b/gn/README.md
@@ -1,14 +1,25 @@
# GN
GN is a meta-build system that generates build files for
-[Ninja](https://ninja-build.org).
+[Ninja](https://ninja-build.org). There is documentation in
+[docs/](https://gn.googlesource.com/gn/+/master/docs/) and
+[a presentation on it](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing).
## Getting started
+You can download the latest version of GN binary for
+[Linux](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest),
+[macOS](https://chrome-infra-packages.appspot.com/dl/gn/gn/mac-amd64/+/latest) and
+[Windows](https://chrome-infra-packages.appspot.com/dl/gn/gn/windows-amd64/+/latest).
+
+Alternatively, you can build GN from source:
+
git clone https://gn.googlesource.com/gn
cd gn
python build/gen.py
ninja -C out
+ # To run tests:
+ out/gn_unittests
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
@@ -18,6 +29,11 @@ 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`.
+## Reporting bugs
+
+If you find a bug, you can see if it is known or report it in the [bug
+database](https://bugs.chromium.org/p/gn/issues/list).
+
## Sending patches
GN uses [Gerrit](https://www.gerritcodereview.com/) for code review. The short
diff --git a/gn/base/files/file_path.cc b/gn/base/files/file_path.cc
index a66c8cc1ffc..014bc9e0cf8 100644
--- a/gn/base/files/file_path.cc
+++ b/gn/base/files/file_path.cc
@@ -11,7 +11,6 @@
#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"
@@ -602,10 +601,6 @@ FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) {
// 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_;
@@ -613,37 +608,21 @@ std::string FilePath::MaybeAsASCII() const {
}
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)
diff --git a/gn/base/files/file_posix.cc b/gn/base/files/file_posix.cc
index 4faf77b944a..ed9a5e2a8e3 100644
--- a/gn/base/files/file_posix.cc
+++ b/gn/base/files/file_posix.cc
@@ -86,14 +86,7 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) {
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)
+#if defined(OS_MACOSX)
time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
@@ -107,6 +100,13 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) {
int64_t last_accessed_nsec = 0;
time_t creation_time_sec = stat_info.st_ctime;
int64_t creation_time_nsec = 0;
+#elif defined(OS_POSIX)
+ time_t last_modified_sec = stat_info.st_mtim.tv_sec;
+ int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
+ time_t last_accessed_sec = stat_info.st_atim.tv_sec;
+ int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
+ time_t creation_time_sec = stat_info.st_ctim.tv_sec;
+ int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
#else
#error
#endif
diff --git a/gn/base/files/file_util.cc b/gn/base/files/file_util.cc
index d087b00f6c9..9a98a0b81e0 100644
--- a/gn/base/files/file_util.cc
+++ b/gn/base/files/file_util.cc
@@ -43,12 +43,6 @@ int64_t ComputeDirectorySize(const FilePath& root_path) {
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
diff --git a/gn/base/files/file_util.h b/gn/base/files/file_util.h
index bfcac0169fc..e22f40f8f53 100644
--- a/gn/base/files/file_util.h
+++ b/gn/base/files/file_util.h
@@ -75,13 +75,6 @@ bool DeleteFile(const FilePath& path, bool recursive);
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
@@ -92,47 +85,6 @@ 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);
@@ -241,14 +193,6 @@ bool IsDirectoryEmpty(const FilePath& dir_path);
// 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.
@@ -446,23 +390,6 @@ bool GetFileSystemType(const FilePath& path, FileSystemType* type);
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_mac.mm b/gn/base/files/file_util_mac.mm
deleted file mode 100644
index 35fd27a6699..00000000000
--- a/gn/base/files/file_util_mac.mm
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_util.h"
-
-#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
index 23c7cb4abc9..eb07e64f8e2 100644
--- a/gn/base/files/file_util_posix.cc
+++ b/gn/base/files/file_util_posix.cc
@@ -34,13 +34,11 @@
#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)
@@ -111,15 +109,7 @@ bool VerifySpecificPathControlledByUser(const FilePath& path,
}
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)
@@ -189,143 +179,6 @@ bool CopyFileContents(File* infile, File* outfile) {
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)
@@ -398,18 +251,6 @@ bool ReplaceFile(const FilePath& from_path,
*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]) {
@@ -578,8 +419,6 @@ bool ExecutableExistsInPath(Environment* env,
#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) {
@@ -590,7 +429,6 @@ bool GetTempDir(FilePath* path) {
*path = FilePath("/tmp");
return true;
}
-#endif // !defined(OS_MACOSX)
#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm.
FilePath GetHomeDir() {
@@ -945,33 +783,5 @@ bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
}
#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
index 2a1e9ecf704..34d328b5fca 100644
--- a/gn/base/files/file_util_win.cc
+++ b/gn/base/files/file_util_win.cc
@@ -91,121 +91,6 @@ void AppendModeCharacter(base::char16 mode_char, base::string16* mode) {
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())
@@ -356,18 +241,6 @@ bool ReplaceFile(const FilePath& from_path,
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);
}
@@ -403,23 +276,6 @@ bool GetTempDir(FilePath* path) {
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;
@@ -831,10 +687,6 @@ int GetMaximumPathComponentLength(const FilePath& path) {
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)
@@ -842,55 +694,4 @@ bool SetNonBlocking(int fd) {
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/logging.cc b/gn/base/logging.cc
index c381a284a89..c2c243f38f0 100644
--- a/gn/base/logging.cc
+++ b/gn/base/logging.cc
@@ -50,7 +50,6 @@
#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)
diff --git a/gn/base/mac/bundle_locations.mm b/gn/base/mac/bundle_locations.mm
deleted file mode 100644
index 54021b85ee0..00000000000
--- a/gn/base/mac/bundle_locations.mm
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/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
deleted file mode 100644
index 6e1ce55cd2c..00000000000
--- a/gn/base/mac/foundation_util.h
+++ /dev/null
@@ -1,406 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_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
deleted file mode 100644
index d88ada8f395..00000000000
--- a/gn/base/mac/foundation_util.mm
+++ /dev/null
@@ -1,475 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/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/numerics/safe_conversions.h b/gn/base/numerics/safe_conversions.h
index a4fe387e8a7..71d6e6113f5 100644
--- a/gn/base/numerics/safe_conversions.h
+++ b/gn/base/numerics/safe_conversions.h
@@ -13,12 +13,7 @@
#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 {
diff --git a/gn/base/numerics/safe_math_clang_gcc_impl.h b/gn/base/numerics/safe_math_clang_gcc_impl.h
index 1760338b089..660a57f2e7f 100644
--- a/gn/base/numerics/safe_math_clang_gcc_impl.h
+++ b/gn/base/numerics/safe_math_clang_gcc_impl.h
@@ -11,12 +11,7 @@
#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 {
diff --git a/gn/base/strings/sys_string_conversions.h b/gn/base/strings/sys_string_conversions.h
deleted file mode 100644
index 9150c05814a..00000000000
--- a/gn/base/strings/sys_string_conversions.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_STRINGS_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
deleted file mode 100644
index 3b78777e5a8..00000000000
--- a/gn/base/strings/sys_string_conversions_mac.mm
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/strings/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
deleted file mode 100644
index 0e1442829a2..00000000000
--- a/gn/base/strings/sys_string_conversions_posix.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/strings/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
deleted file mode 100644
index 232dd782200..00000000000
--- a/gn/base/strings/sys_string_conversions_win.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/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/build/gen.py b/gn/build/gen.py
index 76f88e84224..3edefa7dc60 100755
--- a/gn/build/gen.py
+++ b/gn/build/gen.py
@@ -11,11 +11,9 @@ 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)
@@ -41,6 +39,8 @@ class Platform(object):
self._platform = 'aix'
elif self._platform.startswith('fuchsia'):
self._platform = 'fuchsia'
+ elif self._platform.startswith('freebsd'):
+ self._platform = 'freebsd'
@staticmethod
def known_platforms():
@@ -68,7 +68,7 @@ class Platform(object):
return self._platform == 'aix'
def is_posix(self):
- return self._platform in ['linux', 'darwin', 'aix']
+ return self._platform in ['linux', 'freebsd', 'darwin', 'aix']
def main(argv):
@@ -87,12 +87,12 @@ def main(argv):
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.')
+ parser.add_option('--no-strip', action='store_true',
+ help='Don\'t strip release build. Useful for profiling.')
options, args = parser.parse_args(argv)
if args:
@@ -104,18 +104,13 @@ def main(argv):
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)
+ WriteGNNinja(os.path.join(out_dir, 'build.ninja'), platform, host, options)
return 0
@@ -124,7 +119,7 @@ def GenerateLastCommitPosition(host, header):
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)
+ mo = re.match(ROOT_TAG + '-(\d+)-g([0-9a-f]+)', describe_output.decode())
if not mo:
raise ValueError(
'Unexpected output from git describe when generating version header')
@@ -142,58 +137,14 @@ def GenerateLastCommitPosition(host, header):
# Only write/touch this file if the commit position has changed.
old_contents = ''
if os.path.isfile(header):
- with open(header, 'rb') as f:
+ with open(header, 'r') as f:
old_contents = f.read()
if old_contents != contents:
- with open(header, 'wb') as f:
+ with open(header, 'w') 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=[],
@@ -220,6 +171,7 @@ def WriteGenericNinja(path, static_libraries, executables,
'msvc': 'build_win.ninja.template',
'darwin': 'build_mac.ninja.template',
'linux': 'build_linux.ninja.template',
+ 'freebsd': 'build_linux.ninja.template',
'aix': 'build_aix.ninja.template',
}[platform.platform()])
@@ -259,7 +211,7 @@ def WriteGenericNinja(path, static_libraries, executables,
' '.join(cflags_cc + settings.get('cflags_cc', [])),
])
- for library, settings in static_libraries.iteritems():
+ for library, settings in static_libraries.items():
for src_file in settings['sources']:
build_source(src_file, settings)
@@ -269,7 +221,7 @@ def WriteGenericNinja(path, static_libraries, executables,
ninja_lines.append(' libflags = %s' % ' '.join(libflags))
- for executable, settings in executables.iteritems():
+ for executable, settings in executables.items():
for src_file in settings['sources']:
build_source(src_file, settings)
@@ -298,7 +250,7 @@ def WriteGenericNinja(path, static_libraries, executables,
os.path.relpath(template_filename, os.path.dirname(path)) + '\n')
-def WriteGNNinja(path, platform, host, options, linux_sysroot):
+def WriteGNNinja(path, platform, host, options):
if platform.is_msvc():
cc = os.environ.get('CC', 'cl.exe')
cxx = os.environ.get('CXX', 'cl.exe')
@@ -328,6 +280,8 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
else:
cflags.append('-DNDEBUG')
cflags.append('-O3')
+ if options.no_strip:
+ cflags.append('-g')
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
@@ -341,15 +295,16 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
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')
+ if options.no_strip is None:
+ 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:
+ if options.use_icf and not platform.is_darwin():
ldflags.append('-Wl,--icf=all')
cflags.extend([
@@ -359,30 +314,27 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'-pipe',
'-fno-exceptions',
'-fno-rtti',
+ '-fdiagnostics-color',
])
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',
- ])
+ # This is needed by libc++.
+ libs.append('-ldl')
elif platform.is_darwin():
min_mac_version_flag = '-mmacosx-version-min=10.9'
cflags.append(min_mac_version_flag)
ldflags.append(min_mac_version_flag)
elif platform.is_aix():
cflags_cc.append('-maix64')
- ldflags.extend(['-maix64', '-pthread'])
+ ldflags.append('-maix64')
+
+ if platform.is_posix():
+ ldflags.append('-pthread')
if options.use_lto:
cflags.extend(['-flto', '-fwhole-program-vtables'])
@@ -390,7 +342,7 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
elif platform.is_msvc():
if not options.debug:
- cflags.extend(['/Ox', '/DNDEBUG', '/GL'])
+ cflags.extend(['/O2', '/DNDEBUG', '/GL'])
libflags.extend(['/LTCG'])
ldflags.extend(['/LTCG', '/OPT:REF', '/OPT:ICF'])
@@ -404,7 +356,6 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'/D_UNICODE',
'/D_WIN32_WINNT=0x0A00',
'/FS',
- '/Gy',
'/W4',
'/WX',
'/Zi',
@@ -413,6 +364,7 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'/wd4127',
'/wd4244',
'/wd4267',
+ '/wd4505',
'/wd4838',
'/wd4996',
])
@@ -478,6 +430,7 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'tools/gn/command_format.cc',
'tools/gn/command_gen.cc',
'tools/gn/command_help.cc',
+ 'tools/gn/command_meta.cc',
'tools/gn/command_ls.cc',
'tools/gn/command_path.cc',
'tools/gn/command_refs.cc',
@@ -512,6 +465,7 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'tools/gn/function_template.cc',
'tools/gn/function_toolchain.cc',
'tools/gn/function_write_file.cc',
+ 'tools/gn/generated_file_target_generator.cc',
'tools/gn/group_target_generator.cc',
'tools/gn/header_checker.cc',
'tools/gn/import_manager.cc',
@@ -526,12 +480,15 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'tools/gn/lib_file.cc',
'tools/gn/loader.cc',
'tools/gn/location.cc',
+ 'tools/gn/metadata.cc',
+ 'tools/gn/metadata_walk.cc',
'tools/gn/ninja_action_target_writer.cc',
'tools/gn/ninja_binary_target_writer.cc',
'tools/gn/ninja_build_writer.cc',
'tools/gn/ninja_bundle_data_target_writer.cc',
'tools/gn/ninja_copy_target_writer.cc',
'tools/gn/ninja_create_bundle_target_writer.cc',
+ 'tools/gn/ninja_generated_file_target_writer.cc',
'tools/gn/ninja_group_target_writer.cc',
'tools/gn/ninja_target_command_util.cc',
'tools/gn/ninja_target_writer.cc',
@@ -625,12 +582,15 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'tools/gn/label_pattern_unittest.cc',
'tools/gn/label_unittest.cc',
'tools/gn/loader_unittest.cc',
+ 'tools/gn/metadata_unittest.cc',
+ 'tools/gn/metadata_walk_unittest.cc',
'tools/gn/ninja_action_target_writer_unittest.cc',
'tools/gn/ninja_binary_target_writer_unittest.cc',
'tools/gn/ninja_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_generated_file_target_writer_unittest.cc',
'tools/gn/ninja_group_target_writer_unittest.cc',
'tools/gn/ninja_target_writer_unittest.cc',
'tools/gn/ninja_toolchain_writer_unittest.cc',
@@ -643,6 +603,7 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'tools/gn/runtime_deps_unittest.cc',
'tools/gn/scope_per_file_provider_unittest.cc',
'tools/gn/scope_unittest.cc',
+ 'tools/gn/setup_unittest.cc',
'tools/gn/source_dir_unittest.cc',
'tools/gn/source_file_unittest.cc',
'tools/gn/string_utils_unittest.cc',
@@ -674,32 +635,11 @@ def WriteGNNinja(path, platform, host, options, linux_sysroot):
'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',
diff --git a/gn/docs/faq.md b/gn/docs/faq.md
index 55d8aeffead..699df3da391 100644
--- a/gn/docs/faq.md
+++ b/gn/docs/faq.md
@@ -50,3 +50,9 @@ See [GNCrossCompiles](cross_compiles.md) for more info.
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.
+
+## Are there any public presentations on GN?
+
+[There's at least one](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing), from 2015. There
+haven't been big changes since then apart from moving it to a standalone
+repo, so it should still be relevant.
diff --git a/gn/docs/hacking.md b/gn/docs/hacking.md
deleted file mode 100644
index be3f1328c30..00000000000
--- a/gn/docs/hacking.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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/reference.md b/gn/docs/reference.md
index f2b84f97a19..fe23b196b84 100644
--- a/gn/docs/reference.md
+++ b/gn/docs/reference.md
@@ -5,135 +5,142 @@
## 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)
+ * [analyze: Analyze which targets are affected by a list of files.](#cmd_analyze)
+ * [args: Display or configure arguments declared by the build.](#cmd_args)
+ * [check: Check header dependencies.](#cmd_check)
+ * [clean: Cleans the output directory.](#cmd_clean)
+ * [desc: Show lots of insightful information about a target or config.](#cmd_desc)
+ * [format: Format .gn files.](#cmd_format)
+ * [gen: Generate ninja files.](#cmd_gen)
+ * [help: Does what you think.](#cmd_help)
+ * [ls: List matching targets.](#cmd_ls)
+ * [meta: List target metadata collection results.](#cmd_meta)
+ * [path: Find paths between two targets.](#cmd_path)
+ * [refs: Find stuff referencing a target or file.](#cmd_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)
+ * [action: Declare a target that runs a script a single time.](#func_action)
+ * [action_foreach: Declare a target that runs a script over a set of files.](#func_action_foreach)
+ * [bundle_data: [iOS/macOS] Declare a target without output.](#func_bundle_data)
+ * [copy: Declare a target that copies files.](#func_copy)
+ * [create_bundle: [iOS/macOS] Build an iOS or macOS bundle.](#func_create_bundle)
+ * [executable: Declare an executable target.](#func_executable)
+ * [generated_file: Declare a generated_file target.](#func_generated_file)
+ * [group: Declare a named group of targets.](#func_group)
+ * [loadable_module: Declare a loadable module target.](#func_loadable_module)
+ * [shared_library: Declare a shared library target.](#func_shared_library)
+ * [source_set: Declare a source set target.](#func_source_set)
+ * [static_library: Declare a static library target.](#func_static_library)
+ * [target: Declare an target with the given programmatic type.](#func_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)
+ * [assert: Assert an expression is true at generation time.](#func_assert)
+ * [config: Defines a configuration object.](#func_config)
+ * [declare_args: Declare build arguments.](#func_declare_args)
+ * [defined: Returns whether an identifier is defined.](#func_defined)
+ * [exec_script: Synchronously run a script and return the output.](#func_exec_script)
+ * [foreach: Iterate over a list.](#func_foreach)
+ * [forward_variables_from: Copies variables from a different scope.](#func_forward_variables_from)
+ * [get_label_info: Get an attribute from a target's label.](#func_get_label_info)
+ * [get_path_info: Extract parts of a file or directory name.](#func_get_path_info)
+ * [get_target_outputs: [file list] Get the list of outputs from a target.](#func_get_target_outputs)
+ * [getenv: Get an environment variable.](#func_getenv)
+ * [import: Import a file into the current scope.](#func_import)
+ * [not_needed: Mark variables from scope as not needed.](#func_not_needed)
+ * [pool: Defines a pool object.](#func_pool)
+ * [print: Prints to the console.](#func_print)
+ * [process_file_template: Do template expansion over a list of files.](#func_process_file_template)
+ * [read_file: Read a file into a variable.](#func_read_file)
+ * [rebase_path: Rebase a file or directory to another location.](#func_rebase_path)
+ * [set_default_toolchain: Sets the default toolchain name.](#func_set_default_toolchain)
+ * [set_defaults: Set default values for a target type.](#func_set_defaults)
+ * [set_sources_assignment_filter: Set a pattern to filter source files.](#func_set_sources_assignment_filter)
+ * [split_list: Splits a list into N different sub-lists.](#func_split_list)
+ * [string_replace: Replaces substring in the given string.](#func_string_replace)
+ * [template: Define a template rule.](#func_template)
+ * [tool: Specify arguments to a toolchain tool.](#func_tool)
+ * [toolchain: Defines a toolchain.](#func_toolchain)
+ * [write_file: Write a file to disk.](#func_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)
+ * [current_cpu: [string] The processor architecture of the current toolchain.](#var_current_cpu)
+ * [current_os: [string] The operating system of the current toolchain.](#var_current_os)
+ * [current_toolchain: [string] Label of the current toolchain.](#var_current_toolchain)
+ * [default_toolchain: [string] Label of the default toolchain.](#var_default_toolchain)
+ * [host_cpu: [string] The processor architecture that GN is running on.](#var_host_cpu)
+ * [host_os: [string] The operating system that GN is running on.](#var_host_os)
+ * [invoker: [string] The invoking scope inside a template.](#var_invoker)
+ * [python_path: [string] Absolute path of Python.](#var_python_path)
+ * [root_build_dir: [string] Directory where build commands are run.](#var_root_build_dir)
+ * [root_gen_dir: [string] Directory for the toolchain's generated files.](#var_root_gen_dir)
+ * [root_out_dir: [string] Root directory for toolchain output files.](#var_root_out_dir)
+ * [target_cpu: [string] The desired cpu architecture for the build.](#var_target_cpu)
+ * [target_gen_dir: [string] Directory for a target's generated files.](#var_target_gen_dir)
+ * [target_name: [string] The name of the current target.](#var_target_name)
+ * [target_os: [string] The desired operating system for the build.](#var_target_os)
+ * [target_out_dir: [string] Directory for target output files.](#var_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)
+ * [all_dependent_configs: [label list] Configs to be forced on dependents.](#var_all_dependent_configs)
+ * [allow_circular_includes_from: [label list] Permit includes from deps.](#var_allow_circular_includes_from)
+ * [arflags: [string list] Arguments passed to static_library archiver.](#var_arflags)
+ * [args: [string list] Arguments passed to an action.](#var_args)
+ * [asmflags: [string list] Flags passed to the assembler.](#var_asmflags)
+ * [assert_no_deps: [label pattern list] Ensure no deps on these targets.](#var_assert_no_deps)
+ * [bundle_contents_dir: Expansion of {{bundle_contents_dir}} in create_bundle.](#var_bundle_contents_dir)
+ * [bundle_deps_filter: [label list] A list of labels that are filtered out.](#var_bundle_deps_filter)
+ * [bundle_executable_dir: Expansion of {{bundle_executable_dir}} in create_bundle](#var_bundle_executable_dir)
+ * [bundle_resources_dir: Expansion of {{bundle_resources_dir}} in create_bundle.](#var_bundle_resources_dir)
+ * [bundle_root_dir: Expansion of {{bundle_root_dir}} in create_bundle.](#var_bundle_root_dir)
+ * [cflags: [string list] Flags passed to all C compiler variants.](#var_cflags)
+ * [cflags_c: [string list] Flags passed to the C compiler.](#var_cflags_c)
+ * [cflags_cc: [string list] Flags passed to the C++ compiler.](#var_cflags_cc)
+ * [cflags_objc: [string list] Flags passed to the Objective C compiler.](#var_cflags_objc)
+ * [cflags_objcc: [string list] Flags passed to the Objective C++ compiler.](#var_cflags_objcc)
+ * [check_includes: [boolean] Controls whether a target's files are checked.](#var_check_includes)
+ * [code_signing_args: [string list] Arguments passed to code signing script.](#var_code_signing_args)
+ * [code_signing_outputs: [file list] Output files for code signing step.](#var_code_signing_outputs)
+ * [code_signing_script: [file name] Script for code signing.](#var_code_signing_script)
+ * [code_signing_sources: [file list] Sources for code signing step.](#var_code_signing_sources)
+ * [complete_static_lib: [boolean] Links all deps into a static library.](#var_complete_static_lib)
+ * [configs: [label list] Configs applying to this target or config.](#var_configs)
+ * [contents: Contents to write to file.](#var_contents)
+ * [data: [file list] Runtime data file dependencies.](#var_data)
+ * [data_deps: [label list] Non-linked dependencies.](#var_data_deps)
+ * [data_keys: [string list] Keys from which to collect metadata.](#var_data_keys)
+ * [defines: [string list] C preprocessor defines.](#var_defines)
+ * [depfile: [string] File name for input dependencies for actions.](#var_depfile)
+ * [deps: [label list] Private linked dependencies.](#var_deps)
+ * [friend: [label pattern list] Allow targets to include private headers.](#var_friend)
+ * [include_dirs: [directory list] Additional include directories.](#var_include_dirs)
+ * [inputs: [file list] Additional compile-time dependencies.](#var_inputs)
+ * [ldflags: [string list] Flags passed to the linker.](#var_ldflags)
+ * [lib_dirs: [directory list] Additional library directories.](#var_lib_dirs)
+ * [libs: [string list] Additional libraries to link.](#var_libs)
+ * [metadata: [scope] Metadata of this target.](#var_metadata)
+ * [output_conversion: Data format for generated_file targets.](#var_output_conversion)
+ * [output_dir: [directory] Directory to put output file in.](#var_output_dir)
+ * [output_extension: [string] Value to use for the output's file extension.](#var_output_extension)
+ * [output_name: [string] Name for the output file other than the default.](#var_output_name)
+ * [output_prefix_override: [boolean] Don't use prefix for output name.](#var_output_prefix_override)
+ * [outputs: [file list] Output files for actions and copy targets.](#var_outputs)
+ * [partial_info_plist: [filename] Path plist from asset catalog compiler.](#var_partial_info_plist)
+ * [pool: [string] Label of the pool used by the action.](#var_pool)
+ * [precompiled_header: [string] Header file to precompile.](#var_precompiled_header)
+ * [precompiled_header_type: [string] "gcc" or "msvc".](#var_precompiled_header_type)
+ * [precompiled_source: [file name] Source file to precompile.](#var_precompiled_source)
+ * [product_type: [string] Product type for Xcode projects.](#var_product_type)
+ * [public: [file list] Declare public header files for a target.](#var_public)
+ * [public_configs: [label list] Configs applied to dependents.](#var_public_configs)
+ * [public_deps: [label list] Declare public dependencies.](#var_public_deps)
+ * [rebase: [boolean] Rebase collected metadata as files.](#var_rebase)
+ * [response_file_contents: [string list] Contents of .rsp file for actions.](#var_response_file_contents)
+ * [script: [file name] Script file for actions.](#var_script)
+ * [sources: [file list] Source files for a target.](#var_sources)
+ * [testonly: [boolean] Declares a target must only be used for testing.](#var_testonly)
+ * [visibility: [label list] A list of labels that can depend on a target.](#var_visibility)
+ * [walk_keys: [string list] Key(s) for managing the metadata collection walk.](#var_walk_keys)
+ * [write_runtime_deps: Writes the target's runtime_deps to the given path.](#var_write_runtime_deps)
+ * [xcode_extra_attributes: [scope] Extra attributes for Xcode projects.](#var_xcode_extra_attributes)
+ * [xcode_test_application_name: [string] Name for Xcode test target.](#var_xcode_test_application_name)
* [Other help topics](#other)
- * [all: Print all the help at once](#all)
+ * all: Print all the help at once
* [buildargs: How build arguments work.](#buildargs)
* [dotfile: Info about the toplevel .gn file.](#dotfile)
* [execution: Build graph and execution overview.](#execution)
@@ -146,11 +153,11 @@
* [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)
+ * [switches: Show available command-line switches.](#switch_list)
## <a name="commands"></a>Commands
-### <a name="analyze"></a>**gn analyze <out_dir> <input_path> <output_path>**
+### <a name="cmd_analyze"></a>**gn analyze &lt;out_dir&gt; &lt;input_path&gt; &lt;output_path&gt;**
```
Analyze which targets are affected by a list of files.
@@ -220,9 +227,13 @@
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]**
+### <a name="cmd_args"></a>**gn args**: (command-line tool)
```
+ Display or configure arguments declared by the build.
+
+ gn args <out_dir> [--list] [--short] [--args] [--overrides-only]
+
See also "gn help buildargs" for a more high-level overview of how
build arguments work.
```
@@ -305,7 +316,7 @@
given arguments set (which may affect the values of other
arguments).
```
-### <a name="check"></a>**gn check <out_dir> [<label_pattern>] [\--force]**
+### <a name="cmd_check"></a>**gn check &lt;out_dir&gt; [&lt;label_pattern&gt;] [\--force] [\--check-generated]**
```
GN's include header checker validates that the includes for C-like source
@@ -326,14 +337,19 @@
--force
Ignores specifications of "check_includes = false" and checks all
target's files that match the target label.
+
+ --check-generated
+ Generated files are normally not checked since they do not exist
+ until after a build. With this flag, those generated files that
+ can be found on disk are also checked.
```
#### **What gets checked**
```
- The .gn file may specify a list of targets to be checked. 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".
+ The .gn file may specify a list of targets to be checked in the list
+ check_targets (see "gn help dotfile"). If a label pattern is specified
+ on the command line, check_targets is not used.
Targets can opt-out from checking with "check_includes = false" (see
"gn help check_includes").
@@ -343,6 +359,9 @@
- GN opens all C-like source files in the targets to be checked and scans
the top for includes.
+ - Generated files (that might not exist yet) are ignored unless
+ the --check-generated flag is provided.
+
- Includes with a "nogncheck" annotation are skipped (see
"gn help nogncheck").
@@ -420,16 +439,18 @@
gn check out/Default "//foo/*
Check only the files in targets in the //foo directory tree.
```
-### <a name="clean"></a>**gn clean <out_dir>**
+### <a name="cmd_clean"></a>**gn clean &lt;out_dir&gt;**
```
Deletes the contents of the output directory except for args.gn and
creates a Ninja build environment sufficient to regenerate the build.
```
-### <a name="desc"></a>**gn desc <out_dir> <label or pattern> [<what to show>] [\--blame] "**
-#### **[\--format=json]**
+### <a name="cmd_desc"></a>**gn desc**
```
+ gn desc <out_dir> <label or pattern> [<what to show>] [--blame]
+ [--format=json]
+
Displays information about a given target or config. The build parameters
will be taken for the build in the given <out_dir>.
@@ -438,7 +459,7 @@
targets.
```
-#### **Possibilities for <what to show>**
+#### **Possibilities for &lt;what to show&gt;**
```
(If unspecified an overall summary will be displayed.)
@@ -452,6 +473,7 @@
cflags_cc [--blame]
check_includes
configs [--tree] (see below)
+ data_keys
defines [--blame]
depfile
deps [--all] [--tree] (see below)
@@ -460,13 +482,17 @@
ldflags [--blame]
lib_dirs
libs
+ metadata
+ output_conversion
outputs
public_configs
public
+ rebase
script
sources
testonly
visibility
+ walk_keys
runtime_deps
Compute all runtime deps for the given target. This is a computed list
@@ -585,7 +611,7 @@
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>)**
+### <a name="cmd_format"></a>**gn format [\--dump-tree] (\--stdin | &lt;list of build_files...&gt;)**
```
Formats .gn file to a standard format.
@@ -612,9 +638,9 @@
- 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.
+ --dump-tree[=( text | json )]
+ Dumps the parse tree to stdout and does not update the file or print
+ formatted output. If no format is specified, text format will be used.
--stdin
Read input from stdin and write to stdout rather than update a file
@@ -623,12 +649,12 @@
#### **Examples**
```
- gn format //some/BUILD.gn
+ gn format //some/BUILD.gn //some/other/BUILD.gn //and/another/BUILD.gn
gn format some\\BUILD.gn
gn format /abspath/some/BUILD.gn
gn format --stdin
```
-### <a name="gen"></a>**gn gen [\--check] [<ide options>] <out_dir>**
+### <a name="cmd_gen"></a>**gn gen [\--check] [&lt;ide options&gt;] &lt;out_dir&gt;**
```
Generates ninja files from the current tree and puts them in the given output
@@ -658,6 +684,7 @@
"vs2013" - Visual Studio 2013 project/solution files.
"vs2015" - Visual Studio 2015 project/solution files.
"vs2017" - Visual Studio 2017 project/solution files.
+ "vs2019" - Visual Studio 2019 project/solution files.
"xcode" - Xcode workspace/solution files.
"qtcreator" - QtCreator project files.
"json" - JSON file containing target information
@@ -758,7 +785,7 @@
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>**
+### <a name="cmd_help"></a>**gn help &lt;anything&gt;**
```
Yo dawg, I heard you like help on your help so I put help on the help in the
@@ -780,7 +807,7 @@
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=...]**
+### <a name="cmd_ls"></a>**gn ls &lt;out_dir&gt; [&lt;label_pattern&gt;] [\--all-toolchains] [\--as=...]**
```
[--type=...] [--testonly=...]
@@ -853,7 +880,64 @@
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>**
+### <a name="cmd_meta"></a>**gn meta**
+
+```
+ gn meta <out_dir> <target>* --data=<key>[,<key>*]* [--walk=<key>[,<key>*]*]
+ [--rebase=<dest dir>]
+
+ Lists collected metaresults of all given targets for the given data key(s),
+ collecting metadata dependencies as specified by the given walk key(s).
+
+ See `gn help generated_file` for more information on the walk.
+```
+
+#### **Arguments**
+
+```
+ <target(s)>
+ A list of target labels from which to initiate the walk.
+
+ --data
+ A list of keys from which to extract data. In each target walked, its metadata
+ scope is checked for the presence of these keys. If present, the contents of
+ those variable in the scope are appended to the results list.
+
+ --walk (optional)
+ A list of keys from which to control the walk. In each target walked, its
+ metadata scope is checked for the presence of any of these keys. If present,
+ the contents of those variables is checked to ensure that it is a label of
+ a valid dependency of the target and then added to the set of targets to walk.
+ If the empty string ("") is present in any of these keys, all deps and data_deps
+ are added to the walk set.
+
+ --rebase (optional)
+ A destination directory onto which to rebase any paths found. If set, all
+ collected metadata will be rebased onto this path. This option will throw errors
+ if collected metadata is not a list of strings.
+```
+
+#### **Examples**
+
+```
+ gn meta out/Debug "//base/foo" --data=files
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of its dependency tree.
+
+ gn meta out/Debug "//base/foo" --data=files --data=other
+ Lists collected metaresults for the `files` and `other` keys in the
+ //base/foo:foo target and all of its dependency tree.
+
+ gn meta out/Debug "//base/foo" --data=files --walk=stop
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of the dependencies listed in the `stop` key (and so on).
+
+ gn meta out/Debug "//base/foo" --data=files --rebase="/"
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of its dependency tree, rebasing the strings in the `files`
+ key onto the source directory of the target's declaration relative to "/".
+```
+### <a name="cmd_path"></a>**gn path &lt;out_dir&gt; &lt;target_one&gt; &lt;target_two&gt;**
```
Finds paths of dependencies between two targets. Each unique path will be
@@ -898,9 +982,11 @@
```
gn path out/Default //base //tools/gn
```
-### <a name="refs"></a>**gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)***
+### <a name="cmd_refs"></a>**gn refs**
+
```
- [--all] [--all-toolchains] [--as=...] [--testonly=...] [--type=...]
+ 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:
@@ -1023,7 +1109,7 @@
```
## <a name="targets"></a>Target declarations
-### <a name="action"></a>**action**: Declare a target that runs a script a single time.
+### <a name="func_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
@@ -1087,7 +1173,7 @@
#### **Variables**
```
- args, data, data_deps, depfile, deps, inputs, outputs*, pool,
+ args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
response_file_contents, script*, sources
* = required
```
@@ -1109,7 +1195,7 @@
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.
+### <a name="func_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
@@ -1168,7 +1254,7 @@
#### **Variables**
```
- args, data, data_deps, depfile, deps, inputs, outputs*, pool,
+ args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
response_file_contents, script*, sources*
* = required
```
@@ -1200,7 +1286,7 @@
"/{{source_name_part}}.h" ]
}
```
-### <a name="bundle_data"></a>**bundle_data**: [iOS/macOS] Declare a target without output.
+### <a name="func_bundle_data"></a>**bundle_data**: [iOS/macOS] Declare a target without output.
```
This target type allows to declare data that is required at runtime. It is
@@ -1221,7 +1307,7 @@
#### **Variables**
```
- sources*, outputs*, deps, data_deps, public_deps, visibility
+ sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
* = required
```
@@ -1254,7 +1340,7 @@
]
}
```
-### <a name="copy"></a>**copy**: Declare a target that copies files.
+### <a name="func_copy"></a>**copy**: Declare a target that copies files.
#### **File name handling**
@@ -1291,7 +1377,7 @@
outputs = [ "$target_gen_dir/{{source_file_part}}" ]
}
```
-### <a name="create_bundle"></a>**create_bundle**: [ios/macOS] Build an iOS or macOS bundle.
+### <a name="func_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
@@ -1299,8 +1385,9 @@
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.
+ The "bundle_*_dir" are be used for the expansion of {{bundle_*_dir}} rules in
+ "bundle_data" outputs. The properties are optional but must be defined if any
+ of the "bundle_data" target use them.
This target can be used on all platforms though it is designed only to
generate iOS or macOS bundle. In cross-platform projects, it is advised to put
@@ -1334,12 +1421,11 @@
#### **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
+ bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
+ bundle_executable_dir, bundle_deps_filter, deps, data_deps, public_deps,
+ visibility, product_type, code_signing_args, code_signing_script,
+ code_signing_sources, code_signing_outputs, xcode_extra_attributes,
+ xcode_test_application_name, partial_info_plist, metadata
```
#### **Example**
@@ -1366,7 +1452,7 @@
}
bundle_data("${app_name}_bundle_info_plist") {
- deps = [ ":${app_name}_generate_info_plist" ]
+ public_deps = [ ":${app_name}_generate_info_plist" ]
sources = [ "$gen_path/Info.plist" ]
outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
}
@@ -1383,34 +1469,32 @@
code_signing =
defined(invoker.code_signing) && invoker.code_signing
- if (is_ios && !code_signing) {
+ if (!is_ios || !code_signing) {
bundle_data("${app_name}_bundle_executable") {
- deps = [ ":${app_name}_generate_executable" ]
+ public_deps = [ ":${app_name}_generate_executable" ]
sources = [ "$gen_path/$app_name" ]
outputs = [ "{{bundle_executable_dir}}/$app_name" ]
}
}
- create_bundle("${app_name}.app") {
+ create_bundle("$app_name.app") {
product_type = "com.apple.product-type.application"
if (is_ios) {
- bundle_root_dir = "${root_build_dir}/$target_name"
+ 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"
+ bundle_root_dir = "$root_build_dir/$target_name"
+ bundle_contents_dir = "$bundle_root_dir/Contents"
+ bundle_resources_dir = "$bundle_contents_dir/Resources"
+ bundle_executable_dir = "$bundle_contents_dir/MacOS"
}
deps = [ ":${app_name}_bundle_info_plist" ]
if (is_ios && code_signing) {
@@ -1443,7 +1527,7 @@
}
}
```
-### <a name="executable"></a>**executable**: Declare an executable target.
+### <a name="func_executable"></a>**executable**: Declare an executable target.
#### **Variables**
@@ -1453,10 +1537,144 @@
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
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
```
-### <a name="group"></a>**group**: Declare a named group of targets.
+### <a name="func_generated_file"></a>**generated_file**: Declare a generated_file target.
+
+```
+ Writes data value(s) to disk on resolution. This target type mirrors some
+ functionality of the write_file() function, but also provides the ability to
+ collect metadata from its dependencies on resolution rather than writing out
+ parse time.
+
+ The `outputs` variable is required to be a list with a single element,
+ specifying the intended location of the output file.
+
+ The `output_conversion` variable specified the format to write the
+ value. See `gn help output_conversion`.
+
+ One of `contents` or `data_keys` must be specified; use of `data` will write
+ the contents of that value to file, while use of `data_keys` will trigger a
+ metadata collection walk based on the dependencies of the target and the
+ optional values of the `rebase` and `walk_keys` variables. See
+ `gn help metadata`.
+
+ Collected metadata, if specified, will be returned in postorder of
+ dependencies. See the example for details.
+```
+
+#### **Example (metadata collection)**
+
+```
+ Given the following targets defined in //base/BUILD.gn, where A depends on B
+ and B depends on C and D:
+
+ group("a") {
+ metadata = {
+ doom_melon = [ "enable" ]
+ my_files = [ "foo.cpp" ]
+
+ // Note: this is functionally equivalent to not defining `my_barrier`
+ // at all in this target's metadata.
+ my_barrier = [ "" ]
+ }
+
+ deps = [ ":b" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "bar.cpp" ]
+ my_barrier = [ ":c" ]
+ }
+
+ deps = [ ":c", ":d" ]
+ }
+
+ group("c") {
+ metadata = {
+ doom_melon = [ "disable" ]
+ my_files = [ "baz.cpp" ]
+ }
+ }
+
+ group("d") {
+ metadata = {
+ my_files = [ "missing.cpp" ]
+ }
+ }
+
+ If the following generated_file target is defined:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (less the
+ comments):
+ [
+ "baz.cpp", // from //base:c via //base:b
+ "missing.cpp" // from //base:d via //base:b
+ "bar.cpp", // from //base:b via //base:a
+ "foo.cpp", // from //base:a
+ ]
+
+ Alternatively, as an example of using walk_keys, if the following
+ generated_file target is defined:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_barrier" ]
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (again less
+ the comments):
+ [
+ "baz.cpp", // from //base:c via //base:b
+ "bar.cpp", // from //base:b via //base:a
+ "foo.cpp", // from //base:a
+ ]
+
+ If `rebase` is used in the following generated_file target:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_barrier" ]
+ rebase = root_build_dir
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (again less
+ the comments) (assuming root_build_dir = "//out"):
+ [
+ "../base/baz.cpp", // from //base:c via //base:b
+ "../base/bar.cpp", // from //base:b via //base:a
+ "../base/foo.cpp", // from //base:a
+ ]
+```
+
+#### **Variables**
+
+```
+ contents
+ data_keys
+ rebase
+ walk_keys
+ output_conversion
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+```
+### <a name="func_group"></a>**group**: Declare a named group of targets.
```
This target type allows you to create meta-targets that just collect a set of
@@ -1481,7 +1699,7 @@
]
}
```
-### <a name="loadable_module"></a>**loadable_module**: Declare a loadable module target.
+### <a name="func_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
@@ -1501,10 +1719,11 @@
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
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
```
-### <a name="shared_library"></a>**shared_library**: Declare a shared library target.
+### <a name="func_shared_library"></a>**shared_library**: Declare a shared library target.
```
A shared library will be specified on the linker line for targets listing the
@@ -1522,10 +1741,11 @@
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
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
```
-### <a name="source_set"></a>**source_set**: Declare a source set target.
+### <a name="func_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
@@ -1558,10 +1778,11 @@
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
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
```
-### <a name="static_library"></a>**static_library**: Declare a static library target.
+### <a name="func_static_library"></a>**static_library**: Declare a static library target.
```
Make a ".a" / ".lib" file.
@@ -1580,10 +1801,11 @@
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
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
```
-### <a name="target"></a>**target**: Declare an target with the given programmatic type.
+### <a name="func_target"></a>**target**: Declare an target with the given programmatic type.
```
target(target_type_string, target_name_string) { ... }
@@ -1617,7 +1839,7 @@
```
## <a name="functions"></a>Buildfile functions
-### <a name="assert"></a>**assert**: Assert an expression is true at generation time.
+### <a name="func_assert"></a>**assert**: Assert an expression is true at generation time.
```
assert(<condition> [, <error string>])
@@ -1633,7 +1855,7 @@
assert(is_win)
assert(defined(sources), "Sources must be defined");
```
-### <a name="config"></a>**config**: Defines a configuration object.
+### <a name="func_config"></a>**config**: Defines a configuration object.
```
Configuration objects can be applied to targets and specify sets of compiler
@@ -1654,7 +1876,26 @@
tree in the order that the targets appear in "deps".
```
+#### **More background**
+
+```
+ Configs solve a problem where the build system needs to have a higher-level
+ understanding of various compiler settings. For example, some compiler flags
+ have to appear in a certain order relative to each other, some settings like
+ defines and flags logically go together, and the build system needs to
+ de-duplicate flags even though raw command-line parameters can't always be
+ operated on in that way.
+
+ The config gives a name to a group of settings that can then be reasoned
+ about by GN. GN can know that configs with the same label are the same thing
+ so can be de-duplicated. It allows related settings to be grouped so they
+ are added or removed as a unit. And it allows targets to refer to settings
+ with conceptual names ("no_rtti", "enable_exceptions", etc.) rather than
+ having to hard-coding every compiler's flags each time they are referred to.
+```
+
#### **Variables valid in a config definition**
+
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
@@ -1680,7 +1921,7 @@
configs = [ ":myconfig" ]
}
```
-### <a name="declare_args"></a>**declare_args**: Declare build arguments.
+### <a name="func_declare_args"></a>**declare_args**: Declare build arguments.
```
Introduces the given arguments into the current scope. If they are not
@@ -1740,7 +1981,7 @@
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.
+### <a name="func_defined"></a>**defined**: Returns whether an identifier is defined.
```
Returns true if the given argument is defined. This is most useful in
@@ -1774,7 +2015,7 @@
}
}
```
-### <a name="exec_script"></a>**exec_script**: Synchronously run a script and return the output.
+### <a name="func_exec_script"></a>**exec_script**: Synchronously run a script and return the output.
```
exec_script(filename,
@@ -1790,14 +2031,18 @@
directory. If you are passing file names, you will want to use the
rebase_path() function to make file names relative to this path (see "gn help
rebase_path").
+
+ The default script interpreter is Python ("python" on POSIX, "python.exe" or
+ "python.bat" on Windows). This can be configured by the script_executable
+ variable, see "gn help dotfile".
```
#### **Arguments**:
```
filename:
- File name of python script to execute. Non-absolute names will be treated
- as relative to the current build file.
+ File name of script to execute. Non-absolute names will be treated as
+ relative to the current build file.
arguments:
A list of strings to be passed to the script as arguments. May be
@@ -1830,7 +2075,7 @@
# result.
exec_script("//foo/bar/myscript.py")
```
-### <a name="foreach"></a>**foreach**: Iterate over a list.
+### <a name="func_foreach"></a>**foreach**: Iterate over a list.
```
foreach(<loop_var>, <list>) {
@@ -1864,7 +2109,7 @@
b
c
```
-### <a name="forward_variables_from"></a>**forward_variables_from**: Copies variables from a different scope.
+### <a name="func_forward_variables_from"></a>**forward_variables_from**: Copies variables from a different scope.
```
forward_variables_from(from_scope, variable_list_or_star,
@@ -1901,6 +2146,13 @@
#### **Examples**
```
+ # forward_variables_from(invoker, ["foo"])
+ # is equivalent to:
+ assert(!defined(foo))
+ if (defined(invoker.foo)) {
+ foo = invoker.foo
+ }
+
# This is a common action template. It would invoke a script with some given
# parameters, and wants to use the various types of deps and the visibility
# from the invoker if it's defined. It also injects an additional dependency
@@ -1939,7 +2191,7 @@
}
}
```
-### <a name="get_label_info"></a>**get_label_info**: Get an attribute from a target's label.
+### <a name="func_get_label_info"></a>**get_label_info**: Get an attribute from a target's label.
```
get_label_info(target_label, what)
@@ -2003,7 +2255,7 @@
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.
+### <a name="func_get_path_info"></a>**get_path_info**: Extract parts of a file or directory name.
```
get_path_info(input, what)
@@ -2081,9 +2333,9 @@
# result will be "//foo/bar"
# Extract the source-absolute directory name,
- result = get_path_info(get_path_info(path, "dir"), "abspath"
+ 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.
+### <a name="func_get_target_outputs"></a>**get_target_outputs**: [file list] Get the list of outputs from a target.
```
get_target_outputs(target_label)
@@ -2140,7 +2392,7 @@
sources = get_target_outputs(":my_action")
}
```
-### <a name="getenv"></a>**getenv**: Get an environment variable.
+### <a name="func_getenv"></a>**getenv**: Get an environment variable.
```
value = getenv(env_var_name)
@@ -2160,7 +2412,7 @@
```
home_dir = getenv("HOME")
```
-### <a name="import"></a>**import**: Import a file into the current scope.
+### <a name="func_import"></a>**import**: Import a file into the current scope.
```
The import command loads the rules and variables resulting from executing the
@@ -2195,7 +2447,7 @@
# Looks in the current directory.
import("my_vars.gni")
```
-### <a name="not_needed"></a>**not_needed**: Mark variables from scope as not needed.
+### <a name="func_not_needed"></a>**not_needed**: Mark variables from scope as not needed.
```
not_needed(variable_list_or_star, variable_to_ignore_list = [])
@@ -2216,7 +2468,7 @@
not_needed(invoker, "*", [ "config" ])
not_needed(invoker, [ "data_deps", "deps" ])
```
-### <a name="pool"></a>**pool**: Defines a pool object.
+### <a name="func_pool"></a>**pool**: Defines a pool object.
```
Pool objects can be applied to a tool to limit the parallelism of the
@@ -2260,7 +2512,7 @@
}
}
```
-### <a name="print"></a>**print**: Prints to the console.
+### <a name="func_print"></a>**print**: Prints to the console.
```
Prints all arguments to the console separated by spaces. A newline is
@@ -2280,7 +2532,7 @@
print(sources, deps)
```
-### <a name="process_file_template"></a>**process_file_template**: Do template expansion over a list of files.
+### <a name="func_process_file_template"></a>**process_file_template**: Do template expansion over a list of files.
```
process_file_template(source_list, template)
@@ -2325,7 +2577,7 @@
"//out/Debug/bar.cc"
"//out/Debug/bar.h" ]
```
-### <a name="read_file"></a>**read_file**: Read a file into a variable.
+### <a name="func_read_file"></a>**read_file**: Read a file into a variable.
```
read_file(filename, input_conversion)
@@ -2349,7 +2601,7 @@
```
lines = read_file("foo.txt", "list lines")
```
-### <a name="rebase_path"></a>**rebase_path**: Rebase a file or directory to another location.
+### <a name="func_rebase_path"></a>**rebase_path**: Rebase a file or directory to another location.
```
converted = rebase_path(input,
@@ -2445,7 +2697,7 @@
] + rebase_path(sources, root_build_dir)
}
```
-### <a name="set_default_toolchain"></a>**set_default_toolchain**: Sets the default toolchain name.
+### <a name="func_set_default_toolchain"></a>**set_default_toolchain**: Sets the default toolchain name.
```
set_default_toolchain(toolchain_label)
@@ -2485,7 +2737,7 @@
set_default_toolchain("//toolchains:32")
}
```
-### <a name="set_defaults"></a>**set_defaults**: Set default values for a target type.
+### <a name="func_set_defaults"></a>**set_defaults**: Set default values for a target type.
```
set_defaults(<target_type_name>) { <values...> }
@@ -2513,13 +2765,13 @@
configs = [ "//tools/mything:settings" ]
}
- static_library("mylib")
+ 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.
+### <a name="func_set_sources_assignment_filter"></a>**set_sources_assignment_filter**: Set a pattern to filter source files.
```
The sources assignment filter is a list of patterns that remove files from
@@ -2583,7 +2835,7 @@
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.
+### <a name="func_split_list"></a>**split_list**: Splits a list into N different sub-lists.
```
result = split_list(input, n)
@@ -2606,7 +2858,7 @@
Will print:
[[1, 2], [3, 4], [5, 6]
```
-### <a name="string_replace"></a>**string_replace**: Replaces substring in the given string.
+### <a name="func_string_replace"></a>**string_replace**: Replaces substring in the given string.
```
result = string_replace(str, old, new[, max])
@@ -2627,7 +2879,7 @@
Will print:
Hello, GN!
```
-### <a name="template"></a>**template**: Define a template rule.
+### <a name="func_template"></a>**template**: Define a template rule.
```
A template defines a custom name that acts like a function. It provides a way
@@ -2790,7 +3042,7 @@
deps = [ ":foo_idl_files" ]
}
```
-### <a name="tool"></a>**tool**: Specify arguments to a toolchain tool.
+### <a name="func_tool"></a>**tool**: Specify arguments to a toolchain tool.
#### **Usage**
@@ -3257,7 +3509,7 @@
}
};
```
-### <a name="toolchain"></a>**toolchain**: Defines a toolchain.
+### <a name="func_toolchain"></a>**toolchain**: Defines a toolchain.
```
A toolchain is a set of commands and build flags used to compile the source
@@ -3411,7 +3663,7 @@
}
}
```
-### <a name="write_file"></a>**write_file**: Write a file to disk.
+### <a name="func_write_file"></a>**write_file**: Write a file to disk.
```
write_file(filename, data, output_conversion = "")
@@ -3442,7 +3694,7 @@
```
## <a name="predefined_variables"></a>Built-in predefined variables
-### <a name="current_cpu"></a>**current_cpu**: The processor architecture of the current toolchain.
+### <a name="var_current_cpu"></a>**current_cpu**: The processor architecture of the current toolchain.
```
The build configuration usually sets this value based on the value of
@@ -3456,7 +3708,7 @@
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.
+### <a name="var_current_os"></a>**current_os**: The operating system of the current toolchain.
```
The build configuration usually sets this value based on the value of
@@ -3470,7 +3722,7 @@
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 name="var_current_toolchain"></a>**current_toolchain**: Label of the current toolchain.
```
A fully-qualified label representing the current toolchain. You can use this
@@ -3485,13 +3737,13 @@
executable("output_thats_64_bit_only") {
...
```
-### <a name="default_toolchain"></a>**default_toolchain**: [string] Label of the default toolchain.
+### <a name="var_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.
+### <a name="var_host_cpu"></a>**host_cpu**: The processor architecture that GN is running on.
```
This is value is exposed so that cross-compile toolchains can access the host
@@ -3509,7 +3761,7 @@
- "x64"
- "x86"
```
-### <a name="host_os"></a>**host_os**: [string] The operating system that GN is running on.
+### <a name="var_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
@@ -3526,7 +3778,7 @@
- "mac"
- "win"
```
-### <a name="invoker"></a>**invoker**: [string] The invoking scope inside a template.
+### <a name="var_invoker"></a>**invoker**: [string] The invoking scope inside a template.
```
Inside a template invocation, this variable refers to the scope of the
@@ -3557,14 +3809,14 @@
bar = 123
}
```
-### <a name="python_path"></a>**python_path**: Absolute path of Python.
+### <a name="var_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.
+### <a name="var_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
@@ -3573,7 +3825,7 @@
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.
+### <a name="var_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
@@ -3588,7 +3840,7 @@
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.
+### <a name="var_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
@@ -3614,7 +3866,7 @@
args = [ "-o", rebase_path(root_out_dir, root_build_dir) ]
}
```
-### <a name="target_cpu"></a>**target_cpu**: The desired cpu architecture for the build.
+### <a name="var_target_cpu"></a>**target_cpu**: The desired cpu architecture for the build.
```
This value should be used to indicate the desired architecture for the
@@ -3643,7 +3895,7 @@
- "arm64"
- "mipsel"
```
-### <a name="target_gen_dir"></a>**target_gen_dir**: Directory for a target's generated files.
+### <a name="var_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
@@ -3667,7 +3919,7 @@
args = [ "-o", rebase_path(target_gen_dir, root_build_dir) ]"
}
```
-### <a name="target_name"></a>**target_name**: [string] The name of the current target.
+### <a name="var_target_name"></a>**target_name**: [string] The name of the current target.
```
Inside a target or template invocation, this variable refers to the name
@@ -3706,7 +3958,7 @@
my_template("space_ray") {
}
```
-### <a name="target_os"></a>**target_os**: The desired operating system for the build.
+### <a name="var_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
@@ -3747,7 +3999,7 @@
- "mac"
- "win"
```
-### <a name="target_out_dir"></a>**target_out_dir**: [string] Directory for target output files.
+### <a name="var_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
@@ -3768,12 +4020,11 @@
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 name="var_all_dependent_configs"></a>**all_dependent_configs**: Configs to be forced on dependents.
```
A list of config labels.
@@ -3809,7 +4060,7 @@
"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 name="var_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
@@ -3882,7 +4133,7 @@
public_deps = [ ":c" ]
}
```
-### <a name="arflags"></a>**arflags**: Arguments passed to static_library archiver.
+### <a name="var_arflags"></a>**arflags**: Arguments passed to static_library archiver.
```
A list of flags passed to the archive/lib command that creates static
@@ -3915,7 +4166,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="args"></a>**args**: Arguments passed to an action.
+### <a name="var_args"></a>**args**: (target variable) Arguments passed to an action.
```
For action and action_foreach targets, args is the list of arguments to pass
@@ -3924,7 +4175,7 @@
See also "gn help action" and "gn help action_foreach".
```
-### <a name="asmflags"></a>**asmflags**: Flags passed to the assembler.
+### <a name="var_asmflags"></a>**asmflags**: Flags passed to the assembler.
```
A list of strings.
@@ -3950,7 +4201,7 @@
"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 name="var_assert_no_deps"></a>**assert_no_deps**: Ensure no deps on these targets.
```
A list of label patterns.
@@ -3990,7 +4241,7 @@
]
}
```
-### <a name="bundle_contents_dir"></a>**bundle_contents_dir**: Expansion of {{bundle_contents_dir}} in
+### <a name="var_bundle_contents_dir"></a>**bundle_contents_dir**: Expansion of {{bundle_contents_dir}} in
```
create_bundle.
@@ -4002,7 +4253,7 @@
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 name="var_bundle_deps_filter"></a>**bundle_deps_filter**: [label list] A list of labels that are filtered out.
```
A list of target labels.
@@ -4033,9 +4284,11 @@
]
}
```
-### <a name="bundle_executable_dir"></a>**bundle_executable_dir**: Expansion of {{bundle_executable_dir}} in
+### <a name="var_bundle_executable_dir"></a>**bundle_executable_dir**
+
```
- create_bundle.
+ bundle_executable_dir: Expansion of {{bundle_executable_dir}} in
+ create_bundle.
A string corresponding to a path in $root_build_dir.
@@ -4045,20 +4298,11 @@
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 name="var_bundle_resources_dir"></a>**bundle_resources_dir**
```
- 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.
+ bundle_resources_dir: Expansion of {{bundle_resources_dir}} in
+ create_bundle.
A string corresponding to a path in $root_build_dir.
@@ -4068,7 +4312,7 @@
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 name="var_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.
@@ -4092,10 +4336,9 @@
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 name="var_cflags"></a>**cflags***: Flags passed to the C compiler.
```
A list of strings.
@@ -4128,7 +4371,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="cflags*"></a>**cflags***: Flags passed to the C compiler.
+### <a name="var_cflags_c"></a>**cflags***: Flags passed to the C compiler.
```
A list of strings.
@@ -4161,7 +4404,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="cflags*"></a>**cflags***: Flags passed to the C compiler.
+### <a name="var_cflags_cc"></a>**cflags***: Flags passed to the C compiler.
```
A list of strings.
@@ -4194,7 +4437,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="cflags*"></a>**cflags***: Flags passed to the C compiler.
+### <a name="var_cflags_objc"></a>**cflags***: Flags passed to the C compiler.
```
A list of strings.
@@ -4227,7 +4470,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="cflags*"></a>**cflags***: Flags passed to the C compiler.
+### <a name="var_cflags_objcc"></a>**cflags***: Flags passed to the C compiler.
```
A list of strings.
@@ -4260,7 +4503,7 @@
"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.
+### <a name="var_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
@@ -4288,7 +4531,7 @@
...
}
```
-### <a name="code_signing_args"></a>**code_signing_args**: [string list] Arguments passed to code signing script.
+### <a name="var_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
@@ -4297,7 +4540,7 @@
See also "gn help create_bundle".
```
-### <a name="code_signing_outputs"></a>**code_signing_outputs**: [file list] Output files for code signing step.
+### <a name="var_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
@@ -4305,7 +4548,7 @@
See also "gn help create_bundle".
```
-### <a name="code_signing_script"></a>**code_signing_script**: [file name] Script for code signing."
+### <a name="var_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
@@ -4313,7 +4556,7 @@
See also "gn help create_bundle".
```
-### <a name="code_signing_sources"></a>**code_signing_sources**: [file list] Sources for code signing step.
+### <a name="var_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
@@ -4322,7 +4565,7 @@
See also "gn help create_bundle".
```
-### <a name="complete_static_lib"></a>**complete_static_lib**: [boolean] Links all deps into a static library.
+### <a name="var_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
@@ -4357,7 +4600,7 @@
deps = [ "bar" ]
}
```
-### <a name="configs"></a>**configs**: Configs applying to this target or config.
+### <a name="var_configs"></a>**configs**: Configs applying to this target or config.
```
A list of config labels.
@@ -4450,7 +4693,13 @@
}
}
```
-### <a name="data"></a>**data**: Runtime data file dependencies.
+### <a name="var_contents"></a>**contents**: Contents to write to file.
+
+```
+ The contents of the file for a generated_file target.
+ See "gn help generated_file".
+```
+### <a name="var_data"></a>**data**: Runtime data file dependencies.
```
Lists files or directories required to run the given target. These are
@@ -4479,7 +4728,7 @@
See "gn help runtime_deps" for how these are used.
```
-### <a name="data_deps"></a>**data_deps**: Non-linked dependencies.
+### <a name="var_data_deps"></a>**data_deps**: Non-linked dependencies.
```
A list of target labels.
@@ -4505,7 +4754,16 @@
data_deps = [ "//plugins:my_runtime_plugin" ]
}
```
-### <a name="defines"></a>**defines**: C preprocessor defines.
+### <a name="var_data_keys"></a>**data_keys**: Keys from which to collect metadata.
+
+```
+ These keys are used to identify metadata to collect. If a walked target
+ defines this key in its metadata, its value will be appended to the resulting
+ collection.
+
+ See "gn help generated_file".
+```
+### <a name="var_defines"></a>**defines**: C preprocessor defines.
```
A list of strings
@@ -4537,7 +4795,7 @@
```
defines = [ "AWESOME_FEATURE", "LOG_LEVEL=3" ]
```
-### <a name="depfile"></a>**depfile**: [string] File name for input dependencies for actions.
+### <a name="var_depfile"></a>**depfile**: [string] File name for input dependencies for actions.
```
If nonempty, this string specifies that the current action or action_foreach
@@ -4576,7 +4834,7 @@
args = [ "{{source}}", "-o", depfile ]
}
```
-### <a name="deps"></a>**deps**: Private linked dependencies.
+### <a name="var_deps"></a>**deps**: Private linked dependencies.
```
A list of target labels.
@@ -4609,7 +4867,7 @@
See also "public_deps".
```
-### <a name="friend"></a>**friend**: Allow targets to include private headers.
+### <a name="var_friend"></a>**friend**: Allow targets to include private headers.
```
A list of label patterns (see "gn help label_pattern") that allow dependent
@@ -4670,7 +4928,7 @@
]
}
```
-### <a name="include_dirs"></a>**include_dirs**: Additional include directories.
+### <a name="var_include_dirs"></a>**include_dirs**: Additional include directories.
```
A list of source directories.
@@ -4702,7 +4960,7 @@
```
include_dirs = [ "src/include", "//third_party/foo" ]
```
-### <a name="inputs"></a>**inputs**: Additional compile-time dependencies.
+### <a name="var_inputs"></a>**inputs**: Additional compile-time dependencies.
```
Inputs are compile-time dependencies of the current target. This means that
@@ -4772,7 +5030,7 @@
inputs = [ "input.data" ]
}
```
-### <a name="ldflags"></a>**ldflags**: Flags passed to the linker.
+### <a name="var_ldflags"></a>**ldflags**: Flags passed to the linker.
```
A list of strings.
@@ -4804,7 +5062,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="lib_dirs"></a>**lib_dirs**: Additional library directories.
+### <a name="var_lib_dirs"></a>**lib_dirs**: Additional library directories.
```
A list of directories.
@@ -4847,7 +5105,7 @@
```
lib_dirs = [ "/usr/lib/foo", "lib/doom_melon" ]
```
-### <a name="libs"></a>**libs**: Additional libraries to link.
+### <a name="var_libs"></a>**libs**: Additional libraries to link.
```
A list of library names or library paths.
@@ -4920,7 +5178,38 @@
On Linux:
libs = [ "ld" ]
```
-### <a name="output_dir"></a>**output_dir**: [directory] Directory to put output file in.
+### <a name="var_metadata"></a>**metadata**: Metadata of this target.
+
+```
+ Metadata is a collection of keys and values relating to a particular target.
+ Values must be lists, allowing for sane and predictable collection behavior.
+ Generally, these keys will include three types of lists: lists of ordinary
+ strings, lists of filenames intended to be rebased according to their
+ particular source directory, and lists of target labels intended to be used
+ as barriers to the walk. Verfication of these categories occurs at walk time,
+ not creation time (since it is not clear until the walk which values are
+ intended for which purpose).
+```
+
+#### **Example**
+
+```
+ group("doom_melon") {
+ metadata = {
+ # These keys are not built in to GN but are interpreted when consuming
+ # metadata.
+ my_barrier = []
+ my_files = [ "a.txt", "b.txt" ]
+ }
+ }
+```
+### <a name="var_output_conversion"></a>**"output_conversion**: Data format for generated_file targets.
+
+```
+ Controls how the "contents" of a generated_file target is formatted.
+ See "gn help output_conversion".
+```
+### <a name="var_output_dir"></a>**output_dir**: [directory] Directory to put output file in.
```
For library and executable targets, overrides the directory for the final
@@ -4947,7 +5236,7 @@
...
}
```
-### <a name="output_extension"></a>**output_extension**: Value to use for the output's file extension.
+### <a name="var_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
@@ -4983,7 +5272,7 @@
}
}
```
-### <a name="output_name"></a>**output_name**: Define a name for the output file other than the default.
+### <a name="var_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
@@ -5009,7 +5298,7 @@
output_name = "fluffy_bunny"
}
```
-### <a name="output_prefix_override"></a>**output_prefix_override**: Don't use prefix for output name.
+### <a name="var_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.
@@ -5033,7 +5322,7 @@
...
}
```
-### <a name="outputs"></a>**outputs**: Output files for actions and copy targets.
+### <a name="var_outputs"></a>**outputs**: Output files for actions and copy targets.
```
Outputs is valid for "copy", "action", and "action_foreach" target types and
@@ -5056,7 +5345,7 @@
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.
+### <a name="var_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
@@ -5066,7 +5355,7 @@
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 name="var_pool"></a>**pool**: Label of the pool used by the action.
```
A fully-qualified label representing the pool that will be used for the
@@ -5081,7 +5370,7 @@
...
}
```
-### <a name="precompiled_header"></a>**precompiled_header**: [string] Header file to precompile.
+### <a name="var_precompiled_header"></a>**precompiled_header**: [string] Header file to precompile.
```
Precompiled headers will be used when a target specifies this value, or a
@@ -5149,19 +5438,19 @@
configs += [ ":use_precompiled_headers" ]
...
```
-### <a name="precompiled_header_type"></a>**precompiled_header_type**: [string] "gcc" or "msvc".
+### <a name="var_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.
+### <a name="var_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.
+### <a name="var_product_type"></a>**product_type**: Product type for Xcode projects.
```
Correspond to the type of the product of a create_bundle target. Only
@@ -5170,7 +5459,7 @@
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 name="var_public"></a>**public**: Declare public header files for a target.
```
A list of files that other targets can include. These permissions are checked
@@ -5223,7 +5512,7 @@
# This allows starting compilation in dependent targets earlier.
public = []
```
-### <a name="public_configs"></a>**public_configs**: Configs to be applied on dependents.
+### <a name="var_public_configs"></a>**public_configs**: Configs to be applied on dependents.
```
A list of config labels.
@@ -5314,7 +5603,7 @@
"deps" list. If a dependency is public, they will be applied
recursively.
```
-### <a name="public_deps"></a>**public_deps**: Declare public dependencies.
+### <a name="var_public_deps"></a>**public_deps**: Declare public dependencies.
```
Public dependencies are like private dependencies (see "gn help deps") but
@@ -5365,7 +5654,23 @@
public_deps = [ ":c" ]
}
```
-### <a name="response_file_contents"></a>**response_file_contents**: Contents of a response file for actions.
+### <a name="var_rebase"></a>**rebase**: Rebase collected metadata as files.
+
+```
+ A boolean that triggers a rebase of collected metadata strings based on their
+ declared file. Defaults to false.
+
+ Metadata generally declares files as strings relative to the local build file.
+ However, this data is often used in other contexts, and so setting this flag
+ will force the metadata collection to be rebased according to the local build
+ file's location and thus allow the filename to be used anywhere.
+
+ Setting this flag will raise an error if any target's specified metadata is
+ not a string value.
+
+ See also "gn help generated_file".
+```
+### <a name="var_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
@@ -5403,14 +5708,14 @@
]
}
```
-### <a name="script"></a>**script**: Script file for actions.
+### <a name="var_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 name="var_sources"></a>**sources**: Source files for a target
```
A list of files. Non-absolute paths will be resolved relative to the current
@@ -5448,7 +5753,7 @@
copy
The source are the source files to copy.
```
-### <a name="testonly"></a>**testonly**: Declares a target must only be used for testing.
+### <a name="var_testonly"></a>**testonly**: Declares a target must only be used for testing.
```
Boolean. Defaults to false.
@@ -5469,7 +5774,7 @@
...
}
```
-### <a name="visibility"></a>**visibility**: A list of labels that can depend on a target.
+### <a name="var_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
@@ -5524,7 +5829,21 @@
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.
+### <a name="var_walk_keys"></a>**walk_keys**: Key(s) for managing the metadata collection walk.
+
+```
+ Defaults to [].
+
+ These keys are used to control the next step in a collection walk, acting as
+ barriers. If a specified key is defined in a target's metadata, the walk will
+ use the targets listed in that value to determine which targets are walked.
+
+ If no walk_keys are specified for a generated_file target (i.e. "[]"), the
+ walk will touch all deps and data_deps of the specified target recursively.
+
+ See "gn help generated_file".
+```
+### <a name="var_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
@@ -5544,7 +5863,7 @@
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.
+### <a name="var_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
@@ -5553,7 +5872,7 @@
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.
+### <a name="var_xcode_test_application_name"></a>**xcode_test_application_name**: Name for Xcode test target.
```
Each unit and ui test target must have a test application target, and this
@@ -5668,7 +5987,8 @@
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.
+ checked. If it is the empty list, no targets will be checked. To
+ bypass this list, request an explicit check of targets, like "//*".
The format of this list is identical to that of "visibility" so see "gn
help visibility" for examples.
@@ -6049,12 +6369,19 @@
myvalues.foo += 2
empty_scope.new_thing = [ 1, 2, 3 ]
+
+ Scope equality is defined as single-level scopes identical within the current
+ scope. That is, all values in the first scope must be present and identical
+ within the second, and vice versa. Note that this means inherited scopes are
+ always unequal by definition.
```
-### <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**:
+### <a name="io_conversion"></a>**Input and output conversion**
```
+ Input and output conversions are arguments to file and process functions
+ that specify how to convert data to or from external formats. The possible
+ values for parameters specifying conversions are:
+
"" (the default)
input: Discard the result and return None.
@@ -6063,21 +6390,21 @@
"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.
+ 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.
+ 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:
+ 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
@@ -6122,8 +6449,8 @@
will produce an error if assigned to a variable.
output:
- Render the value contents as a literal rvalue. Strings render with escaped
- quotes.
+ 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.
@@ -6334,108 +6661,6 @@
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**
```
@@ -6626,28 +6851,28 @@
//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)
+### <a name="switch_list"></a>**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.
+ * --color: Force colored output.
+ * --dotfile: Override the name of the ".gn" file.
+ * --fail-on-unused-args: Treat unused build args as fatal errors.
+ * --markdown: Write help output in the Markdown format.
+ * --nocolor: Force non-colored output.
+ * -q: Quiet mode. Don't print output on success.
+ * --root: Explicitly specify source root.
+ * --runtime-deps-list-file: Save runtime dependencies for targets in file.
+ * --script-executable: Set the executable used to execute scripts.
+ * --threads: Specify number of worker threads.
+ * --time: Outputs a summary of how long everything took.
+ * --tracelog: Writes a Chrome-compatible trace log to the given file.
+ * -v: Verbose logging.
+ * --version: Prints the GN version number and exits.
```
diff --git a/gn/docs/standalone.md b/gn/docs/standalone.md
index 29d62360f7f..08d589b5a6a 100644
--- a/gn/docs/standalone.md
+++ b/gn/docs/standalone.md
@@ -41,4 +41,4 @@ 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).
+[//tools/gn/example](../tools/gn/example).
diff --git a/gn/docs/style_guide.md b/gn/docs/style_guide.md
index 466f8727e0d..09f7261914f 100644
--- a/gn/docs/style_guide.md
+++ b/gn/docs/style_guide.md
@@ -34,7 +34,7 @@ Naming advice
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"
+ `"//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.
@@ -156,7 +156,8 @@ import("//foo/bar/baz.gni") # Even if this file is in the foo/bar directory
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.
+to cause problems, but on a large project using the right kind of target can
+be important, so you should know about the following tradeoffs.
Static libraries follow different linking rules. When a static library is
included in a link, only the object files that contain unresolved symbols will
@@ -177,13 +178,15 @@ to the link line of the final binary.
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.
+ * On some platforms, static libraries may 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. Some platforms (or toolchains) may
+ support something called "thin archives" which don't have this problem;
+ but you can't rely on this as a portable solution.
* 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
@@ -197,13 +200,22 @@ to the link line of the final binary.
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
+### Components versus shared libraries versus source sets
-A component is a Chrome primitive (rather than a built-in GN concept) that
+A component is a Chrome template (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.
+libraries for most operations. Chrome developers should almost always use
+a component instead of shared library directly.
+
+Much like the source set versus static library tradeoff, there's no hard
+and fast rule as to when you should use a component or not. Using
+components can significantly speed up incremental builds by making
+linking much faster, but they require you to have to think about which
+symbols need to be exported from the target.
+
+### Loadable modules versus shared libraries
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
@@ -219,9 +231,7 @@ dependency on a loadable module is the same as having a `data_deps`
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
+Use loadable modules for things like plugins. 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
@@ -284,3 +294,13 @@ value
`foo_use_bar` - prefixes can be used to indicate a limited scope for an argument
(e.g. `rtc_use_h264`, `v8_use_snapshot`)
+
+#### Variables
+
+Prefix top-level local variables within `.gni` files with an underscore. This
+prefix causes variables to be unavailable to importing scripts.
+
+```
+_this_var_will_not_be_exported = 1
+but_this_one_will = 2
+```
diff --git a/gn/docs/update_binaries.md b/gn/docs/update_binaries.md
deleted file mode 100644
index d3e3bbce7cc..00000000000
--- a/gn/docs/update_binaries.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# 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
index 46036eee22d..d8005aaf002 100644
--- a/gn/infra/README.recipes.md
+++ b/gn/infra/README.recipes.md
@@ -3,13 +3,61 @@
## Table of Contents
**[Recipe Modules](#Recipe-Modules)**
+ * [macos_sdk](#recipe_modules-macos_sdk) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
* [windows_sdk](#recipe_modules-windows_sdk)
**[Recipes](#Recipes)**
* [gn](#recipes-gn) &mdash; Recipe for building GN.
+ * [macos_sdk:examples/full](#recipes-macos_sdk_examples_full)
* [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
## Recipe Modules
+### *recipe_modules* / [macos\_sdk](/infra/recipe_modules/macos_sdk)
+
+[DEPS](/infra/recipe_modules/macos_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]
+
+The `macos_sdk` module provides safe functions to access a semi-hermetic
+XCode installation.
+
+Available only to Google-run bots.
+
+#### **class [MacOSSDKApi](/infra/recipe_modules/macos_sdk/api.py#14)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+
+API for using OS X SDK distributed via CIPD.
+
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/macos_sdk/api.py#24)(self):**
+
+Sets up the XCode SDK environment.
+
+This call is a no-op on non-Mac platforms.
+
+This will deploy the helper tool and the XCode.app bundle at
+`[START_DIR]/cache/macos_sdk`.
+
+To avoid machines rebuilding these on every run, set up a named cache in
+your cr-buildbucket.cfg file like:
+
+ caches: {
+ # Cache for mac_toolchain tool and XCode.app
+ name: "macos_sdk"
+ path: "macos_sdk"
+ }
+
+If you have builders which e.g. use a non-current SDK, you can give them
+a uniqely named cache:
+
+ caches: {
+ # Cache for N-1 version mac_toolchain tool and XCode.app
+ name: "macos_sdk_old"
+ path: "macos_sdk"
+ }
+
+Usage:
+ with api.macos_sdk():
+ # sdk with mac build bits
+
+Raises:
+ StepFailure or InfraFailure.
### *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]
@@ -18,16 +66,11 @@
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):**
+&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/windows_sdk/api.py#19)(self):**
-Setups the SDK environment when enabled.
+Setups the Windows SDK environment.
-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.
+This call is a no-op on non-Windows platforms.
Raises:
StepFailure or InfraFailure.
@@ -35,11 +78,16 @@ Raises:
### *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]
+[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
Recipe for building GN.
-&mdash; **def [RunSteps](/infra/recipes/gn.py#28)(api, repository):**
+&mdash; **def [RunSteps](/infra/recipes/gn.py#29)(api, repository):**
+### *recipes* / [macos\_sdk:examples/full](/infra/recipe_modules/macos_sdk/examples/full.py)
+
+[DEPS](/infra/recipe_modules/macos_sdk/examples/full.py#5): [macos\_sdk](#recipe_modules-macos_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+&mdash; **def [RunSteps](/infra/recipe_modules/macos_sdk/examples/full.py#13)(api):**
### *recipes* / [windows\_sdk:examples/full](/infra/recipe_modules/windows_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/windows_sdk/examples/full.py#5): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
diff --git a/gn/infra/recipe_modules/macos_sdk/__init__.py b/gn/infra/recipe_modules/macos_sdk/__init__.py
new file mode 100644
index 00000000000..f749bfd5af6
--- /dev/null
+++ b/gn/infra/recipe_modules/macos_sdk/__init__.py
@@ -0,0 +1,44 @@
+# 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/macos_sdk':
+ Property(
+ help='Properties specifically for the macos_sdk module.',
+ param_name='sdk_properties',
+ kind=ConfigGroup( # pylint: disable=line-too-long
+ # XCode build version number. Internally maps to an XCode build id like
+ # '9c40b'. See
+ #
+ # https://chrome-infra-packages.appspot.com/p/infra_internal/ios/xcode/mac/+/
+ #
+ # For an up to date list of the latest SDK builds.
+ sdk_version=Single(str),
+
+ # The CIPD toolchain tool package and version.
+ tool_pkg=Single(str),
+ tool_ver=Single(str),
+ ),
+ default={
+ 'sdk_version':
+ '10b61',
+ 'tool_package':
+ 'infra/tools/mac_toolchain/${platform}',
+ 'tool_version':
+ 'git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e',
+ },
+ )
+}
diff --git a/gn/infra/recipe_modules/macos_sdk/api.py b/gn/infra/recipe_modules/macos_sdk/api.py
new file mode 100644
index 00000000000..23d0066aca3
--- /dev/null
+++ b/gn/infra/recipe_modules/macos_sdk/api.py
@@ -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.
+"""The `macos_sdk` module provides safe functions to access a semi-hermetic
+XCode installation.
+
+Available only to Google-run bots."""
+
+from contextlib import contextmanager
+
+from recipe_engine import recipe_api
+
+
+class MacOSSDKApi(recipe_api.RecipeApi):
+ """API for using OS X SDK distributed via CIPD."""
+
+ def __init__(self, sdk_properties, *args, **kwargs):
+ super(MacOSSDKApi, self).__init__(*args, **kwargs)
+
+ self._sdk_version = sdk_properties['sdk_version'].lower()
+ self._tool_package = sdk_properties['tool_package']
+ self._tool_version = sdk_properties['tool_version']
+
+ @contextmanager
+ def __call__(self):
+ """Sets up the XCode SDK environment.
+
+ This call is a no-op on non-Mac platforms.
+
+ This will deploy the helper tool and the XCode.app bundle at
+ `[START_DIR]/cache/macos_sdk`.
+
+ To avoid machines rebuilding these on every run, set up a named cache in
+ your cr-buildbucket.cfg file like:
+
+ caches: {
+ # Cache for mac_toolchain tool and XCode.app
+ name: "macos_sdk"
+ path: "macos_sdk"
+ }
+
+ If you have builders which e.g. use a non-current SDK, you can give them
+ a uniqely named cache:
+
+ caches: {
+ # Cache for N-1 version mac_toolchain tool and XCode.app
+ name: "macos_sdk_old"
+ path: "macos_sdk"
+ }
+
+ Usage:
+ with api.macos_sdk():
+ # sdk with mac build bits
+
+ Raises:
+ StepFailure or InfraFailure.
+ """
+ if not self.m.platform.is_mac:
+ yield
+ return
+
+ try:
+ with self.m.context(infra_steps=True):
+ sdk_dir = self._ensure_sdk()
+ self.m.step('select XCode',
+ ['sudo', 'xcode-select', '--switch', sdk_dir])
+ yield
+ finally:
+ with self.m.context(infra_steps=True):
+ self.m.step('reset XCode', ['sudo', 'xcode-select', '--reset'])
+
+ def _ensure_sdk(self):
+ """Ensures the mac_toolchain tool and MacOS SDK packages are installed.
+
+ Returns Path to the installed sdk app bundle."""
+ cache_dir = self.m.path['cache'].join('macos_sdk')
+ pkgs = self.m.cipd.EnsureFile()
+ pkgs.add_package(self._tool_package, self._tool_version)
+ self.m.cipd.ensure(cache_dir, pkgs)
+
+ sdk_dir = cache_dir.join('XCode.app')
+ self.m.step('install xcode', [
+ cache_dir.join('mac_toolchain'),
+ 'install',
+ '-kind',
+ 'mac',
+ '-xcode-version',
+ self._sdk_version,
+ '-output-dir',
+ sdk_dir,
+ ])
+ return sdk_dir
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json
new file mode 100644
index 00000000000..51d94309ff2
--- /dev/null
+++ b/gn/infra/recipe_modules/macos_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/macos_sdk/examples/full.expected/mac.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
new file mode 100644
index 00000000000..e5a7fc0c79b
--- /dev/null
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
@@ -0,0 +1,83 @@
+[
+ {
+ "cmd": [
+ "cipd",
+ "ensure",
+ "-root",
+ "[CACHE]/macos_sdk",
+ "-ensure-file",
+ "infra/tools/mac_toolchain/${platform} git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e",
+ "-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-git_revision:434\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/${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": [
+ "[CACHE]/macos_sdk/mac_toolchain",
+ "install",
+ "-kind",
+ "mac",
+ "-xcode-version",
+ "10b61",
+ "-output-dir",
+ "[CACHE]/macos_sdk/XCode.app"
+ ],
+ "infra_step": true,
+ "name": "install xcode"
+ },
+ {
+ "cmd": [
+ "sudo",
+ "xcode-select",
+ "--switch",
+ "[CACHE]/macos_sdk/XCode.app"
+ ],
+ "infra_step": true,
+ "name": "select XCode"
+ },
+ {
+ "cmd": [
+ "gn",
+ "gen",
+ "out/Release"
+ ],
+ "name": "gn"
+ },
+ {
+ "cmd": [
+ "ninja",
+ "-C",
+ "out/Release"
+ ],
+ "name": "ninja"
+ },
+ {
+ "cmd": [
+ "sudo",
+ "xcode-select",
+ "--reset"
+ ],
+ "infra_step": true,
+ "name": "reset XCode"
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json
new file mode 100644
index 00000000000..51d94309ff2
--- /dev/null
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.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/macos_sdk/examples/full.py b/gn/infra/recipe_modules/macos_sdk/examples/full.py
new file mode 100644
index 00000000000..5546ad5058e
--- /dev/null
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.py
@@ -0,0 +1,22 @@
+# 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 = [
+ 'macos_sdk',
+ 'recipe_engine/platform',
+ 'recipe_engine/properties',
+ 'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+ with api.macos_sdk():
+ api.step('gn', ['gn', 'gen', 'out/Release'])
+ api.step('ninja', ['ninja', '-C', 'out/Release'])
+
+
+def GenTests(api):
+ for platform in ('linux', 'mac', 'win'):
+ yield (api.test(platform) + api.platform.name(platform) +
+ api.properties.generic(buildername='test_builder'))
diff --git a/gn/infra/recipe_modules/windows_sdk/__init__.py b/gn/infra/recipe_modules/windows_sdk/__init__.py
index 6183f2b5b7d..3c790ded06b 100644
--- a/gn/infra/recipe_modules/windows_sdk/__init__.py
+++ b/gn/infra/recipe_modules/windows_sdk/__init__.py
@@ -17,11 +17,15 @@ from recipe_engine.config import ConfigGroup, Single
PROPERTIES = {
'$gn/windows_sdk':
Property(
- help='Properties specifically for the infra windows_sdk module.',
+ help='Properties specifically for the 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'},
+ # The CIPD package and version.
+ sdk_package=Single(str),
+ sdk_version=Single(str)),
+ default={
+ 'sdk_package': 'chrome_internal/third_party/sdk/windows',
+ 'sdk_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
index 7c4067deff7..d47f9843239 100644
--- a/gn/infra/recipe_modules/windows_sdk/api.py
+++ b/gn/infra/recipe_modules/windows_sdk/api.py
@@ -13,39 +13,34 @@ class WindowsSDKApi(recipe_api.RecipeApi):
def __init__(self, sdk_properties, *args, **kwargs):
super(WindowsSDKApi, self).__init__(*args, **kwargs)
- self._sdk_properties = sdk_properties
+ self._sdk_package = sdk_properties['sdk_package']
+ self._sdk_version = sdk_properties['sdk_version']
@contextmanager
- def __call__(self, path=None, version=None, enabled=True):
- """Setups the SDK environment when enabled.
+ def __call__(self):
+ """Setups the Windows SDK environment.
- 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.
+ This call is a no-op on non-Windows platforms.
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:
+ if not self.m.platform.is_win:
yield
-
- def _ensure_sdk(self, sdk_dir, sdk_version):
+ return
+
+ try:
+ with self.m.context(infra_steps=True):
+ sdk_dir = self._ensure_sdk()
+ with self.m.context(**self._sdk_env(sdk_dir)):
+ yield
+ finally:
+ # 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'])
+
+ def _ensure_sdk(self):
"""Ensures the Windows SDK CIPD package is installed.
Returns the directory where the SDK package has been installed.
@@ -54,11 +49,11 @@ class WindowsSDKApi(recipe_api.RecipeApi):
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
+ sdk_dir = self.m.path['cache'].join('windows_sdk')
+ pkgs = self.m.cipd.EnsureFile()
+ pkgs.add_package(self._sdk_package, self._sdk_version)
+ self.m.cipd.ensure(sdk_dir, pkgs)
+ return sdk_dir
def _sdk_env(self, sdk_dir):
"""Constructs the environment for the SDK.
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
index d9a7a658e79..092354d907b 100644
--- a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -4,7 +4,7 @@
"cipd.bat",
"ensure",
"-root",
- "[START_DIR]\\cipd\\windows_sdk",
+ "[CACHE]\\windows_sdk",
"-ensure-file",
"chrome_internal/third_party/sdk/windows uploaded:2018-06-13",
"-json-output",
@@ -31,7 +31,7 @@
"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",
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
"/path/to/tmp/json"
],
"name": "read SetEnv.x64.json",
@@ -65,11 +65,11 @@
"out/Release"
],
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "gn"
@@ -81,11 +81,11 @@
"out/Release"
],
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "ninja"
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.py b/gn/infra/recipe_modules/windows_sdk/examples/full.py
index 5e8e503eb1a..902c4918fa4 100644
--- a/gn/infra/recipe_modules/windows_sdk/examples/full.py
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.py
@@ -11,7 +11,7 @@ DEPS = [
def RunSteps(api):
- with api.windows_sdk(enabled=api.platform.is_win):
+ with api.windows_sdk():
api.step('gn', ['gn', 'gen', 'out/Release'])
api.step('ninja', ['ninja', '-C', 'out/Release'])
diff --git a/gn/infra/recipes/gn.expected/ci_linux.json b/gn/infra/recipes/gn.expected/ci_linux.json
new file mode 100644
index 00000000000..993c4918fad
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/ci_linux.json
@@ -0,0 +1,229 @@
+[
+ {
+ "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\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "-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@ \"sysroot\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${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",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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",
+ "--use-lto",
+ "--use-icf"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
+ "-out",
+ "[CLEANUP]/gn.cipd",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "build gn/gn/${platform}",
+ "~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/${platform}\"@@@",
+ "@@@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/ci_mac.json b/gn/infra/recipes/gn.expected/ci_mac.json
new file mode 100644
index 00000000000..bb30be4d360
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/ci_mac.json
@@ -0,0 +1,295 @@
+[
+ {
+ "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": [
+ "cipd",
+ "ensure",
+ "-root",
+ "[CACHE]/macos_sdk",
+ "-ensure-file",
+ "infra/tools/mac_toolchain/${platform} git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed (2)",
+ "~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-git_revision:434\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/${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": [
+ "[CACHE]/macos_sdk/mac_toolchain",
+ "install",
+ "-kind",
+ "mac",
+ "-xcode-version",
+ "10b61",
+ "-output-dir",
+ "[CACHE]/macos_sdk/XCode.app"
+ ],
+ "infra_step": true,
+ "name": "install xcode"
+ },
+ {
+ "cmd": [
+ "sudo",
+ "xcode-select",
+ "--switch",
+ "[CACHE]/macos_sdk/XCode.app"
+ ],
+ "infra_step": true,
+ "name": "select XCode"
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--show-sdk-path"
+ ],
+ "name": "xcrun",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ ]
+ },
+ {
+ "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",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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",
+ "--use-lto",
+ "--use-icf"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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": [
+ "sudo",
+ "xcode-select",
+ "--reset"
+ ],
+ "infra_step": true,
+ "name": "reset XCode"
+ },
+ {
+ "cmd": [
+ "cipd",
+ "pkg-build",
+ "-pkg-def",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
+ "-out",
+ "[CLEANUP]/gn.cipd",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "name": "build gn/gn/${platform}",
+ "~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/${platform}\"@@@",
+ "@@@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/ci.json b/gn/infra/recipes/gn.expected/ci_win.json
index e2e89338efa..764f2fa3835 100644
--- a/gn/infra/recipes/gn.expected/ci.json
+++ b/gn/infra/recipes/gn.expected/ci_win.json
@@ -71,32 +71,19 @@
]
},
{
- "cmd": [],
- "name": "debug"
- },
- {
- "cmd": [],
- "name": "debug.build",
- "~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
- ]
- },
- {
"cmd": [
"cipd.bat",
"ensure",
"-root",
- "[START_DIR]\\cipd\\windows_sdk",
+ "[CACHE]\\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",
+ "name": "ensure_installed (2)",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@2@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
@@ -115,13 +102,11 @@
"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",
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
"/path/to/tmp/json"
],
- "cwd": "[START_DIR]\\gn",
- "name": "debug.build.read SetEnv.x64.json",
+ "name": "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\": [@@@",
@@ -145,6 +130,33 @@
]
},
{
+ "cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug"
+ },
+ {
+ "cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
"cmd": [
"python",
"-u",
@@ -153,11 +165,11 @@
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "debug.build.generate",
@@ -173,11 +185,11 @@
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "debug.build.ninja",
@@ -187,22 +199,16 @@
},
{
"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"
],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
"name": "debug.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -210,10 +216,26 @@
},
{
"cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
"name": "release"
},
{
"cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
"name": "release.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -221,80 +243,19 @@
},
{
"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"
+ "[START_DIR]\\gn\\build\\gen.py",
+ "--use-lto",
+ "--use-icf"
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "release.build.generate",
@@ -310,11 +271,11 @@
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "release.build.ninja",
@@ -324,44 +285,48 @@
},
{
"cmd": [
- "taskkill.exe",
- "/f",
- "/t",
- "/im",
- "mspdbsrv.exe"
+ "[START_DIR]\\gn\\out\\gn_unittests"
],
- "cwd": "[START_DIR]\\gn",
- "name": "release.build.taskkill mspdbsrv",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@2@@@"
+ "@@@STEP_NEST_LEVEL@1@@@"
]
},
{
"cmd": [
- "[START_DIR]\\gn\\out\\gn_unittests"
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
],
- "name": "release.test",
- "~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
- ]
+ "name": "taskkill mspdbsrv"
},
{
"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\"}",
+ "{\"data\": [{\"file\": \"gn.exe\"}, {\"version_file\": \".versions/gn.exe.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]\\\\gn\\\\out\"}",
"-out",
"[CLEANUP]\\gn.cipd",
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/windows-amd64",
+ "name": "build gn/gn/${platform}",
"~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@ \"package\": \"gn/gn/${platform}\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
diff --git a/gn/infra/recipes/gn.expected/cipd_exists.json b/gn/infra/recipes/gn.expected/cipd_exists.json
index 5a7f14a7e13..c6f38253428 100644
--- a/gn/infra/recipes/gn.expected/cipd_exists.json
+++ b/gn/infra/recipes/gn.expected/cipd_exists.json
@@ -50,7 +50,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
"-json-output",
"/path/to/tmp/json"
],
@@ -68,6 +68,12 @@
"@@@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@ \"sysroot\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
@@ -96,8 +102,9 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "debug.build.generate",
"~followup_annotations": [
@@ -114,8 +121,9 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "debug.build.ninja",
"~followup_annotations": [
@@ -146,14 +154,17 @@
"cmd": [
"python",
"-u",
- "[START_DIR]/gn/build/gen.py"
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf"
],
"cwd": "[START_DIR]/gn",
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "release.build.generate",
"~followup_annotations": [
@@ -170,8 +181,9 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "release.build.ninja",
"~followup_annotations": [
@@ -192,18 +204,18 @@
"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\"}",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/linux-amd64",
+ "name": "build gn/gn/${platform}",
"~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@ \"package\": \"gn/gn/${platform}\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
@@ -223,13 +235,13 @@
"cmd": [
"cipd",
"search",
- "gn/gn/linux-amd64",
+ "gn/gn/${platform}",
"-tag",
"git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"-json-output",
"/path/to/tmp/json"
],
- "name": "cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "name": "cipd search gn/gn/${platform} git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": [@@@",
diff --git a/gn/infra/recipes/gn.expected/cipd_register.json b/gn/infra/recipes/gn.expected/cipd_register.json
index 5a31ae9b355..76efe86ef21 100644
--- a/gn/infra/recipes/gn.expected/cipd_register.json
+++ b/gn/infra/recipes/gn.expected/cipd_register.json
@@ -50,7 +50,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
"-json-output",
"/path/to/tmp/json"
],
@@ -68,6 +68,12 @@
"@@@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@ \"sysroot\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
@@ -96,8 +102,9 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "debug.build.generate",
"~followup_annotations": [
@@ -114,8 +121,9 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "debug.build.ninja",
"~followup_annotations": [
@@ -146,14 +154,17 @@
"cmd": [
"python",
"-u",
- "[START_DIR]/gn/build/gen.py"
+ "[START_DIR]/gn/build/gen.py",
+ "--use-lto",
+ "--use-icf"
],
"cwd": "[START_DIR]/gn",
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "release.build.generate",
"~followup_annotations": [
@@ -170,8 +181,9 @@
"env": {
"AR": "[START_DIR]/cipd/bin/llvm-ar",
"CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "-static-libstdc++ -ldl -lpthread"
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
},
"name": "release.build.ninja",
"~followup_annotations": [
@@ -192,18 +204,18 @@
"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\"}",
+ "{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
"-json-output",
"/path/to/tmp/json"
],
- "name": "build gn/gn/linux-amd64",
+ "name": "build gn/gn/${platform}",
"~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@ \"package\": \"gn/gn/${platform}\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
@@ -223,13 +235,13 @@
"cmd": [
"cipd",
"search",
- "gn/gn/linux-amd64",
+ "gn/gn/${platform}",
"-tag",
"git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"-json-output",
"/path/to/tmp/json"
],
- "name": "cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "name": "cipd search gn/gn/${platform} git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": []@@@",
@@ -251,12 +263,12 @@
"-json-output",
"/path/to/tmp/json"
],
- "name": "register gn/gn/linux-amd64",
+ "name": "register gn/gn/${platform}",
"~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@ \"package\": \"gn/gn/${platform}\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
diff --git a/gn/infra/recipes/gn.expected/cq_linux.json b/gn/infra/recipes/gn.expected/cq_linux.json
new file mode 100644
index 00000000000..8fe2ad2a688
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/cq_linux.json
@@ -0,0 +1,234 @@
+[
+ {
+ "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",
+ "ensure",
+ "-root",
+ "[START_DIR]/cipd",
+ "-ensure-file",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "-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@ \"sysroot\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${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",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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",
+ "--use-lto",
+ "--use-icf"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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",
+ "CFLAGS": "--sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=[START_DIR]/cipd/sysroot"
+ },
+ "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@@@"
+ ]
+ },
+ {
+ "name": "$result",
+ "recipe_result": null,
+ "status_code": 0
+ }
+] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cq_mac.json b/gn/infra/recipes/gn.expected/cq_mac.json
new file mode 100644
index 00000000000..7872a57c4ed
--- /dev/null
+++ b/gn/infra/recipes/gn.expected/cq_mac.json
@@ -0,0 +1,300 @@
+[
+ {
+ "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",
+ "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": [
+ "cipd",
+ "ensure",
+ "-root",
+ "[CACHE]/macos_sdk",
+ "-ensure-file",
+ "infra/tools/mac_toolchain/${platform} git_revision:434f5462a77e7103f9d610fa5cabc426bb21502e",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "infra_step": true,
+ "name": "ensure_installed (2)",
+ "~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-git_revision:434\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/${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": [
+ "[CACHE]/macos_sdk/mac_toolchain",
+ "install",
+ "-kind",
+ "mac",
+ "-xcode-version",
+ "10b61",
+ "-output-dir",
+ "[CACHE]/macos_sdk/XCode.app"
+ ],
+ "infra_step": true,
+ "name": "install xcode"
+ },
+ {
+ "cmd": [
+ "sudo",
+ "xcode-select",
+ "--switch",
+ "[CACHE]/macos_sdk/XCode.app"
+ ],
+ "infra_step": true,
+ "name": "select XCode"
+ },
+ {
+ "cmd": [
+ "xcrun",
+ "--show-sdk-path"
+ ],
+ "name": "xcrun",
+ "stdout": "/path/to/tmp/",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ ]
+ },
+ {
+ "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",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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",
+ "--use-lto",
+ "--use-icf"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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",
+ "CFLAGS": "--sysroot=/some/xcode/path",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ },
+ "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": [
+ "sudo",
+ "xcode-select",
+ "--reset"
+ ],
+ "infra_step": true,
+ "name": "reset XCode"
+ },
+ {
+ "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_win.json
index e9b8b251db6..92f665a627b 100644
--- a/gn/infra/recipes/gn.expected/cq.json
+++ b/gn/infra/recipes/gn.expected/cq_win.json
@@ -98,32 +98,19 @@
]
},
{
- "cmd": [],
- "name": "debug"
- },
- {
- "cmd": [],
- "name": "debug.build",
- "~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
- ]
- },
- {
"cmd": [
"cipd.bat",
"ensure",
"-root",
- "[START_DIR]\\cipd\\windows_sdk",
+ "[CACHE]\\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",
+ "name": "ensure_installed (2)",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@2@@@",
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
@@ -142,13 +129,11 @@
"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",
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
"/path/to/tmp/json"
],
- "cwd": "[START_DIR]\\gn",
- "name": "debug.build.read SetEnv.x64.json",
+ "name": "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\": [@@@",
@@ -172,6 +157,33 @@
]
},
{
+ "cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug"
+ },
+ {
+ "cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "debug.build",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@"
+ ]
+ },
+ {
"cmd": [
"python",
"-u",
@@ -180,11 +192,11 @@
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "debug.build.generate",
@@ -200,11 +212,11 @@
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "debug.build.ninja",
@@ -214,22 +226,16 @@
},
{
"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"
],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
"name": "debug.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -237,10 +243,26 @@
},
{
"cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
"name": "release"
},
{
"cmd": [],
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
"name": "release.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -248,80 +270,19 @@
},
{
"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"
+ "[START_DIR]\\gn\\build\\gen.py",
+ "--use-lto",
+ "--use-icf"
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "release.build.generate",
@@ -337,11 +298,11 @@
],
"cwd": "[START_DIR]\\gn",
"env": {
- "VSINSTALLDIR": "[START_DIR]\\cipd\\windows_sdk"
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
},
"env_prefixes": {
"PATH": [
- "[START_DIR]\\cipd\\windows_sdk\\win_sdk\\bin\\x64"
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
"name": "release.build.ninja",
@@ -351,26 +312,30 @@
},
{
"cmd": [
- "taskkill.exe",
- "/f",
- "/t",
- "/im",
- "mspdbsrv.exe"
+ "[START_DIR]\\gn\\out\\gn_unittests"
],
- "cwd": "[START_DIR]\\gn",
- "name": "release.build.taskkill mspdbsrv",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "name": "release.test",
"~followup_annotations": [
- "@@@STEP_NEST_LEVEL@2@@@"
+ "@@@STEP_NEST_LEVEL@1@@@"
]
},
{
"cmd": [
- "[START_DIR]\\gn\\out\\gn_unittests"
+ "taskkill.exe",
+ "/f",
+ "/t",
+ "/im",
+ "mspdbsrv.exe"
],
- "name": "release.test",
- "~followup_annotations": [
- "@@@STEP_NEST_LEVEL@1@@@"
- ]
+ "name": "taskkill mspdbsrv"
},
{
"name": "$result",
diff --git a/gn/infra/recipes/gn.py b/gn/infra/recipes/gn.py
index 0a88862d994..7a079b54d8f 100644
--- a/gn/infra/recipes/gn.py
+++ b/gn/infra/recipes/gn.py
@@ -17,6 +17,7 @@ DEPS = [
'recipe_engine/python',
'recipe_engine/raw_io',
'recipe_engine/step',
+ 'macos_sdk',
'windows_sdk',
]
@@ -52,21 +53,14 @@ def RunSteps(api, repository):
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:
+ if api.platform.is_linux or api.platform.is_mac:
pkgs.add_package('fuchsia/clang/${platform}', 'goma')
+ if api.platform.is_linux:
+ pkgs.add_package('fuchsia/sysroot/${platform}',
+ 'git_revision:a28dfa20af063e5ca00634024c85732e20220419',
+ 'sysroot')
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 = [
{
@@ -75,39 +69,55 @@ def RunSteps(api, repository):
},
{
'name': 'release',
- 'args': []
+ 'args': ['--use-lto', '--use-icf']
},
]
- 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):
+ with api.macos_sdk(), api.windows_sdk():
+ if api.platform.is_linux:
+ sysroot = '--sysroot=%s' % cipd_dir.join('sysroot')
+ env = {
+ 'CC': cipd_dir.join('bin', 'clang'),
+ 'CXX': cipd_dir.join('bin', 'clang++'),
+ 'AR': cipd_dir.join('bin', 'llvm-ar'),
+ 'CFLAGS': sysroot,
+ 'LDFLAGS': sysroot,
+ }
+ elif api.platform.is_mac:
+ sysroot = '--sysroot=%s' % api.step(
+ 'xcrun', ['xcrun', '--show-sdk-path'],
+ stdout=api.raw_io.output(name='sdk-path', add_output_log=True),
+ step_test_data=
+ lambda: api.raw_io.test_api.stream_output('/some/xcode/path')
+ ).stdout.strip()
+ stdlib = '%s %s %s' % (cipd_dir.join('lib', 'libc++.a'),
+ cipd_dir.join('lib', 'libc++abi.a'),
+ cipd_dir.join('lib', 'libunwind.a'))
+ env = {
+ 'CC': cipd_dir.join('bin', 'clang'),
+ 'CXX': cipd_dir.join('bin', 'clang++'),
+ 'AR': cipd_dir.join('bin', 'llvm-ar'),
+ 'CFLAGS': sysroot,
+ 'LDFLAGS': '%s -nostdlib++ %s' % (sysroot, stdlib),
+ }
+ else:
+ env = {}
+
+ for config in configs:
+ with api.step.nest(config['name']):
+ with api.step.nest('build'), api.context(env=env, cwd=src_dir):
api.python(
'generate', src_dir.join('build', 'gen.py'), args=config['args'])
# 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')])
+ 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
+ cipd_pkg_name = 'gn/gn/${platform}'
gn = 'gn' + ('.exe' if api.platform.is_win else '')
pkg_def = api.cipd.PackageDefinition(
@@ -149,23 +159,26 @@ def RunSteps(api, repository):
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,
- ))
+ for platform in ('linux', 'mac', 'win'):
+ yield (api.test('ci_' + platform) + api.platform.name(platform) +
+ 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('cq_' + platform) + api.platform.name(platform) +
+ 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.step_data('cipd search gn/gn/${platform} git_revision:' + REVISION,
api.cipd.example_search('gn/gn/linux-amd64',
['git_revision:' + REVISION])))
@@ -174,5 +187,5 @@ def GenTests(api):
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.step_data('cipd search gn/gn/${platform} git_revision:' + REVISION,
api.cipd.example_search('gn/gn/linux-amd64', [])))
diff --git a/gn/tools/gn/args.cc b/gn/tools/gn/args.cc
index aa42ff3dab9..5889c8fcf68 100644
--- a/gn/tools/gn/args.cc
+++ b/gn/tools/gn/args.cc
@@ -294,6 +294,8 @@ void Args::SetSystemVarsLocked(Scope* dest) const {
os = "mac";
#elif defined(OS_LINUX)
os = "linux";
+#elif defined(OS_FREEBSD)
+ os = "freebsd";
#elif defined(OS_AIX)
os = "aix";
#else
diff --git a/gn/tools/gn/build_settings.cc b/gn/tools/gn/build_settings.cc
index 84f2a3cb5d7..3cc211bb515 100644
--- a/gn/tools/gn/build_settings.cc
+++ b/gn/tools/gn/build_settings.cc
@@ -12,7 +12,8 @@
BuildSettings::BuildSettings() = default;
BuildSettings::BuildSettings(const BuildSettings& other)
- : root_path_(other.root_path_),
+ : dotfile_name_(other.dotfile_name_),
+ root_path_(other.root_path_),
root_path_utf8_(other.root_path_utf8_),
secondary_source_path_(other.secondary_source_path_),
python_path_(other.python_path_),
diff --git a/gn/tools/gn/build_settings.h b/gn/tools/gn/build_settings.h
index 52f3625a4be..51a7d6b4bb3 100644
--- a/gn/tools/gn/build_settings.h
+++ b/gn/tools/gn/build_settings.h
@@ -39,8 +39,10 @@ class BuildSettings {
// Absolute path of the source root on the local system. Everything is
// relative to this. Does not end in a [back]slash.
const base::FilePath& root_path() const { return root_path_; }
+ const base::FilePath& dotfile_name() const { return dotfile_name_; }
const std::string& root_path_utf8() const { return root_path_utf8_; }
void SetRootPath(const base::FilePath& r);
+ void set_dotfile_name(const base::FilePath& d) { dotfile_name_ = d; }
// When nonempty, specifies a parallel directory higherarchy in which to
// search for buildfiles if they're not found in the root higherarchy. This
@@ -119,6 +121,7 @@ class BuildSettings {
private:
Label root_target_label_;
+ base::FilePath dotfile_name_;
base::FilePath root_path_;
std::string root_path_utf8_;
base::FilePath secondary_source_path_;
diff --git a/gn/tools/gn/bundle_data.cc b/gn/tools/gn/bundle_data.cc
index 9f2cf2b84f4..095e98935c1 100644
--- a/gn/tools/gn/bundle_data.cc
+++ b/gn/tools/gn/bundle_data.cc
@@ -112,20 +112,29 @@ void BundleData::GetSourceFiles(SourceFiles* sources) const {
}
}
-void BundleData::GetOutputFiles(const Settings* settings,
- OutputFiles* outputs) const {
+bool BundleData::GetOutputFiles(const Settings* settings,
+ const Target* target,
+ OutputFiles* outputs,
+ Err* err) const {
SourceFiles outputs_as_sources;
- GetOutputsAsSourceFiles(settings, &outputs_as_sources);
+ if (!GetOutputsAsSourceFiles(settings, target, &outputs_as_sources, err))
+ return false;
for (const SourceFile& source_file : outputs_as_sources)
outputs->push_back(OutputFile(settings->build_settings(), source_file));
+ return true;
}
-void BundleData::GetOutputsAsSourceFiles(const Settings* settings,
- SourceFiles* outputs_as_source) const {
+bool BundleData::GetOutputsAsSourceFiles(const Settings* settings,
+ const Target* target,
+ SourceFiles* outputs_as_source,
+ Err* err) const {
for (const BundleFileRule& file_rule : file_rules_) {
for (const SourceFile& source : file_rule.sources()) {
- outputs_as_source->push_back(
- file_rule.ApplyPatternToSource(settings, *this, source));
+ SourceFile expanded_source_file;
+ if (!file_rule.ApplyPatternToSource(settings, target, *this, source,
+ &expanded_source_file, err))
+ return false;
+ outputs_as_source->push_back(expanded_source_file);
}
}
@@ -146,6 +155,8 @@ void BundleData::GetOutputsAsSourceFiles(const Settings* settings,
if (!root_dir_.is_null())
outputs_as_source->push_back(GetBundleRootDirOutput(settings));
+
+ return true;
}
SourceFile BundleData::GetCompiledAssetCatalogPath() const {
diff --git a/gn/tools/gn/bundle_data.h b/gn/tools/gn/bundle_data.h
index dc63bbd56a8..2379dbc568d 100644
--- a/gn/tools/gn/bundle_data.h
+++ b/gn/tools/gn/bundle_data.h
@@ -44,11 +44,16 @@ class BundleData {
void GetSourceFiles(SourceFiles* sources) const;
// Returns the list of outputs.
- void GetOutputFiles(const Settings* settings, OutputFiles* outputs) const;
+ bool GetOutputFiles(const Settings* settings,
+ const Target* target,
+ OutputFiles* outputs,
+ Err* err) const;
// Returns the list of outputs as SourceFile.
- void GetOutputsAsSourceFiles(const Settings* settings,
- SourceFiles* outputs_as_source) const;
+ bool GetOutputsAsSourceFiles(const Settings* settings,
+ const Target* target,
+ SourceFiles* outputs_as_source,
+ Err* err) const;
// Returns the path to the compiled asset catalog. Only valid if
// assets_catalog_sources() is not empty.
@@ -95,9 +100,6 @@ class BundleData {
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_;
}
@@ -165,7 +167,6 @@ class BundleData {
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.
diff --git a/gn/tools/gn/bundle_file_rule.cc b/gn/tools/gn/bundle_file_rule.cc
index 4d4ca3a34eb..5dffb07e929 100644
--- a/gn/tools/gn/bundle_file_rule.cc
+++ b/gn/tools/gn/bundle_file_rule.cc
@@ -4,11 +4,32 @@
#include "tools/gn/bundle_file_rule.h"
+#include "base/strings/stringprintf.h"
#include "tools/gn/output_file.h"
#include "tools/gn/settings.h"
#include "tools/gn/substitution_pattern.h"
#include "tools/gn/substitution_writer.h"
#include "tools/gn/target.h"
+#include "tools/gn/variables.h"
+
+namespace {
+
+Err ErrMissingPropertyForExpansion(const Settings* settings,
+ const Target* target,
+ const BundleFileRule* bundle_file_rule,
+ const char* property_name) {
+ std::string label = bundle_file_rule->target()->label().GetUserVisibleName(
+ settings->default_toolchain_label());
+
+ return Err(target->defined_from(),
+ base::StringPrintf("Property %s is required.", property_name),
+ base::StringPrintf(
+ "In order to expand {{%s}} in %s, the "
+ "property needs to be defined in the create_bundle target.",
+ property_name, label.c_str()));
+}
+
+} // namespace
BundleFileRule::BundleFileRule(const Target* bundle_data_target,
const std::vector<SourceFile> sources,
@@ -22,10 +43,12 @@ BundleFileRule::BundleFileRule(const BundleFileRule& other) = default;
BundleFileRule::~BundleFileRule() = default;
-SourceFile BundleFileRule::ApplyPatternToSource(
- const Settings* settings,
- const BundleData& bundle_data,
- const SourceFile& source_file) const {
+bool BundleFileRule::ApplyPatternToSource(const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ SourceFile* expanded_source_file,
+ Err* err) const {
std::string output_path;
for (const auto& subrange : pattern_.ranges()) {
switch (subrange.type) {
@@ -33,20 +56,37 @@ SourceFile BundleFileRule::ApplyPatternToSource(
output_path.append(subrange.literal);
break;
case SUBSTITUTION_BUNDLE_ROOT_DIR:
+ if (bundle_data.contents_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleRootDir);
+ return false;
+ }
output_path.append(bundle_data.root_dir().value());
break;
case SUBSTITUTION_BUNDLE_CONTENTS_DIR:
+ if (bundle_data.contents_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleContentsDir);
+ return false;
+ }
output_path.append(bundle_data.contents_dir().value());
break;
case SUBSTITUTION_BUNDLE_RESOURCES_DIR:
+ if (bundle_data.resources_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleResourcesDir);
+ return false;
+ }
output_path.append(bundle_data.resources_dir().value());
break;
case SUBSTITUTION_BUNDLE_EXECUTABLE_DIR:
+ if (bundle_data.executable_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(
+ settings, target, this, variables::kBundleExecutableDir);
+ return false;
+ }
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,
@@ -54,13 +94,24 @@ SourceFile BundleFileRule::ApplyPatternToSource(
break;
}
}
- return SourceFile(SourceFile::SWAP_IN, &output_path);
+ *expanded_source_file = SourceFile(SourceFile::SWAP_IN, &output_path);
+ return true;
}
-OutputFile BundleFileRule::ApplyPatternToSourceAsOutputFile(
+bool BundleFileRule::ApplyPatternToSourceAsOutputFile(
const Settings* settings,
+ const Target* target,
const BundleData& bundle_data,
- const SourceFile& source_file) const {
- return OutputFile(settings->build_settings(),
- ApplyPatternToSource(settings, bundle_data, source_file));
+ const SourceFile& source_file,
+ OutputFile* expanded_output_file,
+ Err* err) const {
+ SourceFile expanded_source_file;
+ if (!ApplyPatternToSource(settings, target, bundle_data, source_file,
+ &expanded_source_file, err)) {
+ return false;
+ }
+
+ *expanded_output_file =
+ OutputFile(settings->build_settings(), expanded_source_file);
+ return true;
}
diff --git a/gn/tools/gn/bundle_file_rule.h b/gn/tools/gn/bundle_file_rule.h
index 372e62802c7..b7a2428a600 100644
--- a/gn/tools/gn/bundle_file_rule.h
+++ b/gn/tools/gn/bundle_file_rule.h
@@ -27,13 +27,18 @@ class 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;
+ bool ApplyPatternToSource(const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ SourceFile* expanded_source_file,
+ Err* err) const;
+ bool ApplyPatternToSourceAsOutputFile(const Settings* settings,
+ const Target* target,
+ const BundleData& bundle_data,
+ const SourceFile& source_file,
+ OutputFile* expanded_output_file,
+ Err* err) const;
// Returns the associated target (of type Target::BUNDLE_DATA). May be
// null during testing.
diff --git a/gn/tools/gn/c_include_iterator.cc b/gn/tools/gn/c_include_iterator.cc
index 9f4f0703e63..0c5476daaf7 100644
--- a/gn/tools/gn/c_include_iterator.cc
+++ b/gn/tools/gn/c_include_iterator.cc
@@ -62,15 +62,20 @@ bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) {
IncludeType ExtractInclude(const base::StringPiece& line,
base::StringPiece* path,
int* begin_char) {
- static const char kInclude[] = "#include";
+ static const char kInclude[] = "include";
static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null.
- static const char kImport[] = "#import";
+ static const char kImport[] = "import";
static const size_t kImportLen = arraysize(kImport) - 1; // No null.
base::StringPiece trimmed = TrimLeadingWhitespace(line);
if (trimmed.empty())
return INCLUDE_NONE;
+ if (trimmed[0] != '#')
+ return INCLUDE_NONE;
+
+ trimmed = TrimLeadingWhitespace(trimmed.substr(1));
+
base::StringPiece contents;
if (base::StartsWith(trimmed, base::StringPiece(kInclude, kIncludeLen),
base::CompareCase::SENSITIVE))
diff --git a/gn/tools/gn/c_include_iterator_unittest.cc b/gn/tools/gn/c_include_iterator_unittest.cc
index ef442635390..a88537c9289 100644
--- a/gn/tools/gn/c_include_iterator_unittest.cc
+++ b/gn/tools/gn/c_include_iterator_unittest.cc
@@ -159,3 +159,20 @@ TEST(CIncludeIterator, CStyleComments) {
EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
EXPECT_EQ("foo/bar.h", contents);
}
+
+// Tests that spaces between the hash and directive are ignored.
+TEST(CIncludeIterator, SpacesAfterHash) {
+ std::string buffer("# include \"foo/bar.h\"\n");
+
+ InputFile file(SourceFile("//foo.cc"));
+ file.SetContents(buffer);
+
+ base::StringPiece contents;
+ LocationRange range;
+
+ CIncludeIterator iter(&file);
+ EXPECT_TRUE(iter.GetNextIncludeString(&contents, &range));
+ EXPECT_EQ("foo/bar.h", contents);
+
+ EXPECT_FALSE(iter.GetNextIncludeString(&contents, &range));
+}
diff --git a/gn/tools/gn/command_analyze.cc b/gn/tools/gn/command_analyze.cc
index 9aa698b3c15..d3b24bf895e 100644
--- a/gn/tools/gn/command_analyze.cc
+++ b/gn/tools/gn/command_analyze.cc
@@ -106,6 +106,7 @@ int RunAnalyze(const std::vector<std::string>& args) {
return 1;
}
+ // Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false) || !setup->Run())
return 1;
diff --git a/gn/tools/gn/command_args.cc b/gn/tools/gn/command_args.cc
index 9980333c67d..9957508bb2a 100644
--- a/gn/tools/gn/command_args.cc
+++ b/gn/tools/gn/command_args.cc
@@ -205,6 +205,7 @@ void BuildArgJson(base::Value& dict,
}
int ListArgs(const std::string& build_dir) {
+ // Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(build_dir, false) || !setup->Run())
return 1;
@@ -408,7 +409,11 @@ 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]
+ R"(gn args: (command-line tool)
+
+ Display or configure arguments declared by the build.
+
+ gn args <out_dir> [--list] [--short] [--args] [--overrides-only]
See also "gn help buildargs" for a more high-level overview of how
build arguments work.
diff --git a/gn/tools/gn/command_check.cc b/gn/tools/gn/command_check.cc
index 99fbff502f3..3c74fc1f58f 100644
--- a/gn/tools/gn/command_check.cc
+++ b/gn/tools/gn/command_check.cc
@@ -55,7 +55,7 @@ More information
const char kCheck[] = "check";
const char kCheck_HelpShort[] = "check: Check header dependencies.";
const char kCheck_Help[] =
- R"(gn check <out_dir> [<label_pattern>] [--force]
+ R"(gn check <out_dir> [<label_pattern>] [--force] [--check-generated]
GN's include header checker validates that the includes for C-like source
files match the build dependency graph.
@@ -74,11 +74,16 @@ Command-specific switches
Ignores specifications of "check_includes = false" and checks all
target's files that match the target label.
+ --check-generated
+ Generated files are normally not checked since they do not exist
+ until after a build. With this flag, those generated files that
+ can be found on disk are also checked.
+
What gets checked
- The .gn file may specify a list of targets to be checked. 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".
+ The .gn file may specify a list of targets to be checked in the list
+ check_targets (see "gn help dotfile"). If a label pattern is specified
+ on the command line, check_targets is not used.
Targets can opt-out from checking with "check_includes = false" (see
"gn help check_includes").
@@ -88,6 +93,9 @@ What gets checked
- GN opens all C-like source files in the targets to be checked and scans
the top for includes.
+ - Generated files (that might not exist yet) are ignored unless
+ the --check-generated flag is provided.
+
- Includes with a "nogncheck" annotation are skipped (see
"gn help nogncheck").
@@ -215,9 +223,10 @@ int RunCheck(const std::vector<std::string>& args) {
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
bool force = cmdline->HasSwitch("force");
+ bool check_generated = cmdline->HasSwitch("check-generated");
if (!CheckPublicHeaders(&setup->build_settings(), all_targets,
- targets_to_check, force))
+ targets_to_check, force, check_generated))
return 1;
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) {
@@ -237,11 +246,11 @@ int RunCheck(const std::vector<std::string>& args) {
bool CheckPublicHeaders(const BuildSettings* build_settings,
const std::vector<const Target*>& all_targets,
const std::vector<const Target*>& to_check,
- bool force_check) {
+ bool force_check, bool check_generated) {
ScopedTrace trace(TraceItem::TRACE_CHECK_HEADERS, "Check headers");
scoped_refptr<HeaderChecker> header_checker(
- new HeaderChecker(build_settings, all_targets));
+ new HeaderChecker(build_settings, all_targets, check_generated));
std::vector<Err> header_errors;
header_checker->Run(to_check, force_check, &header_errors);
diff --git a/gn/tools/gn/command_clean.cc b/gn/tools/gn/command_clean.cc
index b138864f61a..b3540393aad 100644
--- a/gn/tools/gn/command_clean.cc
+++ b/gn/tools/gn/command_clean.cc
@@ -63,6 +63,7 @@ int RunClean(const std::vector<std::string>& args) {
return 1;
}
+ // Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false))
return 1;
diff --git a/gn/tools/gn/command_desc.cc b/gn/tools/gn/command_desc.cc
index 2531c7c5507..a499384c559 100644
--- a/gn/tools/gn/command_desc.cc
+++ b/gn/tools/gn/command_desc.cc
@@ -11,6 +11,7 @@
#include "base/command_line.h"
#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "tools/gn/commands.h"
#include "tools/gn/config.h"
@@ -30,6 +31,52 @@ const char kBlame[] = "blame";
const char kTree[] = "tree";
const char kAll[] = "all";
+void PrintDictValue(const base::Value* value,
+ int indentLevel,
+ bool use_first_indent) {
+ std::string indent(indentLevel * 2, ' ');
+ const base::ListValue* list_value = nullptr;
+ const base::DictionaryValue* dict_value = nullptr;
+ std::string string_value;
+ bool bool_value = false;
+ int int_value = 0;
+ if (use_first_indent)
+ OutputString(indent);
+ if (value->GetAsList(&list_value)) {
+ OutputString("[\n");
+ bool first = true;
+ for (const auto& v : *list_value) {
+ if (!first)
+ OutputString(",\n");
+ PrintDictValue(&v, indentLevel + 1, true);
+ first = false;
+ }
+ OutputString("\n" + indent + "]");
+ } else if (value->GetAsString(&string_value)) {
+ OutputString("\"" + string_value + "\"");
+ } else if (value->GetAsBoolean(&bool_value)) {
+ OutputString(bool_value ? "true" : "false");
+ } else if (value->GetAsDictionary(&dict_value)) {
+ OutputString("{\n");
+ std::string indent_plus_one((indentLevel + 1) * 2, ' ');
+ base::DictionaryValue::Iterator iter(*dict_value);
+ bool first = true;
+ while (!iter.IsAtEnd()) {
+ if (!first)
+ OutputString(",\n");
+ OutputString(indent_plus_one + iter.key() + " = ");
+ PrintDictValue(&iter.value(), indentLevel + 1, false);
+ iter.Advance();
+ first = false;
+ }
+ OutputString("\n" + indent + "}");
+ } else if (value->GetAsInteger(&int_value)) {
+ OutputString(base::IntToString(int_value));
+ } else if (value->is_none()) {
+ OutputString("<null>");
+ }
+}
+
// Prints value with specified indentation level
void PrintValue(const base::Value* value, int indentLevel) {
std::string indent(indentLevel * 2, ' ');
@@ -37,6 +84,7 @@ void PrintValue(const base::Value* value, int indentLevel) {
const base::DictionaryValue* dict_value = nullptr;
std::string string_value;
bool bool_value = false;
+ int int_value = 0;
if (value->GetAsList(&list_value)) {
for (const auto& v : *list_value) {
PrintValue(&v, indentLevel);
@@ -56,13 +104,23 @@ void PrintValue(const base::Value* value, int indentLevel) {
PrintValue(&iter.value(), indentLevel + 1);
iter.Advance();
}
+ } else if (value->GetAsInteger(&int_value)) {
+ OutputString(indent);
+ OutputString(base::IntToString(int_value));
+ OutputString("\n");
} else if (value->is_none()) {
OutputString(indent + "<null>\n");
}
}
// Default handler for property
-void DefaultHandler(const std::string& name, const base::Value* value) {
+void DefaultHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
OutputString("\n");
OutputString(name);
OutputString("\n");
@@ -71,9 +129,32 @@ void DefaultHandler(const std::string& name, const base::Value* value) {
// Specific handler for properties that need different treatment
+// Prints the dict in GN scope-sytle.
+void MetadataHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintDictValue(value, 0, true);
+ OutputString("\n");
+ return;
+ }
+ OutputString("\n");
+ OutputString(name);
+ OutputString("\n");
+ PrintDictValue(value, 1, true);
+ OutputString("\n");
+}
+
// Prints label and property value on one line, capitalizing the label.
-void LabelHandler(std::string name, const base::Value* value) {
- name[0] = base::ToUpperASCII(name[0]);
+void LabelHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
+ std::string label = name;
+ label[0] = base::ToUpperASCII(label[0]);
std::string string_value;
if (value->GetAsString(&string_value)) {
OutputString(name + ": ", DECORATION_YELLOW);
@@ -81,51 +162,68 @@ void LabelHandler(std::string name, const base::Value* value) {
}
}
-void VisibilityHandler(const std::string& name, const base::Value* value) {
+void VisibilityHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
const base::ListValue* list;
if (value->GetAsList(&list)) {
if (list->empty()) {
base::Value str("(no visibility)");
- DefaultHandler(name, &str);
+ DefaultHandler(name, &str, value_only);
} else {
- DefaultHandler(name, value);
+ DefaultHandler(name, value, value_only);
}
}
}
-void PublicHandler(const std::string& name, const base::Value* value) {
+void PublicHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
+ if (value_only) {
+ PrintValue(value, 0);
+ return;
+ }
std::string p;
if (value->GetAsString(&p)) {
if (p == "*") {
base::Value str("[All headers listed in the sources are public.]");
- DefaultHandler(name, &str);
+ DefaultHandler(name, &str, value_only);
return;
}
}
- DefaultHandler(name, value);
+ DefaultHandler(name, value, value_only);
}
-void ConfigsHandler(const std::string& name, const base::Value* value) {
+void ConfigsHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
if (tree)
- DefaultHandler(name + " tree (in order applying)", value);
+ DefaultHandler(name + " tree (in order applying)", value, value_only);
else
- DefaultHandler(name + " (in order applying, try also --tree)", value);
+ DefaultHandler(name + " (in order applying, try also --tree)", value,
+ value_only);
}
-void DepsHandler(const std::string& name, const base::Value* value) {
+void DepsHandler(const std::string& name,
+ const base::Value* value,
+ bool value_only) {
bool tree = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
bool all = base::CommandLine::ForCurrentProcess()->HasSwitch(kTree);
if (tree) {
- DefaultHandler("Dependency tree", value);
+ DefaultHandler("Dependency tree", value, value_only);
} else {
if (!all) {
DefaultHandler(
"Direct dependencies "
"(try also \"--all\", \"--tree\", or even \"--all --tree\")",
- value);
+ value, value_only);
} else {
- DefaultHandler("All recursive dependencies", value);
+ DefaultHandler("All recursive dependencies", value, value_only);
}
}
}
@@ -154,9 +252,64 @@ void ProcessOutputs(base::DictionaryValue* target) {
}
}
+using DescHandlerFunc = void (*)(const std::string& name,
+ const base::Value* value,
+ bool value_only);
+std::map<std::string, DescHandlerFunc> GetHandlers() {
+ return {{"type", LabelHandler},
+ {"toolchain", LabelHandler},
+ {variables::kVisibility, VisibilityHandler},
+ {variables::kMetadata, MetadataHandler},
+ {variables::kTestonly, DefaultHandler},
+ {variables::kCheckIncludes, DefaultHandler},
+ {variables::kAllowCircularIncludesFrom, DefaultHandler},
+ {variables::kSources, DefaultHandler},
+ {variables::kPublic, PublicHandler},
+ {variables::kInputs, DefaultHandler},
+ {variables::kConfigs, ConfigsHandler},
+ {variables::kPublicConfigs, ConfigsHandler},
+ {variables::kAllDependentConfigs, ConfigsHandler},
+ {variables::kScript, DefaultHandler},
+ {variables::kArgs, DefaultHandler},
+ {variables::kDepfile, DefaultHandler},
+ {"bundle_data", DefaultHandler},
+ {variables::kArflags, DefaultHandler},
+ {variables::kAsmflags, DefaultHandler},
+ {variables::kCflags, DefaultHandler},
+ {variables::kCflagsC, DefaultHandler},
+ {variables::kCflagsCC, DefaultHandler},
+ {variables::kCflagsObjC, DefaultHandler},
+ {variables::kCflagsObjCC, DefaultHandler},
+ {variables::kDefines, DefaultHandler},
+ {variables::kIncludeDirs, DefaultHandler},
+ {variables::kLdflags, DefaultHandler},
+ {variables::kPrecompiledHeader, DefaultHandler},
+ {variables::kPrecompiledSource, DefaultHandler},
+ {variables::kDeps, DepsHandler},
+ {variables::kLibs, DefaultHandler},
+ {variables::kLibDirs, DefaultHandler},
+ {variables::kDataKeys, DefaultHandler},
+ {variables::kRebase, DefaultHandler},
+ {variables::kWalkKeys, DefaultHandler},
+ {variables::kWriteOutputConversion, DefaultHandler},
+ {"runtime_deps", DefaultHandler}};
+}
+
+void HandleProperty(const std::string& what,
+ const std::map<std::string, DescHandlerFunc>& handler_map,
+ std::unique_ptr<base::Value>& v,
+ std::unique_ptr<base::DictionaryValue>& dict) {
+ if (dict->Remove(what, &v)) {
+ auto pair = handler_map.find(what);
+ if (pair != handler_map.end())
+ pair->second(what, v.get(), false);
+ }
+}
+
bool PrintTarget(const Target* target,
const std::string& what,
bool single_target,
+ const std::map<std::string, DescHandlerFunc>& handler_map,
bool all,
bool tree,
bool blame) {
@@ -168,10 +321,12 @@ bool PrintTarget(const Target* target,
"\".\n");
return false;
}
- // Print single value, without any headers
+ // Print single value
if (!what.empty() && dict->size() == 1 && single_target) {
base::DictionaryValue::Iterator iter(*dict);
- PrintValue(&iter.value(), 0);
+ auto pair = handler_map.find(what);
+ if (pair != handler_map.end())
+ pair->second(what, &iter.value(), true);
return true;
}
@@ -180,51 +335,51 @@ bool PrintTarget(const Target* target,
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);
+ HandleProperty("type", handler_map, v, dict);
+ HandleProperty("toolchain", handler_map, v, dict);
+ HandleProperty(variables::kVisibility, handler_map, v, dict);
+ HandleProperty(variables::kMetadata, handler_map, v, dict);
+ HandleProperty(variables::kTestonly, handler_map, v, dict);
+ HandleProperty(variables::kCheckIncludes, handler_map, v, dict);
+ HandleProperty(variables::kAllowCircularIncludesFrom, handler_map, v, dict);
+ HandleProperty(variables::kSources, handler_map, v, dict);
+ HandleProperty(variables::kPublic, handler_map, v, dict);
+ HandleProperty(variables::kInputs, handler_map, v, dict);
+ HandleProperty(variables::kConfigs, handler_map, v, dict);
+ HandleProperty(variables::kPublicConfigs, handler_map, v, dict);
+ HandleProperty(variables::kAllDependentConfigs, handler_map, v, dict);
+ HandleProperty(variables::kScript, handler_map, v, dict);
+ HandleProperty(variables::kArgs, handler_map, v, dict);
+ HandleProperty(variables::kDepfile, handler_map, v, dict);
ProcessOutputs(dict.get());
- 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
+ HandleProperty("bundle_data", handler_map, v, dict);
+ HandleProperty(variables::kArflags, handler_map, v, dict);
+ HandleProperty(variables::kAsmflags, handler_map, v, dict);
+ HandleProperty(variables::kCflags, handler_map, v, dict);
+ HandleProperty(variables::kCflagsC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsCC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
+ HandleProperty(variables::kDefines, handler_map, v, dict);
+ HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
+ HandleProperty(variables::kLdflags, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
+ HandleProperty(variables::kDeps, handler_map, v, dict);
+ HandleProperty(variables::kLibs, handler_map, v, dict);
+ HandleProperty(variables::kLibDirs, handler_map, v, dict);
+ HandleProperty(variables::kDataKeys, handler_map, v, dict);
+ HandleProperty(variables::kRebase, handler_map, v, dict);
+ HandleProperty(variables::kWalkKeys, handler_map, v, dict);
+ HandleProperty(variables::kWriteOutputConversion, handler_map, v, dict);
+
+#undef HandleProperty
// Process the rest (if any)
base::DictionaryValue::Iterator iter(*dict);
while (!iter.IsAtEnd()) {
- DefaultHandler(iter.key(), &iter.value());
+ DefaultHandler(iter.key(), &iter.value(), false);
iter.Advance();
}
@@ -233,17 +388,20 @@ bool PrintTarget(const Target* target,
bool PrintConfig(const Config* config,
const std::string& what,
- bool single_config) {
+ bool single_config,
+ const std::map<std::string, DescHandlerFunc>& handler_map) {
std::unique_ptr<base::DictionaryValue> dict =
DescBuilder::DescriptionForConfig(config, what);
if (!what.empty() && dict->empty()) {
OutputString("Don't know how to display \"" + what + "\" for a config.\n");
return false;
}
- // Print single value, without any headers
+ // Print single value
if (!what.empty() && dict->size() == 1 && single_config) {
base::DictionaryValue::Iterator iter(*dict);
- PrintValue(&iter.value(), 0);
+ auto pair = handler_map.find(what);
+ if (pair != handler_map.end())
+ pair->second(what, &iter.value(), true);
return true;
}
@@ -252,34 +410,29 @@ bool PrintConfig(const Config* config,
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);
+ HandleProperty("toolchain", handler_map, v, dict);
if (!config->configs().empty()) {
OutputString(
"(This is a composite config, the values below are after the\n"
"expansion of the child configs.)\n");
}
- 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
+ HandleProperty(variables::kArflags, handler_map, v, dict);
+ HandleProperty(variables::kAsmflags, handler_map, v, dict);
+ HandleProperty(variables::kCflags, handler_map, v, dict);
+ HandleProperty(variables::kCflagsC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsCC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjC, handler_map, v, dict);
+ HandleProperty(variables::kCflagsObjCC, handler_map, v, dict);
+ HandleProperty(variables::kDefines, handler_map, v, dict);
+ HandleProperty(variables::kIncludeDirs, handler_map, v, dict);
+ HandleProperty(variables::kInputs, handler_map, v, dict);
+ HandleProperty(variables::kLdflags, handler_map, v, dict);
+ HandleProperty(variables::kLibs, handler_map, v, dict);
+ HandleProperty(variables::kLibDirs, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledHeader, handler_map, v, dict);
+ HandleProperty(variables::kPrecompiledSource, handler_map, v, dict);
+
+#undef HandleProperty
return true;
}
@@ -292,8 +445,10 @@ 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]
+ R"(gn desc
+
+ gn desc <out_dir> <label or pattern> [<what to show>] [--blame]
+ [--format=json]
Displays information about a given target or config. The build parameters
will be taken for the build in the given <out_dir>.
@@ -315,6 +470,7 @@ Possibilities for <what to show>
cflags_cc [--blame]
check_includes
configs [--tree] (see below)
+ data_keys
defines [--blame]
depfile
deps [--all] [--tree] (see below)
@@ -323,13 +479,17 @@ Possibilities for <what to show>
ldflags [--blame]
lib_dirs
libs
+ metadata
+ output_conversion
outputs
public_configs
public
+ rebase
script
sources
testonly
visibility
+ walk_keys
runtime_deps
Compute all runtime deps for the given target. This is a computed list
@@ -488,6 +648,7 @@ int RunDesc(const std::vector<std::string>& args) {
} else {
// Regular (non-json) formatted output
bool multiple_outputs = (target_matches.size() + config_matches.size()) > 1;
+ std::map<std::string, DescHandlerFunc> handlers = GetHandlers();
bool printed_output = false;
for (const Target* target : target_matches) {
@@ -495,7 +656,7 @@ int RunDesc(const std::vector<std::string>& args) {
OutputString("\n\n");
printed_output = true;
- if (!PrintTarget(target, what_to_print, !multiple_outputs,
+ if (!PrintTarget(target, what_to_print, !multiple_outputs, handlers,
cmdline->HasSwitch(kAll), cmdline->HasSwitch(kTree),
cmdline->HasSwitch(kBlame)))
return 1;
@@ -505,7 +666,7 @@ int RunDesc(const std::vector<std::string>& args) {
OutputString("\n\n");
printed_output = true;
- if (!PrintConfig(config, what_to_print, !multiple_outputs))
+ if (!PrintConfig(config, what_to_print, !multiple_outputs, handlers))
return 1;
}
}
diff --git a/gn/tools/gn/command_format.cc b/gn/tools/gn/command_format.cc
index 2d6cea4bdc5..5b9580bdbe4 100644
--- a/gn/tools/gn/command_format.cc
+++ b/gn/tools/gn/command_format.cc
@@ -10,6 +10,7 @@
#include "base/command_line.h"
#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
#include "base/macros.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -26,12 +27,14 @@ namespace commands {
const char kSwitchDryRun[] = "dry-run";
const char kSwitchDumpTree[] = "dump-tree";
+const char kSwitchDumpTreeText[] = "text";
+const char kSwitchDumpTreeJSON[] = "json";
const char kSwitchStdin[] = "stdin";
const char kFormat[] = "format";
-const char kFormat_HelpShort[] = "format: Format .gn file.";
+const char kFormat_HelpShort[] = "format: Format .gn files.";
const char kFormat_Help[] =
- R"(gn format [--dump-tree] (--stdin | <build_file>)
+ R"(gn format [--dump-tree] (--stdin | <list of build_files...>)
Formats .gn file to a standard format.
@@ -55,16 +58,16 @@ Arguments
- 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.
+ --dump-tree[=( text | json )]
+ Dumps the parse tree to stdout and does not update the file or print
+ formatted output. If no format is specified, text format will be used.
--stdin
Read input from stdin and write to stdout rather than update a file
in-place.
Examples
- gn format //some/BUILD.gn
+ gn format //some/BUILD.gn //some/other/BUILD.gn //and/another/BUILD.gn
gn format some\\BUILD.gn
gn format /abspath/some/BUILD.gn
gn format --stdin
@@ -111,8 +114,8 @@ class Printer {
// Format a list of values using the given style.
enum SequenceStyle {
kSequenceStyleList,
- kSequenceStyleBlock,
kSequenceStyleBracedBlock,
+ kSequenceStyleBracedBlockAlreadyOpen,
};
struct Metrics {
@@ -148,6 +151,11 @@ class Printer {
// (both sorted alphabetically).
void SortIfSourcesOrDeps(const BinaryOpNode* binop);
+ // Sort contiguous import() function calls in the given ordered list of
+ // statements (the body of a block or scope).
+ template <class PARSENODE>
+ void SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements);
+
// Heuristics to decide if there should be a blank line added between two
// items. For various "small" items, it doesn't look nice if there's too much
// vertical whitespace added.
@@ -324,11 +332,15 @@ void Printer::AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop) {
}
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;
+ if (const Comments* comments = binop->comments()) {
+ const std::vector<Token>& before = comments->before();
+ if (!before.empty() &&
+ (before.front().value().as_string() == "# NOSORT" ||
+ before.back().value().as_string() == "# NOSORT")) {
+ // Allow disabling of sort for specific actions that might be
+ // order-sensitive.
+ return;
+ }
}
const IdentifierNode* ident = binop->left()->AsIdentifier();
const ListNode* list = binop->right()->AsList();
@@ -343,6 +355,92 @@ void Printer::SortIfSourcesOrDeps(const BinaryOpNode* binop) {
}
}
+template <class PARSENODE>
+void Printer::SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements) {
+ // Build a set of ranges by indices of FunctionCallNode's that are imports.
+
+ std::vector<std::vector<size_t>> import_statements;
+
+ auto is_import = [](const PARSENODE* p) {
+ const FunctionCallNode* func_call = p->AsFunctionCall();
+ return func_call && func_call->function().value() == "import";
+ };
+
+ std::vector<size_t> current_group;
+ for (size_t i = 0; i < statements.size(); ++i) {
+ if (is_import(statements[i].get())) {
+ if (i > 0 && (!is_import(statements[i - 1].get()) ||
+ ShouldAddBlankLineInBetween(statements[i - 1].get(),
+ statements[i].get()))) {
+ if (!current_group.empty()) {
+ import_statements.push_back(current_group);
+ current_group.clear();
+ }
+ }
+ current_group.push_back(i);
+ }
+ }
+
+ if (!current_group.empty())
+ import_statements.push_back(current_group);
+
+ struct CompareByImportFile {
+ bool operator()(const std::unique_ptr<PARSENODE>& a,
+ const std::unique_ptr<PARSENODE>& b) const {
+ const auto& a_args = a->AsFunctionCall()->args()->contents();
+ const auto& b_args = b->AsFunctionCall()->args()->contents();
+ base::StringPiece a_name;
+ base::StringPiece b_name;
+ if (!a_args.empty())
+ a_name = a_args[0]->AsLiteral()->value().value();
+ if (!b_args.empty())
+ b_name = b_args[0]->AsLiteral()->value().value();
+
+ auto is_absolute = [](base::StringPiece import) {
+ return import.size() >= 3 && import[0] == '"' && import[1] == '/' &&
+ import[2] == '/';
+ };
+ int a_is_rel = !is_absolute(a_name);
+ int b_is_rel = !is_absolute(b_name);
+
+ return std::tie(a_is_rel, a_name) < std::tie(b_is_rel, b_name);
+ }
+ };
+
+ int line_after_previous = -1;
+
+ for (const auto& group : import_statements) {
+ size_t begin = group[0];
+ size_t end = group.back() + 1;
+
+ // Save the original line number so that ranges can be re-assigned. They're
+ // contiguous because of the partitioning code above. Later formatting
+ // relies on correct line number to know whether to insert blank lines,
+ // which is why these need to be fixed up. Additionally, to handle multiple
+ // imports on one line, they're assigned sequential line numbers, and
+ // subsequent blocks will be gapped from them.
+ int start_line =
+ std::max(statements[begin]->GetRange().begin().line_number(),
+ line_after_previous + 1);
+
+ std::sort(statements.begin() + begin, statements.begin() + end,
+ CompareByImportFile());
+
+ const PARSENODE* prev = nullptr;
+ for (size_t i = begin; i < end; ++i) {
+ const PARSENODE* node = statements[i].get();
+ int line_number =
+ prev ? prev->GetRange().end().line_number() + 1 : start_line;
+ if (node->comments() && !node->comments()->before().empty())
+ line_number++;
+ const_cast<FunctionCallNode*>(node->AsFunctionCall())
+ ->SetNewLocation(line_number);
+ prev = node;
+ line_after_previous = line_number + 1;
+ }
+ }
+}
+
bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
const ParseNode* b) {
LocationRange a_range = a->GetRange();
@@ -382,6 +480,9 @@ void Printer::Block(const ParseNode* root) {
}
}
+ SortImports(const_cast<std::vector<std::unique_ptr<ParseNode>>&>(
+ block->statements()));
+
size_t i = 0;
for (const auto& stmt : block->statements()) {
Expr(stmt.get(), kPrecedenceLowest, std::string());
@@ -627,10 +728,10 @@ int Printer::Expr(const ParseNode* root,
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);
+ Expr(condition->condition(), kPrecedenceLowest, ") {");
+ Sequence(kSequenceStyleBracedBlockAlreadyOpen,
+ condition->if_true()->statements(), condition->if_true()->End(),
+ false);
if (condition->if_false()) {
Print(" else ");
// If it's a block it's a bare 'else', otherwise it's an 'else if'. See
@@ -687,13 +788,18 @@ void Printer::Sequence(SequenceStyle style,
const std::vector<std::unique_ptr<PARSENODE>>& list,
const ParseNode* end,
bool force_multiline) {
- if (style == kSequenceStyleList)
+ if (style == kSequenceStyleList) {
Print("[");
- else if (style == kSequenceStyleBracedBlock)
+ } else if (style == kSequenceStyleBracedBlock) {
Print("{");
+ } else if (style == kSequenceStyleBracedBlockAlreadyOpen) {
+ style = kSequenceStyleBracedBlock;
+ }
- if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
+ if (style == kSequenceStyleBracedBlock) {
force_multiline = true;
+ SortImports(const_cast<std::vector<std::unique_ptr<PARSENODE>>&>(list));
+ }
force_multiline |= ListWillBeMultiline(list, end);
@@ -956,12 +1062,19 @@ bool Printer::ListWillBeMultiline(
return false;
}
-void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
- if (dump_tree) {
+void DoFormat(const ParseNode* root, TreeDumpMode dump_tree,
+ std::string* output) {
+ if (dump_tree == TreeDumpMode::kPlainText) {
std::ostringstream os;
- root->Print(os, 0);
+ RenderToText(root->GetJSONNode(), 0, os);
fprintf(stderr, "%s", os.str().c_str());
+ } else if (dump_tree == TreeDumpMode::kJSON) {
+ std::string os;
+ base::JSONWriter::WriteWithOptions(root->GetJSONNode(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT, &os);
+ fprintf(stderr, "%s", os.c_str());
}
+
Printer pr;
pr.Block(root);
*output = pr.String();
@@ -988,7 +1101,7 @@ std::string ReadStdin() {
bool FormatFileToString(Setup* setup,
const SourceFile& file,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output) {
Err err;
const ParseNode* parse_node =
@@ -1003,7 +1116,7 @@ bool FormatFileToString(Setup* setup,
}
bool FormatStringToString(const std::string& input,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output) {
SourceFile source_file;
InputFile file(source_file);
@@ -1030,8 +1143,23 @@ bool FormatStringToString(const std::string& input,
int RunFormat(const std::vector<std::string>& args) {
bool dry_run =
base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
- bool dump_tree =
- base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
+ TreeDumpMode dump_tree = TreeDumpMode::kInactive;
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree)) {
+ std::string tree_type = base::CommandLine::ForCurrentProcess()->
+ GetSwitchValueASCII(kSwitchDumpTree);
+ if (tree_type == kSwitchDumpTreeJSON) {
+ dump_tree = TreeDumpMode::kJSON;
+ } else if (tree_type.empty() || tree_type == kSwitchDumpTreeText) {
+ dump_tree = TreeDumpMode::kPlainText;
+ } else {
+ Err(Location(),
+ tree_type + " is an invalid value for --dump-tree. Specify "
+ "\"" + kSwitchDumpTreeText + "\" or \"" + kSwitchDumpTreeJSON +
+ "\".\n")
+ .PrintToStdout();
+ return 1;
+ }
+ }
bool from_stdin =
base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
@@ -1054,10 +1182,8 @@ int RunFormat(const std::vector<std::string>& args) {
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")
+ if (args.size() == 0) {
+ Err(Location(), "Expecting one or more arguments, see `gn help format`.\n")
.PrintToStdout();
return 1;
}
@@ -1066,39 +1192,43 @@ int RunFormat(const std::vector<std::string>& args) {
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;
- }
+ // TODO(scottmg): Eventually, this list of files should be processed in
+ // parallel.
+ for (const auto& arg : args) {
+ Err err;
+ SourceFile file =
+ source_dir.ResolveRelativeFile(Value(nullptr, arg), &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
- std::string output_string;
- if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
- if (!dump_tree) {
- // 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("\"."))
+ std::string output_string;
+ if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
+ if (dump_tree == TreeDumpMode::kInactive) {
+ // Update the file in-place.
+ base::FilePath to_write = setup.build_settings().GetFullPath(file);
+ std::string original_contents;
+ if (!base::ReadFileToString(to_write, &original_contents)) {
+ Err(Location(), std::string("Couldn't read \"") +
+ FilePathToUTF8(to_write) +
+ std::string("\" for comparison."))
.PrintToStdout();
return 1;
}
- printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
+ if (dry_run)
+ return original_contents == output_string ? 0 : 2;
+ if (original_contents != output_string) {
+ if (base::WriteFile(to_write, output_string.data(),
+ static_cast<int>(output_string.size())) == -1) {
+ Err(Location(),
+ std::string("Failed to write formatted output back to \"") +
+ FilePathToUTF8(to_write) + std::string("\"."))
+ .PrintToStdout();
+ return 1;
+ }
+ printf("Wrote formatted to '%s'.\n", FilePathToUTF8(to_write).c_str());
+ }
}
}
}
diff --git a/gn/tools/gn/command_format.h b/gn/tools/gn/command_format.h
index 413937e9ff9..785b0706be4 100644
--- a/gn/tools/gn/command_format.h
+++ b/gn/tools/gn/command_format.h
@@ -12,13 +12,25 @@ class SourceFile;
namespace commands {
+enum class TreeDumpMode {
+ // Normal operation mode. Format the input file.
+ kInactive,
+
+ // Output the token tree with indented plain text. For debugging.
+ kPlainText,
+
+ // Output the token tree in JSON format. Used for exporting a tree to another
+ // program.
+ kJSON
+};
+
bool FormatFileToString(Setup* setup,
const SourceFile& file,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output);
bool FormatStringToString(const std::string& input,
- bool dump_tree,
+ TreeDumpMode dump_tree,
std::string* output);
} // namespace commands
diff --git a/gn/tools/gn/command_format_unittest.cc b/gn/tools/gn/command_format_unittest.cc
index cd69e5d44ad..b00f6acb989 100644
--- a/gn/tools/gn/command_format_unittest.cc
+++ b/gn/tools/gn/command_format_unittest.cc
@@ -23,14 +23,19 @@ using FormatTest = TestWithScheduler;
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)); \
+ &setup, SourceFile("//tools/gn/format_test_data/" #n ".gn"), \
+ commands::TreeDumpMode::kInactive, &out)); \
ASSERT_TRUE(base::ReadFileToString( \
base::FilePath(FILE_PATH_LITERAL("tools/gn/format_test_data/") \
FILE_PATH_LITERAL(#n) \
FILE_PATH_LITERAL(".golden")), \
&expected)); \
EXPECT_EQ(expected, out); \
+ /* Make sure formatting the output doesn't cause further changes. */ \
+ std::string out_again; \
+ EXPECT_TRUE(commands::FormatStringToString(out, \
+ commands::TreeDumpMode::kInactive, &out_again)); \
+ ASSERT_EQ(out, out_again); \
}
// These are expanded out this way rather than a runtime loop so that
@@ -106,3 +111,8 @@ FORMAT_TEST(067)
FORMAT_TEST(068)
FORMAT_TEST(069)
FORMAT_TEST(070)
+FORMAT_TEST(071)
+FORMAT_TEST(072)
+FORMAT_TEST(073)
+FORMAT_TEST(074)
+FORMAT_TEST(075)
diff --git a/gn/tools/gn/command_gen.cc b/gn/tools/gn/command_gen.cc
index 82046d2e583..46d8b897d4e 100644
--- a/gn/tools/gn/command_gen.cc
+++ b/gn/tools/gn/command_gen.cc
@@ -39,6 +39,7 @@ const char kSwitchIdeValueVs[] = "vs";
const char kSwitchIdeValueVs2013[] = "vs2013";
const char kSwitchIdeValueVs2015[] = "vs2015";
const char kSwitchIdeValueVs2017[] = "vs2017";
+const char kSwitchIdeValueVs2019[] = "vs2019";
const char kSwitchIdeValueWinSdk[] = "winsdk";
const char kSwitchIdeValueXcode[] = "xcode";
const char kSwitchIdeValueJson[] = "json";
@@ -200,12 +201,15 @@ bool RunIdeWriter(const std::string& ide,
}
return res;
} else if (ide == kSwitchIdeValueVs || ide == kSwitchIdeValueVs2013 ||
- ide == kSwitchIdeValueVs2015 || ide == kSwitchIdeValueVs2017) {
+ ide == kSwitchIdeValueVs2015 || ide == kSwitchIdeValueVs2017 ||
+ ide == kSwitchIdeValueVs2019) {
VisualStudioWriter::Version version = VisualStudioWriter::Version::Vs2017;
if (ide == kSwitchIdeValueVs2013)
version = VisualStudioWriter::Version::Vs2013;
else if (ide == kSwitchIdeValueVs2015)
version = VisualStudioWriter::Version::Vs2015;
+ else if (ide == kSwitchIdeValueVs2019)
+ version = VisualStudioWriter::Version::Vs2019;
std::string sln_name;
if (command_line->HasSwitch(kSwitchSln))
@@ -333,6 +337,7 @@ IDE options
"vs2013" - Visual Studio 2013 project/solution files.
"vs2015" - Visual Studio 2015 project/solution files.
"vs2017" - Visual Studio 2017 project/solution files.
+ "vs2019" - Visual Studio 2019 project/solution files.
"xcode" - Xcode workspace/solution files.
"qtcreator" - QtCreator project files.
"json" - JSON file containing target information
@@ -436,6 +441,10 @@ int RunGen(const std::vector<std::string>& args) {
// Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup();
+ // Generate an empty args.gn file if it does not exists
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kArgs)) {
+ setup->set_gen_empty_args(true);
+ }
if (!setup->DoSetup(args[0], true))
return 1;
diff --git a/gn/tools/gn/command_help.cc b/gn/tools/gn/command_help.cc
index cff20522a6d..543f5d19e8d 100644
--- a/gn/tools/gn/command_help.cc
+++ b/gn/tools/gn/command_help.cc
@@ -29,65 +29,86 @@ namespace commands {
namespace {
+// Some names exist in multiple sections, these prefixes are used for the
+// internal links to disambiguate when writing markdown.
+const char kCommandLinkPrefix[] = "cmd_";
+const char kFunctionLinkPrefix[] = "func_";
+const char kVariableLinkPrefix[] = "var_";
+
void PrintToplevelHelp() {
PrintSectionHelp("Commands", "<command>", "commands");
for (const auto& cmd : commands::GetCommands())
- PrintShortHelp(cmd.second.help_short);
+ PrintShortHelp(cmd.second.help_short, kCommandLinkPrefix + cmd.first);
// Target declarations.
PrintSectionHelp("Target declarations", "<function>", "targets");
for (const auto& func : functions::GetFunctions()) {
if (func.second.is_target)
- PrintShortHelp(func.second.help_short);
+ PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
}
// Functions.
PrintSectionHelp("Buildfile functions", "<function>", "functions");
for (const auto& func : functions::GetFunctions()) {
if (!func.second.is_target)
- PrintShortHelp(func.second.help_short);
+ PrintShortHelp(func.second.help_short, kFunctionLinkPrefix + func.first);
}
// Built-in variables.
PrintSectionHelp("Built-in predefined variables", "<variable>",
"predefined_variables");
- for (const auto& builtin : variables::GetBuiltinVariables())
- PrintShortHelp(builtin.second.help_short);
+ for (const auto& builtin : variables::GetBuiltinVariables()) {
+ PrintShortHelp(builtin.second.help_short,
+ kVariableLinkPrefix + builtin.first);
+ }
// Target variables.
PrintSectionHelp("Variables you set in targets", "<variable>",
"target_variables");
- for (const auto& target : variables::GetTargetVariables())
- PrintShortHelp(target.second.help_short);
+ for (const auto& target : variables::GetTargetVariables()) {
+ PrintShortHelp(target.second.help_short,
+ kVariableLinkPrefix + target.first);
+ }
PrintSectionHelp("Other help topics", "", "other");
PrintShortHelp("all: Print all the help at once");
- PrintShortHelp("buildargs: How build arguments work.");
- 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("buildargs: How build arguments work.", "buildargs");
+ PrintShortHelp("dotfile: Info about the toplevel .gn file.", "dotfile");
+ PrintShortHelp("execution: Build graph and execution overview.", "execution");
+ PrintShortHelp("grammar: Language and grammar for GN build files.",
+ "grammar");
PrintShortHelp(
- "input_conversion: Processing input from exec_script and read_file.");
- 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.");
+ "input_conversion: Processing input from exec_script and read_file.",
+ "input_conversion");
+ PrintShortHelp("label_pattern: Matching more than one label.",
+ "label_pattern");
+ PrintShortHelp("labels: About labels.", "labels");
+ PrintShortHelp("ninja_rules: How Ninja build rules are named.",
+ "ninja_rules");
+ PrintShortHelp("nogncheck: Annotating includes for checking.", "nogncheck");
PrintShortHelp(
- "output_conversion: Specifies how to transform a value to output.");
- PrintShortHelp("runtime_deps: How runtime dependency computation works.");
- PrintShortHelp("source_expansion: Map sources to outputs for scripts.");
- PrintShortHelp("switches: Show available command-line switches.");
+ "output_conversion: Specifies how to transform a value to output.",
+ "output_conversion");
+ PrintShortHelp("runtime_deps: How runtime dependency computation works.",
+ "runtime_deps");
+ PrintShortHelp("source_expansion: Map sources to outputs for scripts.",
+ "source_expansion");
+ PrintShortHelp("switches: Show available command-line switches.", "switch_list");
}
void PrintSwitchHelp() {
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
bool is_markdown = cmdline->HasSwitch(switches::kMarkdown);
- 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");
+ // This uses "switch_list" for the tag because Markdown seems to generate
+ // implicit tags for headings that match the strings, and some headings are
+ // labeled "switches".
+ PrintLongHelp(R"(Available global switches
+
+ Do "gn help --the_switch_you_want_help_on" for more. Individual commands may
+ take command-specific switches not listed here. See the help on your specific
+ command for more.
+)", "switch_list");
if (is_markdown)
OutputString("```\n", DECORATION_NONE);
@@ -120,58 +141,65 @@ void PrintAllHelp() {
OutputString("\n");
- if (is_markdown)
- OutputString("## <a name=\"commands\"></a>Commands\n\n");
+ if (is_markdown) {
+ OutputString("## <a name=\"commands\"></a>Commands\n\n", DECORATION_NONE,
+ NO_ESCAPING);
+ }
for (const auto& c : commands::GetCommands())
- PrintLongHelp(c.second.help);
+ PrintLongHelp(c.second.help, kCommandLinkPrefix + c.first);
- if (is_markdown)
- OutputString("## <a name=\"targets\"></a>Target declarations\n\n");
+ if (is_markdown) {
+ OutputString("## <a name=\"targets\"></a>Target declarations\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
for (const auto& f : functions::GetFunctions()) {
if (f.second.is_target)
- PrintLongHelp(f.second.help);
+ PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
}
- if (is_markdown)
- OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n");
+ if (is_markdown) {
+ OutputString("## <a name=\"functions\"></a>Buildfile functions\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
for (const auto& f : functions::GetFunctions()) {
if (!f.second.is_target)
- PrintLongHelp(f.second.help);
+ PrintLongHelp(f.second.help, kFunctionLinkPrefix + f.first);
}
if (is_markdown) {
OutputString(
"## <a name=\"predefined_variables\"></a>"
- "Built-in predefined variables\n\n");
+ "Built-in predefined variables\n\n",
+ DECORATION_NONE, NO_ESCAPING);
}
for (const auto& v : variables::GetBuiltinVariables())
- PrintLongHelp(v.second.help);
+ PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
if (is_markdown) {
OutputString(
"## <a name=\"target_variables\"></a>"
- "Variables you set in targets\n\n");
+ "Variables you set in targets\n\n",
+ DECORATION_NONE, NO_ESCAPING);
}
for (const auto& v : variables::GetTargetVariables())
- PrintLongHelp(v.second.help);
+ PrintLongHelp(v.second.help, kVariableLinkPrefix + v.first);
- if (is_markdown)
- OutputString("## <a name=\"other\"></a>Other help topics\n\n");
+ if (is_markdown) {
+ OutputString("## <a name=\"other\"></a>Other help topics\n\n",
+ DECORATION_NONE, NO_ESCAPING);
+ }
PrintLongHelp(kBuildArgs_Help, "buildargs");
PrintLongHelp(kDotfile_Help, "dotfile");
PrintLongHelp(kExecution_Help, "execution");
PrintLongHelp(kGrammar_Help, "grammar");
- PrintLongHelp(kInputOutputConversion_Help, "input_conversion");
+ PrintLongHelp(kInputOutputConversion_Help, "io_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();
}
@@ -188,6 +216,18 @@ bool PrintHelpOnSwitch(const std::string& what) {
return true;
}
+// Special-case help for ambiguous "args" case.
+void PrintArgsHelp() {
+ PrintLongHelp(
+ "The string \"args\" is both a command and a variable for action "
+ "targets.\nShowing help for both...\n\n");
+ PrintLongHelp(commands::kArgs_Help);
+ PrintLongHelp(
+ "\n----------------------------------------------------------------------"
+ "---------\n\n");
+ PrintLongHelp(variables::kArgs_Help);
+}
+
} // namespace
const char kHelp[] = "help";
@@ -236,6 +276,12 @@ int RunHelp(const std::vector<std::string>& args) {
std::vector<base::StringPiece> all_help_topics;
+ // Special-case ambiguous topics.
+ if (what == "args") {
+ PrintArgsHelp();
+ return 0;
+ }
+
// Check commands.
const commands::CommandInfoMap& command_map = commands::GetCommands();
auto found_command = command_map.find(what);
@@ -284,16 +330,13 @@ int RunHelp(const std::vector<std::string>& args) {
random_topics["buildargs"] = []() { PrintLongHelp(kBuildArgs_Help); };
random_topics["dotfile"] = []() { PrintLongHelp(kDotfile_Help); };
random_topics["grammar"] = []() { PrintLongHelp(kGrammar_Help); };
- random_topics["input_conversion"] = []() {
+ random_topics["io_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);
diff --git a/gn/tools/gn/command_ls.cc b/gn/tools/gn/command_ls.cc
index 65ec1a99cba..26094997508 100644
--- a/gn/tools/gn/command_ls.cc
+++ b/gn/tools/gn/command_ls.cc
@@ -68,6 +68,7 @@ int RunLs(const std::vector<std::string>& args) {
return 1;
}
+ // Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false) || !setup->Run())
return 1;
diff --git a/gn/tools/gn/command_meta.cc b/gn/tools/gn/command_meta.cc
new file mode 100644
index 00000000000..060373170a1
--- /dev/null
+++ b/gn/tools/gn/command_meta.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 <algorithm>
+#include <set>
+
+#include "base/command_line.h"
+#include "base/strings/string_split.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/metadata_walk.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/switches.h"
+#include "tools/gn/target.h"
+
+namespace commands {
+
+const char kMeta[] = "meta";
+const char kMeta_HelpShort[] = "meta: List target metadata collection results.";
+const char kMeta_Help[] =
+ R"(gn meta
+
+ gn meta <out_dir> <target>* --data=<key>[,<key>*]* [--walk=<key>[,<key>*]*]
+ [--rebase=<dest dir>]
+
+ Lists collected metaresults of all given targets for the given data key(s),
+ collecting metadata dependencies as specified by the given walk key(s).
+
+ See `gn help generated_file` for more information on the walk.
+
+Arguments
+
+ <target(s)>
+ A list of target labels from which to initiate the walk.
+
+ --data
+ A list of keys from which to extract data. In each target walked, its metadata
+ scope is checked for the presence of these keys. If present, the contents of
+ those variable in the scope are appended to the results list.
+
+ --walk (optional)
+ A list of keys from which to control the walk. In each target walked, its
+ metadata scope is checked for the presence of any of these keys. If present,
+ the contents of those variables is checked to ensure that it is a label of
+ a valid dependency of the target and then added to the set of targets to walk.
+ If the empty string ("") is present in any of these keys, all deps and data_deps
+ are added to the walk set.
+
+ --rebase (optional)
+ A destination directory onto which to rebase any paths found. If set, all
+ collected metadata will be rebased onto this path. This option will throw errors
+ if collected metadata is not a list of strings.
+
+Examples
+
+ gn meta out/Debug "//base/foo" --data=files
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of its dependency tree.
+
+ gn meta out/Debug "//base/foo" --data=files --data=other
+ Lists collected metaresults for the `files` and `other` keys in the
+ //base/foo:foo target and all of its dependency tree.
+
+ gn meta out/Debug "//base/foo" --data=files --walk=stop
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of the dependencies listed in the `stop` key (and so on).
+
+ gn meta out/Debug "//base/foo" --data=files --rebase="/"
+ Lists collected metaresults for the `files` key in the //base/foo:foo
+ target and all of its dependency tree, rebasing the strings in the `files`
+ key onto the source directory of the target's declaration relative to "/".
+)";
+
+int RunMeta(const std::vector<std::string>& args) {
+ if (args.size() == 0) {
+ Err(Location(), "You're holding it wrong.",
+ "Usage: \"gn meta <out_dir> <target>* --data=<key>[,<key>*] "
+ "[--walk=<key>[,<key>*]*] [--rebase=<dest dir>]\"")
+ .PrintToStdout();
+ return 1;
+ }
+
+ Setup* setup = new Setup;
+ if (!setup->DoSetup(args[0], false) || !setup->Run())
+ return 1;
+
+ const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ std::string rebase_dir =
+ cmdline->GetSwitchValueASCII(switches::kMetaRebaseFiles);
+ std::string data_keys_str =
+ cmdline->GetSwitchValueASCII(switches::kMetaDataKeys);
+ std::string walk_keys_str =
+ cmdline->GetSwitchValueASCII(switches::kMetaWalkKeys);
+
+ std::vector<std::string> inputs(args.begin() + 1, args.end());
+
+ UniqueVector<const Target*> targets;
+ for (const auto& input : inputs) {
+ const Target* target = ResolveTargetFromCommandLineString(setup, input);
+ if (!target) {
+ Err(Location(), "Unknown target " + input).PrintToStdout();
+ return 1;
+ }
+ targets.push_back(target);
+ }
+
+ std::vector<std::string> data_keys = base::SplitString(
+ data_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (data_keys.empty()) {
+ return 1;
+ }
+ std::vector<std::string> walk_keys = base::SplitString(
+ walk_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result =
+ WalkMetadata(targets, data_keys, walk_keys, SourceDir(rebase_dir),
+ &targets_walked, &err);
+ if (err.has_error()) {
+ err.PrintToStdout();
+ return 1;
+ }
+
+ OutputString("Metadata values\n", DECORATION_DIM);
+ for (const auto& value : result)
+ OutputString("\n" + value.ToString(false) + "\n");
+
+ // TODO(juliehockett): We should have better dep tracing and error support for
+ // this. Also possibly data about where different values came from.
+ OutputString("\nExtracted from:\n", DECORATION_DIM);
+ bool first = true;
+ for (const auto* target : targets_walked) {
+ if (!first) {
+ first = false;
+ OutputString(", ", DECORATION_DIM);
+ }
+ OutputString(target->label().GetUserVisibleName(true) + "\n");
+ }
+ OutputString("\nusing data keys:\n", DECORATION_DIM);
+ first = true;
+ for (const auto& key : data_keys) {
+ if (!first) {
+ first = false;
+ OutputString(", ");
+ }
+ OutputString(key + "\n");
+ }
+ if (!walk_keys.empty()) {
+ OutputString("\nand using walk keys:\n", DECORATION_DIM);
+ first = true;
+ for (const auto& key : walk_keys) {
+ if (!first) {
+ first = false;
+ OutputString(", ");
+ }
+ OutputString(key + "\n");
+ }
+ }
+ return 0;
+}
+
+} // namespace commands
diff --git a/gn/tools/gn/command_path.cc b/gn/tools/gn/command_path.cc
index 333a78ea3f3..503ba6d9050 100644
--- a/gn/tools/gn/command_path.cc
+++ b/gn/tools/gn/command_path.cc
@@ -319,6 +319,7 @@ int RunPath(const std::vector<std::string>& args) {
return 1;
}
+ // Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false))
return 1;
diff --git a/gn/tools/gn/command_refs.cc b/gn/tools/gn/command_refs.cc
index 92ecf10376b..6364d813df4 100644
--- a/gn/tools/gn/command_refs.cc
+++ b/gn/tools/gn/command_refs.cc
@@ -292,8 +292,10 @@ size_t DoDirectListOutput(
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=...]
+ R"(gn refs
+
+ gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)*
+ [--all] [--all-toolchains] [--as=...] [--testonly=...] [--type=...]
Finds reverse dependencies (which targets reference something). The input is
a list containing:
@@ -407,6 +409,7 @@ int RunRefs(const std::vector<std::string>& args) {
bool all = cmdline->HasSwitch("all");
bool all_toolchains = cmdline->HasSwitch(switches::kAllToolchains);
+ // Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false) || !setup->Run())
return 1;
diff --git a/gn/tools/gn/commands.cc b/gn/tools/gn/commands.cc
index 7fb496d0e44..b7cd87131d8 100644
--- a/gn/tools/gn/commands.cc
+++ b/gn/tools/gn/commands.cc
@@ -386,6 +386,7 @@ const CommandInfoMap& GetCommands() {
INSERT_COMMAND(Gen)
INSERT_COMMAND(Format)
INSERT_COMMAND(Help)
+ INSERT_COMMAND(Meta)
INSERT_COMMAND(Ls)
INSERT_COMMAND(Path)
INSERT_COMMAND(Refs)
diff --git a/gn/tools/gn/commands.h b/gn/tools/gn/commands.h
index 7d6017edb35..d6b672812e4 100644
--- a/gn/tools/gn/commands.h
+++ b/gn/tools/gn/commands.h
@@ -69,6 +69,11 @@ extern const char kHelp_HelpShort[];
extern const char kHelp_Help[];
int RunHelp(const std::vector<std::string>& args);
+extern const char kMeta[];
+extern const char kMeta_HelpShort[];
+extern const char kMeta_Help[];
+int RunMeta(const std::vector<std::string>& args);
+
extern const char kLs[];
extern const char kLs_HelpShort[];
extern const char kLs_Help[];
@@ -131,12 +136,16 @@ bool ResolveFromCommandLineInput(
// force_check, if true, will override targets opting out of header checking
// with "check_includes = false" and will check them anyway.
//
+// Generated files are normally not checked since they do not exist
+// unless a build has been run, but passing true for |check_generated|
+// will attempt to check them anyway, assuming they exist.
+//
// On success, returns true. If the check fails, the error(s) will be printed
// to stdout and false will be returned.
bool CheckPublicHeaders(const BuildSettings* build_settings,
const std::vector<const Target*>& all_targets,
const std::vector<const Target*>& to_check,
- bool force_check);
+ bool force_check, bool check_generated);
// Filters the given list of targets by the given pattern list.
void FilterTargetsByPatterns(const std::vector<const Target*>& input,
diff --git a/gn/tools/gn/create_bundle_target_generator.cc b/gn/tools/gn/create_bundle_target_generator.cc
index 79edd78b3a8..3556b5d86c8 100644
--- a/gn/tools/gn/create_bundle_target_generator.cc
+++ b/gn/tools/gn/create_bundle_target_generator.cc
@@ -42,9 +42,6 @@ void CreateBundleTargetGenerator::DoRun() {
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;
@@ -78,6 +75,8 @@ bool CreateBundleTargetGenerator::FillBundleDir(
const SourceDir& bundle_root_dir,
const base::StringPiece& name,
SourceDir* bundle_dir) {
+ // All bundle_foo_dir properties are optional. They are only required if they
+ // are used in an expansion. The check is performed there.
const Value* value = scope_->GetValue(name, true);
if (!value)
return true;
diff --git a/gn/tools/gn/desc_builder.cc b/gn/tools/gn/desc_builder.cc
index b2fd7162bd3..57f27fdbfa4 100644
--- a/gn/tools/gn/desc_builder.cc
+++ b/gn/tools/gn/desc_builder.cc
@@ -5,6 +5,7 @@
#include <memory>
#include <set>
+#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "tools/gn/commands.h"
#include "tools/gn/config.h"
@@ -14,7 +15,9 @@
#include "tools/gn/input_file.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/runtime_deps.h"
+#include "tools/gn/scope.h"
#include "tools/gn/settings.h"
+#include "tools/gn/standard_out.h"
#include "tools/gn/substitution_writer.h"
#include "tools/gn/variables.h"
@@ -47,6 +50,11 @@
// "deps : [ list of target dependencies ],
// "libs" : [ list of libraries ],
// "lib_dirs" : [ list of library directories ]
+// "metadata" : [ dictionary of target metadata values ]
+// "data_keys" : [ list of target data keys ]
+// "walk_keys" : [ list of target walk keys ]
+// "rebase" : true or false
+// "output_conversion" : "string for output conversion"
// }
//
// Optionally, if "what" is specified while generating description, two other
@@ -144,6 +152,42 @@ class BaseDescBuilder {
return RenderValue(lib.value());
}
+ template <typename T>
+ base::Value ToBaseValue(const std::vector<T>& vector) {
+ base::ListValue res;
+ for (const auto& v : vector)
+ res.GetList().emplace_back(ToBaseValue(v));
+ return std::move(res);
+ }
+
+ base::Value ToBaseValue(const Scope* scope) {
+ base::DictionaryValue res;
+ Scope::KeyValueMap map;
+ scope->GetCurrentScopeValues(&map);
+ for (const auto& v : map)
+ res.SetKey(v.first, ToBaseValue(v.second));
+ return std::move(res);
+ }
+
+ base::Value ToBaseValue(const Value& val) {
+ switch (val.type()) {
+ case Value::STRING:
+ return base::Value(val.string_value());
+ case Value::INTEGER:
+ return base::Value(int(val.int_value()));
+ case Value::BOOLEAN:
+ return base::Value(val.boolean_value());
+ case Value::SCOPE:
+ return ToBaseValue(val.scope_value());
+ case Value::LIST:
+ return ToBaseValue(val.list_value());
+ case Value::NONE:
+ return base::Value();
+ }
+ NOTREACHED();
+ return base::Value();
+ }
+
template <class VectorType>
void FillInConfigVector(base::ListValue* out,
const VectorType& configs,
@@ -273,6 +317,14 @@ class TargetDescBuilder : public BaseDescBuilder {
}
// General target meta variables.
+
+ if (what(variables::kMetadata)) {
+ base::DictionaryValue metadata;
+ for (const auto& v : target_->metadata().contents())
+ metadata.SetKey(v.first, ToBaseValue(v.second));
+ res->SetKey(variables::kMetadata, std::move(metadata));
+ }
+
if (what(variables::kVisibility))
res->SetWithoutPathExpansion(variables::kVisibility,
target_->visibility().AsValue());
@@ -411,6 +463,30 @@ class TargetDescBuilder : public BaseDescBuilder {
FillInPrecompiledHeader(res.get(), target_->config_values());
}
+ // GeneratedFile vars.
+ if (target_->output_type() == Target::GENERATED_FILE) {
+ if (what(variables::kWriteOutputConversion)) {
+ res->SetKey(variables::kWriteOutputConversion,
+ std::move(ToBaseValue(target_->output_conversion())));
+ }
+ if (what(variables::kDataKeys)) {
+ base::ListValue keys;
+ for (const auto& k : target_->data_keys())
+ keys.GetList().push_back(base::Value(k));
+ res->SetKey(variables::kDataKeys, std::move(keys));
+ }
+ if (what(variables::kRebase)) {
+ res->SetWithoutPathExpansion(variables::kRebase,
+ RenderValue(target_->rebase()));
+ }
+ if (what(variables::kWalkKeys)) {
+ base::ListValue keys;
+ for (const auto& k : target_->walk_keys())
+ keys.GetList().push_back(base::Value(k));
+ res->SetKey(variables::kWalkKeys, std::move(keys));
+ }
+ }
+
if (what(variables::kDeps))
res->SetWithoutPathExpansion(variables::kDeps, RenderDeps());
@@ -462,8 +538,7 @@ class TargetDescBuilder : public BaseDescBuilder {
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::sort(sorted_deps.begin(), sorted_deps.end());
std::string indent(indent_level * 2, ' ');
@@ -587,8 +662,6 @@ class TargetDescBuilder : public BaseDescBuilder {
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()));
@@ -608,10 +681,14 @@ class TargetDescBuilder : public BaseDescBuilder {
list->AppendString(elem.AsString());
res->SetWithoutPathExpansion(variables::kOutputs, std::move(list));
- } else if (target_->output_type() == Target::CREATE_BUNDLE) {
+ } else if (target_->output_type() == Target::CREATE_BUNDLE ||
+ target_->output_type() == Target::GENERATED_FILE) {
+ Err err;
std::vector<SourceFile> output_files;
- target_->bundle_data().GetOutputsAsSourceFiles(target_->settings(),
- &output_files);
+ if (!target_->bundle_data().GetOutputsAsSourceFiles(
+ target_->settings(), target_, &output_files, &err)) {
+ err.PrintToStdout();
+ }
res->SetWithoutPathExpansion(variables::kOutputs,
RenderValue(output_files));
} else if (target_->output_type() == Target::ACTION_FOREACH ||
diff --git a/gn/tools/gn/exec_process.cc b/gn/tools/gn/exec_process.cc
index 5c0619ccb71..6c13558d686 100644
--- a/gn/tools/gn/exec_process.cc
+++ b/gn/tools/gn/exec_process.cc
@@ -21,6 +21,7 @@
#else
#include <errno.h>
#include <fcntl.h>
+#include <sys/signal.h>
#include <sys/wait.h>
#include <unistd.h>
diff --git a/gn/tools/gn/exec_process_unittest.cc b/gn/tools/gn/exec_process_unittest.cc
index 323a07048ab..647bf42029e 100644
--- a/gn/tools/gn/exec_process_unittest.cc
+++ b/gn/tools/gn/exec_process_unittest.cc
@@ -67,7 +67,6 @@ TEST(ExecProcessTest, TestExitCode) {
// 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;
@@ -78,7 +77,6 @@ TEST(ExecProcessTest, TestLargeOutput) {
}
TEST(ExecProcessTest, TestStdoutAndStderrOutput) {
- base::ScopedTempDir temp_dir;
std::string std_out, std_err;
int exit_code;
diff --git a/gn/tools/gn/filesystem_utils.cc b/gn/tools/gn/filesystem_utils.cc
index e8f06965f95..2fc4b24af7d 100644
--- a/gn/tools/gn/filesystem_utils.cc
+++ b/gn/tools/gn/filesystem_utils.cc
@@ -428,9 +428,14 @@ base::FilePath MakeAbsoluteFilePathRelativeIfPossible(
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.
+ // so return the target as an absolute path instead.
if (base_components[0] != target_components[0])
return target;
+
+ // GetComponents() returns the first slash after the root. Set it to the
+ // same value in both component lists so that relative paths between
+ // "C:/foo/..." and "C:\foo\..." are computed correctly.
+ target_components[1] = base_components[1];
#endif
size_t i;
for (i = 0; i < base_components.size() && i < target_components.size(); i++) {
diff --git a/gn/tools/gn/filesystem_utils_unittest.cc b/gn/tools/gn/filesystem_utils_unittest.cc
index 87e37f80d5e..dcaa09914fa 100644
--- a/gn/tools/gn/filesystem_utils_unittest.cc
+++ b/gn/tools/gn/filesystem_utils_unittest.cc
@@ -188,6 +188,10 @@ TEST(FilesystemUtils, MakeAbsoluteFilePathRelativeIfPossible) {
base::FilePath(L"..\\.."),
MakeAbsoluteFilePathRelativeIfPossible(
base::FilePath(L"C:\\src\\out\\Debug"), base::FilePath(L"C:\\src")));
+ EXPECT_EQ(
+ base::FilePath(L"..\\.."),
+ MakeAbsoluteFilePathRelativeIfPossible(
+ base::FilePath(L"C:\\src\\out\\Debug"), base::FilePath(L"C:/src")));
EXPECT_EQ(base::FilePath(L"."),
MakeAbsoluteFilePathRelativeIfPossible(base::FilePath(L"C:\\src"),
base::FilePath(L"C:\\src")));
diff --git a/gn/tools/gn/format_test_data/028.golden b/gn/tools/gn/format_test_data/028.golden
index a1d54c550a2..95068963296 100644
--- a/gn/tools/gn/format_test_data/028.golden
+++ b/gn/tools/gn/format_test_data/028.golden
@@ -1,6 +1,6 @@
# Don't separate these.
-import("wee.gni")
import("waa.gni")
+import("wee.gni")
import("woo.gni")
diff --git a/gn/tools/gn/format_test_data/066.gn b/gn/tools/gn/format_test_data/066.gn
index c62eb2ae6ec..0a79d0cd738 100644
--- a/gn/tools/gn/format_test_data/066.gn
+++ b/gn/tools/gn/format_test_data/066.gn
@@ -14,6 +14,7 @@ sources += [
]
# NOSORT
+# This is NOSORT because reason.
sources = [
"z",
"z2",
@@ -21,6 +22,7 @@ sources = [
"y.cc",
]
+# This is NOSORT because reason:
# NOSORT
sources += [
"z",
diff --git a/gn/tools/gn/format_test_data/066.golden b/gn/tools/gn/format_test_data/066.golden
index 45467b880f5..9b7bb5cd09b 100644
--- a/gn/tools/gn/format_test_data/066.golden
+++ b/gn/tools/gn/format_test_data/066.golden
@@ -12,6 +12,7 @@ sources = [
sources += [ "a" ]
# NOSORT
+# This is NOSORT because reason.
sources = [
"z",
"z2",
@@ -19,6 +20,7 @@ sources = [
"y.cc",
]
+# This is NOSORT because reason:
# NOSORT
sources += [
"z",
diff --git a/gn/tools/gn/format_test_data/071.gn b/gn/tools/gn/format_test_data/071.gn
new file mode 100644
index 00000000000..665d099558d
--- /dev/null
+++ b/gn/tools/gn/format_test_data/071.gn
@@ -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.
+
+import("z.gni")
+import("x.gni")
+import("y.gni")
+
+import("b.gni")
+import("a.gni")
+
+
+
+import("m.gni")
+import("d1.gni")
+# Comment here
+import("c1.gni")
+
+import("../something/relative.gni")
+import("//build/stuff.gni")
+import("nopath.gni")
+import("//abc/things.gni")
+
+import("")
+import()
+import("a")
diff --git a/gn/tools/gn/format_test_data/071.golden b/gn/tools/gn/format_test_data/071.golden
new file mode 100644
index 00000000000..6d8f98f296b
--- /dev/null
+++ b/gn/tools/gn/format_test_data/071.golden
@@ -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.
+
+import("x.gni")
+import("y.gni")
+import("z.gni")
+
+import("a.gni")
+import("b.gni")
+
+import("d1.gni")
+import("m.gni")
+
+# Comment here
+import("c1.gni")
+
+import("//abc/things.gni")
+import("//build/stuff.gni")
+import("../something/relative.gni")
+import("nopath.gni")
+
+import()
+import("")
+import("a")
diff --git a/gn/tools/gn/format_test_data/072.gn b/gn/tools/gn/format_test_data/072.gn
new file mode 100644
index 00000000000..10b5a815140
--- /dev/null
+++ b/gn/tools/gn/format_test_data/072.gn
@@ -0,0 +1,8 @@
+import("b") import("c") import("a") import("d")
+
+import("z") declare_args() {} import("y") import("x") import("w")
+
+import("3") import("2") import("1")
+
+import("x") import("y")
+import("z") import("w")
diff --git a/gn/tools/gn/format_test_data/072.golden b/gn/tools/gn/format_test_data/072.golden
new file mode 100644
index 00000000000..b4e5a4b4151
--- /dev/null
+++ b/gn/tools/gn/format_test_data/072.golden
@@ -0,0 +1,21 @@
+import("a")
+import("b")
+import("c")
+import("d")
+
+import("z")
+declare_args() {
+}
+
+import("w")
+import("x")
+import("y")
+
+import("1")
+import("2")
+import("3")
+
+import("w")
+import("x")
+import("y")
+import("z")
diff --git a/gn/tools/gn/format_test_data/073.gn b/gn/tools/gn/format_test_data/073.gn
new file mode 100644
index 00000000000..e3a65614430
--- /dev/null
+++ b/gn/tools/gn/format_test_data/073.gn
@@ -0,0 +1,23 @@
+import("//root/a")
+import("//root/c")
+import("//root/b")
+
+if (stuff) {
+import("3") import("2") import("1")
+ if (things) {
+ import("x")
+ import("z") import("y") import("w") import("q")
+
+ import("f") import("e") if (other) {import("z") import("a")} import("d")
+
+ template("wee") {
+ import("6")
+ import("7")
+ import("5")
+ }
+ }
+} else {
+ import("i")
+ import("h")
+ import("g")
+}
diff --git a/gn/tools/gn/format_test_data/073.golden b/gn/tools/gn/format_test_data/073.golden
new file mode 100644
index 00000000000..e32977b2d30
--- /dev/null
+++ b/gn/tools/gn/format_test_data/073.golden
@@ -0,0 +1,34 @@
+import("//root/a")
+import("//root/b")
+import("//root/c")
+
+if (stuff) {
+ import("1")
+ import("2")
+ import("3")
+ if (things) {
+ import("q")
+ import("w")
+ import("x")
+ import("y")
+ import("z")
+
+ import("e")
+ import("f")
+ if (other) {
+ import("a")
+ import("z")
+ }
+
+ import("d")
+ template("wee") {
+ import("5")
+ import("6")
+ import("7")
+ }
+ }
+} else {
+ import("g")
+ import("h")
+ import("i")
+}
diff --git a/gn/tools/gn/format_test_data/074.gn b/gn/tools/gn/format_test_data/074.gn
new file mode 100644
index 00000000000..d5e5101ff59
--- /dev/null
+++ b/gn/tools/gn/format_test_data/074.gn
@@ -0,0 +1,7 @@
+config("something") {
+ # Makes builds independent of absolute file path.
+ if (symbol_level != 0 && is_clang &&
+ strip_absolute_paths_from_debug_symbols) {
+ print("hi")
+ }
+}
diff --git a/gn/tools/gn/format_test_data/074.golden b/gn/tools/gn/format_test_data/074.golden
new file mode 100644
index 00000000000..d5e5101ff59
--- /dev/null
+++ b/gn/tools/gn/format_test_data/074.golden
@@ -0,0 +1,7 @@
+config("something") {
+ # Makes builds independent of absolute file path.
+ if (symbol_level != 0 && is_clang &&
+ strip_absolute_paths_from_debug_symbols) {
+ print("hi")
+ }
+}
diff --git a/gn/tools/gn/format_test_data/075.gn b/gn/tools/gn/format_test_data/075.gn
new file mode 100644
index 00000000000..8a3c3275298
--- /dev/null
+++ b/gn/tools/gn/format_test_data/075.gn
@@ -0,0 +1,11 @@
+# This tests for a case where formatting did not reach a fixed point after a
+# single run of formatting.
+
+import("stuff.gni")
+
+# Subprojects need to override arguments in {mac,ios}_sdk_overrides.gni in their
+# .gn config, but those arguments are only used on macOS. Including
+# mac_sdk_overrides.gni insures that this doesn't trigger an unused argument
+# warning.
+import("//build/config/mac/mac_sdk_overrides.gni")
+import("//build/config/ios/ios_sdk_overrides.gni")
diff --git a/gn/tools/gn/format_test_data/075.golden b/gn/tools/gn/format_test_data/075.golden
new file mode 100644
index 00000000000..d181bd689cc
--- /dev/null
+++ b/gn/tools/gn/format_test_data/075.golden
@@ -0,0 +1,12 @@
+# This tests for a case where formatting did not reach a fixed point after a
+# single run of formatting.
+
+import("stuff.gni")
+
+import("//build/config/ios/ios_sdk_overrides.gni")
+
+# Subprojects need to override arguments in {mac,ios}_sdk_overrides.gni in their
+# .gn config, but those arguments are only used on macOS. Including
+# mac_sdk_overrides.gni insures that this doesn't trigger an unused argument
+# warning.
+import("//build/config/mac/mac_sdk_overrides.gni")
diff --git a/gn/tools/gn/function_exec_script.cc b/gn/tools/gn/function_exec_script.cc
index 15621b7790c..89570db9c2b 100644
--- a/gn/tools/gn/function_exec_script.cc
+++ b/gn/tools/gn/function_exec_script.cc
@@ -80,11 +80,15 @@ const char kExecScript_Help[] =
rebase_path() function to make file names relative to this path (see "gn help
rebase_path").
+ The default script interpreter is Python ("python" on POSIX, "python.exe" or
+ "python.bat" on Windows). This can be configured by the script_executable
+ variable, see "gn help dotfile".
+
Arguments:
filename:
- File name of python script to execute. Non-absolute names will be treated
- as relative to the current build file.
+ File name of script to execute. Non-absolute names will be treated as
+ relative to the current build file.
arguments:
A list of strings to be passed to the script as arguments. May be
@@ -133,7 +137,7 @@ Value RunExecScript(Scope* scope,
if (!CheckExecScriptPermissions(build_settings, function, err))
return Value();
- // Find the python script to run.
+ // Find the script to run.
std::string script_source_path = cur_dir.ResolveRelativeAs(
true, args[0], err,
scope->settings()->build_settings()->root_path_utf8());
@@ -173,8 +177,8 @@ Value RunExecScript(Scope* scope,
}
// Make the command line.
- const base::FilePath& python_path = build_settings->python_path();
- base::CommandLine cmdline(python_path);
+ const base::FilePath& interpreter_path = build_settings->python_path();
+ base::CommandLine cmdline(interpreter_path);
// CommandLine tries to interpret arguments by default. Disable that so
// that the arguments will be passed through exactly as specified.
@@ -199,10 +203,10 @@ Value RunExecScript(Scope* scope,
Ticks begin_exec = 0;
if (g_scheduler->verbose_logging()) {
#if defined(OS_WIN)
- g_scheduler->Log("Pythoning",
+ g_scheduler->Log("Executing",
base::UTF16ToUTF8(cmdline.GetCommandLineString()));
#else
- g_scheduler->Log("Pythoning", cmdline.GetCommandLineString());
+ g_scheduler->Log("Executing", cmdline.GetCommandLineString());
#endif
begin_exec = TicksNow();
}
@@ -227,14 +231,15 @@ Value RunExecScript(Scope* scope,
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) + "\".");
+ function->function(), "Could not execute interpreter.",
+ "I was trying to execute \"" + FilePathToUTF8(interpreter_path) +
+ "\".");
return Value();
}
}
if (g_scheduler->verbose_logging()) {
g_scheduler->Log(
- "Pythoning",
+ "Executing",
script_source_path + " took " +
base::Int64ToString(
TicksDelta(TicksNow(), begin_exec).InMilliseconds()) +
diff --git a/gn/tools/gn/function_forward_variables_from.cc b/gn/tools/gn/function_forward_variables_from.cc
index de08072c939..875af91b0bd 100644
--- a/gn/tools/gn/function_forward_variables_from.cc
+++ b/gn/tools/gn/function_forward_variables_from.cc
@@ -117,6 +117,13 @@ const char kForwardVariablesFrom_Help[] =
Examples
+ # forward_variables_from(invoker, ["foo"])
+ # is equivalent to:
+ assert(!defined(foo))
+ if (defined(invoker.foo)) {
+ foo = invoker.foo
+ }
+
# This is a common action template. It would invoke a script with some given
# parameters, and wants to use the various types of deps and the visibility
# from the invoker if it's defined. It also injects an additional dependency
diff --git a/gn/tools/gn/function_get_path_info.cc b/gn/tools/gn/function_get_path_info.cc
index 905092cd50a..08f7e8c850d 100644
--- a/gn/tools/gn/function_get_path_info.cc
+++ b/gn/tools/gn/function_get_path_info.cc
@@ -192,7 +192,7 @@ Examples
# result will be "//foo/bar"
# Extract the source-absolute directory name,
- result = get_path_info(get_path_info(path, "dir"), "abspath"
+ result = get_path_info(get_path_info(path, "dir"), "abspath")
)";
Value RunGetPathInfo(Scope* scope,
diff --git a/gn/tools/gn/function_set_defaults.cc b/gn/tools/gn/function_set_defaults.cc
index a2a14afcc7d..8fbc49040a3 100644
--- a/gn/tools/gn/function_set_defaults.cc
+++ b/gn/tools/gn/function_set_defaults.cc
@@ -38,7 +38,7 @@ Example
configs = [ "//tools/mything:settings" ]
}
- static_library("mylib")
+ 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" ]
diff --git a/gn/tools/gn/function_write_file_unittest.cc b/gn/tools/gn/function_write_file_unittest.cc
index 035fabae962..4e317e167e0 100644
--- a/gn/tools/gn/function_write_file_unittest.cc
+++ b/gn/tools/gn/function_write_file_unittest.cc
@@ -87,7 +87,7 @@ TEST_F(WriteFileTest, WithData) {
&last_access_filetime, &last_modified_filetime));
#elif defined(OS_AIX)
struct timeval times[2] = {};
- ASSERT_EQ(utimes(foo_name.AsUTF8Unsafe().c_str(), times), 0);
+ ASSERT_EQ(utimes(foo_name.value().c_str(), times), 0);
#else
struct timeval times[2] = {};
ASSERT_EQ(futimes(foo_file.GetPlatformFile(), times), 0);
diff --git a/gn/tools/gn/functions.cc b/gn/tools/gn/functions.cc
index 7fc8b62b3db..7c54d17539b 100644
--- a/gn/tools/gn/functions.cc
+++ b/gn/tools/gn/functions.cc
@@ -297,7 +297,24 @@ const char kConfig_Help[] =
4. All dependent configs from a breadth-first traversal of the dependency
tree in the order that the targets appear in "deps".
+More background
+
+ Configs solve a problem where the build system needs to have a higher-level
+ understanding of various compiler settings. For example, some compiler flags
+ have to appear in a certain order relative to each other, some settings like
+ defines and flags logically go together, and the build system needs to
+ de-duplicate flags even though raw command-line parameters can't always be
+ operated on in that way.
+
+ The config gives a name to a group of settings that can then be reasoned
+ about by GN. GN can know that configs with the same label are the same thing
+ so can be de-duplicated. It allows related settings to be grouped so they
+ are added or removed as a unit. And it allows targets to refer to settings
+ with conceptual names ("no_rtti", "enable_exceptions", etc.) rather than
+ having to hard-coding every compiler's flags each time they are referred to.
+
Variables valid in a config definition
+
)"
CONFIG_VALUES_VARS_HELP
@@ -677,6 +694,7 @@ Value RunNotNeeded(Scope* scope,
Value* value = nullptr; // Value to use, may point to result_value.
Value result_value; // Storage for the "evaluate" case.
+ Value scope_value; // Storage for an evaluated scope.
const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
if (identifier) {
// Optimize the common case where the input scope is an identifier. This
@@ -699,12 +717,29 @@ Value RunNotNeeded(Scope* scope,
// Extract the source scope if different from current one.
Scope* source = scope;
if (value->type() == Value::SCOPE) {
- source = value->scope_value();
+ if (args_cur == args_vector.end()) {
+ *err = Err(
+ function, "Wrong number of arguments.",
+ "The first argument is a scope, expecting two or three arguments.");
+ return Value();
+ }
+ // Copy the scope value if it will be overridden.
+ if (value == &result_value) {
+ scope_value = Value(nullptr, value->scope_value()->MakeClosure());
+ source = scope_value.scope_value();
+ } else {
+ source = value->scope_value();
+ }
result_value = (*args_cur)->Execute(scope, err);
if (err->has_error())
return Value();
value = &result_value;
args_cur++;
+ } else if (args_vector.size() > 2) {
+ *err = Err(
+ function, "Wrong number of arguments.",
+ "The first argument is not a scope, expecting one or two arguments.");
+ return Value();
}
// Extract the exclusion list if defined.
@@ -1233,6 +1268,7 @@ struct FunctionInfoInitializer {
INSERT_FUNCTION(SourceSet, true)
INSERT_FUNCTION(StaticLibrary, true)
INSERT_FUNCTION(Target, true)
+ INSERT_FUNCTION(GeneratedFile, true)
INSERT_FUNCTION(Assert, false)
INSERT_FUNCTION(Config, false)
diff --git a/gn/tools/gn/functions.h b/gn/tools/gn/functions.h
index 5c707c425ad..97f5b0d825a 100644
--- a/gn/tools/gn/functions.h
+++ b/gn/tools/gn/functions.h
@@ -195,6 +195,15 @@ Value RunGetTargetOutputs(Scope* scope,
const std::vector<Value>& args,
Err* err);
+extern const char kGeneratedFile[];
+extern const char kGeneratedFile_HelpShort[];
+extern const char kGeneratedFile_Help[];
+Value RunGeneratedFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
extern const char kGroup[];
extern const char kGroup_HelpShort[];
extern const char kGroup_Help[];
diff --git a/gn/tools/gn/functions_target.cc b/gn/tools/gn/functions_target.cc
index 2339e174131..be02fc420cb 100644
--- a/gn/tools/gn/functions_target.cc
+++ b/gn/tools/gn/functions_target.cc
@@ -16,9 +16,10 @@
#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"
+#define GENERAL_TARGET_VARS \
+ " General: check_includes, configs, data, friend, inputs, metadata,\n" \
+ " output_name, output_extension, public, sources, testonly,\n" \
+ " visibility\n"
namespace functions {
@@ -138,7 +139,7 @@ File name handling
R"(
Variables
- args, data, data_deps, depfile, deps, inputs, outputs*, pool,
+ args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
response_file_contents, script*, sources
* = required
@@ -208,7 +209,7 @@ File name handling
R"(
Variables
- args, data, data_deps, depfile, deps, inputs, outputs*, pool,
+ args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
response_file_contents, script*, sources*
* = required
@@ -272,7 +273,7 @@ const char kBundleData_Help[] =
Variables
- sources*, outputs*, deps, data_deps, public_deps, visibility
+ sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
* = required
Examples
@@ -326,8 +327,9 @@ const char kCreateBundle_Help[] =
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.
+ The "bundle_*_dir" are be used for the expansion of {{bundle_*_dir}} rules in
+ "bundle_data" outputs. The properties are optional but must be defined if any
+ of the "bundle_data" target use them.
This target can be used on all platforms though it is designed only to
generate iOS or macOS bundle. In cross-platform projects, it is advised to put
@@ -357,12 +359,11 @@ Code signing
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
+ bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
+ bundle_executable_dir, bundle_deps_filter, deps, data_deps, public_deps,
+ visibility, product_type, code_signing_args, code_signing_script,
+ code_signing_sources, code_signing_outputs, xcode_extra_attributes,
+ xcode_test_application_name, partial_info_plist, metadata
Example
@@ -387,7 +388,7 @@ Example
}
bundle_data("${app_name}_bundle_info_plist") {
- deps = [ ":${app_name}_generate_info_plist" ]
+ public_deps = [ ":${app_name}_generate_info_plist" ]
sources = [ "$gen_path/Info.plist" ]
outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
}
@@ -404,34 +405,32 @@ Example
code_signing =
defined(invoker.code_signing) && invoker.code_signing
- if (is_ios && !code_signing) {
+ if (!is_ios || !code_signing) {
bundle_data("${app_name}_bundle_executable") {
- deps = [ ":${app_name}_generate_executable" ]
+ public_deps = [ ":${app_name}_generate_executable" ]
sources = [ "$gen_path/$app_name" ]
outputs = [ "{{bundle_executable_dir}}/$app_name" ]
}
}
- create_bundle("${app_name}.app") {
+ create_bundle("$app_name.app") {
product_type = "com.apple.product-type.application"
if (is_ios) {
- bundle_root_dir = "${root_build_dir}/$target_name"
+ 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"
+ bundle_root_dir = "$root_build_dir/$target_name"
+ bundle_contents_dir = "$bundle_root_dir/Contents"
+ bundle_resources_dir = "$bundle_contents_dir/Resources"
+ bundle_executable_dir = "$bundle_contents_dir/MacOS"
}
deps = [ ":${app_name}_bundle_info_plist" ]
if (is_ios && code_signing) {
@@ -770,4 +769,145 @@ Value RunTarget(Scope* scope,
block, err);
}
+const char kGeneratedFile[] = "generated_file";
+const char kGeneratedFile_HelpShort[] =
+ "generated_file: Declare a generated_file target.";
+const char kGeneratedFile_Help[] =
+ R"(generated_file: Declare a generated_file target.
+
+ Writes data value(s) to disk on resolution. This target type mirrors some
+ functionality of the write_file() function, but also provides the ability to
+ collect metadata from its dependencies on resolution rather than writing out
+ parse time.
+
+ The `outputs` variable is required to be a list with a single element,
+ specifying the intended location of the output file.
+
+ The `output_conversion` variable specified the format to write the
+ value. See `gn help output_conversion`.
+
+ One of `contents` or `data_keys` must be specified; use of `data` will write
+ the contents of that value to file, while use of `data_keys` will trigger a
+ metadata collection walk based on the dependencies of the target and the
+ optional values of the `rebase` and `walk_keys` variables. See
+ `gn help metadata`.
+
+ Collected metadata, if specified, will be returned in postorder of
+ dependencies. See the example for details.
+
+Example (metadata collection)
+
+ Given the following targets defined in //base/BUILD.gn, where A depends on B
+ and B depends on C and D:
+
+ group("a") {
+ metadata = {
+ doom_melon = [ "enable" ]
+ my_files = [ "foo.cpp" ]
+
+ // Note: this is functionally equivalent to not defining `my_barrier`
+ // at all in this target's metadata.
+ my_barrier = [ "" ]
+ }
+
+ deps = [ ":b" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "bar.cpp" ]
+ my_barrier = [ ":c" ]
+ }
+
+ deps = [ ":c", ":d" ]
+ }
+
+ group("c") {
+ metadata = {
+ doom_melon = [ "disable" ]
+ my_files = [ "baz.cpp" ]
+ }
+ }
+
+ group("d") {
+ metadata = {
+ my_files = [ "missing.cpp" ]
+ }
+ }
+
+ If the following generated_file target is defined:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (less the
+ comments):
+ [
+ "baz.cpp", // from //base:c via //base:b
+ "missing.cpp" // from //base:d via //base:b
+ "bar.cpp", // from //base:b via //base:a
+ "foo.cpp", // from //base:a
+ ]
+
+ Alternatively, as an example of using walk_keys, if the following
+ generated_file target is defined:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_barrier" ]
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (again less
+ the comments):
+ [
+ "baz.cpp", // from //base:c via //base:b
+ "bar.cpp", // from //base:b via //base:a
+ "foo.cpp", // from //base:a
+ ]
+
+ If `rebase` is used in the following generated_file target:
+
+ generated_file("my_files_metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files" ]
+ walk_keys = [ "my_barrier" ]
+ rebase = root_build_dir
+
+ deps = [ "//base:a" ]
+ }
+
+ The following will be written to "$root_build_dir/my_files.json" (again less
+ the comments) (assuming root_build_dir = "//out"):
+ [
+ "../base/baz.cpp", // from //base:c via //base:b
+ "../base/bar.cpp", // from //base:b via //base:a
+ "../base/foo.cpp", // from //base:a
+ ]
+
+
+Variables
+
+ contents
+ data_keys
+ rebase
+ walk_keys
+ output_conversion
+)" DEPS_VARS DEPENDENT_CONFIG_VARS;
+
+Value RunGeneratedFile(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kGeneratedFile, scope, function, args,
+ block, err);
+}
+
} // namespace functions
diff --git a/gn/tools/gn/functions_target_unittest.cc b/gn/tools/gn/functions_target_unittest.cc
index 708c06076d4..642b8ce456f 100644
--- a/gn/tools/gn/functions_target_unittest.cc
+++ b/gn/tools/gn/functions_target_unittest.cc
@@ -88,6 +88,58 @@ TEST_F(FunctionsTarget, CheckNotNeeded) {
error_input.parsed()->Execute(setup.scope(), &err);
ASSERT_TRUE(err.has_error());
EXPECT_EQ("Not supported with a variable list.", err.message());
+
+ TestParseInput argcount_error_input(
+ "source_set(\"foo\") {\n"
+ " not_needed()\n"
+ "}\n");
+ ASSERT_FALSE(argcount_error_input.has_error());
+ err = Err();
+ argcount_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ TestParseInput scope_error_input(
+ "source_set(\"foo\") {\n"
+ " a = {x = 1 y = 2}\n"
+ " not_needed(a)\n"
+ "}\n");
+ ASSERT_FALSE(scope_error_input.has_error());
+ err = Err();
+ scope_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ TestParseInput string_error_input(
+ "source_set(\"foo\") {\n"
+ " not_needed(\"*\", {}, \"*\")\n"
+ "}\n");
+ ASSERT_FALSE(string_error_input.has_error());
+ err = Err();
+ string_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Wrong number of arguments.", err.message());
+
+ TestParseInput template_input(
+ R"(# Test that not_needed() propagates through templates correctly;
+ # no error should arise from not using "a".
+ template("inner_templ") {
+ source_set(target_name) {
+ not_needed(invoker, [ "a" ])
+ }
+ }
+ template("outer_templ") {
+ inner_templ(target_name) {
+ forward_variables_from(invoker, "*")
+ }
+ }
+ outer_templ("foo") {
+ a = 1
+ })");
+ ASSERT_FALSE(template_input.has_error());
+ err = Err();
+ template_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
}
// Checks that the defaults applied to a template invoked by target() use
diff --git a/gn/tools/gn/functions_unittest.cc b/gn/tools/gn/functions_unittest.cc
index 589986cc20f..8725c2ecd9d 100644
--- a/gn/tools/gn/functions_unittest.cc
+++ b/gn/tools/gn/functions_unittest.cc
@@ -202,3 +202,15 @@ TEST(Functions, DeclareArgs) {
reading_from_different_call.parsed()->Execute(setup2.scope(), &err);
ASSERT_FALSE(err.has_error());
}
+
+TEST(Functions, NotNeeded) {
+ TestWithScope setup;
+
+ TestParseInput input("not_needed({ a = 1 }, \"*\")");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error())
+ << err.message() << err.location().Describe(true);
+}
diff --git a/gn/tools/gn/generated_file_target_generator.cc b/gn/tools/gn/generated_file_target_generator.cc
new file mode 100644
index 00000000000..1854d5bfb3b
--- /dev/null
+++ b/gn/tools/gn/generated_file_target_generator.cc
@@ -0,0 +1,168 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/generated_file_target_generator.h"
+
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/target.h"
+#include "tools/gn/variables.h"
+
+GeneratedFileTargetGenerator::GeneratedFileTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err),
+ output_type_(type),
+ contents_defined_(false),
+ data_keys_defined_(false) {}
+
+GeneratedFileTargetGenerator::~GeneratedFileTargetGenerator() = default;
+
+void GeneratedFileTargetGenerator::DoRun() {
+ target_->set_output_type(output_type_);
+
+ if (!FillOutputs(false))
+ return;
+ if (target_->action_values().outputs().list().size() != 1) {
+ *err_ = Err(
+ function_call_, "generated_file target must have exactly one output.",
+ "You must specify exactly one value in the \"outputs\" array for the "
+ "destination of the write\n(see \"gn help generated_file\").");
+ return;
+ }
+
+ if (!FillContents())
+ return;
+ if (!FillDataKeys())
+ return;
+
+ // One of data and data_keys should be defined.
+ if (!contents_defined_ && !data_keys_defined_) {
+ *err_ = Err(
+ function_call_, "Either contents or data_keys should be set.",
+ "The generated_file target requires either the \"contents\" variable "
+ "or the \"data_keys\" variable be set. See \"gn help "
+ "generated_file\".");
+ return;
+ }
+
+ if (!FillRebase())
+ return;
+ if (!FillWalkKeys())
+ return;
+
+ if (!FillOutputConversion())
+ return;
+}
+
+bool GeneratedFileTargetGenerator::FillContents() {
+ const Value* value = scope_->GetValue(variables::kWriteValueContents, true);
+ if (!value)
+ return true;
+ target_->set_contents(*value);
+ contents_defined_ = true;
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::IsMetadataCollectionTarget(
+ const base::StringPiece& variable,
+ const ParseNode* origin) {
+ if (contents_defined_) {
+ *err_ =
+ Err(origin, variable.as_string() + " won't be used.",
+ "\"contents\" is defined on this target, and so setting " +
+ variable.as_string() +
+ " will have no effect as no metdata collection will occur.");
+ return false;
+ }
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillOutputConversion() {
+ const Value* value =
+ scope_->GetValue(variables::kWriteOutputConversion, true);
+ if (!value) {
+ target_->set_output_conversion(Value(function_call_, ""));
+ return true;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ // Otherwise, the value itself will be checked when the conversion is done.
+ target_->set_output_conversion(*value);
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillRebase() {
+ const Value* value = scope_->GetValue(variables::kRebase, true);
+ if (!value)
+ return true;
+ if (!IsMetadataCollectionTarget(variables::kRebase, value->origin()))
+ return false;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Treat empty string as the default and do nothing.
+
+ const BuildSettings* build_settings = scope_->settings()->build_settings();
+ SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
+ *value, err_, build_settings->root_path_utf8());
+ if (err_->has_error())
+ return false;
+
+ target_->set_rebase(dir);
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillDataKeys() {
+ const Value* value = scope_->GetValue(variables::kDataKeys, true);
+ if (!value)
+ return true;
+ if (!IsMetadataCollectionTarget(variables::kDataKeys, value->origin()))
+ return false;
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+
+ for (const Value& v : value->list_value()) {
+ // Keys must be strings.
+ if (!v.VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->data_keys().push_back(v.string_value());
+ }
+
+ data_keys_defined_ = true;
+ return true;
+}
+
+bool GeneratedFileTargetGenerator::FillWalkKeys() {
+ const Value* value = scope_->GetValue(variables::kWalkKeys, true);
+ // If we define this and contents, that's an error.
+ if (value &&
+ !IsMetadataCollectionTarget(variables::kWalkKeys, value->origin()))
+ return false;
+
+ // If we don't define it, we want the default value which is a list
+ // containing the empty string.
+ if (!value) {
+ target_->walk_keys().push_back("");
+ return true;
+ }
+
+ // Otherwise, pull and validate the specified value.
+ if (!value->VerifyTypeIs(Value::LIST, err_))
+ return false;
+ for (const Value& v : value->list_value()) {
+ // Keys must be strings.
+ if (!v.VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->walk_keys().push_back(v.string_value());
+ }
+ return true;
+}
diff --git a/gn/tools/gn/generated_file_target_generator.h b/gn/tools/gn/generated_file_target_generator.h
new file mode 100644
index 00000000000..fb2523d4b07
--- /dev/null
+++ b/gn/tools/gn/generated_file_target_generator.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
+#define TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
+
+#include "base/macros.h"
+#include "tools/gn/target.h"
+#include "tools/gn/target_generator.h"
+
+// Collects and writes specified data.
+class GeneratedFileTargetGenerator : public TargetGenerator {
+ public:
+ GeneratedFileTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err);
+ ~GeneratedFileTargetGenerator() override;
+
+ protected:
+ void DoRun() override;
+
+ private:
+ bool FillGeneratedFileOutput();
+ bool FillOutputConversion();
+ bool FillContents();
+ bool FillDataKeys();
+ bool FillWalkKeys();
+ bool FillRebase();
+
+ // Returns false if `contents` is defined (i.e. if this target was provided
+ // with explicit contents to write). Returns false otherwise, indicating that
+ // it is okay to set metadata collection variables on this target.
+ //
+ // Should be called before FillContents().
+ bool IsMetadataCollectionTarget(const base::StringPiece& variable,
+ const ParseNode* origin);
+
+ bool contents_defined_;
+ bool data_keys_defined_;
+
+ Target::OutputType output_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(GeneratedFileTargetGenerator);
+};
+
+#endif // TOOLS_GN_GENERATED_FILE_TARGET_GENERATOR_H_
diff --git a/gn/tools/gn/header_checker.cc b/gn/tools/gn/header_checker.cc
index 03afc463e01..8407bd0f043 100644
--- a/gn/tools/gn/header_checker.cc
+++ b/gn/tools/gn/header_checker.cc
@@ -119,8 +119,10 @@ bool FriendMatches(const Target* annotation_on,
} // namespace
HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
- const std::vector<const Target*>& targets)
- : build_settings_(build_settings), lock_(), task_count_cv_() {
+ const std::vector<const Target*>& targets,
+ bool check_generated)
+ : build_settings_(build_settings), check_generated_(check_generated),
+ lock_(), task_count_cv_() {
for (auto* target : targets)
AddTargetToFileMap(target, &file_map_);
}
@@ -155,14 +157,16 @@ void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
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;
+ if (!check_generated_) {
+ // If any target marks it as generated, don't check it. We have to check
+ // file_map_, which includes all known files; files only includes those
+ // being checked.
+ bool is_generated = false;
+ for (const auto& vect_i : file_map_[file.first])
+ is_generated |= vect_i.is_generated;
+ if (is_generated)
+ continue;
+ }
for (const auto& vect_i : file.second) {
if (vect_i.target->check_includes()) {
@@ -269,12 +273,17 @@ bool HeaderChecker::CheckFile(const Target* from_target,
// 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))
+ if (!check_generated_ && IsFileInOuputDir(file))
return true;
base::FilePath path = build_settings_->GetFullPath(file);
std::string contents;
if (!base::ReadFileToString(path, &contents)) {
+ // A missing (not yet) generated file is an acceptable problem
+ // considering this code does not understand conditional includes.
+ if (IsFileInOuputDir(file))
+ return true;
+
errors->emplace_back(from_target->defined_from(), "Source file not found.",
"The target:\n " +
from_target->label().GetUserVisibleName(false) +
@@ -295,23 +304,22 @@ bool HeaderChecker::CheckFile(const Target* from_target,
target_include_dirs.end());
}
- bool has_errors = false;
+ size_t error_count_before = errors->size();
CIncludeIterator iter(&input_file);
base::StringPiece current_include;
LocationRange range;
+
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
while (iter.GetNextIncludeString(&current_include, &range)) {
Err err;
SourceFile include = SourceFileForInclude(current_include, include_dirs,
input_file, range, &err);
- if (!include.is_null()) {
- if (!CheckInclude(from_target, input_file, include, range, &err)) {
- errors->emplace_back(std::move(err));
- has_errors = true;
- }
- }
+ if (!include.is_null())
+ CheckInclude(from_target, input_file, include, range,
+ &no_dependency_cache, errors);
}
- return !has_errors;
+ return errors->size() == error_count_before;
}
// If the file exists:
@@ -320,11 +328,13 @@ bool HeaderChecker::CheckFile(const Target* from_target,
// - 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 {
+void HeaderChecker::CheckInclude(
+ const Target* from_target,
+ const InputFile& source_file,
+ const SourceFile& include_file,
+ const LocationRange& range,
+ std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
+ std::vector<Err>* errors) const {
// Assume if the file isn't declared in our sources that we don't need to
// check it. It would be nice if we could give an error if this happens, but
// our include finder is too primitive and returns all includes, even if
@@ -332,7 +342,7 @@ bool HeaderChecker::CheckInclude(const Target* from_target,
// 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;
+ return;
const TargetVector& targets = found->second;
Chain chain; // Prevent reallocating in the loop.
@@ -362,7 +372,7 @@ bool HeaderChecker::CheckInclude(const Target* from_target,
}
}
if (!present_in_current_toolchain)
- return true;
+ return;
// For all targets containing this file, we require that at least one be
// a direct or public dependency of the current target, and either (1) the
@@ -380,10 +390,20 @@ bool HeaderChecker::CheckInclude(const Target* from_target,
// target.
const Target* to_target = target.target;
if (to_target == from_target)
- return true;
+ return;
bool is_permitted_chain = false;
- if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
+
+ bool cached_no_dependency =
+ no_dependency_cache->find(std::make_pair(to_target, from_target)) !=
+ no_dependency_cache->end();
+
+ bool add_to_cache = !cached_no_dependency;
+
+ if (!cached_no_dependency &&
+ IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
+ add_to_cache = false;
+
DCHECK(chain.size() >= 2);
DCHECK(chain[0].target == to_target);
DCHECK(chain[chain.size() - 1].target == from_target);
@@ -421,17 +441,22 @@ bool HeaderChecker::CheckInclude(const Target* from_target,
last_error = Err();
break;
}
- }
- if (!found_dependency) {
- DCHECK(!last_error.has_error());
- *err = MakeUnreachableError(source_file, range, from_target, targets);
- return false;
+ if (add_to_cache) {
+ no_dependency_cache->emplace(to_target, from_target);
+ }
}
- if (last_error.has_error()) {
- // Found at least one dependency chain above, but it had an error.
- *err = last_error;
- return false;
+
+ if (!found_dependency || last_error.has_error()) {
+ if (!found_dependency) {
+ DCHECK(!last_error.has_error());
+ Err err = MakeUnreachableError(source_file, range, from_target, targets);
+ errors->push_back(std::move(err));
+ } else {
+ // Found at least one dependency chain above, but it had an error.
+ errors->push_back(std::move(last_error));
+ }
+ return;
}
// One thing we didn't check for is targets that expose their dependents
@@ -451,8 +476,6 @@ bool HeaderChecker::CheckInclude(const Target* from_target,
// - 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,
diff --git a/gn/tools/gn/header_checker.h b/gn/tools/gn/header_checker.h
index ef14729fd13..3932001f8fb 100644
--- a/gn/tools/gn/header_checker.h
+++ b/gn/tools/gn/header_checker.h
@@ -8,6 +8,7 @@
#include <condition_variable>
#include <map>
#include <mutex>
+#include <set>
#include <vector>
#include "base/atomic_ref_count.h"
@@ -47,8 +48,12 @@ class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> {
};
typedef std::vector<ChainLink> Chain;
+ // check_generated, if true, will also check generated
+ // files. Something that can only be done after running a build that
+ // has generated them.
HeaderChecker(const BuildSettings* build_settings,
- const std::vector<const Target*>& targets);
+ const std::vector<const Target*>& targets,
+ bool check_generated = false);
// Runs the check. The targets in to_check will be checked.
//
@@ -119,15 +124,19 @@ class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> {
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;
+ // Checks that the given file in the given target can include the
+ // given include file. If disallowed, adds the error or errors to
+ // the errors array. The range indicates the location of the
+ // include in the file for error reporting.
+ // |no_depeency_cache| is used to cache or check whether there is no
+ // dependency from |from_target| to target having |include_file|.
+ void CheckInclude(
+ const Target* from_target,
+ const InputFile& source_file,
+ const SourceFile& include_file,
+ const LocationRange& range,
+ std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
+ std::vector<Err>* errors) const;
// Returns true if the given search_for target is a dependency of
// search_from.
@@ -171,6 +180,8 @@ class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> {
const BuildSettings* build_settings_;
+ bool check_generated_;
+
// Maps source files to targets it appears in (usually just one target).
FileMap file_map_;
diff --git a/gn/tools/gn/header_checker_unittest.cc b/gn/tools/gn/header_checker_unittest.cc
index 65198715469..c6529d51dfa 100644
--- a/gn/tools/gn/header_checker_unittest.cc
+++ b/gn/tools/gn/header_checker_unittest.cc
@@ -193,34 +193,47 @@ TEST_F(HeaderCheckerTest, CheckInclude) {
scoped_refptr<HeaderChecker> checker(
new HeaderChecker(setup_.build_settings(), targets_));
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
// A file in target A can't include a header from D because A has no
// dependency on D.
- EXPECT_FALSE(checker->CheckInclude(&a_, input_file, d_header, range, &err));
- EXPECT_TRUE(err.has_error());
+ std::vector<Err> errors;
+ checker->CheckInclude(&a_, input_file, d_header, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
// A can include the public header in B.
- err = Err();
- EXPECT_TRUE(checker->CheckInclude(&a_, input_file, b_public, range, &err));
- EXPECT_FALSE(err.has_error());
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, b_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
// Check A depending on the public and private headers in C.
- 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());
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, c_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
// A can depend on a random file unknown to the build.
- err = Err();
- EXPECT_TRUE(checker->CheckInclude(&a_, input_file, SourceFile("//random.h"),
- range, &err));
- EXPECT_FALSE(err.has_error());
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, SourceFile("//random.h"), range,
+ &no_dependency_cache, &errors);
+ EXPECT_EQ(errors.size(), 0);
// A can depend on a file present only in another toolchain even with no
// dependency path.
- err = Err();
- EXPECT_TRUE(checker->CheckInclude(&a_, input_file, otc_header, range, &err));
- EXPECT_FALSE(err.has_error());
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, otc_header, range,
+ &no_dependency_cache, &errors);
+ EXPECT_EQ(errors.size(), 0);
}
// A public chain of dependencies should always be identified first, even if
@@ -282,17 +295,21 @@ TEST_F(HeaderCheckerTest, CheckIncludeAllowCircular) {
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());
+ std::vector<Err> errors;
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+ checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
// Add an allow_circular_includes_from on A that lists B.
a_.allow_circular_includes_from().insert(b_.label());
// Now the include from B to A should be allowed.
- err = Err();
- EXPECT_TRUE(checker->CheckInclude(&b_, input_file, a_public, range, &err));
- EXPECT_FALSE(err.has_error());
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
}
TEST_F(HeaderCheckerTest, SourceFileForInclude) {
@@ -371,12 +388,16 @@ TEST_F(HeaderCheckerTest, Friend) {
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());
+ std::vector<Err> errors;
+ std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
+ checker->CheckInclude(&b_, input_file, c_private, range, &no_dependency_cache,
+ &errors);
+ EXPECT_GT(errors.size(), 0);
// A should be able to because of the friend declaration.
- err = Err();
- EXPECT_TRUE(checker->CheckInclude(&a_, input_file, c_private, range, &err));
- EXPECT_FALSE(err.has_error()) << err.message();
+ errors.clear();
+ no_dependency_cache.clear();
+ checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
+ &errors);
+ EXPECT_EQ(errors.size(), 0);
}
diff --git a/gn/tools/gn/input_conversion.cc b/gn/tools/gn/input_conversion.cc
index df58d9d676a..270d437fdd9 100644
--- a/gn/tools/gn/input_conversion.cc
+++ b/gn/tools/gn/input_conversion.cc
@@ -241,9 +241,11 @@ Value DoConvertInputToValue(const Settings* settings,
} // 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:
+ R"(Input and output conversion
+
+ Input and output conversions are arguments to file and process functions
+ that specify how to convert data to or from external formats. The possible
+ values for parameters specifying conversions are:
"" (the default)
input: Discard the result and return None.
@@ -253,21 +255,21 @@ values for parameters specifying conversions are:
"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.
+ 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.
+ 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:
+ 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
@@ -312,8 +314,8 @@ values for parameters specifying conversions are:
will produce an error if assigned to a variable.
output:
- Render the value contents as a literal rvalue. Strings render with escaped
- quotes.
+ 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.
diff --git a/gn/tools/gn/label_ptr.h b/gn/tools/gn/label_ptr.h
index 5aaa02d5c91..ac994f51d87 100644
--- a/gn/tools/gn/label_ptr.h
+++ b/gn/tools/gn/label_ptr.h
@@ -24,25 +24,23 @@ template <typename T>
struct LabelPtrPair {
typedef T DestType;
- LabelPtrPair() : label(), ptr(nullptr), origin(nullptr) {}
+ LabelPtrPair() = default;
- explicit LabelPtrPair(const Label& l)
- : label(l), ptr(nullptr), origin(nullptr) {}
+ explicit LabelPtrPair(const Label& l) : label(l) {}
// This contructor is typically used in unit tests, it extracts the label
// automatically from a given pointer.
- explicit LabelPtrPair(const T* p)
- : label(p->label()), ptr(p), origin(nullptr) {}
+ explicit LabelPtrPair(const T* p) : label(p->label()), ptr(p) {}
- ~LabelPtrPair() {}
+ ~LabelPtrPair() = default;
Label label;
- const T* ptr; // May be NULL.
+ const T* ptr = nullptr;
// The origin of this dependency. This will be null for internally generated
// dependencies. This happens when a group is automatically expanded and that
// group's members are added to the target that depends on that group.
- const ParseNode* origin;
+ const ParseNode* origin = nullptr;
};
typedef LabelPtrPair<Config> LabelConfigPair;
@@ -51,41 +49,6 @@ 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
diff --git a/gn/tools/gn/metadata.cc b/gn/tools/gn/metadata.cc
new file mode 100644
index 00000000000..3250b031327
--- /dev/null
+++ b/gn/tools/gn/metadata.cc
@@ -0,0 +1,69 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/metadata.h"
+
+#include "tools/gn/filesystem_utils.h"
+
+bool Metadata::WalkStep(const BuildSettings* settings,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::vector<Value>* next_walk_keys,
+ std::vector<Value>* result,
+ Err* err) const {
+ // If there's no metadata, there's nothing to find, so quick exit.
+ if (contents_.empty()) {
+ next_walk_keys->emplace_back(nullptr, "");
+ return true;
+ }
+
+ // Pull the data from each specified key.
+ for (const auto& key : keys_to_extract) {
+ auto iter = contents_.find(key);
+ if (iter == contents_.end())
+ continue;
+ assert(iter->second.type() == Value::LIST);
+
+ if (!rebase_dir.is_null()) {
+ for (const auto& val : iter->second.list_value()) {
+ if (!val.VerifyTypeIs(Value::STRING, err))
+ return false;
+ std::string filename = source_dir_.ResolveRelativeAs(
+ /*as_file = */ true, val, err, settings->root_path_utf8());
+ if (err->has_error())
+ return false;
+
+ result->emplace_back(
+ val.origin(),
+ RebasePath(filename, rebase_dir, settings->root_path_utf8()));
+ }
+ } else {
+ result->insert(result->end(), iter->second.list_value().begin(),
+ iter->second.list_value().end());
+ }
+ }
+
+ // Get the targets to look at next. If no keys_to_walk are present, we push
+ // the empty string to the list so that the target knows to include its deps
+ // and data_deps. The values used here must be lists of strings.
+ bool found_walk_key = false;
+ for (const auto& key : keys_to_walk) {
+ auto iter = contents_.find(key);
+ if (iter != contents_.end()) {
+ found_walk_key = true;
+ assert(iter->second.type() == Value::LIST);
+ for (const auto& val : iter->second.list_value()) {
+ if (!val.VerifyTypeIs(Value::STRING, err))
+ return false;
+ next_walk_keys->emplace_back(val);
+ }
+ }
+ }
+
+ if (!found_walk_key)
+ next_walk_keys->emplace_back(nullptr, "");
+
+ return true;
+}
diff --git a/gn/tools/gn/metadata.h b/gn/tools/gn/metadata.h
new file mode 100644
index 00000000000..da79fdf5dcb
--- /dev/null
+++ b/gn/tools/gn/metadata.h
@@ -0,0 +1,69 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_METADATA_H_
+#define TOOLS_GN_METADATA_H_
+
+#include <memory>
+
+#include "tools/gn/build_settings.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_dir.h"
+
+// Metadata about a particular target.
+//
+// Metadata is a collection of keys and values relating to a particular target.
+// Generally, these keys will include three categories of strings: ordinary
+// strings, filenames intended to be rebased according to their particular
+// source directory, and target labels intended to be used as barriers to the
+// walk. Verfication of these categories occurs at walk time, not creation
+// time (since it is not clear until the walk which values are intended for
+// which purpose).
+//
+// Represented as a scope in the expression language, here it is reduced to just
+// the KeyValueMap (since it doesn't need the logical overhead of a full scope).
+// Values must be lists of strings, as the walking collection logic contatenates
+// their values across targets.
+class Metadata {
+ public:
+ using Contents = Scope::KeyValueMap;
+
+ // Members must be set explicitly.
+ Metadata() = default;
+
+ const ParseNode* origin() const { return origin_; }
+ void set_origin(const ParseNode* origin) { origin_ = origin; }
+
+ // The contents of this metadata varaiable.
+ const Contents& contents() const { return contents_; }
+ Contents& contents() { return contents_; }
+ void set_contents(Contents&& contents) { contents_.swap(contents); }
+
+ // The relative source directory to use when rebasing.
+ const SourceDir& source_dir() const { return source_dir_; }
+ SourceDir& source_dir() { return source_dir_; }
+ void set_source_dir(const SourceDir& d) { source_dir_ = d; }
+
+ // Collect the specified metadata from this instance.
+ //
+ // Calling this will populate `next_walk_keys` with the values of targets to
+ // be walked next (with the empty string "" indicating that the target should
+ // walk all of its deps and data_deps).
+ bool WalkStep(const BuildSettings* settings,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::vector<Value>* next_walk_keys,
+ std::vector<Value>* result,
+ Err* err) const;
+
+ private:
+ const ParseNode* origin_;
+ Contents contents_;
+ SourceDir source_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(Metadata);
+};
+
+#endif // TOOLS_GN_METADATA_H_
diff --git a/gn/tools/gn/metadata_unittest.cc b/gn/tools/gn/metadata_unittest.cc
new file mode 100644
index 00000000000..b15937242bf
--- /dev/null
+++ b/gn/tools/gn/metadata_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/metadata.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(MetadataTest, SetContents) {
+ Metadata metadata;
+
+ ASSERT_TRUE(metadata.contents().empty());
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+
+ Metadata::Contents contents;
+ contents.insert(std::pair<base::StringPiece, Value>("a", a_expected));
+ contents.insert(std::pair<base::StringPiece, Value>("b", b_expected));
+
+ metadata.set_contents(std::move(contents));
+
+ ASSERT_EQ(metadata.contents().size(), 2);
+ auto a_actual = metadata.contents().find("a");
+ auto b_actual = metadata.contents().find("b");
+ ASSERT_FALSE(a_actual == metadata.contents().end());
+ ASSERT_FALSE(b_actual == metadata.contents().end());
+ ASSERT_EQ(a_actual->second, a_expected);
+ ASSERT_EQ(b_actual->second, b_expected);
+}
+
+TEST(MetadataTest, Walk) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
+ a_expected.list_value().push_back(Value(nullptr, "bar.h"));
+
+ metadata.contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.emplace_back("a");
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected;
+ expected.emplace_back(Value(nullptr, "foo.cpp"));
+ expected.emplace_back(Value(nullptr, "bar.h"));
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_EQ(results, expected);
+}
+
+TEST(MetadataTest, WalkWithRebase) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
+ a_expected.list_value().push_back(Value(nullptr, "foo/bar.h"));
+
+ metadata.contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.emplace_back("a");
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected;
+ expected.emplace_back(Value(nullptr, "../home/files/foo.cpp"));
+ expected.emplace_back(Value(nullptr, "../home/files/foo/bar.h"));
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir("/usr/foo_dir/"),
+ &next_walk_keys, &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_EQ(results, expected);
+}
+
+TEST(MetadataTest, WalkWithRebaseError) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
+ a_expected.list_value().push_back(Value(nullptr, true));
+
+ metadata.contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.emplace_back("a");
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ Err err;
+ EXPECT_FALSE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir("/foo_dir/"),
+ &next_walk_keys, &results, &err));
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(MetadataTest, WalkKeysToWalk) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "//target"));
+
+ metadata.contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ std::vector<std::string> walk_keys;
+ walk_keys.emplace_back("a");
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "//target");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_TRUE(results.empty());
+}
+
+TEST(MetadataTest, WalkNoContents) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ std::vector<std::string> data_keys;
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_TRUE(results.empty());
+}
+
+TEST(MetadataTest, WalkNoKeysWithContents) {
+ TestWithScope setup;
+ Metadata metadata;
+ metadata.set_source_dir(SourceDir("/usr/home/files/"));
+
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "//target"));
+
+ metadata.contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ std::vector<std::string> data_keys;
+ std::vector<std::string> walk_keys;
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> results;
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
+ Err err;
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir(), &next_walk_keys,
+ &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_TRUE(results.empty());
+}
diff --git a/gn/tools/gn/metadata_walk.cc b/gn/tools/gn/metadata_walk.cc
new file mode 100644
index 00000000000..3022e5e265c
--- /dev/null
+++ b/gn/tools/gn/metadata_walk.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/metadata_walk.h"
+
+std::vector<Value> WalkMetadata(
+ const UniqueVector<const Target*>& targets_to_walk,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::set<const Target*>* targets_walked,
+ Err* err) {
+ std::vector<Value> result;
+ for (const auto* target : targets_to_walk) {
+ auto pair = targets_walked->insert(target);
+ if (pair.second) {
+ if (!target->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir, false,
+ &result, targets_walked, err))
+ return std::vector<Value>();
+ }
+ }
+ return result;
+} \ No newline at end of file
diff --git a/gn/tools/gn/metadata_walk.h b/gn/tools/gn/metadata_walk.h
new file mode 100644
index 00000000000..9f4f34d3b29
--- /dev/null
+++ b/gn/tools/gn/metadata_walk.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_METADATAWALK_H_
+#define TOOLS_GN_METADATAWALK_H_
+
+#include "tools/gn/build_settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/unique_vector.h"
+#include "tools/gn/value.h"
+
+// Function to collect metadata from resolved targets listed in targets_walked.
+// Intended to be called after all targets are resolved.
+//
+// This populates targets_walked with all targets touched by this walk, and
+// returns the list of metadata values.
+std::vector<Value> WalkMetadata(
+ const UniqueVector<const Target*>& targets_to_walk,
+ const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ std::set<const Target*>* targets_walked,
+ Err* err);
+
+#endif // TOOLS_GN_METADATAWALK_H_
diff --git a/gn/tools/gn/metadata_walk_unittest.cc b/gn/tools/gn/metadata_walk_unittest.cc
new file mode 100644
index 00000000000..634a2c564c1
--- /dev/null
+++ b/gn/tools/gn/metadata_walk_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/metadata_walk.h"
+
+#include "tools/gn/metadata.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scope.h"
+#include "tools/gn/unique_vector.h"
+#include "util/test/test.h"
+
+TEST(MetadataWalkTest, CollectNoRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("b", b_expected));
+
+ one.metadata().set_source_dir(SourceDir("/usr/home/files/"));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_2_expected));
+
+ Value b_2_expected(nullptr, Value::LIST);
+ b_2_expected.list_value().push_back(Value(nullptr, false));
+ two.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("b", b_2_expected));
+
+ two.metadata().set_source_dir(SourceDir("/usr/home/files/inner"));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+ targets.push_back(&two);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, false));
+ EXPECT_EQ(result, expected);
+
+ std::set<const Target*> expected_walked_targets;
+ expected_walked_targets.insert(&one);
+ expected_walked_targets.insert(&two);
+ EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("b", b_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_2_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ EXPECT_EQ(result, expected);
+
+ std::set<const Target*> expected_walked_targets;
+ expected_walked_targets.insert(&one);
+ expected_walked_targets.insert(&two);
+ EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithBarrier) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(
+ Value(nullptr, "//foo:two(//toolchain:default)"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("walk", walk_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_2_expected));
+
+ TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
+ Value a_3_expected(nullptr, Value::LIST);
+ a_3_expected.list_value().push_back(Value(nullptr, "baz"));
+ three.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_3_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+ one.public_deps().push_back(LabelTargetPair(&three));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_FALSE(err.has_error()) << err.message();
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ EXPECT_EQ(result, expected) << result.size();
+
+ std::set<const Target*> expected_walked_targets;
+ expected_walked_targets.insert(&one);
+ expected_walked_targets.insert(&two);
+ EXPECT_EQ(targets_walked, expected_walked_targets);
+}
+
+TEST(MetadataWalkTest, CollectWithError) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("walk", walk_expected));
+
+ UniqueVector<const Target*> targets;
+ targets.push_back(&one);
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::set<const Target*> targets_walked;
+ std::vector<Value> result = WalkMetadata(targets, data_keys, walk_keys,
+ SourceDir(), &targets_walked, &err);
+ EXPECT_TRUE(result.empty());
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.message(),
+ "I was expecting //foo:missing to be a dependency of "
+ "//foo:one(//toolchain:default). "
+ "Make sure it's included in the deps or data_deps, and that you've "
+ "specified the appropriate toolchain.")
+ << err.message();
+}
diff --git a/gn/tools/gn/misc/emacs/gn-mode.el b/gn/tools/gn/misc/emacs/gn-mode.el
index 38564c6dd1e..5930b585220 100644
--- a/gn/tools/gn/misc/emacs/gn-mode.el
+++ b/gn/tools/gn/misc/emacs/gn-mode.el
@@ -63,7 +63,7 @@ variable name or the '{{' and '}}' which surround it."
(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"))
+ "generated_file" "target"))
;; pool() is handled specially since it's also a variable name
(defvar gn-font-lock-buildfile-fun-keywords
@@ -83,16 +83,17 @@ variable name or the '{{' and '}}' which surround it."
(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"))
+ "bundle_resources_dir" "bundle_root_dir" "cflags" "cflags_c" "cflags_cc"
+ "cflags_objc" "cflags_objcc" "check_includes" "code_signing_args"
+ "code_signing_outputs" "code_signing_script" "code_signing_sources"
+ "complete_static_lib" "configs" "data" "data_deps" "defines" "depfile"
+ "deps" "include_dirs" "inputs" "ldflags" "lib_dirs" "libs" "output_dir"
+ "output_extension" "output_name" "output_prefix_override" "outputs" "pool"
+ "precompiled_header" "precompiled_header_type" "precompiled_source"
+ "product_type" "public" "public_configs" "public_deps"
+ "response_file_contents" "script" "sources" "testonly" "visibility"
+ "write_runtime_deps" "bundle_contents_dir" "contents" "output_conversion"
+ "rebase" "data_keys" "walk_keys"))
(defconst gn-font-lock-keywords
`((,(regexp-opt gn-font-lock-reserved-keywords 'words) .
diff --git a/gn/tools/gn/misc/tm/GN.tmLanguage b/gn/tools/gn/misc/tm/GN.tmLanguage
index 9c244ff86d4..5cccf4be399 100644
--- a/gn/tools/gn/misc/tm/GN.tmLanguage
+++ b/gn/tools/gn/misc/tm/GN.tmLanguage
@@ -65,7 +65,7 @@
<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>
+ <string>\b(?:action|action_foreach|copy|executable|group|loadable_module|shared_library|source_set|static_library|generated_file)\b</string>
<key>name</key>
<string>entity.name.tag.gn</string>
</dict>
@@ -89,7 +89,7 @@
<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>
+ <string>\b(?:all_dependent_configs|allow_circular_includes_from|args|asmflags|cflags|cflags_c|cflags_cc|cflags_objc|cflags_objcc|check_includes|complete_static_lib|configs|data|data_deps|defines|depfile|deps|include_dirs|inputs|ldflags|lib_dirs|libs|output_extension|output_name|outputs|public|public_configs|public_deps|script|sources|testonly|visibility|contents|output_conversion|rebase|data_keys|walk_keys)\b</string>
<key>name</key>
<string>entity.other.attribute-name.gn</string>
</dict>
diff --git a/gn/tools/gn/misc/vim/README.chromium b/gn/tools/gn/misc/vim/README.chromium
deleted file mode 100644
index 7b73cfc2675..00000000000
--- a/gn/tools/gn/misc/vim/README.chromium
+++ /dev/null
@@ -1,5 +0,0 @@
-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/README.md b/gn/tools/gn/misc/vim/README.md
new file mode 100644
index 00000000000..f64e22ad0dc
--- /dev/null
+++ b/gn/tools/gn/misc/vim/README.md
@@ -0,0 +1,29 @@
+# GN vim syntax plugin
+
+## Installation with a plugin manager
+
+You can use modern plugin managers to download the GN repo and manage the vim
+plugin:
+
+Example config for [vim-plug](https://github.com/junegunn/vim-plug):
+
+```
+Plug 'https://gn.googlesource.com/gn', { 'rtp': 'tools/gn/misc/vim' }
+```
+
+Or, for [Vundle](https://github.com/VundleVim/Vundle.vim) users:
+
+```
+Plugin 'https://gn.googlesource.com/gn', { 'rtp': 'tools/gn/misc/vim' }
+```
+
+## Manual installation
+
+If you don't use a plugin manager or would prefer to manage the GN repo
+yourself, you can add this explicitly to `rtp` in your `.vimrc`:
+
+```
+set runtimepath+=/path/to/src/tools/gn/misc/vim
+" ...
+filetype plugin indent on " or a similar command to turn on filetypes in vim
+```
diff --git a/gn/tools/gn/misc/vim/syntax/gn.vim b/gn/tools/gn/misc/vim/syntax/gn.vim
index 9dee605f305..9d5b06043c8 100644
--- a/gn/tools/gn/misc/vim/syntax/gn.vim
+++ b/gn/tools/gn/misc/vim/syntax/gn.vim
@@ -27,7 +27,7 @@ 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
+syn keyword gnTarget loadable_module generated_file
hi def link gnTarget Type
" Buildfile functions
@@ -48,7 +48,8 @@ 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
+syn keyword gnVariable visibility contents output_conversion rebase
+syn keyword gnVariable data_keys walk_keys
hi def link gnVariable Keyword
" Strings
diff --git a/gn/tools/gn/ninja_action_target_writer_unittest.cc b/gn/tools/gn/ninja_action_target_writer_unittest.cc
index 19f81aefc03..90173b3c0fd 100644
--- a/gn/tools/gn/ninja_action_target_writer_unittest.cc
+++ b/gn/tools/gn/ninja_action_target_writer_unittest.cc
@@ -60,8 +60,7 @@ TEST(NinjaActionTargetWriter, ActionNoSources) {
NinjaActionTargetWriter writer(&target, out);
writer.Run();
- const char* expected = 1 /* skip initial newline */ + R"(
-rule __foo_bar___rule
+ const char* expected = R"(rule __foo_bar___rule
command = /usr/bin/python ../../foo/script.py
description = ACTION //foo:bar()
restat = 1
@@ -70,7 +69,7 @@ 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());
+ EXPECT_EQ(expected, out.str()) << expected << "--" << out.str();
}
// Tests an action with no sources and pool
@@ -105,8 +104,7 @@ TEST(NinjaActionTargetWriter, ActionNoSourcesConsole) {
// 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
+ const char* expected = R"(rule __foo_bar___rule
command = /usr/bin/python ../../foo/script.py
description = ACTION //foo:bar()
restat = 1
diff --git a/gn/tools/gn/ninja_build_writer.cc b/gn/tools/gn/ninja_build_writer.cc
index f26c9a7fd64..f13a8b98733 100644
--- a/gn/tools/gn/ninja_build_writer.cc
+++ b/gn/tools/gn/ninja_build_writer.cc
@@ -47,7 +47,10 @@ struct Counts {
const Target* last_seen;
};
-std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
+} // namespace
+
+base::CommandLine GetSelfInvocationCommandLine(
+ const BuildSettings* build_settings) {
const base::FilePath build_path =
build_settings->build_dir().Resolve(build_settings->root_path());
@@ -81,6 +84,18 @@ std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
escape_shell.inhibit_quoting = true;
#endif
+ // If both --root and --dotfile are passed, make sure the --dotfile is
+ // made relative to the build dir here.
+ base::FilePath dotfile_path = build_settings->dotfile_name();
+ if (!dotfile_path.empty()) {
+ if (build_path.IsAbsolute()) {
+ dotfile_path =
+ MakeAbsoluteFilePathRelativeIfPossible(build_path, dotfile_path);
+ }
+ cmdline.AppendSwitchPath(std::string("--") + switches::kDotfile,
+ dotfile_path.NormalizePathSeparatorsTo('/'));
+ }
+
const base::CommandLine& our_cmdline =
*base::CommandLine::ForCurrentProcess();
const base::CommandLine::SwitchMap& switches = our_cmdline.GetSwitches();
@@ -91,13 +106,21 @@ std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
// 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) {
+ i->first != switches::kDotfile && i->first != switches::kArgs) {
std::string escaped_value =
EscapeString(FilePathToUTF8(i->second), escape_shell, nullptr);
cmdline.AppendSwitchASCII(i->first, escaped_value);
}
}
+ return cmdline;
+}
+
+namespace {
+
+std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
+ base::CommandLine cmdline = GetSelfInvocationCommandLine(
+ build_settings);
#if defined(OS_WIN)
return base::WideToUTF8(cmdline.GetCommandLineString());
#else
@@ -163,12 +186,14 @@ NinjaBuildWriter::NinjaBuildWriter(
const BuildSettings* build_settings,
const std::unordered_map<const Settings*, const Toolchain*>&
used_toolchains,
+ const std::vector<const Target*>& all_targets,
const Toolchain* default_toolchain,
const std::vector<const Target*>& default_toolchain_targets,
std::ostream& out,
std::ostream& dep_out)
: build_settings_(build_settings),
used_toolchains_(used_toolchains),
+ all_targets_(all_targets),
default_toolchain_(default_toolchain),
default_toolchain_targets_(default_toolchain_targets),
out_(out),
@@ -221,8 +246,9 @@ bool NinjaBuildWriter::RunAndWriteFile(const BuildSettings* build_settings,
std::stringstream file;
std::stringstream depfile;
- NinjaBuildWriter gen(build_settings, used_toolchains, default_toolchain,
- default_toolchain_targets, file, depfile);
+ NinjaBuildWriter gen(build_settings, used_toolchains, all_targets,
+ default_toolchain, default_toolchain_targets,
+ file, depfile);
if (!gen.Run(err))
return false;
@@ -301,7 +327,7 @@ void NinjaBuildWriter::WriteAllPools() {
}
}
- for (const Target* target : default_toolchain_targets_) {
+ for (const Target* target : all_targets_) {
if (target->output_type() == Target::ACTION) {
const LabelPtrPair<Pool>& pool = target->action_values().pool();
if (pool.ptr)
diff --git a/gn/tools/gn/ninja_build_writer.h b/gn/tools/gn/ninja_build_writer.h
index eee3a6e1f84..f4351e4bc86 100644
--- a/gn/tools/gn/ninja_build_writer.h
+++ b/gn/tools/gn/ninja_build_writer.h
@@ -20,6 +20,10 @@ class Settings;
class Target;
class Toolchain;
+namespace base {
+class CommandLine;
+} // base
+
// Generates the toplevel "build.ninja" file. This references the individual
// toolchain files and lists all input .gn files as dependencies of the
// build itself.
@@ -28,6 +32,7 @@ class NinjaBuildWriter {
NinjaBuildWriter(const BuildSettings* settings,
const std::unordered_map<const Settings*, const Toolchain*>&
used_toolchains,
+ const std::vector<const Target*>& all_targets,
const Toolchain* default_toolchain,
const std::vector<const Target*>& default_toolchain_targets,
std::ostream& out,
@@ -56,6 +61,7 @@ class NinjaBuildWriter {
const BuildSettings* build_settings_;
const std::unordered_map<const Settings*, const Toolchain*>& used_toolchains_;
+ const std::vector<const Target*>& all_targets_;
const Toolchain* default_toolchain_;
const std::vector<const Target*>& default_toolchain_targets_;
@@ -68,4 +74,8 @@ class NinjaBuildWriter {
extern const char kNinjaRules_Help[];
+// Exposed for testing.
+base::CommandLine GetSelfInvocationCommandLine(
+ const BuildSettings* build_settings);
+
#endif // TOOLS_GN_NINJA_BUILD_WRITER_H_
diff --git a/gn/tools/gn/ninja_build_writer_unittest.cc b/gn/tools/gn/ninja_build_writer_unittest.cc
index bd93d36a8e2..50530b199d6 100644
--- a/gn/tools/gn/ninja_build_writer_unittest.cc
+++ b/gn/tools/gn/ninja_build_writer_unittest.cc
@@ -4,9 +4,12 @@
#include <sstream>
+#include "base/command_line.h"
+#include "base/files/file_util.h"
#include "tools/gn/ninja_build_writer.h"
#include "tools/gn/pool.h"
#include "tools/gn/scheduler.h"
+#include "tools/gn/switches.h"
#include "tools/gn/target.h"
#include "tools/gn/test_with_scheduler.h"
#include "tools/gn/test_with_scope.h"
@@ -14,6 +17,55 @@
using NinjaBuildWriterTest = TestWithScheduler;
+class ScopedDotGNFile {
+ public:
+ ScopedDotGNFile(const base::FilePath& path)
+ : path_(path),
+ file_(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE) {
+ EXPECT_TRUE(file_.IsValid());
+ }
+ ~ScopedDotGNFile() {
+ file_.Close();
+ base::DeleteFile(path_, false);
+ }
+
+ private:
+ base::FilePath path_;
+ base::File file_;
+};
+
+TEST_F(NinjaBuildWriterTest, GetSelfInvocationCommandLine) {
+ // TestWithScope sets up a config with a build dir of //out/Debug.
+ TestWithScope setup;
+ base::CommandLine cmd_out(base::CommandLine::NO_PROGRAM);
+
+ // Setup sets the default root dir to ".".
+ base::FilePath root(FILE_PATH_LITERAL("."));
+ base::FilePath root_realpath = base::MakeAbsoluteFilePath(root);
+
+ base::FilePath gn(FILE_PATH_LITERAL("testdot.gn"));
+
+ // The file must exist on disk for MakeAbsoluteFilePath() to work.
+ ScopedDotGNFile dot_gn(gn);
+ base::FilePath gn_realpath = base::MakeAbsoluteFilePath(gn);
+
+ // Without any parameters the self invocation should pass --root=../..
+ // (from //out/Debug to //).
+ setup.build_settings()->SetRootPath(root_realpath);
+ cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
+ EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
+ EXPECT_FALSE(cmd_out.HasSwitch(switches::kDotfile));
+
+ // If --root is . and --dotfile is foo/.gn, then --dotfile also needs
+ // to to become ../../foo/.gn.
+ setup.build_settings()->SetRootPath(root_realpath);
+ setup.build_settings()->set_dotfile_name(gn_realpath);
+ cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
+ EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
+ EXPECT_EQ("../../testdot.gn",
+ cmd_out.GetSwitchValueASCII(switches::kDotfile));
+}
+
TEST_F(NinjaBuildWriterTest, TwoTargets) {
TestWithScope setup;
Err err;
@@ -47,6 +99,24 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
other_toolchain.GetTool(Toolchain::TYPE_LINK)
->set_pool(LabelPtrPair<Pool>(&other_regular_pool));
+ // Make another target that uses its own pool
+
+ Pool another_regular_pool(
+ setup.settings(),
+ Label(SourceDir("//another/"), "depth_pool", other_toolchain_label.dir(),
+ other_toolchain_label.name()));
+ another_regular_pool.set_depth(7);
+
+ Target target_baz(setup.settings(), Label(SourceDir("//baz/"), "baz"));
+ target_baz.set_output_type(Target::ACTION);
+ target_baz.action_values().set_script(SourceFile("//baz/script.py"));
+ target_baz.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out5.out", "//out/Debug/out6.out");
+ target_baz.SetToolchain(&other_toolchain);
+ target_baz.action_values().set_pool(
+ LabelPtrPair<Pool>(&another_regular_pool));
+ ASSERT_TRUE(target_baz.OnResolved(&err));
+
// The console pool must be in the default toolchain.
Pool console_pool(setup.settings(), Label(SourceDir("//"), "console",
setup.toolchain()->label().dir(),
@@ -63,12 +133,12 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
used_toolchains[setup.settings()] = setup.toolchain();
used_toolchains[&other_settings] = &other_toolchain;
- std::vector<const Target*> targets = {&target_foo, &target_bar};
+ std::vector<const Target*> targets = {&target_foo, &target_bar, &target_baz};
std::ostringstream ninja_out;
std::ostringstream depfile_out;
- NinjaBuildWriter writer(setup.build_settings(), used_toolchains,
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
setup.toolchain(), targets, ninja_out, depfile_out);
ASSERT_TRUE(writer.Run(&err));
@@ -78,17 +148,23 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
" generator = 1\n"
" depfile = build.ninja.d\n";
const char expected_other_pool[] =
+ "pool other_toolchain_another_depth_pool\n"
+ " depth = 7\n"
+ "\n"
"pool other_toolchain_other_depth_pool\n"
" depth = 42\n";
const char expected_toolchain[] = "subninja toolchain.ninja\n";
const char expected_targets[] =
"build bar: phony obj/bar/bar.stamp\n"
+ "build baz: phony obj/baz/baz.stamp\n"
"build foo$:bar: phony obj/foo/bar.stamp\n"
- "build bar$:bar: phony obj/bar/bar.stamp\n";
+ "build bar$:bar: phony obj/bar/bar.stamp\n"
+ "build baz$:baz: phony obj/baz/baz.stamp\n";
const char expected_root_target[] =
"build all: phony $\n"
" obj/foo/bar.stamp $\n"
- " obj/bar/bar.stamp\n";
+ " obj/bar/bar.stamp $\n"
+ " obj/baz/baz.stamp\n";
const char expected_default[] = "default all\n";
std::string out_str = ninja_out.str();
#define EXPECT_SNIPPET(expected) \
@@ -133,7 +209,7 @@ TEST_F(NinjaBuildWriterTest, DuplicateOutputs) {
std::vector<const Target*> targets = {&target_foo, &target_bar};
std::ostringstream ninja_out;
std::ostringstream depfile_out;
- NinjaBuildWriter writer(setup.build_settings(), used_toolchains,
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
setup.toolchain(), targets, ninja_out, depfile_out);
ASSERT_FALSE(writer.Run(&err));
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer.cc b/gn/tools/gn/ninja_create_bundle_target_writer.cc
index 9834aba1c66..cfc3d91dbdb 100644
--- a/gn/tools/gn/ninja_create_bundle_target_writer.cc
+++ b/gn/tools/gn/ninja_create_bundle_target_writer.cc
@@ -16,6 +16,11 @@
namespace {
+bool TargetRequireAssetCatalogCompilation(const Target* target) {
+ return !target->bundle_data().assets_catalog_sources().empty() ||
+ !target->bundle_data().partial_info_plist().is_null();
+}
+
void FailWithMissingToolError(Toolchain::ToolType tool, const Target* target) {
const std::string& tool_name = Toolchain::ToolTypeToName(tool);
g_scheduler->FailWithError(
@@ -33,7 +38,6 @@ void FailWithMissingToolError(Toolchain::ToolType tool, const Target* target) {
bool EnsureAllToolsAvailable(const Target* target) {
const Toolchain::ToolType kRequiredTools[] = {
Toolchain::TYPE_COPY_BUNDLE_DATA,
- Toolchain::TYPE_COMPILE_XCASSETS,
Toolchain::TYPE_STAMP,
};
@@ -44,6 +48,15 @@ bool EnsureAllToolsAvailable(const Target* target) {
}
}
+ // The compile_xcassets tool is only required if the target has asset
+ // catalog resources to compile.
+ if (TargetRequireAssetCatalogCompilation(target)) {
+ if (!target->toolchain()->GetTool(Toolchain::TYPE_COMPILE_XCASSETS)) {
+ FailWithMissingToolError(Toolchain::TYPE_COMPILE_XCASSETS, target);
+ return false;
+ }
+ }
+
return true;
}
@@ -135,12 +148,18 @@ void NinjaCreateBundleTargetWriter::WriteCopyBundleFileRuleSteps(
// 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);
+ // There is no need to check for errors here as the substitution will have
+ // been performed when computing the list of output of the target during
+ // the Target::OnResolved phase earlier.
+ OutputFile expanded_output_file;
+ file_rule.ApplyPatternToSourceAsOutputFile(
+ settings_, target_, target_->bundle_data(), source_file,
+ &expanded_output_file,
+ /*err=*/nullptr);
+ output_files->push_back(expanded_output_file);
out_ << "build ";
- path_output_.WriteFile(out_, output_file);
+ path_output_.WriteFile(out_, expanded_output_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
<< Toolchain::ToolTypeToName(Toolchain::TYPE_COPY_BUNDLE_DATA) << " ";
path_output_.WriteFile(out_, source_file);
@@ -157,8 +176,7 @@ void NinjaCreateBundleTargetWriter::WriteCopyBundleFileRuleSteps(
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())
+ if (!TargetRequireAssetCatalogCompilation(target_))
return;
OutputFile compiled_catalog;
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc b/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
index 3c62c59d133..af7a7f5618b 100644
--- a/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
+++ b/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -22,8 +22,6 @@ void SetupBundleDataDir(BundleData* bundle_data, const std::string& root_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) {
diff --git a/gn/tools/gn/ninja_generated_file_target_writer.cc b/gn/tools/gn/ninja_generated_file_target_writer.cc
new file mode 100644
index 00000000000..7dc60b02c57
--- /dev/null
+++ b/gn/tools/gn/ninja_generated_file_target_writer.cc
@@ -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.
+
+#include "tools/gn/ninja_generated_file_target_writer.h"
+
+#include "base/strings/string_util.h"
+#include "tools/gn/deps_iterator.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/output_conversion.h"
+#include "tools/gn/output_file.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/target.h"
+#include "tools/gn/trace.h"
+
+NinjaGeneratedFileTargetWriter::NinjaGeneratedFileTargetWriter(
+ const Target* target,
+ std::ostream& out)
+ : NinjaTargetWriter(target, out) {}
+
+NinjaGeneratedFileTargetWriter::~NinjaGeneratedFileTargetWriter() = default;
+
+void NinjaGeneratedFileTargetWriter::Run() {
+ // Write the file.
+ GenerateFile();
+
+ // A generated_file target should generate a stamp file with dependencies
+ // on each of the deps and data_deps in the target. The actual collection is
+ // done at gen time, and so ninja doesn't need to know about it.
+ std::vector<OutputFile> output_files;
+ for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED))
+ output_files.push_back(pair.ptr->dependency_output_file());
+
+ std::vector<OutputFile> data_output_files;
+ const LabelTargetVector& data_deps = target_->data_deps();
+ for (const auto& pair : data_deps)
+ data_output_files.push_back(pair.ptr->dependency_output_file());
+
+ WriteStampForTarget(output_files, data_output_files);
+}
+
+void NinjaGeneratedFileTargetWriter::GenerateFile() {
+ Err err;
+
+ // If this is a metadata target, populate the write value with the appropriate
+ // data.
+ Value contents;
+ if (target_->contents().type() == Value::NONE) {
+ // Origin is set to the outputs location, so that errors with this value
+ // get flagged on the right target.
+ CHECK(target_->action_values().outputs().list().size() == 1U);
+ contents = Value(target_->action_values().outputs().list()[0].origin(),
+ Value::LIST);
+ std::set<const Target*> targets_walked;
+ if (!target_->GetMetadata(target_->data_keys(), target_->walk_keys(),
+ target_->rebase(), /*deps_only = */ true,
+ &contents.list_value(), &targets_walked, &err)) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+ } else {
+ contents = target_->contents();
+ }
+
+ std::vector<SourceFile> outputs_as_sources;
+ target_->action_values().GetOutputsAsSourceFiles(target_,
+ &outputs_as_sources);
+ CHECK(outputs_as_sources.size() == 1);
+
+ base::FilePath output =
+ settings_->build_settings()->GetFullPath(outputs_as_sources[0]);
+ ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, outputs_as_sources[0].value());
+
+ // Compute output.
+ std::ostringstream out;
+ ConvertValueToOutput(settings_, contents, target_->output_conversion(), out,
+ &err);
+
+ if (err.has_error()) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ WriteFileIfChanged(output, out.str(), &err);
+
+ if (err.has_error()) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+}
diff --git a/gn/tools/gn/ninja_generated_file_target_writer.h b/gn/tools/gn/ninja_generated_file_target_writer.h
new file mode 100644
index 00000000000..fa2e6c2ee58
--- /dev/null
+++ b/gn/tools/gn/ninja_generated_file_target_writer.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "tools/gn/ninja_target_writer.h"
+
+// Writes a .ninja file for a group target type.
+class NinjaGeneratedFileTargetWriter : public NinjaTargetWriter {
+ public:
+ NinjaGeneratedFileTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaGeneratedFileTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ void GenerateFile();
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaGeneratedFileTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_GENERATED_FILE_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc b/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc
new file mode 100644
index 00000000000..a01ef7b0c5f
--- /dev/null
+++ b/gn/tools/gn/ninja_generated_file_target_writer_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/ninja_generated_file_target_writer.h"
+
+#include "tools/gn/source_file.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scheduler.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using NinjaGeneratedFileTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaGeneratedFileTargetWriterTest, Run) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::GENERATED_FILE);
+ target.visibility().SetPublic();
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.json");
+ target.set_contents(Value(nullptr, true));
+ target.set_output_conversion(Value(nullptr, "json"));
+
+ Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
+ dep.set_output_type(Target::ACTION);
+ dep.visibility().SetPublic();
+ dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
+
+ Target dep2(setup.settings(), Label(SourceDir("//foo/"), "dep2"));
+ dep2.set_output_type(Target::ACTION);
+ dep2.visibility().SetPublic();
+ dep2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep2.OnResolved(&err));
+
+ Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
+ datadep.set_output_type(Target::ACTION);
+ datadep.visibility().SetPublic();
+ datadep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(datadep.OnResolved(&err));
+
+ target.public_deps().push_back(LabelTargetPair(&dep));
+ target.public_deps().push_back(LabelTargetPair(&dep2));
+ target.data_deps().push_back(LabelTargetPair(&datadep));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err)) << err.message();
+
+ std::ostringstream out;
+ NinjaGeneratedFileTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "build obj/foo/bar.stamp: stamp obj/foo/dep.stamp obj/foo/dep2.stamp || "
+ "obj/foo/datadep.stamp\n";
+ EXPECT_EQ(expected, out.str());
+}
diff --git a/gn/tools/gn/ninja_target_writer.cc b/gn/tools/gn/ninja_target_writer.cc
index de87e8f9a51..253436b4f68 100644
--- a/gn/tools/gn/ninja_target_writer.cc
+++ b/gn/tools/gn/ninja_target_writer.cc
@@ -17,6 +17,7 @@
#include "tools/gn/ninja_bundle_data_target_writer.h"
#include "tools/gn/ninja_copy_target_writer.h"
#include "tools/gn/ninja_create_bundle_target_writer.h"
+#include "tools/gn/ninja_generated_file_target_writer.h"
#include "tools/gn/ninja_group_target_writer.h"
#include "tools/gn/ninja_utils.h"
#include "tools/gn/output_file.h"
@@ -83,6 +84,9 @@ std::string NinjaTargetWriter::RunAndWriteFile(const Target* target) {
} else if (target->output_type() == Target::GROUP) {
NinjaGroupTargetWriter writer(target, rules);
writer.Run();
+ } else if (target->output_type() == Target::GENERATED_FILE) {
+ NinjaGeneratedFileTargetWriter writer(target, rules);
+ writer.Run();
} else if (target->IsBinary()) {
needs_file_write = true;
NinjaBinaryTargetWriter writer(target, rules);
diff --git a/gn/tools/gn/operators.cc b/gn/tools/gn/operators.cc
index b5227f4666a..ad25159ea80 100644
--- a/gn/tools/gn/operators.cc
+++ b/gn/tools/gn/operators.cc
@@ -283,7 +283,8 @@ void RemoveMatchesFromList(const BinaryOpNode* op_node,
switch (to_remove.type()) {
case Value::BOOLEAN:
case Value::INTEGER: // Filter out the individual int/string.
- case Value::STRING: {
+ case Value::STRING:
+ case Value::SCOPE: {
bool found_match = false;
for (size_t i = 0; i < v.size(); /* nothing */) {
if (v[i] == to_remove) {
@@ -311,7 +312,7 @@ void RemoveMatchesFromList(const BinaryOpNode* op_node,
}
break;
- default:
+ case Value::NONE:
break;
}
}
diff --git a/gn/tools/gn/operators_unittest.cc b/gn/tools/gn/operators_unittest.cc
index 7e27fad9bda..2569bc56601 100644
--- a/gn/tools/gn/operators_unittest.cc
+++ b/gn/tools/gn/operators_unittest.cc
@@ -40,7 +40,9 @@ class TestParseNode : public ParseNode {
const std::string& help) const override {
return Err(this, msg);
}
- void Print(std::ostream& out, int indent) const override {}
+ base::Value GetJSONNode() const override {
+ return base::Value();
+ }
private:
Value value_;
@@ -232,6 +234,45 @@ TEST(Operators, ListRemove) {
EXPECT_EQ("bar", new_value->list_value()[0].string_value());
}
+TEST(Operators, ListSubtractWithScope) {
+ Err err;
+ TestWithScope setup;
+
+ Scope* scope_a = new Scope(setup.settings());
+ Value scopeval_a(nullptr, std::unique_ptr<Scope>(scope_a));
+ scope_a->SetValue("a", Value(nullptr, "foo"), nullptr);
+
+ Scope* scope_b = new Scope(setup.settings());
+ Value scopeval_b(nullptr, std::unique_ptr<Scope>(scope_b));
+ scope_b->SetValue("b", Value(nullptr, "bar"), nullptr);
+
+ Value lval(nullptr, Value::LIST);
+ lval.list_value().push_back(scopeval_a);
+ lval.list_value().push_back(scopeval_b);
+
+ Scope* scope_a_other = new Scope(setup.settings());
+ Value scopeval_a_other(nullptr, std::unique_ptr<Scope>(scope_a_other));
+ scope_a_other->SetValue("a", Value(nullptr, "foo"), nullptr);
+
+ Value rval(nullptr, Value::LIST);
+ rval.list_value().push_back(scopeval_a_other);
+
+ TestBinaryOpNode node(Token::MINUS, "-");
+ node.SetLeftToValue(lval);
+ node.SetRightToValue(rval);
+ Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+ node.right(), &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(Value::LIST, ret.type());
+
+ std::vector<Value> expected;
+ Scope* scope_expected = new Scope(setup.settings());
+ Value scopeval_expected(nullptr, std::unique_ptr<Scope>(scope_expected));
+ scope_expected->SetValue("b", Value(nullptr, "bar"), nullptr);
+ expected.push_back(scopeval_expected);
+ EXPECT_EQ(expected, ret.list_value());
+}
+
TEST(Operators, IntegerAdd) {
Err err;
TestWithScope setup;
diff --git a/gn/tools/gn/output_conversion.cc b/gn/tools/gn/output_conversion.cc
index 70c21fa5f88..7f62896a032 100644
--- a/gn/tools/gn/output_conversion.cc
+++ b/gn/tools/gn/output_conversion.cc
@@ -135,6 +135,10 @@ void DoConvertValueToOutput(const Value& output,
if (output_conversion == "") {
OutputDefault(output, out);
} else if (output_conversion == "list lines") {
+ if (output.type() != Value::LIST) {
+ *err = Err(original_output_conversion, "Not a valid list.");
+ return;
+ }
OutputListLines(output, out);
} else if (output_conversion == "string") {
OutputString(output, out);
diff --git a/gn/tools/gn/parse_tree.cc b/gn/tools/gn/parse_tree.cc
index 20f272b6efd..821fb7eeba4 100644
--- a/gn/tools/gn/parse_tree.cc
+++ b/gn/tools/gn/parse_tree.cc
@@ -10,6 +10,7 @@
#include <string>
#include <tuple>
+#include "base/json/string_escape.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "tools/gn/functions.h"
@@ -17,6 +18,14 @@
#include "tools/gn/scope.h"
#include "tools/gn/string_utils.h"
+// Dictionary keys used for JSON-formatted tree dump.
+const char kJsonNodeChild[] = "child";
+const char kJsonNodeType[] = "type";
+const char kJsonNodeValue[] = "value";
+const char kJsonBeforeComment[] = "before_comment";
+const char kJsonSuffixComment[] = "suffix_comment";
+const char kJsonAfterComment[] = "after_comment";
+
namespace {
enum DepsCategory {
@@ -53,10 +62,6 @@ std::tuple<base::StringPiece, base::StringPiece> SplitAtFirst(
: 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.
@@ -133,15 +138,42 @@ Comments* ParseNode::comments_mutable() {
return comments_.get();
}
-void ParseNode::PrintComments(std::ostream& out, int indent) const {
+base::Value ParseNode::CreateJSONNode(const char* type) const {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey(kJsonNodeType, base::Value(type));
+ AddCommentsJSONNodes(&dict);
+ return dict;
+}
+
+base::Value ParseNode::CreateJSONNode(const char* type,
+ const base::StringPiece& value) const {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey(kJsonNodeType, base::Value(type));
+ dict.SetKey(kJsonNodeValue, base::Value(value));
+ AddCommentsJSONNodes(&dict);
+ return dict;
+}
+
+void ParseNode::AddCommentsJSONNodes(base::Value* out_value) const {
if (comments_) {
- 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";
+ if (comments_->before().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->before())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonBeforeComment, std::move(comment_values));
+ }
+ if (comments_->suffix().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->suffix())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonSuffixComment, std::move(comment_values));
+ }
+ if (comments_->after().size()) {
+ base::Value comment_values(base::Value::Type::LIST);
+ for (const auto& token : comments_->after())
+ comment_values.GetList().push_back(base::Value(token.value()));
+ out_value->SetKey(kJsonAfterComment, std::move(comment_values));
+ }
}
}
@@ -178,14 +210,15 @@ Err AccessorNode::MakeErrorDescribing(const std::string& msg,
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";
+base::Value AccessorNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("ACCESSOR", base_.value()));
+ base::Value child(base::Value::Type::LIST);
if (index_)
- index_->Print(out, indent + 1);
+ child.GetList().push_back(index_->GetJSONNode());
else if (member_)
- member_->Print(out, indent + 1);
+ child.GetList().push_back(member_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
Value AccessorNode::ExecuteArrayAccess(Scope* scope, Err* err) const {
@@ -269,12 +302,18 @@ bool AccessorNode::ComputeAndValidateListIndex(Scope* scope,
"You gave me " + base::Int64ToString(index_int) + ".");
return false;
}
+ if (max_len == 0) {
+ *err = Err(index_->GetRange(), "Array subscript out of range.",
+ "You gave me " + base::Int64ToString(index_int) + " but the " +
+ "array has no elements.");
+ return false;
+ }
size_t index_sizet = static_cast<size_t>(index_int);
if (index_sizet >= max_len) {
*err = Err(index_->GetRange(), "Array subscript out of range.",
"You gave me " + base::Int64ToString(index_int) +
" but I was expecting something from 0 to " +
- base::NumberToString(max_len) + ", inclusive.");
+ base::NumberToString(max_len - 1) + ", inclusive.");
return false;
}
@@ -305,11 +344,13 @@ Err BinaryOpNode::MakeErrorDescribing(const std::string& msg,
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);
+base::Value BinaryOpNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("BINARY", op_.value()));
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(left_->GetJSONNode());
+ child.GetList().push_back(right_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
// BlockNode ------------------------------------------------------------------
@@ -382,13 +423,16 @@ Err BlockNode::MakeErrorDescribing(const std::string& msg,
return Err(GetRange(), msg, help);
}
-void BlockNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "BLOCK\n";
- PrintComments(out, indent);
+base::Value BlockNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("BLOCK"));
+ base::Value statements(base::Value::Type::LIST);
for (const auto& statement : statements_)
- statement->Print(out, indent + 1);
+ statements.GetList().push_back(statement->GetJSONNode());
if (end_ && end_->comments())
- end_->Print(out, indent + 1);
+ statements.GetList().push_back(end_->GetJSONNode());
+
+ dict.SetKey("child", std::move(statements));
+ return dict;
}
// ConditionNode --------------------------------------------------------------
@@ -435,13 +479,16 @@ Err ConditionNode::MakeErrorDescribing(const std::string& msg,
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);
+base::Value ConditionNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode("CONDITION");
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(condition_->GetJSONNode());
+ child.GetList().push_back(if_true_->GetJSONNode());
+ if (if_false_) {
+ child.GetList().push_back(if_false_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return std::move(dict);
}
// FunctionCallNode -----------------------------------------------------------
@@ -471,12 +518,30 @@ Err FunctionCallNode::MakeErrorDescribing(const std::string& msg,
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);
+base::Value FunctionCallNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode("FUNCTION", function_.value());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(args_->GetJSONNode());
+ if (block_) {
+ child.GetList().push_back(block_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
+}
+
+void FunctionCallNode::SetNewLocation(int line_number) {
+ Location func_old_loc = function_.location();
+ Location func_new_loc =
+ Location(func_old_loc.file(), line_number, func_old_loc.column_number(),
+ func_old_loc.byte());
+ function_.set_location(func_new_loc);
+
+ Location args_old_loc = args_->Begin().location();
+ Location args_new_loc =
+ Location(args_old_loc.file(), line_number, args_old_loc.column_number(),
+ args_old_loc.byte());
+ const_cast<Token&>(args_->Begin()).set_location(args_new_loc);
+ const_cast<Token&>(args_->End()->value()).set_location(args_new_loc);
}
// IdentifierNode --------------------------------------------------------------
@@ -518,9 +583,8 @@ Err IdentifierNode::MakeErrorDescribing(const std::string& msg,
return Err(value_, msg, help);
}
-void IdentifierNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "IDENTIFIER(" << value_.value() << ")\n";
- PrintComments(out, indent);
+base::Value IdentifierNode::GetJSONNode() const {
+ return CreateJSONNode("IDENTIFIER", value_.value());
}
void IdentifierNode::SetNewLocation(int line_number) {
@@ -568,14 +632,17 @@ Err ListNode::MakeErrorDescribing(const std::string& msg,
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);
+base::Value ListNode::GetJSONNode() const {
+ base::Value dict(CreateJSONNode("LIST"));
+ base::Value child(base::Value::Type::LIST);
+ for (const auto& cur : contents_) {
+ child.GetList().push_back(cur->GetJSONNode());
+ }
+ if (end_ && end_->comments()) {
+ child.GetList().push_back(end_->GetJSONNode());
+ }
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
template <typename Comparator>
@@ -773,9 +840,8 @@ Err LiteralNode::MakeErrorDescribing(const std::string& msg,
return Err(value_, msg, help);
}
-void LiteralNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "LITERAL(" << value_.value() << ")\n";
- PrintComments(out, indent);
+base::Value LiteralNode::GetJSONNode() const {
+ return CreateJSONNode("LITERAL", value_.value());
}
void LiteralNode::SetNewLocation(int line_number) {
@@ -810,10 +876,12 @@ Err UnaryOpNode::MakeErrorDescribing(const std::string& msg,
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);
+base::Value UnaryOpNode::GetJSONNode() const {
+ base::Value dict = CreateJSONNode("UNARY", op_.value());
+ base::Value child(base::Value::Type::LIST);
+ child.GetList().push_back(operand_->GetJSONNode());
+ dict.SetKey(kJsonNodeChild, std::move(child));
+ return dict;
}
// BlockCommentNode ------------------------------------------------------------
@@ -839,9 +907,10 @@ Err BlockCommentNode::MakeErrorDescribing(const std::string& msg,
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);
+base::Value BlockCommentNode::GetJSONNode() const {
+ std::string escaped;
+ base::EscapeJSONString(comment_.value().as_string(), false, &escaped);
+ return CreateJSONNode("BLOCK_COMMENT", escaped);
}
// EndNode ---------------------------------------------------------------------
@@ -867,7 +936,6 @@ Err EndNode::MakeErrorDescribing(const std::string& msg,
return Err(value_, msg, help);
}
-void EndNode::Print(std::ostream& out, int indent) const {
- out << IndentFor(indent) << "END(" << value_.value() << ")\n";
- PrintComments(out, indent);
+base::Value EndNode::GetJSONNode() const {
+ return CreateJSONNode("END", value_.value());
}
diff --git a/gn/tools/gn/parse_tree.h b/gn/tools/gn/parse_tree.h
index 415041eebca..6c61b724559 100644
--- a/gn/tools/gn/parse_tree.h
+++ b/gn/tools/gn/parse_tree.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/macros.h"
+#include "base/values.h"
#include "tools/gn/err.h"
#include "tools/gn/token.h"
#include "tools/gn/value.h"
@@ -29,6 +30,14 @@ class LiteralNode;
class Scope;
class UnaryOpNode;
+// Dictionary keys used for JSON-formatted tree dump.
+extern const char kJsonNodeChild[];
+extern const char kJsonNodeType[];
+extern const char kJsonNodeValue[];
+extern const char kJsonBeforeComment[];
+extern const char kJsonSuffixComment[];
+extern const char kJsonAfterComment[];
+
class Comments {
public:
Comments();
@@ -92,15 +101,24 @@ class ParseNode {
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;
+ // Generates a representation of this node in base::Value, to be used for
+ // exporting the tree as a JSON or formatted text with indents.
+ virtual base::Value GetJSONNode() const = 0;
const Comments* comments() const { return comments_.get(); }
Comments* comments_mutable();
- void PrintComments(std::ostream& out, int indent) const;
+
+ protected:
+ // Helper functions for GetJSONNode. Creates and fills a Value object with
+ // given type (and value).
+ base::Value CreateJSONNode(const char* type) const;
+ base::Value CreateJSONNode(const char* type, const base::StringPiece& value)
+ const;
private:
+ // Helper function for CreateJSONNode.
+ void AddCommentsJSONNodes(base::Value* out_value) const;
+
std::unique_ptr<Comments> comments_;
DISALLOW_COPY_AND_ASSIGN(ParseNode);
@@ -142,7 +160,7 @@ class AccessorNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
// Base is the thing on the left of the [] or dot, currently always required
// to be an identifier token.
@@ -196,7 +214,7 @@ class BinaryOpNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& op() const { return op_; }
void set_op(const Token& t) { op_ = t; }
@@ -241,7 +259,7 @@ class BlockNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
void set_begin_token(const Token& t) { begin_token_ = t; }
void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
@@ -282,7 +300,7 @@ class ConditionNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
void set_if_token(const Token& token) { if_token_ = token; }
@@ -323,7 +341,7 @@ class FunctionCallNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& function() const { return function_; }
void set_function(Token t) { function_ = t; }
@@ -334,6 +352,8 @@ class FunctionCallNode : public ParseNode {
const BlockNode* block() const { return block_.get(); }
void set_block(std::unique_ptr<BlockNode> b) { block_ = std::move(b); }
+ void SetNewLocation(int line_number);
+
private:
Token function_;
std::unique_ptr<ListNode> args_;
@@ -356,7 +376,7 @@ class IdentifierNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& value() const { return value_; }
void set_value(const Token& t) { value_ = t; }
@@ -382,9 +402,10 @@ class ListNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
void set_begin_token(const Token& t) { begin_token_ = t; }
+ const Token& Begin() const { return begin_token_; }
void set_end(std::unique_ptr<EndNode> e) { end_ = std::move(e); }
const EndNode* End() const { return end_.get(); }
@@ -443,7 +464,7 @@ class LiteralNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& value() const { return value_; }
void set_value(const Token& t) { value_ = t; }
@@ -469,7 +490,7 @@ class UnaryOpNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& op() const { return op_; }
void set_op(const Token& t) { op_ = t; }
@@ -504,7 +525,7 @@ class BlockCommentNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& comment() const { return comment_; }
void set_comment(const Token& t) { comment_ = t; }
@@ -532,7 +553,7 @@ class EndNode : public ParseNode {
Err MakeErrorDescribing(
const std::string& msg,
const std::string& help = std::string()) const override;
- void Print(std::ostream& out, int indent) const override;
+ base::Value GetJSONNode() const override;
const Token& value() const { return value_; }
void set_value(const Token& t) { value_ = t; }
diff --git a/gn/tools/gn/parser.cc b/gn/tools/gn/parser.cc
index df57c9ec7f8..e10ed41d159 100644
--- a/gn/tools/gn/parser.cc
+++ b/gn/tools/gn/parser.cc
@@ -214,6 +214,11 @@ Scopes
myvalues.foo += 2
empty_scope.new_thing = [ 1, 2, 3 ]
+
+ Scope equality is defined as single-level scopes identical within the current
+ scope. That is, all values in the first scope must be present and identical
+ within the second, and vice versa. Note that this means inherited scopes are
+ always unequal by definition.
)*";
enum Precedence {
@@ -884,3 +889,49 @@ void Parser::AssignComments(ParseNode* file) {
const_cast<ParseNode*>(*i)->comments_mutable()->ReverseSuffix();
}
}
+
+std::string IndentFor(int value) {
+ return std::string(value, ' ');
+}
+
+void RenderToText(const base::Value& node, int indent_level,
+ std::ostringstream& os) {
+ const base::Value* child = node.FindKey(std::string("child"));
+ std::string node_type(node.FindKey("type")->GetString());
+ if (node_type == "ACCESSOR") {
+ // AccessorNode is a bit special, in that it holds a Token, not a ParseNode
+ // for the base.
+ os << IndentFor(indent_level) << node_type << std::endl;
+ os << IndentFor(indent_level + 1) << node.FindKey("value")->GetString()
+ << std::endl;
+ } else {
+ os << IndentFor(indent_level) << node_type;
+ if (node.FindKey("value")) {
+ os << "(" << node.FindKey("value")->GetString() << ")";
+ }
+ os << std::endl;
+ }
+ if (node.FindKey(kJsonBeforeComment)) {
+ for (auto& v : node.FindKey(kJsonBeforeComment)->GetList()) {
+ os << IndentFor(indent_level + 1) <<
+ "+BEFORE_COMMENT(\"" << v.GetString() << "\")\n";
+ }
+ }
+ if (node.FindKey(kJsonSuffixComment)) {
+ for (auto& v : node.FindKey(kJsonSuffixComment)->GetList()) {
+ os << IndentFor(indent_level + 1) <<
+ "+SUFFIX_COMMENT(\"" << v.GetString() << "\")\n";
+ }
+ }
+ if (node.FindKey(kJsonAfterComment)) {
+ for (auto& v : node.FindKey(kJsonAfterComment)->GetList()) {
+ os << IndentFor(indent_level + 1) <<
+ "+AFTER_COMMENT(\"" << v.GetString() << "\")\n";
+ }
+ }
+ if (child) {
+ for (const base::Value& n : child->GetList()) {
+ RenderToText(n, indent_level + 1, os);
+ }
+ }
+}
diff --git a/gn/tools/gn/parser.h b/gn/tools/gn/parser.h
index c1930b91f58..5ecbd9dc51e 100644
--- a/gn/tools/gn/parser.h
+++ b/gn/tools/gn/parser.h
@@ -147,4 +147,9 @@ struct ParserHelper {
int precedence;
};
+// Renders parse subtree as a formatted text, indenting by the given number of
+// spaces.
+void RenderToText(const base::Value& node, int indent_level,
+ std::ostringstream& os);
+
#endif // TOOLS_GN_PARSER_H_
diff --git a/gn/tools/gn/parser_unittest.cc b/gn/tools/gn/parser_unittest.cc
index e2bbf39cdfe..d24067c352d 100644
--- a/gn/tools/gn/parser_unittest.cc
+++ b/gn/tools/gn/parser_unittest.cc
@@ -32,7 +32,7 @@ void DoParserPrintTest(const char* input, const char* expected) {
ASSERT_TRUE(result);
std::ostringstream collector;
- result->Print(collector, 0);
+ RenderToText(result->GetJSONNode(), 0, collector);
EXPECT_EQ(expected, collector.str());
}
@@ -48,7 +48,7 @@ void DoExpressionPrintTest(const char* input, const char* expected) {
ASSERT_TRUE(result);
std::ostringstream collector;
- result->Print(collector, 0);
+ RenderToText(result->GetJSONNode(), 0, collector);
EXPECT_EQ(expected, collector.str());
}
diff --git a/gn/tools/gn/qt_creator_writer.cc b/gn/tools/gn/qt_creator_writer.cc
index b43b7d68650..c24502430dc 100644
--- a/gn/tools/gn/qt_creator_writer.cc
+++ b/gn/tools/gn/qt_creator_writer.cc
@@ -11,6 +11,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/optional.h"
#include "tools/gn/builder.h"
#include "tools/gn/config_values_extractors.h"
@@ -114,7 +115,119 @@ void QtCreatorWriter::AddToSources(const Target::FileList& files) {
}
}
+namespace QtCreatorWriterUtils {
+
+enum class CVersion {
+ C99,
+ C11,
+};
+
+enum class CxxVersion {
+ CXX98,
+ CXX03,
+ CXX11,
+ CXX14,
+ CXX17,
+};
+
+std::string ToMacro(CVersion version) {
+ const std::string s = "__STDC_VERSION__";
+
+ switch(version) {
+ case CVersion::C99:
+ return s + " 199901L";
+ case CVersion::C11:
+ return s + " 201112L";
+ }
+
+ return std::string();
+}
+
+std::string ToMacro(CxxVersion version) {
+ const std::string name = "__cplusplus";
+
+ switch(version) {
+ case CxxVersion::CXX98:
+ case CxxVersion::CXX03:
+ return name + " 199711L";
+ case CxxVersion::CXX11:
+ return name + " 201103L";
+ case CxxVersion::CXX14:
+ return name + " 201402L";
+ case CxxVersion::CXX17:
+ return name + " 201703L";
+ }
+
+ return std::string();
+}
+
+const std::map<std::string, CVersion> kFlagToCVersion{
+ {"-std=gnu99" , CVersion::C99},
+ {"-std=c99" , CVersion::C99},
+ {"-std=gnu11" , CVersion::C11},
+ {"-std=c11" , CVersion::C11}
+};
+
+const std::map<std::string, CxxVersion> kFlagToCxxVersion{
+ {"-std=gnu++11", CxxVersion::CXX11}, {"-std=c++11", CxxVersion::CXX11},
+ {"-std=gnu++98", CxxVersion::CXX98}, {"-std=c++98", CxxVersion::CXX98},
+ {"-std=gnu++03", CxxVersion::CXX03}, {"-std=c++03", CxxVersion::CXX03},
+ {"-std=gnu++14", CxxVersion::CXX14}, {"-std=c++14", CxxVersion::CXX14},
+ {"-std=c++1y" , CxxVersion::CXX14},
+ {"-std=gnu++17", CxxVersion::CXX17}, {"-std=c++17", CxxVersion::CXX17},
+ {"-std=c++1z" , CxxVersion::CXX17},
+};
+
+template<typename Enum>
+struct CompVersion {
+ bool operator()(Enum a, Enum b) {
+ return static_cast<int>(a) < static_cast<int>(b);
+ }
+};
+
+struct CompilerOptions {
+ base::Optional<CVersion> c_version_;
+ base::Optional<CxxVersion> cxx_version_;
+
+ void SetCVersion(CVersion ver) {
+ SetVersionImpl(c_version_, ver);
+ }
+
+ void SetCxxVersion(CxxVersion ver) {
+ SetVersionImpl(cxx_version_, ver);
+ }
+
+private:
+ template<typename Version>
+ void SetVersionImpl(base::Optional<Version> &cur_ver, Version ver) {
+ if (cur_ver)
+ cur_ver = std::max(*cur_ver, ver, CompVersion<Version> {});
+ else
+ cur_ver = ver;
+ }
+};
+
+void ParseCompilerOption(const std::string& flag, CompilerOptions* options) {
+ auto c_ver = kFlagToCVersion.find(flag);
+ if (c_ver != kFlagToCVersion.end())
+ options->SetCVersion(c_ver->second);
+
+ auto cxx_ver = kFlagToCxxVersion.find(flag);
+ if (cxx_ver != kFlagToCxxVersion.end())
+ options->SetCxxVersion(cxx_ver->second);
+}
+
+void ParseCompilerOptions(const std::vector<std::string>& cflags,
+ CompilerOptions* options) {
+ for (const std::string& flag : cflags)
+ ParseCompilerOption(flag, options);
+}
+
+} // QtCreatorWriterUtils
+
void QtCreatorWriter::HandleTarget(const Target* target) {
+ using namespace QtCreatorWriterUtils;
+
SourceFile build_file = Loader::BuildFileForLabel(target->label());
sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(build_file)));
AddToSources(target->settings()->import_manager().GetImportedFiles());
@@ -137,13 +250,26 @@ void QtCreatorWriter::HandleTarget(const Target* target) {
FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
}
+ static constexpr const char *define_str = "#define ";
for (std::string define : it.cur().defines()) {
size_t equal_pos = define.find('=');
if (equal_pos != std::string::npos)
define[equal_pos] = ' ';
- define.insert(0, "#define ");
+ define.insert(0, define_str);
defines_.insert(define);
}
+
+ CompilerOptions options;
+ ParseCompilerOptions(it.cur().cflags(), &options);
+ ParseCompilerOptions(it.cur().cflags_c(), &options);
+ ParseCompilerOptions(it.cur().cflags_cc(), &options);
+
+ auto add_define_version = [this] (auto &ver) {
+ if (ver)
+ defines_.insert(define_str + ToMacro(*ver));
+ };
+ add_define_version(options.c_version_);
+ add_define_version(options.cxx_version_);
}
}
diff --git a/gn/tools/gn/scheduler.cc b/gn/tools/gn/scheduler.cc
index bca14724614..3e4eee98114 100644
--- a/gn/tools/gn/scheduler.cc
+++ b/gn/tools/gn/scheduler.cc
@@ -123,6 +123,16 @@ bool Scheduler::IsFileGeneratedByWriteRuntimeDeps(
return false;
}
+void Scheduler::AddGeneratedFile(const SourceFile& entry) {
+ std::lock_guard<std::mutex> lock(lock_);
+ generated_files_.insert(std::make_pair(entry, true));
+}
+
+bool Scheduler::IsFileGeneratedByTarget(const SourceFile& file) const {
+ std::lock_guard<std::mutex> lock(lock_);
+ return generated_files_.find(file) != generated_files_.end();
+}
+
std::multimap<SourceFile, const Target*> Scheduler::GetUnknownGeneratedInputs()
const {
std::lock_guard<std::mutex> lock(lock_);
diff --git a/gn/tools/gn/scheduler.h b/gn/tools/gn/scheduler.h
index b6f55329c4d..160921e5d9c 100644
--- a/gn/tools/gn/scheduler.h
+++ b/gn/tools/gn/scheduler.h
@@ -68,6 +68,10 @@ class Scheduler {
std::vector<const Target*> GetWriteRuntimeDepsTargets() const;
bool IsFileGeneratedByWriteRuntimeDeps(const OutputFile& file) const;
+ // Tracks generated_file calls.
+ void AddGeneratedFile(const SourceFile& entry);
+ bool IsFileGeneratedByTarget(const SourceFile& file) const;
+
// Unknown generated inputs are files that a target declares as an input
// in the output directory, but which aren't generated by any dependency.
//
@@ -140,6 +144,7 @@ class Scheduler {
std::vector<SourceFile> written_files_;
std::vector<const Target*> write_runtime_deps_targets_;
std::multimap<SourceFile, const Target*> unknown_generated_inputs_;
+ std::map<SourceFile, bool> generated_files_;
DISALLOW_COPY_AND_ASSIGN(Scheduler);
};
diff --git a/gn/tools/gn/scope.cc b/gn/tools/gn/scope.cc
index 236835629ec..dd1a576840a 100644
--- a/gn/tools/gn/scope.cc
+++ b/gn/tools/gn/scope.cc
@@ -274,6 +274,23 @@ void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
(*output)[pair.first] = pair.second.value;
}
+bool Scope::CheckCurrentScopeValuesEqual(const Scope* other) const {
+ // If there are containing scopes, equality shouldn't work.
+ if (containing()) {
+ return false;
+ }
+ if (values_.size() != other->values_.size()) {
+ return false;
+ }
+ for (const auto& pair : values_) {
+ const Value* v = other->GetValue(pair.first);
+ if (!v || *v != pair.second.value) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool Scope::NonRecursiveMergeTo(Scope* dest,
const MergeOptions& options,
const ParseNode* node_for_err,
diff --git a/gn/tools/gn/scope.h b/gn/tools/gn/scope.h
index 5e9745a58b0..585f1513a87 100644
--- a/gn/tools/gn/scope.h
+++ b/gn/tools/gn/scope.h
@@ -220,6 +220,11 @@ class Scope {
// scopes.
void GetCurrentScopeValues(KeyValueMap* output) const;
+ // Returns true if the values in the current scope are the same as all
+ // values in the given scope, without going to the parent scopes. Returns
+ // false if not.
+ bool CheckCurrentScopeValuesEqual(const Scope* other) const;
+
// Copies this scope's values into the destination. Values from the
// containing scope(s) (normally shadowed into the current one) will not be
// copied, neither will the reference to the containing scope (this is why
diff --git a/gn/tools/gn/setup.cc b/gn/tools/gn/setup.cc
index 94f5d798bff..e461e26bafc 100644
--- a/gn/tools/gn/setup.cc
+++ b/gn/tools/gn/setup.cc
@@ -18,7 +18,6 @@
#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"
@@ -71,7 +70,8 @@ Variables
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.
+ checked. If it is the empty list, no targets will be checked. To
+ bypass this list, request an explicit check of targets, like "//*".
The format of this list is identical to that of "visibility" so see "gn
help visibility" for examples.
@@ -149,6 +149,7 @@ Example .gn file contents
namespace {
const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
+const char kDefaultArgsGn[] = "# Set build arguments here. See `gn buildargs`.";
base::FilePath FindDotFile(const base::FilePath& current_dir) {
base::FilePath try_this_file = current_dir.Append(kGnFile);
@@ -190,6 +191,24 @@ void DecrementWorkCount() {
#if defined(OS_WIN)
+std::wstring SysMultiByteToWide(base::StringPiece mb) {
+ if (mb.empty())
+ return std::wstring();
+
+ int mb_length = static_cast<int>(mb.length());
+ // Compute the length of the buffer.
+ int charcount =
+ MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
+ if (charcount == 0)
+ return std::wstring();
+
+ std::wstring wide;
+ wide.resize(charcount);
+ MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, &wide[0], charcount);
+
+ return wide;
+}
+
// Given the path to a batch file that runs Python, extracts the name of the
// executable actually implementing Python. Generally people write a batch file
// to put something named "python" on the path, which then just redirects to
@@ -215,7 +234,7 @@ base::FilePath PythonBatToExe(const base::FilePath& bat_path) {
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));
+ base::FilePath exe_path(SysMultiByteToWide(python_path));
// Check for reasonable output, cmd may have output an error message.
if (base::PathExists(exe_path))
@@ -282,7 +301,8 @@ Setup::Setup()
dotfile_settings_(&build_settings_, std::string()),
dotfile_scope_(&dotfile_settings_),
default_args_(nullptr),
- fill_arguments_(true) {
+ fill_arguments_(true),
+ gen_empty_args_(false) {
dotfile_settings_.set_toolchain_label(Label());
build_settings_.set_item_defined_callback(
@@ -297,20 +317,25 @@ Setup::Setup()
Setup::~Setup() = default;
bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
- base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ return DoSetup(build_dir, force_create,
+ *base::CommandLine::ForCurrentProcess());
+}
- scheduler_.set_verbose_logging(cmdline->HasSwitch(switches::kVerbose));
- if (cmdline->HasSwitch(switches::kTime) ||
- cmdline->HasSwitch(switches::kTracelog))
+bool Setup::DoSetup(const std::string& build_dir,
+ bool force_create,
+ const base::CommandLine& cmdline) {
+ scheduler_.set_verbose_logging(cmdline.HasSwitch(switches::kVerbose));
+ if (cmdline.HasSwitch(switches::kTime) ||
+ cmdline.HasSwitch(switches::kTracelog))
EnableTracing();
ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "DoSetup");
- if (!FillSourceDir(*cmdline))
+ if (!FillSourceDir(cmdline))
return false;
if (!RunConfigFile())
return false;
- if (!FillOtherConfig(*cmdline))
+ if (!FillOtherConfig(cmdline))
return false;
// Must be after FillSourceDir to resolve.
@@ -326,10 +351,10 @@ bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
}
if (fill_arguments_) {
- if (!FillArguments(*cmdline))
+ if (!FillArguments(cmdline))
return false;
}
- if (!FillPythonPath(*cmdline))
+ if (!FillPythonPath(cmdline))
return false;
// Check for unused variables in the .gn file.
@@ -343,10 +368,14 @@ bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
}
bool Setup::Run() {
+ return Run(*base::CommandLine::ForCurrentProcess());
+}
+
+bool Setup::Run(const base::CommandLine& cmdline) {
RunPreMessageLoop();
if (!scheduler_.Run())
return false;
- return RunPostMessageLoop();
+ return RunPostMessageLoop(cmdline);
}
SourceFile Setup::GetBuildArgFile() const {
@@ -361,7 +390,7 @@ void Setup::RunPreMessageLoop() {
loader_->Load(root_build_file_, LocationRange(), Label());
}
-bool Setup::RunPostMessageLoop() {
+bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
Err err;
if (!builder_.CheckForBadItems(&err)) {
err.PrintToStdout();
@@ -369,8 +398,7 @@ bool Setup::RunPostMessageLoop() {
}
if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kFailOnUnusedArgs)) {
+ if (cmdline.HasSwitch(switches::kFailOnUnusedArgs)) {
err.PrintToStdout();
return false;
}
@@ -392,17 +420,16 @@ bool Setup::RunPostMessageLoop() {
}
if (!commands::CheckPublicHeaders(&build_settings_, all_targets, to_check,
- false)) {
+ false, false)) {
return false;
}
}
// Write out tracing and timing if requested.
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- if (cmdline->HasSwitch(switches::kTime))
+ if (cmdline.HasSwitch(switches::kTime))
PrintLongHelp(SummarizeTraces());
- if (cmdline->HasSwitch(switches::kTracelog))
- SaveTraces(cmdline->GetSwitchValuePath(switches::kTracelog));
+ if (cmdline.HasSwitch(switches::kTracelog))
+ SaveTraces(cmdline.GetSwitchValuePath(switches::kTracelog));
return true;
}
@@ -410,9 +437,18 @@ bool Setup::RunPostMessageLoop() {
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)))
+ // If --args is not set, args.gn file does not exist and gen_empty_args
+ // is set, generate an empty args.gn file with default comments.
+
+ base::FilePath build_arg_file =
+ build_settings_.GetFullPath(GetBuildArgFile());
+ auto switch_value = cmdline.GetSwitchValueASCII(switches::kArgs);
+ if (cmdline.HasSwitch(switches::kArgs) ||
+ (gen_empty_args_ && !PathExists(build_arg_file))) {
+ if (!FillArgsFromCommandLine(switch_value.empty() ? kDefaultArgsGn
+ : switch_value)) {
return false;
+ }
SaveArgsToFile();
return true;
}
@@ -502,7 +538,7 @@ bool Setup::SaveArgsToFile() {
base::CreateDirectory(build_arg_file.DirName());
std::string contents = args_input_file_->contents();
- commands::FormatStringToString(contents, false, &contents);
+ commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive, &contents);
#if defined(OS_WIN)
// Use Windows lineendings for this file since it will often open in
// Notepad which can't handle Unix ones.
@@ -543,19 +579,21 @@ bool Setup::FillSourceDir(const base::CommandLine& cmdline) {
// 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 =
+ base::FilePath dotfile_path =
cmdline.GetSwitchValuePath(switches::kDotfile);
- if (dot_file_path.empty()) {
+ if (dotfile_path.empty()) {
dotfile_name_ = root_path.Append(kGnFile);
} else {
- dotfile_name_ = base::MakeAbsoluteFilePath(dot_file_path);
+ dotfile_name_ = base::MakeAbsoluteFilePath(dotfile_path);
if (dotfile_name_.empty()) {
Err(Location(), "Could not load dotfile.",
- "The file \"" + FilePathToUTF8(dot_file_path) +
+ "The file \"" + FilePathToUTF8(dotfile_path) +
"\" couldn't be loaded.")
.PrintToStdout();
return false;
}
+ // Only set dotfile_name if it was passed explicitly.
+ build_settings_.set_dotfile_name(dotfile_name_);
}
} else {
// In the default case, look for a dotfile and that also tells us where the
@@ -695,6 +733,12 @@ bool Setup::RunConfigFile() {
return false;
}
+ // Add a dependency on the build arguments file. If this changes, we want
+ // to re-generate the build. This causes the dotfile to make it into
+ // build.ninja.d.
+ g_scheduler->AddGenDependency(dotfile_name_);
+
+ // Also add a build dependency to the scope, which is used by `gn analyze`.
dotfile_scope_.AddBuildDependencyFile(SourceFile("//.gn"));
dotfile_root_->Execute(&dotfile_scope_, &err);
if (err.has_error()) {
diff --git a/gn/tools/gn/setup.h b/gn/tools/gn/setup.h
index 13d0da5f9dc..cf0136a21c7 100644
--- a/gn/tools/gn/setup.h
+++ b/gn/tools/gn/setup.h
@@ -29,7 +29,7 @@ class CommandLine;
extern const char kDotfile_Help[];
-// Helper class to setup the build settings and environment for the various
+// Helper class to set up the build settings and environment for the various
// commands to run.
class Setup {
public:
@@ -49,12 +49,24 @@ class Setup {
// generation should set this to true to create it, but querying commands
// should set it to false to prevent creating oddly-named directories in case
// the user omits the build directory argument (which is easy to do).
+ //
+ // cmdline is the gn invocation command, with flags like --root and --dotfile.
+ // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
+ // is used.
bool DoSetup(const std::string& build_dir, bool force_create);
+ bool DoSetup(const std::string& build_dir,
+ bool force_create,
+ const base::CommandLine& cmdline);
// Runs the load, returning true on success. On failure, prints the error
// and returns false. This includes both RunPreMessageLoop() and
// RunPostMessageLoop().
+ //
+ // cmdline is the gn invocation command, with flags like --root and --dotfile.
+ // If no explicit cmdline is passed, base::CommandLine::ForCurrentProcess()
+ // is used.
bool Run();
+ bool Run(const base::CommandLine& cmdline);
Scheduler& scheduler() { return scheduler_; }
@@ -72,6 +84,10 @@ class Setup {
// headers to be checked. Defaults to false.
void set_check_public_headers(bool s) { check_public_headers_ = s; }
+ // Before DoSetup, setting this will generate an empty args.gn if
+ // it does not exist and set up correct dependencies for it.
+ void set_gen_empty_args(bool ge) { gen_empty_args_ = ge; }
+
// Read from the .gn file, these are the targets to check. If the .gn file
// does not specify anything, this will be null. If the .gn file specifies
// the empty list, this will be non-null but empty.
@@ -93,7 +109,7 @@ class Setup {
// Performs the two sets of operations to run the generation before and after
// the message loop is run.
void RunPreMessageLoop();
- bool RunPostMessageLoop();
+ bool RunPostMessageLoop(const base::CommandLine& cmdline);
// Fills build arguments. Returns true on success.
bool FillArguments(const base::CommandLine& cmdline);
@@ -158,6 +174,9 @@ class Setup {
// line or build argument file. See setter above.
bool fill_arguments_;
+ // Generate an empty args.gn file if it does not exists.
+ bool gen_empty_args_;
+
// 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.
diff --git a/gn/tools/gn/setup_unittest.cc b/gn/tools/gn/setup_unittest.cc
new file mode 100644
index 00000000000..20c8effa0a4
--- /dev/null
+++ b/gn/tools/gn/setup_unittest.cc
@@ -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.
+
+#include "tools/gn/setup.h"
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/switches.h"
+#include "tools/gn/test_with_scheduler.h"
+
+using SetupTest = TestWithScheduler;
+
+static void WriteFile(const base::FilePath& file, const std::string& data) {
+ CHECK_EQ(static_cast<int>(data.size()), // Way smaller than INT_MAX.
+ base::WriteFile(file, data.data(), data.size()));
+}
+
+TEST_F(SetupTest, DotGNFileIsGenDep) {
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+ // pass it as --root.
+ base::ScopedTempDir in_temp_dir;
+ ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+ base::FilePath in_path = in_temp_dir.GetPath();
+ base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+ WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n");
+ WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+ cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+
+ // Create another temp dir for writing the generated files to.
+ base::ScopedTempDir build_temp_dir;
+ ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+ // Run setup and check that the .gn file is in the scheduler's gen deps.
+ Setup setup;
+ EXPECT_TRUE(
+ setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline));
+ std::vector<base::FilePath> gen_deps = g_scheduler->GetGenDependencies();
+ ASSERT_EQ(1u, gen_deps.size());
+ EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
+}
diff --git a/gn/tools/gn/source_dir.h b/gn/tools/gn/source_dir.h
index 5c1621b542f..bfc260d514f 100644
--- a/gn/tools/gn/source_dir.h
+++ b/gn/tools/gn/source_dir.h
@@ -119,6 +119,16 @@ class SourceDir {
return base::StringPiece(&value_[1], value_.size() - 1);
}
+ // Returns a path that does not end with a slash.
+ //
+ // This function simply returns the reference to the value if the path is a
+ // root, e.g. "/" or "//".
+ base::StringPiece SourceWithNoTrailingSlash() const {
+ if (value_.size() > 2)
+ return base::StringPiece(&value_[0], value_.size() - 1);
+ return base::StringPiece(value_);
+ }
+
void SwapValue(std::string* v);
bool operator==(const SourceDir& other) const {
diff --git a/gn/tools/gn/source_dir_unittest.cc b/gn/tools/gn/source_dir_unittest.cc
index adf865bee90..d5ada23f0c4 100644
--- a/gn/tools/gn/source_dir_unittest.cc
+++ b/gn/tools/gn/source_dir_unittest.cc
@@ -185,3 +185,24 @@ TEST(SourceDir, ResolveRelativeDir) {
EXPECT_FALSE(err.has_error());
#endif
}
+
+TEST(SourceDir, SourceWithNoTrailingSlash) {
+ Err err;
+ SourceDir base("//base/");
+ SourceDir base_no_slash("//base/");
+ EXPECT_EQ(base.SourceWithNoTrailingSlash(), "//base");
+ EXPECT_EQ(base_no_slash.SourceWithNoTrailingSlash(), "//base");
+
+ SourceDir relative_root("//");
+ EXPECT_EQ(relative_root.SourceWithNoTrailingSlash(), "//");
+
+#if defined(OS_WIN)
+ SourceDir root("C:/");
+ SourceDir root_no_slash("C:");
+ EXPECT_EQ(root.SourceWithNoTrailingSlash(), "C:");
+ EXPECT_EQ(root_no_slash.SourceWithNoTrailingSlash(), "C:");
+#else
+ SourceDir root("/");
+ EXPECT_EQ(root.SourceWithNoTrailingSlash(), "/");
+#endif
+}
diff --git a/gn/tools/gn/standard_out.cc b/gn/tools/gn/standard_out.cc
index 6dd6a6a888c..3056418008d 100644
--- a/gn/tools/gn/standard_out.cc
+++ b/gn/tools/gn/standard_out.cc
@@ -35,6 +35,9 @@ bool is_console = false;
bool is_markdown = false;
+// True while output is going into a markdown ```...``` code block.
+bool in_body = false;
+
void EnsureInitialized() {
if (initialized)
return;
@@ -75,8 +78,8 @@ void WriteToStdOut(const std::string& output) {
#endif // !defined(OS_WIN)
void OutputMarkdownDec(TextDecoration dec) {
- // The markdown rendering turns "dim" text to italics and any
- // other colored text to bold.
+// The markdown rendering turns "dim" text to italics and any
+// other colored text to bold.
#if defined(OS_WIN)
DWORD written = 0;
@@ -96,7 +99,9 @@ void OutputMarkdownDec(TextDecoration dec) {
#if defined(OS_WIN)
-void OutputString(const std::string& output, TextDecoration dec) {
+void OutputString(const std::string& output,
+ TextDecoration dec,
+ HtmlEscaping escaping) {
EnsureInitialized();
DWORD written = 0;
@@ -135,6 +140,12 @@ void OutputString(const std::string& output, TextDecoration dec) {
// at least escape the instances where this shows up in a heading.
base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
}
+ if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
+ // Markdown auto-escapes < and > in code sections (and converts &lt; to
+ // &amp;tl; there), but not elsewhere.
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
+ }
::WriteFile(hstdout, tmpstr.c_str(), static_cast<DWORD>(tmpstr.size()),
&written, nullptr);
@@ -147,7 +158,9 @@ void OutputString(const std::string& output, TextDecoration dec) {
#else
-void OutputString(const std::string& output, TextDecoration dec) {
+void OutputString(const std::string& output,
+ TextDecoration dec,
+ HtmlEscaping escaping) {
EnsureInitialized();
if (is_markdown) {
OutputMarkdownDec(dec);
@@ -168,7 +181,7 @@ void OutputString(const std::string& output, TextDecoration dec) {
WriteToStdOut("\e[34m\e[1m");
break;
case DECORATION_YELLOW:
- WriteToStdOut("\e[33m\e[1m");
+ WriteToStdOut("\e[33m");
break;
}
}
@@ -181,6 +194,12 @@ void OutputString(const std::string& output, TextDecoration dec) {
// at least escape the instances where this shows up in a heading.
base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "--", "\\--");
}
+ if (is_markdown && !in_body && escaping == DEFAULT_ESCAPING) {
+ // Markdown auto-escapes < and > in code sections (and converts &lt; to
+ // &amp;tl; there), but not elsewhere.
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, "<", "&lt;");
+ base::ReplaceSubstringsAfterOffset(&tmpstr, 0, ">", "&gt;");
+ }
WriteToStdOut(tmpstr.data());
if (is_markdown) {
@@ -207,26 +226,24 @@ void PrintSectionHelp(const std::string& line,
}
}
-void PrintShortHelp(const std::string& line) {
+void PrintShortHelp(const std::string& line, const std::string& link_tag) {
EnsureInitialized();
+ if (is_markdown) {
+ if (link_tag.empty())
+ OutputString(" * " + line + "\n");
+ else
+ OutputString(" * [" + line + "](#" + link_tag + ")\n");
+ return;
+ }
+
size_t colon_offset = line.find(':');
size_t first_normal = 0;
if (colon_offset != std::string::npos) {
- 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");
+ OutputString(" " + line.substr(0, colon_offset), DECORATION_YELLOW);
+ first_normal = colon_offset;
}
- 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] == '[') {
@@ -248,7 +265,7 @@ void PrintLongHelp(const std::string& text, const std::string& tag) {
EnsureInitialized();
bool first_header = true;
- bool in_body = false;
+ 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)) {
@@ -270,16 +287,9 @@ void PrintLongHelp(const std::string& text, const std::string& tag) {
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);
+ if (first_header && !tag.empty()) {
+ OutputString("### <a name=\"" + tag + "\"></a>", DECORATION_NONE,
+ NO_ESCAPING);
first_header = false;
} else {
OutputString("#### ", DECORATION_NONE);
diff --git a/gn/tools/gn/standard_out.h b/gn/tools/gn/standard_out.h
index f2bb733b6bd..5c0fcceabb1 100644
--- a/gn/tools/gn/standard_out.h
+++ b/gn/tools/gn/standard_out.h
@@ -16,8 +16,17 @@ enum TextDecoration {
DECORATION_YELLOW
};
+enum HtmlEscaping {
+ NO_ESCAPING,
+
+ // Convert < and > to &lt; and &gt; when writing markdown output in non-code
+ // sections.
+ DEFAULT_ESCAPING,
+};
+
void OutputString(const std::string& output,
- TextDecoration dec = DECORATION_NONE);
+ TextDecoration dec = DECORATION_NONE,
+ HtmlEscaping = DEFAULT_ESCAPING);
// If printing markdown, this generates table-of-contents entries with
// links to the actual help; otherwise, prints a one-line description.
@@ -29,13 +38,24 @@ void PrintSectionHelp(const std::string& line,
// 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 link_tag is set, it will be used for markdown output links. This is
+// used when generating the markdown for all help topics. If empty, no link tag
+// will be emitted. In non-markdown mode, this parameter will be ignored.
+//
// The line is indented 2 spaces.
-void PrintShortHelp(const std::string& line);
+void PrintShortHelp(const std::string& line,
+ const std::string& link_tag = std::string());
+// Prints a longer help section.
+//
// 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.
+//
+// The tag will be used as a link target for the first header. This is used
+// when generating the markdown for all help topics. If empty, no link tag will
+// be emitted. Used only in markdown mode.
void PrintLongHelp(const std::string& text, const std::string& tag = "");
#endif // TOOLS_GN_STANDARD_OUT_H_
diff --git a/gn/tools/gn/substitution_pattern.h b/gn/tools/gn/substitution_pattern.h
index 2f01e6bf277..850d736280c 100644
--- a/gn/tools/gn/substitution_pattern.h
+++ b/gn/tools/gn/substitution_pattern.h
@@ -67,6 +67,8 @@ class SubstitutionPattern {
const std::vector<Subrange>& ranges() const { return ranges_; }
bool empty() const { return ranges_.empty(); }
+ const ParseNode* origin() const { return origin_; }
+
private:
std::vector<Subrange> ranges_;
const ParseNode* origin_;
diff --git a/gn/tools/gn/substitution_type.cc b/gn/tools/gn/substitution_type.cc
index 8c0fbb4c30c..d47a8d0fdc6 100644
--- a/gn/tools/gn/substitution_type.cc
+++ b/gn/tools/gn/substitution_type.cc
@@ -54,7 +54,6 @@ const char* kSubstitutionNames[SUBSTITUTION_NUM_TYPES] = {
"{{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,
@@ -109,7 +108,6 @@ const char* kSubstitutionNinjaNames[SUBSTITUTION_NUM_TYPES] = {
"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
@@ -143,8 +141,7 @@ 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;
+ type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR;
}
bool IsValidBundleDataSubstitution(SubstitutionType type) {
@@ -155,8 +152,7 @@ bool IsValidBundleDataSubstitution(SubstitutionType type) {
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;
+ type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR;
}
bool IsValidSourceSubstitution(SubstitutionType type) {
diff --git a/gn/tools/gn/substitution_type.h b/gn/tools/gn/substitution_type.h
index 7363e187246..9e2bdb728b9 100644
--- a/gn/tools/gn/substitution_type.h
+++ b/gn/tools/gn/substitution_type.h
@@ -69,7 +69,6 @@ enum SubstitutionType {
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}}
diff --git a/gn/tools/gn/substitution_writer.h b/gn/tools/gn/substitution_writer.h
index d5cb1c31bd1..530f0647695 100644
--- a/gn/tools/gn/substitution_writer.h
+++ b/gn/tools/gn/substitution_writer.h
@@ -6,6 +6,7 @@
#define TOOLS_GN_SUBSTITUTION_WRITER_H_
#include <iosfwd>
+#include <string>
#include <vector>
#include "tools/gn/substitution_type.h"
diff --git a/gn/tools/gn/switches.cc b/gn/tools/gn/switches.cc
index 3d77642016a..f6a47043845 100644
--- a/gn/tools/gn/switches.cc
+++ b/gn/tools/gn/switches.cc
@@ -63,9 +63,6 @@ const char kDotfile_Help[] =
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";
@@ -114,6 +111,39 @@ const char kScriptExecutable_Help[] =
interpreter.
)";
+const char kMetaDataKeys[] = "data";
+const char kMetaDataKeys_HelpShort[] =
+ "--data: list of data keys to concatenate when collecting metadata.";
+const char kMetaDataKeys_Help[] =
+ R"(--data: list of data keys to concatenate when collecting metadata.
+
+ Data keys identify which variables in the given targets' `metadata`
+ scopes should be collected. At least one data key must be specified.
+)";
+
+const char kMetaWalkKeys[] = "walk";
+const char kMetaWalkKeys_HelpShort[] =
+ "--walk: list of walk keys to traverse when collecting metadata.";
+const char kMetaWalkKeys_Help[] =
+ R"(--walk: list of walk keys to traverse when collecting metadata.
+
+ Walk keys identify which variables in the given targets' `metadata`
+ scopes contain the list of dependencies to walk next. Absence of any
+ walk keys indicates that all deps and data_deps should be walked.
+)";
+
+const char kMetaRebaseFiles[] = "rebase-files";
+const char kMetaRebaseFiles_HelpShort[] =
+ "--rebase-files (boolean): whether to rebase the paths of the collected "
+ "metadata.";
+const char kMetaRebaseFiles_Help[] =
+ R"(--rebase-files: whether to rebase the paths of the collected metadata.
+
+ This flag indicates whether or not to rebase the collected results onto their
+ declaring source directory path. Note that this requires the data key(s) to
+ contain only lists of strings, which will be interpreted as file names.
+)";
+
const char kQuiet[] = "q";
const char kQuiet_HelpShort[] =
"-q: Quiet mode. Don't print output on success.";
diff --git a/gn/tools/gn/switches.h b/gn/tools/gn/switches.h
index 1dc98285ff3..d1abb1ff6d0 100644
--- a/gn/tools/gn/switches.h
+++ b/gn/tools/gn/switches.h
@@ -47,6 +47,18 @@ extern const char kMarkdown[];
extern const char kMarkdown_HelpShort[];
extern const char kMarkdown_Help[];
+extern const char kMetaDataKeys[];
+extern const char kMetaDataKeys_HelpShort[];
+extern const char kMetaDataKeys_Help[];
+
+extern const char kMetaWalkKeys[];
+extern const char kMetaWalkKeys_HelpShort[];
+extern const char kMetaWalkKeys_Help[];
+
+extern const char kMetaRebaseFiles[];
+extern const char kMetaRebaseFiles_HelpShort[];
+extern const char kMetaRebaseFiles_Help[];
+
extern const char kNoColor[];
extern const char kNoColor_HelpShort[];
extern const char kNoColor_Help[];
diff --git a/gn/tools/gn/target.cc b/gn/tools/gn/target.cc
index fbbfd50a111..94fb994e277 100644
--- a/gn/tools/gn/target.cc
+++ b/gn/tools/gn/target.cc
@@ -318,6 +318,8 @@ const char* Target::GetStringForOutputType(OutputType type) {
return functions::kBundleData;
case CREATE_BUNDLE:
return functions::kCreateBundle;
+ case GENERATED_FILE:
+ return functions::kGeneratedFile;
default:
return "";
}
@@ -379,7 +381,8 @@ bool Target::OnResolved(Err* err) {
if (!ResolvePrecompiledHeaders(err))
return false;
- FillOutputFiles();
+ if (!FillOutputFiles(err))
+ return false;
if (!CheckVisibility(err))
return false;
@@ -392,6 +395,12 @@ bool Target::OnResolved(Err* err) {
if (!write_runtime_deps_output_.value().empty())
g_scheduler->AddWriteRuntimeDepsTarget(this);
+ if (output_type_ == GENERATED_FILE) {
+ DCHECK(!computed_outputs_.empty());
+ g_scheduler->AddGeneratedFile(
+ computed_outputs_[0].AsSourceFile(settings()->build_settings()));
+ }
+
return true;
}
@@ -621,7 +630,7 @@ void Target::PullRecursiveBundleData() {
bundle_data_.OnTargetResolved(this);
}
-void Target::FillOutputFiles() {
+bool Target::FillOutputFiles(Err* err) {
const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
bool check_tool_outputs = false;
switch (output_type_) {
@@ -631,7 +640,8 @@ void Target::FillOutputFiles() {
case SOURCE_SET:
case COPY_FILES:
case ACTION:
- case ACTION_FOREACH: {
+ case ACTION_FOREACH:
+ case GENERATED_FILE: {
// These don't get linked to and use stamps which should be the first
// entry in the outputs. These stamps are named
// "<target_out_dir>/<targetname>.stamp".
@@ -703,8 +713,10 @@ void Target::FillOutputFiles() {
}
// Count anything generated from bundle_data dependencies.
- if (output_type_ == CREATE_BUNDLE)
- bundle_data_.GetOutputFiles(settings(), &computed_outputs_);
+ if (output_type_ == CREATE_BUNDLE) {
+ if (!bundle_data_.GetOutputFiles(settings(), this, &computed_outputs_, err))
+ return false;
+ }
// Count all outputs from this tool as something generated by this target.
if (check_tool_outputs) {
@@ -725,6 +737,8 @@ void Target::FillOutputFiles() {
action_values_.GetOutputsAsSourceFiles(this, &outputs_as_sources);
for (const SourceFile& out : outputs_as_sources)
computed_outputs_.push_back(OutputFile(settings()->build_settings(), out));
+
+ return true;
}
bool Target::ResolvePrecompiledHeaders(Err* err) {
@@ -863,7 +877,9 @@ void Target::CheckSourceGenerated(const SourceFile& source) const {
&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_data_deps =
+ g_scheduler->IsFileGeneratedByWriteRuntimeDeps(out_file) ||
+ g_scheduler->IsFileGeneratedByTarget(source);
// Check object files (much slower and very rare) only if the "normal"
// output check failed.
consider_object_files = !check_data_deps;
@@ -873,3 +889,87 @@ void Target::CheckSourceGenerated(const SourceFile& source) const {
g_scheduler->AddUnknownGeneratedInput(this, source);
}
}
+
+bool Target::GetMetadata(const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ bool deps_only,
+ std::vector<Value>* result,
+ std::set<const Target*>* targets_walked,
+ Err* err) const {
+ std::vector<Value> next_walk_keys;
+ std::vector<Value> current_result;
+ // If deps_only, this is the top-level target and thus we don't want to
+ // collect its metadata, only that of its deps and data_deps.
+ if (deps_only) {
+ // Empty string will be converted below to mean all deps and data_deps.
+ // Origin is null because this isn't declared anywhere, and should never
+ // trigger any errors.
+ next_walk_keys.push_back(Value(nullptr, ""));
+ } else {
+ // Otherwise, we walk this target and collect the appropriate data.
+ if (!metadata_.WalkStep(settings()->build_settings(), keys_to_extract,
+ keys_to_walk, rebase_dir, &next_walk_keys,
+ &current_result, err))
+ return false;
+ }
+
+ // Gather walk keys and find the appropriate target. Targets identified in
+ // the walk key set must be deps or data_deps of the declaring target.
+ const DepsIteratorRange& all_deps = GetDeps(Target::DEPS_ALL);
+ for (const auto& next : next_walk_keys) {
+ DCHECK(next.type() == Value::STRING);
+
+ // If we hit an empty string in this list, add all deps and data_deps. The
+ // ordering in the resulting list of values as a result will be the data
+ // from each explicitly listed dep prior to this, followed by all data in
+ // walk order of the remaining deps.
+ if (next.string_value().empty()) {
+ for (const auto& dep : all_deps) {
+ // If we haven't walked this dep yet, go down into it.
+ auto pair = targets_walked->insert(dep.ptr);
+ if (pair.second) {
+ if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
+ false, result, targets_walked, err))
+ return false;
+ }
+ }
+
+ // Any other walk keys are superfluous, as they can only be a subset of
+ // all deps.
+ break;
+ }
+
+ // Otherwise, look through the target's deps for the specified one.
+ bool found_next = false;
+ for (const auto& dep : all_deps) {
+ // Match against the label with the toolchain.
+ if (dep.label.GetUserVisibleName(true) == next.string_value()) {
+ // If we haven't walked this dep yet, go down into it.
+ auto pair = targets_walked->insert(dep.ptr);
+ if (pair.second) {
+ if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
+ false, result, targets_walked, err))
+ return false;
+ }
+ // We found it, so we can exit this search now.
+ found_next = true;
+ break;
+ }
+ }
+ // If we didn't find the specified dep in the target, that's an error.
+ // Propagate it back to the user.
+ if (!found_next) {
+ *err = Err(next.origin(),
+ std::string("I was expecting ") + next.string_value() +
+ std::string(" to be a dependency of ") +
+ label().GetUserVisibleName(true) +
+ ". Make sure it's included in the deps or data_deps, and "
+ "that you've specified the appropriate toolchain.");
+ return false;
+ }
+ }
+ result->insert(result->end(), std::make_move_iterator(current_result.begin()),
+ std::make_move_iterator(current_result.end()));
+ return true;
+}
diff --git a/gn/tools/gn/target.h b/gn/tools/gn/target.h
index 8754b6184d7..b73ae575d8e 100644
--- a/gn/tools/gn/target.h
+++ b/gn/tools/gn/target.h
@@ -20,6 +20,7 @@
#include "tools/gn/label_pattern.h"
#include "tools/gn/label_ptr.h"
#include "tools/gn/lib_file.h"
+#include "tools/gn/metadata.h"
#include "tools/gn/ordered_set.h"
#include "tools/gn/output_file.h"
#include "tools/gn/source_file.h"
@@ -45,6 +46,7 @@ class Target : public Item {
ACTION_FOREACH,
BUNDLE_DATA,
CREATE_BUNDLE,
+ GENERATED_FILE,
};
enum DepsIterationType {
@@ -144,6 +146,36 @@ class Target : public Item {
complete_static_lib_ = complete;
}
+ // Metadata. Target takes ownership of the resulting scope.
+ const Metadata& metadata() const { return metadata_; }
+ Metadata& metadata() { return metadata_; }
+
+ // Get metadata from this target and its dependencies. This is intended to
+ // be called after the target is resolved.
+ bool GetMetadata(const std::vector<std::string>& keys_to_extract,
+ const std::vector<std::string>& keys_to_walk,
+ const SourceDir& rebase_dir,
+ bool deps_only,
+ std::vector<Value>* result,
+ std::set<const Target*>* targets_walked,
+ Err* err) const;
+
+ // GeneratedFile-related methods.
+ bool GenerateFile(Err* err) const;
+
+ const Value& contents() const { return contents_; }
+ void set_contents(const Value& value) { contents_ = value; }
+ const Value& output_conversion() const { return output_conversion_; }
+ void set_output_conversion(const Value& value) { output_conversion_ = value; }
+
+ // Metadata collection methods for GeneratedFile targets.
+ const SourceDir& rebase() const { return rebase_; }
+ void set_rebase(const SourceDir& value) { rebase_ = value; }
+ const std::vector<std::string>& data_keys() const { return data_keys_; }
+ std::vector<std::string>& data_keys() { return data_keys_; }
+ const std::vector<std::string>& walk_keys() const { return walk_keys_; }
+ std::vector<std::string>& walk_keys() { return walk_keys_; }
+
bool testonly() const { return testonly_; }
void set_testonly(bool value) { testonly_ = value; }
@@ -170,7 +202,7 @@ class Target : public Item {
bool hard_dep() const {
return output_type_ == ACTION || output_type_ == ACTION_FOREACH ||
output_type_ == COPY_FILES || output_type_ == CREATE_BUNDLE ||
- output_type_ == BUNDLE_DATA;
+ output_type_ == BUNDLE_DATA || output_type_ == GENERATED_FILE;
}
// Returns the iterator range which can be used in range-based for loops
@@ -314,7 +346,7 @@ class Target : public Item {
void PullRecursiveBundleData();
// Fills the link and dependency output files when a target is resolved.
- void FillOutputFiles();
+ bool FillOutputFiles(Err* err);
// Checks precompiled headers from configs and makes sure the resulting
// values are in config_values_.
@@ -388,6 +420,17 @@ class Target : public Item {
OutputFile dependency_output_file_;
std::vector<OutputFile> runtime_outputs_;
+ Metadata metadata_;
+
+ // GeneratedFile values.
+ Value output_conversion_;
+ Value contents_; // Value::NONE if metadata collection should occur.
+
+ // GeneratedFile as metadata collection values.
+ SourceDir rebase_;
+ std::vector<std::string> data_keys_;
+ std::vector<std::string> walk_keys_;
+
DISALLOW_COPY_AND_ASSIGN(Target);
};
diff --git a/gn/tools/gn/target_generator.cc b/gn/tools/gn/target_generator.cc
index 4bc693c0c4f..9ad8969d9a3 100644
--- a/gn/tools/gn/target_generator.cc
+++ b/gn/tools/gn/target_generator.cc
@@ -19,7 +19,9 @@
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/functions.h"
+#include "tools/gn/generated_file_target_generator.h"
#include "tools/gn/group_target_generator.h"
+#include "tools/gn/metadata.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/scope.h"
@@ -50,6 +52,9 @@ void TargetGenerator::Run() {
if (!FillDependencies())
return;
+ if (!FillMetadata())
+ return;
+
if (!FillTestonly())
return;
@@ -135,6 +140,10 @@ void TargetGenerator::GenerateTarget(Scope* scope,
BinaryTargetGenerator generator(target.get(), scope, function_call,
Target::STATIC_LIBRARY, err);
generator.Run();
+ } else if (output_type == functions::kGeneratedFile) {
+ GeneratedFileTargetGenerator generator(target.get(), scope, function_call,
+ Target::GENERATED_FILE, err);
+ generator.Run();
} else {
*err = Err(function_call, "Not a known target type",
"I am very confused by the target type \"" + output_type + "\"");
@@ -254,6 +263,36 @@ bool TargetGenerator::FillDependencies() {
return true;
}
+bool TargetGenerator::FillMetadata() {
+ // Need to get a mutable value to mark all values in the scope as used. This
+ // cannot be done on a const Scope.
+ Value* value = scope_->GetMutableValue(variables::kMetadata,
+ Scope::SEARCH_CURRENT, true);
+
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::SCOPE, err_))
+ return false;
+
+ Scope* scope_value = value->scope_value();
+
+ scope_value->GetCurrentScopeValues(&target_->metadata().contents());
+ scope_value->MarkAllUsed();
+
+ // Metadata values should always hold lists of Values, such that they can be
+ // collected and concatenated. Any additional specific type verification is
+ // done at walk time.
+ for (const auto& iter : target_->metadata().contents()) {
+ if (!iter.second.VerifyTypeIs(Value::LIST, err_))
+ return false;
+ }
+
+ target_->metadata().set_source_dir(scope_->GetSourceDir());
+ target_->metadata().set_origin(value->origin());
+ return true;
+}
+
bool TargetGenerator::FillTestonly() {
const Value* value = scope_->GetValue(variables::kTestonly, true);
if (value) {
diff --git a/gn/tools/gn/target_generator.h b/gn/tools/gn/target_generator.h
index 549f2cd14c7..627505da2da 100644
--- a/gn/tools/gn/target_generator.h
+++ b/gn/tools/gn/target_generator.h
@@ -68,6 +68,7 @@ class TargetGenerator {
bool FillDependentConfigs(); // Includes all types of dependent configs.
bool FillData();
bool FillDependencies(); // Includes data dependencies.
+ bool FillMetadata();
bool FillTestonly();
bool FillAssertNoDeps();
bool FillWriteRuntimeDeps();
diff --git a/gn/tools/gn/target_unittest.cc b/gn/tools/gn/target_unittest.cc
index 62dad5263a0..673422e92c8 100644
--- a/gn/tools/gn/target_unittest.cc
+++ b/gn/tools/gn/target_unittest.cc
@@ -1044,12 +1044,18 @@ TEST_F(TargetTest, PullRecursiveBundleData) {
e.public_deps().push_back(LabelTargetPair(&f));
e.public_deps().push_back(LabelTargetPair(&b));
+ a.bundle_data().root_dir() = SourceDir("//out/foo_a.bundle");
+ a.bundle_data().resources_dir() = SourceDir("//out/foo_a.bundle/Resources");
+
b.sources().push_back(SourceFile("//foo/b1.txt"));
b.sources().push_back(SourceFile("//foo/b2.txt"));
b.action_values().outputs() = SubstitutionList::MakeForTest(
"{{bundle_resources_dir}}/{{source_file_part}}");
ASSERT_TRUE(b.OnResolved(&err));
+ c.bundle_data().root_dir() = SourceDir("//out/foo_c.bundle");
+ c.bundle_data().resources_dir() = SourceDir("//out/foo_c.bundle/Resources");
+
d.sources().push_back(SourceFile("//foo/d.txt"));
d.action_values().outputs() = SubstitutionList::MakeForTest(
"{{bundle_resources_dir}}/{{source_file_part}}");
@@ -1092,3 +1098,211 @@ TEST_F(TargetTest, PullRecursiveBundleData) {
ASSERT_TRUE(e.bundle_data().assets_catalog_sources().empty());
ASSERT_EQ(e.bundle_data().bundle_deps().size(), 2u);
}
+
+TEST(TargetTest, CollectMetadataNoRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("b", b_expected));
+
+ one.metadata().set_source_dir(SourceDir("/usr/home/files/"));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ EXPECT_EQ(result, expected);
+}
+
+TEST(TargetTest, CollectMetadataWithRecurse) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value b_expected(nullptr, Value::LIST);
+ b_expected.list_value().push_back(Value(nullptr, true));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("b", b_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_2_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+ data_keys.push_back("b");
+
+ std::vector<std::string> walk_keys;
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_FALSE(err.has_error());
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ expected.push_back(Value(nullptr, true));
+ EXPECT_EQ(result, expected);
+}
+
+TEST(TargetTest, CollectMetadataWithBarrier) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(
+ Value(nullptr, "//foo:two(//toolchain:default)"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("walk", walk_expected));
+
+ TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
+ Value a_2_expected(nullptr, Value::LIST);
+ a_2_expected.list_value().push_back(Value(nullptr, "bar"));
+ two.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_2_expected));
+
+ TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
+ Value a_3_expected(nullptr, Value::LIST);
+ a_3_expected.list_value().push_back(Value(nullptr, "baz"));
+ three.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_3_expected));
+
+ one.public_deps().push_back(LabelTargetPair(&two));
+ one.public_deps().push_back(LabelTargetPair(&three));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_FALSE(err.has_error()) << err.message();
+
+ std::vector<Value> expected;
+ expected.push_back(Value(nullptr, "bar"));
+ expected.push_back(Value(nullptr, "foo"));
+ EXPECT_EQ(result, expected) << result.size();
+}
+
+TEST(TargetTest, CollectMetadataWithError) {
+ TestWithScope setup;
+
+ TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
+ Value a_expected(nullptr, Value::LIST);
+ a_expected.list_value().push_back(Value(nullptr, "foo"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("a", a_expected));
+
+ Value walk_expected(nullptr, Value::LIST);
+ walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
+ one.metadata().contents().insert(
+ std::pair<base::StringPiece, Value>("walk", walk_expected));
+
+ std::vector<std::string> data_keys;
+ data_keys.push_back("a");
+
+ std::vector<std::string> walk_keys;
+ walk_keys.push_back("walk");
+
+ Err err;
+ std::vector<Value> result;
+ std::set<const Target*> targets;
+ one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
+ &err);
+ EXPECT_TRUE(err.has_error());
+ EXPECT_EQ(err.message(),
+ "I was expecting //foo:missing to be a dependency of "
+ "//foo:one(//toolchain:default). "
+ "Make sure it's included in the deps or data_deps, and that you've "
+ "specified the appropriate toolchain.")
+ << err.message();
+}
+
+TEST_F(TargetTest, WriteMetadataCollection) {
+ TestWithScope setup;
+ Err err;
+
+ SourceFile source_file("//out/Debug/metadata.json");
+ OutputFile output_file(setup.build_settings(), source_file);
+
+ TestTarget generator(setup, "//foo:write", Target::GENERATED_FILE);
+ generator.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/metadata.json");
+ EXPECT_TRUE(generator.OnResolved(&err));
+
+ TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
+ middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(middle_data_dep.OnResolved(&err));
+
+ // This target has a generated metadata input and no dependency makes it.
+ TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
+ dep_missing.sources().push_back(source_file);
+ EXPECT_TRUE(dep_missing.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've directly dependended on it.
+ TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
+ dep_present.sources().push_back(source_file);
+ dep_present.private_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(dep_present.OnResolved(&err));
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+
+ // This target has a generated file and we've indirectly dependended on it
+ // via data_deps.
+ TestTarget dep_indirect(setup, "//foo:indirect_dep", Target::EXECUTABLE);
+ dep_indirect.sources().push_back(source_file);
+ dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
+ EXPECT_TRUE(dep_indirect.OnResolved(&err));
+ AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
+ scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();
+
+ // This target has a generated file and we've directly dependended on it
+ // via data_deps.
+ TestTarget data_dep_present(setup, "//foo:with_data_dep", Target::EXECUTABLE);
+ data_dep_present.sources().push_back(source_file);
+ data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
+ EXPECT_TRUE(data_dep_present.OnResolved(&err));
+ EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
+}
diff --git a/gn/tools/gn/toolchain.cc b/gn/tools/gn/toolchain.cc
index e34acb5228c..d9aff43e060 100644
--- a/gn/tools/gn/toolchain.cc
+++ b/gn/tools/gn/toolchain.cc
@@ -180,6 +180,7 @@ 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.
+ // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
switch (target->output_type()) {
case Target::GROUP:
return TYPE_STAMP;
@@ -198,6 +199,7 @@ Toolchain::ToolType Toolchain::GetToolTypeForTargetFinalOutput(
case Target::BUNDLE_DATA:
case Target::CREATE_BUNDLE:
case Target::COPY_FILES:
+ case Target::GENERATED_FILE:
return TYPE_STAMP;
default:
NOTREACHED();
diff --git a/gn/tools/gn/value.cc b/gn/tools/gn/value.cc
index 04db7eebee4..a54db03030a 100644
--- a/gn/tools/gn/value.cc
+++ b/gn/tools/gn/value.cc
@@ -11,73 +11,135 @@
#include "base/strings/string_util.h"
#include "tools/gn/scope.h"
-Value::Value()
- : type_(NONE), boolean_value_(false), int_value_(0), origin_(nullptr) {}
+// NOTE: Cannot use = default here due to the use of a union member.
+Value::Value() {}
-Value::Value(const ParseNode* origin, Type t)
- : type_(t), boolean_value_(false), int_value_(0), origin_(origin) {}
+Value::Value(const ParseNode* origin, Type t) : type_(t), origin_(origin) {
+ switch (type_) {
+ case NONE:
+ break;
+ case BOOLEAN:
+ boolean_value_ = false;
+ break;
+ case INTEGER:
+ int_value_ = 0;
+ break;
+ case STRING:
+ new (&string_value_) std::string();
+ break;
+ case LIST:
+ new (&list_value_) std::vector<Value>();
+ break;
+ case SCOPE:
+ new (&scope_value_) std::unique_ptr<Scope>();
+ break;
+ }
+}
Value::Value(const ParseNode* origin, bool bool_val)
: type_(BOOLEAN),
boolean_value_(bool_val),
- 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(const Value& other) : type_(other.type_), origin_(other.origin_) {
+ switch (type_) {
+ case NONE:
+ break;
+ case BOOLEAN:
+ boolean_value_ = other.boolean_value_;
+ break;
+ case INTEGER:
+ int_value_ = other.int_value_;
+ break;
+ case STRING:
+ new (&string_value_) std::string(other.string_value_);
+ break;
+ case LIST:
+ new (&list_value_) std::vector<Value>(other.list_value_);
+ break;
+ case SCOPE:
+ new (&scope_value_) std::unique_ptr<Scope>(
+ other.scope_value_.get() ? other.scope_value_->MakeClosure()
+ : nullptr);
+ break;
+ }
}
-Value::Value(Value&& other) noexcept = default;
-
-Value::~Value() = default;
+Value::Value(Value&& other) noexcept
+ : type_(other.type_), origin_(other.origin_) {
+ switch (type_) {
+ case NONE:
+ break;
+ case BOOLEAN:
+ boolean_value_ = other.boolean_value_;
+ break;
+ case INTEGER:
+ int_value_ = other.int_value_;
+ break;
+ case STRING:
+ new (&string_value_) std::string(std::move(other.string_value_));
+ break;
+ case LIST:
+ new (&list_value_) std::vector<Value>(std::move(other.list_value_));
+ break;
+ case SCOPE:
+ new (&scope_value_) std::unique_ptr<Scope>(std::move(other.scope_value_));
+ break;
+ }
+}
Value& Value::operator=(const Value& other) {
- 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_;
+ if (this != &other) {
+ this->~Value();
+ new (this) Value(other);
+ }
+ return *this;
+}
+
+Value& Value::operator=(Value&& other) noexcept {
+ if (this != &other) {
+ this->~Value();
+ new (this) Value(std::move(other));
+ }
return *this;
}
+Value::~Value() {
+ using namespace std;
+ switch (type_) {
+ case STRING:
+ string_value_.~string();
+ break;
+ case LIST:
+ list_value_.~vector<Value>();
+ break;
+ case SCOPE:
+ scope_value_.~unique_ptr<Scope>();
+ break;
+ default:;
+ }
+}
+
// static
const char* Value::DescribeType(Type t) {
switch (t) {
@@ -196,11 +258,11 @@ bool Value::operator==(const Value& other) const {
}
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 scope_value()->CheckCurrentScopeValuesEqual(other.scope_value());
+ case Value::NONE:
return false;
default:
+ NOTREACHED();
return false;
}
}
diff --git a/gn/tools/gn/value.h b/gn/tools/gn/value.h
index 13beb6fdcf2..38349107e38 100644
--- a/gn/tools/gn/value.h
+++ b/gn/tools/gn/value.h
@@ -47,7 +47,7 @@ class Value {
~Value();
Value& operator=(const Value& other);
- Value& operator=(Value&& other) = default;
+ Value& operator=(Value&& other) noexcept;
Type type() const { return type_; }
@@ -113,23 +113,25 @@ class Value {
// false and sets the error.
bool VerifyTypeIs(Type t, Err* err) const;
- // Compares values. Only the "value" is compared, not the origin.
+ // Compares values. Only the "value" is compared, not the origin. Scope
+ // values check only the contents of the current scope, and do not go to
+ // parent scopes.
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
private:
- // 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_;
+ void Deallocate();
+
+ Type type_ = NONE;
+ const ParseNode* origin_ = nullptr;
+
+ union {
+ bool boolean_value_;
+ int64_t int_value_;
+ std::string string_value_;
+ std::vector<Value> list_value_;
+ std::unique_ptr<Scope> scope_value_;
+ };
};
#endif // TOOLS_GN_VALUE_H_
diff --git a/gn/tools/gn/value_unittest.cc b/gn/tools/gn/value_unittest.cc
index 40ddf38ecbc..dd9be955134 100644
--- a/gn/tools/gn/value_unittest.cc
+++ b/gn/tools/gn/value_unittest.cc
@@ -33,11 +33,28 @@ TEST(Value, ToString) {
// Scopes.
TestWithScope setup;
- Scope* scope = new Scope(setup.scope());
+ Scope* scope = new Scope(setup.settings());
Value scopeval(nullptr, std::unique_ptr<Scope>(scope));
EXPECT_EQ("{ }", scopeval.ToString(false));
+ // Test that an empty scope equals an empty scope.
+ EXPECT_TRUE(scopeval == scopeval);
+
scope->SetValue("a", Value(nullptr, static_cast<int64_t>(42)), nullptr);
scope->SetValue("b", Value(nullptr, "hello, world"), nullptr);
EXPECT_EQ("{\n a = 42\n b = \"hello, world\"\n}", scopeval.ToString(false));
+ EXPECT_TRUE(scopeval == scopeval);
+
+ Scope* inner_scope = new Scope(setup.settings());
+ Value inner_scopeval(nullptr, std::unique_ptr<Scope>(inner_scope));
+ inner_scope->SetValue("d", Value(nullptr, static_cast<int64_t>(42)), nullptr);
+ scope->SetValue("c", inner_scopeval, nullptr);
+
+ // Test inner scope equality.
+ EXPECT_TRUE(scopeval == scopeval);
+
+ // Nested scopes should not be equal.
+ Scope* nested_scope = new Scope(scope);
+ Value nested_scopeval(nullptr, std::unique_ptr<Scope>(nested_scope));
+ EXPECT_FALSE(nested_scopeval == nested_scopeval);
}
diff --git a/gn/tools/gn/variables.cc b/gn/tools/gn/variables.cc
index 67975ce52dd..a809a1cc54d 100644
--- a/gn/tools/gn/variables.cc
+++ b/gn/tools/gn/variables.cc
@@ -377,7 +377,6 @@ Example
action("myscript") {
# Pass the output dir to the script.
args = [ "-o", rebase_path(target_out_dir, root_build_dir) ]"
-
}
)";
@@ -517,7 +516,7 @@ 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.
+ R"(args: (target variable) Arguments passed to an action.
For action and action_foreach targets, args is the list of arguments to pass
to the script. Typically you would use source expansion (see "gn help
@@ -593,7 +592,6 @@ Example
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"
}
)";
@@ -619,8 +617,10 @@ 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.
+ R"(bundle_resources_dir
+
+ bundle_resources_dir: Expansion of {{bundle_resources_dir}} in
+ create_bundle.
A string corresponding to a path in $root_build_dir.
@@ -669,8 +669,10 @@ 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.
+ R"(bundle_executable_dir
+
+ bundle_executable_dir: Expansion of {{bundle_executable_dir}} in
+ create_bundle.
A string corresponding to a path in $root_build_dir.
@@ -681,22 +683,6 @@ const char kBundleExecutableDir_Help[] =
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.";
@@ -1001,6 +987,19 @@ Example
}
)";
+const char kDataKeys[] = "data_keys";
+const char kDataKeys_HelpShort[] =
+ "data_keys: [string list] Keys from which to collect metadata.";
+const char kDataKeys_Help[] =
+ R"(data_keys: Keys from which to collect metadata.
+
+ These keys are used to identify metadata to collect. If a walked target
+ defines this key in its metadata, its value will be appended to the resulting
+ collection.
+
+ See "gn help generated_file".
+)";
+
const char kDefines[] = "defines";
const char kDefines_HelpShort[] =
"defines: [string list] C preprocessor defines.";
@@ -1338,6 +1337,32 @@ Examples
libs = [ "ld" ]
)";
+const char kMetadata[] = "metadata";
+const char kMetadata_HelpShort[] = "metadata: [scope] Metadata of this target.";
+const char kMetadata_Help[] =
+ R"(metadata: Metadata of this target.
+
+ Metadata is a collection of keys and values relating to a particular target.
+ Values must be lists, allowing for sane and predictable collection behavior.
+ Generally, these keys will include three types of lists: lists of ordinary
+ strings, lists of filenames intended to be rebased according to their
+ particular source directory, and lists of target labels intended to be used
+ as barriers to the walk. Verfication of these categories occurs at walk time,
+ not creation time (since it is not clear until the walk which values are
+ intended for which purpose).
+
+Example
+
+ group("doom_melon") {
+ metadata = {
+ # These keys are not built in to GN but are interpreted when consuming
+ # metadata.
+ my_barrier = []
+ my_files = [ "a.txt", "b.txt" ]
+ }
+ }
+)";
+
const char kOutputExtension[] = "output_extension";
const char kOutputExtension_HelpShort[] =
"output_extension: [string] Value to use for the output's file extension.";
@@ -1798,6 +1823,26 @@ Example
}
)";
+const char kRebase[] = "rebase";
+const char kRebase_HelpShort[] =
+ "rebase: [boolean] Rebase collected metadata as files.";
+const char kRebase_Help[] =
+ R"(rebase: Rebase collected metadata as files.
+
+ A boolean that triggers a rebase of collected metadata strings based on their
+ declared file. Defaults to false.
+
+ Metadata generally declares files as strings relative to the local build file.
+ However, this data is often used in other contexts, and so setting this flag
+ will force the metadata collection to be rebased according to the local build
+ file's location and thus allow the filename to be used anywhere.
+
+ Setting this flag will raise an error if any target's specified metadata is
+ not a string value.
+
+ See also "gn help generated_file".
+)";
+
const char kResponseFileContents[] = "response_file_contents";
const char kResponseFileContents_HelpShort[] =
"response_file_contents: [string list] Contents of .rsp file for actions.";
@@ -1888,10 +1933,9 @@ Sources for non-binary targets
const char kXcodeTestApplicationName[] = "xcode_test_application_name";
const char kXcodeTestApplicationName_HelpShort[] =
- "test_application_name: [string] Test application name for unit or ui test "
- "target.";
+ "xcode_test_application_name: [string] Name for Xcode test target.";
const char kXcodeTestApplicationName_Help[] =
- R"(test_application_name: Test application name for unit or ui test target.
+ R"(xcode_test_application_name: Name for Xcode test target.
Each unit and ui test target must have a test application target, and this
value is used to specify the relationship. Only meaningful to Xcode (used as
@@ -1985,6 +2029,45 @@ Examples
visibility = [ "./*", "//bar/*" ]
)";
+const char kWalkKeys[] = "walk_keys";
+const char kWalkKeys_HelpShort[] =
+ "walk_keys: [string list] Key(s) for managing the metadata collection "
+ "walk.";
+const char kWalkKeys_Help[] =
+ R"(walk_keys: Key(s) for managing the metadata collection walk.
+
+ Defaults to [].
+
+ These keys are used to control the next step in a collection walk, acting as
+ barriers. If a specified key is defined in a target's metadata, the walk will
+ use the targets listed in that value to determine which targets are walked.
+
+ If no walk_keys are specified for a generated_file target (i.e. "[]"), the
+ walk will touch all deps and data_deps of the specified target recursively.
+
+ See "gn help generated_file".
+)";
+
+const char kWriteValueContents[] = "contents";
+const char kWriteValueContents_HelpShort[] =
+ "contents: Contents to write to file.";
+const char kWriteValueContents_Help[] =
+ R"(contents: Contents to write to file.
+
+ The contents of the file for a generated_file target.
+ See "gn help generated_file".
+)";
+
+const char kWriteOutputConversion[] = "output_conversion";
+const char kWriteOutputConversion_HelpShort[] =
+ "output_conversion: Data format for generated_file targets.";
+const char kWriteOutputConversion_Help[] =
+ R"("output_conversion: Data format for generated_file targets.
+
+ Controls how the "contents" of a generated_file target is formatted.
+ See "gn help output_conversion".
+)";
+
const char kWriteRuntimeDeps[] = "write_runtime_deps";
const char kWriteRuntimeDeps_HelpShort[] =
"write_runtime_deps: Writes the target's runtime_deps to the given path.";
@@ -2069,7 +2152,6 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(BundleResourcesDir)
INSERT_VARIABLE(BundleDepsFilter)
INSERT_VARIABLE(BundleExecutableDir)
- INSERT_VARIABLE(BundlePlugInsDir)
INSERT_VARIABLE(Cflags)
INSERT_VARIABLE(CflagsC)
INSERT_VARIABLE(CflagsCC)
@@ -2084,6 +2166,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(Configs)
INSERT_VARIABLE(Data)
INSERT_VARIABLE(DataDeps)
+ INSERT_VARIABLE(DataKeys)
INSERT_VARIABLE(Defines)
INSERT_VARIABLE(Depfile)
INSERT_VARIABLE(Deps)
@@ -2093,6 +2176,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(Ldflags)
INSERT_VARIABLE(Libs)
INSERT_VARIABLE(LibDirs)
+ INSERT_VARIABLE(Metadata)
INSERT_VARIABLE(OutputDir)
INSERT_VARIABLE(OutputExtension)
INSERT_VARIABLE(OutputName)
@@ -2107,12 +2191,16 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(Public)
INSERT_VARIABLE(PublicConfigs)
INSERT_VARIABLE(PublicDeps)
+ INSERT_VARIABLE(Rebase)
INSERT_VARIABLE(ResponseFileContents)
INSERT_VARIABLE(Script)
INSERT_VARIABLE(Sources)
INSERT_VARIABLE(XcodeTestApplicationName)
INSERT_VARIABLE(Testonly)
INSERT_VARIABLE(Visibility)
+ INSERT_VARIABLE(WalkKeys)
+ INSERT_VARIABLE(WriteOutputConversion)
+ INSERT_VARIABLE(WriteValueContents)
INSERT_VARIABLE(WriteRuntimeDeps)
INSERT_VARIABLE(XcodeExtraAttributes)
}
diff --git a/gn/tools/gn/variables.h b/gn/tools/gn/variables.h
index 080989b4f5b..09f2c1872ed 100644
--- a/gn/tools/gn/variables.h
+++ b/gn/tools/gn/variables.h
@@ -123,10 +123,6 @@ 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;
@@ -183,6 +179,10 @@ extern const char kDataDeps[];
extern const char kDataDeps_HelpShort[];
extern const char kDataDeps_Help[];
+extern const char kDataKeys[];
+extern const char kDataKeys_HelpShort[];
+extern const char kDataKeys_Help[];
+
extern const char kDefines[];
extern const char kDefines_HelpShort[];
extern const char kDefines_Help[];
@@ -219,6 +219,10 @@ extern const char kLibs[];
extern const char kLibs_HelpShort[];
extern const char kLibs_Help[];
+extern const char kMetadata[];
+extern const char kMetadata_HelpShort[];
+extern const char kMetadata_Help[];
+
extern const char kOutputDir[];
extern const char kOutputDir_HelpShort[];
extern const char kOutputDir_Help[];
@@ -275,6 +279,10 @@ extern const char kPublicDeps[];
extern const char kPublicDeps_HelpShort[];
extern const char kPublicDeps_Help[];
+extern const char kRebase[];
+extern const char kRebase_HelpShort[];
+extern const char kRebase_Help[];
+
extern const char kResponseFileContents[];
extern const char kResponseFileContents_HelpShort[];
extern const char kResponseFileContents_Help[];
@@ -299,6 +307,18 @@ extern const char kVisibility[];
extern const char kVisibility_HelpShort[];
extern const char kVisibility_Help[];
+extern const char kWalkKeys[];
+extern const char kWalkKeys_HelpShort[];
+extern const char kWalkKeys_Help[];
+
+extern const char kWriteValueContents[];
+extern const char kWriteValueContents_HelpShort[];
+extern const char kWriteValueContents_Help[];
+
+extern const char kWriteOutputConversion[];
+extern const char kWriteOutputConversion_HelpShort[];
+extern const char kWriteOutputConversion_Help[];
+
extern const char kWriteRuntimeDeps[];
extern const char kWriteRuntimeDeps_HelpShort[];
extern const char kWriteRuntimeDeps_Help[];
diff --git a/gn/tools/gn/visual_studio_writer.cc b/gn/tools/gn/visual_studio_writer.cc
index c4fd906bbdc..9870fbcf4f7 100644
--- a/gn/tools/gn/visual_studio_writer.cc
+++ b/gn/tools/gn/visual_studio_writer.cc
@@ -72,12 +72,15 @@ struct SourceFileWriter {
const char kToolsetVersionVs2013[] = "v120"; // Visual Studio 2013
const char kToolsetVersionVs2015[] = "v140"; // Visual Studio 2015
const char kToolsetVersionVs2017[] = "v141"; // Visual Studio 2017
+const char kToolsetVersionVs2019[] = "v142"; // Visual Studio 2019
const char kProjectVersionVs2013[] = "12.0"; // Visual Studio 2013
const char kProjectVersionVs2015[] = "14.0"; // Visual Studio 2015
const char kProjectVersionVs2017[] = "15.0"; // Visual Studio 2017
+const char kProjectVersionVs2019[] = "16.0"; // Visual Studio 2019
const char kVersionStringVs2013[] = "Visual Studio 2013"; // Visual Studio 2013
const char kVersionStringVs2015[] = "Visual Studio 2015"; // Visual Studio 2015
const char kVersionStringVs2017[] = "Visual Studio 2017"; // Visual Studio 2017
+const char kVersionStringVs2019[] = "Visual Studio 2019"; // Visual Studio 2019
const char kWindowsKitsVersion[] = "10"; // Windows 10 SDK
const char kWindowsKitsDefaultVersion[] = "10.0.17134.0"; // Windows 10 SDK
@@ -299,6 +302,11 @@ VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings,
toolset_version_ = kToolsetVersionVs2017;
version_string_ = kVersionStringVs2017;
break;
+ case Version::Vs2019:
+ project_version_ = kProjectVersionVs2019;
+ toolset_version_ = kToolsetVersionVs2019;
+ version_string_ = kVersionStringVs2019;
+ break;
default:
NOTREACHED() << "Not a valid Visual Studio Version: " << version;
}
@@ -517,13 +525,7 @@ bool VisualStudioWriter::WriteProjectFileContents(
{
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("OutDir")->Text("$(SolutionDir)");
properties->SubElement("TargetName")->Text("$(ProjectName)");
if (target->output_type() != Target::GROUP) {
properties->SubElement("TargetPath")->Text("$(OutDir)\\" + ninja_target);
diff --git a/gn/tools/gn/visual_studio_writer.h b/gn/tools/gn/visual_studio_writer.h
index 3193646b7fc..9b2a239f11c 100644
--- a/gn/tools/gn/visual_studio_writer.h
+++ b/gn/tools/gn/visual_studio_writer.h
@@ -29,7 +29,8 @@ class VisualStudioWriter {
enum Version {
Vs2013 = 1, // Visual Studio 2013
Vs2015, // Visual Studio 2015
- Vs2017 // Visual Studio 2017
+ Vs2017, // Visual Studio 2017
+ Vs2019, // Visual Studio 2019
};
// Writes Visual Studio project and solution files. |sln_name| is the optional
diff --git a/gn/tools/gn/xcode_writer.cc b/gn/tools/gn/xcode_writer.cc
index e41c7a0037a..0d4a35a7227 100644
--- a/gn/tools/gn/xcode_writer.cc
+++ b/gn/tools/gn/xcode_writer.cc
@@ -376,17 +376,14 @@ bool XcodeWriter::RunAndWriteFiles(const std::string& workspace_name,
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();
+ const std::string source_path = FilePathToUTF8(
+ UTF8ToFilePath(RebasePath("//", build_settings->build_dir()))
+ .StripTrailingSeparators());
+
+ std::string config_name = FilePathToUTF8(build_settings->build_dir()
+ .Resolve(base::FilePath())
+ .StripTrailingSeparators()
+ .BaseName());
DCHECK(!config_name.empty());
std::string::size_type separator = config_name.find('-');
diff --git a/gn/util/exe_path.cc b/gn/util/exe_path.cc
index f2adab52bcb..2f80d5a1fa1 100644
--- a/gn/util/exe_path.cc
+++ b/gn/util/exe_path.cc
@@ -13,6 +13,9 @@
#include <mach-o/dyld.h>
#elif defined(OS_WIN)
#include <windows.h>
+#elif defined(OS_FREEBSD)
+#include <sys/sysctl.h>
+#include <sys/types.h>
#endif
#if defined(OS_MACOSX)
@@ -46,6 +49,18 @@ base::FilePath GetExePath() {
return base::FilePath(system_buffer);
}
+#elif defined(OS_FREEBSD)
+
+base::FilePath GetExePath() {
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ char buf[PATH_MAX];
+ size_t buf_size = PATH_MAX;
+ if (sysctl(mib, 4, buf, &buf_size, nullptr, 0) == -1) {
+ return base::FilePath();
+ }
+ return base::FilePath(buf);
+}
+
#else
base::FilePath GetExePath() {
diff --git a/gn/util/semaphore.cc b/gn/util/semaphore.cc
index 2ae7b7b69c0..d5c82be0738 100644
--- a/gn/util/semaphore.cc
+++ b/gn/util/semaphore.cc
@@ -36,7 +36,7 @@ void Semaphore::Wait() {
}
}
-#elif defined(OS_LINUX) || defined(OS_AIX)
+#elif defined(OS_POSIX)
Semaphore::Semaphore(int count) {
DCHECK_GE(count, 0);
diff --git a/gn/util/semaphore.h b/gn/util/semaphore.h
index 2de27594311..92a1df6453f 100644
--- a/gn/util/semaphore.h
+++ b/gn/util/semaphore.h
@@ -15,7 +15,7 @@
#include <windows.h>
#elif defined(OS_MACOSX)
#include <mach/mach.h>
-#elif defined(OS_LINUX) || defined(OS_AIX)
+#elif defined(OS_POSIX)
#include <semaphore.h>
#else
#error Port.
@@ -35,7 +35,7 @@ class Semaphore {
#if defined(OS_MACOSX)
typedef semaphore_t NativeHandle;
-#elif defined(OS_LINUX) || defined(OS_AIX)
+#elif defined(OS_POSIX)
typedef sem_t NativeHandle;
#elif defined(OS_WIN)
typedef HANDLE NativeHandle;
diff --git a/gn/util/ticks.cc b/gn/util/ticks.cc
index 2a90c90e310..f26b5d80960 100644
--- a/gn/util/ticks.cc
+++ b/gn/util/ticks.cc
@@ -11,7 +11,7 @@
#include <windows.h>
#elif defined(OS_MACOSX)
#include <mach/mach_time.h>
-#elif defined(OS_LINUX) || defined(OS_AIX)
+#elif defined(OS_POSIX)
#include <time.h>
#else
#error Port.
@@ -27,7 +27,7 @@ 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)
+#elif defined(OS_POSIX)
uint64_t g_start;
#else
#error Port.
@@ -44,7 +44,7 @@ void Init() {
#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)
+#elif defined(OS_POSIX)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
g_start = static_cast<uint64_t>(ts.tv_sec) * kNano +
@@ -74,7 +74,7 @@ Ticks TicksNow() {
#elif defined(OS_MACOSX)
now =
((mach_absolute_time() * g_timebase.numer) / g_timebase.denom) - g_start;
-#elif defined(OS_LINUX) || defined(OS_AIX)
+#elif defined(OS_POSIX)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
now = (static_cast<uint64_t>(ts.tv_sec) * kNano +