blob: c22ed45980906e0d8f0f6a089a20cb73bda47be4 [file] [log] [blame]
// 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;
}