Adding .so files by wildcard search to an android library project using gradle 7.4+ - gradle

I am trying to add .so files by wildcard search to my android library project.
In my library structure I have 2 sub-projects with build.gradle files called primary and testapp. The project primary generates some libraries in the form testLib_*.so. In my testapp I am trying to grab all these libraries and include them. I currently got this working by utilizing sourceSets as follows;
sourceSets {
main {
jniLibs {
srcDirs += "../primary/build/intermediates/cmake/debug/obj/"
}
}
}
Such that this path contains target folders (i.e. arm64-v8a, etc.) which then holds the required test libs.
However my problem with this approach is that I prefer to have simply a wildcard search which can pickup the required files. I want to avoid having these long paths which may change with time and also want to avoid including all libraries and instead only the ones that match my search. For example I would like to have something like the following;
sourceSets {
main {
jniLibs {
srcDirs += "../primary/**/testLib_*.so"
}
}
}
However all my wildcard searches have come up empty handed and simply do not end up including any files.

Related

Classes in bundle 'app' do not match with execution data - Android

Although similar questions are already present on internet, but I am unable to find any solution to this problem with regards to android platform. My project has multiple product flavors, uses kotlin and hilt. I believe byte code transformation while compiling the project is the root cause of disrupture.
I first thought probably Hilt is injecting code inside classes, therefore I made sure to copy classes before Hilt tasks execution into a separate folder, then use those classes as source for jacoco. But it didn't work.
Error
[ant:jacocoReport] Classes in bundle 'app' do not match with execution data. For report generation the same class files must be used as at runtime.
[ant:jacocoReport] Execution data for class com/company/myapp/Data$callApi$1 does not match.
[ant:jacocoReport] Execution data for class com/company/myapp/factory/SomeFactory$SomeGenerator does not match.
and the list continues for whole bunch of classes in the app. Due to these errors, code coverage is always zero although there are bunch of unit test already written in the app.
gradle.projectsEvaluated {
def hiltTaskPattern = ~/\bhilt.*\w+FlavorADebug\b/
def tasksList = getSubprojects()
.collect { it.tasks }
.flatten()
def copyFilesTask = tasksList.find { it.name == "copyClassFilesForJacoco" }
if (copyFilesTask != null) {
tasksList.findAll { hiltTaskPattern.matcher(it.name).matches() }
.each { it.dependsOn copyFilesTask }
}
}
task copyClassFilesForJacoco(dependsOn: "compileDebugJavaWithJavac", type: Copy) {
def javaDebugTree = fileTree(dir: "${buildDir}/intermediates/javac/flavorADebug/classes", excludes: androidFilesExcluded)
def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/flavorADebug", excludes: androidFilesExcluded)
from javaDebugTree, kotlinDebugTree
into layout.buildDirectory.dir(classFilesPathForInstrumentation)
doLast {
println("Files copied to ${classFilesPathForInstrumentation}")
}
}
Then in the testCoverage task of type JacocoReport, classDirectories point out to copied files
Also, kotlinx-kover seemed interesting, but it's in pre-mature state and lacks support of multiple product flavors along with outdated documentation which makes its usage unfavourable.
This answer explains very well reason for the problem, and also provide potential solution. But it is old, and not applicable to android project since it uses java plugin and jacoco-ant agent which is not compatible with android.
Can someone guide towards potential solutions for the aforementioned problem? TIA

Gradle distribution plugin: conditionally copy assets

I'm packaging my java application using Gradle's Distribution plugin. I wanted to make 2 distributions, one which doesn't include a JRE and another one that bundles a JRE with the app.
I've set up a copyJre task and wanted to only make Distributions plugin include a folder (jre-8 in the example below) only when copyJre task is in the tasks graph. Here's my attempt which doesn't work.
distributions {
main {
contents {
from('/') {
include 'tools/**'
}
// my attempt to conditionally copy
// jre-8 directory only when tasks graph contains
// a task named 'copyJre'
if (tasks.findByName('copyJre') != null) {
from('../../jre-dist/') {
include 'jre-8/**'
}
}
}
}
}
There probably should be a better approach in general. This looks like kludges.
From a Gradle perspective, you are better expressing what you need the other way around:
Create a different distribution that will include the JRE, possibly extracting the common part of the copy spec.
And if you really only want a single output, make it replace the default distribution after building it.

How do I apply a patch file in Gradle?

