aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/ijar
diff options
context:
space:
mode:
authorGravatar tomlu <tomlu@google.com>2018-03-31 10:07:39 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-03-31 10:08:51 -0700
commitdf7731f13e58e719d0b0a703a0b53f2bc1e2d795 (patch)
tree0fc5736e046090f0a58dd05beb2f4d2eb2cd9012 /third_party/ijar
parentd5527767b08cbc77a0a71948dcf55ce7814ebbb0 (diff)
Make ijar support not stripping the class data.
This is done by adding a --[no]strip_classes flag to ijar. PiperOrigin-RevId: 191184258
Diffstat (limited to 'third_party/ijar')
-rw-r--r--third_party/ijar/ijar.cc292
-rw-r--r--third_party/ijar/test/BUILD49
-rw-r--r--third_party/ijar/test/IjarTests.java101
-rw-r--r--third_party/ijar/test/jar-with-manifest-and-target-label.jarbin0 -> 3338 bytes
-rw-r--r--third_party/ijar/test/jar-with-manifest.jarbin0 -> 3312 bytes
-rw-r--r--third_party/ijar/test/jar-without-manifest.jarbin0 -> 2952 bytes
6 files changed, 373 insertions, 69 deletions
diff --git a/third_party/ijar/ijar.cc b/third_party/ijar/ijar.cc
index 160645d7ae..a50b97037d 100644
--- a/third_party/ijar/ijar.cc
+++ b/third_party/ijar/ijar.cc
@@ -15,11 +15,11 @@
// ijar.cpp -- .jar -> _interface.jar tool.
//
+#include <errno.h>
+#include <limits.h>
#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
-#include <limits.h>
-#include <errno.h>
+#include <string.h>
#include <memory>
#include "third_party/ijar/zip.h"
@@ -31,9 +31,9 @@ bool verbose = false;
// Reads a JVM class from classdata_in (of the specified length), and
// writes out a simplified class to classdata_out, advancing the
// pointer. Returns true if the class should be kept.
-bool StripClass(u1*& classdata_out, const u1* classdata_in, size_t in_length);
+bool StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length);
-const char* CLASS_EXTENSION = ".class";
+const char *CLASS_EXTENSION = ".class";
const size_t CLASS_EXTENSION_LENGTH = strlen(CLASS_EXTENSION);
const char *MANIFEST_DIR_PATH = "META-INF/";
@@ -51,29 +51,34 @@ const size_t TARGET_LABEL_KEY_LENGTH = strlen(TARGET_LABEL_KEY);
const char *INJECTING_RULE_KIND_KEY = "Injecting-Rule-Kind: ";
const size_t INJECTING_RULE_KIND_KEY_LENGTH = strlen(INJECTING_RULE_KIND_KEY);
+class JarExtractorProcessor : public ZipExtractorProcessor {
+ public:
+ // Set the ZipBuilder to add the ijar class to the output zip file.
+ // This pointer should not be deleted while this class is still in use and
+ // it should be set before any call to the Process() method.
+ void SetZipBuilder(ZipBuilder *builder) { this->builder_ = builder; }
+ virtual void WriteManifest(const char *target_label,
+ const char *injecting_rule_kind) = 0;
+
+ protected:
+ // Not owned by JarStripperProcessor, see SetZipBuilder().
+ ZipBuilder *builder_;
+};
+
// ZipExtractorProcessor that select only .class file and use
// StripClass to generate an interface class, storing as a new file
// in the specified ZipBuilder.
-class JarStripperProcessor : public ZipExtractorProcessor {
+class JarStripperProcessor : public JarExtractorProcessor {
public:
JarStripperProcessor() {}
virtual ~JarStripperProcessor() {}
- virtual void Process(const char* filename, const u4 attr,
- const u1* data, const size_t size);
- virtual bool Accept(const char* filename, const u4 attr);
-
- private:
- // Not owned by JarStripperProcessor, see SetZipBuilder().
- ZipBuilder* builder;
+ virtual void Process(const char *filename, const u4 attr, const u1 *data,
+ const size_t size);
+ virtual bool Accept(const char *filename, const u4 attr);
- public:
- // Set the ZipBuilder to add the ijar class to the output zip file.
- // This pointer should not be deleted while this class is still in use and
- // it should be set before any call to the Process() method.
- void SetZipBuilder(ZipBuilder* builder) {
- this->builder = builder;
- }
+ virtual void WriteManifest(const char *target_label,
+ const char *injecting_rule_kind);
};
bool JarStripperProcessor::Accept(const char *filename, const u4 /*attr*/) {
@@ -86,8 +91,8 @@ bool JarStripperProcessor::Accept(const char *filename, const u4 /*attr*/) {
return true;
}
-static bool IsModuleInfo(const char* filename) {
- const char* slash = strrchr(filename, '/');
+static bool IsModuleInfo(const char *filename) {
+ const char *slash = strrchr(filename, '/');
if (slash == NULL) {
slash = filename;
} else {
@@ -102,36 +107,195 @@ void JarStripperProcessor::Process(const char *filename, const u4 /*attr*/,
fprintf(stderr, "INFO: StripClass: %s\n", filename);
}
if (IsModuleInfo(filename)) {
- u1* q = builder->NewFile(filename, 0);
+ u1 *q = builder_->NewFile(filename, 0);
memcpy(q, data, size);
- builder->FinishFile(size, false, true);
+ builder_->FinishFile(size, /* compress: */ false, /* compute_crc: */ true);
} else {
- u1* buf = reinterpret_cast<u1*>(malloc(size));
- u1* classdata_out = buf;
+ u1 *buf = reinterpret_cast<u1 *>(malloc(size));
+ u1 *classdata_out = buf;
if (!StripClass(buf, data, size)) {
free(classdata_out);
return;
}
- u1* q = builder->NewFile(filename, 0);
+ u1 *q = builder_->NewFile(filename, 0);
size_t out_length = buf - classdata_out;
memcpy(q, classdata_out, out_length);
- builder->FinishFile(out_length, false, true);
+ builder_->FinishFile(out_length, /* compress: */ false,
+ /* compute_crc: */ true);
free(classdata_out);
}
}
-// Copies the string into the buffer without the null terminator, returns length
-static size_t WriteStr(u1 *buf, const char *str) {
+// Copies the string into the buffer without the null terminator, returns
+// updated buffer pointer
+static u1 *WriteStr(u1 *buf, const char *str) {
size_t len = strlen(str);
memcpy(buf, str, len);
- return len;
+ return buf + len;
+}
+
+// Writes a manifest attribute including a "\r\n" line break, returns updated
+// buffer pointer.
+static u1 *WriteManifestAttr(u1 *buf, const char *key, const char *val) {
+ buf = WriteStr(buf, key);
+ buf = WriteStr(buf, val);
+ *buf++ = '\r';
+ *buf++ = '\n';
+ return buf;
+}
+
+void JarStripperProcessor::WriteManifest(const char *target_label,
+ const char *injecting_rule_kind) {
+ if (target_label == nullptr) {
+ return;
+ }
+ builder_->WriteEmptyFile(MANIFEST_DIR_PATH);
+ u1 *start = builder_->NewFile(MANIFEST_PATH, 0);
+ u1 *buf = start;
+ buf = WriteStr(buf, MANIFEST_HEADER);
+ buf = WriteManifestAttr(buf, TARGET_LABEL_KEY, target_label);
+ if (injecting_rule_kind) {
+ buf = WriteManifestAttr(buf, INJECTING_RULE_KIND_KEY, injecting_rule_kind);
+ }
+ size_t total_len = buf - start;
+ builder_->FinishFile(total_len, /* compress: */ false,
+ /* compute_crc: */ true);
+}
+
+class JarCopierProcessor : public JarExtractorProcessor {
+ public:
+ JarCopierProcessor(const char *jar) : jar_(jar) {}
+ virtual ~JarCopierProcessor() {}
+
+ virtual void Process(const char *filename, const u4 /*attr*/, const u1 *data,
+ const size_t size);
+ virtual bool Accept(const char *filename, const u4 /*attr*/);
+
+ virtual void WriteManifest(const char *target_label,
+ const char *injecting_rule_kind);
+
+ private:
+ class ManifestLocator : public ZipExtractorProcessor {
+ public:
+ ManifestLocator() : manifest_buf_(nullptr), manifest_size_(0) {}
+ virtual ~ManifestLocator() { free(manifest_buf_); }
+
+ u1 *manifest_buf_;
+ size_t manifest_size_;
+
+ virtual bool Accept(const char *filename, const u4 /*attr*/) {
+ return strcmp(filename, MANIFEST_PATH) == 0;
+ }
+
+ virtual void Process(const char * /*filename*/, const u4 /*attr*/,
+ const u1 *data, const size_t size) {
+ manifest_buf_ = (u1 *)malloc(size);
+ memmove(manifest_buf_, data, size);
+ manifest_size_ = size;
+ }
+ };
+
+ const char *jar_;
+
+ u1 *AppendTargetLabelToManifest(u1 *buf, const u1 *manifest_data,
+ const size_t size, const char *target_label,
+ const char *injecting_rule_kind);
+};
+
+void JarCopierProcessor::Process(const char *filename, const u4 /*attr*/,
+ const u1 *data, const size_t size) {
+ if (verbose) {
+ fprintf(stderr, "INFO: CopyFile: %s\n", filename);
+ }
+ // We already handled the manifest in WriteManifest
+ if (strcmp(filename, MANIFEST_DIR_PATH) == 0 ||
+ strcmp(filename, MANIFEST_PATH) == 0) {
+ return;
+ }
+ u1 *q = builder_->NewFile(filename, 0);
+ memcpy(q, data, size);
+ builder_->FinishFile(size, /* compress: */ false, /* compute_crc: */ true);
+}
+
+bool JarCopierProcessor::Accept(const char * /*filename*/, const u4 /*attr*/) {
+ return true;
+}
+
+void JarCopierProcessor::WriteManifest(const char *target_label,
+ const char *injecting_rule_kind) {
+ ManifestLocator manifest_locator;
+ std::unique_ptr<ZipExtractor> in(
+ ZipExtractor::Create(jar_, &manifest_locator));
+ in->ProcessAll();
+
+ bool wants_manifest =
+ manifest_locator.manifest_buf_ != nullptr || target_label != nullptr;
+ if (wants_manifest) {
+ builder_->WriteEmptyFile(MANIFEST_DIR_PATH);
+ u1 *start = builder_->NewFile(MANIFEST_PATH, 0);
+ u1 *buf = start;
+ // Three cases:
+ // 1. We need to merge the target label into a pre-existing manifest
+ // 2. Write a manifest from scratch with a target label
+ // 3. Copy existing manifest without adding target label
+ if (manifest_locator.manifest_buf_ != nullptr && target_label != nullptr) {
+ buf = AppendTargetLabelToManifest(buf, manifest_locator.manifest_buf_,
+ manifest_locator.manifest_size_,
+ target_label, injecting_rule_kind);
+ } else if (target_label != nullptr) {
+ buf = WriteStr(buf, MANIFEST_HEADER);
+ buf = WriteManifestAttr(buf, TARGET_LABEL_KEY, target_label);
+ if (injecting_rule_kind) {
+ buf = WriteManifestAttr(buf, INJECTING_RULE_KIND_KEY,
+ injecting_rule_kind);
+ }
+ } else {
+ memcpy(buf, manifest_locator.manifest_buf_,
+ manifest_locator.manifest_size_);
+ buf += manifest_locator.manifest_size_;
+ }
+
+ size_t total_len = buf - start;
+ builder_->FinishFile(total_len, /* compress: */ false,
+ /* compute_crc: */ true);
+ }
+}
+
+u1 *JarCopierProcessor::AppendTargetLabelToManifest(
+ u1 *buf, const u1 *manifest_data, const size_t size,
+ const char *target_label, const char *injecting_rule_kind) {
+ const char *line_start = (const char *)manifest_data;
+ const char *data_end = (const char *)manifest_data + size;
+ while (line_start < data_end) {
+ const char *line_end = strchr(line_start, '\n');
+ // Go past return char to point to next line, or to end of data buffer
+ line_end = line_end != nullptr ? line_end + 1 : data_end;
+
+ // Copy line unless it's Target-Label/Injecting-Rule-Kind and we're writing
+ // that ourselves
+ if (strncmp(line_start, TARGET_LABEL_KEY, TARGET_LABEL_KEY_LENGTH) != 0 &&
+ strncmp(line_start, INJECTING_RULE_KIND_KEY,
+ INJECTING_RULE_KIND_KEY_LENGTH) != 0) {
+ size_t len = line_end - line_start;
+ // Skip empty lines
+ if (len > 0 && line_start[0] != '\r' && line_start[0] != '\n') {
+ memcpy(buf, line_start, len);
+ buf += len;
+ }
+ }
+ line_start = line_end;
+ }
+ buf = WriteManifestAttr(buf, TARGET_LABEL_KEY, target_label);
+ if (injecting_rule_kind != nullptr) {
+ buf = WriteManifestAttr(buf, INJECTING_RULE_KIND_KEY, injecting_rule_kind);
+ }
+ return buf;
}
-// Computes the size of zip file content for the manifest created by
// WriteManifest, including zip file format overhead.
static size_t EstimateManifestOutputSize(const char *target_label,
const char *injecting_rule_kind) {
- if (target_label == NULL) {
+ if (target_label == nullptr) {
return 0;
}
// local headers
@@ -152,36 +316,21 @@ static size_t EstimateManifestOutputSize(const char *target_label,
return length;
}
-static void WriteManifest(ZipBuilder *out, const char *target_label,
- const char *injecting_rule_kind) {
- if (target_label == NULL) {
- return;
- }
- out->WriteEmptyFile(MANIFEST_DIR_PATH);
- u1 *start = out->NewFile(MANIFEST_PATH, 0);
- u1 *buf = start;
- buf += WriteStr(buf, MANIFEST_HEADER);
- buf += WriteStr(buf, TARGET_LABEL_KEY);
- buf += WriteStr(buf, target_label);
- *buf++ = '\r';
- *buf++ = '\n';
- if (injecting_rule_kind) {
- buf += WriteStr(buf, INJECTING_RULE_KIND_KEY);
- buf += WriteStr(buf, injecting_rule_kind);
- *buf++ = '\r';
- *buf++ = '\n';
- }
- size_t total_len = buf - start;
- out->FinishFile(total_len);
-}
-
// Opens "file_in" (a .jar file) for reading, and writes an interface
// .jar to "file_out".
static void OpenFilesAndProcessJar(const char *file_out, const char *file_in,
- const char *target_label,
+ bool strip_jar, const char *target_label,
const char *injecting_rule_kind) {
- JarStripperProcessor processor;
- std::unique_ptr<ZipExtractor> in(ZipExtractor::Create(file_in, &processor));
+ std::unique_ptr<JarExtractorProcessor> processor;
+ if (strip_jar) {
+ processor =
+ std::unique_ptr<JarExtractorProcessor>(new JarStripperProcessor());
+ } else {
+ processor =
+ std::unique_ptr<JarExtractorProcessor>(new JarCopierProcessor(file_in));
+ }
+ std::unique_ptr<ZipExtractor> in(
+ ZipExtractor::Create(file_in, processor.get()));
if (in.get() == NULL) {
fprintf(stderr, "Unable to open Zip file %s: %s\n", file_in,
strerror(errno));
@@ -196,9 +345,8 @@ static void OpenFilesAndProcessJar(const char *file_out, const char *file_in,
strerror(errno));
abort();
}
- processor.SetZipBuilder(out.get());
-
- WriteManifest(out.get(), target_label, injecting_rule_kind);
+ processor->SetZipBuilder(out.get());
+ processor->WriteManifest(target_label, injecting_rule_kind);
// Process all files in the zip
if (in->ProcessAll() < 0) {
@@ -219,9 +367,8 @@ static void OpenFilesAndProcessJar(const char *file_out, const char *file_in,
size_t in_length = in->GetSize();
size_t out_length = out->GetSize();
if (verbose) {
- fprintf(stderr, "INFO: produced interface jar: %s -> %s (%d%%).\n",
- file_in, file_out,
- static_cast<int>(100.0 * out_length / in_length));
+ fprintf(stderr, "INFO: produced interface jar: %s -> %s (%d%%).\n", file_in,
+ file_out, static_cast<int>(100.0 * out_length / in_length));
}
}
} // namespace devtools_ijar
@@ -232,13 +379,15 @@ static void OpenFilesAndProcessJar(const char *file_out, const char *file_in,
static void usage() {
fprintf(stderr,
"Usage: ijar "
- "[-v] [--target label label] [--injecting_rule_kind kind] "
+ "[-v] [--[no]strip_jar] "
+ "[--target label label] [--injecting_rule_kind kind] "
"x.jar [x_interface.jar>]\n");
fprintf(stderr, "Creates an interface jar from the specified jar file.\n");
exit(1);
}
int main(int argc, char **argv) {
+ bool strip_jar = true;
const char *target_label = NULL;
const char *injecting_rule_kind = NULL;
const char *filename_in = NULL;
@@ -247,6 +396,10 @@ int main(int argc, char **argv) {
for (int ii = 1; ii < argc; ++ii) {
if (strcmp(argv[ii], "-v") == 0) {
devtools_ijar::verbose = true;
+ } else if (strcmp(argv[ii], "--strip_jar") == 0) {
+ strip_jar = true;
+ } else if (strcmp(argv[ii], "--nostrip_jar") == 0) {
+ strip_jar = false;
} else if (strcmp(argv[ii], "--target_label") == 0) {
if (++ii >= argc) {
usage();
@@ -279,7 +432,8 @@ int main(int argc, char **argv) {
strcpy(filename_out_buf + len - 4, "-interface.jar");
filename_out = filename_out_buf;
} else {
- fprintf(stderr, "Can't determine output filename since input filename "
+ fprintf(stderr,
+ "Can't determine output filename since input filename "
"doesn't end with '.jar'.\n");
return 1;
}
@@ -289,7 +443,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "INFO: writing to '%s'.\n", filename_out);
}
- devtools_ijar::OpenFilesAndProcessJar(filename_out, filename_in, target_label,
- injecting_rule_kind);
+ devtools_ijar::OpenFilesAndProcessJar(filename_out, filename_in, strip_jar,
+ target_label, injecting_rule_kind);
return 0;
}
diff --git a/third_party/ijar/test/BUILD b/third_party/ijar/test/BUILD
index 62b6edd3a3..816ae1221d 100644
--- a/third_party/ijar/test/BUILD
+++ b/third_party/ijar/test/BUILD
@@ -110,6 +110,46 @@ genrule(
)
genrule(
+ name = "interface_ijar_testlib_nostrip",
+ srcs = [":ijar_testlib"],
+ outs = ["interface_ijar_testlib_nostrip.jar"],
+ cmd = "$(location //third_party/ijar) --target_label //foo:foo --nostrip_jar $< $@",
+ tools = ["//third_party/ijar"],
+)
+
+genrule(
+ name = "jar_with_manifest_nostrip",
+ srcs = ["jar-with-manifest.jar"],
+ outs = ["jar-with-manifest-nostrip.jar"],
+ cmd = "$(location //third_party/ijar) --target_label //foo:foo --nostrip_jar $< $@",
+ tools = ["//third_party/ijar"],
+)
+
+genrule(
+ name = "jar_with_manifest_and_target_label_nostrip",
+ srcs = ["jar-with-manifest-and-target-label.jar"],
+ outs = ["jar-with-manifest-and-target-label-nostrip.jar"],
+ cmd = "$(location //third_party/ijar) --target_label //foo:foo --nostrip_jar $< $@",
+ tools = ["//third_party/ijar"],
+)
+
+genrule(
+ name = "jar_without_manifest_nostrip",
+ srcs = ["jar-without-manifest.jar"],
+ outs = ["jar-without-manifest-nostrip.jar"],
+ cmd = "$(location //third_party/ijar) --target_label //foo:foo --nostrip_jar $< $@",
+ tools = ["//third_party/ijar"],
+)
+
+genrule(
+ name = "jar_without_manifest_nostrip_idempotence",
+ srcs = ["jar-without-manifest-nostrip.jar"],
+ outs = ["jar-without-manifest-nostrip-idempotence.jar"],
+ cmd = "$(location //third_party/ijar) --target_label //foo:foo --nostrip_jar $< $@",
+ tools = ["//third_party/ijar"],
+)
+
+genrule(
name = "empty_with_target_label",
srcs = [":empty_zip.jar"],
outs = ["empty_with_target_label.jar"],
@@ -194,10 +234,19 @@ java_test(
"PrivateNestedClass.java",
"UseDeprecatedParts.java",
"UseRestrictedAnnotation.java",
+ "jar-with-manifest.jar",
+ "jar-with-manifest-and-target-label.jar",
+ "jar-without-manifest.jar",
"package-info.java",
":empty_with_target_label",
+ ":ijar_testlib",
":interface_ijar_testlib",
+ ":interface_ijar_testlib_nostrip",
":interface_ijar_testlib_with_target_label",
+ ":jar_with_manifest_and_target_label_nostrip",
+ ":jar_with_manifest_nostrip",
+ ":jar_without_manifest_nostrip",
+ ":jar_without_manifest_nostrip_idempotence",
":liblocal_and_anonymous_lib.jar",
":local_and_anonymous-interface.jar",
":module_info-interface.jar",
diff --git a/third_party/ijar/test/IjarTests.java b/third_party/ijar/test/IjarTests.java
index f88616f469..f8e487ff64 100644
--- a/third_party/ijar/test/IjarTests.java
+++ b/third_party/ijar/test/IjarTests.java
@@ -25,6 +25,9 @@ import com.google.common.io.ByteStreams;
import com.google.devtools.build.java.bazel.BazelJavaCompiler;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
@@ -309,4 +312,102 @@ public class IjarTests {
LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneOffset.systemDefault()).toInstant());
}
}
+
+ // Tests --nostrip_jar with a jar that already has a manifest, but no target label
+ @Test
+ public void testNoStripJarWithManifest() throws Exception {
+ JarFile original = new JarFile("third_party/ijar/test/jar-with-manifest.jar");
+ JarFile stripped = new JarFile("third_party/ijar/test/jar-with-manifest-nostrip.jar");
+ try {
+ ImmutableList<String> strippedEntries =
+ stripped.stream().map(JarEntry::getName).collect(toImmutableList());
+ assertThat(strippedEntries.get(0)).isEqualTo("META-INF/");
+ assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF");
+ Manifest manifest = stripped.getManifest();
+ Attributes attributes = manifest.getMainAttributes();
+ assertThat(attributes.getValue("Manifest-Version")).isEqualTo("1.0");
+ // Created-By was already in manifest, doesn't get overwritten
+ assertThat(attributes.getValue("Created-By")).isEqualTo("test-code");
+ assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo");
+ assertNonManifestFilesBitIdentical(original, stripped);
+ } finally {
+ original.close();
+ stripped.close();
+ }
+ }
+
+ // Tests --nostrip_jar with a jar that already has a manifest with a target label
+ @Test
+ public void testNoStripJarWithManifestAndTargetLabel() throws Exception {
+ JarFile original = new JarFile("third_party/ijar/test/jar-with-manifest-and-target-label.jar");
+ JarFile stripped =
+ new JarFile("third_party/ijar/test/jar-with-manifest-and-target-label-nostrip.jar");
+ try {
+ ImmutableList<String> strippedEntries =
+ stripped.stream().map(JarEntry::getName).collect(toImmutableList());
+ assertThat(strippedEntries.get(0)).isEqualTo("META-INF/");
+ assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF");
+ Manifest manifest = stripped.getManifest();
+ Attributes attributes = manifest.getMainAttributes();
+ assertThat(attributes.getValue("Manifest-Version")).isEqualTo("1.0");
+ // Created-By was already in manifest, doesn't get overwritten
+ assertThat(attributes.getValue("Created-By")).isEqualTo("test-code");
+ assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo");
+ assertNonManifestFilesBitIdentical(original, stripped);
+ } finally {
+ original.close();
+ stripped.close();
+ }
+ }
+
+ // Tests --nostrip_jar with a jar that didn't already have a manifest
+ @Test
+ public void testNoStripJarWithoutManifest() throws Exception {
+ JarFile original = new JarFile("third_party/ijar/test/jar-without-manifest.jar");
+ JarFile stripped = new JarFile("third_party/ijar/test/jar-without-manifest-nostrip.jar");
+ try {
+ ImmutableList<String> strippedEntries =
+ stripped.stream().map(JarEntry::getName).collect(toImmutableList());
+ assertThat(strippedEntries.get(0)).isEqualTo("META-INF/");
+ assertThat(strippedEntries.get(1)).isEqualTo("META-INF/MANIFEST.MF");
+ Manifest manifest = stripped.getManifest();
+ Attributes attributes = manifest.getMainAttributes();
+ assertThat(attributes.getValue("Manifest-Version")).isEqualTo("1.0");
+ assertThat(attributes.getValue("Created-By")).isEqualTo("bazel");
+ assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo");
+ assertNonManifestFilesBitIdentical(original, stripped);
+ } finally {
+ original.close();
+ stripped.close();
+ }
+ }
+
+ // Tests idempotence of --nostrip
+ @Test
+ public void testNoStripIdempotence() throws Exception {
+ byte[] original =
+ Files.readAllBytes(Paths.get("third_party/ijar/test/jar-without-manifest-nostrip.jar"));
+ byte[] stripped =
+ Files.readAllBytes(
+ Paths.get("third_party/ijar/test/jar-without-manifest-nostrip-idempotence.jar"));
+ assertThat(original).isEqualTo(stripped);
+ }
+
+ private static void assertNonManifestFilesBitIdentical(JarFile original, JarFile stripped)
+ throws IOException {
+ // Make sure that all other files came across bitwise equal
+ for (String classEntry :
+ original
+ .stream()
+ .map(JarEntry::getName)
+ .filter(name -> !name.equals("META-INF/MANIFEST.MF"))
+ .collect(toImmutableList())) {
+ ZipEntry originalEntry = original.getEntry(classEntry);
+ ZipEntry strippedEntry = stripped.getEntry(classEntry);
+ InputStream originalStream = original.getInputStream(originalEntry);
+ InputStream strippedStream = stripped.getInputStream(strippedEntry);
+ assertThat(ByteStreams.toByteArray(strippedStream))
+ .isEqualTo(ByteStreams.toByteArray(originalStream));
+ }
+ }
}
diff --git a/third_party/ijar/test/jar-with-manifest-and-target-label.jar b/third_party/ijar/test/jar-with-manifest-and-target-label.jar
new file mode 100644
index 0000000000..aab7c701f2
--- /dev/null
+++ b/third_party/ijar/test/jar-with-manifest-and-target-label.jar
Binary files differ
diff --git a/third_party/ijar/test/jar-with-manifest.jar b/third_party/ijar/test/jar-with-manifest.jar
new file mode 100644
index 0000000000..7191935efb
--- /dev/null
+++ b/third_party/ijar/test/jar-with-manifest.jar
Binary files differ
diff --git a/third_party/ijar/test/jar-without-manifest.jar b/third_party/ijar/test/jar-without-manifest.jar
new file mode 100644
index 0000000000..d2bd18d006
--- /dev/null
+++ b/third_party/ijar/test/jar-without-manifest.jar
Binary files differ