How to disable default gradle buildType suffix (-release, -debug) - android-gradle-plugin

I migrated a 3rd-party tool's gradle.build configs, so it uses android gradle plugin 3.5.3 and gradle 5.4.1.
The build goes all smoothly, but when I'm trying to make an .aab archive, things got broken because the toolchain expects the output .aab file to be named MyApplicationId.aab, but the new gradle defaults to output MyApplicationId-release.aab, with the buildType suffix which wasn't there.
I tried to search for a solution, but documentations about product flavors are mostly about adding suffix. How do I prevent the default "-release" suffix to be added? There wasn't any product flavor blocks in the toolchain's gradle config files.

I realzed that I have to create custom tasks after reading other questions and answers:
How to change the generated filename for App Bundles with Gradle?
Renaming applicationVariants.outputs' outputFileName does not work because those are for .apks.
I'm using Gradle 5.4.1 so my Copy task syntax reference is here.
I don't quite understand where the "app.aab" name string came from, so I defined my own aabFile name string to match my toolchain's output.
I don't care about the source file so it's not deleted by another delete task.
Also my toolchain seems to be removing unknown variables surrounded by "${}" so I had to work around ${buildDir} and ${flavor} by omitting the brackets and using concatenation for proper delimiting.
tasks.whenTaskAdded { task ->
if (task.name.startsWith("bundle")) { // e.g: buildRelease
def renameTaskName = "rename${task.name.capitalize()}Aab" // renameBundleReleaseAab
def flavorSuffix = task.name.substring("bundle".length()).uncapitalize() // "release"
tasks.create(renameTaskName, Copy) {
def path = "$buildDir/outputs/bundle/" + "$flavorSuffix/"
def aabFile = "${android.defaultConfig.applicationId}-" + "$flavorSuffix" + ".aab"
from(path) {
include aabFile
rename aabFile, "${android.defaultConfig.applicationId}.aab"
}
into path
}
task.finalizedBy(renameTaskName)
}
}
As the original answer said: This will add more tasks than necessary, but those tasks will be skipped since they don't match any folder.
e.g.
Task :app:renameBundleReleaseResourcesAab NO-SOURCE

Related

How to fix third party dll include not being staged correctly by Unreal Build Tool?

