I have the following code:
static def getFamilyDependencies(ConfigurationContainer configurations) {
def result = configurations.collect { configuration ->
configuration.allDependencies.findAll { dependency ->
dependency instanceof DefaultProjectDependency
} collect { projectDependency ->
projectDependency.dependencyProject.name
}
} flatten()
result as Set
}
and I would like to test it. So far, I have:
#Test
void shouldGetFamilyDependencies() {
final Project project = ProjectBuilder.builder().build()
final configurations = project.getConfigurations()
configurations.create('configuration0')
configurations.create('configuration1')
configurations.each { configuration ->
println "***************** ${configuration}"
configuration.allDependencies.each {
println "################# ${it}"
}
}
}
How do I add dependencies to the configurations? The following doesn't work:
final Project subproject = ProjectBuilder.builder().build()
configurations.configuration0 {
subproject
}
configurations.configuration1 {
allDependencies {
subproject
}
}
This should do the trick:
configuration.getDependencies().add(dependenyMock);
#Test
void shouldGetFamilyDependenciesAcrossAllConfigurations() {
final expected = ['subproject-0', 'subproject-1']
final Project project = ProjectBuilder.builder().build()
final configurations = project.getConfigurations()
configurations.create('configuration-0')
final Project subproject0 = ProjectBuilder.builder().withName(expected[0]).build()
project.dependencies {
delegate.'configuration-0'(subproject0)
}
configurations.create('configuration-1')
final Project subproject1 = ProjectBuilder.builder().withName(expected[1]).build()
project.dependencies {
delegate.'configuration-1'(subproject1)
}
final actual = RestorePublishedArtifactTask.getFamilyDependencies(configurations)
assertThat(actual, hasItems(expected.toArray(new String[expected.size()])))
}
Try to do it in this way:
project.getDependencies().add('compile', project(':common-configuration'))
compile - a name of the configuration
:common-configuration - a name of the project to add (or any other dependencies)
Related
I use gradle.properties and local.properties to inject configuration into buildSrc.
// in buildSrc
private val localVersions = LocalVersions.create()
object Versions {
kotlin = localVersions.kotlin
}
object Libs {
kotlin = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}"
}
// gradle.properties
versions.kotlin=1.3.21
This works great. But let's say I add this line to local.propeties (which should override the value in gradle.properties).
// local properties
versions.kotlin=1.3.30
When I build again, Versions.kotlin still returns 1.3.21, because it does not realize that a change to local.properties will affect the buildSrc classpath.
private data class LocalVersions(
val kotlin: String
) {
companion object {
private const val gradlePropertiesFileName = "gradle.properties"
private const val localPropertiesFileName = "local.properties"
private const val kotlinVersionPropertyName = "versions.kotlin"
#JvmStatic
fun create(): LocalVersions {
val props = Properties().apply {
fun File.asInputStream(): InputStream = BufferedInputStream(FileInputStream(this))
val gradlePropsFile = File(gradlePropertiesFileName).apply {
require(exists()) {
"File does not exist. ${this.absolutePath}"
}
}
val localPropsFile = File(localPropertiesFileName)
if (!gradlePropsFile.exists()) {
throw FileNotFoundException("Could not find file: $gradlePropsFile.absolutePath")
}
load(gradlePropsFile.asInputStream())
if (localPropsFile.exists()) {
load(localPropsFile.asInputStream())
}
}
fun findProperty(name: String): String {
return props.getProperty(name)
?: throw IllegalStateException("Could not find property `$this` in `gradle.properties` or `local.properties`.")
}
return LocalVersions(
kotlin = findProperty(kotlinVersionPropertyName)
)
}
}
}
If I make a change to the value in gradle.properties, Gradle realizes that buildSrc has been modified. If I make a similar change to local.properties, Gradle doesn't realize a sync is needed. How can I instruct Gradle to realize this?
As the title suggest, the coroutine builder runBlocking is missing in the coroutine liblary I just added in my build.gradle. Funny thing is every other thing appears to be available, GlobalScope, CoroutineScope.launch CoroutineScope.async all present. runBlocking isn't. What am I doing wrong?
here is my build.gradle
buildscript {
ext {
ktor_version = "1.1.1"
kotlin_version = "1.3.20-eap-52"
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.44"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
plugins {
id 'kotlin-multiplatform' version '1.3.20-eap-100'
}
repositories {
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
maven { url 'https://dl.bintray.com/kotlin/kotlin-js-wrappers' }
maven { url 'https://dl.bintray.com/kotlinx/kotlinx' }
maven { url "https://kotlin.bintray.com/kotlinx" }
jcenter()
mavenCentral()
}
group 'books'
version '0.0.0'
apply plugin: 'maven-publish'
apply plugin: "org.jetbrains.kotlin.frontend"
kotlin {
jvm() {
compilations.all {
tasks[compileKotlinTaskName].kotlinOptions {
jvmTarget = "1.8"
}
}
}
js() {
compilations.all {
tasks[compileKotlinTaskName].kotlinOptions {
def optDir = compileKotlinTaskName.contains("Test") ? "test/${project.name}.test.js" : "main/${project.name}.js"
kotlinOptions.metaInfo = true
kotlinOptions.outputFile = "$project.buildDir.path/js/$optDir"
kotlinOptions.sourceMap = true
kotlinOptions.moduleKind = 'commonjs'
kotlinOptions.main = "call"
}
}
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$ktor_version"
}
}
commonTest {
dependsOn commonMain
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$ktor_version"
implementation kotlin('stdlib-jdk8')
}
}
jvmTest {
dependsOn jvmMain
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$ktor_version"
implementation kotlin('stdlib-js')
}
}
jsTest {
dependsOn jsMain
dependencies {
implementation kotlin('test-js')
}
}
}
}
task runJest(type: Exec) {
group = "verification"
commandLine "sh", "runJest.sh"
}
runJest.dependsOn(jsTest)
task testAll() {
group = "verification"
dependsOn(jvmTest, runJest)
}
kotlinFrontend {
npm {
devDependency("karma")
}
sourceMaps = true
webpackBundle {
bundleName = "main"
host = "0.0.0.0"
contentPath = file("$buildDir.path/resources/main")
}
}
With that gradle configuration, I have been able to write tests well (Learning TDD) with kotlin-multiplatform. And here is my sample below
import kotlin.test.*
import com.luge.books.*
import kotlinx.coroutines.*
class BookTest {
#BeforeTest
fun setup() {
val book = Book()
}
#Test
fun testingInstantiation() {
val book = Book()
assertEquals(book.year, 1990, "Books do match the year")
}
#Test
fun willFail() {
assertFalse(false)
}
#Test
fun testingCoroutines() {
val job = GlobalScope.launch {
delay(5000)
println("Doing stuff")
assertTrue(false)
}
}
}
If you look closely, the test testingCoroutines passes, but since I am launching from the GlobalScope, it just fires and forgets and the test returns without throwing any error. If I incoporate runBlocking, the IDE highlights it with red color (you know, as something it doesn't understant), end even the kotlin compiler shouts, unresolved reference runBlockin. Help please....
After struggling here and there, I finally knew that runBlocking is only available in kotlin/jvm. So, it is not in kotlin/js or kotlin/common.
Just for future references, if you want to run multiplatform tests, then use this work around
I write a plugin for Gradle and I need to create dynamic tasks based on my extension configuration.
Example from build.gradle file:
exampleext {
t1 {
}
t2 {
}
}
So I want to create tasks like sometask#t1 and sometask#t2 and so on.
I could not find any info, how could I read this Closure configuration and use it for building these tasks? It's read in tasks only, but I want to use it before executing tasks.
Thanks in advance.
You could use Groovy's dynamic features:
class ExamplePlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("exampleext", ExampleExt, project)
}
}
class ExampleExt {
Project project
ExampleExt(Project project) {
this.project = project
}
def methodMissing(String name, Object args) {
def configClosure = args ? args[0] : {}
project.tasks.create(name: "sometask#$name", type: Copy, configClosure)
}
}
apply plugin: ExamplePlugin
exampleext {
t1 {
from "src/main/java"
into "$buildDir/tmp/main"
}
t2 {
from "src/test/java"
into "$buildDir/tmp/test"
}
}
You can have a look at https://github.com/tschulte/gradle-jnlp-plugin/blob/374360c118e2a7373ee2fa5be7d1b784240bb1aa/gradle-jnlp-plugin/src/main/groovy/de/gliderpilot/gradle/jnlp/war/GradleJnlpWarPluginExtension.groovy, where I allow dynamic task creation plus some more nesting. E.g.
jnlpWar {
versions {
"1.0"('org.example:application:1.0:webstart#zip')
}
}
is made possible by
void versions(Closure closure) {
closure.delegate = new Versions()
closure()
}
private class Versions {
#Override
Object invokeMethod(String name, Object args) {
project.configurations.maybeCreate(name)
return project.dependencies.invokeMethod(name, args)
}
}
However, maybe you should have a look at the incubating gradle model (https://docs.gradle.org/current/userguide/software_model.html).
As a software house, we are being asked, to deliver the software with all of its dependencies. The dependencies should be published to another artifactory. In another words - we would like to take all of the project's dependencies from our artifactory and publish them into another artifactory in a way that would enable the client to build the software.
Is there a way to do that in Gradle?
Adapted from this gist
public class MavenArtifactCopyTask extends DefaultTask {
#Input
List<Configuration> configurations;
#OutputDirectory
File repoDir
#TaskAction
void build() {
for (Configuration configuration : configurations) {
copyJars(configuration)
copyPoms(configuration)
}
}
private void copyJars(Configuration configuration) {
configuration.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def moduleVersionId = artifact.moduleVersion.id
File moduleDir = new File(repoDir, "${moduleVersionId.group.replace('.','/')}/${moduleVersionId.name}/${moduleVersionId.version}")
GFileUtils.mkdirs(moduleDir)
GFileUtils.copyFile(artifact.file, new File(moduleDir, artifact.file.name))
}
}
private void copyPoms(Configuration configuration) {
def componentIds = configuration.incoming.resolutionResult.allDependencies.collect { it.selected.id }
def result = project.dependencies.createArtifactResolutionQuery()
.forComponents(componentIds)
.withArtifacts(MavenModule, MavenPomArtifact)
.execute()
for(component in result.resolvedComponents) {
def componentId = component.id
if(componentId instanceof ModuleComponentIdentifier) {
File moduleDir = new File(repoDir, "${componentId.group.replace('.','/')}/${componentId.module}/${componentId.version}")
GFileUtils.mkdirs(moduleDir)
File pomFile = component.getArtifacts(MavenPomArtifact)[0].file
GFileUtils.copyFile(pomFile, new File(moduleDir, pomFile.name))
}
}
}
}
Usage
task copyMavenArtifacts(type: MavenArtifactCopyTask) {
configurations = [project.configurations.all, project.buildScript.configurations.classpath]
repoDir = file("$buildDir/mavenArtifacts")
}
Once all the jars & poms are in a local folder in a maven directory structure you can
Upload them all to another repository
Use the folder as a maven repository
You can use repository replication https://www.jfrog.com/confluence/display/RTF/Repository+Replication
I have implemented a Gradle plugin using the Gradle DSL style. The plugin is adding multiple aspects such as a adding a custom task, and configuring more other tasks. Overall, the plugin is generating some metadata property file in a source folder that must be configured by a plugin extension.
apply plugin: 'artifactMetadata'
// configure the path for the metadata
artifactMetadata {
destinationDirectory = "src/main/generated/otherlocation/resources"
}
I have been able to figure out how to configure the task using the extension properties, however it's tricking me with the remaining stuff. What is a good approach to configure the source set, the clean task and the idea plugin (see the #n: TODO comments in the plugin code below)? The implementation below will always use the default value, not the one injected through the plugin extension.
class ArtifactMetadataPlugin implements Plugin<Project> {
public static final String EXTENSION_NAME = 'artifactMetadata'
public static final String TASK_NAME = 'generateArtifactMetadata'
void apply(Project project) {
createExtension(project)
project.configure (project) {
task (TASK_NAME, type: GenerateArtifactMetadata) {
group = project.group
artifact = project.name
version = project.version.toString()
}
sourceSets {
main {
// #1:TODO to get the plugin extension property current value here output.dir(project.artifactMetadata.destinationDirectory, builtBy: TASK_NAME)
resources.srcDirs += file(project.artifactMetadata.destinationDirectory)
}
}
clean {
// #2:TODO get the plugin extension property here
delete file(project.artifactMetadata.destinationDirectory)
}
if (project.plugins.hasPlugin(IdeaPlugin)) {
idea {
module {
// #3:TODO get the plugin extension property here
sourceDirs += file(project.artifactMetadata.destinationDirectory)
}
}
}
}
project.afterEvaluate {
def extension = project.extensions.findByName(EXTENSION_NAME)
project.tasks.withType(GenerateArtifactMetadata).all { task ->
task.destinationDirectory = project.file(extension.destinationDirectory)
}
}
}
private static void createExtension(Project project) {
def extension = project.extensions.create(EXTENSION_NAME, ArtifactMetadataPluginExtension)
extension.with {
destinationDirectory = "src/main/generated/artifactinfo/resources"
}
}
}