blob: 2a6fead87eeedcc7cd2b3ad766a18635187f58a7 [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "rs_bindings_from_cc/cmdline.h"
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include "third_party/absl/flags/flag.h"
#include "third_party/absl/strings/substitute.h"
#include "third_party/json/include/nlohmann/json.hpp"
#include "util/task/status_macros.h"
ABSL_FLAG(bool, do_nothing, false,
"if set to true the tool will produce empty files "
"(useful for testing Blaze integration)");
ABSL_FLAG(std::string, rs_out, "",
"output path for the Rust source file with bindings");
ABSL_FLAG(std::string, cc_out, "",
"output path for the C++ source file with bindings implementation");
ABSL_FLAG(std::string, ir_out, "",
"(optional) output path for the JSON IR. If not present, the JSON IR "
"will not be dumped.");
ABSL_FLAG(std::vector<std::string>, public_headers, std::vector<std::string>(),
"public headers of the cc_library this tool should generate bindings "
"for, in a format suitable for usage in google3-relative quote "
"include (#include \"\").");
ABSL_FLAG(std::string, targets_and_headers, std::string(),
"Information about which headers belong to which targets, encoded as "
"a JSON array. For example: "
"[\n"
" {\n"
" \"t\": \"//foo/bar:baz\",\n"
" \"h\": [\"foo/bar/header1.h\", \"foo/bar/header2.h\"]\n"
" },\n"
"...\n"
"]");
namespace rs_bindings_from_cc {
absl::StatusOr<Cmdline> Cmdline::Create() {
return CreateFromArgs(
absl::GetFlag(FLAGS_cc_out), absl::GetFlag(FLAGS_rs_out),
absl::GetFlag(FLAGS_ir_out), absl::GetFlag(FLAGS_do_nothing),
absl::GetFlag(FLAGS_public_headers),
absl::GetFlag(FLAGS_targets_and_headers));
}
absl::StatusOr<Cmdline> Cmdline::CreateFromArgs(
std::string cc_out, std::string rs_out, std::string ir_out, bool do_nothing,
std::vector<std::string> public_headers,
std::string targets_and_headers_str) {
Cmdline cmdline;
if (rs_out.empty()) {
return absl::InvalidArgumentError("please specify --rs_out");
}
cmdline.rs_out_ = std::move(rs_out);
if (cc_out.empty()) {
return absl::InvalidArgumentError("please specify --cc_out");
}
cmdline.cc_out_ = std::move(cc_out);
cmdline.ir_out_ = std::move(ir_out);
cmdline.do_nothing_ = do_nothing;
if (public_headers.empty()) {
return absl::InvalidArgumentError("please specify --public_headers");
}
std::transform(public_headers.begin(), public_headers.end(),
std::back_inserter(cmdline.public_headers_),
[](const std::string& s) { return HeaderName(s); });
if (targets_and_headers_str.empty()) {
return absl::InvalidArgumentError("please specify --targets_and_headers");
}
nlohmann::json targets_and_headers =
nlohmann::json::parse(std::move(targets_and_headers_str));
if (!targets_and_headers.is_array()) {
return absl::InvalidArgumentError(
"Expected `--targets_and_headers` to be a JSON array of objects");
}
for (const auto& target_and_headers : targets_and_headers) {
if (!target_and_headers["h"].is_array()) {
return absl::InvalidArgumentError(
"Expected `h` fields of `--targets_and_headers` to be an array");
}
BlazeLabel target = BlazeLabel{target_and_headers["t"]};
for (const auto& header : target_and_headers["h"]) {
cmdline.headers_to_targets_.insert(
std::make_pair(HeaderName(std::string(header)), target));
}
}
ASSIGN_OR_RETURN(cmdline.current_target_,
cmdline.FindHeader(cmdline.public_headers_[0]));
for (const HeaderName& public_header : cmdline.public_headers_) {
ASSIGN_OR_RETURN(BlazeLabel header_target,
cmdline.FindHeader(public_header));
if (cmdline.current_target_ != header_target) {
return absl::InvalidArgumentError(absl::Substitute(
"Expected all public headers to belong to the current target '$0', "
"but header '$1' belongs to '$2'",
cmdline.current_target_.value(), public_header.IncludePath(),
header_target.value()));
}
}
return cmdline;
}
absl::StatusOr<BlazeLabel> Cmdline::FindHeader(const HeaderName& header) const {
auto it = headers_to_targets_.find(header);
if (it == headers_to_targets_.end()) {
return absl::InvalidArgumentError(absl::Substitute(
"Couldn't find header '$0' in the `headers_to_target` map "
"derived from the --targets_and_headers cmdline argument",
header.IncludePath()));
}
return it->second;
}
Cmdline::Cmdline() = default;
} // namespace rs_bindings_from_cc