I am using a pre-built C++ library in my Unreal project using a dynamic library file (let's say it's called MyPluginLib.dll). The library is contained in a plugin, let’s call it MyPlugin.
Building, packaging, playing in editor works fine. However, a packaged build doesn’t start, giving the following error: Code execution cannot proceed, MyPluginLib.dll was not found.
The packaging process places MyPluginLib.dll file in MyGame\Plugins\MyPlugin\Binaries. However, the execution process is seemingly looking for it in MyGame\Binaries – moving the library there manually solves this issue.
Why is the OS unable to find the dll in the first folder? Is there something wrong in the build.cs, or my folder structure?
The folder structure of the plugin folder is as follows:
Includes in Plugins\MyPlugin\Source\ThirdParty\MyPluginLib\
Binaries in Plugins\MyPlugin\Binaries\(PLATFORM)\
The plugin’s Build.cs looks like this:
public class MyPlugin : ModuleRules
{
public MyPlugin(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
string PluginRoot = Path.GetFullPath(Path.Combine(ModuleDirectory, "..", ".."));
string PlatformString = Target.Platform.ToString();
string LibraryDirectory = Path.Combine(PluginRoot, "Binaries", PlatformString);
PublicIncludePaths.Add(Path.Combine(PluginRoot, "Source", "ThirdParty", "MyPluginLib"));
if ((Target.Platform == UnrealTargetPlatform.Win64))
{
PublicAdditionalLibraries.Add(Path.Combine(LibraryDirectory, "MyPluginLib.lib"));
RuntimeDependencies.Add(Path.Combine(LibraryDirectory, "MyPluginLib.dll"), StagedFileType.NonUFS);
}
else if (Target.Platform == UnrealTargetPlatform.Linux)
{
// linux binaries...
}
}
Would appreciate any help.
Check your packaged games files, unreal loves to not include certain thing in packaged builds regarding plugins.

How to build a gradle project without a specific subfolder?

I have a Kotlin project in Gradle with 3 main folders: myorg.folder1, myorg.folder2, and myorg.folder3. Due to project constraints I cannot change this structure. As part of the project, I would like to have two different jar distributions: One containing all 3 folders, and one that only contains folder1 and folder2. The project has been set up so that these folders can operate independently of folder3 without errors. How can I do this?
In order to do this, (following broot's advice, thanks), I was able to find that I needed to use a sourceSet to make this work.
sourceSets {
noFolder3
kotlin {
exclude "myorg/folder3/**"
}
}
}
Then, I can build with all 3 folders using build, and without folder3 using build noFolder3.
One extra note is that since I was using Intellij IDEA, all of my code was stored in the directory src/main/kotlin, so I also added the following line inside the kotlin {} block:
srcDirs += "src/main/kotlin"

Use of Gradle Kotlin DSL Jar.from()

I'm trying to include a single source file for the Main-Class of a jar -- actually I have a toplevel directory of such files, demo/, but I don't want them all in a jar. I want separate jars, each using only one of these.
This seems like sort of an anti-pattern in gradle, as the fundamental mechanism infers or prefers that I should instead place each in a distinct sourceSet. Ugh.
A casual reading of the docs implies Jar.from() might be useful this way: "Specifies the source files or directories..."
As it turns out, "source" is perhaps a bit of a misnomer. Here's an example, a typical kotlin fat jar with the added from("demo/LockingBufferDemo.kt"):
val jar by tasks.getting(Jar::class) {
manifest { attributes["Main-Class"] = "LockingBufferDemoKt" }
from(sourceSets.main.get().output)
from("demo/LockingBufferDemo.kt")
dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get().filter {
it.name.endsWith("jar") }.map { zipTree(it) }
})
}
Forgive my naivety: Guess what does not end up in the jar? LockingBufferDemo.class. Guess what does? LockingBufferDemo.kt. In other words, this is treated more like a resource, not a source, and what would have been the simplest answer is a dead end.
Another way to approach this would be add the demo directory as an independent sourceSet and then use from(sourceSets["demo"].get(), except I can't find a way to complete that; according to IntelliJ get() returns a rather opaque "Provider" which I can't find mentioned in the actual javadoc: 1, 2 and I really feel like I'm heading down the garden path at this point with the woods rapidly growing darker around me.
This should not be this complicated.
How can I add a single file (or class derived from such) into a jar in gradle without having to put it alone in a directory and create a sourceSet for every such directory?
Regarding your explanations at the start of your post, you should consider creating multiple tasks of type Jar on your own, as every task of type Jar will only create a single JAR-file, and you "want separate jars". I do not think you should use different source sets, as all of the files are Java Kotlin source files in the end and are processed in the same way (compilation, tests, docs ...). Multiple source sets would complicate this common pipeline.
"Specifies the source files or directories..." As it turns out, "source" is perhaps a bit of a misnomer.
Well, the documentation does not stop there, but it says "for a copy and creates a child CopySpec". So it is not the source as in source code, but the source of a copy operation. In Gradle, tasks that create an archive (ZIP, JAR) share their API with tasks that copy files, as the creation of an archive can be seen as copying files from their source location to their target location (inside the archive).
So, the from method can be used to specify the files that are copied / archived. But it does not only take a sourcePath parameter, but also a closure or action for configuration. Using this second parameter, you can narrow your source files or directories down to the one file you need, for example using the method include:
val jar by tasks.getting(Jar::class) {
manifest { attributes["Main-Class"] = "LockingBufferDemoKt" }
from(sourceSets.main.get().output) {
include("**/LockingBufferDemo.class")
}
dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get().filter {
it.name.endsWith("jar") }.map { zipTree(it) }
})
}

Gradle / Groovy properties

I would like to control 'global' config in Gradle build scripts using external property files on each build machine (dev, ci, uat,...) and specify the filename with a command line argument.
e.g. gradle -DbuildProperties=/example/config/build.properties
I specifically don't want to use gradle.properties as we have existing projects that already use this approach and (for example) we want to be able to amend database urls and jdbc drivers without having to change every project.
So far have tried:-
Properties props = new Properties()
props.load(new FileInputStream("$filename"))
project.setProperty('props', props)
which works but has a deprecated warning, but I can't figure out how to avoid this.
Have also tried using groovy style config files with ConfigSlurper:-
environments {
dev {
db.security {
driver=net.sourceforge.jtds.jdbc.Driver
url=jdbc:someserver://somehost:1234/some_db
username=userId
password=secret
}
}
}
but the colons and forward slashes are causing exceptions and we don't want to have to mess up config with escape characters.
There must be a non-deprecated way to do this - can anyone suggest the 'right' way to do it?
Thanks
You can get rid of the deprecated warning quite easily. The message you got probably looks something like this:
Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties.
Deprecated dynamic property: "props" on "root project 'private'", value: "true".
It can be fixed by replacing:
project.setProperty('props', props)
with
project.ext.props = props
Just to supplement the response given by #Steinar:
it's still possible to use next syntax:
project.ext.set('prop_name', prop_value)
in case you have several properties from file:
props.each({ project.ext.set(it.key, it.value)} )

