Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ IntelliJ IDEA 一样按项目列出任务分组和依赖配置,运行任务、
| 内容根 | source set 的 Java/Kotlin/Groovy/Scala 源码、资源和注解处理器生成目录;`build` 与 `.gradle` 标为排除 |
| 同步 | 构建脚本、`gradle.properties`、`gradle/libs.versions.toml`、Wrapper 配置变化后重新读取项目 |

## 新建项目

插件在 New Project 窗口提供 “Gradle” 条目,生成一个 Gradle Java 项目:可选 Kotlin 或 Groovy DSL、JDK、是否添加示例代码,
以及 GroupId 和 ArtifactId。模板在 `io.github.lithe-idea.gradle-build/templates/java/`,从 Java Support 插件移来,
生成的文件与原来相同。条目要求 `java` 语言和 `gradle` 构建系统同时可用,所以生成的项目需要安装 Java Support 插件提供 Java 语言支持。

## 依赖的程序

插件不带 Gradle,使用你项目或电脑上的 Gradle,按以下顺序查找:
Expand Down Expand Up @@ -67,6 +73,7 @@ Lithe 已预置 Lithe Store 社区插件源;如果你之前移除了它,先
io.github.lithe-idea.gradle-build/plugin.json # 插件描述,目录名必须等于插件 id
io.github.lithe-idea.gradle-build/lithe.init.gradle # 初始化脚本,导出模型和依赖树
io.github.lithe-idea.gradle-build/icons/ # 工具窗口和工具栏图标(来源见 NOTICE)
io.github.lithe-idea.gradle-build/templates/java/ # New Project 的 Gradle Java 项目模板(Kotlin/Groovy DSL、示例代码)
test/fixtures/multi/ # 两个子项目的示例构建,供集成测试使用
scripts/package.mjs # 打包成 dist/gradle-build-<版本>-darwin.zip
scripts/test.mjs # 用真实 Gradle 运行清单里的命令并检查输出
Expand Down
78 changes: 76 additions & 2 deletions io.github.lithe-idea.gradle-build/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"displayName": "Gradle",
"version": "0.1.1",
"pluginAPIVersion": 3,
"requires": { "buildSystems": 1 },
"requires": { "buildSystems": 1, "projectGenerators": 1 },
"vendor": {
"id": "io.github.lithe-idea",
"displayName": "Lithe Community",
"signatureRequirement": "none"
},
"permissions": ["process.build"],
"permissions": ["process.build", "project.generate"],
"activationEvents": ["onBuildSystem:gradle"],
"languages": [],
"buildSystems": [
Expand Down Expand Up @@ -173,5 +173,79 @@
"layout": { "excluded": [".gradle", "build"] },
"languageImport": { "kind": "gradle", "options": { "offline": "offline", "gradleUserHome": "gradleUserHome" } }
}
],
"projectGenerators": [
{
"id": "gradle-java",
"displayName": "Gradle",
"description": "A Java project built with Gradle.",
"section": "project",
"order": 30,
"icon": { "path": "icons/gradle.svg" },
"fields": [
{
"id": "gradleDsl",
"type": "enum",
"title": "Gradle DSL",
"presentation": "segmented",
"options": [
{ "value": "kotlin", "title": "Kotlin" },
{ "value": "groovy", "title": "Groovy" }
]
},
{
"id": "jdk",
"type": "runtime",
"title": "JDK",
"runtime": "java",
"minimumVersion": "11"
},
{
"id": "addSampleCode",
"type": "boolean",
"title": "Add sample code",
"default": true
},
{
"id": "groupId",
"type": "text",
"title": "GroupId",
"description": "Usually the reversed domain name of your organization, such as com.example.",
"advanced": true,
"default": "org.example",
"pattern": "[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*",
"maxLength": 200
},
{
"id": "artifactId",
"type": "text",
"title": "ArtifactId",
"description": "The name of the artifact the build produces.",
"advanced": true,
"default": "${project.name}",
"pattern": "[A-Za-z0-9_][A-Za-z0-9_.-]*",
"maxLength": 200
}
],
"requirements": [{ "languageID": "java" }, { "buildSystemID": "gradle" }],
"steps": [
{ "kind": "files", "source": "templates/java/gradle-kotlin", "when": { "field": "gradleDsl", "equals": "kotlin" } },
{ "kind": "files", "source": "templates/java/gradle-groovy", "when": { "field": "gradleDsl", "equals": "groovy" } },
{ "kind": "files", "source": "templates/java/standard-sample", "when": { "field": "addSampleCode", "equals": true } },
{
"kind": "files",
"source": "templates/java/standard-sample-main",
"target": "src/main/java/${field.groupId|package|path}",
"when": { "field": "addSampleCode", "equals": true }
},
{ "kind": "files", "source": "templates/java/standard-empty", "when": { "field": "addSampleCode", "equals": false } }
],
"openFiles": [
{
"path": "src/main/java/${field.groupId|package|path}/Main.java",
"when": { "field": "addSampleCode", "equals": true }
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
id 'java'
}

group = '{{ field.groupId }}'
version = '1.0-SNAPSHOT'

java {
toolchain {
languageVersion = JavaLanguageVersion.of({{ field.jdk.major }})
}
}

repositories {
mavenCentral()
}

dependencies {
testImplementation platform('org.junit:junit-bom:5.13.4')
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### Lithe ###
.lithe/workspace/

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### macOS ###
.DS_Store
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = '{{ field.artifactId }}'
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
java
}

group = "{{ field.groupId }}"
version = "1.0-SNAPSHOT"

java {
toolchain {
languageVersion = JavaLanguageVersion.of({{ field.jdk.major }})
}
}

repositories {
mavenCentral()
}

dependencies {
testImplementation(platform("org.junit:junit-bom:5.13.4"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### Lithe ###
.lithe/workspace/

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### macOS ###
.DS_Store
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "{{ field.artifactId }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package {{ field.groupId | package }};

public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
39 changes: 35 additions & 4 deletions scripts/package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,38 @@
// Packages the plugin into dist/<name>-<version>-darwin.zip, the asset name
// Lithe Store's release tracker recognizes (docs/publishing.md there).
//
// The package is plain data: plugin.json, icons, and the init script that
// runs inside the user's own Gradle, so one archive serves every Mac. The
// The package is plain data: plugin.json, icons, New Project templates, and
// the init script that runs inside the user's own Gradle, so one archive
// serves every Mac. The
// unpacked package stays in dist/package/ for installing a local
// development copy into Lithe.
//
// Usage: node scripts/package.mjs [--expect-tag <tag>]

import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { cpSync, lstatSync, readdirSync, readFileSync, rmSync } from "node:fs";
import { join, resolve } from "node:path";
import { cpSync, lstatSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
import { isAbsolute, join, resolve } from "node:path";

const PACKAGE_DIR = "io.github.lithe-idea.gradle-build";
const ASSET_PREFIX = "gradle-build";
const PLATFORM = "darwin";
const PACKAGE_ROOT = join("dist", "package");

/**
* Why `path` is not an existing `kind` ("icon file" or "template directory")
* inside the plugin package, or undefined when it is one. Absolute paths and
* `..` segments are refused because the package is copied and zipped as is.
*/
function packageEntryProblem(path, kind) {
if (typeof path !== "string" || path === "" || isAbsolute(path) || path.split(/[\\/]/).includes("..")) {
return `${kind} ${path} must be a relative path inside the package`;
}
const stat = statSync(join(PACKAGE_DIR, path), { throwIfNoEntry: false });
const found = kind === "template directory" ? stat?.isDirectory() : stat?.isFile();
return found ? undefined : `missing ${kind} ${path}`;
}

const manifest = JSON.parse(readFileSync(`${PACKAGE_DIR}/plugin.json`, "utf8"));
const problems = [];
if (manifest.id !== PACKAGE_DIR) problems.push(`plugin.json id ${manifest.id} must equal the directory ${PACKAGE_DIR}`);
Expand All @@ -31,6 +46,22 @@ if (tagFlag >= 0) {
const tag = process.argv[tagFlag + 1];
if (tag !== `v${manifest.version}`) problems.push(`tag ${tag} does not match plugin.json version ${manifest.version} (expected v${manifest.version})`);
}

// New Project entries point at icons and template directories by path; a typo
// there would only surface when a user opens the New Project window.
for (const generator of manifest.projectGenerators ?? []) {
const entries = [];
if (generator.icon?.path !== undefined) entries.push(["icon file", generator.icon.path]);
if (generator.icon?.darkPath !== undefined) entries.push(["icon file", generator.icon.darkPath]);
for (const step of generator.steps ?? []) {
if (step.source !== undefined) entries.push(["template directory", step.source]);
}
for (const [kind, path] of entries) {
const problem = packageEntryProblem(path, kind);
if (problem !== undefined) problems.push(`generator ${generator.id}: ${problem}`);
}
}

if (problems.length > 0) {
for (const problem of problems) console.error(`✗ ${problem}`);
process.exit(1);
Expand Down
Loading