Running one task in a loop inside another. Migrating from Ant

Hey there,
I’ve been working on migrating a project’s build system from Ant to Gradle. So far, I’ve faced no issues in the migration except that there’s seems no way to run a task from another task inside a loop. So, I need some help to convert the following set* of Ant targets into Gradle task:

<target name="jarAll">
    <foreach target="jar" param="file" inheritall="true">
      <path>
        <dirset dir="${classes.dir}">
          <include name="*" />
        </dirset>
      </path>
    </foreach>
  </target>

  <target name="jar">
    <basename property="classFolder" file="${file}" />
    <jar destfile="${classes.dir}/${classFolder}.jar"
         basedir="${classes.dir}/${classFolder}"
         includes="**/*.class"
         excludes="*.jar" />
    <copy file="${classes.dir}/${classFolder}.jar"
          tofile="${dest.dir}/${classFolder}/files/myArchive.jar"/>
  </target>

Here’s what I’ve done*, but I don’t want to use the Ant tasks in any way here. I need a Gradle-only approach.

ext {
    ClassesDir = "${project.buildDir}/myClasses"
    DestDir = "${project.buildDir}/dest"
}

task jarAll {
    doLast {
        file(ClassesDir).listFiles().findAll { it.directory }.each { classDir ->
            def classFolder = classDir.name

            ant.jar(
                    destfile: "$ClassesDir/${classFolder}.jar",
                    basedir: "$ClassesDir/${classFolder}",
                    includes: '**/*.class',
                    excludes: '*.jar'
            )

            ant.copy(
                    file: "$ClassesDir/${classFolder}.jar",
                    tofile: "$DestDir/${classFolder}/files/myArchive.jar"
            )

        }
    }
}

*These are just examples. Real code vary.

~ Shreyash

I can’t see why you need a jar task and a copy task. You could do something like.

task jarAll {}
file(ClassesDir).listFiles().findAll { it.directory }.each { classDir ->
   def classFolder = classDir.name
   Task jarTask = tasks.create("${classFolder}Jar", Jar) {
      from classDir
      include '**/*.class'
   } 
   jarAll.dependsOn jarTask 
} 

This requires that the class dirs are available at configuration time which is probably not the case. Do you know the class dirs by another means? Ie is there another way to know the class dirs instead of file(ClassesDir).listFiles()

Yes, you’re right. There are a couple of tasks that run before this one, and the ClassesDir is generated by one of those tasks.

I’m not sure if I understood what do you mean by that.

There are a couple of tasks that run before this one, and the ClassesDir is generated by one of those tasks.

OK, so the task which generates the ClassesDir knows which subfolders to create (and ultimately which jars to create). Can you use the same information to determine the jar tasks (instead of driving everything from the class directory list which is empty in the configuration phase)

1 Like

Can you please give me an example of how to do that? Like, how to get information from another task? Sorry if that question sounds stupid and is too basic. I’m quite new to Gradle.

PS: The task that runs before the jarAll task basically executes a JAR file that generate those files.

My question could be summarised as this

Just by looking at your sources, is there a way to know which class directories will be produced by your ant build? Eg is the list configured in an xml file? Is it determined by some java source directories?

You should use this same logic to drive the jar task creation, not the classes directories (since the classes directories don’t exist at configuration time)

The class directories contain the .class files as well as the libraries used to compile the Java source files. There can be several packages in the source directory and each of these packages need to be converted in separate JARs for the further processes to work properly. And that is why I need to run the jar task in a loop to JAR all the complied packages in separate JAR files.
I hope I explained it well.

So, in your java sources do you have exactly the same subdirectories as the compiled class directories?

Use these instead to drive the logic (java sources exist after a clean, class directories don’t)

Yes, in the Java source I have the same directories as compiled class directory. For example, if I have a Java file in format like - com/myJava/file.java then the compiled class directory would be - com.myJava/com/myJava/file.class

Great, so drive the logic from these directories instead.

And that’s the problem :sweat_smile:
Like I said I’m new to Gradle, I don’t actually know how to do that. I tried to get the directory structure like this…

…but you’re saying to drive logic from the directories. How do I do that?

file("path/to/sources/root").listFiles().findAll { it.directory }.each 

Isn’t it the same? Anyways, my main question was how to run a task inside a loop, which I guess you’ve already answered.

BTW, can we run multiple tasks this way? I mean can I have multiple tasks inside a loop? Also, is there anything else you’d like to tell me regarding this?

Isn’t it the same?

No, definitely not! The configuration phase (where all the jar tasks are added to the Gradle model) runs first, before any classes are compiled.

I mean can I have multiple tasks inside a loop?

In my example the tasks are created/added to the model in a loop in the configuration phase. The allJars task then depends on them all.

So when the allJars task is executed, it executes all of the jar tasks

1 Like

Please read about Gradle’s build phases

1 Like

@Lance
I followed your solution but it doesn’t seems to be working. The task inside the loop never gets executed.

Are you running gradle jarAll?

I tried running it as well as depending another task on it, but unfortunately, it failed both the ways.

You haven’t provided enough information for someone to help you

Well, there’s nothing else I could provide. Like I said, I tried running the task (jarAll) directly, as well as depending another task on it, but it didn’t worked. The build is successful, but there are no traces of the task, which clearly shows that the task was not run. Also, no jar was created.