Skip to content

Commit

Permalink
DEVEX-631 Do not include non-fs dependencies in lib directory
Browse files Browse the repository at this point in the history
Dependencies not defined using fs-configurations like fsModuleCompile
will no longer be placed in the lib directory.

If a project specifies multiple versions of the same library, this
will now result in a build error pointing out that only a single
version is going to be included in the FSM.
  • Loading branch information
windmueller committed Mar 21, 2024
1 parent f8cb5c4 commit 6b1f5cd
Show file tree
Hide file tree
Showing 52 changed files with 518 additions and 280 deletions.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ To use the plugin, include the following snippet on top of your build script:

```kotlin
plugins {
id("de.espirit.firstspirit-module") version "6.0.0"
id("de.espirit.firstspirit-module") version "6.1.0"
}
```

Expand All @@ -105,7 +105,7 @@ To use the plugin, include the following snippet on top of your build script:

```kotlin
plugins {
id("de.espirit.firstspirit-module-annotations") version "6.0.0"
id("de.espirit.firstspirit-module-annotations") version "6.1.0"
}
```

Expand All @@ -117,7 +117,7 @@ Please take a loot at (#dependency-management) for a detailed description of the

```kotlin
plugins {
id("de.espirit.firstspirit-module-configurations") version "6.0.0"
id("de.espirit.firstspirit-module-configurations") version "6.1.0"
}
```

Expand Down Expand Up @@ -447,10 +447,14 @@ fsModuleCompile | Same as the usual `implementation` configuration, but the depe
fsWebCompile | Same as the usual `implementation` configuration, but the dependency and all transitive dependencies are added to the web-resources tag in the module-isolated.xml

Dependencies with other scopes than these (for example the regular compile scope) are not treated as a resource to be used for module-isolated.xml file generation.
That means if you use compile scope, you can compile your source files against it like in any other project, but the resource won't be listed in the module-isolated.xml.
That means if you use compile scope, you can compile your source files against it like in any other project, but the resource won't be listed in the module-isolated.xml
or included in the `lib` directory.

In a multi-project build make sure to only use the dependency configurations in the project that assembles the fsm. The dependencies of other subprojects should be defined without using the plugin dependency configurations. The resource entries of these dependencies are created depending on how the respective subproject is referenced from the project executing the assembleFSM task.
In a multi-project build make sure to only use the dependency configurations in the project that assembles the fsm. The dependencies of other subprojects should be defined without using the plugin dependency configurations. The resource entries of these dependencies are created depending on how the respective subproject is referenced from the project executing the assembleFSM task.

Local Jars which are part of the project directory will not be included when they are defined with one of the three
scopes above because FirstSpirit requires the artifact metadata (like group-id and version) to detect conflicts
between different modules.

### Example

Expand Down Expand Up @@ -566,7 +570,7 @@ You can use the following snippet as a starting point:
// Groovy
plugins {
id 'de.espirit.firstspirit-module' version '6.0.0'
id 'de.espirit.firstspirit-module' version '6.1.0'
}
description = 'Example FSM Gradle build'
Expand Down Expand Up @@ -601,7 +605,7 @@ firstSpiritModule {
// Kotlin

plugins {
id("de.espirit.firstspirit-module") version "6.0.0"
id("de.espirit.firstspirit-module") version "6.1.0"
}

description = "Example FSM Gradle build"
Expand Down
17 changes: 17 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# Upgrading from 6.0.0 to 6.1.0

* Dependencies not defined using fs-configurations like `fsModuleCompile` will no longer be placed in the `lib`
directory. This results in smaller FSM files without any side effects since those files were not visible to the
classloader anyway.
* If a project specifies multiple versions of the same library, this will now result in a build error pointing out that
only a single version is going to be included in the FSM. In case your build fails with an error message like below,
please check the dependency tree of your project to ensure consistent versions.

```
* What went wrong:
Execution failed for task ':validateDescriptor'.
> File 'lib/commons-io-2.10.0.jar' specified for resource 'commons-io:commons-io' in component of
type 'web-app' with name 'custom-webapp' but is not found in the FSM. However, the different
version 'lib/commons-io-2.7.jar' was found. Please check your project for inconsistent dependency versions.
```

# Upgrading from 5.x.x to 6.0.0

* When packaging an FSM file, only `fsm-resources` of the current project and its dependencies will be included.
Expand Down
20 changes: 10 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.nio.file.Files
import java.util.*

plugins {
kotlin("jvm") version "1.9.0"
kotlin("jvm") version "1.9.20"
id("maven-publish")
id("idea")
id("java-gradle-plugin")
Expand Down Expand Up @@ -66,21 +66,21 @@ val fsRuntimeVersion = "5.2.230909" // FirstSpirit 2023-09

dependencies {
implementation(gradleApi())
implementation("io.github.classgraph:classgraph:4.8.154")
implementation("io.github.classgraph:classgraph:4.8.165")
implementation("com.github.jk1:gradle-license-report:2.3")
implementation("org.redundent:kotlin-xml-builder:1.9.0")
implementation("org.json:json:20230618")
implementation("org.apache.maven:maven-artifact:3.9.4")
implementation("org.apache.httpcomponents.client5:httpclient5:5.2.1")
implementation("org.redundent:kotlin-xml-builder:1.9.1")
implementation("org.json:json:20231013")
implementation("org.apache.maven:maven-artifact:3.9.6")
implementation("org.apache.httpcomponents.client5:httpclient5:5.3")
implementation("com.espirit.moddev.components:annotations:${fsmAnnotationsVersion}")
implementation("de.espirit.firstspirit:fs-isolated-runtime:${fsRuntimeVersion}")
testImplementation("de.espirit.firstspirit:fs-isolated-runtime:${fsRuntimeVersion}")
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation(platform("org.junit:junit-bom:5.10.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.assertj:assertj-core:3.24.2")
testImplementation("org.mockito:mockito-junit-jupiter:5.5.0")
testImplementation("org.ow2.asm:asm:9.5")
testImplementation("org.assertj:assertj-core:3.25.1")
testImplementation("org.mockito:mockito-junit-jupiter:5.9.0")
testImplementation("org.ow2.asm:asm:9.6")
testImplementation(gradleTestKit())
}

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Thu Dec 10 08:28:02 CET 2020
version=6.0.1-SNAPSHOT
version=6.1.0-SNAPSHOT
jira.versionPrefix=FSMGP
jira.excludeFromBundleVersion=true
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://artifactory.e-spirit.de/artifactory/gradle-distributions/gradle-8.3-all.zip
distributionUrl=https\://artifactory.e-spirit.de/artifactory/gradle-distributions/gradle-8.6-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
14 changes: 7 additions & 7 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,15 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
Expand Down Expand Up @@ -202,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.

set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
Expand Down
20 changes: 10 additions & 10 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand All @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand Down
16 changes: 8 additions & 8 deletions src/main/kotlin/org/gradle/plugins/fsm/descriptor/Components.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ class Components(private val project: Project, private val scanResult: Component

init {
node = xml("components") {
components(PublicComponent::class, ::nodesForPublicComponent, scanResult).forEach(this::addNode)
components(ScheduleTaskComponent::class, ::nodesForScheduleTaskComponent, scanResult).forEach(this::addNode)
components(GadgetComponent::class, ::nodesForGadgetComponent, scanResult).forEach(this::addNode)
components(UrlFactoryComponent::class, ::nodesForUrlFactoryComponent, scanResult).forEach(this::addNode)
components(ServiceComponent::class, ::nodesForServiceComponent, scanResult).forEach(this::addNode)
ProjectAppComponents(project, scanResult).nodes.forEach(this::addNode)
LibraryComponents(project).nodes.forEach(this::addNode)
components(PublicComponent::class, ::nodesForPublicComponent, scanResult).forEach(this::addElement)
components(ScheduleTaskComponent::class, ::nodesForScheduleTaskComponent, scanResult).forEach(this::addElement)
components(GadgetComponent::class, ::nodesForGadgetComponent, scanResult).forEach(this::addElement)
components(UrlFactoryComponent::class, ::nodesForUrlFactoryComponent, scanResult).forEach(this::addElement)
components(ServiceComponent::class, ::nodesForServiceComponent, scanResult).forEach(this::addElement)
ProjectAppComponents(project, scanResult).nodes.forEach(this::addElement)
LibraryComponents(project).nodes.forEach(this::addElement)

val webAppComponents = WebAppComponents(project, scanResult)
webAppComponents.nodes.forEach(this::addNode)
webAppComponents.nodes.forEach(this::addElement)
webXmlPaths = webAppComponents.webXmlPaths
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import org.gradle.api.Project
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.plugins.JavaPlugin
import org.gradle.jvm.tasks.Jar
import org.gradle.plugins.fsm.configurations.FSMConfigurationsPlugin.Companion.FS_MODULE_COMPILE_CONFIGURATION_NAME
import org.gradle.plugins.fsm.configurations.FSMConfigurationsPlugin.Companion.FS_SERVER_COMPILE_CONFIGURATION_NAME
import java.io.File

fun ResolvedArtifact.hasSameModuleAs(other: ResolvedArtifact): Boolean {
Expand All @@ -17,4 +19,34 @@ fun ResolvedArtifact.hasSameModuleAs(other: ResolvedArtifact): Boolean {
fun Project.buildJar(): File {
val jarTask = tasks.getByName(JavaPlugin.JAR_TASK_NAME) as Jar
return jarTask.archiveFile.get().asFile
}

/**
* Returns all artifacts defined on the server scope, i.e. with `fsServerCompile`
*/
fun Project.serverScopeDependencies(): Set<ResolvedArtifact> {
val fsModuleCompileConfiguration = configurations.getByName(FS_MODULE_COMPILE_CONFIGURATION_NAME)
val fsServerCompileConfiguration = configurations.getByName(FS_SERVER_COMPILE_CONFIGURATION_NAME)

// Remove duplicate resolved resources from module scope
val resolvedModuleScopeArtifacts = fsModuleCompileConfiguration.resolvedConfiguration.resolvedArtifacts
val resolvedServerScopeArtifacts = fsServerCompileConfiguration.resolvedConfiguration.resolvedArtifacts
return resolvedModuleScopeArtifacts.filter {
// Module scope configuration extends server scope configuration, so we need to filter duplicates
moduleScoped -> resolvedServerScopeArtifacts.any { it.hasSameModuleAs(moduleScoped) }
}.toSet()
}


/**
* Returns all artifacts on the module scope not superseded by server-scoped dependencies
*/
fun Project.moduleScopeDependencies(): Set<ResolvedArtifact> {
val fsModuleCompileConfiguration = configurations.getByName(FS_MODULE_COMPILE_CONFIGURATION_NAME)
val resolvedModuleScopeArtifacts = fsModuleCompileConfiguration.resolvedConfiguration.resolvedArtifacts

// Remove duplicate resolved resources from module scope
val cleanedCompileDependenciesModuleScoped = resolvedModuleScopeArtifacts.toMutableSet()
cleanedCompileDependenciesModuleScoped.removeAll(serverScopeDependencies())
return cleanedCompileDependenciesModuleScoped
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class LibraryComponents(project: Project): ComponentsWithResources(project) {
LOGGER.warn("Library '${library.name}' does not specify any resources.")
}

nodes.forEach(::addNode)
nodes.forEach(::addElement)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ class ModuleDescriptor(private val project: Project) {
version = XmlVersion.V10
moduleInformation(this)
"dependencies" {
dependencies.forEach(this::addNode)
dependencies.forEach(this::addElement)
}
moduleClass.nodes.forEach(this::addNode)
addNode(componentsNode)
addNode(resources.node)
moduleClass.nodes.forEach(this::addElement)
addElement(componentsNode)
addElement(resources.node)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ProjectAppComponents(project: Project, private val scanResult: ComponentSc
val resources = nodesForResources(annotation)
if (resources.isNotEmpty()) {
"resources" {
resources.forEach(this::addNode)
resources.forEach(this::addElement)
}
}
}
Expand Down
39 changes: 7 additions & 32 deletions src/main/kotlin/org/gradle/plugins/fsm/descriptor/Resources.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package org.gradle.plugins.fsm.descriptor

import de.espirit.firstspirit.server.module.ModuleInfo.Mode
import org.gradle.api.Project
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.plugins.fsm.configurations.FSMConfigurationsPlugin
import org.gradle.plugins.fsm.configurations.FSMConfigurationsPlugin.Companion.FS_MODULE_COMPILE_CONFIGURATION_NAME
import org.gradle.plugins.fsm.configurations.FSMConfigurationsPlugin.Companion.FS_SERVER_COMPILE_CONFIGURATION_NAME
import org.redundent.kotlin.xml.Node
import org.redundent.kotlin.xml.PrintOptions
import org.redundent.kotlin.xml.xml
Expand All @@ -19,9 +16,9 @@ class Resources(private val project: Project, private val webXmlPaths: List<Stri

val node by lazy {
xml("resources") {
projectResource()?.let(this::addNode)
fsmResources().forEach(this::addNode)
dependencies().forEach(this::addNode)
projectResource()?.let(this::addElement)
fsmResources().forEach(this::addElement)
dependencies().forEach(this::addElement)
}
}

Expand Down Expand Up @@ -50,7 +47,7 @@ class Resources(private val project: Project, private val webXmlPaths: List<Stri
attribute("name", "${project.group}:${project.name}")
attribute("version", project.version)
attribute("scope", "module")
attribute("mode", Mode.ISOLATED.name.lowercase())
attribute("mode", "isolated")
-"lib/${jarFile.name}"
}
}
Expand Down Expand Up @@ -125,7 +122,7 @@ class Resources(private val project: Project, private val webXmlPaths: List<Stri
attribute("name", "${project.group}:${project.name}-${relativePath}")
attribute("version", project.version)
attribute("scope", scope)
attribute("mode", Mode.ISOLATED.name.lowercase())
attribute("mode", "isolated")
text(relativePath.toString())
}
fsmResources.add(relativePath.toString() to node)
Expand All @@ -140,39 +137,17 @@ class Resources(private val project: Project, private val webXmlPaths: List<Stri
private fun dependencies(): List<Node> {
val dependencies = mutableListOf<Node>()

val configurations = project.configurations
val fsModuleCompileConfiguration = configurations.getByName(FS_MODULE_COMPILE_CONFIGURATION_NAME)
val fsServerCompileConfiguration = configurations.getByName(FS_SERVER_COMPILE_CONFIGURATION_NAME)
val uncleanedDependenciesModuleScoped = fsModuleCompileConfiguration.resolvedConfiguration.resolvedArtifacts
val resolvedServerScopeArtifacts = fsServerCompileConfiguration.resolvedConfiguration.resolvedArtifacts

val compileDependenciesServerScoped = uncleanedDependenciesModuleScoped.filter {
moduleScoped -> resolvedServerScopeArtifacts.any { it.hasSameModuleAs(moduleScoped) }
}.toMutableSet()
val cleanedCompileDependenciesModuleScoped = uncleanedDependenciesModuleScoped.toMutableSet()
cleanedCompileDependenciesModuleScoped.removeAll(compileDependenciesServerScoped)

logIgnoredModuleScopeDependencies(uncleanedDependenciesModuleScoped, cleanedCompileDependenciesModuleScoped)

compileDependenciesServerScoped
project.serverScopeDependencies()
.map { Resource(project, it, "server").node }
.forEach(dependencies::add)

cleanedCompileDependenciesModuleScoped
project.moduleScopeDependencies()
.map { Resource(project, it, "module").node }
.forEach(dependencies::add)

return dependencies
}

private fun logIgnoredModuleScopeDependencies(uncleanedDependenciesModuleScoped: Set<ResolvedArtifact>, compileDependenciesModuleScoped: Set<ResolvedArtifact>) {
val ignoredDependenciesModuleScoped = uncleanedDependenciesModuleScoped - compileDependenciesModuleScoped
LOGGER.debug("The following dependencies are found on both module and server scope. The scope will be resolved to server.")
ignoredDependenciesModuleScoped.forEach {
LOGGER.debug(it.toString())
}
}

companion object {
val LOGGER: Logger = Logging.getLogger(Resources::class.java)
private val PRINT_OPTIONS = PrintOptions(singleLineTextElements = true)
Expand Down
Loading

0 comments on commit 6b1f5cd

Please sign in to comment.