| // Copyright 2024 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <fstream> | |
| #include <iostream> | |
| #include <memory> | |
| #include <ostream> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/container/flat_hash_map.h" | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/log/die_if_null.h" | |
| #include "absl/strings/str_split.h" | |
| #include "absl/strings/string_view.h" | |
| #include "src/tools/one_version/allowlist.h" | |
| #include "src/tools/one_version/duplicate_class_collector.h" | |
| #include "src/tools/one_version/one_version.h" | |
| #include "src/tools/singlejar/input_jar.h" | |
| #include "src/tools/singlejar/token_stream.h" | |
| #include "src/tools/singlejar/zip_headers.h" | |
| // Scans a classpath and reports one version violations. | |
| // | |
| // usage: --output <file to touch> | |
| // --inputs <jar1,label1 jar2,label2 ... jarN,labelN> | |
| int main(int argc, char *argv[]) { | |
| std::string output_file; | |
| bool succeed_on_found_violations = false; | |
| std::string allowlist_file; | |
| std::vector<std::string> inputs; | |
| ArgTokenStream tokens(argc - 1, argv + 1); | |
| while (!tokens.AtEnd()) { | |
| if (tokens.MatchAndSet("--output", &output_file) || | |
| tokens.MatchAndSet("--succeed_on_found_violations", | |
| &succeed_on_found_violations) || | |
| tokens.MatchAndSet("--allowlist", &allowlist_file) || | |
| tokens.MatchAndSet("--inputs", &inputs)) { | |
| } else { | |
| std::cerr << "error: bad command line argument " << tokens.token() | |
| << std::endl; | |
| return 1; | |
| } | |
| } | |
| std::unique_ptr<one_version::Allowlist> allowlist; | |
| if (allowlist_file.empty()) { | |
| allowlist = std::make_unique<one_version::MapAllowlist>( | |
| absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>>()); | |
| } else { | |
| std::ifstream in(allowlist_file); | |
| if (!in) { | |
| std::cerr << "error: unable to open allowlist file: " << allowlist_file | |
| << std::endl; | |
| return 1; | |
| } | |
| absl::flat_hash_map<std::string, absl::flat_hash_set<std::string>> map; | |
| std::string line; | |
| while (std::getline(in, line)) { | |
| std::vector<std::string> parts = | |
| absl::StrSplit(line, absl::MaxSplits(' ', 1)); | |
| if (parts.size() != 2) { | |
| std::cerr << "error: expected <package> <label>, got: " << line | |
| << std::endl; | |
| return 1; | |
| } | |
| map[parts[0]].insert(parts[1]); | |
| } | |
| allowlist = std::make_unique<one_version::MapAllowlist>(std::move(map)); | |
| } | |
| one_version::OneVersion one_version(std::move(allowlist)); | |
| for (const std::string &input : inputs) { | |
| std::vector<std::string> pieces = absl::StrSplit(input, ','); | |
| if (pieces.size() != 2) { | |
| std::cerr << "error: expected <jar path>,<label>, got: " << input | |
| << std::endl; | |
| return 1; | |
| } | |
| std::string jar = pieces[0]; | |
| std::string label = pieces[1]; | |
| InputJar input_jar; | |
| if (!input_jar.Open(jar)) { | |
| std::cerr << "error: unable to open: " << jar << std::endl; | |
| return 1; | |
| } | |
| const CDH *dir_entry; | |
| const LH *local_header; | |
| while ((dir_entry = input_jar.NextEntry(&local_header))) { | |
| absl::string_view file_name(ABSL_DIE_IF_NULL(local_header)->file_name(), | |
| local_header->file_name_length()); | |
| one_version.Add(file_name, dir_entry, | |
| one_version::Label(label, jar, /*allowlisted=*/false)); | |
| } | |
| input_jar.Close(); | |
| } | |
| std::vector<one_version::Violation> violations = one_version.Report(); | |
| // Touch the output file, which exists because blaze actions are required to | |
| // have outputs but is always empty. | |
| std::ofstream o(output_file.c_str()); | |
| if (!violations.empty()) { | |
| // TODO(cushon): support other classpaths (runtime, android resources) | |
| std::cerr << "Found one definition violations on the runtime classpath:\n" | |
| << one_version::DuplicateClassCollector::Report(violations); | |
| return succeed_on_found_violations ? 0 : 1; | |
| } | |
| return 0; | |
| } |