diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/AbstractDependencyExtractorPlugin.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/AbstractDependencyExtractorPlugin.kt new file mode 100644 index 00000000..6ce31807 --- /dev/null +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/AbstractDependencyExtractorPlugin.kt @@ -0,0 +1,127 @@ +package org.gradle.github.dependencygraph + +import org.gradle.api.Plugin +import org.gradle.api.invocation.Gradle +import org.gradle.api.provider.Provider +import org.gradle.github.dependencygraph.internal.DependencyExtractor +import org.gradle.github.dependencygraph.internal.DependencyExtractorBuildService +import org.gradle.github.dependencygraph.internal.LegacyDependencyExtractor +import org.gradle.github.dependencygraph.internal.util.GradleExtensions +import org.gradle.github.dependencygraph.internal.util.service +import org.gradle.internal.build.event.BuildEventListenerRegistryInternal +import org.gradle.util.GradleVersion + +abstract class AbstractDependencyExtractorPlugin : Plugin { + // Register extension functions on `Gradle` type + private companion object : GradleExtensions() + + abstract fun getRendererClassName(): String + + internal lateinit var dependencyExtractorProvider: Provider + + override fun apply(gradle: Gradle) { + val gradleVersion = GradleVersion.current() + // Create the adapter based upon the version of Gradle + val applicatorStrategy = when { + gradleVersion < GradleVersion.version("8.0") -> PluginApplicatorStrategy.LegacyPluginApplicatorStrategy + else -> PluginApplicatorStrategy.DefaultPluginApplicatorStrategy + } + + // Create the service + dependencyExtractorProvider = applicatorStrategy.createExtractorService(gradle, getRendererClassName()) + + gradle.rootProject { project -> + dependencyExtractorProvider + .get() + .rootProjectBuildDirectory = project.buildDir + } + + // Register the service to listen for Build Events + applicatorStrategy.registerExtractorListener(gradle, dependencyExtractorProvider) + + // Register the shutdown hook that should execute at the completion of the Gradle build. + applicatorStrategy.registerExtractorServiceShutdown(gradle, dependencyExtractorProvider) + } + + /** + * Adapters for creating the [DependencyExtractor] and installing it into [Gradle] based upon the Gradle version. + */ + private interface PluginApplicatorStrategy { + + fun createExtractorService( + gradle: Gradle, + rendererClassName: String + ): Provider + + fun registerExtractorListener( + gradle: Gradle, + extractorServiceProvider: Provider + ) + + fun registerExtractorServiceShutdown( + gradle: Gradle, + extractorServiceProvider: Provider + ) + + object LegacyPluginApplicatorStrategy : PluginApplicatorStrategy { + + override fun createExtractorService( + gradle: Gradle, + rendererClassName: String + ): Provider { + val dependencyExtractor = LegacyDependencyExtractor(rendererClassName) + return gradle.providerFactory.provider { dependencyExtractor } + } + + override fun registerExtractorListener( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + gradle.buildOperationListenerManager + .addListener(extractorServiceProvider.get()) + } + + override fun registerExtractorServiceShutdown( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + gradle.buildFinished { + extractorServiceProvider.get().close() + gradle.buildOperationListenerManager + .removeListener(extractorServiceProvider.get()) + } + } + } + + object DefaultPluginApplicatorStrategy : PluginApplicatorStrategy { + private const val SERVICE_NAME = "gitHubDependencyExtractorService" + + override fun createExtractorService( + gradle: Gradle, + rendererClassName: String + ): Provider { + return gradle.sharedServices.registerIfAbsent( + SERVICE_NAME, + DependencyExtractorBuildService::class.java + ) { + it.parameters.rendererClassName.set(rendererClassName) + } + } + + override fun registerExtractorListener( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + gradle.service() + .onOperationCompletion(extractorServiceProvider) + } + + override fun registerExtractorServiceShutdown( + gradle: Gradle, + extractorServiceProvider: Provider + ) { + // No-op as DependencyExtractorService is Auto-Closable + } + } + } +} diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyExtractorPlugin.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyExtractorPlugin.kt index 95f09713..e6532da9 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyExtractorPlugin.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/GitHubDependencyExtractorPlugin.kt @@ -1,123 +1,12 @@ package org.gradle.github.dependencygraph -import org.gradle.api.Plugin -import org.gradle.api.invocation.Gradle -import org.gradle.api.provider.Provider -import org.gradle.github.dependencygraph.internal.DependencyExtractor -import org.gradle.github.dependencygraph.internal.DependencyExtractorBuildService -import org.gradle.github.dependencygraph.internal.LegacyDependencyExtractor -import org.gradle.github.dependencygraph.internal.util.GradleExtensions -import org.gradle.github.dependencygraph.internal.util.service -import org.gradle.internal.build.event.BuildEventListenerRegistryInternal -import org.gradle.util.GradleVersion +import org.gradle.github.dependencygraph.internal.github.GitHubDependencyGraphRenderer /** * A plugin that collects all resolved dependencies in a Gradle build and exports it using the GitHub API format. */ -class GitHubDependencyExtractorPlugin : Plugin { - // Register extension functions on `Gradle` type - private companion object : GradleExtensions() - - internal lateinit var dependencyExtractorProvider: Provider - - override fun apply(gradle: Gradle) { - val gradleVersion = GradleVersion.current() - // Create the adapter based upon the version of Gradle - val applicatorStrategy = when { - gradleVersion < GradleVersion.version("8.0") -> PluginApplicatorStrategy.LegacyPluginApplicatorStrategy - else -> PluginApplicatorStrategy.DefaultPluginApplicatorStrategy - } - - // Create the service - dependencyExtractorProvider = applicatorStrategy.createExtractorService(gradle) - - gradle.rootProject { project -> - dependencyExtractorProvider - .get() - .rootProjectBuildDirectory = project.buildDir - } - - // Register the service to listen for Build Events - applicatorStrategy.registerExtractorListener(gradle, dependencyExtractorProvider) - - // Register the shutdown hook that should execute at the completion of the Gradle build. - applicatorStrategy.registerExtractorServiceShutdown(gradle, dependencyExtractorProvider) - } - - /** - * Adapters for creating the [DependencyExtractor] and installing it into [Gradle] based upon the Gradle version. - */ - private interface PluginApplicatorStrategy { - - fun createExtractorService( - gradle: Gradle - ): Provider - - fun registerExtractorListener( - gradle: Gradle, - extractorServiceProvider: Provider - ) - - fun registerExtractorServiceShutdown( - gradle: Gradle, - extractorServiceProvider: Provider - ) - - object LegacyPluginApplicatorStrategy : PluginApplicatorStrategy { - - override fun createExtractorService( - gradle: Gradle - ): Provider { - val dependencyExtractor = LegacyDependencyExtractor() - return gradle.providerFactory.provider { dependencyExtractor } - } - - override fun registerExtractorListener( - gradle: Gradle, - extractorServiceProvider: Provider - ) { - gradle.buildOperationListenerManager - .addListener(extractorServiceProvider.get()) - } - - override fun registerExtractorServiceShutdown( - gradle: Gradle, - extractorServiceProvider: Provider - ) { - gradle.buildFinished { - extractorServiceProvider.get().close() - gradle.buildOperationListenerManager - .removeListener(extractorServiceProvider.get()) - } - } - } - - object DefaultPluginApplicatorStrategy : PluginApplicatorStrategy { - private const val SERVICE_NAME = "gitHubDependencyExtractorService" - - override fun createExtractorService( - gradle: Gradle - ): Provider { - return gradle.sharedServices.registerIfAbsent( - SERVICE_NAME, - DependencyExtractorBuildService::class.java - ) {} - } - - override fun registerExtractorListener( - gradle: Gradle, - extractorServiceProvider: Provider - ) { - gradle.service() - .onOperationCompletion(extractorServiceProvider) - } - - override fun registerExtractorServiceShutdown( - gradle: Gradle, - extractorServiceProvider: Provider - ) { - // No-op as DependencyExtractorService is Auto-Closable - } - } +class GitHubDependencyExtractorPlugin : AbstractDependencyExtractorPlugin() { + override fun getRendererClassName(): String { + return GitHubDependencyGraphRenderer::class.java.name } } diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractor.kt index 3e55ad5b..a60b9fa2 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractor.kt @@ -5,9 +5,8 @@ import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.artifacts.result.ResolvedDependencyResult import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier import org.gradle.api.internal.artifacts.configurations.ResolveConfigurationDependenciesBuildOperationType +import org.gradle.api.logging.Logging import org.gradle.github.GitHubDependencyGraphPlugin -import org.gradle.github.dependencygraph.internal.github.GitHubDependencyGraphOutput -import org.gradle.github.dependencygraph.internal.github.GitHubSnapshotParams import org.gradle.github.dependencygraph.internal.model.* import org.gradle.github.dependencygraph.internal.util.* import org.gradle.initialization.EvaluateSettingsBuildOperationType @@ -50,14 +49,7 @@ abstract class DependencyExtractor : pluginParameters.loadOptional(PARAM_REPORT_DIR) } - private val gitHubSnapshotParams by lazy { - GitHubSnapshotParams( - pluginParameters.load(PARAM_JOB_CORRELATOR), - pluginParameters.load(PARAM_JOB_ID), - pluginParameters.load(PARAM_GITHUB_SHA), - pluginParameters.load(PARAM_GITHUB_REF) - ) - } + abstract fun getRendererClassName(): String override fun started(buildOperation: BuildOperationDescriptor, startEvent: OperationStartEvent) { // This method will never be called when registered in a `BuildServiceRegistry` (ie. Gradle 6.1 & higher) @@ -239,8 +231,12 @@ abstract class DependencyExtractor : } private fun writeDependencyGraph() { - val builder = GitHubDependencyGraphOutput(gitHubSnapshotParams, getOutputDir()) - builder.outputDependencyGraph(resolvedConfigurations, buildLayout) + createRenderer().outputDependencyGraph(pluginParameters, buildLayout, resolvedConfigurations, getOutputDir()) + } + + private fun createRenderer(): DependencyGraphRenderer { + LOGGER.lifecycle("Constructing renderer: ${getRendererClassName()}") + return Class.forName(getRendererClassName()).getDeclaredConstructor().newInstance() as DependencyGraphRenderer } private fun getOutputDir(): File { @@ -275,6 +271,10 @@ abstract class DependencyExtractor : ) } } + + companion object { + private val LOGGER = Logging.getLogger(DependencyExtractor::class.java) + } } private inline fun handleBuildOperationTypeRaw( diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractorBuildService.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractorBuildService.kt index 25fae7fe..dd31087b 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractorBuildService.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyExtractorBuildService.kt @@ -1,8 +1,19 @@ package org.gradle.github.dependencygraph.internal +import org.gradle.api.provider.Property import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters abstract class DependencyExtractorBuildService : DependencyExtractor(), - BuildService + BuildService +{ + // Some parameters for the web server + internal interface Params : BuildServiceParameters { + val rendererClassName: Property + } + + override fun getRendererClassName(): String { + return parameters.rendererClassName.get() + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyGraphRenderer.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyGraphRenderer.kt new file mode 100644 index 00000000..9bf409eb --- /dev/null +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/DependencyGraphRenderer.kt @@ -0,0 +1,14 @@ +package org.gradle.github.dependencygraph.internal + +import org.gradle.github.dependencygraph.internal.model.BuildLayout +import org.gradle.github.dependencygraph.internal.model.ResolvedConfiguration +import org.gradle.github.dependencygraph.internal.util.PluginParameters +import java.io.File + +interface DependencyGraphRenderer { + fun outputDependencyGraph(pluginParameters: PluginParameters, + buildLayout: BuildLayout, + resolvedConfigurations: MutableList, + outputDirectory: File + ) +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/LegacyDependencyExtractor.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/LegacyDependencyExtractor.kt index c73621d5..e71c1aca 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/LegacyDependencyExtractor.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/LegacyDependencyExtractor.kt @@ -2,7 +2,11 @@ package org.gradle.github.dependencygraph.internal import org.gradle.initialization.EvaluateSettingsBuildOperationType -class LegacyDependencyExtractor : DependencyExtractor() { +class LegacyDependencyExtractor(private val rendererClassName: String) : DependencyExtractor() { + override fun getRendererClassName(): String { + return rendererClassName + } + override fun extractSettings(details: EvaluateSettingsBuildOperationType.Details) { // Extraction fails for included builds on Gradle 5.x. // It's OK to ignore these events since we only care about the root build settings file at this stage. diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubDependencyGraphOutput.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubDependencyGraphRenderer.kt similarity index 54% rename from plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubDependencyGraphOutput.kt rename to plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubDependencyGraphRenderer.kt index 62101a84..0e5b6cfd 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubDependencyGraphOutput.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubDependencyGraphRenderer.kt @@ -1,21 +1,35 @@ package org.gradle.github.dependencygraph.internal.github import org.gradle.api.logging.Logging +import org.gradle.github.dependencygraph.internal.DependencyGraphRenderer import org.gradle.github.dependencygraph.internal.github.json.GitHubRepositorySnapshot import org.gradle.github.dependencygraph.internal.model.BuildLayout import org.gradle.github.dependencygraph.internal.model.ResolvedConfiguration +import org.gradle.github.dependencygraph.internal.util.* import java.io.File -class GitHubDependencyGraphOutput(private val snapshotParams: GitHubSnapshotParams, private val outputDir: File) { +class GitHubDependencyGraphRenderer() : DependencyGraphRenderer { + + override fun outputDependencyGraph( + pluginParameters: PluginParameters, + buildLayout: BuildLayout, + resolvedConfigurations: MutableList, + outputDirectory: File + ) { + val snapshotParams = GitHubSnapshotParams( + pluginParameters.load(PARAM_JOB_CORRELATOR), + pluginParameters.load(PARAM_JOB_ID), + pluginParameters.load(PARAM_GITHUB_SHA), + pluginParameters.load(PARAM_GITHUB_REF) + ) - fun outputDependencyGraph(resolvedConfigurations: MutableList, buildLayout: BuildLayout) { val gitHubRepositorySnapshotBuilder = GitHubRepositorySnapshotBuilder(snapshotParams) // Use the job correlator as the manifest name val manifestName = snapshotParams.dependencyGraphJobCorrelator val manifest = gitHubRepositorySnapshotBuilder.buildManifest(manifestName, resolvedConfigurations, buildLayout) val snapshot = gitHubRepositorySnapshotBuilder.buildSnapshot(manifest) - val outputFile = File(outputDir, "${snapshotParams.dependencyGraphJobCorrelator}.json") + val outputFile = File(outputDirectory, "${snapshotParams.dependencyGraphJobCorrelator}.json") writeDependencySnapshot(snapshot, outputFile) } @@ -23,10 +37,10 @@ class GitHubDependencyGraphOutput(private val snapshotParams: GitHubSnapshotPara private fun writeDependencySnapshot(graph: GitHubRepositorySnapshot, manifestFile: File) { manifestFile.parentFile.mkdirs() manifestFile.writeText(JacksonJsonSerializer.serializeToJson(graph)) - LOGGER.lifecycle("\nGitHubDependencyGraphPlugin: Wrote dependency snapshot to \n${manifestFile.canonicalPath}") + LOGGER.lifecycle("\nGitHubDependencyGraphRenderer: Wrote dependency snapshot to \n${manifestFile.canonicalPath}") } companion object { - private val LOGGER = Logging.getLogger(GitHubDependencyGraphOutput::class.java) + private val LOGGER = Logging.getLogger(GitHubDependencyGraphRenderer::class.java) } } \ No newline at end of file diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubRepositorySnapshotBuilder.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubRepositorySnapshotBuilder.kt index c41102b0..7bc591c7 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubRepositorySnapshotBuilder.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/github/GitHubRepositorySnapshotBuilder.kt @@ -31,11 +31,11 @@ class GitHubRepositorySnapshotBuilder( } // Use the root build as the Manifest file location - val manifestFile = buildLayout.getManifestFile() + val manifestFile = buildLayout.getRootBuildFileRelativePath() return GitHubManifest( manifestName, dependencyCollector.getDependencies(), - manifestFile + manifestFile?.let { GitHubManifestFile(sourceLocation = it) } ) } diff --git a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/model/BuildLayout.kt b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/model/BuildLayout.kt index d4e4ae08..34c40879 100644 --- a/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/model/BuildLayout.kt +++ b/plugin/src/main/kotlin/org/gradle/github/dependencygraph/internal/model/BuildLayout.kt @@ -1,6 +1,5 @@ package org.gradle.github.dependencygraph.internal.model -import org.gradle.github.dependencygraph.internal.github.json.GitHubManifestFile import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -18,12 +17,13 @@ class BuildLayout(private val gitWorkspaceDirectory: Path) { projectPathToBuildFile[identityPath] = buildFileAbsolutePath } - fun getManifestFile(): GitHubManifestFile? { - return getManifestFileRelativePath(":")?.let { filePath -> - // Clean up path for Windows systems - val sourceLocation = filePath.toString().replace('\\', '/') - GitHubManifestFile(sourceLocation = sourceLocation) - } + /** + * Returns the relative path to the root build settings file if it exists, or the root build file if not. + */ + fun getRootBuildFileRelativePath(): String? { + val filePath = getManifestFileRelativePath(":") + // Clean up path for Windows systems + return filePath?.toString()?.replace('\\', '/') } private fun getManifestFileRelativePath(identityPath: String): Path? {