Ignore `Multi-Release:` attributes in input jars PiperOrigin-RevId: 436332447
diff --git a/src/tools/singlejar/BUILD b/src/tools/singlejar/BUILD index 39ff13a..625f006 100644 --- a/src/tools/singlejar/BUILD +++ b/src/tools/singlejar/BUILD
@@ -117,11 +117,15 @@ ":zip_headers", ":zlib_interface", ], + data = [ + "data/multi_release.jar", + ], # Requires at least 5 GiB of memory exec_compatible_with = ["//:highcpu_machine"], deps = [ ":combiners", ":input_jar", + ":test_util", "//third_party/zlib", "@com_google_googletest//:gtest_main", ],
diff --git a/src/tools/singlejar/combiners.cc b/src/tools/singlejar/combiners.cc index fa10f76..ec84c27 100644 --- a/src/tools/singlejar/combiners.cc +++ b/src/tools/singlejar/combiners.cc
@@ -179,3 +179,44 @@ bool PropertyCombiner::Merge(const CDH * /*cdh*/, const LH * /*lh*/) { return false; // This should not be called. } + +ManifestCombiner::~ManifestCombiner() {} + +static const char *MULTI_RELEASE = "Multi-Release: true"; +static const size_t MULTI_RELEASE_LENGTH = strlen(MULTI_RELEASE); + +static const char *MULTI_RELEASE_PREFIX = "Multi-Release: "; +static const size_t MULTI_RELEASE_PREFIX_LENGTH = strlen(MULTI_RELEASE_PREFIX); + +void ManifestCombiner::AppendLine(const std::string &line) { + if (line.find(MULTI_RELEASE_PREFIX, 0, MULTI_RELEASE_PREFIX_LENGTH) != + std::string::npos) { + if (line.find("true", MULTI_RELEASE_PREFIX_LENGTH) != std::string::npos) { + multi_release_ = true; + } else if (line.find("false", MULTI_RELEASE_PREFIX_LENGTH) != + std::string::npos) { + multi_release_ = false; + } + return; + } + concatenator_->Append(line); + if (line[line.size() - 1] != '\n') { + concatenator_->Append("\r\n"); + } +} + +bool ManifestCombiner::Merge(const CDH *cdh, const LH *lh) { + // Ignore Multi-Release attributes in inputs: we write the manifest first, + // before inputs are processed, so we reply on deploy_manifest_lines to + // create Multi-Release jars instead of doing it automatically based on + // the inputs. + return true; +} + +void *ManifestCombiner::OutputEntry(bool compress) { + if (multi_release_) { + concatenator_->Append(MULTI_RELEASE); + } + concatenator_->Append("\r\n"); + return concatenator_->OutputEntry(compress); +}
diff --git a/src/tools/singlejar/combiners.h b/src/tools/singlejar/combiners.h index 28dbbad..a8e372e 100644 --- a/src/tools/singlejar/combiners.h +++ b/src/tools/singlejar/combiners.h
@@ -136,4 +136,28 @@ } }; +// Combines the contents of the multiple manifests. +class ManifestCombiner : public Combiner { + public: + ManifestCombiner(const std::string &filename) + : filename_(filename), multi_release_(false) { + concatenator_.reset(new Concatenator(filename_, false)); + } + ~ManifestCombiner() override; + + void AppendLine(const std::string &line); + + bool Merge(const CDH *cdh, const LH *lh) override; + + void *OutputEntry(bool compress) override; + + const std::string filename() const { return filename_; } + + private: + std::unique_ptr<Concatenator> concatenator_; + const std::string filename_; + bool multi_release_; + std::unique_ptr<Inflater> inflater_; +}; + #endif // SRC_TOOLS_SINGLEJAR_COMBINERS_H_
diff --git a/src/tools/singlejar/combiners_test.cc b/src/tools/singlejar/combiners_test.cc index 6efde7b..d5df6e5 100644 --- a/src/tools/singlejar/combiners_test.cc +++ b/src/tools/singlejar/combiners_test.cc
@@ -15,22 +15,30 @@ #include "src/tools/singlejar/combiners.h" #include "src/tools/singlejar/input_jar.h" +#include "src/tools/singlejar/test_util.h" #include "src/tools/singlejar/zip_headers.h" #include "src/tools/singlejar/zlib_interface.h" #include "googletest/include/gtest/gtest.h" namespace { +using bazel::tools::cpp::runfiles::Runfiles; + static const char kTag1Contents[] = "<tag1>Contents1</tag1>"; static const char kTag2Contents[] = "<tag2>Contents2</tag2>"; static const char kCombinedXmlContents[] = "<toplevel>\n<tag1>Contents1</tag1><tag2>Contents2</tag2></toplevel>\n"; static const char kConcatenatedContents[] = "<tag1>Contents1</tag1>\n<tag2>Contents2</tag2>"; +const char kCombinedManifestContents[] = "Multi-Release: true\r\n"; +const char kCombinedManifestContentsDisabled[] = "\r\n"; const uint8_t kPoison = 0xFA; // A test fixture is used because test case setup is needed. class CombinersTest : public ::testing::Test { + public: + void SetUp() override { runfiles.reset(Runfiles::CreateForTest()); } + protected: static void SetUpTestCase() { ASSERT_EQ(0, chdir(getenv("TEST_TMPDIR"))); @@ -51,6 +59,8 @@ } return true; } + + std::unique_ptr<Runfiles> runfiles; }; // Test Concatenator. @@ -246,4 +256,120 @@ free(reinterpret_cast<void *>(entry)); } +// Test ManifestCombiner. +TEST_F(CombinersTest, ManifestCombiner) { + InputJar input_jar; + ManifestCombiner manifest_combiner("META-INF/MANIFEST.MF"); + ASSERT_TRUE( + input_jar.Open(runfiles + ->Rlocation("io_bazel/src/tools/" + "singlejar/data/multi_release.jar") + .c_str())); + const LH *lh; + const CDH *cdh; + while ((cdh = input_jar.NextEntry(&lh))) { + if (cdh->file_name_is("META-INF/MANIFEST.MF")) { + ASSERT_TRUE(manifest_combiner.Merge(cdh, lh)); + } + } + + // check that Multi-Release is de-duped, e.g. if present both in deps and + // deploy_manifest_lines + manifest_combiner.AppendLine("Multi-Release: true"); + + // Create output, verify Local Header contents. + LH *entry = reinterpret_cast<LH *>(manifest_combiner.OutputEntry(true)); + EXPECT_TRUE(entry->is()); + EXPECT_EQ(20, entry->version()); + EXPECT_EQ(Z_NO_COMPRESSION, entry->compression_method()); + uint64_t original_size = entry->uncompressed_file_size(); + uint64_t compressed_size = entry->compressed_file_size(); + EXPECT_EQ(strlen(kCombinedManifestContents), original_size); + EXPECT_LE(compressed_size, original_size); + EXPECT_TRUE(entry->file_name_is("META-INF/MANIFEST.MF")); + EXPECT_EQ(0, entry->extra_fields_length()); + + // Check contents. + EXPECT_EQ( + kCombinedManifestContents, + std::string(reinterpret_cast<char *>(entry->data()), original_size)); + free(reinterpret_cast<void *>(entry)); +} + +// Test ManifestCombiner. +TEST_F(CombinersTest, ManifestCombinerJarOnly) { + InputJar input_jar; + ManifestCombiner manifest_combiner("META-INF/MANIFEST.MF"); + ASSERT_TRUE( + input_jar.Open(runfiles + ->Rlocation("io_bazel/src/tools/" + "singlejar/data/multi_release.jar") + .c_str())); + const LH *lh; + const CDH *cdh; + while ((cdh = input_jar.NextEntry(&lh))) { + if (cdh->file_name_is("META-INF/MANIFEST.MF")) { + ASSERT_TRUE(manifest_combiner.Merge(cdh, lh)); + } + } + + // check that Multi-Release is ignored if present only in deps + + // Create output, verify Local Header contents. + LH *entry = reinterpret_cast<LH *>(manifest_combiner.OutputEntry(true)); + EXPECT_TRUE(entry->is()); + EXPECT_EQ(20, entry->version()); + EXPECT_EQ(Z_NO_COMPRESSION, entry->compression_method()); + uint64_t original_size = entry->uncompressed_file_size(); + uint64_t compressed_size = entry->compressed_file_size(); + EXPECT_EQ(strlen(kCombinedManifestContentsDisabled), original_size); + EXPECT_LE(compressed_size, original_size); + EXPECT_TRUE(entry->file_name_is("META-INF/MANIFEST.MF")); + EXPECT_EQ(0, entry->extra_fields_length()); + + // Check contents. + EXPECT_EQ( + kCombinedManifestContentsDisabled, + std::string(reinterpret_cast<char *>(entry->data()), original_size)); + free(reinterpret_cast<void *>(entry)); +} + +TEST_F(CombinersTest, ManifestCombinerFalse) { + InputJar input_jar; + ManifestCombiner manifest_combiner("META-INF/MANIFEST.MF"); + ASSERT_TRUE( + input_jar.Open(runfiles + ->Rlocation("io_bazel/src/tools/" + "singlejar/data/multi_release.jar") + .c_str())); + const LH *lh; + const CDH *cdh; + while ((cdh = input_jar.NextEntry(&lh))) { + if (cdh->file_name_is("META-INF/MANIFEST.MF")) { + ASSERT_TRUE(manifest_combiner.Merge(cdh, lh)); + } + } + + // check that deploy_manifest_lines can disable the setting in input jars + manifest_combiner.AppendLine("Multi-Release: false"); + + // Create output, verify Local Header contents. + LH *entry = reinterpret_cast<LH *>(manifest_combiner.OutputEntry(true)); + EXPECT_TRUE(entry->is()); + EXPECT_EQ(20, entry->version()); + EXPECT_EQ(Z_NO_COMPRESSION, entry->compression_method()); + uint64_t original_size = entry->uncompressed_file_size(); + uint64_t compressed_size = entry->compressed_file_size(); + EXPECT_EQ(strlen(kCombinedManifestContentsDisabled), original_size); + EXPECT_LE(compressed_size, original_size); + EXPECT_TRUE(entry->file_name_is("META-INF/MANIFEST.MF")); + EXPECT_EQ(0, entry->extra_fields_length()); + + // Check contents. + EXPECT_EQ( + kCombinedManifestContentsDisabled, + std::string(reinterpret_cast<char *>(entry->data()), original_size)); + free(reinterpret_cast<void *>(entry)); +} + } // anonymous namespace
diff --git a/src/tools/singlejar/data/multi_release.jar b/src/tools/singlejar/data/multi_release.jar new file mode 100644 index 0000000..6976b3e --- /dev/null +++ b/src/tools/singlejar/data/multi_release.jar Binary files differ
diff --git a/src/tools/singlejar/output_jar.cc b/src/tools/singlejar/output_jar.cc index 147fcce..2b03d736 100644 --- a/src/tools/singlejar/output_jar.cc +++ b/src/tools/singlejar/output_jar.cc
@@ -72,9 +72,8 @@ known_members_.emplace(manifest_.filename(), EntryInfo{&manifest_}); known_members_.emplace(protobuf_meta_handler_.filename(), EntryInfo{&protobuf_meta_handler_}); - manifest_.Append( - "Manifest-Version: 1.0\r\n" - "Created-By: singlejar\r\n"); + manifest_.AppendLine("Manifest-Version: 1.0"); + manifest_.AppendLine("Created-By: singlejar"); } static std::string Basename(const std::string &path) { @@ -127,9 +126,7 @@ if (!options_->main_class.empty()) { build_properties_.AddProperty("main.class", options_->main_class); - manifest_.Append("Main-Class: "); - manifest_.Append(options_->main_class); - manifest_.Append("\r\n"); + manifest_.AppendLine("Main-Class: " + options_->main_class); } // Copy CDS archive file (.jsa) if it is set. @@ -139,10 +136,7 @@ for (auto &manifest_line : options_->manifest_lines) { if (!manifest_line.empty()) { - manifest_.Append(manifest_line); - if (manifest_line[manifest_line.size() - 1] != '\n') { - manifest_.Append("\r\n"); - } + manifest_.AppendLine(manifest_line); } } @@ -206,7 +200,6 @@ // First, write a directory entry for the META-INF, followed by the manifest // file, followed by the build properties file. WriteMetaInf(); - manifest_.Append("\r\n"); WriteEntry(manifest_.OutputEntry(compress)); if (!options_->exclude_build_data) { WriteEntry(build_properties_.OutputEntry(compress)); @@ -1056,8 +1049,7 @@ snprintf( cds_manifest_attr, sizeof(cds_manifest_attr), "Jsa-Offset: %ld", (long)aligned_offset); // NOLINT(runtime/int, // google-runtime-int) - manifest_.Append(cds_manifest_attr); - manifest_.Append("\r\n"); + manifest_.AppendLine(cds_manifest_attr); // Add to build_properties build_properties_.AddProperty("cds.archive",
diff --git a/src/tools/singlejar/output_jar.h b/src/tools/singlejar/output_jar.h index a7ceb35..21a93fa 100644 --- a/src/tools/singlejar/output_jar.h +++ b/src/tools/singlejar/output_jar.h
@@ -128,7 +128,7 @@ Concatenator spring_handlers_; Concatenator spring_schemas_; Concatenator protobuf_meta_handler_; - Concatenator manifest_; + ManifestCombiner manifest_; PropertyCombiner build_properties_; NullCombiner null_combiner_; std::vector<std::unique_ptr<Concatenator> > service_handlers_;