Merge changes I1de10391,I4399ca26,Iec3b2b0b am: bff3c9b4c1

am: bae8872294

* commit 'bae88722945130a0c8ebbb1ef27eaa845e9f03fd':
  Use libstdc++ for ijar
  Build ijar for apps build
  Use .KATI_RESTAT to reduce unnecessary rebuilds of .jar files
This commit is contained in:
Colin Cross 2015-12-01 00:05:38 +00:00 committed by android-build-merger
commit dbeab8de65
16 changed files with 3976 additions and 14 deletions

View File

@ -223,25 +223,15 @@ LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE)
###########################################################
## Create .toc files from shared objects to reduce unnecessary rebuild
# .toc files have the list of external dynamic symbols without their addresses.
# For ninja build, .toc files will be updated only when the content of .toc
# files are changed. As .KATI_RESTAT is specified to .toc files, dependent
# binaries of a .toc file will be rebuilt only when the content of
# As .KATI_RESTAT is specified to .toc files and commit-change-for-toc is used,
# dependent binaries of a .toc file will be rebuilt only when the content of
# the .toc file is changed.
###########################################################
ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)
LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE).toc
$(LOCAL_BUILT_MODULE).toc: $(LOCAL_BUILT_MODULE)
ifeq ($(BUILDING_WITH_NINJA),true)
$(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@.tmp)
$(hide) if cmp -s $@.tmp $@ ; then \
rm $@.tmp ; \
else \
mv $@.tmp $@ ; \
fi
else
@# make doesn't support restat. We always update .toc files so the dependents will always be updated too.
$(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@)
endif
$(call commit-change-for-toc,$@)
# Kati adds restat=1 to ninja. GNU make does nothing for this.
.KATI_RESTAT: $(LOCAL_BUILT_MODULE).toc

View File

@ -540,6 +540,10 @@ ifndef TARGET_BUILD_APPS
ZIPTIME := $(HOST_OUT_EXECUTABLES)/ziptime$(HOST_EXECUTABLE_SUFFIX)
endif
# ijar converts a .jar file to a smaller .jar file which only has its
# interfaces.
IJAR := $(HOST_OUT_EXECUTABLES)/ijar$(BUILD_EXECUTABLE_SUFFIX)
# relocation packer
RELOCATION_PACKER := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/relocation_packer/relocation_packer

View File

@ -1928,6 +1928,42 @@ define transform-jar-to-jack
$(hide) rm $@.tmpjill.jack
endef
# Moves $1.tmp to $1 if necessary. This is designed to be used with
# .KATI_RESTAT. For kati, this function doesn't update the timestamp
# of $1 when $1.tmp is identical to $1 so that ninja won't rebuild
# targets which depend on $1. For GNU make, this function simply
# copies $1.tmp to $1.
ifeq ($(BUILDING_WITH_NINJA),true)
define commit-change-for-toc
$(hide) if cmp -s $1.tmp $1 ; then \
rm $1.tmp ; \
else \
mv $1.tmp $1 ; \
fi
endef
else
define commit-change-for-toc
@# make doesn't support restat. We always update .toc files so the dependents will always be updated too.
$(hide) mv $1.tmp $1
endef
endif
## Rule to creates a table of contents from a .jar file.
## Must be called with $(eval).
# $1: A .jar file
define _transform-jar-to-toc
$1.toc: $1 | $(IJAR)
@echo Generating TOC: $$@
$(hide) $(IJAR) $$< $$@.tmp
$$(call commit-change-for-toc,$$@)
endef
## Define a rule which generates .jar.toc and mark it as .KATI_RESTAT.
define define-jar-to-toc-rule
$(eval $(call _transform-jar-to-toc,$1))
$(eval .KATI_RESTAT: $1.toc)
endef
# Invoke Jack to compile java from source to jack files without shrink or obfuscation.
#

View File

@ -456,6 +456,8 @@ $(full_classes_jar): $(full_classes_emma_jar) | $(ACP)
@echo Copying: $@
$(hide) $(ACP) -fp $< $@
$(call define-jar-to-toc-rule, $(full_classes_jar))
# Run proguard if necessary, otherwise just copy the file.
ifdef LOCAL_PROGUARD_ENABLED
ifneq ($(filter-out full custom nosystem obfuscation optimization shrinktests,$(LOCAL_PROGUARD_ENABLED)),)

View File

@ -147,6 +147,7 @@ endif # LOCAL_SDK_VERSION
full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
full_java_lib_deps := $(call java-lib-deps,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
full_java_lib_deps := $(addsuffix .toc, $(full_java_lib_deps))
else # LOCAL_IS_HOST_MODULE

View File

@ -315,6 +315,8 @@ $(common_classes_jar) : $(my_src_jar) | $(ACP)
$(common_javalib_jar) : $(common_classes_jar) | $(ACP)
$(transform-prebuilt-to-target)
$(call define-jar-to-toc-rule, $(common_classes_jar))
# make sure the classes.jar and javalib.jar are built before $(LOCAL_BUILT_MODULE)
$(built_module) : $(common_javalib_jar)
endif # TARGET JAVA_LIBRARIES

View File

@ -27,6 +27,6 @@ endif # PDK
else # TARGET_BUILD_APPS
include $(LOCAL_PATH)/apicheck/Android.mk
include $(LOCAL_PATH)/apicheck/Android.mk $(LOCAL_PATH)/ijar/Android.mk
endif

16
tools/ijar/Android.mk Normal file
View File

@ -0,0 +1,16 @@
# Copyright 2015 The Android Open Source Project
#
# The rest of files in this directory comes from
# https://github.com/bazelbuild/bazel/tree/master/third_party/ijar
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := classfile.cc ijar.cc zip.cc
LOCAL_CFLAGS += -Wall
LOCAL_SHARED_LIBRARIES := libz-host
LOCAL_MODULE := ijar
# libc++ is not supported for TARGET_BUILD_APPS builds
LOCAL_CXX_STL := libstdc++
include $(BUILD_HOST_EXECUTABLE)

203
tools/ijar/LICENSE Normal file
View File

@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

120
tools/ijar/README.txt Normal file
View File

@ -0,0 +1,120 @@
ijar: A tool for generating interface .jars from normal .jars
=============================================================
Alan Donovan, 26 May 2007.
Rationale:
In order to improve the speed of compilation of Java programs in
Bazel, the output of build steps is cached.
This works very nicely for C++ compilation: a compilation unit
includes a .cc source file and typically dozens of header files.
Header files change relatively infrequently, so the need for a
rebuild is usually driven by a change in the .cc file. Even after
syncing a slightly newer version of the tree and doing a rebuild,
many hits in the cache are still observed.
In Java, by contrast, a compilation unit involves a set of .java
source files, plus a set of .jar files containing already-compiled
JVM .class files. Class files serve a dual purpose: from the JVM's
perspective, they are containers of executable code, but from the
compiler's perspective, they are interface definitions. The problem
here is that .jar files are very much more sensitive to change than
C++ header files, so even a change that is insignificant to the
compiler (such as the addition of a print statement to a method in a
prerequisite class) will cause the jar to change, and any code that
depends on this jar's interface will be recompiled unnecessarily.
The purpose of ijar is to produce, from a .jar file, a much smaller,
simpler .jar file containing only the parts that are significant for
the purposes of compilation. In other words, an interface .jar
file. By changing ones compilation dependencies to be the interface
jar files, unnecessary recompilation is avoided when upstream
changes don't affect the interface.
Details:
ijar is a tool that reads a .jar file and emits a .jar file
containing only the parts that are relevant to Java compilation.
For example, it throws away:
- Files whose name does not end in ".class".
- All executable method code.
- All private methods and fields.
- All constants and attributes except the minimal set necessary to
describe the class interface.
- All debugging information
(LineNumberTable, SourceFile, LocalVariableTables attributes).
It also sets to zero the file modification times in the index of the
.jar file.
Implementation:
ijar is implemented in C++, and runs very quickly. For example
(when optimized) it takes only 530ms to process a 42MB
.jar file containing 5878 classe, resulting in an interface .jar
file of only 11.4MB in size. For more usual .jar sizes of a few
megabytes, a runtime of 50ms is typical.
The implementation strategy is to mmap both the input jar and the
newly-created _interface.jar, and to scan through the former and
emit the latter in a single pass. There are a couple of locations
where some kind of "backpatching" is required:
- in the .zip file format, for each file, the size field precedes
the data. We emit a zero but note its location, generate and emit
the stripped classfile, then poke the correct size into the
location.
- for JVM .class files, the header (including the constant table)
precedes the body, but cannot be emitted before it because it's
not until we emit the body that we know which constants are
referenced and which are garbage. So we emit the body into a
temporary buffer, then emit the header to the output jar, followed
by the contents of the temp buffer.
Also note that the zip file format has unnecessary duplication of
the index metadata: it has header+data for each file, then another
set of (similar) headers at the end. Rather than save the metadata
explicitly in some datastructure, we just record the addresses of
the already-emitted zip metadata entries in the output file, and
then read from there as necessary.
Notes:
This code has no dependency except on the STL and on zlib.
Almost all of the getX/putX/ReadX/WriteX functions in the code
advance their first argument pointer, which is passed by reference.
It's tempting to discard package-private classes and class members.
However, this would be incorrect because they are a necessary part
of the package interface, as a Java package is often compiled in
multiple stages. For example: in Bazel, both java tests and java
code inhabit the same Java package but are compiled separately.
Assumptions:
We assume that jar files are uncompressed v1.0 zip files (created
with 'jar c0f') with a zero general_purpose_bit_flag.
We assume that javap/javac don't need the correct CRC checksums in
the .jar file.
We assume that it's better simply to abort in the face of unknown
input than to risk leaving out something important from the output
(although in the case of annotations, it should be safe to ignore
ones we don't understand).
TODO:
Maybe: ensure a canonical sort order is used for every list (jar
entries, class members, attributes, etc.) This isn't essential
because we can assume the compiler is deterministic and the order in
the source files changes little. Also, it would require two passes. :(
Maybe: delete dynamically-allocated memory.
Add (a lot) more tests. Include a test of idempotency.

1788
tools/ijar/classfile.cc Normal file

File diff suppressed because it is too large Load Diff

102
tools/ijar/common.h Normal file
View File

@ -0,0 +1,102 @@
// Copyright 2001,2007 Alan Donovan. All rights reserved.
//
// Author: Alan Donovan <adonovan@google.com>
//
// 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.
//
// common.h -- common definitions.
//
#ifndef INCLUDED_DEVTOOLS_IJAR_COMMON_H
#define INCLUDED_DEVTOOLS_IJAR_COMMON_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
namespace devtools_ijar {
typedef unsigned long long u8;
typedef uint32_t u4;
typedef uint16_t u2;
typedef uint8_t u1;
// be = big endian, le = little endian
inline u1 get_u1(const u1 *&p) {
return *p++;
}
inline u2 get_u2be(const u1 *&p) {
u4 x = (p[0] << 8) | p[1];
p += 2;
return x;
}
inline u2 get_u2le(const u1 *&p) {
u4 x = (p[1] << 8) | p[0];
p += 2;
return x;
}
inline u4 get_u4be(const u1 *&p) {
u4 x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
p += 4;
return x;
}
inline u4 get_u4le(const u1 *&p) {
u4 x = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
p += 4;
return x;
}
inline void put_u1(u1 *&p, u1 x) {
*p++ = x;
}
inline void put_u2be(u1 *&p, u2 x) {
*p++ = x >> 8;
*p++ = x & 0xff;
}
inline void put_u2le(u1 *&p, u2 x) {
*p++ = x & 0xff;
*p++ = x >> 8;;
}
inline void put_u4be(u1 *&p, u4 x) {
*p++ = x >> 24;
*p++ = (x >> 16) & 0xff;
*p++ = (x >> 8) & 0xff;
*p++ = x & 0xff;
}
inline void put_u4le(u1 *&p, u4 x) {
*p++ = x & 0xff;
*p++ = (x >> 8) & 0xff;
*p++ = (x >> 16) & 0xff;
*p++ = x >> 24;
}
// Copy n bytes from src to p, and advance p.
inline void put_n(u1 *&p, const u1 *src, size_t n) {
memcpy(p, src, n);
p += n;
}
extern bool verbose;
} // namespace devtools_ijar
#endif // INCLUDED_DEVTOOLS_IJAR_COMMON_H

182
tools/ijar/ijar.cc Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2001,2007 Alan Donovan. All rights reserved.
//
// Author: Alan Donovan <adonovan@google.com>
//
// 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.
//
// ijar.cpp -- .jar -> _interface.jar tool.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <memory>
#include "zip.h"
namespace devtools_ijar {
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.
void StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length);
const char* CLASS_EXTENSION = ".class";
const size_t CLASS_EXTENSION_LENGTH = strlen(CLASS_EXTENSION);
// 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 {
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;
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;
}
};
bool JarStripperProcessor::Accept(const char* filename, const u4) {
ssize_t offset = strlen(filename) - CLASS_EXTENSION_LENGTH;
if (offset >= 0) {
return strcmp(filename + offset, CLASS_EXTENSION) == 0;
}
return false;
}
void JarStripperProcessor::Process(const char* filename, const u4,
const u1* data, const size_t size) {
if (verbose) {
fprintf(stderr, "INFO: StripClass: %s\n", filename);
}
u1 *q = builder->NewFile(filename, 0);
u1 *classdata_out = q;
StripClass(q, data, size); // actually process it
size_t out_length = q - classdata_out;
builder->FinishFile(out_length);
}
// Opens "file_in" (a .jar file) for reading, and writes an interface
// .jar to "file_out".
void OpenFilesAndProcessJar(const char *file_out, const char *file_in) {
JarStripperProcessor processor;
std::unique_ptr<ZipExtractor> in(ZipExtractor::Create(file_in, &processor));
if (in.get() == NULL) {
fprintf(stderr, "Unable to open Zip file %s: %s\n", file_in,
strerror(errno));
abort();
}
u8 output_length = in->CalculateOutputLength();
std::unique_ptr<ZipBuilder> out(ZipBuilder::Create(file_out, output_length));
if (out.get() == NULL) {
fprintf(stderr, "Unable to open output file %s: %s\n", file_out,
strerror(errno));
abort();
}
processor.SetZipBuilder(out.get());
// Process all files in the zip
if (in->ProcessAll() < 0) {
fprintf(stderr, "%s\n", in->GetError());
abort();
}
// Add dummy file, since javac doesn't like truly empty jars.
if (out->GetNumberFiles() == 0) {
out->WriteEmptyFile("dummy");
}
// Finish writing the output file
if (out->Finish() < 0) {
fprintf(stderr, "%s\n", out->GetError());
abort();
}
// Get all file size
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));
}
}
} // namespace devtools_ijar
//
// main method
//
static void usage() {
fprintf(stderr, "Usage: ijar [-v] 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) {
const char *filename_in = NULL;
const char *filename_out = NULL;
for (int ii = 1; ii < argc; ++ii) {
if (strcmp(argv[ii], "-v") == 0) {
devtools_ijar::verbose = true;
} else if (filename_in == NULL) {
filename_in = argv[ii];
} else if (filename_out == NULL) {
filename_out = argv[ii];
} else {
usage();
}
}
if (filename_in == NULL) {
usage();
}
// Guess output filename from input:
char filename_out_buf[PATH_MAX];
if (filename_out == NULL) {
size_t len = strlen(filename_in);
if (len > 4 && strncmp(filename_in + len - 4, ".jar", 4) == 0) {
strcpy(filename_out_buf, filename_in);
strcpy(filename_out_buf + len - 4, "-interface.jar");
filename_out = filename_out_buf;
} else {
fprintf(stderr, "Can't determine output filename since input filename "
"doesn't end with '.jar'.\n");
return 1;
}
}
if (devtools_ijar::verbose) {
fprintf(stderr, "INFO: writing to '%s'.\n", filename_out);
}
devtools_ijar::OpenFilesAndProcessJar(filename_out, filename_in);
return 0;
}

1031
tools/ijar/zip.cc Normal file

File diff suppressed because it is too large Load Diff

173
tools/ijar/zip.h Normal file
View File

@ -0,0 +1,173 @@
// Copyright 2015 Google Inc. 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.
//
// zip.h -- .zip (.jar) file reading/writing routines.
//
// This file specifies the interface to use the ZIP implementation of ijar.
//
#ifndef INCLUDED_THIRD_PARTY_IJAR_ZIP_H
#define INCLUDED_THIRD_PARTY_IJAR_ZIP_H
#include <sys/stat.h>
#include "common.h"
namespace devtools_ijar {
// Tells if this is a directory entry from the mode. This method
// is safer than zipattr_to_mode(attr) & S_IFDIR because the unix
// mode might not be set in DOS zip files.
inline bool zipattr_is_dir(u4 attr) { return (attr & 0x10) != 0; }
// Convert a Unix file mode to a ZIP file attribute
inline u4 mode_to_zipattr(mode_t m) {
return (((u4) m) << 16) + ((m & S_IFDIR) != 0 ? 0x10 : 0);
}
// Convert a ZIP file attribute to a Unix file mode
inline mode_t zipattr_to_mode(u4 attr) {
return ((mode_t) ((attr >> 16) & 0xffff));
}
//
// Class interface for building ZIP files
//
class ZipBuilder {
public:
virtual ~ZipBuilder() {}
// Returns the text for the last error, or null on no last error.
virtual const char* GetError() = 0;
// Add a new file to the ZIP, the file will have path "filename"
// and external attributes "attr". This function returns a pointer
// to a memory buffer to write the data of the file into. This buffer
// is owned by ZipBuilder and should not be free'd by the caller. The
// file length is then specified when the files is finished written
// using the FinishFile(size_t) function.
// On failure, returns NULL and GetError() will return an non-empty message.
virtual u1* NewFile(const char* filename, const u4 attr) = 0;
// Finish writing a file and specify its length. After calling this method
// one should not reuse the pointer given by NewFile. The file can be
// compressed using the deflate algorithm by setting `compress` to true.
// By default, CRC32 are not computed as java tooling doesn't care, but
// computing it can be activated by setting `compute_crc` to true.
// On failure, returns -1 and GetError() will return an non-empty message.
virtual int FinishFile(size_t filelength,
bool compress = false,
bool compute_crc = false) = 0;
// Write an empty file, it is equivalent to:
// NewFile(filename, 0);
// FinishFile(0);
// On failure, returns -1 and GetError() will return an non-empty message.
virtual int WriteEmptyFile(const char* filename) = 0;
// Finish writing the ZIP file. This method can be called only once
// (subsequent calls will do nothing) and none of
// NewFile/FinishFile/WriteEmptyFile should be called after calling Finish. If
// this method was not called when the object is destroyed, it will be called.
// It is here as a convenience to get information on the final generated ZIP
// file.
// On failure, returns -1 and GetError() will return an non-empty message.
virtual int Finish() = 0;
// Get the current size of the ZIP file. This size will not be matching the
// final ZIP file until Finish() has been called because Finish() is actually
// writing the central directory of the ZIP File.
virtual size_t GetSize() = 0;
// Returns the current number of files stored in the ZIP.
virtual int GetNumberFiles() = 0;
// Create a new ZipBuilder writing the file zip_file and the size of the
// output will be at most estimated_size. Use ZipBuilder::EstimateSize() or
// ZipExtractor::CalculateOuputLength() to have an estimated_size depending on
// a list of file to store.
// On failure, returns NULL. Refer to errno for error code.
static ZipBuilder* Create(const char* zip_file, u8 estimated_size);
// Estimate the maximum size of the ZIP files containing files in the "files"
// null-terminated array.
// Returns 0 on error.
static u8 EstimateSize(char **files);
};
//
// An abstract class to process data from a ZipExtractor.
// Derive from this class if you wish to process data from a ZipExtractor.
//
class ZipExtractorProcessor {
public:
virtual ~ZipExtractorProcessor() {}
// Tells whether to skip or process the file "filename". "attr" is the
// external file attributes and can be converted to unix mode using the
// zipattr_to_mode() function. This method is suppoed to returns true
// if the file should be processed and false if it should be skipped.
virtual bool Accept(const char* filename, const u4 attr) = 0;
// Process a file accepted by Accept. The file "filename" has external
// attributes "attr" and length "size". The file content is accessible
// in the buffer pointed by "data".
virtual void Process(const char* filename, const u4 attr,
const u1* data, const size_t size) = 0;
};
//
// Class interface for reading ZIP files
//
class ZipExtractor {
public:
virtual ~ZipExtractor() {}
// Returns the text for the last error, or null on no last error.
virtual const char* GetError() = 0;
// Process the next files, returns false if the end of ZIP file has been
// reached. The processor provided by the Create method will be called
// if a file is encountered. If false is returned, check the return value
// of GetError() for potential errors.
virtual bool ProcessNext() = 0;
// Process the all files, returns -1 on error (GetError() will be populated
// on error).
virtual int ProcessAll();
// Reset the file pointer to the beginning.
virtual void Reset() = 0;
// Return the size of the ZIP file.
virtual size_t GetSize() = 0;
// Return the size of the resulting zip file by keeping only file
// accepted by the processor and storing them uncompressed. This
// method can be used to create a ZipBuilder for storing a subset
// of the input files.
// On error, 0 is returned and GetError() returns a non-empty message.
virtual u8 CalculateOutputLength() = 0;
// Create a ZipExtractor that extract the zip file "filename" and process
// it with "processor".
// On error, a null pointer is returned and the value of errno should be
// checked.
static ZipExtractor* Create(const char* filename,
ZipExtractorProcessor *processor);
};
} // namespace devtools_ijar
#endif // INCLUDED_THIRD_PARTY_IJAR_ZIP_H

312
tools/ijar/zip_main.cc Normal file
View File

@ -0,0 +1,312 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Author: Alan Donovan <adonovan@google.com>
//
// 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.
//
// Zip / Unzip file using ijar zip implementation.
//
// Note that this Zip implementation intentionally don't compute CRC-32
// because it is useless computation for jar because Java doesn't care.
// CRC-32 of all files in the zip file will be set to 0.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <memory>
#include "zip.h"
namespace devtools_ijar {
#define SYSCALL(expr) do { \
if ((expr) < 0) { \
perror(#expr); \
abort(); \
} \
} while (0)
//
// A ZipExtractorProcessor that extract all files in the ZIP file.
//
class UnzipProcessor : public ZipExtractorProcessor {
public:
// Create a processor who will extract the files into output_root
// if "extract" is set to true and will print the list of files and
// their unix modes if "verbose" is set to true.
UnzipProcessor(const char *output_root, bool verbose, bool extract)
: output_root_(output_root), verbose_(verbose), extract_(extract) {}
virtual ~UnzipProcessor() {}
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) {
return true;
}
private:
const char *output_root_;
const bool verbose_;
const bool extract_;
};
// Concatene 2 path, path1 and path2, using / as a directory separator and
// puting the result in "out". "size" specify the size of the output buffer
void concat_path(char* out, const size_t size,
const char *path1, const char *path2) {
int len1 = strlen(path1);
size_t l = len1;
strncpy(out, path1, size - 1);
out[size-1] = 0;
if (l < size - 1 && path1[len1] != '/' && path2[0] != '/') {
out[l] = '/';
l++;
out[l] = 0;
}
if (l < size - 1) {
strncat(out, path2, size - 1 - l);
}
}
// Do a recursive mkdir of all folders of path except the last path
// segment (if path ends with a / then the last path segment is empty).
// All folders are created using "mode" for creation mode.
void mkdirs(const char *path, mode_t mode) {
char path_[PATH_MAX];
struct stat statst;
strncpy(path_, path, PATH_MAX);
path_[PATH_MAX-1] = 0;
char *pointer = path_;
while ((pointer = strchr(pointer, '/')) != NULL) {
if (path_ != pointer) { // skip leading slash
*pointer = 0;
if (stat(path_, &statst) != 0) {
if (mkdir(path_, mode) < 0) {
fprintf(stderr, "Cannot create folder %s: %s\n",
path_, strerror(errno));
abort();
}
}
*pointer = '/';
}
pointer++;
}
}
void UnzipProcessor::Process(const char* filename, const u4 attr,
const u1* data, const size_t size) {
mode_t mode = zipattr_to_mode(attr);
mode_t perm = mode & 0777;
bool isdir = (mode & S_IFDIR) != 0;
if (attr == 0) {
// Fallback when the external attribute is not set.
isdir = filename[strlen(filename)-1] == '/';
perm = 0777;
}
if (verbose_) {
printf("%c %o %s\n", isdir ? 'd' : 'f', perm, filename);
}
if (extract_) {
char path[PATH_MAX];
int fd;
concat_path(path, PATH_MAX, output_root_, filename);
mkdirs(path, perm);
if (!isdir) {
fd = open(path, O_CREAT | O_WRONLY, perm);
if (fd < 0) {
fprintf(stderr, "Cannot open file %s for writing: %s\n",
path, strerror(errno));
abort();
}
SYSCALL(write(fd, data, size));
SYSCALL(close(fd));
}
}
}
// Get the basename of path and store it in output. output_size
// is the size of the output buffer.
void basename(const char *path, char *output, size_t output_size) {
const char *pointer = strrchr(path, '/');
if (pointer == NULL) {
pointer = path;
} else {
pointer++; // Skip the leading slash.
}
strncpy(output, pointer, output_size);
output[output_size-1] = 0;
}
// Execute the extraction (or just listing if just v is provided)
int extract(char *zipfile, bool verbose, bool extract) {
char output_root[PATH_MAX];
getcwd(output_root, PATH_MAX);
UnzipProcessor processor(output_root, verbose, extract);
std::unique_ptr<ZipExtractor> extractor(ZipExtractor::Create(zipfile,
&processor));
if (extractor.get() == NULL) {
fprintf(stderr, "Unable to open zip file %s: %s.\n", zipfile,
strerror(errno));
return -1;
}
if (extractor->ProcessAll() < 0) {
fprintf(stderr, "%s.\n", extractor->GetError());
return -1;
}
return 0;
}
// Execute the create operation
int create(char *zipfile, char **files, bool flatten, bool verbose,
bool compress) {
struct stat statst;
u8 size = ZipBuilder::EstimateSize(files);
if (size == 0) {
return -1;
}
std::unique_ptr<ZipBuilder> builder(ZipBuilder::Create(zipfile, size));
if (builder.get() == NULL) {
fprintf(stderr, "Unable to create zip file %s: %s.\n",
zipfile, strerror(errno));
return -1;
}
for (int i = 0; files[i] != NULL; i++) {
stat(files[i], &statst);
char path[PATH_MAX];
bool isdir = (statst.st_mode & S_IFDIR) != 0;
if (flatten && isdir) {
continue;
}
// Compute the path, flattening it if requested
if (flatten) {
basename(files[i], path, PATH_MAX);
} else {
strncpy(path, files[i], PATH_MAX);
path[PATH_MAX-1] = 0;
size_t len = strlen(path);
if (isdir && len < PATH_MAX - 1) {
// Add the trailing slash for folders
path[len] = '/';
path[len+1] = 0;
}
}
if (verbose) {
mode_t perm = statst.st_mode & 0777;
printf("%c %o %s\n", isdir ? 'd' : 'f', perm, path);
}
u1 *buffer = builder->NewFile(path, mode_to_zipattr(statst.st_mode));
if (isdir || statst.st_size == 0) {
builder->FinishFile(0);
} else {
// mmap the input file and memcpy
int fd = open(files[i], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can't open file %s for reading: %s.\n",
files[i], strerror(errno));
return -1;
}
void *data = mmap(NULL, statst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "Can't mmap file %s for reading: %s.\n",
files[i], strerror(errno));
return -1;
}
memcpy(buffer, data, statst.st_size);
munmap(data, statst.st_size);
builder->FinishFile(statst.st_size, compress, true);
}
}
if (builder->Finish() < 0) {
fprintf(stderr, "%s\n", builder->GetError());
return -1;
}
return 0;
}
} // namespace devtools_ijar
//
// main method
//
static void usage(char *progname) {
fprintf(stderr, "Usage: %s [vxc[fC]] x.zip [file1...filen]\n", progname);
fprintf(stderr, " v verbose - list all file in x.zip\n");
fprintf(stderr, " x extract - extract file in x.zip in current directory\n");
fprintf(stderr, " c create - add files to x.zip\n");
fprintf(stderr, " f flatten - flatten files to use with create operation\n");
fprintf(stderr,
" C compress - compress files when using the create operation\n");
fprintf(stderr, "x and c cannot be used in the same command-line.\n");
exit(1);
}
int main(int argc, char **argv) {
bool extract = false;
bool verbose = false;
bool create = false;
bool compress = false;
bool flatten = false;
if (argc < 3) {
usage(argv[0]);
}
for (int i = 0; argv[1][i] != 0; i++) {
switch (argv[1][i]) {
case 'x':
extract = true;
break;
case 'v':
verbose = true;
break;
case 'c':
create = true;
break;
case 'f':
flatten = true;
break;
case 'C':
compress = true;
break;
default:
usage(argv[0]);
}
}
if (create) {
if (extract) {
usage(argv[0]);
}
// Create a zip
return devtools_ijar::create(argv[2], argv + 3, flatten, verbose, compress);
} else {
if (flatten) {
usage(argv[0]);
}
// Extraction / list mode
return devtools_ijar::extract(argv[2], verbose, extract);
}
}