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");