forked from openkylin/platform_build
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:
commit
dbeab8de65
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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)),)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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.
|
||||
|
|
@ -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.
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue