Skip to content

Commit

Permalink
feat(core): add gradle plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Dec 24, 2024
1 parent fbd3db8 commit 6dc0f8b
Show file tree
Hide file tree
Showing 29 changed files with 1,146 additions and 490 deletions.
Binary file removed e2e/gradle/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
10 changes: 3 additions & 7 deletions e2e/gradle/src/utils/create-gradle-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,10 @@ export function createGradleProject(
packageName: string = 'gradleProject',
addProjectJsonNamePrefix: string = ''
) {
e2eConsoleLogger(
`Using java version: ${execSync('java -version')} ${execSync(
'echo $JAVA_HOME'
)}`
);
e2eConsoleLogger(`Using java version: ${execSync('java -version')}`);
const gradleCommand = isWindows()
? resolve(`${__dirname}/../../gradlew.bat`)
: resolve(`${__dirname}/../../gradlew`);
? resolve(`${__dirname}/../../../../packages/gradle/native/gradlew.bat`)
: resolve(`${__dirname}/../../../../packages/gradle/native/gradlew`);
e2eConsoleLogger(
'Using gradle version: ' +
execSync(`${gradleCommand} --version`, {
Expand Down
File renamed without changes.
5 changes: 5 additions & 0 deletions packages/gradle/native/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
6 changes: 6 additions & 0 deletions packages/gradle/native/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties

org.gradle.parallel=true
org.gradle.caching=true
version=0.1.0
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format

[plugins]
jvm = { id = "org.jetbrains.kotlin.jvm", version = "1.9.20" }
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
validateDistributionUrl=false
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
File renamed without changes.
File renamed without changes.
68 changes: 68 additions & 0 deletions packages/gradle/native/plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Gradle plugin project to get you started.
* For more details on writing Custom Plugins, please refer to https://docs.gradle.org/8.5/userguide/custom_plugins.html in the Gradle documentation.
* This project uses @Incubating APIs which are subject to change.
*/

plugins {
// Apply the Java Gradle plugin development plugin to add support for developing Gradle plugins
`java-gradle-plugin`
`maven-publish`

// Apply the Kotlin JVM plugin to add support for Kotlin.
alias(libs.plugins.jvm)
}

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

testing {
suites {
// Configure the built-in test suite
val test by getting(JvmTestSuite::class) {
// Use Kotlin Test test framework
useKotlinTest("1.9.20")
}

// Create a new test suite
val functionalTest by registering(JvmTestSuite::class) {
// Use Kotlin Test test framework
useKotlinTest("1.9.20")

dependencies {
// functionalTest test suite depends on the production code in tests
implementation(project())
}

targets {
all {
// This test suite should run after the built-in test suite has run its tests
testTask.configure { shouldRunAfter(test) }
}
}
}
}
}

gradlePlugin {
// Define the plugin
val Nodes by plugins.creating {
id = "io.nx.gradle.plugin.Nodes"
implementationClass = "io.nx.gradle.plugin.Nodes"
}
}

dependencies {
implementation("com.google.code.gson:gson:2.11.0")
}

// gradlePlugin.testSourceSets.add(sourceSets["functionalTest"])

// tasks.named<Task>("check") {
// Include functionalTest as part of the check lifecycle
// dependsOn(testing.suites.named("functionalTest"))
// }
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package io.nx.gradle.plugin

import org.gradle.api.DefaultTask
import org.gradle.api.Project
import java.io.File
import org.gradle.api.tasks.options.Option
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.lang.Error
import java.nio.file.Path

data class GradleTargets(val targets: MutableMap<String, Any>, val targetGroups: MutableMap<String, MutableList<String>>)
data class Metadata(val targetGroups: MutableMap<String, MutableList<String>>, val technologies: Array<String>)
data class ProjectConfiguration(val targets: MutableMap<String, Any>, val metadata: Metadata, val name: String)
data class Dependency(val source: String, val target: String, val sourceFile: String)
data class Node(val project: ProjectConfiguration, val dependencies: MutableList<Dependency>)

abstract class CreateNodesTask : DefaultTask() {
@Option(option = "outputDirectory", description = "Output directory, default to {workspaceRoot}/.nx/cache")
@Input
var outputDirectory: String = ""

@Option(option = "workspaceRoot", description = "Workspace root, default to cwd")
@Input
var workspaceRoot: String = ""

@TaskAction
fun action() {
val rootProjectDirectory = project.getProjectDir()
if (workspaceRoot == "") {
// assign the workspace root to root project's path
workspaceRoot = System.getProperty("user.dir")
}
if (outputDirectory == "") {
outputDirectory = File(workspaceRoot, ".nx/cache").getPath()
}

val projectNodes = mutableMapOf<String, Node>()
project.getAllprojects().forEach { project ->
try {
// get dependencies of project
var dependencies = mutableListOf<Dependency>();
project.getChildProjects().forEach { childProject ->
dependencies.add(
Dependency(
project.getProjectDir().getPath(),
childProject.value.getProjectDir().getPath(),
project.getBuildFile().getPath()
)
)
}
project.getGradle().includedBuilds.forEach { includedBuild ->
dependencies.add(
Dependency(
project.getProjectDir().getPath(),
includedBuild.getProjectDir().getPath(),
project.getBuildFile().getPath()
)
)
}

val gradleTargets = this.processTargetsForProject(project, workspaceRoot, rootProjectDirectory)
var projectRoot = project.getProjectDir().getPath()
val projectConfig = ProjectConfiguration(
gradleTargets.targets,
Metadata(gradleTargets.targetGroups, arrayOf("Gradle")),
project.getName()
)
projectNodes.put(projectRoot, Node(projectConfig, dependencies))
} catch (e: Error) {
} // ignore errors
}
val gson = Gson()
val json = gson.toJson(projectNodes)
val file = File(outputDirectory, "${project.name}.json")
file.writeText(json)
println(file)
}

fun processTargetsForProject(project: Project, workspaceRoot: String, rootProjectDirectory: File): GradleTargets {
val targets = mutableMapOf<String, Any>();
val targetGroups = mutableMapOf<String, MutableList<String>>();
val projectRoot = project.getProjectDir().getPath()

var command: String;
val operSys = System.getProperty("os.name").lowercase();
if (operSys.contains("win")) {
command = ".\\gradlew.bat "
} else {
command = "./gradlew "
}
command += project.getBuildTreePath()
if (!command.endsWith(":")) {
command += ":"
}

project.getTasks().forEach { task ->
val target = mutableMapOf<String, Any?>()
val metadata = mutableMapOf<String, Any?>()
var taskCommand = command.toString()
metadata.put("description", task.getDescription())
metadata.put("technologies", arrayOf("Gradle"))
val group: String? = task.getGroup();
if (!group.isNullOrBlank()) {
if (targetGroups.contains(group)) {
targetGroups.get(group)?.add(task.name)
} else {
targetGroups.set(group, mutableListOf<String>(task.name))
}
}

var inputs = task.getInputs().getSourceFiles()
if (!inputs.isEmpty()) {
target.put("inputs", inputs.mapNotNull { file ->
var path: String = file.getPath()
path = replaceRootInPath(path, projectRoot, workspaceRoot)
path
})
}
val outputs = task.getOutputs().getFiles()
if (!outputs.isEmpty()) {
target.put("outputs", outputs.map { file ->
var path: String = file.getPath()
path = replaceRootInPath(path, projectRoot, workspaceRoot)
path
})
}
target.put("cache", true)

val dependsOn = task.getTaskDependencies().getDependencies(task)
if (!dependsOn.isEmpty()) {
target.put("dependsOn", dependsOn.map { depTask -> "${depTask.getProject().name}:${depTask.name}" })
}
target.put("metadata", metadata)

taskCommand += task.name
target.put("command", taskCommand)
target.put("options", mapOf("cwd" to rootProjectDirectory.getPath()))

targets.put(task.name, target)
}

return GradleTargets(
targets,
targetGroups
);
}
}

fun replaceRootInPath(p: String, projectRoot: String, workspaceRoot: String): String {
var path = p
if (path.startsWith(projectRoot)) {
path = path.replace(projectRoot, "{projectRoot}")
} else if (path.startsWith(workspaceRoot)) {
path = path.replace(workspaceRoot, "{workspaceRoot}")
}
return path
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.nx.gradle.plugin

import org.gradle.api.Project
import org.gradle.api.Plugin

/**
* A plugin to create nx targets
*/
class Nodes: Plugin<Project> {
override fun apply(project: Project) {
// Register a task
project.tasks.register("createNodes", CreateNodesTask::class.java) { task ->
task.setDescription("Create nodes and dependencies for Nx")
task.setGroup("Nx Custom")
// Run task for composite builds
project.getGradle().includedBuilds.forEach { includedBuild ->
task.dependsOn(includedBuild.task(":createNodes"))
}
}
}
}
10 changes: 10 additions & 0 deletions packages/gradle/native/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
* For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.5/userguide/building_swift_projects.html in the Gradle documentation.
* This project uses @Incubating APIs which are subject to change.
*/

rootProject.name = "plugin"
include("plugin")
2 changes: 2 additions & 0 deletions packages/gradle/plugin-v1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { createDependencies } from './src/plugin-v1/dependencies';
export { createNodes, createNodesV2 } from './src/plugin-v1/nodes';
2 changes: 1 addition & 1 deletion packages/gradle/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { createDependencies } from './src/plugin/dependencies';
export { createNodes, createNodesV2 } from './src/plugin/nodes';
export { createNodesV2 } from './src/plugin/nodes';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
import { GradlePluginOptions } from '../../plugin/nodes';
import { GradlePluginOptions } from '../../plugin-v1/nodes';

// This function add options includeSubprojectsTasks as true in nx.json for gradle plugin
export default function update(tree: Tree) {
Expand Down
Loading

0 comments on commit 6dc0f8b

Please sign in to comment.