About Bintray-release plugin
I am using bintray-release to upload my library to maven.Its doc says how to use it:
Use the publish closure to set the info of your package:
publish {
userOrg = 'novoda'
groupId = 'com.novoda'
artifactId = 'bintray-release'
publishVersion = '0.3.4'
desc = 'Oh hi, this is a nice description for a project, right?'
website = 'https://github.com/novoda/bintray-release'
}
Finally, use the task bintrayUpload to publish
$ ./gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false
In my case
Then I define my publish closure:
publish {
groupId = 'com.uniquestudio'
artifactId = 'parsingplayer'
publishVersion = '2.0.6'
website = 'https://github.com/TedaLIEz/ParsingPlayer'
Properties properties = new Properties()
InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() ;
properties.load( inputStream )
bintrayUser = properties.getProperty('bintrayUser')
bintrayKey = properties.getProperty('bintrayKey')
}
As you can see,out of safety I put bintrayUser and bintrayKey into local.properties.
My Question
First
I know I can put bintrayUser and bintrayKey in loacal.properties and gradle.properties.Is there any other way to store private data while I don't think is't suitable to store private data within current project ?
Second
Everything is ok but when I push my project to CI.I get error:
/home/travis/build/TedaLIEz/ParsingPlayer/local.properties (No such file or directory)
So I want to know How gradle task deal with extension objects,in my case,publish object.Is there any way to fix it?
First, I have to tell you that it is not recommended to ask two questions at once via StackOverflow, mainly because it may be hard to choose a correct answer, if two answers help you with the different questions you asked.
Anyhow, I'll try to answer both of your questions:
First
To use an additional properties file (local.properties in your case) is not a Gradle approach. It is in fact pure Java. You should only read properties on your own in very rare cases and never in a build script. If you really need an additional properties file, develop a Gradle plugin, which handles the file access.
Gradle automatically reads the gradle.properties file, but not only in the project directory, but also in the user-specific gradle home directory (e.g. C:\Users\*<User>*\.gradle). This is helpful to define private data, which won't find its way into version control, even if you forget to ignore the files manually. The defined data will be accessible to any project.
Second
Well, I assume the file local.properties does not exist, because you did neither put it under version control nor let your CI add it automatically. Where should the login data come from?
The solution is simple. Just add the required data to the CI user gradle home directories (e.g. /home/travis/.gradle) gradle.properties file. This way, you can also simply add access right management, by entering the login data of a CI user. Local builds will be published by your local user account (if allowed), CI builds by the CI system.
Appendix
Your question includes the Gradle specific term 'extension', but, to be honest, it got nothing to do with your question. It is correct, that most configuration in Gradle is done via so-called extension objects, that are added to the Project object, but it is an internal term, you do not need to understand it to fix this problem.
Edit: Comment answer
Now I can understand your confusion. Gradle distinguishes between the configuration phase and the execution phase. Nearly everything in your build script is executed during the configuration phase, only task actions (what a task does, e.g. copying, deleting ...), doFirst and doLast closures (so basically tasks) are executed during execution phase. If you define the list of tasks to be executed (via command line), it only affects the execution phase, but your configuration code will be executed at every single build, even if only one independent task is executed afterwards.
To solve this problem, follow the solution in the First block and add your private data to the user-specific Gradle directory gradle.properties file. It will be added to the project object and therefor, it will be accessible from the build file. But, since the file (or the data) does not exist on your CI, accessing it directly will raise an error when building on the CI. You can use the findProperty(propertyName) method as a fail-safe way to access the property value. If the property does not exist, it returns null (in the configuration phase), so no error occurs, as long as you don not execute the bintrayUpload task (which is not your goal on the CI).
Related
Unity has a default gradle.properties file that gets added during the build process. While its possible to change the build.gradle and the settings.gradle files as mentioned
here https://docs.unity3d.com/Manual/android-gradle-overview.html
there is no mention of being able to change gradle.properties within the unity docs. The file also gets recreated every build attempt so editing it within the temp/gradleOut after a build and building again doesn't work. I know exporting the project is possible as well, but I'm looking for a solution where the project can be run directly from unity.
Btw this question is NOT a duplicate of this question How to use Gradle in Unity
The answer here has nothing to do with modifying the gradle.properties file.
This is a duplicate of this question that got incorrectly marked as a duplicate how to change default gradle.properties of Unity?
Maybe my answer is a bit outdated but in Unity 2020 you can do it in:
Player Settings -> Tab Android (with robot obviously) -> Publishing Settings -> Custom Gradle Properties Template (checkbox).
After enabling the checkbox you will see the path to gradleTemplate.properties (usually it appears in Assets/Plugins/Android directory) file which will be merged with final gradle.properties.
Everything you need you can write to the end of file after **ADDITIONAL_PROPERTIES** string.
Example:
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
android.enableR8=**MINIFY_WITH_R_EIGHT**
**ADDITIONAL_PROPERTIES**
android.useAndroidX = true // I added this property to fix error: This project uses AndroidX dependencies, but the 'android.useAndroidX' property is not enabled. Set this property to true in the gradle.properties file and retry.
Also on screenshot:
This was something that was slightly hard to discover. I was going to do a regular post build processor like I had for my iOS build, but as I was searching for a manner to load and determine where the properties file was, I ran across the following interface in the documentation : IPostGenerateGradleAndroidProject.
According to the documentation:
Implement this interface to receive a callback after the Android
Gradle project is generated.
So below is my initial brute force implementation for turning on androidX and jetifier.
public class AndroidPostBuildProcessor : IPostGenerateGradleAndroidProject
{
public int callbackOrder
{
get
{
return 999;
}
}
void IPostGenerateGradleAndroidProject.OnPostGenerateGradleAndroidProject(string path)
{
Debug.Log("Bulid path : " + path);
string gradlePropertiesFile = path + "/gradle.properties";
if (File.Exists(gradlePropertiesFile))
{
File.Delete(gradlePropertiesFile);
}
StreamWriter writer = File.CreateText(gradlePropertiesFile);
writer.WriteLine("org.gradle.jvmargs=-Xmx4096M");
writer.WriteLine("android.useAndroidX=true");
writer.WriteLine("android.enableJetifier=true");
writer.Flush();
writer.Close();
}
}
Theoretically you should be able to manipulate the generated gradle project in any manner to your choosing during the post build processor. Some additional tools might be helpful, like the PBXProject support on iOS, but until then, this will do.
IPostGenerateGradleAndroidProject is a new Interface added after Unity2018.
As my project based on Unity2017, it's not a good solution. Then I found this. A solution with Gradle.
([rootProject] + (rootProject.subprojects as List)).each {
ext {
it.setProperty("android.useAndroidX", true)
it.setProperty("android.enableJetifier", true)
}
}
Although this is not a perfect solution, you can use the "Export Project" option.
Build Settings
After exporting the project, you can modify gradle.properties and build using AndroidStudio or command line.
In the newer Unity versions (2019.4+) it is possible to generate a custom gradle properties template by going to Project Settings > Player > (Android Tab) > Other Settings > and marking "Custom Gradle Properties Template".
After selecting that a gradleTemplate.properties file is generated at "Assets/Plugins/Android/gradleTemplate.properties".
This is the best way of generating the file since it is git friendly and preserves other settings.
I'd like to create a Gradle task that computes the classes.dex CRC, then writes the resulting value into a resource string. This value will be checked at runtime to determine whether the APK has been tampered or not. The problem is that beginning with Gradle plugin 1.4.+ it is not possible to access the dex task anymore. Instead, we should use Transform API. I found very little documentation about Gradle tasks in the Android environment, so I would ask a few questions:
What's the Gradle task that deals with the classes.dex file?
How should the Transform work with this task?
I've seen lots of threads about this argument, but none of these have a working solution. Thanks in advance!
According to Xavier Ducrohet:
You have to build twice. classes.dex contains R.class which is generate from the res compilation. So by the time you're computing the CRC32 it's too late to put it in.
In general you really shouldn't be modifying the model during task execution. In fact, Gradle will introduce task parallelization that really will require to not touch the model when the task are running. So we're going to (try to) fix this by making it impossible to do this. I just filed > https://code.google.com/p/android/issues/detail?id=82574
So I would do the following:
- in the evaluation phase of your project, read a file that contains the CRC and set it as a resources. Something like this (using Guava):
android.applicationVariants.all { variant ->
variant.resValue "string", "CRC", com.google.common.io.Files.toString(file("$buildDir/intermediates/checksum/$variant.dirName/classes.crc32"), Charsets.UTF_8)
}
setup a task that creates the file that contains the CRC32.
android.applicationVariants.all { variant ->
variant,outputs.each {
// create the task here. it depends on the dex task, and make the outputs.packageApplication task depend on it.
}
}
Note: this is not enough. What you know need to do is ensure that if the newly computed CRC32 is different than the current file, the build breaks, forcing you to build a 2nd time. This way you have the two cases:
- CRC32 file is missing or the content is incorrect. You compute the new CRC32, put it in the file and fail the build forcing to build again with this new value.
- CRC32 is already valid, which mean the resource contains the right value, the task does nothing more and the build continues.
https://groups.google.com/d/msg/adt-dev/W2aYLBSeGUE/fzOqyH8YibQJ
I am trying to do a very simple thing. As gradle removes all files in the build dir when cleaning I want to move the apks somewhere else when creating release versions. So I added a copy task into the chain and I set it to be the last. Anything I tried did't work. So I simplified it and added some logging to make a point. I think it just doesn't work.
Using two variables, I can check that at task definition time and execution time the input and output paths are valid. I can also check that the task is executed. I put some more files in the input directory to make sure there is also something there in any case. This is the script:
def buildPath
def outPath
task copyApks(type: Copy) {
buildPath = "$buildDir\\outputs\\apk"
outPath ="$buildDir\\outputs\\apk2"
logger.error("Source Folder is $buildPath")
logger.error("Destination Folder is $outPath")
from buildPath
into outPath
}
assembleRelease.doLast {
android.applicationVariants.all { variant ->
println "Variant $variant.name"
logger.error("Source Folder is $buildPath")
logger.error("Destination Folder is $outPath")
copyApks
}
}
And this is the output, where one can see that the paths are correct (they exist and are valid) both at definition and execution time. Also one can see that the task is executed:
What is wrong?
Executing external task 'assembleRelease'...
Parallel execution with configuration on demand is an incubating feature.
Source Folder is C:\Users\Administrator\Projects\Gradle\MB6\app\build\outputs\apk
Destination Folder is C:\Users\Administrator\Projects\Gradle\MB6\app\build\outputs\apk2
................
some other gradle logs
................
:app:assembleRelease
Variant debug
Source Folder is C:\Users\Administrator\Projects\Gradle\MB6\app\build\outputs\apk
Destination Folder is C:\Users\Administrator\Projects\Gradle\MB6\app\build\outputs\apk2
Variant release
Source Folder is C:\Users\Administrator\Projects\Gradle\MB6\app\build\outputs\apk
Destination Folder is C:\Users\Administrator\Projects\Gradle\MB6\app\build\outputs\apk2
BUILD SUCCESSFUL
First of all, you have to know, that just adding the task name into your closure, in your case it's copyApks, doesn't really mean that this task should be executed. It's just the same, as you specified a variable, but do nothing with it.
And one more, note, the both variants paths are the same, that means that you are trying to copy tha same files twice. Actually, that not the only reason, you have to understand, that your copy task is configured yet in the configuration phase, when you are trying to call it during the execution phase, so you can't change it's from and into parameters, and this task will always behave the same.
If you want to call some tasks one after another, you have a number of choices, like task dependencies, task finalization or task ordering. You can read about it in the official user guide. There is a way to call some task like a method call, but this is a very poor solution and you have to avoid using it.
So, if you want to call a copy task, then you may try solution like this
assembleRelease.finalizedBy copyApks
This will call a copy task always every time assembling is done.
I am working on an app that uses appengine as its backend. I would like to download the backend server side code for my app using gradle's command line.
I type gradlew appengineDownloadApp at the command prompt and this is what I get
FAILURE: Build failed with an exception.
What went wrong:
A problem was found with the configuration of task ':app_server:appengineDownloadApp'.
No value has been specified for property 'appId'.
Where do I enter the appId? I am assuming this is the project id from my appengine account but I am unsure where to stick this value for gradle to read it.
I have been looking for the answer and finally ended up on https://github.com/GoogleCloudPlatform/gradle-appengine-plugin#convention-properties page:
The task appengineDownloadApp requires you to at least define the application ID and directory to write the files to. Define the tasks properties in the closure app:
- id: The application ID.
- version: The current application version (defaults to current default version).
- outputDirectory: The directory where you wish to save the files (defaults to build/downloaded-app).
So the property "appId" in error message is a bit misleading... As described in plugin README, you only need to add to build.gradle:
appengine {
app {
id = 'your-app-id'
}
}
As per the introduction of Custom Class Loading in Dalvik by Fred Chung on the Android Developers Blog:
The Dalvik VM provides facilities for developers to perform custom
class loading. Instead of loading Dalvik executable (“dex”) files from
the default location, an application can load them from alternative
locations such as internal storage or over the network.
However, not many developers have the need to do custom class loading. But those who do and follow the instructions on that blog post, might have some problems mimicking the same behavior with Gradle, the new build system for Android introduced in Google I/O 2013.
How exactly one can adapt the new build system to perform the same intermediary steps as in the old (Ant based) build system?
My team and I recently reached the 64K method references in our app, which is the maximum number of supported in a dex file. To get around this limitation, we need to partition part of the program into multiple secondary dex files, and load them at runtime.
We followed the blog post mentioned in the question for the old, Ant based, build system and everything was working just fine. But we recently felt the need to move to the new build system, based on Gradle.
This answer does not intend to replace the full blog post with a complete example. Instead, it will simply explain how to use Gradle to tweak the build process and achieve the same thing. Please note that this is probably just one way of doing it and how we are currently doing it in our team. It doesn't necessarily mean it's the only way.
Our project is structured a little different and this example works as an individual Java project that will compile all the source code into .class files, assemble them into a single .dex file and to finish, package that single .dex file into a .jar file.
Let's start...
In the root build.gradle we have the following piece of code to define some defaults:
ext.androidSdkDir = System.env.ANDROID_HOME
if(androidSdkDir == null) {
Properties localProps = new Properties()
localProps.load(new FileInputStream(file('local.properties')))
ext.androidSdkDir = localProps['sdk.dir']
}
ext.buildToolsVersion = '18.0.1'
ext.compileSdkVersion = 18
We need the code above because although the example is an individual Java project, we still need to use components from the Android SDK. And we will also be needing some of the other properties later on... So, on the build.gradle of the main project, we have this dependency:
dependencies {
compile files("${androidSdkDir}/platforms/android-${compileSdkVersion}/android.jar")
}
We are also simplifying the source sets of this project, which might not be necessary for your project:
sourceSets {
main {
java.srcDirs = ['src']
}
}
Next, we change the default configuration of the build-in jar task to simply include the classes.dex file instead of all .class files:
configure(jar) {
include 'classes.dex'
}
Now we need to have new task that will actually assemble all .class files into a single .dex file. In our case, we also need to include the Protobuf library JAR into the .dex file. So I'm including that in the example here:
task dexClasses << {
String protobufJarPath = ''
String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? '.bat' : ''
configurations.compile.files.find {
if(it.name.startsWith('protobuf-java')) {
protobufJarPath = it.path
}
}
exec {
commandLine "${androidSdkDir}/build-tools/${buildToolsVersion}/dx${cmdExt}", '--dex',
"--output=${buildDir}/classes/main/classes.dex",
"${buildDir}/classes/main", "${protobufJarPath}"
}
}
Also, make sure you have the following import somewhere (usually at the top, of course) on your build.gradle file:
import org.apache.tools.ant.taskdefs.condition.Os
Now we must make the jar task depend on our dexClasses task, to make sure that our task is executed before the final .jar file is assembled. We do that with a simple line of code:
jar.dependsOn(dexClasses)
And we're done... Simply invoke Gradle with the usual assemble task and your final .jar file, ${buildDir}/libs/${archivesBaseName}.jar will contain a single classes.dex file (besides the MANIFEST.MF file). Just copy that into your app assets folder (you can always automate that with Gradle as we've done but that is out of scope of this question) and follow the rest of the blog post.
If you have any questions, just shout in the comments. I'll try to help to the best of my abilities.
The Android Studio Gradle plugin now provides native multidex support, which effectively solves the Android 65k method limit without having to manually load classes from a jar file, and thus makes Fred Chung's blog obsolete for that purpose. However, loading custom classes from a jar file at runtime in Android is still useful for the purpose of extensibility (e.g. making a plugin framework for your app), so I'll address that usage scenario below:
I have created a port of the original example app on Fred Chung's blog to Android Studio on my github page over here using the Android library plugin rather than the Java plugin. Instead of trying to modify the existing dex process to split up into two modules like in the blog, I've put the code which we want to go into the jar file into its own module, and added a custom task assembleExternalJar which dexes the necessary class files after the main assemble task has finished.
Here is relevant part of the build.gradle file for the library. If your library module has any dependencies which are not in the main project then you will probably need to modify this script to add them.
apply plugin: 'com.android.library'
// ... see github project for the full build.gradle file
// Define some tasks which are used in the build process
task copyClasses(type: Copy) { // Copy the assembled *.class files for only the current namespace into a new directory
// get directory for current namespace (PLUGIN_NAMESPACE = 'com.example.toastlib')
def namespacePath = PLUGIN_NAMESPACE.replaceAll("\\.","/")
// set source and destination directories
from "build/intermediates/classes/release/${namespacePath}/"
into "build/intermediates/dex/${namespacePath}/"
// exclude classes which don't have a corresponding .java entry in the source directory
def remExt = { name -> name.lastIndexOf('.').with {it != -1 ? name[0..<it] : name} }
eachFile {details ->
def thisFile = new File("${projectDir}/src/main/java/${namespacePath}/", remExt(details.name)+".java")
if (!(thisFile.exists())) {
details.exclude()
}
}
}
task assembleExternalJar << {
// Get the location of the Android SDK
ext.androidSdkDir = System.env.ANDROID_HOME
if(androidSdkDir == null) {
Properties localProps = new Properties()
localProps.load(new FileInputStream(file('local.properties')))
ext.androidSdkDir = localProps['sdk.dir']
}
// Make sure no existing jar file exists as this will cause dx to fail
new File("${buildDir}/intermediates/dex/${PLUGIN_NAMESPACE}.jar").delete();
// Use command line dx utility to convert *.class files into classes.dex inside jar archive
String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? '.bat' : ''
exec {
commandLine "${androidSdkDir}/build-tools/${BUILD_TOOLS_VERSION}/dx${cmdExt}", '--dex',
"--output=${buildDir}/intermediates/dex/${PLUGIN_NAMESPACE}.jar",
"${buildDir}/intermediates/dex/"
}
copyJarToOutputs.execute()
}
task copyJarToOutputs(type: Copy) {
// Copy the built jar archive to the outputs folder
from 'build/intermediates/dex/'
into 'build/outputs/'
include '*.jar'
}
// Set the dependencies of the build tasks so that assembleExternalJar does a complete build
copyClasses.dependsOn(assemble)
assembleExternalJar.dependsOn(copyClasses)
For more detailed information see the full source code for the sample app on my github.
See my answer over here. The key points are:
Use the additionalParameters property on the dynamically created dexCamelCase tasks to pass --multi-dex to dx and create multiple dex files.
Use the multidex class loader to use the multiple dex files.