diff options
author | Lukacs T. Berki <lberki@google.com> | 2017-04-18 14:23:19 +0200 |
---|---|---|
committer | Lukacs T. Berki <lberki@google.com> | 2017-04-18 14:35:16 +0200 |
commit | 55828e256af4fe04e95f1c342a73d649d8525435 (patch) | |
tree | e9eb87f376897af767937ef71b126dac94f60a42 /third_party | |
parent | df8d804acb5826a120cad9acfc78dd6f0071d864 (diff) |
Vendor jarjar instead of using a binary version of it.
It appears that jarjar is not actually used except for renaming classes
in the coverage collecting Java test runner as verified by
`find . -name BUILD | xargs grep jarjar`
Yak shaving for #2820.
This is necessary because the current version of jarjar does not work
with Java 8 classes because it embeds a version of ASM that doesn't
support that yet.
Change-Id: I6ac59b84bbbc1e85fe8e7f4f4876b98fc6129df0
Diffstat (limited to 'third_party')
95 files changed, 5657 insertions, 5 deletions
diff --git a/third_party/java/jarjar/BUILD b/third_party/java/jarjar/BUILD index 8f31f169c6..a13054f06d 100644 --- a/third_party/java/jarjar/BUILD +++ b/third_party/java/jarjar/BUILD @@ -7,13 +7,25 @@ filegroup( srcs = glob(["**"]), ) -java_import( - name = "jarjar", - jars = ["jarjar-1.4.jar"], +java_library( + name = "jarjar_core", + srcs = glob(["jarjar-core/src/main/**/*.java"]), + deps = [ + "//third_party:asm", + "//third_party:asm-commons", + "//third_party:asm-util", + "//third_party:jsr305", + "//third_party:slf4j", + ], ) java_binary( - name = "jarjar_bin", + name = "jarjar_command", + srcs = glob(["jarjar-command/src/main/**/*.java"]), main_class = "com.tonicsystems.jarjar.Main", - runtime_deps = [":jarjar"], + deps = [ + ":jarjar_core", + "//third_party:jsr305", + "//third_party/java/jopt-simple", + ], ) diff --git a/third_party/java/jarjar/COPYING b/third_party/java/jarjar/COPYING new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/java/jarjar/COPYING @@ -0,0 +1,202 @@ + + 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. diff --git a/third_party/java/jarjar/README.bazel.md b/third_party/java/jarjar/README.bazel.md new file mode 100644 index 0000000000..8a71e818ce --- /dev/null +++ b/third_party/java/jarjar/README.bazel.md @@ -0,0 +1,6 @@ +This is jarjar. They don't seem to tag their releases, so I just used whatever was at HEAD. Reproduction: + +1. `git clone https://github.com/shevek/jarjar` +2. `git checkout 69d2972ea10eefa66cdc2c1c283cb6ef79b3c5ba` +3. Copy the git tree to `third_party/java/jarjar` +4. Keep the existing `BUILD` file diff --git a/third_party/java/jarjar/README.md b/third_party/java/jarjar/README.md new file mode 100644 index 0000000000..6d7097c41f --- /dev/null +++ b/third_party/java/jarjar/README.md @@ -0,0 +1,184 @@ +Jar Jar Links is a utility that makes it easy to repackage Java +libraries and embed them into your own distribution. This is useful +for two reasons: + +You can easily ship a single jar file with no external dependencies. + +You can avoid problems where your library depends on a specific +version of a library, which may conflict with the dependencies of +another library. + +How does it work? + +Jar Jar Links includes an Ant task that extends the built-in jar +task. The normal zipfileset element is used to embed jar files. A +new rule element is added which uses wildcards patterns to rename +the embedded class files. Bytecode transformation (via ASM) is used +to change references to the renamed classes, and special handling is +provided for moving resource files and transforming string literals. + +Using with ant +-------------- + +In our imaginary project, the Ant "jar" target looks like: + +``` +<target name="jar" depends="compile"> + <jar jarfile="dist/example.jar"> + <fileset dir="build/main"/> + </jar> +</target> +``` + +To use Jar Jar Links, we define a new task named "jarjar", and +substitute it wherever we used the jar task. Because the JarJarTask +class extends the normal Ant Jar task, you can use jarjar without +any of its additional features, if you want: + +``` +<target name="jar" depends="compile"> + <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" + classpath="lib/jarjar.jar"/> + <jarjar jarfile="dist/example.jar"> + <fileset dir="build/main"/> + </jarjar> +</target> +``` + +Just like with the "jar" task, we can include the contents of another +jar file using the "zipfileset" element. But simply including another +projects classes is not good enough to avoid jar hell, since the class +names remain unchanged and can still conflict with other versions. + +To rename the classes, JarJarTask adds a new "rule" element. The +rule takes a "pattern" attribute, which uses wildcards to match +against class names, and a "result" attribute, which describes how +to transform the matched names. + +In this example we include classes from jaxen.jar and add a rule +that changes any class name starting with "org.jaxen" to start with +"org.example.jaxen" instead (in our imaginary world we control the +example.org domain): + +``` +<target name="jar" depends="compile"> + <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" + classpath="lib/jarjar.jar"/> + <jarjar jarfile="dist/example.jar"> + <fileset dir="build/main"/> + <zipfileset src="lib/jaxen.jar"/> + <rule pattern="org.jaxen.**" result="org.example.@1"/> + </jarjar> +</target> +``` + +The ** in the pattern means to match against any valid package +substring. To match against a single package component (by excluding +dots (.) from the match), a single * may be used instead. + +The @1 in the result is a reference to the ** portion of the rule. For +every * or ** in the rule, a numbered reference is available for use +in the result. References are numbered from left to right, starting +with @1, then @2, and so on. + +The special @0 reference refers to the entire class name. + +Using with gradle +----------------- + +``` + dependencies { + // Use jarjar.repackage in place of a dependency notation. + compile jarjar.repackage { + from 'com.google.guava:guava:18.0' + + classDelete "com.google.common.base.**" + + classRename "com.google.**" "org.private.google.@1" + } + } +``` + +See (jarjar-gradle/example/build.gradle) for some complete examples. + +Using from the command line +--------------------------- + +From the command-line + +``` +java -jar jarjar.jar [help] +``` + +Prints this help message. + +``` +java -jar jarjar.jar strings <cp> +``` + +Dumps all string literals in classpath `<cp>`. Line numbers will be +included if the classes have debug information. + +``` +java -jar jarjar.jar find <level> <cp1> [<cp2>] +``` + +Prints dependencies on classpath `<cp2>` in classpath `<cp1>`. If `<cp2>` +is omitted, `<cp1>` is used for both arguments. + +The level argument must be class or jar. The former prints dependencies +between individual classes, while the latter only prints jar->jar +dependencies. A "jar" in this context is actually any classpath +component, which can be a jar file, a zip file, or a parent directory +(see below). + +``` +java -jar jarjar.jar process <rulesFile> <inJar> <outJar> +``` + +Transform the `<inJar>` jar file, writing a new jar file to `<outJar>`. Any +existing file named by `<outJar>` will be deleted. + +The transformation is defined by a set of rules in the file specified +by the rules argument (see below). Classpath format + +The classpath argument is a colon or semi-colon delimited +set (depending on platform) of directories, jar files, +or zip files. See the following page for more details: +http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/classpath.html + +Mustang-style wildcards are also supported: +http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6268383 Rules +file format + +The rules file is a text file, one rule per line. Leading and trailing +whitespace is ignored. There are three types of rules: + +``` +rule <pattern> <result> +zap <pattern> +keep <pattern> +``` + +The standard rule (rule) is used to rename classes. All references to +the renamed classes will also be updated. If a class name is matched +by more than one rule, only the first one will apply. + +`<pattern>` is a class name with optional wildcards. `**` will match +against any valid class name substring. To match a single package +component (by excluding . from the match), a single `*` may be used +instead. + +`<result>` is a class name which can optionally reference the substrings +matched by the wildcards. A numbered reference is available for every +`*` or `**` in the `<pattern>`, starting from left to right: @1, @2, etc. A +special @0 reference contains the entire matched class name. + +The zap rule causes any matched class to be removed from the resulting +jar file. All zap rules are processed before renaming rules. + +The keep rule marks all matched classes as "roots". If any keep rules +are defined all classes which are not reachable from the roots via +dependency analysis are discarded when writing the output jar. This +is the last step in the process, after renaming and zapping. + diff --git a/third_party/java/jarjar/build.gradle b/third_party/java/jarjar/build.gradle new file mode 100644 index 0000000000..4789994ad6 --- /dev/null +++ b/third_party/java/jarjar/build.gradle @@ -0,0 +1,148 @@ +buildscript { + apply from: file('gradle/buildscript.gradle'), to: buildscript +} + +apply plugin: 'org.anarres.stdproject' +stdproject { +} + +subprojects { + apply plugin: 'org.anarres.stdmodule' + stdmodule { + description "Jar Jar Links is a utility that makes it easy to repackage Java libraries and embed them into your own distribution." + author id: 'shevek', name: 'Shevek', email: 'github@anarres.org' + license 'Apache-2.0' + } + + group = "org.anarres.jarjar" + + dependencies { + compile 'com.google.code.findbugs:jsr305:2.0.2' + compile 'org.slf4j:slf4j-api:1.7.12' + } + + sourceCompatibility = 1.5 + animalsniffer { + signature = "org.codehaus.mojo.signature:java15:+@signature" + } +} + +project(':jarjar-testdata') { + dependencies { + compile 'com.google.guava:guava:17.0' + } + + for (int i = 0; i < 4; i++) { + String name = "jar$i"; + Task t = task(name, type: Jar) { + classifier = name + from(sourceSets.main.output) { + include "**/pkg$i/**"; + } + } + + artifacts { + archives t + } + } + +/* + assemble << { + println configurations.archives.dump() + println "\nArtifacts:\n" + configurations.archives.artifacts.dump() + println "\nFiles:\n" + configurations.archives.files.dump() + println "\nEach:\n" + for (Object o : configurations.archives.artifacts.files) { + println o.dump() + } + // println configurations.archives + println jar0.dump() + println jar0.archivePath + } +*/ + + uploadArchives.enabled = false +} + +project(':jarjar-core') { + dependencies { + compile 'org.ow2.asm:asm-commons:5.0.3' + compile 'org.ow2.asm:asm-util:5.0.3' + // compile 'org.ow2.asm:asm-commons:4.0' + + // testCompile project(':jarjar-testdata') + } + + def samplesProject = project(':jarjar-testdata') + test.dependsOn samplesProject.tasks['assemble'] + test { + systemProperty "jar", samplesProject.jar.archivePath + for (int i = 0; i < 4; i++) { + systemProperty "jar$i", samplesProject."jar$i".archivePath + } + } +} + +project(':jarjar-ant') { + dependencies { + compile project(':jarjar-core') + compile 'org.apache.ant:ant:1.7.0' + + testCompile project(':jarjar-core').sourceSets.test.output + } +} + +project(':jarjar-maven') { + dependencies { + compile project(':jarjar-core') + compile 'org.apache.maven:maven-plugin-api:2.0' + + testCompile project(':jarjar-core').sourceSets.test.output + } +} + +project(':jarjar-gradle') { + apply plugin: 'org.anarres.stdplugin' + + stdplugin { + implementation "org.anarres.jarjar", "org.anarres.gradle.plugin.jarjar.JarjarPlugin" + } + +/* + tasks.removeAll { + // println it.dump(); + // println it.name; + return it.name == "sourcesJar"; + } + + // apply plugin: 'java-gradle-plugin' + // apply plugin: 'nu.studer.plugindev' + // apply from: file('../gradle/plugin.gradle') +*/ + + dependencies { + compile project(':jarjar-core') + compile 'org.apache.commons:commons-compress:1.9' + compile gradleApi() + compile 'oro:oro:2.0.8' + + testCompile project(':jarjar-core').sourceSets.test.output + } + + animalsniffer { + skip = true + } +} + +project(':jarjar-command') { + dependencies { + compile project(':jarjar-core') + compile 'net.sf.jopt-simple:jopt-simple:4.8' + + testCompile project(':jarjar-core').sourceSets.test.output + } + + apply plugin: 'application' + + mainClassName = "com.tonicsystems.jarjar.Main" +} diff --git a/third_party/java/jarjar/codequality/HEADER b/third_party/java/jarjar/codequality/HEADER new file mode 100644 index 0000000000..169c3d19a3 --- /dev/null +++ b/third_party/java/jarjar/codequality/HEADER @@ -0,0 +1,13 @@ +Copyright ${year} Shevek. + +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. diff --git a/third_party/java/jarjar/codequality/checkstyle.xml b/third_party/java/jarjar/codequality/checkstyle.xml new file mode 100644 index 0000000000..47c01a2ea1 --- /dev/null +++ b/third_party/java/jarjar/codequality/checkstyle.xml @@ -0,0 +1,188 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC + "-//Puppy Crawl//DTD Check Configuration 1.2//EN" + "http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> + +<module name="Checker"> + + <!-- Checks that a package-info.java file exists for each package. --> + <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage --> + <!-- + <module name="JavadocPackage"> + <property name="allowLegacy" value="true"/> + </module> + --> + + <!-- Checks whether files end with a new line. --> + <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile --> + <module name="NewlineAtEndOfFile"/> + + <!-- Checks that property files contain the same keys. --> + <!-- See http://checkstyle.sf.net/config_misc.html#Translation --> + <module name="Translation"/> + + <!-- Checks for Size Violations. --> + <!-- See http://checkstyle.sf.net/config_sizes.html --> + <module name="FileLength"/> + + <!-- Checks for whitespace --> + <!-- See http://checkstyle.sf.net/config_whitespace.html --> + <module name="FileTabCharacter"/> + + <!-- Miscellaneous other checks. --> + <!-- See http://checkstyle.sf.net/config_misc.html --> + <module name="RegexpSingleline"> + <property name="format" value="\s+$"/> + <property name="minimum" value="0"/> + <property name="maximum" value="0"/> + <property name="message" value="Line has trailing spaces."/> + <property name="severity" value="info"/> + </module> + + <module name="TreeWalker"> + + <!-- Checks for Javadoc comments. --> + <!-- See http://checkstyle.sf.net/config_javadoc.html --> + <module name="JavadocMethod"> + <property name="scope" value="package"/> + <property name="allowMissingParamTags" value="true"/> + <property name="allowMissingThrowsTags" value="true"/> + <property name="allowMissingReturnTag" value="true"/> + <property name="allowThrowsTagsForSubclasses" value="true"/> + <property name="allowUndeclaredRTE" value="true"/> + <property name="allowMissingPropertyJavadoc" value="true"/> + </module> + <module name="JavadocType"> + <property name="scope" value="package"/> + </module> + <module name="JavadocVariable"> + <property name="scope" value="package"/> + </module> + <module name="JavadocStyle"> + <property name="checkEmptyJavadoc" value="true"/> + </module> + + <!-- Checks for Naming Conventions. --> + <!-- See http://checkstyle.sf.net/config_naming.html --> + <module name="ConstantName"/> + <module name="LocalFinalVariableName"/> + <module name="LocalVariableName"/> + <module name="MemberName"/> + <module name="MethodName"/> + <module name="PackageName"/> + <module name="ParameterName"/> + <module name="StaticVariableName"/> + <module name="TypeName"/> + + <!-- Checks for imports --> + <!-- See http://checkstyle.sf.net/config_import.html --> + <module name="AvoidStarImport"/> + <module name="IllegalImport"/> <!-- defaults to sun.* packages --> + <module name="RedundantImport"/> + <module name="UnusedImports"/> + + + <!-- Checks for Size Violations. --> + <!-- See http://checkstyle.sf.net/config_sizes.html --> + <module name="LineLength"> + <!-- what is a good max value? --> + <property name="max" value="120"/> + <!-- ignore lines like "$File: //depot/... $" --> + <property name="ignorePattern" value="\$File.*\$"/> + <property name="severity" value="info"/> + </module> + <module name="MethodLength"/> + <module name="ParameterNumber"/> + + + <!-- Checks for whitespace --> + <!-- See http://checkstyle.sf.net/config_whitespace.html --> + <module name="EmptyForIteratorPad"/> + <module name="GenericWhitespace"/> + <module name="MethodParamPad"/> + <module name="NoWhitespaceAfter"/> + <module name="NoWhitespaceBefore"/> + <module name="OperatorWrap"/> + <module name="ParenPad"/> + <module name="TypecastParenPad"/> + <module name="WhitespaceAfter"/> + <module name="WhitespaceAround"/> + + <!-- Modifier Checks --> + <!-- See http://checkstyle.sf.net/config_modifiers.html --> + <module name="ModifierOrder"/> + <module name="RedundantModifier"/> + + + <!-- Checks for blocks. You know, those {}'s --> + <!-- See http://checkstyle.sf.net/config_blocks.html --> + <module name="AvoidNestedBlocks"/> + <module name="EmptyBlock"> + <property name="option" value="text"/> + </module> + <module name="LeftCurly"/> + <module name="NeedBraces"/> + <module name="RightCurly"/> + + + <!-- Checks for common coding problems --> + <!-- See http://checkstyle.sf.net/config_coding.html --> + <!-- <module name="AvoidInlineConditionals"/> --> + <module name="EmptyStatement"/> + <module name="EqualsHashCode"/> + <module name="HiddenField"> + <property name="ignoreConstructorParameter" value="true"/> + <property name="ignoreSetter" value="true"/> + <property name="severity" value="warning"/> + </module> + <module name="IllegalInstantiation"/> + <module name="InnerAssignment"/> + <module name="MagicNumber"> + <property name="severity" value="warning"/> + </module> + <module name="MissingSwitchDefault"/> + <!-- Problem with finding exception types... --> + <module name="RedundantThrows"> + <property name="allowUnchecked" value="true"/> + <property name="suppressLoadErrors" value="true"/> + <property name="severity" value="info"/> + </module> + <module name="SimplifyBooleanExpression"/> + <module name="SimplifyBooleanReturn"/> + + <!-- Checks for class design --> + <!-- See http://checkstyle.sf.net/config_design.html --> + <!-- <module name="DesignForExtension"/> --> + <module name="FinalClass"/> + <module name="HideUtilityClassConstructor"/> + <module name="InterfaceIsType"/> + <module name="VisibilityModifier"/> + + + <!-- Miscellaneous other checks. --> + <!-- See http://checkstyle.sf.net/config_misc.html --> + <module name="ArrayTypeStyle"/> + <!-- <module name="FinalParameters"/> --> + <module name="TodoComment"> + <property name="format" value="TODO"/> + <property name="severity" value="info"/> + </module> + <module name="UpperEll"/> + + <module name="FileContentsHolder"/> <!-- Required by comment suppression filters --> + + </module> + + <!-- Enable suppression comments --> + <module name="SuppressionCommentFilter"> + <property name="offCommentFormat" value="CHECKSTYLE IGNORE\s+(\S+)"/> + <property name="onCommentFormat" value="CHECKSTYLE END IGNORE\s+(\S+)"/> + <property name="checkFormat" value="$1"/> + </module> + <module name="SuppressWithNearbyCommentFilter"> + <!-- Syntax is "SUPPRESS CHECKSTYLE name" --> + <property name="commentFormat" value="SUPPRESS CHECKSTYLE (\w+)"/> + <property name="checkFormat" value="$1"/> + <property name="influenceFormat" value="1"/> + </module> +</module> diff --git a/third_party/java/jarjar/gradle.properties b/third_party/java/jarjar/gradle.properties new file mode 100644 index 0000000000..aef125e083 --- /dev/null +++ b/third_party/java/jarjar/gradle.properties @@ -0,0 +1 @@ +version=1.0.0 diff --git a/third_party/java/jarjar/gradle/buildscript.gradle b/third_party/java/jarjar/gradle/buildscript.gradle new file mode 100644 index 0000000000..c80830916e --- /dev/null +++ b/third_party/java/jarjar/gradle/buildscript.gradle @@ -0,0 +1,11 @@ +// Executed in context of buildscript +repositories { + mavenLocal() + mavenCentral() + jcenter() + // maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } +} + +dependencies { + classpath 'org.anarres.gradle:gradle-stdproject-plugin:1.0.2' +} diff --git a/third_party/java/jarjar/gradle/plugin.gradle b/third_party/java/jarjar/gradle/plugin.gradle new file mode 100644 index 0000000000..666d0774bc --- /dev/null +++ b/third_party/java/jarjar/gradle/plugin.gradle @@ -0,0 +1,20 @@ +plugindev { + pluginImplementationClass 'org.anarres.gradle.plugin.jarjar.JarjarPlugin' + pluginDescription "Jarjar Links makes it easy to repackage Java libraries." + pluginLicenses 'Apache-2.0' + pluginTags 'gradle', 'plugin', 'jarjar', 'links', 'asm' + authorId 'shevek' + authorName 'Shevek' + authorEmail 'github@anarres.org' + projectUrl "https://github.com/shevek/${rootProject.githubProjectName}" + projectIssuesUrl "https://github.com/shevek/${rootProject.githubProjectName}/issues" + projectVcsUrl "https://github.com/shevek/${rootProject.githubProjectName}.git" + projectInceptionYear '2014' + done() +} + +bintray { + user = rootProject.hasProperty('bintrayUsername')?rootProject.bintrayUsername:'' + key = rootProject.hasProperty('bintrayApiKey')?rootProject.bintrayApiKey:'' + pkg.repo = 'gradle-plugins' +} diff --git a/third_party/java/jarjar/gradle/wrapper/gradle-wrapper.jar b/third_party/java/jarjar/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000000..c97a8bdb90 --- /dev/null +++ b/third_party/java/jarjar/gradle/wrapper/gradle-wrapper.jar diff --git a/third_party/java/jarjar/gradle/wrapper/gradle-wrapper.properties b/third_party/java/jarjar/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..ca384f3642 --- /dev/null +++ b/third_party/java/jarjar/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Mar 29 22:39:26 PDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip diff --git a/third_party/java/jarjar/gradlew b/third_party/java/jarjar/gradlew new file mode 100755 index 0000000000..91a7e269e1 --- /dev/null +++ b/third_party/java/jarjar/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/third_party/java/jarjar/gradlew.bat b/third_party/java/jarjar/gradlew.bat new file mode 100644 index 0000000000..aec99730b4 --- /dev/null +++ b/third_party/java/jarjar/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/third_party/java/jarjar/jarjar-ant/build.gradle b/third_party/java/jarjar/jarjar-ant/build.gradle new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/build.gradle diff --git a/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/JarJarTask.java b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/JarJarTask.java new file mode 100644 index 0000000000..2f54ad01a7 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/JarJarTask.java @@ -0,0 +1,67 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import com.tonicsystems.jarjar.transform.config.ClassDelete; +import com.tonicsystems.jarjar.transform.config.ClassKeepTransitive; +import com.tonicsystems.jarjar.transform.config.ClassRename; +import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor; +import com.tonicsystems.jarjar.util.AntJarProcessor; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import org.apache.tools.ant.BuildException; + +public class JarJarTask extends AntJarProcessor { + + private DefaultJarProcessor processor = new DefaultJarProcessor(); + + @Nonnull + private static String checkNotNull(@CheckForNull String in, @Nonnull String msg) { + if (in == null) + throw new IllegalArgumentException(msg); + return in; + } + + public void addConfiguredRule(Rule rule) { + processor.addClassRename(new ClassRename( + checkNotNull(rule.getPattern(), "The <rule> element requires the \"pattern\" attribute."), + checkNotNull(rule.getResult(), "The <rule> element requires the \"result\" attribute.") + )); + } + + public void addConfiguredZap(Zap zap) { + processor.addClassDelete(new ClassDelete( + checkNotNull(zap.getPattern(), "The <zap> element requires a \"pattern\" attribute.") + )); + } + + public void addConfiguredKeep(Keep keep) { + processor.addClassKeepTransitive(new ClassKeepTransitive( + checkNotNull(keep.getPattern(), "The <keep> element requires a \"pattern\" attribute.") + )); + } + + @Override + public void execute() throws BuildException { + execute(processor); + } + + @Override + protected void cleanHelper() { + super.cleanHelper(); + processor = new DefaultJarProcessor(); + } +} diff --git a/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Keep.java b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Keep.java new file mode 100644 index 0000000000..70f407e851 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Keep.java @@ -0,0 +1,13 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar; + +/** + * + * @author shevek + */ +public class Keep extends PatternElement { +} diff --git a/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/PatternElement.java b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/PatternElement.java new file mode 100644 index 0000000000..de6722fcd2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/PatternElement.java @@ -0,0 +1,38 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import javax.annotation.Nonnull; + +/** + * This object and its subclasses are also exposed to ant, so need setters for XML. + * + * @author shevek + */ +public abstract class PatternElement { + + private String pattern; + + @Nonnull + public String getPattern() { + return pattern; + } + + public void setPattern(@Nonnull String pattern) { + this.pattern = pattern; + } + +} diff --git a/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Rule.java b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Rule.java new file mode 100644 index 0000000000..886d431d16 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Rule.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar; + +/** + * + * @author shevek + */ +public class Rule extends PatternElement { + + private String result; + + public void setResult(String result) { + this.result = result; + } + + public String getResult() { + return result; + } +} diff --git a/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Zap.java b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Zap.java new file mode 100644 index 0000000000..36288574e3 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/Zap.java @@ -0,0 +1,13 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar; + +/** + * + * @author shevek + */ +public class Zap extends PatternElement { +} diff --git a/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/util/AntJarProcessor.java b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/util/AntJarProcessor.java new file mode 100644 index 0000000000..afa229fc88 --- /dev/null +++ b/third_party/java/jarjar/jarjar-ant/src/main/java/com/tonicsystems/jarjar/util/AntJarProcessor.java @@ -0,0 +1,120 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.util; + +import com.tonicsystems.jarjar.transform.Transformable; +import com.tonicsystems.jarjar.transform.jar.JarProcessor; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Jar; +import org.apache.tools.ant.types.ZipFileSet; +import org.apache.tools.zip.JarMarker; +import org.apache.tools.zip.ZipExtraField; +import org.apache.tools.zip.ZipOutputStream; + +public abstract class AntJarProcessor extends Jar { + + private final Transformable struct = new Transformable(); + private JarProcessor proc; + private final byte[] buf = new byte[0x2000]; + + private final Set<String> dirs = new HashSet<String>(); + private boolean filesOnly; + + protected boolean verbose; + + private static final ZipExtraField[] JAR_MARKER = new ZipExtraField[]{ + JarMarker.getInstance() + }; + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + @Override + public abstract void execute() throws BuildException; + + public void execute(JarProcessor proc) throws BuildException { + this.proc = proc; + super.execute(); + } + + @Override + public void setFilesonly(boolean f) { + super.setFilesonly(f); + filesOnly = f; + } + + @Override + protected void zipDir(File dir, ZipOutputStream zOut, String vPath, int mode) + throws IOException { + } + + // TODO: Rewrite this entirely. + @Override + protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath, + long lastModified, File fromArchive, int mode) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IoUtil.copy(is, baos, buf); + struct.data = baos.toByteArray(); + struct.name = vPath; + struct.time = lastModified; + if (proc.process(struct) != JarProcessor.Result.DISCARD) { + if (mode == 0) + mode = ZipFileSet.DEFAULT_FILE_MODE; + if (!filesOnly) { + addParentDirs(struct.name, zOut); + } + super.zipFile(new ByteArrayInputStream(struct.data), + zOut, struct.name, struct.time, fromArchive, mode); + } + } + + private void addParentDirs(String file, ZipOutputStream zOut) throws IOException { + int slash = file.lastIndexOf('/'); + if (slash >= 0) { + String dir = file.substring(0, slash); + if (dirs.add(dir)) { + addParentDirs(dir, zOut); + super.zipDir((File) null, zOut, dir + "/", ZipFileSet.DEFAULT_DIR_MODE, JAR_MARKER); + } + } + } + + @Override + public void reset() { + super.reset(); + cleanHelper(); + } + + @Override + protected void cleanUp() { + super.cleanUp(); + cleanHelper(); + } + + protected void cleanHelper() { + verbose = false; + filesOnly = false; + dirs.clear(); + } +} diff --git a/third_party/java/jarjar/jarjar-command/build.gradle b/third_party/java/jarjar/jarjar-command/build.gradle new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-command/build.gradle diff --git a/third_party/java/jarjar/jarjar-command/src/main/java/com/tonicsystems/jarjar/Main.java b/third_party/java/jarjar/jarjar-command/src/main/java/com/tonicsystems/jarjar/Main.java new file mode 100644 index 0000000000..985af607d3 --- /dev/null +++ b/third_party/java/jarjar/jarjar-command/src/main/java/com/tonicsystems/jarjar/Main.java @@ -0,0 +1,147 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor; +import com.tonicsystems.jarjar.transform.config.RulesFileParser; +import com.tonicsystems.jarjar.dependencies.TextDependencyHandler; +import com.tonicsystems.jarjar.dependencies.DependencyFinder; +import com.tonicsystems.jarjar.dependencies.DependencyHandler; +import com.tonicsystems.jarjar.strings.StringDumper; +import com.tonicsystems.jarjar.transform.config.AbstractPattern; +import com.tonicsystems.jarjar.transform.JarTransformer; +import java.io.File; +import java.io.IOException; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; + +public class Main { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String PATH_SEPARATOR = System.getProperty("path.separator"); + + public static enum Mode { + + strings, find, process; + } + + private final OptionParser parser = new OptionParser(); + private final OptionSpec<Void> helpOption = parser.accepts("help") + .forHelp(); + private final OptionSpec<Mode> modeOption = parser.accepts("mode") + .withRequiredArg().ofType(Mode.class).defaultsTo(Mode.process).describedAs("Mode to run (strings, find, process)"); + private final OptionSpec<DependencyHandler.Level> levelOption = parser.accepts("level") + .withRequiredArg().ofType(DependencyHandler.Level.class).defaultsTo(DependencyHandler.Level.CLASS).describedAs("Level for DepHandler."); + private final OptionSpec<File> fromFilesOption = parser.accepts("from") + .withRequiredArg().ofType(File.class).withValuesSeparatedBy(PATH_SEPARATOR).describedAs("Classpath for strings, find."); + private final OptionSpec<File> rulesOption = parser.accepts("rules") + .withRequiredArg().ofType(File.class).describedAs("Rules file."); + private final OptionSpec<File> outputOption = parser.accepts("output") + .withRequiredArg().ofType(File.class).describedAs("Output JAR file."); + private final OptionSpec<File> filesOption = parser.nonOptions() + .ofType(File.class).describedAs("JAR files or directories to process."); + + public void run(@Nonnull String[] args) throws Exception { + OptionSet options = parser.parse(args); + if (options.has(helpOption)) { + parser.printHelpOn(System.err); + System.exit(1); + } + + Mode mode = options.valueOf(modeOption); + switch (mode) { + case find: + find(options); + break; + case process: + process(options); + break; + case strings: + strings(options); + break; + default: + throw new IllegalArgumentException("Illegal mode " + mode); + } + } + + private static boolean isEmpty(@CheckForNull List<?> values) { + if (values == null) + return true; + return values.isEmpty(); + } + + @Nonnull + private <T> T valueOf(@Nonnull OptionSet options, @Nonnull OptionSpec<T> option) { + T value = options.valueOf(option); + if (value == null) + throw new IllegalArgumentException(option + " is required."); + return value; + } + + @Nonnull + private <T> List<T> valuesOf(@Nonnull OptionSet options, @Nonnull OptionSpec<T> option) { + List<T> values = options.valuesOf(option); + if (isEmpty(values)) + throw new IllegalArgumentException(option + " is required."); + return values; + } + + private static ClassPath newClassPath(Iterable<? extends File> files) { + return new ClassPath(new File(System.getProperty("user.dir")), files); + } + + public void strings(@Nonnull OptionSet options) throws IOException { + List<File> files = options.valuesOf(filesOption); + File parent = new File(System.getProperty("user.dir")); + new StringDumper().run(System.out, newClassPath(files)); + System.out.flush(); + } + + public void find(@Nonnull OptionSet options) throws IOException { + List<File> toFiles = valuesOf(options, filesOption); + List<File> fromFiles = options.valuesOf(fromFilesOption); + if (isEmpty(fromFiles)) + fromFiles = toFiles; + DependencyHandler.Level level = valueOf(options, levelOption); + DependencyHandler handler = new TextDependencyHandler(System.out, level); + new DependencyFinder().run(handler, newClassPath(fromFiles), newClassPath(toFiles)); + System.out.flush(); + } + + public void process(@Nonnull OptionSet options) throws IOException { + File outputFile = valueOf(options, outputOption); + File rulesFile = valueOf(options, rulesOption); + List<File> files = valuesOf(options, filesOption); + + DefaultJarProcessor processor = new DefaultJarProcessor(); + RulesFileParser.parse(processor, rulesFile); + processor.setSkipManifest(Boolean.getBoolean("skipManifest")); + + JarTransformer transformer = new JarTransformer(outputFile, processor); + transformer.transform(newClassPath(files)); + } + + public static void main(String[] args) throws Exception { + Main main = new Main(); + main.run(args); + // MainUtil.runMain(new Main(), args, "help"); + } +} diff --git a/third_party/java/jarjar/jarjar-command/src/test/resources/com/tonicsystems/jarjar/help.txt b/third_party/java/jarjar/jarjar-command/src/test/resources/com/tonicsystems/jarjar/help.txt new file mode 100644 index 0000000000..8410909164 --- /dev/null +++ b/third_party/java/jarjar/jarjar-command/src/test/resources/com/tonicsystems/jarjar/help.txt @@ -0,0 +1,75 @@ +Jar Jar Links - A utility to repackage and embed Java libraries +Copyright 2007 Google Inc. + +Command-line usage: + + java -jar jarjar.jar [help] + + Prints this help message. + + java -jar jarjar.jar strings <cp> + + Dumps all string literals in classpath <cp>. Line numbers will be + included if the classes have debug information. + + java -jar jarjar.jar find <level> <cp1> [<cp2>] + + Prints dependencies on classpath <cp2> in classpath <cp1>. If <cp2> + is omitted, <cp1> is used for both arguments. + + The level argument must be "class" or "jar". The former prints + dependencies between individual classes, while the latter only + prints jar->jar dependencies. A "jar" in this context is actually + any classpath component, which can be a jar file, a zip file, or a + parent directory (see below). + + java -jar jarjar.jar process <rulesFile> <inJar> <outJar> + + Transform the <inJar> jar file, writing a new jar file to <outJar>. + Any existing file named by <outJar> will be deleted. + + The transformation is defined by a set of rules in the file specified + by the rules argument (see below). + +Classpath format: + + The classpath argument is a colon or semi-colon delimited set + (depending on platform) of directories, jar files, or zip files. See + the following page for more details: + http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/classpath.html + + Mustang-style wildcards are also supported: + http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6268383 + +Rules file format: + + The rules file is a text file, one rule per line. Leading and trailing + whitespace is ignored. There are three types of rules: + + rule <pattern> <result> + zap <pattern> + keep <pattern> + + The standard rule ("rule") is used to rename classes. All references + to the renamed classes will also be updated. If a class name is + matched by more than one rule, only the first one will apply. + + <pattern> is a class name with optional wildcards. "**" will + match against any valid class name substring. To match a single + package component (by excluding "." from the match), a single "*" may + be used instead. + + <result> is a class name which can optionally reference the + substrings matched by the wildcards. A numbered reference is available + for every "*" or "**" in the <pattern>, starting from left to + right: "@1", "@2", etc. A special "@0" reference contains the entire + matched class name. + + The "zap" rule causes any matched class to be removed from the resulting + jar file. All zap rules are processed before renaming rules. + + The "keep" rule marks all matched classes as "roots". If any keep + rules are defined all classes which are not reachable from the roots + via dependency analysis are discarded when writing the output + jar. This is the last step in the process, after renaming and zapping. + diff --git a/third_party/java/jarjar/jarjar-core/build.gradle b/third_party/java/jarjar/jarjar-core/build.gradle new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/build.gradle diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPath.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPath.java new file mode 100644 index 0000000000..c12929b3d2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPath.java @@ -0,0 +1,71 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.classpath; + +import java.io.File; +import java.util.Arrays; +import java.util.Iterator; +import javax.annotation.Nonnull; + +/** + * + * @author shevek + */ +public class ClassPath implements Iterable<ClassPathArchive> { + + private final File root; + private final Iterable<? extends File> entries; + + public ClassPath(@Nonnull File root, @Nonnull Iterable<? extends File> entries) { + this.root = root; + this.entries = entries; + } + + public ClassPath(@Nonnull File root, @Nonnull File[] entries) { + this(root, Arrays.asList(entries)); + } + + @Nonnull + public File getRoot() { + return root; + } + + @Override + public Iterator<ClassPathArchive> iterator() { + return new PathIterator(); + } + + private class PathIterator implements Iterator<ClassPathArchive> { + + private final Iterator<? extends File> entryIterator; + + public PathIterator() { + this.entryIterator = entries.iterator(); + } + + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public ClassPathArchive next() { + File entryFile = entryIterator.next(); + if (!entryFile.isAbsolute()) + entryFile = new File(root, entryFile.getPath()); + if (entryFile.isDirectory()) + return new ClassPathArchive.DirectoryArchive(entryFile); + else + return new ClassPathArchive.ZipArchive(entryFile); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPathArchive.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPathArchive.java new file mode 100644 index 0000000000..9c3a4bf470 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPathArchive.java @@ -0,0 +1,200 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.classpath; + +import com.tonicsystems.jarjar.util.ClassNameUtils; +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.BufferedInputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import javax.annotation.Nonnull; + +/** + * + * @author shevek + */ +public abstract class ClassPathArchive implements Iterable<ClassPathResource> { + + protected final File root; + + public ClassPathArchive(@Nonnull File root) { + this.root = root; + } + + @Nonnull + public String getArchiveName() { + return root.getPath(); + } + + public static class ZipArchive extends ClassPathArchive { + + public ZipArchive(@Nonnull File root) { + super(root); + } + + @Override + public Iterator<ClassPathResource> iterator() { + try { + return new ZipIterator(root); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + } + + } + + private static class ZipIterator implements Iterator<ClassPathResource>, Closeable { + + private final ZipFile zipFile; + private Enumeration<? extends ZipEntry> zipEntries; + + ZipIterator(@Nonnull File file) throws IOException { + this.zipFile = new ZipFile(file); + this.zipEntries = zipFile.entries(); + } + + @Override + public boolean hasNext() { + if (!zipEntries.hasMoreElements()) { + // close(); + return false; + } else { + return true; + } + } + + @Override + public ClassPathResource next() { + final ZipEntry entry = zipEntries.nextElement(); + if (entry == null) + throw new NoSuchElementException(); + return new ClassPathResource() { + @Override + public String getArchiveName() { + return zipFile.getName(); + } + + @Override + public String getName() { + return entry.getName(); + } + + @Override + public long getLastModifiedTime() { + return entry.getTime(); + } + + @Override + public InputStream openStream() throws IOException { + return zipFile.getInputStream(entry); + } + }; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + zipFile.close(); + } + } + + public static class DirectoryArchive extends ClassPathArchive { + + public DirectoryArchive(@Nonnull File root) { + super(root); + } + + @Override + public Iterator<ClassPathResource> iterator() { + return new DirectoryIterator(root); + } + + } + + private static class DirectoryIterator implements Iterator<ClassPathResource> { + + @Nonnull + private static void findClassFiles(@Nonnull Collection<? super File> out, @Nonnull File dir) { + for (File file : dir.listFiles()) { + if (file.isDirectory()) { + findClassFiles(out, file); + } else if (file.isFile()) { + if (ClassNameUtils.isClass(file.getName())) { + out.add(file); + } + } + } + } + + private final File root; + private final Iterator<File> entries; + + DirectoryIterator(@Nonnull File root) { + this.root = root; + List<File> files = new ArrayList<File>(); + findClassFiles(files, root); + this.entries = files.iterator(); + } + + @Override + public boolean hasNext() { + return entries.hasNext(); + } + + @Override + public ClassPathResource next() { + final File file = entries.next(); + return new ClassPathResource() { + @Override + public String getArchiveName() { + return root.getPath(); + } + + @Override + public String getName() { + return file.getName(); + } + + @Override + public long getLastModifiedTime() { + return file.lastModified(); + } + + @Override + public InputStream openStream() throws IOException { + return new BufferedInputStream(new FileInputStream(file)); + } + }; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + root + ")"; + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPathResource.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPathResource.java new file mode 100644 index 0000000000..587bfdaa13 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/classpath/ClassPathResource.java @@ -0,0 +1,40 @@ +/** + * Copyright 2008 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.classpath; + +import java.io.InputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public abstract class ClassPathResource { + + @Nonnull + public abstract String getArchiveName(); + + @Nonnull + public abstract String getName(); + + public abstract long getLastModifiedTime(); + + @Nonnull + public abstract InputStream openStream() throws IOException; + + @Override + public String toString() { + return getArchiveName() + "!" + getName(); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/AbstractDependencyHandler.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/AbstractDependencyHandler.java new file mode 100644 index 0000000000..17e38d1427 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/AbstractDependencyHandler.java @@ -0,0 +1,53 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import javax.annotation.Nonnull; + +public abstract class AbstractDependencyHandler implements DependencyHandler { + + protected final Level level; + private final Set<Pair<String>> seen = new HashSet<Pair<String>>(); + + protected AbstractDependencyHandler(Level level) { + this.level = level; + } + + @Override + public void handle(Dependency from, Dependency to) throws IOException { + Pair<String> pair; + if (level == Level.JAR) { + pair = new Pair<String>(from.getClassPath(), to.getClassPath()); + } else { + pair = new Pair<String>(from.getClassName(), to.getClassName()); + } + if (seen.add(pair)) + handle(pair.getLeft(), pair.getRight()); + } + + protected abstract void handle(@Nonnull String from, @Nonnull String to) throws IOException; + + @Override + public void handleStart() throws IOException { + } + + @Override + public void handleEnd() throws IOException { + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/ClassHeaderReader.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/ClassHeaderReader.java new file mode 100644 index 0000000000..25a8a74d28 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/ClassHeaderReader.java @@ -0,0 +1,188 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Array; + +/* pp */ class ClassHeaderReader { + + private int access; + private String thisClass; + private String superClass; + private String[] interfaces; + + private InputStream in; + private byte[] b = new byte[0x2000]; + private int[] items = new int[1000]; + private int bsize = 0; + private MyByteArrayInputStream bin = new MyByteArrayInputStream(); + private DataInputStream data = new DataInputStream(bin); + + public int getAccess() { + return access; + } + + public String getClassName() { + return thisClass; + } + + public String getSuperName() { + return superClass; + } + + public String[] getInterfaces() { + return interfaces; + } + + public void read(InputStream in) throws IOException { + try { + this.in = in; + bsize = 0; + access = 0; + thisClass = superClass = null; + interfaces = null; + + try { + buffer(4); + } catch (IOException e) { + // ignore + } + if (b[0] != (byte) 0xCA || b[1] != (byte) 0xFE || b[2] != (byte) 0xBA || b[3] != (byte) 0xBE) + throw new ClassFormatError("Bad magic number"); + + buffer(6); + readUnsignedShort(4); // minorVersion + readUnsignedShort(6); // majorVersion + // TODO: check version + int constant_pool_count = readUnsignedShort(8); + items = (int[]) resizeArray(items, constant_pool_count); + + int index = 10; + for (int i = 1; i < constant_pool_count; i++) { + int size; + buffer(index + 3); // TODO: reduce calls to buffer + int tag = b[index]; + items[i] = index + 1; + switch (tag) { + case 9: // Fieldref + case 10: // Methodref + case 11: // InterfaceMethodref + case 3: // Integer + case 4: // Float + case 12: // NameAndType + size = 4; + break; + case 5: // Long + case 6: // Double + size = 8; + i++; + break; + case 1: // Utf8 + size = 2 + readUnsignedShort(index + 1); + break; + case 7: // Class + case 8: // String + size = 2; + break; + default: + throw new IllegalStateException("Unknown constant pool tag " + tag); + } + index += size + 1; + } + buffer(index + 8); + access = readUnsignedShort(index); + thisClass = readClass(index + 2); + superClass = readClass(index + 4); + int interfaces_count = readUnsignedShort(index + 6); + + index += 8; + buffer(index + interfaces_count * 2); + interfaces = new String[interfaces_count]; + for (int i = 0; i < interfaces_count; i++) { + interfaces[i] = readClass(index); + index += 2; + } + } finally { + in.close(); + } + } + + private String readClass(int index) throws IOException { + index = readUnsignedShort(index); + if (index == 0) + return null; + index = readUnsignedShort(items[index]); + bin.readFrom(b, items[index]); + return data.readUTF(); + } + + private int readUnsignedShort(int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + private static final int CHUNK = 2048; + + private void buffer(int amount) throws IOException { + if (amount > b.length) + b = (byte[]) resizeArray(b, b.length * 2); + if (amount > bsize) { + int rounded = (int) (CHUNK * Math.ceil((float) amount / CHUNK)); + bsize += read(in, b, bsize, rounded - bsize); + if (amount > bsize) + throw new EOFException(); + } + } + + private static int read(InputStream in, byte[] b, int off, int len) throws IOException { + int total = 0; + while (total < len) { + int result = in.read(b, off + total, len - total); + if (result == -1) + break; + total += result; + } + return total; + } + + private static Object resizeArray(Object array, int length) { + if (Array.getLength(array) < length) { + Object newArray = Array.newInstance(array.getClass().getComponentType(), length); + System.arraycopy(array, 0, newArray, 0, Array.getLength(array)); + return newArray; + } else { + return array; + } + } + + private static class MyByteArrayInputStream extends ByteArrayInputStream { + + public MyByteArrayInputStream() { + super(new byte[0]); + } + + public void readFrom(byte[] buf, int pos) { + this.buf = buf; + this.pos = pos; + this.count = buf.length; + } + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/Dependency.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/Dependency.java new file mode 100644 index 0000000000..77f0041189 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/Dependency.java @@ -0,0 +1,44 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import javax.annotation.Nonnull; + +public class Dependency { + + private final String classPath; + private final String className; + + public Dependency(@Nonnull String classPath, @Nonnull String className) { + this.classPath = classPath; + this.className = className; + } + + @Nonnull + public String getClassPath() { + return classPath; + } + + @Nonnull + public String getClassName() { + return className; + } + + @Override + public String toString() { + return classPath + "!" + className; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyFinder.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyFinder.java new file mode 100644 index 0000000000..964b23da25 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyFinder.java @@ -0,0 +1,74 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.classpath.ClassPathArchive; +import com.tonicsystems.jarjar.classpath.ClassPathResource; +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassReader; + +public class DependencyFinder { + + private File curDir = new File(System.getProperty("user.dir")); + + public void setCurrentDirectory(File curDir) { + this.curDir = curDir; + } + + public void run(DependencyHandler handler, ClassPath from, ClassPath to) throws IOException { + try { + ClassHeaderReader header = new ClassHeaderReader(); + Map<String, String> classToArchiveMap = new HashMap<String, String>(); + for (ClassPathArchive toArchive : to) { + for (ClassPathResource toResource : toArchive) { + InputStream in = toResource.openStream(); + try { + header.read(in); + classToArchiveMap.put(header.getClassName(), toArchive.getArchiveName()); + } catch (Exception e) { + System.err.println("Error reading " + toResource.getName() + ": " + e.getMessage()); + } finally { + in.close(); + } + } + } + + handler.handleStart(); + for (ClassPathArchive fromArchive : from) { + for (ClassPathResource fromResource : fromArchive) { + InputStream in = fromResource.openStream(); + try { + new ClassReader(in).accept(new DependencyFinderClassVisitor(classToArchiveMap, fromArchive.getArchiveName(), handler), + ClassReader.SKIP_DEBUG); + } catch (Exception e) { + System.err.println("Error reading " + fromResource.getName() + ": " + e.getMessage()); + } finally { + in.close(); + } + } + } + handler.handleEnd(); + } catch (RuntimeIOException e) { + throw (IOException) e.getCause(); + } + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyFinderClassVisitor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyFinderClassVisitor.java new file mode 100644 index 0000000000..968a4c5a4a --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyFinderClassVisitor.java @@ -0,0 +1,69 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.IOException; +import java.util.Map; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.RemappingClassAdapter; + +class DependencyFinderClassVisitor extends RemappingClassAdapter { + + public DependencyFinderClassVisitor(Map<String, String> classToArchiveMap, String archiveName, DependencyHandler handler) throws IOException { + super(null, new DependencyFinderRemapper(classToArchiveMap, archiveName, handler)); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + ((DependencyFinderRemapper) remapper).setClassName(name); + super.visit(version, access, name, signature, superName, interfaces); + } + + private static class DependencyFinderRemapper extends Remapper { + + private final Map<String, String> classes; + private final String archiveName; + private final DependencyHandler handler; + private Dependency curPathClass; + + public DependencyFinderRemapper(Map<String, String> classes, String archiveName, DependencyHandler handler) throws IOException { + this.classes = classes; + this.archiveName = archiveName; + this.handler = handler; + } + + public void setClassName(String name) { + curPathClass = new Dependency(archiveName, name); + } + + @Override + public String map(String key) { + try { + if (classes.containsKey(key)) { + String otherSource = classes.get(key); + if (!archiveName.equals(otherSource)) { + // TODO: some escape mechanism? + handler.handle(curPathClass, new Dependency(otherSource, key)); + } + } + } catch (IOException e) { + throw new RuntimeIOException(e); + } + return null; + } + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyHandler.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyHandler.java new file mode 100644 index 0000000000..7d01748e1a --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/DependencyHandler.java @@ -0,0 +1,32 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import java.io.IOException; + +public interface DependencyHandler { + + public enum Level { + + CLASS, JAR; + } + + void handleStart() throws IOException; + + void handle(Dependency from, Dependency to) throws IOException; + + void handleEnd() throws IOException; +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/Pair.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/Pair.java new file mode 100644 index 0000000000..8682bf19fe --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/Pair.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.dependencies; + +import javax.annotation.Nonnull; + +/** + * + * @author shevek + */ +public class Pair<T> { + + private final T left; + private final T right; + + public Pair(@Nonnull T left, @Nonnull T right) { + this.left = left; + this.right = right; + } + + @Nonnull + public T getLeft() { + return left; + } + + @Nonnull + public T getRight() { + return right; + } + + @Override + public int hashCode() { + return (left.hashCode() << 8) ^ right.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (null == obj) + return false; + if (!getClass().equals(obj.getClass())) + return false; + Pair o = (Pair) obj; + return left.equals(o.left) + && right.equals(o.right); + } + + @Override + public String toString() { + return left + " : " + right; + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/TextDependencyHandler.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/TextDependencyHandler.java new file mode 100644 index 0000000000..205b12e281 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/dependencies/TextDependencyHandler.java @@ -0,0 +1,33 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.dependencies; + +import java.io.IOException; + +public class TextDependencyHandler extends AbstractDependencyHandler { + + private final Appendable out; + + public TextDependencyHandler(Appendable out, Level level) { + super(level); + this.out = out; + } + + @Override + protected void handle(String from, String to) throws IOException { + out.append(from + " -> " + to + "\n"); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/strings/StringDumper.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/strings/StringDumper.java new file mode 100644 index 0000000000..589764ca96 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/strings/StringDumper.java @@ -0,0 +1,111 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.strings; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.classpath.ClassPathArchive; +import com.tonicsystems.jarjar.classpath.ClassPathResource; +import com.tonicsystems.jarjar.util.IoUtil; +import com.tonicsystems.jarjar.util.RuntimeIOException; +import java.io.IOException; +import java.io.InputStream; +import javax.annotation.Nonnull; +import org.objectweb.asm.ClassReader; + +public class StringDumper { + + public void run(Appendable out, ClassPath classPath) throws IOException { + StringReader stringReader = new DumpStringReader(out); + for (ClassPathArchive classPathArchive : classPath) { + for (ClassPathResource classPathResource : classPathArchive) { + InputStream in = classPathResource.openStream(); + try { + new ClassReader(in).accept(stringReader, 0); + } catch (Exception e) { + System.err.println("Error reading " + classPathResource + ": " + e.getMessage()); + } finally { + in.close(); + } + IoUtil.flush(out); + } + } + } + + private static class DumpStringReader extends StringReader { + + private final Appendable out; + private String className; + + public DumpStringReader(@Nonnull Appendable out) { + this.out = out; + } + + @Override + public void visitString(String className, String value, int line) { + if (value.length() > 0) { + try { + if (!className.equals(this.className)) { + this.className = className; + out.append(className.replace('/', '.')); + } + out.append("\t"); + if (line >= 0) + out.append(line + ": "); + out.append(escapeStringLiteral(value)); + out.append("\n"); + } catch (IOException e) { + throw new RuntimeIOException(e); + } + } + } + }; + + @Nonnull + private static String escapeStringLiteral(@Nonnull String value) { + StringBuilder sb = new StringBuilder(); + sb.append("\""); + for (int i = 0, size = value.length(); i < size; i++) { + char ch = value.charAt(i); + switch (ch) { + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\t': + sb.append("\\t"); + break; + case '\"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + default: + sb.append(ch); + } + } + sb.append("\""); + return sb.toString(); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/strings/StringReader.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/strings/StringReader.java new file mode 100644 index 0000000000..b4bf18d9b9 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/strings/StringReader.java @@ -0,0 +1,114 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.strings; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public abstract class StringReader extends ClassVisitor { + + private int line = -1; + private String className; + + public StringReader() { + super(Opcodes.ASM5); + } + + public abstract void visitString(@Nonnull String className, @Nonnull String value, @Nonnegative int line); + + private void handleObject(Object value) { + if (value instanceof String) + visitString(className, (String) value, line); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + className = name; + line = -1; + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + handleObject(value); + return new FieldVisitor(Opcodes.ASM5) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return StringReader.this.visitAnnotation(desc, visible); + } + }; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new AnnotationVisitor(Opcodes.ASM5) { + @Override + public void visit(String name, Object value) { + handleObject(value); + } + + @Override + public void visitEnum(String name, String desc, String value) { + handleObject(value); + } + + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + return this; + } + }; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM5) { + @Override + public void visitLdcInsn(Object cst) { + handleObject(cst); + } + + @Override + public void visitLineNumber(int line, Label start) { + StringReader.this.line = line; + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, + Handle bsm, Object... bsmArgs) { + for (Object bsmArg : bsmArgs) + handleObject(bsmArg); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return StringReader.this.visitAnnotation(desc, visible); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, + String desc, boolean visible) { + return StringReader.this.visitAnnotation(desc, visible); + } + }; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/JarTransformer.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/JarTransformer.java new file mode 100644 index 0000000000..2fc5b3d0a9 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/JarTransformer.java @@ -0,0 +1,132 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.classpath.ClassPathArchive; +import com.tonicsystems.jarjar.classpath.ClassPathResource; +import com.tonicsystems.jarjar.transform.jar.JarProcessor; +import com.tonicsystems.jarjar.util.IoUtil; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JarTransformer { + + private static final Logger LOG = LoggerFactory.getLogger(JarTransformer.class); + + public static enum DuplicatePolicy { + + DISCARD, ERROR; + } + private final File outputFile; + private final JarProcessor processor; + private DuplicatePolicy duplicatePolicy = DuplicatePolicy.DISCARD; + private final byte[] buf = new byte[0x2000]; + private final Set<String> dirs = new HashSet<String>(); + private final Set<String> files = new HashSet<String>(); + + public JarTransformer(@Nonnull File outputFile, @Nonnull JarProcessor processor) { + this.outputFile = outputFile; + this.processor = processor; + } + + @Nonnull + private Transformable newTransformable(@Nonnull ClassPathResource inputResource) + throws IOException { + Transformable struct = new Transformable(); + struct.name = inputResource.getName(); + struct.time = inputResource.getLastModifiedTime(); + + InputStream in = inputResource.openStream(); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IoUtil.copy(in, out, buf); + struct.data = out.toByteArray(); + } finally { + in.close(); + } + + return struct; + } + + private void addDirs(JarOutputStream outputJarStream, String name) throws IOException { + int dirIdx = name.lastIndexOf('/'); + if (dirIdx == -1) + return; + String dirName = name.substring(0, dirIdx + 1); + if (dirs.add(dirName)) { + JarEntry dirEntry = new JarEntry(dirName); + outputJarStream.putNextEntry(dirEntry); + } + } + + public void transform(@Nonnull ClassPath inputPath) throws IOException { + + SCAN: + { + for (ClassPathArchive inputArchive : inputPath) { + LOG.debug("Scanning archive {}", inputArchive); + for (ClassPathResource inputResource : inputArchive) { + Transformable struct = newTransformable(inputResource); + processor.scan(struct); + } + } + } + + OUT: + { + Set<String> dirs = new HashSet<String>(); + + JarOutputStream outputJarStream = new JarOutputStream(new FileOutputStream(outputFile)); + for (ClassPathArchive inputArchive : inputPath) { + LOG.info("Transforming archive {}", inputArchive); + for (ClassPathResource inputResource : inputArchive) { + Transformable struct = newTransformable(inputResource); + if (processor.process(struct) == JarProcessor.Result.DISCARD) + continue; + + addDirs(outputJarStream, struct.name); + + if (DuplicatePolicy.DISCARD.equals(duplicatePolicy)) { + if (!files.add(struct.name)) { + LOG.debug("Discarding duplicate {}", struct.name); + continue; + } + } + + LOG.debug("Writing {}", struct.name); + JarEntry outputEntry = new JarEntry(struct.name); + outputEntry.setTime(struct.time); + outputEntry.setCompressedSize(-1); + outputJarStream.putNextEntry(outputEntry); + outputJarStream.write(struct.data); + } + } + outputJarStream.close(); + } + + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/Transformable.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/Transformable.java new file mode 100644 index 0000000000..765724ef3d --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/Transformable.java @@ -0,0 +1,23 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform; + +public class Transformable { + + public byte[] data; + public String name; + public long time; +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/ClassTransformer.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/ClassTransformer.java new file mode 100644 index 0000000000..6e99cb95e4 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/ClassTransformer.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform.asm; + +import javax.annotation.Nonnull; +import org.objectweb.asm.ClassVisitor; + +/** + * + * @author shevek + */ +public interface ClassTransformer { + + @Nonnull + public ClassVisitor transform(@Nonnull ClassVisitor v); +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/GetNameClassWriter.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/GetNameClassWriter.java new file mode 100644 index 0000000000..d62f8a3d2c --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/GetNameClassWriter.java @@ -0,0 +1,44 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.asm; + +import javax.annotation.Nonnull; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +public class GetNameClassWriter extends ClassVisitor { + + private String className; + + /** + * Constructs a new GetNameClassWriter. + */ + // * @param flags may include {@link ClassWriter#COMPUTE_FRAMES} * or {@link ClassWriter#COMPUTE_MAXS}. + public GetNameClassWriter(ClassVisitor cv) { + super(Opcodes.ASM5, cv); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + className = name; + super.visit(version, access, name, signature, superName, interfaces); + } + + @Nonnull + public String getClassName() { + return className; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/PackageRemapper.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/PackageRemapper.java new file mode 100644 index 0000000000..1b2aa028dd --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/PackageRemapper.java @@ -0,0 +1,138 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.asm; + +import com.tonicsystems.jarjar.transform.config.ClassRename; +import com.tonicsystems.jarjar.transform.config.PatternUtils; +import com.tonicsystems.jarjar.util.ClassNameUtils; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import org.objectweb.asm.commons.Remapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PackageRemapper extends Remapper { + + private static final Logger LOG = LoggerFactory.getLogger(PackageRemapper.class); + private static final String RESOURCE_SUFFIX = "RESOURCE"; + + private final List<ClassRename> patterns; + private final Map<String, String> typeCache = new HashMap<String, String>(); + private final Map<String, String> pathCache = new HashMap<String, String>(); + private final Map<Object, String> valueCache = new HashMap<Object, String>(); + + public PackageRemapper(@Nonnull Iterable<? extends ClassRename> patterns) { + this.patterns = PatternUtils.toList(patterns); + } + + public PackageRemapper(@Nonnull ClassRename... patterns) { + this(Arrays.asList(patterns)); + } + + public void addRule(@Nonnull ClassRename pattern) { + this.patterns.add(pattern); + } + + @Override + public String map(String key) { + String s = typeCache.get(key); + if (s == null) { + s = replaceHelper(key); + if (key.equals(s)) + s = null; + typeCache.put(key, s); + } + return s; + } + + public String mapPath(String path) { + String s = pathCache.get(path); + if (s == null) { + s = path; + int slash = s.lastIndexOf('/'); + String end; + if (slash < 0) { + end = s; + s = RESOURCE_SUFFIX; + } else { + end = s.substring(slash + 1); + s = s.substring(0, slash + 1) + RESOURCE_SUFFIX; + } + boolean absolute = s.startsWith("/"); + if (absolute) + s = s.substring(1); + + s = replaceHelper(s); + + if (absolute) + s = "/" + s; + if (s.indexOf(RESOURCE_SUFFIX) < 0) + return path; + s = s.substring(0, s.length() - RESOURCE_SUFFIX.length()) + end; + pathCache.put(path, s); + } + return s; + } + + @Override + public Object mapValue(Object value) { + if (value instanceof String) { + String s = valueCache.get(value); + if (s == null) { + s = (String) value; + if (ClassNameUtils.isArrayForName(s)) { + String desc1 = s.replace('.', '/'); + String desc2 = mapDesc(desc1); + if (!desc2.equals(desc1)) + return desc2.replace('/', '.'); + } else { + s = mapPath(s); + if (s.equals(value)) { + boolean hasDot = s.indexOf('.') >= 0; + boolean hasSlash = s.indexOf('/') >= 0; + if (!(hasDot && hasSlash)) { + if (hasDot) { + s = replaceHelper(s.replace('.', '/')).replace('/', '.'); + } else { + s = replaceHelper(s); + } + } + } + } + valueCache.put(value, s); + } + // TODO: add back class name to verbose message + if (!s.equals(value)) + if (LOG.isDebugEnabled()) + LOG.debug("Changed \"" + value + "\" -> \"" + s + "\""); + return s; + } else { + return super.mapValue(value); + } + } + + private String replaceHelper(String value) { + for (ClassRename pattern : patterns) { + String result = pattern.replace(value); + if (result != null) + return result; + } + return value; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/RemappingClassTransformer.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/RemappingClassTransformer.java new file mode 100644 index 0000000000..a30341c6d0 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/asm/RemappingClassTransformer.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform.asm; + +import javax.annotation.Nonnull; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.RemappingClassAdapter; + +/** + * + * @author shevek + */ +public class RemappingClassTransformer implements ClassTransformer { + + private final Remapper remapper; + + public RemappingClassTransformer(@Nonnull Remapper remapper) { + this.remapper = remapper; + } + + @Override + public ClassVisitor transform(ClassVisitor v) { + return new RemappingClassAdapter(v, remapper); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractClassPattern.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractClassPattern.java new file mode 100644 index 0000000000..d76d9e4af7 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractClassPattern.java @@ -0,0 +1,26 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform.config; + +import javax.annotation.Nonnull; + +/** + * + * @author shevek + */ +public abstract class AbstractClassPattern extends AbstractPattern { + + private static String check(String patternText) { + if (patternText.indexOf('/') >= 0) + throw new IllegalArgumentException("Class patterns cannot contain slashes"); + return patternText.replace('.', '/'); + } + + public AbstractClassPattern(@Nonnull String patternText) { + super(check(patternText)); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractPattern.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractPattern.java new file mode 100644 index 0000000000..f88a8093c1 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractPattern.java @@ -0,0 +1,68 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * This object and its subclasses are also exposed to ant, so need setters for XML. + * + * @author shevek + */ +public abstract class AbstractPattern { + + private final String patternText; + private final Pattern pattern; + + public AbstractPattern(@Nonnull String patternText) { + if (patternText == null) + throw new IllegalArgumentException("Pattern text may not be null."); + this.patternText = patternText; + this.pattern = PatternUtils.newPattern(patternText); + } + + @Nonnull + public String getPatternText() { + return patternText; + } + + @Nonnull + public Pattern getPattern() { + return pattern; + } + + @CheckForNull + protected Matcher getMatcher(@Nonnull String value) { + if (!PatternUtils.isPossibleQualifiedName(value, "/")) + return null; + Matcher matcher = pattern.matcher(value); + if (matcher.matches()) + return matcher; + return null; + } + + public boolean matches(@Nonnull String value) { + return getMatcher(value) != null; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + pattern + ")"; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractResourcePattern.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractResourcePattern.java new file mode 100644 index 0000000000..1810a810f3 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/AbstractResourcePattern.java @@ -0,0 +1,20 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform.config; + +import javax.annotation.Nonnull; + +/** + * + * @author shevek + */ +public abstract class AbstractResourcePattern extends AbstractPattern { + + public AbstractResourcePattern(@Nonnull String patternText) { + super(patternText); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassDelete.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassDelete.java new file mode 100644 index 0000000000..503d63084d --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassDelete.java @@ -0,0 +1,31 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import javax.annotation.Nonnull; + +/** + * Requires that any matched class is removed. + * + * @author shevek + */ +public class ClassDelete extends AbstractClassPattern { + + public ClassDelete(@Nonnull String pattern) { + super(pattern); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassKeep.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassKeep.java new file mode 100644 index 0000000000..1d5cca4286 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassKeep.java @@ -0,0 +1,30 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import javax.annotation.Nonnull; + +/** + * Requires that any matched class is kept. + * + * @author shevek + */ +public class ClassKeep extends AbstractClassPattern { + + public ClassKeep(@Nonnull String pattern) { + super(pattern); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassKeepTransitive.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassKeepTransitive.java new file mode 100644 index 0000000000..175fe7f6b6 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassKeepTransitive.java @@ -0,0 +1,30 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import javax.annotation.Nonnull; + +/** + * Requires that any matched class and all classes directly or indirectly reachable from it are kept. + * + * @author shevek + */ +public class ClassKeepTransitive extends AbstractClassPattern { + + public ClassKeepTransitive(@Nonnull String pattern) { + super(pattern); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassRename.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassRename.java new file mode 100644 index 0000000000..02a500e615 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ClassRename.java @@ -0,0 +1,44 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * Requires that any matched class is renamed. + * + * @author shevek + */ +public class ClassRename extends AbstractClassPattern { + + // private final String replaceText; + private final List<Object> replace; + + public ClassRename(@Nonnull String patternText, @Nonnull String replaceText) { + super(patternText); + if (replaceText == null) + throw new IllegalArgumentException("Result may not be null."); + // this.replaceText = replaceText; + this.replace = PatternUtils.newReplace(getPattern(), replaceText); + } + + @CheckForNull + public String replace(@Nonnull String value) { + return PatternUtils.replace(this, replace, value); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/PatternUtils.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/PatternUtils.java new file mode 100644 index 0000000000..cd88ed4c3e --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/PatternUtils.java @@ -0,0 +1,245 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; + +public class PatternUtils { + + private PatternUtils() { + } + + private static final Pattern dstar = Pattern.compile("\\*\\*"); + private static final Pattern star = Pattern.compile("\\*"); + private static final Pattern estar = Pattern.compile("\\+\\??\\)\\Z"); + + @Nonnull + private static String replaceAllLiteral(@Nonnull String value, @Nonnull Pattern pattern, @Nonnull String replace) { + return pattern.matcher(value).replaceAll(Matcher.quoteReplacement(replace)); + } + + @Nonnull + public static Pattern newPattern(@Nonnull String pattern) { + if (pattern.equals("**")) + throw new IllegalArgumentException("'**' is not a valid pattern"); + if (!isPossibleQualifiedName(pattern, "/*")) + throw new IllegalArgumentException("Not a valid package pattern: " + pattern); + if (pattern.indexOf("***") >= 0) + throw new IllegalArgumentException("The sequence '***' is invalid in a package pattern"); + + String regex = pattern; + regex = replaceAllLiteral(regex, dstar, "(.+?)"); // One wildcard test requires the argument to be allowably empty. + regex = replaceAllLiteral(regex, star, "([^/]+)"); + regex = replaceAllLiteral(regex, estar, "*\\??)"); // Although we replaced with + above, we mean * + return Pattern.compile("\\A" + regex + "\\Z"); + // this.count = this.pattern.matcher("foo").groupCount(); + } + + private static enum State { + + NORMAL, ESCAPE; + } + + @Nonnull + public static List<Object> newReplace(@Nonnull Pattern pattern, @Nonnull String result) { + List<Object> parts = new ArrayList<Object>(16); + // TODO: check for illegal characters + int max = 0; + State state = State.NORMAL; + for (int i = 0, mark = 0, len = result.length(); i < len + 1; i++) { + char ch = (i == len) ? '@' : result.charAt(i); + switch (state) { + case NORMAL: + if (ch == '@') { + parts.add(result.substring(mark, i).replace('.', '/')); + mark = i + 1; + state = State.ESCAPE; + } + break; + case ESCAPE: + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + if (i == mark) + throw new IllegalArgumentException("Backslash not followed by a digit"); + int n = Integer.parseInt(result.substring(mark, i)); + if (n > max) + max = n; + parts.add(Integer.valueOf(n)); + mark = i--; + state = State.NORMAL; + break; + } + break; + } + } + + int count = pattern.matcher("foo").groupCount(); + if (count < max) + throw new IllegalArgumentException("Result includes impossible placeholder \"@" + max + "\": " + result); + // System.err.println(this); + return parts; + } + + public static String replace(@Nonnull AbstractPattern pattern, @Nonnull List<Object> replace, String value) { + Matcher matcher = pattern.getMatcher(value); + if (matcher == null) + return null; + StringBuilder sb = new StringBuilder(); + for (Object part : replace) { + if (part instanceof String) + sb.append((String) part); + else + sb.append(matcher.group((Integer) part)); + } + return sb.toString(); + } + + public static final String PACKAGE_INFO = "package-info"; + + /* pp */ static boolean isPossibleQualifiedName(@Nonnull String value, @Nonnull String extraAllowedCharacters) { + // package-info violates the spec for Java Identifiers. + // Nevertheless, expressions that end with this string are still legal. + // See 7.4.1.1 of the Java language spec for discussion. + if (value.endsWith(PACKAGE_INFO)) { + value = value.substring(0, value.length() - PACKAGE_INFO.length()); + } + for (int i = 0, len = value.length(); i < len; i++) { + char c = value.charAt(i); + if (Character.isJavaIdentifierPart(c)) + continue; + if (extraAllowedCharacters.indexOf(c) >= 0) + continue; + return false; + } + return true; + } + + /** + * Copies the given {@link Iterable} into a new {@link List}. + * + * @param <T> The free parameter for the element type. + * @param in The Iterable to copy. + * @return A new, mutable {@link ArrayList}. + */ + @Nonnull + public static <T extends AbstractPattern> List<T> toList(@Nonnull Iterable<? extends T> in) { + List<T> out = new ArrayList<T>(); + for (T i : in) + out.add(i); + return out; + } + + // Adapted from http://stackoverflow.com/questions/1247772/is-there-an-equivalent-of-java-util-regex-for-glob-type-patterns + @Nonnull + public static String convertGlobToRegEx(@Nonnull String line) { + line = line.trim(); + int strLen = line.length(); + StringBuilder sb = new StringBuilder(strLen); + // Remove beginning and ending * globs because they're useless + if (line.startsWith("*")) { + line = line.substring(1); + strLen--; + } + if (line.endsWith("*")) { + line = line.substring(0, strLen - 1); + strLen--; + } + boolean escaping = false; + int inCurlies = 0; + CHAR: + for (char currentChar : line.toCharArray()) { + switch (currentChar) { + case '*': + if (escaping) + sb.append("\\*"); + else + sb.append(".*"); + break; + case '?': + if (escaping) + sb.append("\\?"); + else + sb.append('.'); + break; + case '.': + case '(': + case ')': + case '+': + case '|': + case '^': + case '$': + case '@': + case '%': + sb.append('\\'); + sb.append(currentChar); + break; + case '\\': + if (escaping) + sb.append("\\\\"); + else { + escaping = true; + continue CHAR; + } + break; + case '{': + if (escaping) + sb.append("\\{"); + else { + sb.append('('); + inCurlies++; + } + break; + case '}': + if (escaping) + sb.append("\\}"); + else if (inCurlies > 0) { + sb.append(')'); + inCurlies--; + } else + sb.append("}"); + break; + case ',': + if (escaping) + sb.append("\\,"); + else if (inCurlies > 0) + sb.append('|'); + else + sb.append(","); + break; + default: + sb.append(currentChar); + break; + } + escaping = false; + } + return sb.toString(); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ResourceRename.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ResourceRename.java new file mode 100644 index 0000000000..91cfc4020c --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/ResourceRename.java @@ -0,0 +1,44 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * Requires that any matched resource is renamed. + * + * @author shevek + */ +public class ResourceRename extends AbstractResourcePattern { + + // private final String replaceText; + private final List<Object> replace; + + public ResourceRename(@Nonnull String patternText, @Nonnull String replaceText) { + super(patternText); + if (replaceText == null) + throw new IllegalArgumentException("Result may not be null."); + // this.replaceText = replaceText; + this.replace = PatternUtils.newReplace(getPattern(), replaceText); + } + + @CheckForNull + public String replace(@Nonnull String value) { + return PatternUtils.replace(this, replace, value); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/RulesFileParser.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/RulesFileParser.java new file mode 100644 index 0000000000..59377ad9eb --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/config/RulesFileParser.java @@ -0,0 +1,105 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.config; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import javax.annotation.WillClose; + +public class RulesFileParser { + + public interface Output { + + public void addClassDelete(@Nonnull ClassDelete classDelete); + + public void addClassRename(@Nonnull ClassRename classRename); + + public void addClassKeep(@Nonnull ClassKeep classKeep); + + public void addClassKeepTransitive(@Nonnull ClassKeepTransitive classKeepTransitive); + } + + private RulesFileParser() { + } + + @Nonnull + public static void parse(@Nonnull Output output, @Nonnull File file) throws IOException { + parse(output, new FileReader(file)); + } + + @Nonnull + public static void parse(@Nonnull Output output, @Nonnull String value) throws IOException { + parse(output, new StringReader(value)); + } + + @Nonnull + private static List<String> split(@Nonnull String line) { + StringTokenizer tok = new StringTokenizer(line); + List<String> out = new ArrayList<String>(); + while (tok.hasMoreTokens()) { + String token = tok.nextToken(); + if (token.startsWith("#")) + break; + out.add(token); + } + return out; + } + + @Nonnull + private static void parse(@Nonnull Output output, @Nonnull @WillClose Reader r) throws IOException { + try { + BufferedReader br = new BufferedReader(r); + int lineNumber = 1; + String line; + while ((line = br.readLine()) != null) { + List<String> words = split(line); + if (words.isEmpty()) + continue; + if (words.size() < 2) + throw error(lineNumber, words, "not enough words on line."); + String type = words.get(0); + if (type.equals("rule")) { + if (words.size() < 3) + throw error(lineNumber, words, "'rule' requires 2 arguments."); + output.addClassRename(new ClassRename(words.get(1), words.get(2))); + } else if (type.equals("zap")) { + output.addClassDelete(new ClassDelete(words.get(1))); + } else if (type.equals("keep")) { + output.addClassKeepTransitive(new ClassKeepTransitive(words.get(1))); + } else { + throw error(lineNumber, words, "Unrecognized keyword " + type); + } + lineNumber++; + } + } finally { + r.close(); + } + } + + @Nonnull + private static IllegalArgumentException error(@Nonnegative int lineNumber, @Nonnull List<String> words, @Nonnull String reason) { + throw new IllegalArgumentException("Error on line " + lineNumber + ": " + words + ": " + reason); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/AbstractFilterJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/AbstractFilterJarProcessor.java new file mode 100644 index 0000000000..dc9ce4130a --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/AbstractFilterJarProcessor.java @@ -0,0 +1,47 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.Transformable; +import java.io.IOException; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author shevek + */ +public abstract class AbstractFilterJarProcessor implements JarProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractFilterJarProcessor.class); + + protected abstract boolean isFiltered(@Nonnull String name); + + protected boolean isVerbose() { + return true; + } + + @Override + public Result scan(Transformable struct) throws IOException { + if (isFiltered(struct.name)) { + if (isVerbose()) + LOG.debug("{}.scan discarded {}", getClass().getSimpleName(), struct.name); + return Result.DISCARD; + } + return Result.KEEP; + } + + @Override + public Result process(Transformable struct) throws IOException { + if (isFiltered(struct.name)) { + if (isVerbose()) + LOG.debug("{}.process discarded {}", getClass().getSimpleName(), struct.name); + return Result.DISCARD; + } + return Result.KEEP; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassClosureJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassClosureJarProcessor.java new file mode 100644 index 0000000000..c5f9318e43 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassClosureJarProcessor.java @@ -0,0 +1,143 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.config.PatternUtils; +import com.tonicsystems.jarjar.transform.config.ClassKeepTransitive; +import com.tonicsystems.jarjar.transform.Transformable; +import com.tonicsystems.jarjar.util.ClassNameUtils; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nonnull; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.RemappingClassAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Keeps all classes reachable from a given set of roots. + * + * Put this early in the chain as it does not honour renames. + */ +public class ClassClosureJarProcessor extends AbstractFilterJarProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(ClassClosureJarProcessor.class); + + private static class DependencyCollector extends Remapper { + + private final Set<String> dependencies = new HashSet<String>(); + + @Override + public String map(String key) { + if (key.startsWith("java/") || key.startsWith("javax/")) + return null; + dependencies.add(key); + return null; + } + + @Override + public Object mapValue(Object value) { + if (value instanceof String) { + String s = (String) value; + if (ClassNameUtils.isArrayForName(s)) { + mapDesc(s.replace('.', '/')); + } else if (ClassNameUtils.isForName(s)) { + map(s.replace('.', '/')); + } + return value; + } else { + return super.mapValue(value); + } + } + } + + private final List<ClassKeepTransitive> patterns; + private final List<String> roots = new ArrayList<String>(); + private final Map<String, Set<String>> dependencies = new HashMap<String, Set<String>>(); + private Set<String> closure; + + public ClassClosureJarProcessor(@Nonnull Iterable<? extends ClassKeepTransitive> patterns) { + this.patterns = PatternUtils.toList(patterns); + } + + public ClassClosureJarProcessor(@Nonnull ClassKeepTransitive... patterns) { + this(Arrays.asList(patterns)); + } + + public void addKeep(@Nonnull ClassKeepTransitive pattern) { + patterns.add(pattern); + } + + private boolean isEnabled() { + return !patterns.isEmpty(); + } + + @Override + public Result scan(Transformable struct) throws IOException { + if (!isEnabled()) + return Result.KEEP; + try { + if (ClassNameUtils.isClass(struct.name)) { + String name = struct.name.substring(0, struct.name.length() - 6); + for (ClassKeepTransitive pattern : patterns) + if (pattern.matches(name)) + roots.add(name); + DependencyCollector collector = new DependencyCollector(); + dependencies.put(name, collector.dependencies); + new ClassReader(new ByteArrayInputStream(struct.data)).accept(new RemappingClassAdapter(null, collector), ClassReader.EXPAND_FRAMES); + collector.dependencies.remove(name); + } + } catch (Exception e) { + LOG.warn("Error reading " + struct.name + ": " + e.getMessage()); + } + return Result.KEEP; + } + + private void addTransitiveClosure(Collection<? super String> out, Collection<String> itemDependencies) { + if (itemDependencies == null) + return; + for (String name : itemDependencies) + if (out.add(name)) + addTransitiveClosure(out, dependencies.get(name)); + } + + @Override + protected boolean isFiltered(String name) { + if (closure == null) { + closure = new HashSet<String>(); + addTransitiveClosure(closure, roots); + } + return !closure.contains(name); + } + + @Override + public Result process(Transformable struct) throws IOException { + if (!isEnabled()) + return Result.KEEP; + if (!ClassNameUtils.isClass(struct.name)) + return Result.KEEP; + return super.process(struct); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassFilterJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassFilterJarProcessor.java new file mode 100644 index 0000000000..79798ba02b --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassFilterJarProcessor.java @@ -0,0 +1,83 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.config.AbstractPattern; +import com.tonicsystems.jarjar.transform.config.ClassDelete; +import com.tonicsystems.jarjar.transform.config.ClassKeep; +import com.tonicsystems.jarjar.util.ClassNameUtils; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Filters classes by name. + * + * Keeps all classes specified by ClassKeep (default all classes). + * Then removes all classes specified by ClassDelete (default no classes). + * Ignores non-class resources. + * + * @see ClassNameUtils#isClass(String) + * @author shevek + */ +public class ClassFilterJarProcessor extends AbstractFilterJarProcessor { + + // private static final Logger LOG = LoggerFactory.getLogger(ClassFilterJarProcessor.class); + private final List<ClassKeep> keepPatterns = new ArrayList<ClassKeep>(); + private final List<ClassDelete> deletePatterns = new ArrayList<ClassDelete>(); + + public void addClassKeep(@Nonnull ClassKeep pattern) { + keepPatterns.add(pattern); + } + + public void addClassDelete(@Nonnull ClassDelete pattern) { + deletePatterns.add(pattern); + } + + @CheckForNull + protected <T extends AbstractPattern> T getMatchingPattern(@Nonnull List<? extends T> patterns, @Nonnull String name) { + for (T pattern : patterns) { + if (pattern.matches(name)) { + // LOG.debug(pattern + " matches " + name); + return pattern; + } + } + // LOG.debug("No pattern matches " + name); + return null; + } + + @Override + protected boolean isFiltered(@Nonnull String name) { + if (!ClassNameUtils.isClass(name)) + return false; + name = name.substring(0, name.length() - 6); + // LOG.debug("Looking to include " + name); + INCLUDE: + { + if (keepPatterns.isEmpty()) + break INCLUDE; + if (getMatchingPattern(keepPatterns, name) != null) + break INCLUDE; + // We have include patterns, but none matched. Filter it. + return true; + } + // LOG.debug("Looking to exclude " + name); + return getMatchingPattern(deletePatterns, name) != null; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassTransformerJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassTransformerJarProcessor.java new file mode 100644 index 0000000000..1dfc805a84 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ClassTransformerJarProcessor.java @@ -0,0 +1,73 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.asm.ClassTransformer; +import com.tonicsystems.jarjar.transform.asm.GetNameClassWriter; +import com.tonicsystems.jarjar.transform.Transformable; +import com.tonicsystems.jarjar.util.ClassNameUtils; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import javax.annotation.Nonnull; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A JarProcessor which applies a list of {@link ClassTransformer ClassTransformers} + * to any files ending in .class. + */ +public class ClassTransformerJarProcessor implements JarProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(ClassTransformerJarProcessor.class); + private final List<ClassTransformer> classProcessors; + + public ClassTransformerJarProcessor(@Nonnull List<ClassTransformer> classProcessors) { + this.classProcessors = classProcessors; + } + + public ClassTransformerJarProcessor(@Nonnull ClassTransformer... classProcessors) { + this(Arrays.asList(classProcessors)); + } + + @Override + public Result scan(Transformable struct) throws IOException { + return Result.KEEP; + } + + @Override + public Result process(Transformable struct) throws IOException { + if (ClassNameUtils.isClass(struct.name)) { + try { + ClassReader reader = new ClassReader(struct.data); + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + GetNameClassWriter namer = new GetNameClassWriter(writer); + ClassVisitor cv = namer; + for (ClassTransformer classProcessor : classProcessors) + cv = classProcessor.transform(cv); + reader.accept(cv, ClassReader.EXPAND_FRAMES); + struct.name = ClassNameUtils.javaNameToPath(namer.getClassName()); + struct.data = writer.toByteArray(); + } catch (Exception e) { + LOG.warn("Failed to read class " + struct.name + ": " + e); + } + } + return Result.KEEP; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/DefaultJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/DefaultJarProcessor.java new file mode 100644 index 0000000000..c53757c334 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/DefaultJarProcessor.java @@ -0,0 +1,72 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.asm.PackageRemapper; +import com.tonicsystems.jarjar.transform.config.ClassDelete; +import com.tonicsystems.jarjar.transform.config.ClassKeepTransitive; +import com.tonicsystems.jarjar.transform.config.ClassRename; +import com.tonicsystems.jarjar.transform.asm.RemappingClassTransformer; +import com.tonicsystems.jarjar.transform.config.ClassKeep; +import com.tonicsystems.jarjar.transform.config.RulesFileParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultJarProcessor extends JarProcessorChain implements RulesFileParser.Output { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultJarProcessor.class); + // private final Map<String, String> renames = new HashMap<String, String>(); + + private final ManifestFilterJarProcessor manifestFilterJarProcessor = new ManifestFilterJarProcessor(); + private final ClassFilterJarProcessor classFilterJarProcessor = new ClassFilterJarProcessor(); + private final ClassClosureJarProcessor classClosureFilterJarProcessor = new ClassClosureJarProcessor(); + private final PackageRemapper packageRemapper = new PackageRemapper(); + private final RemappingClassTransformer remappingClassTransformer = new RemappingClassTransformer(packageRemapper); + private final ResourceRenamerJarProcessor resourceRenamerJarProcessor = new ResourceRenamerJarProcessor(packageRemapper); + + public DefaultJarProcessor() { + add(new DirectoryFilterJarProcessor()); + add(manifestFilterJarProcessor); + add(classFilterJarProcessor); + add(classClosureFilterJarProcessor); + add(new ClassTransformerJarProcessor(remappingClassTransformer)); + add(resourceRenamerJarProcessor); + } + + @Override + public void addClassDelete(ClassDelete classDelete) { + classFilterJarProcessor.addClassDelete(classDelete); + } + + @Override + public void addClassRename(ClassRename classRename) { + packageRemapper.addRule(classRename); + } + + @Override + public void addClassKeep(ClassKeep classKeep) { + classFilterJarProcessor.addClassKeep(classKeep); + } + + @Override + public void addClassKeepTransitive(ClassKeepTransitive classKeepTransitive) { + classClosureFilterJarProcessor.addKeep(classKeepTransitive); + } + + public void setSkipManifest(boolean value) { + manifestFilterJarProcessor.setEnabled(value); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/DirectoryFilterJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/DirectoryFilterJarProcessor.java new file mode 100644 index 0000000000..30fdb2a82e --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/DirectoryFilterJarProcessor.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform.jar; + +/** + * + * @author shevek + */ +public class DirectoryFilterJarProcessor extends AbstractFilterJarProcessor { + + @Override + protected boolean isFiltered(String name) { + return name.endsWith("/"); + } + + @Override + protected boolean isVerbose() { + return false; + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/JarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/JarProcessor.java new file mode 100644 index 0000000000..5c652dabe3 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/JarProcessor.java @@ -0,0 +1,48 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.Transformable; +import java.io.IOException; +import javax.annotation.Nonnull; + +public interface JarProcessor { + + public static enum Result { + + KEEP, + DISCARD; + } + + // public boolean isEnabled(); + + @Nonnull + public Result scan(@Nonnull Transformable struct) throws IOException; + + /** + * Process the entry (e.g. rename the file) + * <p> + * Returns <code>true</code> if the processor wants to retain the entry. In this case, the entry can be removed + * from the jar file in a future time. Return <code>false</code> for the entries which do not have been changed and + * there fore are not to be deleted + * + * @param struct The archive entry to be transformed. + * @return <code>true</code> if he process chain can continue after this process + * @throws IOException if it all goes upside down + */ + @Nonnull + public Result process(@Nonnull Transformable struct) throws IOException; +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/JarProcessorChain.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/JarProcessorChain.java new file mode 100644 index 0000000000..397aba6b67 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/JarProcessorChain.java @@ -0,0 +1,50 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.Transformable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import javax.annotation.Nonnull; + +public class JarProcessorChain extends ArrayList<JarProcessor> implements JarProcessor { + + public JarProcessorChain(@Nonnull Iterable<? extends JarProcessor> processors) { + for (JarProcessor processor : processors) + add(processor); + } + + public JarProcessorChain(@Nonnull JarProcessor... processors) { + this(Arrays.asList(processors)); + } + + @Override + public Result scan(Transformable struct) throws IOException { + for (JarProcessor processor : this) + if (processor.scan(struct) == Result.DISCARD) + return Result.DISCARD; + return Result.KEEP; + } + + @Override + public Result process(Transformable struct) throws IOException { + for (JarProcessor processor : this) + if (processor.process(struct) == Result.DISCARD) + return Result.DISCARD; + return Result.KEEP; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ManifestFilterJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ManifestFilterJarProcessor.java new file mode 100644 index 0000000000..d5711fd409 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ManifestFilterJarProcessor.java @@ -0,0 +1,48 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import java.util.Collections; + +/** + * Excludes the manifest. + */ +public class ManifestFilterJarProcessor extends PathFilterJarProcessor { + + public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + + private boolean enabled = false; + + public ManifestFilterJarProcessor() { + super(Collections.singleton(MANIFEST_PATH)); + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + protected boolean isFiltered(String name) { + if (!isEnabled()) + return false; + return super.isFiltered(name); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/PathFilterJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/PathFilterJarProcessor.java new file mode 100644 index 0000000000..c54cd7ca74 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/PathFilterJarProcessor.java @@ -0,0 +1,36 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import java.util.Set; +import javax.annotation.Nonnull; + +/** + * Excludes resources by exact name. + */ +public class PathFilterJarProcessor extends AbstractFilterJarProcessor { + + private final Set<? extends String> excludes; + + public PathFilterJarProcessor(@Nonnull Set<? extends String> excludes) { + this.excludes = excludes; + } + + @Override + protected boolean isFiltered(String name) { + return excludes.contains(name); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ResourceRenamerJarProcessor.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ResourceRenamerJarProcessor.java new file mode 100644 index 0000000000..42c018da40 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/transform/jar/ResourceRenamerJarProcessor.java @@ -0,0 +1,46 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.transform.jar; + +import com.tonicsystems.jarjar.transform.asm.PackageRemapper; +import com.tonicsystems.jarjar.transform.Transformable; +import com.tonicsystems.jarjar.util.ClassNameUtils; +import java.io.IOException; +import javax.annotation.Nonnull; + +/** + * Allows any file which is NOT a JAR file. + */ +public class ResourceRenamerJarProcessor implements JarProcessor { + + private final PackageRemapper pr; + + public ResourceRenamerJarProcessor(@Nonnull PackageRemapper pr) { + this.pr = pr; + } + + @Override + public Result scan(Transformable struct) throws IOException { + return Result.KEEP; + } + + @Override + public Result process(Transformable struct) throws IOException { + if (!ClassNameUtils.isClass(struct.name)) + struct.name = pr.mapPath(struct.name); + return Result.KEEP; + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/ClassNameUtils.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/ClassNameUtils.java new file mode 100644 index 0000000000..7b133aa136 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/ClassNameUtils.java @@ -0,0 +1,79 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.util; + +import java.io.File; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; + +/** + * + * @author shevek + */ +public class ClassNameUtils { + + private static final Pattern ARRAY_FOR_NAME_PATTERN + = Pattern.compile("\\[L[\\p{javaJavaIdentifierPart}\\.]+?;"); + + /** + * Returns true if the given string looks like a Java array name. + * @param value The name to inspect. + * @return true if the given string looks like a Java array name. + */ + // also used by KeepProcessor + public static boolean isArrayForName(String value) { + // Type type = Type.getType(value); + // type.getSort() == ARRAY; + // type.getElementType(); + return ARRAY_FOR_NAME_PATTERN.matcher(value).matches(); + } + + // TODO: use this for package remapping too? + /** + * Returns true if the String looks like a Java type name. + * @param value The name to inspect. + * @return true if the String looks like a Java type name. + */ + public static boolean isForName(@Nonnull String value) { + if (value.equals("")) + return false; + for (int i = 0, len = value.length(); i < len; i++) { + char c = value.charAt(i); + if (c != '.' && !Character.isJavaIdentifierPart(c)) + return false; + } + return true; + } + + @Nonnull + public static String javaNameToPath(@Nonnull String className) { + return className.replace('.', '/') + ClassNameUtils.EXT_CLASS; + } + + @Nonnull + public static String pathToJavaName(@Nonnull String path) { + if (isClass(path)) + path = path.substring(0, path.length() - EXT_CLASS.length()); + return path.replace('/', '.'); + } + + public static final String EXT_CLASS = ".class"; + + public static boolean isClass(@Nonnull String name) { + return hasExtension(name, EXT_CLASS); + } + + public static boolean hasExtension(@Nonnull File file, @Nonnull String ext) { + return hasExtension(file.getName(), ext); + } + + public static boolean hasExtension(@Nonnull String name, @Nonnull String ext) { + if (name.length() < ext.length()) + return false; + String actual = name.substring(name.length() - ext.length()); + return actual.equalsIgnoreCase(ext); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/IoUtil.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/IoUtil.java new file mode 100644 index 0000000000..53d630eecc --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/IoUtil.java @@ -0,0 +1,60 @@ +/** + * Copyright 2008 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.util; + +import java.io.*; +import javax.annotation.Nonnull; +import javax.annotation.WillNotClose; + +public class IoUtil { + + private IoUtil() { + } + + public static void copy(@Nonnull @WillNotClose InputStream is, @Nonnull @WillNotClose OutputStream out, @Nonnull byte[] buf) throws IOException { + for (;;) { + int amt = is.read(buf); + if (amt < 0) + break; + out.write(buf, 0, amt); + } + } + + public static void copy(@Nonnull File from, @Nonnull File to, @Nonnull byte[] buf) throws IOException { + InputStream in = new FileInputStream(from); + try { + OutputStream out = new FileOutputStream(to); + try { + copy(in, out, buf); + } finally { + out.close(); + } + } finally { + in.close(); + } + } + + public static void flush(Object o) throws IOException { + if (o instanceof Flushable) + ((Flushable) o).flush(); + } + + public static void close(Object o) throws IOException { + if (o instanceof Closeable) + ((Closeable) o).close(); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/RuntimeIOException.java b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/RuntimeIOException.java new file mode 100644 index 0000000000..b666385161 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/main/java/com/tonicsystems/jarjar/util/RuntimeIOException.java @@ -0,0 +1,28 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar.util; + +import java.io.IOException; +import javax.annotation.Nonnull; + +public class RuntimeIOException extends RuntimeException { + + private static final long serialVersionUID = 0L; + + public RuntimeIOException(@Nonnull IOException e) { + super(e); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/GenericsTest.java b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/GenericsTest.java new file mode 100644 index 0000000000..a7079471a8 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/GenericsTest.java @@ -0,0 +1,33 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import com.tonicsystems.jarjar.transform.asm.PackageRemapper; +import com.tonicsystems.jarjar.transform.config.ClassRename; +import org.junit.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.commons.RemappingClassAdapter; + +public class GenericsTest { + + @Test + public void testTransform() throws Exception { + ClassRename rule = new ClassRename("java.lang.String", "com.tonicsystems.String"); + RemappingClassAdapter t = new RemappingClassAdapter(null, new PackageRemapper(rule)); + ClassReader reader = new ClassReader(getClass().getResourceAsStream("/Generics.class")); + reader.accept(t, 0); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/PackageRemapperTest.java b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/PackageRemapperTest.java new file mode 100644 index 0000000000..5e36533adc --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/PackageRemapperTest.java @@ -0,0 +1,60 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import com.tonicsystems.jarjar.transform.asm.PackageRemapper; +import com.tonicsystems.jarjar.transform.config.ClassRename; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PackageRemapperTest { + + protected PackageRemapper remapper; + + @Before + public void setUp() { + ClassRename rule = new ClassRename("org.**", "foo.@1"); + remapper = new PackageRemapper(Collections.singletonList(rule)); + } + + @Test + public void testMapValue() { + assertUnchangedValue("[^\\s;/@&=,.?:+$]"); + assertUnchangedValue("[Ljava/lang/Object;"); + assertUnchangedValue("[Lorg/example/Object;"); + assertUnchangedValue("[Ljava.lang.Object;"); + assertUnchangedValue("[Lorg.example/Object;"); + assertUnchangedValue("[L;"); + assertUnchangedValue("[Lorg.example.Object;;"); + assertUnchangedValue("[Lorg.example.Obj ct;"); + assertUnchangedValue("org.example/Object"); + + assertEquals("[Lfoo.example.Object;", remapper.mapValue("[Lorg.example.Object;")); + assertEquals("foo.example.Object", remapper.mapValue("org.example.Object")); + assertEquals("foo/example/Object", remapper.mapValue("org/example/Object")); + assertEquals("foo/example.Object", remapper.mapValue("org/example.Object")); // path match + + assertEquals("foo.example.package-info", remapper.mapValue("org.example.package-info")); + assertEquals("foo/example/package-info", remapper.mapValue("org/example/package-info")); + assertEquals("foo/example.package-info", remapper.mapValue("org/example.package-info")); + } + + private void assertUnchangedValue(String value) { + assertEquals(value, remapper.mapValue(value)); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/RulesFileParserTest.java b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/RulesFileParserTest.java new file mode 100644 index 0000000000..06b1c42288 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/RulesFileParserTest.java @@ -0,0 +1,26 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import org.junit.Test; + +public class RulesFileParserTest { + + @Test + public void testSimple() throws Exception { + // TODO + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/WildcardTest.java b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/WildcardTest.java new file mode 100644 index 0000000000..7253a2c4da --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/WildcardTest.java @@ -0,0 +1,47 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import com.tonicsystems.jarjar.transform.config.ResourceRename; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static org.junit.Assert.*; + +public class WildcardTest { + + private static final Logger LOG = LoggerFactory.getLogger(WildcardTest.class); + + @Test + public void testWildcards() { + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/proxy/Mixin$Generator", + "foo/proxy/Mixin$Generator"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar", "foo/Bar"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/Bar/Baz", "foo/Bar/Baz"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/", "foo/"); + wildcard("net/sf/cglib/**", "foo/@1", "net/sf/cglib/!", null); + wildcard("net/sf/cglib/*", "foo/@1", "net/sf/cglib/Bar", "foo/Bar"); + wildcard("net/sf/cglib/*/*", "foo/@2/@1", "net/sf/cglib/Bar/Baz", "foo/Baz/Bar"); + } + + private static void wildcard(String pattern, String result, String value, String expect) { + ResourceRename wc = new ResourceRename(pattern, result); + LOG.info("Compile: " + pattern + " -> " + wc); + String actual = wc.replace(value); + LOG.info("Replace: " + value + " -> " + actual + " (expected " + expect + ")"); + assertEquals(expect, actual); + } +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/transform/AbstractJarTransformerTest.java b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/transform/AbstractJarTransformerTest.java new file mode 100644 index 0000000000..a58eb867bd --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/transform/AbstractJarTransformerTest.java @@ -0,0 +1,52 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import javax.annotation.Nonnull; +import static org.junit.Assert.*; + +/** + * + * @author shevek + */ +public class AbstractJarTransformerTest { + + @Nonnull + protected static File newJar(String propertyName) { + return new File(System.getProperty(propertyName)); + } + protected final File jar = newJar("jar"); + protected final File[] jars = new File[]{ + newJar("jar0"), + newJar("jar1"), + newJar("jar2"), + newJar("jar3") + }; + + @Nonnull + protected Method getMethod(@Nonnull File file, @Nonnull String className, @Nonnull String methodName, @Nonnull Class<?>... parameterTypes) throws Exception { + URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()}, getClass().getClassLoader()); + Class<?> c = loader.loadClass(className); + return c.getMethod("main", parameterTypes); + } + + protected static void assertContains(@Nonnull JarFile jarFile, @Nonnull String resourceName) { + JarEntry jarEntry = jarFile.getJarEntry(resourceName); + assertNotNull("JarFile " + jarFile + " does not contain entry " + resourceName, jarEntry); + } + + protected static void assertNotContains(@Nonnull JarFile jarFile, @Nonnull String resourceName) { + JarEntry jarEntry = jarFile.getJarEntry(resourceName); + assertNull("JarFile " + jarFile + " does contains unexpected entry " + resourceName, jarEntry); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/transform/JarTransformerTest.java b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/transform/JarTransformerTest.java new file mode 100644 index 0000000000..79a533f94e --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/java/com/tonicsystems/jarjar/transform/JarTransformerTest.java @@ -0,0 +1,54 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.tonicsystems.jarjar.transform; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor; +import java.io.File; +import java.lang.reflect.Method; +import java.util.jar.JarFile; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author shevek + */ +public class JarTransformerTest extends AbstractJarTransformerTest { + + private static final Logger LOG = LoggerFactory.getLogger(JarTransformerTest.class); + + private final File outputFile = new File("build/tmp/output.jar"); + private final DefaultJarProcessor processor = new DefaultJarProcessor(); + private final JarTransformer transformer = new JarTransformer(outputFile, processor); + private final ClassPath classPath = new ClassPath(new File("/"), jars); + + /* + @Test + public void testTransformNoop() throws Exception { + processor.setSkipManifest(true); + processor.add(new PathFilterJarProcessor(Collections.singleton("META-INF/jarjar-testdata.properties"))); + transformer.transform(classPath); + } + */ + @Test + public void testTransformRename() throws Exception { + processor.setSkipManifest(true); + // processor.add(new PathFilterJarProcessor(Collections.singleton("META-INF/jarjar-testdata.properties"))); + transformer.transform(classPath); + + Method m = getMethod(outputFile, "org.anarres.jarjar.testdata.pkg0.Main", "main", String[].class); + m.invoke(null, (Object) new String[]{}); + + JarFile jarFile = new JarFile(outputFile); + assertContains(jarFile, "org/anarres/jarjar/testdata/pkg0/Main.class"); + assertContains(jarFile, "org/anarres/jarjar/testdata/pkg1/Cls1.class"); + assertContains(jarFile, "org/anarres/jarjar/testdata/pkg2/Cls2.class"); + assertContains(jarFile, "org/anarres/jarjar/testdata/pkg3/Cls3.class"); + } + +} diff --git a/third_party/java/jarjar/jarjar-core/src/test/resources/Generics.class b/third_party/java/jarjar/jarjar-core/src/test/resources/Generics.class Binary files differnew file mode 100644 index 0000000000..827519a2e2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/resources/Generics.class diff --git a/third_party/java/jarjar/jarjar-core/src/test/resources/enumtest.jar b/third_party/java/jarjar/jarjar-core/src/test/resources/enumtest.jar Binary files differnew file mode 100644 index 0000000000..df34d1650b --- /dev/null +++ b/third_party/java/jarjar/jarjar-core/src/test/resources/enumtest.jar diff --git a/third_party/java/jarjar/jarjar-gradle/build.gradle b/third_party/java/jarjar/jarjar-gradle/build.gradle new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/build.gradle diff --git a/third_party/java/jarjar/jarjar-gradle/example/build.gradle b/third_party/java/jarjar/jarjar-gradle/example/build.gradle new file mode 100644 index 0000000000..4bc3010fa5 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/example/build.gradle @@ -0,0 +1,130 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + + dependencies { + def properties = new Properties(); + file('../../gradle.properties').withReader { properties.load(it); } + classpath "org.anarres.jarjar:jarjar-gradle:${properties.version}" + } +} + +subprojects { + apply plugin: 'java' + apply plugin: 'application' + apply plugin: 'org.anarres.jarjar' + + mainClassName = "org.anarres.jarjar.test.Main" + + repositories { + mavenCentral() + maven { url 'http://repo.hortonworks.com/content/repositories/releases/' } + } + + sourceSets { + main { + java { srcDir rootProject.file('src/main/java') } + } + } +} + +project(':sub0') { + + configurations { + upstream + literal { + extendsFrom upstream + resolutionStrategy { + // http://gradle.org/docs/current/javadoc/org/gradle/api/artifacts/ResolutionStrategy.html + eachDependency { DependencyResolveDetails details -> + println "DependencyResolveDetails " + details.dump() + } + // http://gradle.org/docs/current/javadoc/org/gradle/api/artifacts/ComponentSelectionRules.html + componentSelection { + withModule("org.apache.hive:hive-metastore") { ComponentSelection selection -> + selection.reject("Rejecting all hive-metastore") + println "ComponentSelection: " + selection.dump() + } + } + } + } + compile { + extendsFrom literal + } + } + + dependencies { + upstream('org.apache.hive:hive-exec:0.13.0.2.1.5.0-695') { + exclude group: 'net.hydromatic', module: 'optiq-core' + // transitive = false + } + + compile jarjar.repackage { + from configurations.upstream + + archiveBypass "commons*.jar" + archiveExclude "slf4j*.jar" + + classDelete "org.apache.thrift.**" + + classRename 'org.json.**', 'org.anarres.hive.json.@1' + classRename 'org.iq80.**', 'org.anarres.hive.iq80.@1' + classRename 'org.codehaus.jackson.**', 'org.anarres.hive.jackson.@1' + classRename 'com.google.**', 'org.anarres.hive.google.@1' + classRename 'javolution.**', 'org.anarres.hive.javolution.@1' + classRename 'com.esotericsoftware.kryo.**', 'org.anarres.hive.kryo.@1' + } + } + +} + +project(':sub1') { + + dependencies { + compile jarjar.repackage { + from ('org.apache.hive:hive-exec:0.13.0.2.1.5.0-695') { + exclude group: 'net.hydromatic', module: 'optiq-core' + // transitive = false + } + + archiveBypass "commons*.jar" + archiveExclude "slf4j*.jar" + + classDelete "org.apache.thrift.**" + + classRename 'org.json.**', 'org.anarres.hive.json.@1' + classRename 'org.iq80.**', 'org.anarres.hive.iq80.@1' + classRename 'org.codehaus.jackson.**', 'org.anarres.hive.jackson.@1' + classRename 'com.google.**', 'org.anarres.hive.google.@1' + classRename 'javolution.**', 'org.anarres.hive.javolution.@1' + classRename 'com.esotericsoftware.kryo.**', 'org.anarres.hive.kryo.@1' + } + } + +} + +project(':sub2') { + + dependencies { + compile jarjar.repackage { + from gradleApi() + + archiveBypass "commons*.jar" + archiveExclude "slf4j*.jar" + + } + } + +} + +/* +project(':sub3') { + dependencies { + compile jarjar.dependency('org.apache.hive:hive-exec:0.13.0.2.1.5.0-695') { + } + } +} +*/ + diff --git a/third_party/java/jarjar/jarjar-gradle/example/settings.gradle b/third_party/java/jarjar/jarjar-gradle/example/settings.gradle new file mode 100644 index 0000000000..c0fb16e043 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/example/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name='jarjar-gradle-example' +include 'sub0', 'sub1', 'sub2' // , 'sub3' diff --git a/third_party/java/jarjar/jarjar-gradle/example/src/main/java/org/anarres/jarjar/test/Main.java b/third_party/java/jarjar/jarjar-gradle/example/src/main/java/org/anarres/jarjar/test/Main.java new file mode 100644 index 0000000000..6d88240d50 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/example/src/main/java/org/anarres/jarjar/test/Main.java @@ -0,0 +1,7 @@ +package org.anarres.jarjar.test; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello, world."); + } +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarArchiveTask.java b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarArchiveTask.java new file mode 100644 index 0000000000..8824b4a7b2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarArchiveTask.java @@ -0,0 +1,32 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import groovy.lang.Closure; +import javax.annotation.Nonnull; +import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.tasks.bundling.Jar; + +/** + * + * @author shevek + */ +public class JarjarArchiveTask extends Jar { + + @Override + protected CopyAction createCopyAction() { + DocumentationRegistry documentationRegistry = getServices().get(DocumentationRegistry.class); + return new JarjarCopyAction(getArchivePath(), getCompressor(), documentationRegistry); + } + + public void fromJar(@Nonnull Object... sourcePaths) { + } + + public void fromJar(@Nonnull Object sourcePath, @Nonnull Closure c) { + } + +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarController.java b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarController.java new file mode 100644 index 0000000000..e7913ca1fc --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarController.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import groovy.lang.Closure; +import groovy.lang.GroovyObjectSupport; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nonnull; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.file.FileCollection; +import org.gradle.api.internal.ClosureBackedAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This object appears as 'jarjar' in the project extensions. + * + * @author shevek + */ +public class JarjarController extends GroovyObjectSupport { + + private static final Logger LOG = LoggerFactory.getLogger(JarjarController.class); + private static final AtomicInteger SEQ = new AtomicInteger(0); + private final Project project; + + public JarjarController(@Nonnull Project project) { + this.project = project; + } + + @Nonnull + public Dependency dependency(Object dependencyNotation, Closure configurationClosure) { + Dependency d = project.getDependencies().create(dependencyNotation, configurationClosure); + LOG.info("sub is " + d); + return new JarjarDependency(d); + } + + @Nonnull + public Dependency dependency(Object dependencyNotation) { + return dependency(dependencyNotation, Closure.IDENTITY); + } + + @Nonnull + public FileCollection repackage(@Nonnull String name, @Nonnull Closure<?> c) { + JarjarTask jarjar = project.getTasks().create( + name, + JarjarTask.class, + new ClosureBackedAction<JarjarTask>(c)); + return jarjar.getOutputs().getFiles(); + } + + @Nonnull + public FileCollection repackage(@Nonnull Closure<?> c) { + return repackage("jarjar-" + SEQ.getAndIncrement(), c); + } + +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarCopyAction.java b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarCopyAction.java new file mode 100644 index 0000000000..8dd0263b06 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarCopyAction.java @@ -0,0 +1,137 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import java.io.File; +import java.io.FileOutputStream; +import javax.annotation.Nonnull; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.UnixStat; +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.UncheckedIOException; +import org.gradle.api.file.FileCopyDetails; +import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.file.CopyActionProcessingStreamAction; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.internal.file.copy.CopyActionProcessingStream; +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal; +import org.gradle.api.internal.file.copy.ZipCompressor; +import org.gradle.api.internal.tasks.SimpleWorkResult; +import org.gradle.api.tasks.WorkResult; +import org.gradle.api.tasks.bundling.Zip; +import org.gradle.api.tasks.bundling.internal.Zip64RequiredException; +import org.gradle.internal.IoActions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Based on ZipCopyAction from Gradle sources. + * + * @author shevek + */ +public class JarjarCopyAction implements CopyAction { + + private static final Logger LOG = LoggerFactory.getLogger(JarjarCopyAction.class); + private final File zipFile; + // private final ZipCompressor compressor; + private final DocumentationRegistry documentationRegistry; + + public JarjarCopyAction(@Nonnull File zipFile, @Nonnull ZipCompressor compressor, @Nonnull DocumentationRegistry documentationRegistry) { + this.zipFile = zipFile; + // this.compressor = compressor; + this.documentationRegistry = documentationRegistry; + } + + @Nonnull + @Override + public WorkResult execute(@Nonnull final CopyActionProcessingStream stream) { + LOG.info("CopyAction Executing " + stream); + + stream.process(new ScanAction()); + + final JarArchiveOutputStream zipOutStr; + + try { + zipOutStr = new JarArchiveOutputStream(new FileOutputStream(zipFile)); + } catch (Exception e) { + throw new GradleException(String.format("Could not create ZIP '%s'.", zipFile), e); + } + + try { + IoActions.withResource(zipOutStr, new Action<JarArchiveOutputStream>() { + @Override + public void execute(@Nonnull JarArchiveOutputStream outputStream) { + stream.process(new ProcessAction(outputStream)); + } + }); + } catch (UncheckedIOException e) { + if (e.getCause() instanceof Zip64RequiredException) { + throw new org.gradle.api.tasks.bundling.internal.Zip64RequiredException( + String.format("%s\n\nTo build this archive, please enable the zip64 extension.\nSee: %s", e.getCause().getMessage(), documentationRegistry.getDslRefForProperty(Zip.class, "zip64")) + ); + } + } + + return new SimpleWorkResult(true); + } + + private class ScanAction implements CopyActionProcessingStreamAction { + + @Override + public void processFile(FileCopyDetailsInternal details) { + LOG.info("CopyAction Scanning " + details); + } + } + + private class ProcessAction implements CopyActionProcessingStreamAction { + + private final JarArchiveOutputStream zipOutStr; + + public ProcessAction(@Nonnull JarArchiveOutputStream zipOutStr) { + this.zipOutStr = zipOutStr; + } + + @Override + public void processFile(@Nonnull FileCopyDetailsInternal details) { + LOG.info("CopyAction Processing " + details); + + if (details.isDirectory()) { + visitDir(details); + } else { + visitFile(details); + } + } + + private void visitFile(@Nonnull FileCopyDetails fileDetails) { + try { + JarArchiveEntry archiveEntry = new JarArchiveEntry(fileDetails.getRelativePath().getPathString()); + archiveEntry.setTime(fileDetails.getLastModified()); + archiveEntry.setUnixMode(UnixStat.FILE_FLAG | fileDetails.getMode()); + zipOutStr.putArchiveEntry(archiveEntry); + fileDetails.copyTo(zipOutStr); + zipOutStr.closeArchiveEntry(); + } catch (Exception e) { + throw new GradleException(String.format("Could not add %s to ZIP '%s'.", fileDetails, zipFile), e); + } + } + + private void visitDir(@Nonnull FileCopyDetails dirDetails) { + try { + // Trailing slash in name indicates that entry is a directory + JarArchiveEntry archiveEntry = new JarArchiveEntry(dirDetails.getRelativePath().getPathString() + '/'); + archiveEntry.setTime(dirDetails.getLastModified()); + archiveEntry.setUnixMode(UnixStat.DIR_FLAG | dirDetails.getMode()); + zipOutStr.putArchiveEntry(archiveEntry); + zipOutStr.closeArchiveEntry(); + } catch (Exception e) { + throw new GradleException(String.format("Could not add %s to ZIP '%s'.", dirDetails, zipFile), e); + } + } + } + +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarDependency.java b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarDependency.java new file mode 100644 index 0000000000..50e292f676 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarDependency.java @@ -0,0 +1,67 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import java.util.Collections; +import java.util.Set; +import javax.annotation.Nonnull; +import org.gradle.api.Buildable; +import org.gradle.api.Task; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.internal.artifacts.ResolvableDependency; +import org.gradle.api.tasks.TaskDependency; + +/** + * + * @author shevek + */ +public class JarjarDependency implements /* ResolvableDependency, */ Dependency, Buildable { + + private final Dependency delegate; + + public JarjarDependency(@Nonnull Dependency delegate) { + this.delegate = delegate; + } + + @Override + public String getGroup() { + return delegate.getGroup(); + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public String getVersion() { + return delegate.getVersion(); + } + + @Override + public boolean contentEquals(Dependency d) { + while (d instanceof JarjarDependency) + d = ((JarjarDependency) d).delegate; + return delegate.contentEquals(d); + } + + @Override + public Dependency copy() { + return new JarjarDependency(delegate); + } + + @Override + public TaskDependency getBuildDependencies() { + if (delegate instanceof Buildable) + return ((Buildable) delegate).getBuildDependencies(); + return new TaskDependency() { + @Override + public Set<? extends Task> getDependencies(Task task) { + return Collections.emptySet(); + } + }; + } +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarPlugin.java b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarPlugin.java new file mode 100644 index 0000000000..93c3903a02 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarPlugin.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import groovy.lang.Closure; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; +import org.gradle.api.internal.ClosureBackedAction; +import static org.bouncycastle.asn1.x500.style.RFC4519Style.c; +import static org.gradle.internal.Transformers.name; + +/** + * + * @author shevek + */ +public class JarjarPlugin implements Plugin<Project> { + + @Override + public void apply(final Project project) { + project.getLogger().info("Applying " + this); + // project.getExtensions().getExtraProperties().set("Jarjar", JarjarTask.class); + /* + project.getExtensions().getExtraProperties().set("jarjarDependency", new Closure<FileCollection>(JarjarPlugin.this) { + @Override + public FileCollection call(Object... args) { + JarjarTask jarjar = project.getTasks().create( + name, + JarjarTask.class, + new ClosureBackedAction<JarjarTask>(c)); + return jarjar.getOutputs().getFiles(); + } + }); + */ + project.getExtensions().create("jarjar", JarjarController.class, project); + } + +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarTask.java b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarTask.java new file mode 100644 index 0000000000..9d863caf35 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/main/java/org/anarres/gradle/plugin/jarjar/JarjarTask.java @@ -0,0 +1,232 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.transform.JarTransformer; +import com.tonicsystems.jarjar.transform.config.ClassKeepTransitive; +import com.tonicsystems.jarjar.transform.config.ClassDelete; +import com.tonicsystems.jarjar.transform.config.ClassRename; +import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor; +import groovy.lang.Closure; +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Nonnull; +import org.apache.oro.text.GlobCompiler; +import org.apache.oro.text.regex.MalformedPatternException; +import org.apache.oro.text.regex.Pattern; +import org.apache.oro.text.regex.Perl5Matcher; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileCollection; +import org.gradle.api.internal.ConventionTask; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.OutputFiles; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.TaskOutputs; + +/** + * + * @author shevek + */ +public class JarjarTask extends ConventionTask { + + private class FilterSpec implements Spec<File> { + + private final String message; + private final Iterable<? extends Pattern> patterns; + private final boolean result; + + public FilterSpec(@Nonnull String message, @Nonnull Iterable<? extends Pattern> patterns, boolean result) { + this.message = message; + this.patterns = patterns; + this.result = result; + } + + @Override + public boolean isSatisfiedBy(File t) { + if (matchesAny(patterns, t.getName())) { + getLogger().info(message + " " + t); + return result; + } + return !result; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(patterns=" + patterns + ")"; + } + } + + private static final Perl5Matcher globMatcher = new Perl5Matcher(); + + private static boolean matchesAny(@Nonnull Iterable<? extends Pattern> patterns, @Nonnull String text) { + for (Pattern pattern : patterns) { + if (globMatcher.matches(text, pattern)) { + return true; + } + } + return false; + } + + @Nonnull + private static Iterable<Pattern> toPatterns(@Nonnull Iterable<? extends String>... patterns) throws MalformedPatternException { + GlobCompiler compiler = new GlobCompiler(); + List<Pattern> out = new ArrayList<Pattern>(); + for (Iterable<? extends String> in : patterns) + for (String pattern : in) + out.add(compiler.compile(pattern)); + return out; + } + + private final ConfigurableFileCollection sourceFiles; + private final Set<String> archiveBypasses = new HashSet<String>(); + private final Set<String> archiveExcludes = new HashSet<String>(); + private File destinationDir; + private String destinationName; + + private final DefaultJarProcessor processor = new DefaultJarProcessor(); + + public JarjarTask() { + sourceFiles = getProject().files(); + } + + @InputFiles + public FileCollection getSourceFiles() { + return sourceFiles; + } + + /** + * Returns the directory where the archive is generated into. + * + * @return the directory + */ + public File getDestinationDir() { + File out = destinationDir; + if (out == null) + out = new File(getProject().getBuildDir(), "jarjar"); + return out; + } + + public void setDestinationDir(File destinationDir) { + this.destinationDir = destinationDir; + } + + /** + * Returns the file name of the generated archive. + * + * @return the name + */ + public String getDestinationName() { + String out = destinationName; + if (out == null) + out = getName() + ".jar"; + return out; + } + + public void setDestinationName(String destinationName) { + this.destinationName = destinationName; + } + + /** + * The path where the archive is constructed. + * The path is simply the {@code destinationDir} plus the {@code destinationName}. + * + * @return a File object with the path to the archive + */ + @OutputFile + public File getDestinationPath() { + return new File(getDestinationDir(), getDestinationName()); + } + + @OutputFiles + public FileCollection getBypassedArchives() throws MalformedPatternException { + return sourceFiles.filter(new FilterSpec("Bypassing archive", toPatterns(archiveBypasses), true)); + } + + /** + * Processes a FileCollection, which may be simple, a {@link Configuration}, + * or derived from a {@link TaskOutputs}. + * + * @param files The input FileCollection to consume. + */ + public void from(@Nonnull FileCollection files) { + sourceFiles.from(files); + } + + /** + * Processes a Dependency directly, which may be derived from + * {@link DependencyHandler#create(java.lang.Object)}, + * {@link DependencyHandler#project(java.util.Map)}, + * {@link DependencyHandler#module(java.lang.Object)}, + * {@link DependencyHandler#gradleApi()}, etc. + * + * @param dependency The dependency to process. + */ + public void from(@Nonnull Dependency dependency) { + Configuration configuration = getProject().getConfigurations().detachedConfiguration(dependency); + from(configuration); + } + + /** + * Processes a dependency specified by name. + * + * @param dependencyNotation The dependency, in a notation described in {@link DependencyHandler}. + * @param configClosure The closure to use to configure the dependency. + * @see DependencyHandler + */ + public void from(@Nonnull String dependencyNotation, Closure configClosure) { + from(getProject().getDependencies().create(dependencyNotation, configClosure)); + } + + /** + * Processes a dependency specified by name. + * + * @param dependencyNotation The dependency, in a notation described in {@link DependencyHandler}. + */ + public void from(@Nonnull String dependencyNotation) { + from(getProject().getDependencies().create(dependencyNotation)); + } + + public void archiveBypass(@Nonnull String pattern) throws MalformedPatternException { + archiveBypasses.add(pattern); + } + + public void archiveExclude(@Nonnull String pattern) throws MalformedPatternException { + archiveExcludes.add(pattern); + } + + public void classRename(@Nonnull String pattern, @Nonnull String replacement) { + processor.addClassRename(new ClassRename(pattern, replacement)); + } + + public void classDelete(@Nonnull String pattern) { + processor.addClassDelete(new ClassDelete(pattern)); + } + + public void classClosureRoot(@Nonnull String pattern) { + processor.addClassKeepTransitive(new ClassKeepTransitive(pattern)); + } + + @TaskAction + public void run() throws Exception { + FileCollection inputFiles = sourceFiles.filter(new FilterSpec("Excluding archive", toPatterns(archiveBypasses, archiveExcludes), false)); + final File outputFile = getDestinationPath(); + outputFile.getParentFile().mkdirs(); + getLogger().info("Running jarjar for {}", outputFile); + getLogger().info("Inputs are {}", inputFiles); + + JarTransformer transformer = new JarTransformer(outputFile, processor); + transformer.transform(new ClassPath(getProject().getProjectDir(), inputFiles)); + } +} diff --git a/third_party/java/jarjar/jarjar-gradle/src/test/java/org/anarres/gradle/plugin/jarjar/JarjarDependencyTest.java b/third_party/java/jarjar/jarjar-gradle/src/test/java/org/anarres/gradle/plugin/jarjar/JarjarDependencyTest.java new file mode 100644 index 0000000000..b6a82716c3 --- /dev/null +++ b/third_party/java/jarjar/jarjar-gradle/src/test/java/org/anarres/gradle/plugin/jarjar/JarjarDependencyTest.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.gradle.plugin.jarjar; + +import org.junit.Test; +// import static org.junit.Assert.*; + +/** + * + * @author shevek + */ +public class JarjarDependencyTest { + + @Test + public void testTask() { + } + +} diff --git a/third_party/java/jarjar/jarjar-maven/build.gradle b/third_party/java/jarjar/jarjar-maven/build.gradle new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/third_party/java/jarjar/jarjar-maven/build.gradle diff --git a/third_party/java/jarjar/jarjar-maven/src/main/java/com/tonicsystems/jarjar/JarJarMojo.java b/third_party/java/jarjar/jarjar-maven/src/main/java/com/tonicsystems/jarjar/JarJarMojo.java new file mode 100644 index 0000000000..140cb1f24d --- /dev/null +++ b/third_party/java/jarjar/jarjar-maven/src/main/java/com/tonicsystems/jarjar/JarJarMojo.java @@ -0,0 +1,57 @@ +/** + * Copyright 2007 Google Inc. + * + * 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. + */ +package com.tonicsystems.jarjar; + +import com.tonicsystems.jarjar.classpath.ClassPath; +import com.tonicsystems.jarjar.transform.jar.DefaultJarProcessor; +import com.tonicsystems.jarjar.transform.config.RulesFileParser; +import com.tonicsystems.jarjar.transform.JarTransformer; +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; + +public class JarJarMojo extends AbstractMojo { + + private File fromJar; + private File toJar; + private File rulesFile; + private String rules; + @Deprecated // Maven might need this for compatibility. + private boolean verbose; + + @Override + public void execute() throws MojoExecutionException { + if (!((rulesFile == null || !rulesFile.exists()) ^ (rules == null))) + throw new MojoExecutionException("Exactly one of rules or rulesFile is required"); + + try { + DefaultJarProcessor processor = new DefaultJarProcessor(); + if (rules != null) { + RulesFileParser.parse(processor, rules); + } else { + RulesFileParser.parse(processor, rulesFile); + } + // TODO: refactor with Main.java + JarTransformer transformer = new JarTransformer(toJar, processor); + ClassPath fromClassPath = new ClassPath(new File(System.getProperty("user.dir")), Collections.singleton(fromJar)); + transformer.transform(fromClassPath); + } catch (IOException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + } +} diff --git a/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg0/Main.java b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg0/Main.java new file mode 100644 index 0000000000..97608f621c --- /dev/null +++ b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg0/Main.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.jarjar.testdata.pkg0; + +import org.anarres.jarjar.testdata.pkg1.Cls1; + +/** + * + * @author shevek + */ +public class Main { + + public static void main(String[] args) { + Cls1.m_s(); + Cls1 c = new Cls1(); + c.m_d(); + } +} diff --git a/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg1/Cls1.java b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg1/Cls1.java new file mode 100644 index 0000000000..51daf8f254 --- /dev/null +++ b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg1/Cls1.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.jarjar.testdata.pkg1; + +import org.anarres.jarjar.testdata.pkg2.Cls2; + +/** + * + * @author shevek + */ +public class Cls1 { + + public void m_d() { + Cls2 c = new Cls2(); + c.m_d(); + } + + public static void m_s() { + Cls2.m_s(); + } +} diff --git a/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg2/Cls2.java b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg2/Cls2.java new file mode 100644 index 0000000000..1125b56cfe --- /dev/null +++ b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg2/Cls2.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.jarjar.testdata.pkg2; + +import org.anarres.jarjar.testdata.pkg3.Cls3; + +/** + * + * @author shevek + */ +public class Cls2 { + + public void m_d() { + Cls3 c = new Cls3(); + c.m_d(); + } + + public static void m_s() { + Cls3.m_s(); + } +} diff --git a/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg3/Cls3.java b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg3/Cls3.java new file mode 100644 index 0000000000..637e0a17fe --- /dev/null +++ b/third_party/java/jarjar/jarjar-testdata/src/main/java/org/anarres/jarjar/testdata/pkg3/Cls3.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.anarres.jarjar.testdata.pkg3; + +/** + * + * @author shevek + */ +public class Cls3 { + + public void m_d() { + } + + public static void m_s() { + } +} diff --git a/third_party/java/jarjar/settings.gradle b/third_party/java/jarjar/settings.gradle new file mode 100644 index 0000000000..e5e182a88e --- /dev/null +++ b/third_party/java/jarjar/settings.gradle @@ -0,0 +1,7 @@ +rootProject.name='jarjar' +include 'jarjar-testdata', + 'jarjar-core', + 'jarjar-ant', + 'jarjar-maven', + 'jarjar-gradle', + 'jarjar-command' diff --git a/third_party/java/jarjar/src/main/ghpages/index.html b/third_party/java/jarjar/src/main/ghpages/index.html new file mode 100644 index 0000000000..32292c3702 --- /dev/null +++ b/third_party/java/jarjar/src/main/ghpages/index.html @@ -0,0 +1,6 @@ +<html> +<body> +<a href="docs/javadoc/">Javadoc</a> +<a href="docs/cobertura/">Coverage</a> +</body> +</html> |