Generate Android project assets on the fly

In my Android project, I have a folder whose content is generated by a gradle task, and I need to package this folder as assets for the .apk I build. Right now, here is how I am setting up my gradle build file:

android {
    ...
    sourceSets {
        main {
            assets {
                srcDir generatedAssetsDir
            }
        }
    }
    ...
}
...
afterEvaluate { evaluated ->
    evaluated.tasks.getByPath("packageReleaseAssets").dependsOn evaluated.tasks.getByPath('taskThatGeneratesTheAssetsDir')
}

Is there a better way to achieve this, especially something that avoids using afterEvaluate?

Thank you

I don’t do Android development, so it just a guess, but for a similar case, generating sources in a non-Android project, you just register the generating task as srcDir, requiring that the generating task has its outputs properly defined. This way you automatically get the necessary task dependencies wherever that source directory set is needed. Besides the afterEvaluate that you know is evil, almost any explicit dependsOn (except with a lifecycle task on the left-hand side) is a code smell. :slight_smile:

@Vampire , I followed the example here and created two gradle projects, one acting as producer and one as consumer. The relevant portions of the producer project’s build.gradle looks like:

abstract class MyTaskClass extends DefaultTask {
    @OutputDirectory
    abstract DirectoryProperty getOutputDir()

    @TaskAction
    def run() {
        def src = new File(getProject().getRootDir().toString()+'/in/src.txt')
        def dst = new File(getOutputDir().get().getAsFile().toString()+'/dst.txt')
        dst << src.text
    }
}

tasks.register("myTask", MyTaskClass) {
    outputDir = new File(getProject().getRootDir().toString()+'/out')
}

configurations {
    prepAssets {
        canBeConsumed = true
        canBeResolved = false
        extendsFrom implementation, runtimeOnly
    }
}

artifacts {
    prepAssets(myTask.outputDir) {
        builtBy(myTask)
    }
}

While the build.gradle file of the consumer looks like:

android {
    ...
    sourceSets {
        main {
            assets {
                srcDir new File(getProject().getRootDir().toString()+'/out')
            }
         }
    }
    ...
}

configurations {
    assetsConsumer {
        canBeConsumed = false
        canBeResolved = true
    }
}

dependencies {
    assetsConsumer project(path: ":mylibrary", configuration: 'prepAssets')
    implementation project(path: ":mylibrary")
    ...
}

The idea is that when myTask inside the producer runs, it should create a text file inside <Project_Root>/out/ folder. This folder is then specified as the Android assets folder in the consumer project. However, when I build the consumer project, :mylibrary:myTask does not run and no file is generated in <Project_Root>/out/.

Don’t configure paths, if you configure paths you always miss proper task dependencies.
Iirc it will not work so use a configuration directly as srcDir, but you might need an intermediate task that just copies it from the configuration somewhere and then you can use that task as srcDir and it should work.

@Vampire I’m not sure what you mean by " use a configuration directly as srcDir". I tried:

    sourceSets {
        main {
            assets {
                srcDir assetsConsumer
            }
         }
    }

, but that doesn’t seem to work (gradle script won’t compile).

It would be configurations.assetsConsumer and would probably only be available if you defined the configuration before you try to use it.
But I actually anyway said, that this will most probably not work. :smiley:

After I change the consumer to:

configurations {
    assetsConsumer {
        canBeConsumed = false
        canBeResolved = true
    }
}

android {
    ...
    sourceSets {
        main {
            assets {
                srcDir configurations.assetsConsumer
            }
        }
    }
    ....
}

dependencies {
    assetsConsumer project(path: ":mylibrary", configuration: 'prepAssets')
    ...
}

I am getting this strange compile error:

Could not resolve all dependencies for configuration ':app:assetsConsumer'.
Could not create task ':app:generateDebugLintModel'.
Could not resolve all dependencies for configuration ':app:assetsConsumer'.
Cannot query the value of this property because it has no value available.
The value of this property is derived from:
  - task ':app:generateDebugLintModel' property 'outputDirectory'

Hard to say for me with that little excerpt.
I think somewhere you use the value of generateDebugLintModels outputDirectory property which is not set yet.
Sounds like an eager evaluation of a property that should really be used lazily.

generateDebugLintModel is a task created by the android plugin, I believe. I do not explicitly configure that task anywhere.

But in any case, in a previous reply, you said: “but you might need an intermediate task that just copies it from the configuration somewhere”. What did you mean by that? Do I need to create an additional task inside the consumer?

Yes, most probably.
Something like

tasks.register('foo', Sync) {
    from configurations.assetsConsumer
    into temporaryDir
}

android {
    sourceSets {
        main {
            assets {
                srcDir foo
            }
        }
    }
}