I have a Gradle build script that successfully builds my project and compiles all the artifacts I need.
However, in a couple of cases I'd like to give other developers the option to patch some of the files. For example, in one of the archives there's an xml file with information about database hooks - some of the devs use other versions (or even engines) and need to change these before they can use the build output.
Instead of having them make changes to a version-controlled file, which they might commit by mistake, I'd like to give them the option to have a local, individual patch file which the build script applies.
In an old ant script, we did something like this
<target name="appcontext-patch" if="applicationContext.patch.file">
<patch patchfile="${applicationContext.patch.file}" originalfile="${dist.dir}/applicationContext.xml"/>
</target>
but I can't figure out how to do the equivalent in Gradle. Is there a better (i.e. more idiomatic) way of doing this than trying to directly convert this into a call to ant.patch?
Some context
This is how the file ends up in the archive in the first place:
into('META-INF') {
from 'deployment', {
include 'applicationContext.xml'
rename { fn -> "jboss-spring.xml" }
}
}
It would be fantabulous if I could just do something like
into('META-INF') {
from 'deployment', {
include 'applicationContext.xml'
rename { fn -> "jboss-spring.xml' }
patch 'local/applicationContext.xml.patch'
}
}
and have the patch file applied before the file is put in the archive. I don't mind writing some code to make this possible, but I'm quite new to Gradle and I have no idea where to begin.
You should be able to translate your ant call into gradle pretty directly.
The gradle doc on how to do this generically. Basically attributes become named arguments and child tags become closures. The documentation has a bunch of good examples.
Once you have your translated ant task you can put in in a doFirst or doLast block on an appropriate task.
My first guess would be something like this:
apply plugin: 'java'
assemble.doFirst {
ant.patch(patchfile: applicationContext.patch.file,
originalFile: "${dist.dir}/applicationContext.xml")
}
That's untested, so but I'm pretty sure it will get you started on the right path. The intent is that just before the java plugin assembles your archive you want gradle to call a closure. In this case the closure will perform an ant action that patches your xml.
Alternately you could use the task you have above that performs a copy and tag onto that.
task myCopyTask(type: Copy) {
...
} << {
ant.patch(patchfile: applicationContext.patch.file,
originalFile: "${dist.dir}/applicationContext.xml")
}
In this case you are writing the task yourself and the left-shift operator (<<) is equivalent to .doLast but a whole lot cooler. I'm not sure which method you prefer, but if you already have a copy task that gets the file there in the first place, I think doLast keeps the relevant code blocks as close to each other as possible.
RFC 5621 defines an XML patching language that uses XPath to target the location in the document to patch. It's great for tweaking config files.
There is an open source implementation in Java (Disclaimer: I am the author). It includes a filter that can be used from Gradle to patch XML files during any task that implements CopySpec. For example:
buildscript {
repositories { jcenter() }
dependencies { classpath "com.github.dnault:xml-patch:0.3.0" }
}
import com.github.dnault.xmlpatch.filter.XmlPatch
task copyAndPatch(type: Copy) {
// Patch file in RFC 5621 format
def patchPath = 'local/applicationContext-patch.xml'
inputs.file patchPath
into('META-INF') {
from 'deployment', {
include 'applicationContext.xml'
rename { 'jboss-spring.xml' }
filter(XmlPatch, patch: patchPath)
}
}
}
If you'd like to do this more on the fly I can think of two main techniques. Both involve writing some code, but they may be more appealing to you and I'm pretty confident gradle doesn't have this behavior built-in anywhere.
Personally I think #1 is the better solution, since you don't need to muck around with the internals of the Copy task. A custom filter feels cleaner and more reusable.
1) Write a custom filter that you specify in your copy task. I can't help with the details of how to write a custom filter, but I'd start here. You should be able to put the custom filter in buildSrc (lots of info about that at gradle.org) and then you simply need to import it at the top of your gradle file. If you write it in groovy I think you can even just use ant.patch() again.
task copyAndPatch() {
into('META-INF') {
from 'deployment', {
include 'applicationContext.xml'
rename { fn -> "jboss-spring.xml' }
filter(MyCustomFilterThatDoesAPatch, patchFile: 'local/applicationContext.xml.patch')
}
}
2) Write a custom task. Again, I'll leave the details to the experts but you can probably get away with subclassing the Copy task, adding a 'patch' property, and then jumping in during execution to do the dirty work.

Dynamically configuring a task in a parent build.gradle

I have a multi-project C++ Gradle build, which produces a number of libraries and executables. I'm trying to get the executables (but not the libraries) subprojects to get compiled in with a 'fingerprint' object. This works fine if I sprinkle smth like this in individual subprojects' build.gradle:
compileMain.doFirst {
// code to generate a 'BuildInfo.cpp' from from a template.
// embeds name of executable in so has to be generated anew for each exe
}
Following DRY principles, I'd much rather do this once and for all in a top level build.gradle. This is my attempt, to apply it to just the subprojects that use the cpp-exe plugin, following these instructions:
configure(subprojects.findAll { it.plugins.hasPlugin('cpp-exe') }) {
compileMain.doFirst {
// same code as above
}
}
Alas, this doesn't get triggered. However, if I put smth like this in a less restrictive configure, block, this demonstrates that the idea of querying the plugin should work:
configure(subprojects.findAll { true }) {
task mydebug << {
if ( project.plugins.hasPlugin( 'cpp-exe' ) ) {
println ">>> $project.name has it!"
}
}
}
Could it be that the plugins don't get applied to the subprojects at the time the configure closure is evaluated (in the top-level build.gradle)? There may well be a much simpler way of achieving this altogether?
You probably apply the cpp-exe plugin in the child projects' build scripts. By default, a parent build script gets evaluated before its children, which explains why it's not finding any projects that have cpp-exe applied.
There are several ways to solve this problem. One way is to move all configuration that's specific to a cpp-exe project (like applying the plugin and adding the action) to the same spot. Either you do all such configuration from the parent build script (for example by enumerating the cpp-exe subprojects and configuring them with a single configure(cppExeProjects) { ... }), or you move the cpp-exe specific configuration into its own build script (say gradle/cpp-exe.gradle) and apply it from selected subprojects like so: apply from: "$rootDir/gradle/cpp-exe.gradle".
Another solution is to change the evaluation order of build scripts. But I would only use this as a last resort, and it is certainly not necessary here.
Gradle 1.5 is recently out, I am not sure if this is a new feature but as it looks, you can solve the issue by using afterEvaluate.
Take a look at section 53.6.1 in http://www.gradle.org/docs/current/userguide/build_lifecycle.html
Something like:
subprojects {subProject ->
afterEvaluate {
if ( subProject.plugins.hasPlugin('cpp-exe')){
println "Project $subProject.name has plugin cpp-exe"
}
}
}
would give you a start.

How to find all dependency (class files) for a C# class file

I have a number of test classes in C# (NUnit Test scripts, compiling on Mono).
Instead of compiling all unit tests into one big assembly, as usual, I'd like to compile the individual class files into separate assemblies. To do so, I'd like to do a dependency analysis, so I can generate the separate assemblies automatically.
What I'm looking for is similar to class dependency analyser which exists for Java
Have a look at Mono Cecil.
This library has the capability to 'reflect' (not a very good name for it) on the actual assembly image to do analysis. This assumes that you would be willing to compile down to a 'big' assembly in order to run the dependency analysis using Mono.Cecil.
Edit In fact, you might simply use Cecil to copy the 'big' assembly while filtering out parts of it. That way, you'll not have much of the complexity of compiling the separate assemblies; Look at CecilRoundtrip sample for an example of how to roundtrip (read -> manipulate -> save) assemblies in Cecil.
I have previously published quite extensive examples of how to use Mono Cecil for 'advanced' searches (static call tree search, in essence):
Get types used inside a C# method body
Look if a method is called inside a method using reflection
The absolute bare minimum that would be most useful to you would probably be:
var types = assemblies
.SelectMany(assembly => assembly.MainModule.Types.Cast<TypeDefinition>());
var dependencies = types.ToDictionary(
key => key,
typedef => new HashSet<string>(typedef.Methods.Cast<MethodDefinition>()
.Where(method => null != method.Body) // ignore abstracts and generics
.SelectMany(method => method.Body.Instructions.Cast<Instruction>())
.Select(instr => instr.Operand)
.OfType<TypeReference>().Distinct()
// .Where(type => !type.Namespace.StartsWith("System"))
.Select(type => type.Name)));
foreach (var entry in dependencies)
{
Console.WriteLine("{0}\t{1}", entry.Key.Name, string.Join(", ", entry.Value.ToArray()));
}
Note the commented line optionally filters out things from the framework (System.String, System.Char etc.).
This will list required types per declared type. To list types used, simply tag on the lookup to assembly name:
.Select(type => type.Module.Assembly.Name.Name)));
Sample output of the first kind (types required):
SegmentSoortKenmerk SegmentSoortKenmerk
OperatorValue
Koppelpad Koppelpad, CodeLeidendVolgend
RedenWaarschuwing
RelExceptions
GecontroleerdDocument GecontroleerdDocument, GecontroleerdDocument[]
OwiExtraKenmerk OwiExtraKenmerk, Gegeven, BackofficeRelatie
Entiteit Entiteit, SleutelSysteemObject[], EniteitType
Similar query but using the assembly name lookup:
SegmentSoortKenmerk Lspo.Business
OperatorValue
Koppelpad Lspo.Business
RedenWaarschuwing
RelExceptions
GecontroleerdDocument Lspo.Business
OwiExtraKenmerk Lspo.Business
Entiteit Lspo.Business
Assuming you want to run nunit tests on mono, that should work just fine, ( I am happily using NUnit 2.5 and 2.4 on mono 2.10.6 ).
One common mistake is only copying or keeping track of the .dll file containing the tests. Like any program, the test has dependencies, in your case, they will at least be nunit.framework.dll plus the class/assembly you wish to test ( and any of it's dependencies )
If all you want however is to find the assemblies a given dll references (things it needs to run) you can do this quite easily:
using System.Reflection;
void PrintRefs( string dllfile ){
var asm = Assembly.ReflectionOnlyLoad ( dllfile );
foreach ( var aname in asm.GetReferencedAssemblies() ){
Console.WriteLine( aname.Name );
}
}
Beware, this will only find the names of assemblies the program or library was compiled with, not any that it might dynamically load at runtime.

Resources