diff --git a/garnet/packages/tools/BUILD.gn b/garnet/packages/tools/BUILD.gn index 89a572e342cbc8492e580663c16697a2bf4a4ef6..7068e5abc7353ae029a2f583cca00f55cb3d8f3b 100644 --- a/garnet/packages/tools/BUILD.gn +++ b/garnet/packages/tools/BUILD.gn @@ -42,6 +42,15 @@ group("zxdb") { ] } +group("fidl_tools") { + testonly = true + public_deps = [ + "//garnet/go/src/fidl", + "//tools/fidlgen_llcpp_zircon", + "//garnet/packages/tools:gidl", + ] +} + group("all") { testonly = true public_deps = [ @@ -51,9 +60,8 @@ group("all") { "//garnet/packages/tools:crashpad_database_util", "//garnet/packages/tools:cs", "//garnet/packages/tools:curl", - "//garnet/go/src/fidl", + "//garnet/packages/tools:fidl_tools", "//garnet/packages/tools:fidlmerge", - "//garnet/packages/tools:gidl", "//garnet/packages/tools:iperf", "//garnet/packages/tools:magma", "//garnet/packages/tools:make-efi", diff --git a/tools/fidlgen_llcpp_zircon/BUILD.gn b/tools/fidlgen_llcpp_zircon/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ee567c8e1b52e1cc03f69cf70f26e4189702ee22 --- /dev/null +++ b/tools/fidlgen_llcpp_zircon/BUILD.gn @@ -0,0 +1,45 @@ +# Copyright 2018 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/host.gni") +import("//build/compiled_action.gni") +import("//build/config/fuchsia/zircon.gni") + +executable("fidlgen_llcpp_zircon") { + sources = [ + "llcpp_codegen.cc", + "llcpp_codegen.h", + "main.cc", + ] + deps = [ "//third_party/rapidjson" ] + data_deps = [ "//garnet/go/src/fidl" ] +} + +compiled_action("validate") { + tool = ":fidlgen_llcpp_zircon" + args = [ + "validate", + rebase_path(zircon_root_build_dir, root_build_dir), + rebase_path("$root_build_dir/tools/fidlgen_llcpp", root_build_dir), + rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_validated.stamp", root_build_dir), + rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_validated.d", root_build_dir) + ] + depfile = "$target_gen_dir/fidlgen_llcpp_zircon_validated.d" + inputs = [ "$zircon_root_build_dir/fidl_gen.json" ] + outputs = [ "$target_gen_dir/fidlgen_llcpp_zircon_validated.stamp" ] +} + +compiled_action("update") { + tool = ":fidlgen_llcpp_zircon" + args = [ + "update", + rebase_path(zircon_root_build_dir, root_build_dir), + rebase_path("$root_build_dir/tools/fidlgen_llcpp", root_build_dir), + rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_updated.stamp", root_build_dir), + rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_updated.d", root_build_dir) + ] + depfile = "$target_gen_dir/fidlgen_llcpp_zircon_updated.d" + inputs = [ "$zircon_root_build_dir/fidl_gen.json" ] + outputs = [ "$target_gen_dir/fidlgen_llcpp_zircon_updated.stamp" ] +} diff --git a/tools/fidlgen_llcpp_zircon/README.md b/tools/fidlgen_llcpp_zircon/README.md new file mode 100644 index 0000000000000000000000000000000000000000..35da4c4e90efd9e461673d56dc280500559aee0b --- /dev/null +++ b/tools/fidlgen_llcpp_zircon/README.md @@ -0,0 +1,25 @@ +## Generated FIDL Low-Level C++ Bindings + +Because of BLD-427 and BLD-353 blocking invoking `fidlgen` from the zircon build, +we intend to check in copies of llcpp bindings for select FIDL libraries, +as a workaround, to support limited use of llcpp. + +Each checked in library can be referenced similar to the C bindings, e.g. whereas one would write +`"$zx/system/fidl/fuchsia-mem:c"` to get the auto-generated C FIDL bindings, the llcpp version is +`"$zx/system/fidl/fuchsia-mem:llcpp"`. + +When using it in source code, whereas one would write `#include <fuchsia/mem/c/fidl.h>` to import +the C bindings header, the corresponding llcpp directive would be +`#include <fuchsia/mem/llcpp/fidl.h>`. + +To regenerate all the bindings, simply run the following command: +```bash +fx build -k 0 tools/fidlgen_llcpp_zircon:update +``` +The `-k 0` switches would keep the build going even if parts of zircon failed to build. + +[TODO(yifeit): Implement] As extra precaution measure, the full build will validate that the +generated bindings are up-to-date. You may manually run the same check with the following command: +```bash +fx build tools/fidlgen_llcpp_zircon:validate +``` diff --git a/tools/fidlgen_llcpp_zircon/llcpp_codegen.cc b/tools/fidlgen_llcpp_zircon/llcpp_codegen.cc new file mode 100644 index 0000000000000000000000000000000000000000..9441db0bc0c3ed243211103dd9beda1ca5337038 --- /dev/null +++ b/tools/fidlgen_llcpp_zircon/llcpp_codegen.cc @@ -0,0 +1,146 @@ +// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <cstdio> +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <memory> +#include <string> +#include <vector> + +#include <unistd.h> +#include <sys/wait.h> + +#include "llcpp_codegen.h" +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/istreamwrapper.h" + +namespace fs = std::filesystem; + +namespace { + +[[noreturn]] void FatalError(const std::string& info) { + std::cerr << "Error: " << info << ", errno: " << strerror(errno) << std::endl; + exit(1); +} + +rapidjson::Document ReadMetadata(const fs::path& path) { + rapidjson::Document metadata; + std::ifstream contents(path); + if (contents.fail()) { + FatalError("Failed to read GN metadata at " + std::string(path)); + } + rapidjson::IStreamWrapper isw(contents); + rapidjson::ParseResult parse_result = metadata.ParseStream(isw); + if (!parse_result) { + FatalError("Failed to parse " + std::string(path) + ", " + + rapidjson::GetParseError_En(parse_result.Code()) + ", offset " + + std::to_string(parse_result.Offset())); + } + if (!metadata.IsArray()) { + FatalError("Metadata is not an array"); + } + return metadata; +} + +struct Target { + fs::path gen_dir; + std::vector<fs::path> fidl_sources; + std::vector<std::string> args; +}; + +std::vector<Target> AllTargets(const fs::path& zircon_build_root) { + std::vector<Target> targets_vector; + const auto metadata = ReadMetadata(zircon_build_root / "fidl_gen.json"); + for (auto value_it = metadata.Begin(); + value_it != metadata.End(); ++value_it) { + const auto& target = *value_it; + const rapidjson::Value& args = target["args"]; + if (!args.IsArray()) { + FatalError("args in metadata JSON must be an array"); + } + std::vector<std::string> args_vector; + for (auto arg_it = args.Begin(); arg_it != args.End(); ++arg_it) { + args_vector.emplace_back(arg_it->GetString()); + } + const rapidjson::Value& fidl_sources = target["fidl_sources"]; + if (!fidl_sources.IsArray()) { + FatalError("fidl_sources in metadata JSON must be an array"); + } + std::vector<fs::path> fidl_sources_vector; + for (auto it = fidl_sources.Begin(); it != fidl_sources.End(); ++it) { + fidl_sources_vector.emplace_back(fs::path(it->GetString())); + } + targets_vector.push_back(Target { + .gen_dir = fs::path(target["target_gen_dir"].GetString()), + .args = std::move(args_vector), + .fidl_sources = std::move(fidl_sources_vector) + }); + } + return targets_vector; +} + +// Run a command with the specified command, working directory, and arguments. +void RunCommand(const std::string& cmd, const std::string& working_directory, + std::vector<std::string> args) { + pid_t pid = fork(); + int status; + switch (pid) { + case -1: + FatalError("Failed to fork"); + case 0: { + status = chdir(working_directory.c_str()); + if (status != 0) { + FatalError("Failed to chdir to " + working_directory); + } + std::vector<char *> c_args; + c_args.push_back(const_cast<char *>(cmd.c_str())); + for (const auto& arg : args) { + c_args.push_back(const_cast<char *>(arg.c_str())); + } + c_args.push_back(nullptr); + execv(cmd.c_str(), &c_args[0]); + FatalError("when executing " + cmd + ", execv should not return"); + } + default: + pid_t ret_pid = waitpid(pid, &status, 0); + if (pid != ret_pid) { + FatalError("when executing " + cmd + + ", unexpected return value from waitpid: " + + std::to_string(ret_pid)); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + FatalError(cmd + " returned an error: " + std::to_string(status)); + } + } +} + +} // namespace + +bool DoValidate(std::filesystem::path zircon_build_root, + std::filesystem::path fidlgen_llcpp_path, + std::vector<fs::path>* out_dependencies) { + auto all_targets = AllTargets(zircon_build_root); + for (const auto& target : all_targets) { + for (const auto& source : target.fidl_sources) { + out_dependencies->push_back(zircon_build_root / source); + } + // TODO(yifeit): Implement. + } + return true; +} + +void DoUpdate(fs::path zircon_build_root, + fs::path fidlgen_llcpp_path, + std::vector<fs::path>* out_dependencies) { + const auto all_targets = AllTargets(zircon_build_root); + for (const auto& target : all_targets) { + for (const auto& source : target.fidl_sources) { + out_dependencies->push_back(zircon_build_root / source); + } + RunCommand(fidlgen_llcpp_path, zircon_build_root, target.args); + } +} diff --git a/tools/fidlgen_llcpp_zircon/llcpp_codegen.h b/tools/fidlgen_llcpp_zircon/llcpp_codegen.h new file mode 100644 index 0000000000000000000000000000000000000000..997addb8932ffe85fe0cb71d41bce8205790bf14 --- /dev/null +++ b/tools/fidlgen_llcpp_zircon/llcpp_codegen.h @@ -0,0 +1,23 @@ +// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_FIDLGEN_LLCPP_ZIRCON_LLCPP_CODEGEN_H_ +#define TOOLS_FIDLGEN_LLCPP_ZIRCON_LLCPP_CODEGEN_H_ + +#include <filesystem> +#include <string> +#include <vector> + +// Validate without touching the checked-in sources. +// Returns true when sources are up-to-date. +bool DoValidate(std::filesystem::path zircon_build_root, + std::filesystem::path fidlgen_llcpp_path, + std::vector<std::filesystem::path>* out_dependencies); + +// Update the checked-in sources. +void DoUpdate(std::filesystem::path zircon_build_root, + std::filesystem::path fidlgen_llcpp_path, + std::vector<std::filesystem::path>* out_dependencies); + +#endif // TOOLS_FIDLGEN_LLCPP_ZIRCON_LLCPP_CODEGEN_H_ diff --git a/tools/fidlgen_llcpp_zircon/main.cc b/tools/fidlgen_llcpp_zircon/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..1b5685d934510724c85515f0b279b3b631b07506 --- /dev/null +++ b/tools/fidlgen_llcpp_zircon/main.cc @@ -0,0 +1,88 @@ +// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <cstdio> +#include <cstdlib> +#include <filesystem> +#include <fstream> +#include <iostream> +#include <string.h> +#include <utility> + +#include "llcpp_codegen.h" + +namespace fs = std::filesystem; + +static void Usage(const char* exe_name) { + fprintf( + stderr, + "Generate or validate the checked-in low-level C++ bindings in zircon.\n" + "Usage: %s (validate|update) ZIRCON_BUILDROOT FIDLGEN_LLCPP_PATH " + "STAMP DEPFILE\n" + "ZIRCON_BUILDROOT is the root build directory of the Zircon GN build.\n" + "FIDLGEN_LLCPP_PATH is the path to the fidlgen_llcpp executable.\n" + "STAMP is the output path to a file indicating the success of the tool.\n" + "DEPFILE is the output path to a depfile describing the FIDL files\n" + "which when updated should trigger a re-run of this tool.\n" + "\n" + "When validate is specified, it will validate that the generated\n" + "bindings are up-to-date, exiting with an error if not so.\n" + "Files in the source tree are not modified.\n" + "\n" + "When update is specified, it will regenerate the bindings in\n" + "zircon/system/fidl from GN metadata.\n" + "\n", + exe_name); +} + +int main(int argc, char** argv) { + if (argc != 6) { + std::cerr << argv[0] << ": Invalid arguments" << "\n\n"; + Usage(argv[0]); + return -1; + } + // Since we're dealing with two builds, it's less ambiguous if we start with + // all absolute paths in the beginning, then convert to relative paths + // where required, similar to rebase_path in GN. + fs::path zircon_build_root = fs::absolute(argv[2]); + fs::path fidlgen_llcpp_path = fs::absolute(argv[3]); + fs::path stamp_path = fs::absolute(argv[4]); + fs::remove(stamp_path); + fs::path depfile_path = fs::absolute(argv[5]); + fs::remove(depfile_path); + + std::vector<fs::path> dependencies; + if (strcmp(argv[1], "validate") == 0) { + DoValidate(zircon_build_root, fidlgen_llcpp_path, &dependencies); + } else if (strcmp(argv[1], "update") == 0) { + DoUpdate(zircon_build_root, fidlgen_llcpp_path, &dependencies); + } else { + std::cerr << argv[0] << ": Expected validate or update, not " << argv[1] + << "\n\n"; + Usage(argv[0]); + return -1; + } + // Generate stamp file + std::fstream stamp; + stamp.open(stamp_path, std::ios::out); + if (!stamp) { + std::cerr << "Failed to stamp " << stamp_path << std::endl; + return -1; + } + stamp.close(); + // Generate depfile + std::fstream depfile; + depfile.open(depfile_path, std::ios::out); + if (!depfile) { + std::cerr << "Failed to create depfile " << depfile_path << std::endl; + return -1; + } + depfile << fs::relative(stamp_path).string() << ":"; + for (const auto& dep : dependencies) { + depfile << " " << fs::relative(dep).string(); + } + depfile << std::endl; + depfile.close(); + return 0; +} \ No newline at end of file diff --git a/zircon/system/fidl/fuchsia-mem/gen/llcpp/fidl.cc b/zircon/system/fidl/fuchsia-mem/gen/llcpp/fidl.cc new file mode 100644 index 0000000000000000000000000000000000000000..9a0d935d6a27e370b61ba66b1f2447cdc31d3274 --- /dev/null +++ b/zircon/system/fidl/fuchsia-mem/gen/llcpp/fidl.cc @@ -0,0 +1,10 @@ +// WARNING: This file is machine generated by fidlgen. + +#include <fuchsia/mem/llcpp/fidl.h> +#include <memory> + +namespace fuchsia { +namespace mem { + +} // namespace mem +} // namespace fuchsia diff --git a/zircon/system/fidl/fuchsia-mem/gen/llcpp/include/fuchsia/mem/llcpp/fidl.h b/zircon/system/fidl/fuchsia-mem/gen/llcpp/include/fuchsia/mem/llcpp/fidl.h new file mode 100644 index 0000000000000000000000000000000000000000..6d1169911f59cef7d316071f6a3b5549ad7b83ee --- /dev/null +++ b/zircon/system/fidl/fuchsia-mem/gen/llcpp/include/fuchsia/mem/llcpp/fidl.h @@ -0,0 +1,56 @@ +// WARNING: This file is machine generated by fidlgen. + +#pragma once + +#include <lib/fidl/internal.h> +#include <lib/fidl/cpp/vector_view.h> +#include <lib/fidl/cpp/string_view.h> +#include <lib/fidl/llcpp/array.h> +#include <lib/fidl/llcpp/coding.h> +#include <lib/fidl/llcpp/traits.h> +#include <lib/fidl/llcpp/transaction.h> +#include <lib/zx/vmo.h> +#include <zircon/fidl.h> + +namespace fuchsia { +namespace mem { + +struct Buffer; + +extern "C" const fidl_type_t fuchsia_mem_BufferTable; + +// A Buffer for data whose size is not necessarily a multiple of the page +// size. +// +// VMO objects have a physical size that is always a multiple of the page +// size. As such, VMO alone cannot serve as a buffer for arbitrarly sized +// data. |fuchsia.mem.Buffer| is a standard struct that aggregate the VMO +// and its size. +struct Buffer { + static constexpr const fidl_type_t* Type = &fuchsia_mem_BufferTable; + static constexpr uint32_t MaxNumHandles = 1; + static constexpr uint32_t PrimarySize = 16; + [[maybe_unused]] + static constexpr uint32_t MaxOutOfLine = 0; + + // The vmo. + ::zx::vmo vmo{}; + + // The size of the data in the vmo in bytes. This size must be smaller + // than the physical size of the vmo. + uint64_t size{}; +}; + +} // namespace mem +} // namespace fuchsia + +namespace fidl { + +template <> +struct IsFidlType<::fuchsia::mem::Buffer> : public std::true_type {}; +static_assert(std::is_standard_layout_v<::fuchsia::mem::Buffer>); +static_assert(offsetof(::fuchsia::mem::Buffer, vmo) == 0); +static_assert(offsetof(::fuchsia::mem::Buffer, size) == 8); +static_assert(sizeof(::fuchsia::mem::Buffer) == ::fuchsia::mem::Buffer::PrimarySize); + +} // namespace fidl