Modifying an Android Libraries' package using gradle - java

Is it, or would it be possible to modify a libraries' packagename at compile time using a gradle task similar to how jarjar works for changing the packagenames of a .jar file.
The library is pretty standard and included as follows:
dependencies {
compile 'com.scottyab:rootbeer-lib:0.0.6'
}
The purpose of this would be to change the name to avoid static detection of the library by root cloaking apps.
Any advice or guidance much appreciated! Thanks

There is already a Gradle plugin that does something similar. It relocates dependencies, Shadow.
It bundles and relocates common dependencies in libraries to avoid classpath conflicts.Shadow uses the ASM library to modify class byte code to replace the package name and any import statements for a class. Any non-class files that are stored within a package structure are also relocated to the new location.
This is how you relocate a package:
shadowJar {
relocate 'junit.framework', 'shadow.junit'
}
The code snippet will rewrite the location for any class in the junit.framework to be shadow.junit. For example, the class junit.textui.TestRunner becomes shadow.junit.TestRunner. In the resulting JAR, the class file is relocated from junit/textui/TestRunner.class to shadow/junit/TestRunner.class.

There is already a Gradle plugin that does something similar. It relocates dependencies, Shadow.
It bundles and relocates common dependencies in libraries to avoid classpath conflicts.Shadow uses the ASM library to modify class byte code to replace the package name and any import statements for a class. Any non-class files that are stored within a package structure are also relocated to the new location.
This is how you relocate a package:
shadowJar {
relocate 'junit.framework', 'shadow.junit'
}
The code snippet will rewrite the location for any class in the junit.framework to be shadow.junit. For example, the class junit.textui.TestRunner becomes shadow.junit.TestRunner. In the resulting JAR, the class file is relocated from junit/textui/TestRunner.class to shadow/junit/TestRunner.class.

I can see the library you are using is an open source library. You can simply clone the repo from here https://github.com/scottyab/rootbeer
and modify it to use any name you want. You don't even need to build your changes as a aar library. Simply import it as a module in Android Studio.
Gradle tasks are very powerful as well and it should be possible to do it with gradle but I don't know how.

Related

Gradle + Eclipse : use class from existing project in a new project

