[rust project] Extract compiler target Extract the compiler arguments, and then from them, the compilation target triple that is being passed to rustc. This is also a step towards deduplicating targets by compilation target and options (combining test and non-test, and preferring cross-compilation targets over the host (default toolchain over a separate toolchain, if there are multiple listings of the same rlib/binary/test. Change-Id: I1af0ef2da5dca41729e9367389048a068efd7959 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8840 Commit-Queue: Aaron Wood <aaronwood@google.com> Reviewed-by: Tyler Mandry <tmandry@google.com>
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc index 21ac261..7bd0561 100644 --- a/src/gn/rust_project_writer.cc +++ b/src/gn/rust_project_writer.cc
@@ -5,6 +5,7 @@ #include "gn/rust_project_writer.h" #include <fstream> +#include <optional> #include <sstream> #include <tuple> @@ -44,7 +45,9 @@ // "unix", // "atomic" value config options // "rust_panic=\"abort\""", // key="value" config options // ] -// "root_module": "absolute path to crate" +// "root_module": "absolute path to crate", +// "label": "//path/target:value", // GN target for the crate +// "target": "x86_64-unknown-linux" // optional rustc target // }, // } // @@ -105,8 +108,62 @@ return deps; } -// TODO(bwb) Parse sysroot structure from toml files. This is fragile and might -// break if upstream changes the dependency structure. +std::vector<std::string> ExtractCompilerArgs(const Target* target) { + std::vector<std::string> args; + for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) { + auto rustflags = iter.cur().rustflags(); + for (auto flag_iter = rustflags.begin(); flag_iter != rustflags.end(); + flag_iter++) { + args.push_back(*flag_iter); + } + } + return args; +} + +std::optional<std::string> FindArgValue(const char* arg, + const std::vector<std::string>& args) { + for (auto current = args.begin(); current != args.end();) { + // capture the current value + auto previous = *current; + // and increment + current++; + + // if the previous argument matches `arg`, and after the above increment the + // end hasn't been reached, this current argument is the desired value. + if (previous == arg && current != args.end()) { + return std::make_optional(*current); + } + } + return std::nullopt; +} + +std::optional<std::string> FindArgValueAfterPrefix( + const std::string& prefix, + const std::vector<std::string>& args) { + for (auto arg : args) { + if (!arg.compare(0, prefix.size(), prefix)) { + auto value = arg.substr(prefix.size()); + return std::make_optional(value); + } + } + return std::nullopt; +} + +std::vector<std::string> FindAllArgValuesAfterPrefix( + const std::string& prefix, + const std::vector<std::string>& args) { + std::vector<std::string> values; + for (auto arg : args) { + if (!arg.compare(0, prefix.size(), prefix)) { + auto value = arg.substr(prefix.size()); + values.push_back(value); + } + } + return values; +} + +// TODO(bwb) Parse sysroot structure from toml files. This is fragile and +// might break if upstream changes the dependency structure. const std::string_view sysroot_crates[] = {"std", "core", "alloc", @@ -218,6 +275,9 @@ return; } + auto compiler_args = ExtractCompilerArgs(target); + auto compiler_target = FindArgValue("--target", compiler_args); + // Check what sysroot this target needs. Add it to the crate list if it // hasn't already been added. auto rust_tool = @@ -243,28 +303,21 @@ SourceFile crate_root = target->rust_values().crate_root(); std::string crate_label = target->label().GetUserVisibleName(false); - std::string cfg_prefix("--cfg="); - std::string edition_prefix("--edition="); - ConfigList cfgs; - - std::string edition = "2015"; // default to be overridden as needed - - for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) { - for (const auto& flag : iter.cur().rustflags()) { - // extract the edition of this target - if (!flag.compare(0, edition_prefix.size(), edition_prefix)) { - edition = flag.substr(edition_prefix.size()); - } - if (!flag.compare(0, cfg_prefix.size(), cfg_prefix)) { - auto cfg = flag.substr(cfg_prefix.size()); - std::string escaped_config; - base::EscapeJSONString(cfg, false, &escaped_config); - cfgs.push_back(escaped_config); - } - } + auto edition = + FindArgValueAfterPrefix(std::string("--edition="), compiler_args); + if (!edition.has_value()) { + edition = FindArgValue("--edition", compiler_args); } - Crate crate = Crate(crate_root, crate_id, crate_label, edition); + Crate crate = + Crate(crate_root, crate_id, crate_label, edition.value_or("2015")); + + crate.SetCompilerArgs(compiler_args); + if (compiler_target.has_value()) + crate.SetCompilerTarget(compiler_target.value()); + + ConfigList cfgs = + FindAllArgValuesAfterPrefix(std::string("--cfg="), compiler_args); crate.AddConfigItem("test"); crate.AddConfigItem("debug_assertions"); @@ -293,17 +346,22 @@ void WriteCrates(const BuildSettings* build_settings, CrateList& crate_list, std::ostream& rust_project) { + // produce a de-duplicated set of source roots: + std::set<std::string> roots; + for (auto& crate : crate_list) { + roots.insert( + FilePathToUTF8(build_settings->GetFullPath(crate.root().GetDir()))); + } + rust_project << "{" NEWLINE; rust_project << " \"roots\": ["; bool first_root = true; - for (auto& crate : crate_list) { + for (auto& root : roots) { if (!first_root) rust_project << ","; first_root = false; - std::string root_dir = - FilePathToUTF8(build_settings->GetFullPath(crate.root().GetDir())); - rust_project << NEWLINE " \"" << root_dir << "\""; + rust_project << NEWLINE " \"" << root << "\""; } rust_project << NEWLINE " ]," NEWLINE; rust_project << " \"crates\": ["; @@ -313,14 +371,38 @@ rust_project << ","; first_crate = false; - auto crate_root = FilePathToUTF8(build_settings->GetFullPath(crate.root())); + auto crate_module = + FilePathToUTF8(build_settings->GetFullPath(crate.root())); rust_project << NEWLINE << " {" NEWLINE << " \"crate_id\": " << crate.index() << "," NEWLINE - << " \"root_module\": \"" << crate_root << "\"," NEWLINE - << " \"label\": \"" << crate.label() << "\"," NEWLINE - << " \"deps\": ["; + << " \"root_module\": \"" << crate_module << "\"," NEWLINE + << " \"label\": \"" << crate.label() << "\"," NEWLINE; + auto compiler_target = crate.CompilerTarget(); + if (compiler_target.has_value()) { + rust_project << " \"target\": \"" << compiler_target.value() + << "\"," NEWLINE; + } + + auto compiler_args = crate.CompilerArgs(); + if (!compiler_args.empty()) { + rust_project << " \"compiler_args\": ["; + bool first_arg = true; + for (auto& arg : crate.CompilerArgs()) { + if (!first_arg) + rust_project << ", "; + first_arg = false; + + std::string escaped_arg; + base::EscapeJSONString(arg, false, &escaped_arg); + + rust_project << "\"" << escaped_arg << "\""; + } + rust_project << "]," << NEWLINE; + } + + rust_project << " \"deps\": ["; bool first_dep = true; for (auto& dep : crate.dependencies()) { if (!first_dep) @@ -343,8 +425,11 @@ rust_project << ","; first_cfg = false; + std::string escaped_config; + base::EscapeJSONString(cfg, false, &escaped_config); + rust_project << NEWLINE; - rust_project << " \"" << cfg << "\""; + rust_project << " \"" << escaped_config << "\""; } rust_project << NEWLINE; rust_project << " ]" NEWLINE; // end cfgs
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h index 25340b3..0020e2c 100644 --- a/src/gn/rust_project_writer_helpers.h +++ b/src/gn/rust_project_writer_helpers.h
@@ -6,6 +6,7 @@ #define TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_ #include <fstream> +#include <optional> #include <sstream> #include <string> #include <tuple> @@ -47,6 +48,12 @@ deps_.push_back(std::make_pair(index, name)); } + // Set the compiler arguments used to invoke the compilation of this crate + void SetCompilerArgs(std::vector<std::string> args) { compiler_args_ = args; } + + // Set the compiler target ("e.g. x86_64-linux-kernel") + void SetCompilerTarget(std::string target) { compiler_target_ = target; } + // Returns the root file for the crate. SourceFile& root() { return root_; } @@ -65,6 +72,14 @@ // Return the set of dependencies for this crate. DependencyList& dependencies() { return deps_; } + // Return the compiler arguments used to invoke the compilation of this crate + const std::vector<std::string>& CompilerArgs() { return compiler_args_; } + + // Return the compiler target "triple" from the compiler args + const std::optional<std::string>& CompilerTarget() { + return compiler_target_; + } + private: SourceFile root_; CrateIndex index_; @@ -72,6 +87,8 @@ std::string edition_; ConfigList configs_; DependencyList deps_; + std::optional<std::string> compiler_target_; + std::vector<std::string> compiler_args_; }; using CrateList = std::vector<Crate>; @@ -97,4 +114,27 @@ CrateList& crate_list, std::ostream& rust_project); +// Assemble the compiler arguments for the given GN Target. +std::vector<std::string> ExtractCompilerArgs(const Target* target); + +// Find the value of an argument that's passed to the compiler as two +// consecutive strings in the list of arguments: ["arg", "value"] +std::optional<std::string> FindArgValue(const char* arg, + const std::vector<std::string>& args); + +// Find the first argument that matches the prefix, returning the value after +// the prefix. e.g. ˝--arg=value", is returned as "value" if the prefix +// "--arg=" is used. +std::optional<std::string> FindArgValueAfterPrefix( + const std::string& prefix, + const std::vector<std::string>& args); + +// Find all arguments that match the given prefix, returning the value after +// the prefix for each one. e.g. "--cfg=value" is returned as "value" if the +// prefix "--cfg=" is used. +std::vector<std::string> FindAllArgValuesAfterPrefix( + const std::string& prefix, + const std::vector<std::string>& args); + + #endif // TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
diff --git a/src/gn/rust_project_writer_helpers_unittest.cc b/src/gn/rust_project_writer_helpers_unittest.cc index 23657ee..c19e709 100644 --- a/src/gn/rust_project_writer_helpers_unittest.cc +++ b/src/gn/rust_project_writer_helpers_unittest.cc
@@ -18,11 +18,13 @@ TestWithScope setup; CrateList crates; - Crate dep = Crate(SourceFile("/root/tortoise/lib.rs"), 0, "//tortoise:bar", "2015"); - Crate target = Crate(SourceFile("/root/hare/lib.rs"), 1, "//hare:bar", "2015"); + Crate dep = + Crate(SourceFile("/root/tortoise/lib.rs"), 0, "//tortoise:bar", "2015"); + Crate target = + Crate(SourceFile("/root/hare/lib.rs"), 1, "//hare:bar", "2015"); target.AddDependency(0, "tortoise"); target.AddConfigItem("unix"); - target.AddConfigItem("feature=\\\"test\\\""); + target.AddConfigItem("feature=\"test\""); crates.push_back(dep); crates.push_back(target); @@ -36,8 +38,8 @@ const char expected_json[] = "{\n" " \"roots\": [\n" - " \"/root/tortoise/\",\n" - " \"/root/hare/\"\n" + " \"/root/hare/\",\n" + " \"/root/tortoise/\"\n" " ],\n" " \"crates\": [\n" " {\n" @@ -88,297 +90,466 @@ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); #endif -const char expected_json[] = - "{\n" - " \"roots\": [\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/\",\n" - " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/\"\n" - " ],\n" - " \"crates\": [\n" - " {\n" - " \"crate_id\": 0,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/lib.rs\",\n" - " \"label\": \"core\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 1,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/lib.rs\",\n" - " \"label\": \"alloc\",\n" - " \"deps\": [\n" - " {\n" - " \"crate\": 0,\n" - " \"name\": \"core\"\n" - " }\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 2,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/lib.rs\",\n" - " \"label\": \"panic_abort\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 3,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/lib.rs\",\n" - " \"label\": \"unwind\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 4,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/lib.rs\",\n" - " \"label\": \"std\",\n" - " \"deps\": [\n" - " {\n" - " \"crate\": 1,\n" - " \"name\": \"alloc\"\n" - " },\n" - " {\n" - " \"crate\": 0,\n" - " \"name\": \"core\"\n" - " },\n" - " {\n" - " \"crate\": 2,\n" - " \"name\": \"panic_abort\"\n" - " },\n" - " {\n" - " \"crate\": 3,\n" - " \"name\": \"unwind\"\n" - " }\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 5,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/lib.rs\",\n" - " \"label\": \"collections\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 6,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/lib.rs\",\n" - " \"label\": \"libc\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 7,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/lib.rs\",\n" - " \"label\": \"panic_unwind\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 8,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/lib.rs\",\n" - " \"label\": \"proc_macro\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 9,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/lib.rs\",\n" - " \"label\": \"rustc_unicode\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 10,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/lib.rs\",\n" - " \"label\": \"std_unicode\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 11,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/lib.rs\",\n" - " \"label\": \"test\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 12,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/lib.rs\",\n" - " \"label\": \"alloc_jemalloc\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 13,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/lib.rs\",\n" - " \"label\": \"alloc_system\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 14,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/lib.rs\",\n" - " \"label\": \"compiler_builtins\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 15,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/lib.rs\",\n" - " \"label\": \"getopts\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 16,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/lib.rs\",\n" - " \"label\": \"build_helper\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 17,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/lib.rs\",\n" - " \"label\": \"rustc_asan\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 18,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/lib.rs\",\n" - " \"label\": \"rustc_lsan\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 19,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/lib.rs\",\n" - " \"label\": \"rustc_msan\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 20,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/lib.rs\",\n" - " \"label\": \"rustc_tsan\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " },\n" - " {\n" - " \"crate_id\": 21,\n" - " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/lib.rs\",\n" - " \"label\": \"syntax\",\n" - " \"deps\": [\n" - " ],\n" - " \"edition\": \"2018\",\n" - " \"cfg\": [\n" - " \"debug_assertions\"\n" - " ]\n" - " }\n" - " ]\n" - "}\n"; -; + const char expected_json[] = + "{\n" + " \"roots\": [\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/\",\n" + " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/\"\n" + " ],\n" + " \"crates\": [\n" + " {\n" + " \"crate_id\": 0,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcore/lib.rs\",\n" + " \"label\": \"core\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 1,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc/lib.rs\",\n" + " \"label\": \"alloc\",\n" + " \"deps\": [\n" + " {\n" + " \"crate\": 0,\n" + " \"name\": \"core\"\n" + " }\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 2,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_abort/" + "lib.rs\",\n" + " \"label\": \"panic_abort\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 3,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libunwind/lib.rs\",\n" + " \"label\": \"unwind\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 4,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd/lib.rs\",\n" + " \"label\": \"std\",\n" + " \"deps\": [\n" + " {\n" + " \"crate\": 1,\n" + " \"name\": \"alloc\"\n" + " },\n" + " {\n" + " \"crate\": 0,\n" + " \"name\": \"core\"\n" + " },\n" + " {\n" + " \"crate\": 2,\n" + " \"name\": \"panic_abort\"\n" + " },\n" + " {\n" + " \"crate\": 3,\n" + " \"name\": \"unwind\"\n" + " }\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 5,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcollections/" + "lib.rs\",\n" + " \"label\": \"collections\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 6,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liblibc/lib.rs\",\n" + " \"label\": \"libc\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 7,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libpanic_unwind/" + "lib.rs\",\n" + " \"label\": \"panic_unwind\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 8,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libproc_macro/" + "lib.rs\",\n" + " \"label\": \"proc_macro\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 9,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_unicode/" + "lib.rs\",\n" + " \"label\": \"rustc_unicode\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 10,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libstd_unicode/" + "lib.rs\",\n" + " \"label\": \"std_unicode\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 11,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libtest/lib.rs\",\n" + " \"label\": \"test\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 12,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_jemalloc/" + "lib.rs\",\n" + " \"label\": \"alloc_jemalloc\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 13,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/liballoc_system/" + "lib.rs\",\n" + " \"label\": \"alloc_system\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 14,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libcompiler_builtins/" + "lib.rs\",\n" + " \"label\": \"compiler_builtins\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 15,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libgetopts/" + "lib.rs\",\n" + " \"label\": \"getopts\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 16,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libbuild_helper/" + "lib.rs\",\n" + " \"label\": \"build_helper\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 17,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_asan/" + "lib.rs\",\n" + " \"label\": \"rustc_asan\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 18,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_lsan/" + "lib.rs\",\n" + " \"label\": \"rustc_lsan\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 19,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_msan/" + "lib.rs\",\n" + " \"label\": \"rustc_msan\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 20,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/librustc_tsan/" + "lib.rs\",\n" + " \"label\": \"rustc_tsan\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " },\n" + " {\n" + " \"crate_id\": 21,\n" + " \"root_module\": " + "\"/root/out/Debug/sysroot/lib/rustlib/src/rust/src/libsyntax/lib.rs\",\n" + " \"label\": \"syntax\",\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"debug_assertions\"\n" + " ]\n" + " }\n" + " ]\n" + "}\n"; + EXPECT_EQ(expected_json, out); } + +TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleSimple) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\""); + target.config_values().rustflags().push_back("--target"); + target.config_values().rustflags().push_back("x86-someos"); + target.config_values().rustflags().push_back("--edition=2018"); + + auto args = ExtractCompilerArgs(&target); + std::optional<std::string> result = FindArgValue("--target", args); + auto expected = std::optional<std::string>{"x86-someos"}; + EXPECT_EQ(expected, result); +} + +TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleMissing) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back( + "--cfg=featur4e=\"foo_enabled\""); + target.config_values().rustflags().push_back("x86-someos"); + target.config_values().rustflags().push_back("--edition=2018"); + + auto args = ExtractCompilerArgs(&target); + std::optional<std::string> result = FindArgValue("--target", args); + auto expected = std::nullopt; + EXPECT_EQ(expected, result); +} + +TEST_F(RustProjectWriterHelper, ExtractCompilerTargetTupleDontFallOffEnd) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\""); + target.config_values().rustflags().push_back("--edition=2018"); + target.config_values().rustflags().push_back("--target"); + + auto args = ExtractCompilerArgs(&target); + std::optional<std::string> result = FindArgValue("--target", args); + auto expected = std::nullopt; + EXPECT_EQ(expected, result); +} + +TEST_F(RustProjectWriterHelper, ExtractFirstArgValueWithPrefixMissing) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\""); + target.config_values().rustflags().push_back("--edition=2018"); + target.config_values().rustflags().push_back("--target"); + + auto args = ExtractCompilerArgs(&target); + std::optional<std::string> result = + FindArgValueAfterPrefix("--missing", args); + auto expected = std::nullopt; + EXPECT_EQ(expected, result); +} + +TEST_F(RustProjectWriterHelper, ExtractFirstArgValueWithPrefix) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\""); + target.config_values().rustflags().push_back("--edition=2018"); + target.config_values().rustflags().push_back("--target"); + + auto args = ExtractCompilerArgs(&target); + std::optional<std::string> result = + FindArgValueAfterPrefix("--edition=", args); + auto expected = std::optional<std::string>{"2018"}; + EXPECT_EQ(expected, result); +} + +TEST_F(RustProjectWriterHelper, ExtractAllArgValueWithPrefix) { + TestWithScope setup; + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\""); + target.config_values().rustflags().push_back("--edition=2018"); + target.config_values().rustflags().push_back("--cfg=feature=\"bar_enabled\""); + target.config_values().rustflags().push_back("--target"); + + auto args = ExtractCompilerArgs(&target); + std::vector<std::string> result = FindAllArgValuesAfterPrefix("--cfg=", args); + std::vector<std::string> expected = {"feature=\"foo_enabled\"", + "feature=\"bar_enabled\""}; + EXPECT_EQ(expected, result); +} \ No newline at end of file
diff --git a/src/gn/rust_project_writer_unittest.cc b/src/gn/rust_project_writer_unittest.cc index 7891576..9df1ad2 100644 --- a/src/gn/rust_project_writer_unittest.cc +++ b/src/gn/rust_project_writer_unittest.cc
@@ -50,6 +50,7 @@ " \"crate_id\": 0,\n" " \"root_module\": \"path/foo/lib.rs\",\n" " \"label\": \"//foo:bar\",\n" + " \"compiler_args\": [\"--cfg=feature=\\\"foo_enabled\\\"\"],\n" " \"deps\": [\n" " ],\n" " \"edition\": \"2015\",\n" @@ -102,8 +103,8 @@ const char expected_json[] = "{\n" " \"roots\": [\n" - " \"tortoise/\",\n" - " \"hare/\"\n" + " \"hare/\",\n" + " \"tortoise/\"\n" " ],\n" " \"crates\": [\n" " {\n" @@ -188,9 +189,9 @@ const char expected_json[] = "{\n" " \"roots\": [\n" - " \"tortoise/\",\n" " \"achilles/\",\n" - " \"hare/\"\n" + " \"hare/\",\n" + " \"tortoise/\"\n" " ],\n" " \"crates\": [\n" " {\n" @@ -305,9 +306,9 @@ const char expected_json[] = "{\n" " \"roots\": [\n" + " \"hare/\",\n" " \"tortoise/\",\n" - " \"tortoise/macro/\",\n" - " \"hare/\"\n" + " \"tortoise/macro/\"\n" " ],\n" " \"crates\": [\n" " {\n" @@ -359,3 +360,156 @@ EXPECT_EQ(expected_json, out); } + +TEST_F(RustProjectJSONWriter, OneRustTargetWithRustcTargetSet) { + Err err; + TestWithScope setup; + setup.build_settings()->SetRootPath(UTF8ToFilePath("path")); + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--target"); + target.config_values().rustflags().push_back("x86-64_unknown"); + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + std::ostringstream stream; + std::vector<const Target*> targets; + targets.push_back(&target); + RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream); + std::string out = stream.str(); +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); +#endif + const char expected_json[] = + "{\n" + " \"roots\": [\n" + " \"path/foo/\"\n" + " ],\n" + " \"crates\": [\n" + " {\n" + " \"crate_id\": 0,\n" + " \"root_module\": \"path/foo/lib.rs\",\n" + " \"label\": \"//foo:bar\",\n" + " \"target\": \"x86-64_unknown\",\n" + " \"compiler_args\": [\"--target\", \"x86-64_unknown\"],\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2015\",\n" + " \"cfg\": [\n" + " \"test\",\n" + " \"debug_assertions\"\n" + " ]\n" + " }\n" + " ]\n" + "}\n"; + + EXPECT_EQ(expected_json, out); +} + +TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSet) { + Err err; + TestWithScope setup; + setup.build_settings()->SetRootPath(UTF8ToFilePath("path")); + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--edition=2018"); + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + std::ostringstream stream; + std::vector<const Target*> targets; + targets.push_back(&target); + RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream); + std::string out = stream.str(); +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); +#endif + const char expected_json[] = + "{\n" + " \"roots\": [\n" + " \"path/foo/\"\n" + " ],\n" + " \"crates\": [\n" + " {\n" + " \"crate_id\": 0,\n" + " \"root_module\": \"path/foo/lib.rs\",\n" + " \"label\": \"//foo:bar\",\n" + " \"compiler_args\": [\"--edition=2018\"],\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"test\",\n" + " \"debug_assertions\"\n" + " ]\n" + " }\n" + " ]\n" + "}\n"; + + EXPECT_EQ(expected_json, out); +} + +TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSetAlternate) { + Err err; + TestWithScope setup; + setup.build_settings()->SetRootPath(UTF8ToFilePath("path")); + + Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); + target.set_output_type(Target::RUST_LIBRARY); + target.visibility().SetPublic(); + SourceFile lib("//foo/lib.rs"); + target.sources().push_back(lib); + target.source_types_used().Set(SourceFile::SOURCE_RS); + target.rust_values().set_crate_root(lib); + target.rust_values().crate_name() = "foo"; + target.config_values().rustflags().push_back("--edition"); + target.config_values().rustflags().push_back("2018"); + target.SetToolchain(setup.toolchain()); + ASSERT_TRUE(target.OnResolved(&err)); + + std::ostringstream stream; + std::vector<const Target*> targets; + targets.push_back(&target); + RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream); + std::string out = stream.str(); +#if defined(OS_WIN) + base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n"); +#endif + const char expected_json[] = + "{\n" + " \"roots\": [\n" + " \"path/foo/\"\n" + " ],\n" + " \"crates\": [\n" + " {\n" + " \"crate_id\": 0,\n" + " \"root_module\": \"path/foo/lib.rs\",\n" + " \"label\": \"//foo:bar\",\n" + " \"compiler_args\": [\"--edition\", \"2018\"],\n" + " \"deps\": [\n" + " ],\n" + " \"edition\": \"2018\",\n" + " \"cfg\": [\n" + " \"test\",\n" + " \"debug_assertions\"\n" + " ]\n" + " }\n" + " ]\n" + "}\n"; + + EXPECT_EQ(expected_json, out); +} \ No newline at end of file