Source sets with another module build outputs


#1

I have a module called “import” which obviously contains some import data. It has to be a separate module.

Second module is just a Spring Boot application.

In module “import” I’ve got a simple task, which zips everything inside the /src/main/resources

task zipImport(type: Zip){
    from "$project.projectDir/src/main/resources"
    into "/"
    archiveName "import.zip"
    destinationDir project.buildDir
}

In server module, I would like to add this “import.zip” file to my resources - so that every time I build the project, server has the archive on the classpath. I know that I can create a copy task, and I can add “dependsOn” like:

task copyZip(dependsOn: ":import:zipImport", type: Copy) {
    from "${project(":import").buildDir}/import.zip"
    into "${project.buildDir}/resources/main"
}

processResources.dependsOn copyZip

but the IntelliJ does not invoke this task when I import the project. I would to have it automatically built. How can I add this to the sourceSets? Is this a good idea? Currently I’ve got something like:

sourceSets {
    main {
        resources {
            task(":import:zipImport")
        }
    }
}

But I don’t have any idea how to do it correctly.


(uklance) #2

Do you need a zip? You could use a configuration instead

project(':import') {
   configurations {
      resources 
   } 
   dependencies {
      resources fileTree('src/main/resources') 
   } 
} 
project(':spring-boot') {
   dependencies {
      runtime project(path: ':import', configuration: 'resources') 
   } 
} 

#3

Unfortunatelly yes.

Basically, I would like to treat the src/main/resources as a source, and build ( which is import.zip ) as its product. I would like to attach it “as is” as a resource in spring.


#4

I am using gradle for 3 years now, but I am just a typical user, so not so frequent I want to achieve some customs.

I tried to follow something like “generated resources” approach, but it did not work quite right with IntelliJ. Maybe this is the way to do it?


(uklance) #5

No problemo, here’s how to do it with a zip instead of a fileTree

project(':import') {
   configurations {
      importConfig
   } 
   task zipImport(type: Zip){  
      from 'src/main/resources'  
      archiveName 'import.zip'
   }
   artifacts {
      importConfig zipImport
   }
} 
project(':spring-boot') {
   evaluationDependsOn(':import') // not sure this is required but can't hurt
   dependencies {
      runtime project(path: ':import', configuration: 'importConfig') 
   } 
}

#6

Ok, I can see where it goes and I like it!

So, basically my import’s build.gradle file now looks like this:

apply plugin: 'base'

configurations {
    importConfig
}
task zipImport(type: Zip){
    from 'src/main/resources'
    archiveName 'import.zip'
}
artifacts {
    importConfig zipImport
}

When I build it, I’ve got import.zip file placed inside import/build/distributions/import.zip directory.

The spring’s build.gradle file looks like:

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'docker'

sourceCompatibility = 1.8
targetCompatibility = 1.8


repositories {
    jcenter()
    mavenCentral()
}

bootRun {
    systemProperties System.properties
}

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.2.RELEASE")
        classpath('se.transmode.gradle:gradle-docker:1.2')
    }
}


configurations {
    all*.exclude module: 'spring-boot-starter-logging'
}

evaluationDependsOn(':import') // not sure this is required but can't hurt

dependencies {
    runtime project(path: ':import', configuration: 'importConfig')
// bla bla bla
}

But when I assemble it, I cannot see import.zip file inside the app.jar. How can it happen? The same with the build directory.


(Chris Doré) #7

To clarify, which one do you want:

  1. import.zip on the classpath with app.jar.
  2. The contents of import.zip merged into app.jar as resources.
  3. import.zip inside app.jar as a resource.

#8
  1. import.zip inside app.jar as a resource.

The import project is just the source for the zip. From the business point of view it contains various images, documents etc which are then processed during the application startup. The import.zip file is also used in unit tests/integration tests which are run via IntelliJ & CMD to make sure that its contents fits to the current database scheme. Also, the archive form of this import is required - historical and integration reasons.
Currently, work with this piece looks like:

  • Ok guys, I want to add some data during import. I have to change the JPA entities inside the boot, unpack the old import.zip, change the required XLSXs, pack it back to zip and place it in spring-boot/src/main/resources and run test to make sure everything is ok.

So this is pretty much manual work. It would be perfect if the IDE ( in our case IntelliJ ) automatically updates ( or create a completely new ) archive - if something in import/src/main/resources changes before the IntelliJ’s run just like it does when some .java classes change.


(Chris Doré) #9

Building on what you already have; In the consuming project:

configurations {
    importResources
}
dependencies {
    importResources project( path: ':import', configuration: 'importConfig' )
}
task collectImportResources( type: Sync ) {
    into temporaryDir
    from configurations.importResources
}
sourceSets {
    main {
        resources {
            srcDir tasks.collectImportResources
        }
    }
}

NOTE: I didn’t test the intellij part of your question.


#10

This works perfectly ok! I am impressed!

As you mentioned, IntelliJ does not want to invoke the whole thing during its build process. Somebody know how to force it?


(uklance) #11

I really don’t understand why it needs to be packed inside the spring boot jar. Why not have import.zip packed inside import-x.y.z.jar and put that on the classpath? Either way it can be obtained via ClassLoader.getResource("import.zip") in a test or at runtime in the application.

There must be something I’m not “getting”


#12

There is propably nothing that you’re not “getting”. The way how you described it is totally ok, it is way cleaner for me, but the solution you proposed simply didn’t work.

I tried to fix it somehow.
You proposed to use:

project(':spring-boot') {
   evaluationDependsOn(':import') // not sure this is required but can't hurt
   dependencies {
      runtime project(path: ':import', configuration: 'importConfig') 
   } 

But the boot.jar didn’t contain the import.zip file. To make it works I’ve change the runtime scope to compile and then ok - the file was in the jar’s /lib folder. But, I am not sure if it is SpringBoot-related, the classloader does not want to give me import.zip the way you described.
However, the @Chris_Dore solution works perfectly fine, probably because the zip file was placed in the jar’s /classes directory. It is not perfect, but it is acceptable.

Do you have any ideas how to improve your approach?


(uklance) #13

Assuming that :import project contains only resources and is not also a java project, here’s how I’d do it (note I’m using 'default' instead of default because it’s a keyword in java/groovy). I reckon that intellij might be happier with this approach too :slight_smile:

project(':import') {
   configurations.create('default')
   task zip(type: Zip) {
      from 'src/main/resources'
      archiveName 'import.zip'
   }
   task jar(type: Jar) {
      dependsOn zip
      from zip.archivePath
   }
   artifacts.add('default', jar)
}
project(':spring-boot') {
    dependencies {
      compile project(':import') // references the 'default' configuration from the import project
   }
}

Share source files among different gradle projects
(Rajesh Kalyanasundaram) #14

@Chris_Dore, how do i do

  1. import.zip on the classpath with app.jar.

Can you please provide a sample?
Thanks