How to recursively parse xsd files to generate a list of included schemas for incremental build in Maven?

I have a Maven project that uses the jaxb2-maven-plugin to compile some xsd files. It uses the staleFile to determine whether or not any of the referenced schemaFiles have been changed. Unfortunately, the xsd files in question use <xs:include schemaLocation="../relative/path.xsd"/> tags to include other schema files that are not listed in the schemaFile argument so the staleFile calculation in the plugin doesn't accurately detect when things need to be actually recompiled. This winds up breaking incremental builds as the included schemas evolve.
Obviously, one solution would be to list all the recursively referenced files in the execution's schemaFile. However, there are going to be cases where developers don't do this and break the build. I'd like instead to automate the generation of this list in some way.
One approach that comes to mind would be to somehow parse the top-level XSD files and then either sets a property or outputs a file that I can then pass into the schemaFile parameter or schemaFiles parameter. The Groovy gmaven plugin seems like it might be a natural way to embed that functionality right into the POM. But I'm not familiar enough with Groovy to get started.
Can anyone provide some sample code? Or offer an alternative implementation/solution?
Thanks!
Not sure how you'd integrate it into your Maven build -- Maven isn't really my thing :-(
However, if you have the path to an xsd file, you should be able to get the files it references by doing something like:
def rootXsd = new File( 'path/to/xsd' )
def refs = new XmlSlurper().parse( rootXsd ).depthFirst().findAll { it.name()=='include' }.#schemaLocation*.text()
println "$rootXsd references $refs"
So refs is a list of Strings which should be the paths to the included xsds
Based on tim_yates's answer, the following is a workable solution, which you may have to customize based on how you are configuring the jaxb2 plugin.
Configure a gmaven-plugin execution early in the lifecycle (e.g., in the initialize phase) that runs with the following configuration...
Start with a function to collect File objects of referenced schemas (this is a refinement of Tim's answer):
def findRefs { f ->
def relPaths = new XmlSlurper().parse(f).depthFirst().findAll {
it.name()=='include'
}*.#schemaLocation*.text()
relPaths.collect { new File(f.absoluteFile.parent + "/" + it).canonicalFile }
}
Wrap that in a function that iterates on the results until all children are found:
def recursiveFindRefs = { schemaFiles ->
def outputs = [] as Set
def inputs = schemaFiles as Queue
// Breadth-first examine all refs in all schema files
while (xsd = inputs.poll()) {
outputs << xsd
findRefs(xsd).each {
if (!outputs.contains(it)) inputs.add(it)
}
}
outputs
}
The real magic then comes in when you parse the Maven project to determine what to do.
First, find the JAXB plugin:
jaxb = project.build.plugins.find { it.artifactId == 'jaxb2-maven-plugin' }
Then, parse each execution of that plugin (if you have multiple). The code assumes that each execution sets schemaDirectory, schemaFiles and staleFile (i.e., does not use the defaults!) and that you are not using schemaListFileName:
jaxb.executions.each { ex ->
log.info("Processing jaxb execution $ex")
// Extract the schema locations; the configuration is an Xpp3Dom
ex.configuration.children.each { conf ->
switch (conf.name) {
case "schemaDirectory":
schemaDirectory = conf.value
break
case "schemaFiles":
schemaFiles = conf.value.split(/,\s*/)
break
case "staleFile":
staleFile = conf.value
break
}
}
Finally, we can open the schemaFiles, parse them using the functions we've defined earlier:
def schemaHandles = schemaFiles.collect { new File("${project.basedir}/${schemaDirectory}", it) }
def allSchemaHandles = recursiveFindRefs(schemaHandles)
...and compare their last modified times against the stale file's modification time,
unlinking the stale file if necessary.
def maxLastModified = allSchemaHandles.collect {
it.lastModified()
}.max()
def staleHandle = new File(staleFile)
if (staleHandle.lastModified() < maxLastModified) {
log.info(" New schemas detected; unlinking $staleFile.")
staleHandle.delete()
}
}