I know there are a lot of questions that seem similar. I have also spent a few hours getting to grips with Gradle multiprojects. But I still don't understand what the best course of action is here. Incidentally I am using Groovy as my coding language, but explanations referencing Java would be just as good.
I have developed an Eclipse Gradle project, "ProjectA", which in particular has a class, IndexManager, which is responsible for creating and opening and querying Lucene indices.
Now I am developing a new Eclipse Gradle project, "ProjectB", which would like to use the IndexManager class from ProjectA.
This doesn't really mean that I would like both projects to be part of a multiproject. I don't want to compile the latest version of ProjectA each time I compile ProjectB - instead I would like ProjectB to be dependent on a specific version of ProjectA's IndexManager. With the option of upgrading to a new version at some future point. I.e. much as with the sorts of dependencies you get from Maven or JCenter...
Both projects have the application plugin, so ProjectA produces an executable .jar file whose name incorporates the version. But currently this contains only the .class files, the resource files, and a file called MANIFEST.MF containing the line "Manifest-Version: 1.0". Obviously it doesn't contain any of the dependencies (e.g. Lucene jar files) needed by the .class files.
The application plugin also lets you produce a runnable distribution: this consists of an executable file (2 in fact, one for *nix/Cygwin, one for Windows), but also all the .jar dependencies needed to run it.
Could someone explain how I might accomplish the task of packaging up this class, IndexManager (or alternatively all the classes in ProjectA possibly), and then including it in my dependencies clause of ProjectB's build.gradle... and then using it in a given file (Groovy or Java) of ProjectB?
Or point to some tutorial about the best course of action?
One possible answer to this which I seem to have found, but find a bit unsatisfactory, appears to be to take the class which is to be used by multiple projects, here IndexManager, and put it in a Gradle project which is specifically designed to be a Groovy library. To this end, you can kick it off by creating the project directory and then:
$ gradle init --type groovy-library
... possible to do from the Cygwin prompt, but not from within Eclipse as far as I know. So you then have to import it into Eclipse. build.gradle in this library project then has to include the dependencies needed by IndexManager, in this case:
compile 'org.apache.lucene:lucene-analyzers-common:6.+'
compile 'org.apache.lucene:lucene-queryparser:6.+'
compile 'org.apache.lucene:lucene-highlighter:6.+'
compile 'commons-io:commons-io:2.6'
compile 'org.apache.poi:poi-ooxml:4.0.0'
compile 'ch.qos.logback:logback-classic:1.2.1'
After this, I ran gradle jar to create the .jar which contains this IndexManager class, initially without any fancy stuff in the manifest (e.g. name, version). And I put this .jar file in a dedicated local directory.
Then I created another Gradle project to use this .jar file, the critical dependency here being
compile files('D:/My Documents/software projects/misc/localJars/XGradleLibExp.jar' )
The file to use this class looks like this:
package core
import XGradleLibExp.IndexManager
class Test {
public static void main( args ) {
println "hello xxx"
Printer printer = new Printer()
IndexManager im = new IndexManager( printer )
def result = im.makeIndexFromDbaseTable()
println "call result $result"
}
}
class Printer {
def outPS = new PrintStream(System.out, true, 'UTF-8' )
}
... I had designed IndexManager to use an auxiliary class, which had a property outPS. Groovy duck-typing means you just have to supply anything with such a property and hopefully things work.
The above arrangement didn't run: although you can do build and installdist without errors, the attempt to execute the distributed executable fails because the above 6 compile dependency lines are not present in build.gradle of the "consumer" project. When you put them in this "consumer" Gradle project's build.gradle, it works.
No doubt you can add the version to the generated .jar file, and thus keep older versions for use with "consumer" projects. What I don't understand is how you might harness the mechanism which makes the downloading and use of the dependencies needed by the .jar as automatic as we are used to for things obtained from "real repositories".
PS in the course of my struggles today I seem to have found that Gradle's "maven-publish" plugin is not compatible with Gradle 5.+ (which I'm using). This may or may not be relevant: some people have talked of using a "local Maven repository". I have no idea whether this is the answer to my problem... Await input from an über-Gradle-geek... :)
You should be able to update the Eclipse model to reflect this project-to-project dependency. It looks something like this (in ProjectB's build.gradle):
apply plugin: 'eclipse'
eclipse {
classpath.file.whenMerged {
entries << new org.gradle.plugins.ide.eclipse.model.ProjectDependency('/ProjectA')
}
project.file.whenMerged {
// add a project reference, which should show up in /ProjectB/.project's <projects> element
}
}
These changes may be to the running data model, so they may not actually alter the .classpath and .project files. More info can be found here: https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.eclipse.model.EclipseModel.html
This issue is discussed here: http://gradle.1045684.n5.nabble.com/Gradle-s-Eclipse-DSL-and-resolving-dependencies-to-workspace-projects-td4856525.html and a bug was opened but never resolved here: https://issues.gradle.org/browse/GRADLE-1014

How can I refrence a method from one library's dependency library in Android Studio?

Assuming that I build a LibraryA and compile it into a LibA.jar, then I create a LibraryB and put the generated LibA.jar into LibraryB/libs, and compile it into a LibB.jar.
The problem is, as I put LibB.jar into a new Android App Module, I can not directly reference methods in LibA.jar. Looks like I can only reference methods in LibB.jar, but the LibB.jar is compiled with a LibA dependency. Is there any way I can walk through this problem?
Assuming that you have all source code access to LibraryA and LibraryB, you can simply solve it by making LibraryA as a seperated module for LibraryB. So when you want to include LibraryB in a project, you must include LibraryA too.
If LibraryA is hidden in LibraryB for external use, you can make a public method where you can get the LibraryA from LibraryB, something like getLibraryA().methodA1().
You can use .aar to wrap the library. The format of .aar allows to "embed" dependencies inside. There is a Gradle lib on GitHub that helps with this. If you use Maven repos, the POM file for LibraryB can describe its dependency LibraryA.

Gradle multi project - sharing source code between projects

In a gradle multi project (java), it is possible to share source code so that each project has access to it and compiles against it?
For example, sub projects 'mouse', 'cat', 'dog', containing a single java class in each - Mouse.java Cat.java, and Dog.java respectively. Each of these classes extends Animal.java.
A caveat is that when any given sub-project (e.g, 'mouse') is being built, it has some compile time dependencies defined (from maven central) and it requires that Animal.java gets compiled using those some dependencies. The same goes for the other projects. So, I can't compile and jar up Animal.java and then use that jar as a dependency of the 3 projects. Animal must get recompiled together with the source of the dependent project. The result should be mouse.jar, cat.jar, and dog.jar, each in their respective project build dir (as usual) and each also containing their version of Animal.class.
I understand that the example isn't very intuitive (as Animal should be generic enough to be compiled independently). It's the best I could find to simulate the hard to explain scenario I'm trying to solve.
Is that possible to do in Gradle?
Thank you.
solved this with sourceSets. Added the parent project main source set to the sub projects:
e.g, added to build.gradle of project 'mouse':
sourceSets {
main.java.srcDir project(':animal').sourceSets.main.java
}

Creating a Java Library or class or package with Jar files

I have about 100 jar files and I think I want to make a library with them. What is the best way to do this? How does importing work with this. Do I still have to ask for each class or do I just refer to the new library?
More detail
I have the GeoTools9.4 package (in a zip). It has about 100 jar files. When I import these into my project in eclipse, it takes each jar file and stuffs it in and clutters up my structure. So I think I want a library (or a package or a class) I am not sure what the terminology is here.
More detail on how to call the classes in the new library.
Right now here is how I call the classes
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureSource;
If I put all of these jar files in a library can I replace the above lines with a new import like
import org.geotools.local
or do I not need to change the way they are called?
I propose you to use a Maven for this stuff.
Maven is a greay build tool, that could take care of problems, like adding dependency jars to a project. Also, GeoTools support Maven and have a nice manual for it (http://docs.geotools.org/latest/userguide/tutorial/quickstart/maven.html)
About last question - when you'll add this libraries, full name of these classes will be the same, so you must import and use them as you import them right now.
This is not usually refered to as "calling" the classes, but rather importing the classes meaning that they become available to the class that uses them.
No matter how you have those classes (in many jars or a single jar) you still need to have the import statements in the beggining of the class file for the source to be compiled
I am not sure how Eclipse "clutters up" your structure. You can place all the jars in a single folder e.g. lib and then import them into your eclipse project from that folder. If you mean that the jars show up in the left pane then there are filters that can hide them. In Eclipse there is the concept of a Working set where you can select what it would be visible and what not.
I hope it helps

Calling static methods from a class defined in another project requires libraries added for both projects

I have two projects in my NetBeans window
MyProject
Tester.java
Utilities
Utils.java
The Utils.java file contains a number of static methods written by others that we can re-use. Recently I added a new set of static methods to Utils.java that uses new external jar's. I added the libraries to the Utilities project via Properties --> Libraries --> Add Library
I then proceeded to call these methods from within Tester.java but received java.lang.NoClassDefFoundError exceptions for the classes that were defined in those external libraries. This does not occur when I call the methods from within the Utilities project.
I solved the problem by adding the required libraries to the MyProject project as well, but is there a reason why I have to do this?
You need to have the external libraries in your MyProject as well because it is transitively dependent on those libraries. You are getting java.lang.NoClassDefFoundError because the required classes were available for the Utilities during the compile time to build the jar but those classes are missing at the runtime.
The reason is simple: Utils.class relies on the classes from the library to work. So if you don't have the classes of the library in the classpath, Utils.class can't work. Just like just having an accelerator is not sufficient to make a car move. Without the car engine, the accelerator can't work. The fact that you, as a driver, don't mess with the engine directly, but only through the accelerator, doesn't mean the engine is not necessary. (sorry for this car analogy, but hopefully it makes things clearer).

Categories