Passing arguments to a task

So I have a few tasks that do essentially the same thing but with different values, e.g.,

task tarArtifact (type: Tar) { 
  compression = Compression.GZIP
  from "foobar" into "newfoobar"
  extension = "tar.gz"
  baseName = "MyArtifact"
  version = "${project.artifactVersion}"
 }
 tarArtifact.dependsOn someBuildTask

It’s being called from maven publish tasks , e.g.,

 publications {
        Module1(MavenPublication) {
            groupId "..."
            artifactId "Module1"
            version "..."
            artifact tarArtifact{ }
        }

        Module2(MavenPublication) {
            groupId "..."
            artifactId "Module2"
            version "..."
            artifact tarArtifact{ }
        }

What I like to do it is pass into tarArtifact values for from, to, baseName and someBuildTask, I can’t seem to find a simple way to do this, is this possible?

If I understand you correctly, you basically want to create a bunch of tasks based on a “template”. You could create a method to do this which takes the properties as method arguments.

def tarArtifactTask(fromDir, toDir, baseName, buildTask) {
    return tasks.create("tar${baseName}Artifact", Tar) {
        compression = Compression.GZIP
        from fromDir {
            into toDir
        }
        extension = "tar.gz"
        baseName = baseName
        version = "${project.artifactVersion}"
        dependsOn buildTask
    }
}

Mark, thanks for the suggestion, that looks exactly like what I needed but when I tried it, I got the error

Cannot add task ':tarFooBarArtifact' as a task with that name already exists.

I added a StackTraceElement call to see why it’s being called twice and got this

called by defaultCall
called by tarArtifactTask

Any idea why this is being called twice? I assume one is the actual call but the other is a mystery to me.

Could you post the bit of your build script where you are calling the method?

Sure, here’s the snippet

publications {
        Module1(MavenPublication) {
            groupId "..."
            artifactId "Module1"
            version "..."
            artifact tarArtifactTask("sourcepath", "destpath", "${project.ArtifactName}", xcodebuildDebug)
        }

Let me know if you need something else

That second call sounds like it’s calling itself which doesn’t seem right. I wasn’t able to reproduce this error locally. Can you post your tarArtifactTask() method implementation?

Yeah, that’s what it looked like to me too, it’s your code verbatim with StackTraceElement code included, here it is

def tarArtifactTask(fromDir, toDir, baseName, buildTask) {
	StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
	println "called by " + stackTraceElements[2].getMethodName()
    return tasks.create("tar${baseName}Artifact", Tar) {
        compression = Compression.GZIP
        from fromDir {
            into toDir
        }
        extension = "tar.gz"
        baseName = baseName
        version = "${project.artifactVersion}"
        dependsOn buildTask
    }
}

Looks like we’ll have to rename the baseName argument to something unique. Also, I’m assuming you are configuring multiple publications. In that case you’ll have to pass something unique for baseName as project.ArtifactName will create multiple tasks of the same name.

Yeah, that fixed the problem but now I get a new error

A problem occurred configuring root project 'Example'.
> Exception thrown while executing model rule: org.gradle.api.publish.plugins.PublishingPlugin$Rules#publishing(org.gradle.api.plugins.ExtensionContainer)
   > Could not find property 'baseName' on root project 'Example'.

Here’s the revised code

def tarArtifactTask(fromDir, toDir, artifactName, buildTask) {
	StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
	println "called by " + stackTraceElements[2].getMethodName()
    return tasks.create("tar${baseName}Artifact", Tar) {
        compression = Compression.GZIP
        from fromDir {
            into toDir
        }
        extension = "tar.gz"
        baseName = artifactName
        version = "${project.artifactVersion}"
        dependsOn buildTask
    }
}

Yes, you are correct, I have multiple publications and plan on passing in a unique value each time, thanks for all your help.

Your task name should use the new artifactName argument.

"tar${artifactName}Artifact"

Oops, my bad, I fixed that but now got the same original error where tarArtifactTask called itself

called by defaultCall
called by tarArtifactTask

   > Cannot add task ':tarFooBarArtifact' as a task with that name already exists.

Revised code

def tarArtifactTask(fromDir, toDir, artifactName, buildTask) {
	StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
	println "called by " + stackTraceElements[2].getMethodName()
    return tasks.create("tar${artifactName}Artifact", Tar) {
        compression = Compression.GZIP
        from fromDir {
            into toDir
        }
        extension = "tar.gz"
        baseName = artifactName
        version = "${project.artifactVersion}"
        dependsOn buildTask
    }
}

And you’re certain you’re passing unique values for the artifactName argument? I tested this locally with multiple publications and it works. I get the same results from the StackTraceElement. I would add some println statements to show what arguments are being passed to the method call to help debug.

Yes I am passing unique values for artifactName, I commented out all publishing task except for one and it still gave me the same error

called by defaultCall, artifactName = FooBar
called by tarArtifactTask, artifactName = FooBar

Revised code

def tarArtifactTask(fromDir, toDir, artifactName, buildTask) {
	StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
	println "called by " + stackTraceElements[2].getMethodName() + ", artifactName = " + artifactName
    return tasks.create("tar${artifactName}Artifact", Tar) {
        compression = Compression.GZIP
        from fromDir {
            into toDir
        }
        extension = "tar.gz"
        baseName = artifactName
        version = "${project.artifactVersion}"
        dependsOn buildTask
    }
}

Are you saying that your StackTraceElement also showed 2 calls, defaultCall and tarArtifactTask?

Not sure how relevant this is but here’s the snippet for xcodebuildDebug

gradle.taskGraph.whenReady {graph ->
    if (graph.hasTask(xcodebuildRelease)) {
        xcodebuild {
            scheme     = 'MyScheme'
		target        = 'MyTarget'
            arch          = 'arm64 armv7'
            sdk           = 'iphoneos'
            configuration = 'Release'
        }
    }

    if (graph.hasTask(xcodebuildDebug)) {
        xcodebuild {
            scheme     = 'MyScheme'
		target        = 'MyTarget'
            arch          = 'i386 x86_64'
            sdk           = 'iphonesimulator'
            configuration = 'Debug'
            destination {
		  platform = 'iOS Simulator'
              name = 'iPhone 6'
              os   = '8.2'
            }
        }	
    }
}

I am using the gradle-xcodePlugin for iOS builds

Yes, but only when I have multiple publications configured. If you have only one publication and it’s still getting called twice, that is indeed a mystery to me. If you can provide a standalone test case for this I’d be happy to debug it. Otherwise, as a workaround for now you might just want to put a check in to see if the task exists before attempting to create it.

Ah ok, thank you for the info. Ok, I will try the workaround as suggested and if I have time, create a standalone test case, will keep you posted. Thanks and your help is much appreciated.

So I used maybeCreate and it only creates if the task doesn’t exist, maybeCreate returns "The found or created object. Never null." Got past the 2 calls issue and later it threw this error

> Exception thrown while executing model rule: org.gradle.api.publish.plugins.PublishingPlugin$Rules#publishing(org.gradle.api.plugins.ExtensionContainer)
   > Could not find method tarArtifactTask() for arguments [build/sym/Foobar.framework/, Foobar.framework, Foobar, task ':xcodebuildDebug'] on org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication_Decorated@523130fa.

So it successfully calls the method but later it errors saying it can’t find the method?

Yeah, so weird. The error is reported on the same line as when it calls it, i.e., the last line in this code

publications {
        Module1(MavenPublication) {
            groupId "..."
            artifactId "Module1"
            version "..."
            artifact tarArtifactTask("sourcepath", "destpath", "${project.ArtifactName}", xcodebuildDebug)
        }

The log shows that it called it twice just like before.

There is definitely some goofyness going on here, which is hard for me to determine without knowing more about your build. This is the example I’ve been using to test. Running the publishToMavenLocal task is successful in this example. Perhaps it can help you troubleshoot your problem.

apply plugin: 'java'
apply plugin: 'maven-publish'

group = 'org.gradle.foo'
version = '1.0.0-SNAPSHOT'

publishing {
    publications {
        module1(MavenPublication) {
            artifact myTask("foo")
        }
        module2(MavenPublication) {
            artifact myTask("bar")
        }
    }
}

def myTask(artifactName) {
    return tasks.create("my${artifactName}Task", Zip) {
        from 'build.gradle'
        baseName = artifactName
    }
}

Ok let me look into that tomorrow, gotta run now, will keep you posted. Thanks again for your help.