For example, the build.gradle file for the server/platform/pipeline module includes the following:
import org.labkey.gradle.util.BuildUtils
import org.labkey.gradle.util.ExternalDependency
plugins {
id 'org.labkey.build.module'
}
dependencies {
implementation "com.sun.mail:jakarta.mail:${javaMailVersion}"
BuildUtils.addExternalDependency(
project,
new ExternalDependency(
"org.apache.activemq:activemq-core:${activemqCoreVersion}",
"Apache ActiveMQ",
"Apache",
"http://activemq.apache.org/",
ExternalDependency.APACHE_2_LICENSE_NAME,
ExternalDependency.APACHE_2_LICENSE_URL,
"Java Message Service queuing",
),
{
exclude group: "javax.servlet", module: "servlet-api"
}
)
< … snip lines … >
BuildUtils.addExternalDependency(
project,
new ExternalDependency(
"com.thoughtworks.xstream:xstream:${xstreamVersion}",
"XStream",
"XStream",
"http://x-stream.github.io/index.html",
"BSD",
"http://x-stream.github.io/license.html",
"Pipeline (Mule dependency)",
)
)
BuildUtils.addLabKeyDependency(project: project, config: "implementation", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "core"), depProjectConfig: 'apiJarFile')
}
External dependencies should be declared using the LabKey-defined "external" configuration. This configuration is used when creating the .module file to know which libraries to include with the module. In the LabKey gradle plugins, we have a utility method (BuildUtils.addExternalDependency) for adding these dependencies that allows you to supply some extra metadata about the dependency related to its licensing and source as well as a short description. This metadata is then displayed on the Admin Console's Credits page. Using this method for external dependencies should be preferred over the usual dependency declaration syntax from Gradle. The arguments to BuildUtils.addExternalDependency are the current project and an ExternalDependency object. The constructor for this object takes 7 arguments as follows:
If the library you need is not available in one of the existing repositories, those who have access to the LabKey Artifactory can navigate to the "ext-release-local" artifact set, click the Deploy link in the upper right corner, and upload the JAR file. Artifactory will attempt to guess what a reasonable group, artifact, and version number might be, but correct as needed. Once added, it can be referenced in a module's build.gradle file like any other dependency. The artifact will need a proper pom file, so you should be sure to check the box next to "Generate Default POM/Deploy jar's Internal POM" when deploying.
For internal dependencies, the BuildUtils.addLabKeyDependecy method referenced above will reference the buildFromSource gradle property to determine if a project dependency should be declared (meaning the artifacts are produced by a local build) or a package dependency (meaning the artifacts are pulled from the artifact server). The argument to this method is a map with the following entries:
import org.labkey.gradle.util.BuildUtils
BuildUtils.addLabKeyDependency(project: project, config: "implementation", depProjectPath: ":server:modules:someModule", depProjectConfig: "apiJarFile")
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:someModule", depProjectConfig: "published", depExtension: "module")
If your module relies on the API jar file of another module, but does not need the module itself (likely because the code checks for the presence of the module before using its functionality), you can use the "labkey" configuration when declaring this dependency. It is likely this dependency should not be transitive:
import org.labkey.gradle.util.BuildUtils
BuildUtils.addLabKeyDependency(project: project, config: "labkey", depProjectPath: ":server:modules:someModule", depProjectConfig: 'apiJarFile', transitive: false)
If the api jar file of the module you depend on exposes classes from a different jar file used to build this jar file, you should declare a dependency on the "runtimeElements" configuration from the other module. That module should be declaring its dependency to that separate jar file as an 'external' dependency. (This works because internally gradle's now-standard 'api' configuration, on which 'runtimeElements' is based, extends from the LabKey 'external' configuration.)
The moduleDependencies module-level property is used by LabKey server to determine the module initialization order and to control the order in which SQL scripts run. It is also used when publishing the .module file to record the proper dependencies in the pom file for that publication. These dependencies should be declared within a module's build.gradle file.
For moduleA that depends on moduleB, you would add the following line to the moduleA/build.gradle file:
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:myModules:moduleB", depProjectConfig: 'published', depExtension: 'module')
The behavior when a module is not in the settings.gradle file and/or when you do not have an enlistment for a module is as follows:
import org.labkey.gradle.util.BuildUtils
dependencies {
modules ("org.labkey.module:api:${labkeyVersion}@module") { transitive = true }
modules ("org.labkey.module:audit:${labkeyVersion}@module") { transitive = true }
modules ("org.labkey.module:core:${labkeyVersion}@module") { transitive = true }
modules ("org.labkey.module:experiment:${labkeyVersion}@module") { transitive = true }
modules ("org.labkey.module:filecontent:${labkeyVersion}@module") { transitive = true }
modules ("org.labkey.module:pipeliine:${labkeyVersion}@module") { transitive = true }
modules ("org.labkey.module:query:${labkeyVersion}@module") { transitive = true }
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:myModules:moduleB", depProjectConfig: 'published', depExtension: 'module')
}
There is also a utility method available in the BuildUtils class of the LabKey gradle plugins that can be used to declare the base module dependencies, so the above could be changed to
import org.labkey.gradle.util.BuildUtils
BuildUtils.addBaseModuleDependencies(project)
BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:myModules:moduleB", depProjectConfig: 'published', depExtension: 'module')
The task 'allDepInsight' can help to determine where dependencies for a given module come from:
./gradlew allDepInsight --dependency=MODULE_NAME --configuration=modules
If you are building locally and want to include a .module file that is not an explicit dependency of any of your other modules, this can be done by declaring a dependency from the server project's modules configuration to the other module you wish to deploy. This is probably easiest to do by adding something like the following in your module's build.gradle file:
import org.labkey.gradle.util.BuildUtils
BuildUtils.addLabKeyDependency(project: BuildUtils.getServerProject(project), config: "modules", depProjectPath: ":server:modules:experiment", depProjectConfig: "published", depExtension: "module")
Any developers working with Premium Editions of LabKey Server may want to include one or more premium modules in their local development machines. As an example, in order to use ETLs, you must have the dataintegration module on your server. Premium deployments include this module, but individual developers may also need it to be present on their development machines.
To include this module in your build, include a dependency to 'pull' the prebuilt module from the LabKey Artifactory.
Step 1: Access to the LabKey Artifactory is required and can be provided upon request to your Account Manager. Once access has been granted, you have a few options for providing your credentials when building. Learn more in this topic:
Step 2: Once you have the necessary access, declare a dependency on the desired module in the build.gradle file for a module that is present in your source enlistment.BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: ":server:modules:dataintegration", depProjectConfig: 'published', depExtension: 'module')
When you build locally, the prebuilt dataintegration module will be included and thus available on your local server.
If you include this dependency but do not have the appropriate credentials on Artifactory, you'll see a build error similar to the following. Note that this can happen when your credentials have not been granted as well as if they have been locked for some reason. Contact your Account Manager for assistance.
Could not resolve [library name]…
> Could not GET 'https://labkey.jfrog.io/artifactory/...rest of URL ending in.pom'. Received status code 403 from server:
After adding a new external dependency, or updating the version of an existing external dependency, you will want to make sure the dependency hasn't introduced a version inconsistency with other modules. To do this, you can run the task 'showDiscrepancies' and you will want to include as many modules as possible for this task, so using the module set 'all' is a good idea:
./gradlew -PmoduleSet=all showDiscrepancies
commons-collections:commons-collections has 3 versions as follows:
3.2 [:server:modules:query, :server:modules:saml]
3.2.1 [:externalModules:labModules:LDK]
3.2.2 [:server:api]
./gradlew allDepInsight --configuration=external --dependency=commons-collections
If updating direct dependency versions does not resolve the conflict, you can force a certain version of a dependency, which will apply to direct and transitive dependencies. See the root-level build.gradle file for examples of the syntax for forcing a version.
When version numbers are updated, either for LabKey itself or for external dependencies, a local build can accumulate multiple, conflicting versions of certain jar files in its deploy directory, or the individual module build directories. This is never desirable. With gradlePlugin version 1.3, tasks have been added to the regular build process that check for such conflicts.
By default, the build will fail if a version conflict is detected, but the property 'versionConflictAction' can be used to control that behavior. Valid values for this property are:
> Task :server:api:checkModuleJarVersions
INFO: Artifact versioning problem(s) in directory /labkeyEnlistment/build/modules/api/explodedModule/lib:
Conflicting version of commons-compress jar file (1.14 in directory vs. 1.16.1 from build).
Conflicting version of objenesis jar file (1.0 in directory vs. 2.6 from build).
INFO: Removing existing files that conflict with those from the build.
Deleting /labkeyEnlistment/build/modules/api/explodedModule/lib/commons-compress-1.14.jar
Deleting /labkeyEnlistment/build/modules/api/explodedModule/lib/objenesis-1.0.jar
BUILD SUCCESSFUL in 5s
Execution failed for task ':server:api:checkModuleJarVersions'.
> Artifact versioning problem(s) in directory /labkeyEnlistment/build/modules/api/explodedModule/lib:
Multiple existing annotations jar files.
Run the :server:api:clean task to remove existing artifacts in that directory.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 4s
Execution failed for task ':server:api:checkModuleJarVersions'.
> Artifact versioning problem(s) in directory /labkeyEnlistment/build/modules/api/explodedModule/lib:
Conflicting version of commons-compress jar file (1.14 in directory vs. 1.16.1 from build).
Conflicting version of objenesis jar file (1.0 in directory vs. 2.6 from build).
Run the :server:api:clean task to remove existing artifacts in that directory.
BUILD FAILED in 20s
> Task :server:api:checkModuleJarVersions
WARNING: Artifact versioning problem(s) in directory /labkeyEnlistment/build/modules/api/explodedModule/lib:
Conflicting version of commons-compress jar file (1.14 in directory vs. 1.16.1 from build).
Conflicting version of objenesis jar file (1.0 in directory vs. 2.6 from build).
Run the :server:api:clean task to remove existing artifacts in that directory.
BUILD SUCCESSFUL in 5s
Though these tasks are included as part of the task dependency chains for building and deploying modules, the four tasks can also be executed individually, which can be helpful for resolving version conflicts without resorting to cleaning out the entire build directory. The tasks are: