I'm trying to configure an Android library project to deploy multiple artifacts to a locally hosted Maven repository. I've gotten far enough such that both artifacts have their own POM generated, and it gets deployed properly to the repo, with the following script:
android {
// Publish both debug and release
publishNonDefault true
}
uploadArchives {
repositories.mavenDeployer {
def majorVersion = 1
def minorVersion = 1
def buildVersion = project.properties.get('RELEASE', '0').toInteger()
addFilter('release') { artifact, file ->
file.name.contains('release')
}
addFilter('debug') { artifact, file ->
file.name.contains('debug')
}
activePomFilters.each { filter ->
pom(filter.name) {
groupId = 'com.redacted'
artifactId = 'redacted'
packaging = 'aar'
version = "${majorVersion}.${minorVersion}.${buildVersion}"
if (!project.hasProperty('RELEASE')) {
version += "-SNAPSHOT"
}
if (filter.name == 'debug') {
artifactId += '-debug'
}
}
}
}
}
The expected delivery is:
com/
redacted/
redacted/
1.1.0-SNAPSHOT/
redacted-debug/
1.1.0-SNAPSHOT/
Which happens as expected, but it seems to publish the artifacts with an additional suffix (which breaks the dependency discovery), and I cannot figure out where it is coming from, or how to change it. What I see is:
com/redacted/redacted/1.1.0-SNAPSHOT/
redacted-1.1.0-20150717.213849-1-release.aar
redacted-1.1.0-20150717.213849-1-release.aar.md5
redacted-1.1.0-20150717.213849-1-release.aar.sha1
redacted-1.1.0-20150717.213849-1.pom
redacted-1.1.0-20150717.213849-1.pom.md5
redacted-1.1.0-20150717.213849-1.pom.sha1
For some reason, it's appending the date, as well as a -release suffix to only the AAR-related files, but not the POM files. If I manually rename these files, everything works as expected. For example, this is what I expect to be output:
com/redacted/redacted/1.1.0-SNAPSHOT/
redacted-1.1.0-20150717.213849-1.aar
redacted-1.1.0-20150717.213849-1.aar.md5
redacted-1.1.0-20150717.213849-1.aar.sha1
redacted-1.1.0-20150717.213849-1.pom
redacted-1.1.0-20150717.213849-1.pom.md5
redacted-1.1.0-20150717.213849-1.pom.sha1
How can I change how these files are delivered?
What you are running in to is this (emphasis mine):
Important: When enabling publishing of non default, the Maven publishing plugin will publish these additional variants as extra packages (with classifier). This means that this is not really compatible with publishing to a maven repository. You should either publish a single variant to a repository OR enable all config publishing for inter-project dependencies.
See the documentation: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication
The suffixes release and debug that you see are the classifiers introduced by enabling publishing of non-default artifacts. The <artifact> elements in build/ivy.xml, which is used as the basis for the Maven configuration, contain these classifiers.
Iterating over the artifacts in the configurations and removing the classifier does not work. Although setting the classifier is allowed, its original value is kept.
But what does work is wrapping the original artifacts. The wrapper will always return null for a classifier. This does result in the release and debug artifact having the same fully-qualified ID (= name + classifier), which results in only one artifact being published. This can be fixed by using a different name for debug artifacts:
class UnclassifiedPublishArtifact implements PublishArtifact {
private PublishArtifact delegatee;
private boolean isDebugArtifact;
UnclassifiedPublishArtifact(PublishArtifact delegatee, isDebugArtifact) {
this.delegatee = delegatee
this.isDebugArtifact = isDebugArtifact
}
#Override
String getName() {
return delegatee.name + (isDebugArtifact ? '-debug' : '')
}
#Override
String getExtension() {
return delegatee.extension
}
#Override
String getType() {
return delegatee.type
}
#Override
String getClassifier() {
return null
}
#Override
File getFile() {
return delegatee.file
}
#Override
Date getDate() {
return delegatee.date
}
#Override
TaskDependency getBuildDependencies() {
return delegatee.buildDependencies
}
}
project.afterEvaluate {
configurations.each { configuration ->
def artifacts = configuration.artifacts
if (!artifacts.isEmpty()) {
def unclassifiedArtifacts = []
unclassifiedArtifacts.addAll(artifacts.collect { classifiedArtifact ->
new UnclassifiedPublishArtifact(classifiedArtifact, classifiedArtifact.classifier == 'debug')
})
artifacts.clear()
artifacts.addAll(unclassifiedArtifacts)
}
}
}
I can't quite understand from the documentation what the consequences are for project dependencies, so you should check if these still work.
Related
Tried the Android code as given in the Docs.
I am unable to exclude the files.
testOptions {
unitTests.all {
if (name == "testDebugUnitTest") {
kover {
disabled = false
binaryReportFile.set(file("$buildDir/custom/debug-report.bin"))
// includes = ['com.example.*']
excludes = [
"com.makeappssimple.abhimanyu.financemanager.android.navigation.di.NavigationManagerModule"
]
}
}
}
}
I expect this code to exclude com.makeappssimple.abhimanyu.financemanager.android.navigation.di.NavigationManagerModule file, but it is not working.
Also tried with wildcard names.
Kover setup,
plugins {
id "com.android.application"
id "kotlin-android"
id "kotlin-kapt"
id "dagger.hilt.android.plugin"
id "com.google.gms.google-services"
id "org.jetbrains.kotlinx.kover" version "0.5.0"
}
// Kover
kover {
disabled = false // true to disable instrumentation of all test tasks in all projects
coverageEngine.set(kotlinx.kover.api.CoverageEngine.INTELLIJ) // change instrumentation agent and reporter
intellijEngineVersion.set('1.0.656') // change version of IntelliJ agent and reporter
jacocoEngineVersion.set('0.8.7') // change version of JaCoCo agent and reporter
generateReportOnCheck = true // false to do not execute `koverMergedReport` task before `check` task
disabledProjects = [] // ["project-name"] or [":project-name"] to disable coverage for project with path `:project-name` (`:` for the root project)
instrumentAndroidPackage = false // true to instrument packages `android.*` and `com.android.*`
runAllTestsForProjectTask = false // true to run all tests in all projects if `koverHtmlReport`, `koverXmlReport`, `koverReport`, `koverVerify` or `check` tasks executed on some project
}
P.S: I have also raised the same issue here
You can also try to add a filter the kover plugin like this version 0.6.0
kover {
instrumentation {
excludeTasks.add("testReleaseUnitTest")
}
filters {
classes {
excludes += listOf(
"dagger.hilt.internal.aggregatedroot.codegen.*",
"hilt_aggregated_deps.*",
"*ComposableSingletons*",
"*_HiltModules*",
"*Hilt_*",
"*BuildConfig",
".*_Factory.*",
)
}
}
}
Thanks to shanshin's comment, I understood the issue.
Fixed the unit test coverage report exclusion list using this code
tasks.koverHtmlReport {
excludes = [
// Hilt
"*.di.*",
"dagger.hilt.**",
"hilt_aggregated_deps.*",
"<package_name>.*.*_Factory",
// Room
// MyRoomDatabase_AutoMigration_*_Impl, *Dao_Impl
"<package_name>.*.*_Impl*",
// BuildConfig
"<package_name>.BuildConfig",
// Moshi - Json Adapter
"<package_name>.*.*JsonAdapter",
]
}
The exclusion list mentioned in the question is to exclude tests.
I am using this code in build.gradle:
android {
applicationVariants.all { variant ->
variant.packageApplicationProvider.configure { packageApplicationTask ->
doLast {
packageApplicationTask.apkNames.each { apkName ->
def apkDir = "./build/outputs/apk/${flavorName}/${buildType.name}"
def apkDestName = apkName.replace("app-", "stickerly-android-" + variant.versionName + "-").replace(".apk", "-" + getDate() + ".apk")
println "#####Rename ${variant.name} Apk File"
copy {
from("$apkDir/$apkName").into(apkDir).rename { String fileName -> apkDestName }
}
println "#####Copy mapping File"
def mappingDir = "./build/outputs/mapping/${flavorName}${buildType.name.capitalize()}"
copy {
from("$mappingDir/mapping.txt").into(mappingDir).rename {
"mapping-stickerly-${variant.versionName}.txt"
}
}
}
}
}
}
}
With this code I rename apk file and copy mapping file. I worked in android gradle plugin 4.0, but it does not work in 4.1 with this message
Where:
Script '/Users/snow/workspace/stickerly-android/app/build-android-extra.gradle' line: 5
What went wrong:
Execution failed for task ':app:packageExternalArm8Debug'.
Could not get unknown property 'apkNames' for task ':app:packageExternalArm8Debug' of type com.android.build.gradle.tasks.PackageApplication.
I think API has changed but I can not find any documents. Someone can help me?
Thanks.
property apkNames is removed in AGP 4.1
you can try this
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (!state.failure
&& task.project.name == project.name
&& task.name.startsWith("package")
&& !task.name.endsWith("UnitTest")) {
def outputDir = task.outputDirectory.getAsFile().get()
task.variantOutputs.getOrNull()?.each { variant ->
println "" + outputDir + "/" + variant.outputFileName.get()
}
}
}
add this at the end of your build.gradle file.
change println to whatever logic you want.
by the way, if you want to check the properties you may use, just add gradle plugin as a dependency of your project, click Sync in Android Stuido, then you can find it in External Librarys (key map: Command + 1, and switch to project view).
like this
dependencies {
implementation 'com.android.tools.build:gradle:4.1.0'
}
and these properties and tasks are intentionally invisible in lib com.android.tools.build:gradle-api, modifications in future releases are expected.
I have 5 different apps in my monorepo, the build.gradle for each of these apps contains a nearly indentical task, which along with an upload script is in charge of uploading files matching to our Jenkins file server.
we can call this task releaseBuilds()
This code is in a file called artifactUploads.gradle, this file also contains a custom plugin called UploadPlugin. Now my concern is with the System.out.println() line in the task.
apply plugin: UploadPlugin
task copyReleaseBuilds() {
doLast {
def dir = project.file('build/outputs/apk')
dir.eachFileRecurse(FILES) { file ->
if (file.getName().contains(".apk") && file.getName().contains("release")) {
System.out.println("uploading ${file.getName()}")
def rootDir = project.getRootDir().path
def filePath = file.path
def fileName = file.name.replace('app-', '')
exec {
workingDir rootDir
commandLine "./uploadBuild.sh", filePath, "$dirPath" + fileName
}.assertNormalExitValue()
}
}
}
}
class UploadPlugin implements Plugin<Project> {
void apply(Project project) {
//Create container instance for config object
//plugin stuff
NamedDomainObjectContainer<Config> configContainer =
project.container(Config)
project.extensions.add('directory', configContainer)
project.task('uploadConfig') << {
def uploadConfig = project.extensions.getByName('directory')
}
}
class Config{
String name
String dirPath
Config(final String name){
this.name = name
}
}
In the build.gradle file for each of my apps/projects. I have the line
apply from: "${rootDir}/artifactUploads.gradle
along with
directory{
name{
dirPath = "hca/"
println "stuff"
}
}
directory is some object that is created in my CustomPlugin.
Now when I build the line println "Stuff" is output to the gradle log, however I can not find any console outputs that are put in artifactUploads.gradle which contains my CustomPlugin and the task that all my apps share.
From what I understand, since each of my apps build.gradle has
apply from: artifactUploads.gradle, each app should now run the copyReleaseBuilds() task, with the custom parameter dirPath which is set in the DSL part of each app build.gradle.
Is there another reason the build would not be hitting that System.out.println()? I'm very new to gradle in general so thanks for your help
I have a library that is being build within a Travis CI instance. After the build completes, I would like it to upload the artifact using ./gradlew uploadArchives. The problem right now is that, while I can do this locally, the Travis instance doesn't have my server key in its .ssh/known_hosts file (it probably doesn't even HAVE a .ssh/known_hosts file.
I am using the following configuration of uploadArchives in my gradle configuration:
uploadArchives {
repositories {
mavenDeployer {
pom.packaging = "aar"
pom.groupId = project.CORE_GROUP
pom.version = project.CORE_VERSION_NAME
}
}
}
gradle.taskGraph.beforeTask { Task aTask ->
if (aTask == uploadArchives) {
checkArtifactPublishProperties()
aTask.repositories.mavenDeployer.configuration = configurations.deployerJars
aTask.repositories.mavenDeployer.repository(url: "scp://" + project.publishArtifactHost + ":" + getArtifactTargetDirectory()) {
authentication(userName: project.publishArtifactUsername, privateKey: ext.publishArtifactKeyFile)
}
}
}
I'm not sure how to specify the StrictHostKeyChecking=no as a parameter to this method. My gut tells me that I have to add it to the configuration configurations.deployerJars, but I'm not sure how to do this.
Can someone point me to the documentation for this and perhaps provide a sample of how I might go about this?
I'm trying migrating a normal Android Studio (IntelliJ) project to Gradle project recently. And currently I'm encounter a problem: IntelliJ gives me a warning on the beginning of every file says that my 'package name does not correspond to the file path'. e.g.
The first line of my some/prefixes/a/b/c/d/E.java is:
package a.b.c.d;
....
IntelliJ thinks the package name should be 'c.d' instead of 'a.b.c.d'. Because I set
SourceSets {
main.java.srcDirs = ["some/prefixes/a/b"]
}
in the module's build.gradle.
I know I could do the change below to make IntelliJ happy:
SourceSets {
main.java.srcDirs = ['some/prefixes']
}
But I can't do that because there're huge numbers of projects under 'some/prefixes' and I definitely don't want to introduce all of them into this module.
I used to add a packagePrefix="a.b" in my 'module.iml' in my original Android studio project and it works well:
https://www.jetbrains.com/idea/help/configuring-content-roots.html#d2814695e312
But I don't know how to accomplish similar fix after migrating to Gradle project.
I end up to write a task for gradle.
The task add the famous packagePrefix to the *.iml file.
This solution only work for intelliJ, I hope someone have a better solution.
task addPackagePrefix << {
println 'addPackagePrefix'
def imlFile = file(MODULE_NAME+".iml")
if (!imlFile.exists()) {
println 'no module find '
return
}
def parsedXml = (new XmlParser()).parse(imlFile)
if(parsedXml.component[1] && parsedXml.component[1].content){
parsedXml.component[1].content.findAll { Node node ->
node.sourceFolder.findAll { Node s ->
def url = s.attribute("url").toString()
if (url.endsWith(SRC_DIR)) {
println 'Node founded '
def attr = s.attribute('packagePrefix')
if (attr == null) {
// add prefix
println 'Adding package prefix'
s.attributes().put('packagePrefix', PACKAGE_NAME)
println s.toString()
// writing
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
imlFile.text = writer.toString()
}
}
}
}
}