each client impl is a separate github repo and gradle project and includes the “core-…” repos.
Settings.gradle ex.
include ":core"
project(":core").projectDir = file("/path/to/core/repo")
The problem is that we are duplicating build logic in each of the client impls. It would be ideal if we could include common build logic in “core” repo buildSrc, but I don’t think gradle can do this.
What are some ways we can consolidate the duplicated code?
How are the separate projects currently depending on each other? I’m guessing you are publishing artifacts to a maven repo. If that’s the case thent I suggest that you also publish a gradle plugin to the same maven repo and reference via the GAV in other projects. As you suggested another option is a single multi project build but that’s usually only suitable for projects with a common release cycle.
so each “core” project is a separate build, however each clientImpl includes the “core” project as a multi-project build
so…
clientImpl-A is a multi-project build consisting of clientImpl-A, core, and core-feature-A
clientImpl-B is a multi-project build consisting of clientImpl-B and core
how would you recommend organizing the projects?
We currently don’t do versioning, but this is part of the process of implementing versioning
Are you duplicating the “core” sources in both projects? Or do you have multi module builds that span two (or more) source repositories (eg 1 build for 2 git repos). I don’t recommend either of these approaches.
I’d recommend that projects that are versioned together and released together are in a multi module build (and all in the one source repository). Projects with separate release cycles should have separate builds and should live in separate source repositories. A single build should not cover 2 or more source repositories (unless local development via composite build)
A continuous integration server (eg Hudson/Jenkins/Travis) should constantly be building all projects and publishing snapshots to a maven repository. Dependencies for CI builds will also be sourced from the maven repo. Some people recommend never using “-SNAPSHOT” versions (eg use git hash instead) but I’ve never done this myself.
When building locally you will either source dependencies from the maven repo (if not changing said dependencies). If also changing the dependencies locally you can develop two projects in parallel using composite builds or maven local repository.
I was not able to do this using the gradle composite build, however I was able to implement this a modified version of the CompositePlugin in this post.
I modified the resolveDependency function to use gradle’s dependencySubstitution as follows:
if (!forceJars && projectPath) {
Project childProject
try {
childProject = project.project(":${projectPath}")
} catch (UnknownProjectException e) {
// No local project, use maven repository for dependency resolution. This is not an error condition but an expect
childProject = null
}
if (childProject) {
project.configurations.each { cfg ->
cfg.resolutionStrategy.dependencySubstitution {
substitute it.module(dependencyNotation) with it.project(childProject.path)
}
}
if (log.isTraceEnabled()) {
childProject.tasks.each { log.trace(it) }
}
log.quiet("Found non-jar project dependency: " + childProject)
project.composite.resolvedProject = true
}
}
Then you can add another substitutionRule to replace the project with the module:
BTW, most of the time we no longer use the “forceJars” property because it affects all builds and dependencies. Instead we just temporarily comment out the appropriate includeFlatIfLocal lines in settings.gradle.