Create parent directories for resources This makes singlejar's handling of resources consistent with JavaBuilder, and allows the JavaBuilder resource handling to be replaced by a singlejar action that adds resources to library jars separately from compilation (see unknown commit). PiperOrigin-RevId: 152082884
diff --git a/src/tools/singlejar/output_jar.cc b/src/tools/singlejar/output_jar.cc index 26f445f..1b19de8 100644 --- a/src/tools/singlejar/output_jar.cc +++ b/src/tools/singlejar/output_jar.cc
@@ -220,6 +220,17 @@ } } } + + // Add parent directory entries. + size_t pos = classpath_resource->filename().find('/'); + while (pos != std::string::npos) { + std::string dir(classpath_resource->filename(), 0, pos + 1); + if (NewEntry(dir)) { + WriteDirEntry(dir, nullptr, 0); + } + pos = classpath_resource->filename().find('/', pos + 1); + } + WriteEntry(classpath_resource->OutputEntry(do_compress)); } @@ -580,8 +591,7 @@ } void OutputJar::WriteMetaInf() { - const char path[] = "META-INF/"; - size_t n_path = strlen(path); + std::string path("META-INF/"); // META_INF/ is always the first entry, and as such it should have an extra // field with the tag 0xCAFE and zero bytes of data. This is not the part of @@ -590,7 +600,14 @@ const uint8_t extra_fields[] = {0xFE, 0xCA, 0, 0}; const uint16_t n_extra_fields = sizeof(extra_fields) / sizeof(extra_fields[0]); - size_t lh_size = sizeof(LH) + n_path + n_extra_fields; + WriteDirEntry(path, extra_fields, n_extra_fields); +} + +// Writes a directory entry with the given name and extra fields. +void OutputJar::WriteDirEntry(const std::string &name, + const uint8_t *extra_fields, + const uint16_t n_extra_fields) { + size_t lh_size = sizeof(LH) + name.size() + n_extra_fields; LH *lh = reinterpret_cast<LH *>(malloc(lh_size)); lh->signature(); lh->version(20); // 2.0 @@ -599,9 +616,9 @@ lh->crc32(0); lh->compressed_file_size32(0); lh->uncompressed_file_size32(0); - lh->file_name(path, n_path); + lh->file_name(name.c_str(), name.size()); lh->extra_fields(extra_fields, n_extra_fields); - known_members_.emplace(path, EntryInfo{&null_combiner_}); + known_members_.emplace(name, EntryInfo{&null_combiner_}); WriteEntry(lh); }
diff --git a/src/tools/singlejar/output_jar.h b/src/tools/singlejar/output_jar.h index f5ee200..53f6978 100644 --- a/src/tools/singlejar/output_jar.h +++ b/src/tools/singlejar/output_jar.h
@@ -73,6 +73,9 @@ void WriteEntry(void *local_header_and_payload); // Write META_INF/ entry (the first entry on output). void WriteMetaInf(); + // Write a directory entry. + void WriteDirEntry(const std::string &name, const uint8_t *extra_fields, + const uint16_t n_extra_fields); // Create output Central Directory Header for the given input entry and // append it to CEN (Central Directory) buffer. void AppendToDirectoryBuffer(const CDH *cdh, off_t local_header_offset,
diff --git a/src/tools/singlejar/output_jar_simple_test.cc b/src/tools/singlejar/output_jar_simple_test.cc index ef71fba..036b883 100644 --- a/src/tools/singlejar/output_jar_simple_test.cc +++ b/src/tools/singlejar/output_jar_simple_test.cc
@@ -320,6 +320,37 @@ EXPECT_EQ("res2.line1\nres2.line2\n", res2); } +TEST_F(OutputJarSimpleTest, ResourcesParentDirectories) { + string res1_path = CreateTextFile("res1", "res1.line1\nres1.line2\n"); + string res2_path = CreateTextFile("res2", "res2.line1\nres2.line2\n"); + + string out_path = OutputFilePath("out.jar"); + CreateOutput(out_path, {"--exclude_build_data", "--resources", + res1_path + ":the/resources/res1", + res2_path + ":the/resources2/res2"}); + + string res1 = GetEntryContents(out_path, "the/resources/res1"); + EXPECT_EQ("res1.line1\nres1.line2\n", res1); + + string res2 = GetEntryContents(out_path, "the/resources2/res2"); + EXPECT_EQ("res2.line1\nres2.line2\n", res2); + + // The output should contain entries for parent directories + std::vector<string> expected_entries( + {"META-INF/", "META-INF/MANIFEST.MF", "the/", "the/resources/", + "the/resources/res1", "the/resources2/", "the/resources2/res2"}); + std::vector<string> jar_entries; + InputJar input_jar; + ASSERT_TRUE(input_jar.Open(out_path)); + const LH *lh; + const CDH *cdh; + while ((cdh = input_jar.NextEntry(&lh))) { + jar_entries.push_back(cdh->file_name_string()); + } + input_jar.Close(); + EXPECT_EQ(expected_entries, jar_entries); +} + // --classpath_resources TEST_F(OutputJarSimpleTest, ClasspathResources) { string res1_path = OutputFilePath("cp_res");