diff --git a/.github/workflows/check-codes.yml b/.github/workflows/check-codes.yml index 3cd88ca7f84..a4486fda2b4 100644 --- a/.github/workflows/check-codes.yml +++ b/.github/workflows/check-codes.yml @@ -26,7 +26,7 @@ jobs: with: cache-cleanup: never - name: Check Codes - run: ./gradlew checkstyle checkTranslations --no-daemon --parallel --stacktrace + run: ./gradlew checkstyle checkTranslations --no-daemon --parallel --configuration-cache --stacktrace - if: failure() && github.event_name == 'pull_request' name: Setup reviewdog uses: reviewdog/action-setup@d8edfce3dd5e1ec6978745e801f9c50b5ef80252 # v1.4.0 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b2b40d47719..a276ba05fc9 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -24,7 +24,7 @@ jobs: with: cache-cleanup: never - name: Build with Gradle - run: xvfb-run --auto-servernum ./gradlew build --no-daemon --parallel + run: xvfb-run --auto-servernum ./gradlew build --no-daemon --parallel --configuration-cache env: JAVA_TOOL_OPTIONS: -Djava.awt.headless=true MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c0715591d0..fe56f2ab332 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: distribution: 'temurin' java-version: '25' - name: Fetch last version - run: ./gradlew ${{ matrix.task }} --no-daemon --info --stacktrace + run: ./gradlew ${{ matrix.task }} --no-daemon --configuration-cache --info --stacktrace - name: Check for existing tags run: if [ -z "$(git tag -l "$HMCL_TAG_NAME")" ]; then echo "continue=true" >> $GITHUB_ENV; fi - name: Download artifacts diff --git a/HMCL/build.gradle.kts b/HMCL/build.gradle.kts index 94c97a8b792..f813a441e14 100644 --- a/HMCL/build.gradle.kts +++ b/HMCL/build.gradle.kts @@ -1,29 +1,23 @@ -import org.jackhuang.hmcl.gradle.TerracottaConfigUpgradeTask import org.jackhuang.hmcl.gradle.ci.GitHubActionUtils import org.jackhuang.hmcl.gradle.ci.JenkinsUtils -import org.jackhuang.hmcl.gradle.l10n.CheckTranslations -import org.jackhuang.hmcl.gradle.l10n.CreateLanguageList -import org.jackhuang.hmcl.gradle.l10n.CreateLocaleNamesResourceBundle -import org.jackhuang.hmcl.gradle.l10n.SyncTranslations -import org.jackhuang.hmcl.gradle.l10n.UpsideDownTranslate +import org.jackhuang.hmcl.gradle.l10n.* import org.jackhuang.hmcl.gradle.mod.ParseModDataTask import org.jackhuang.hmcl.gradle.pack.CreateDeb import org.jackhuang.hmcl.gradle.pack.ReleaseType +import org.jackhuang.hmcl.gradle.terracotta.AbstractTerracottaTask +import org.jackhuang.hmcl.gradle.terracotta.TerracottaConfigUpgradeTask +import org.jackhuang.hmcl.gradle.terracotta.TerracottaConfigValidateTask +import org.jackhuang.hmcl.gradle.utils.ArtifactUtils.Companion.resolveArtifactFile +import org.jackhuang.hmcl.gradle.utils.ArtifactUtils.Companion.attachSignature +import org.jackhuang.hmcl.gradle.utils.ArtifactUtils.Companion.createChecksum import org.jackhuang.hmcl.gradle.utils.PropertiesUtils -import java.net.URI -import java.nio.file.FileSystems -import java.nio.file.Files -import java.security.KeyFactory -import java.security.MessageDigest -import java.security.Signature -import java.security.spec.PKCS8EncodedKeySpec import java.util.zip.ZipFile plugins { alias(libs.plugins.shadow) } -val projectConfig = PropertiesUtils.load(rootProject.file("config/project.properties").toPath()) +val projectConfig = PropertiesUtils.load(providers.fileContents(rootProject.layout.projectDirectory.file("config/project.properties"))) val isOfficial = JenkinsUtils.IS_ON_CI || GitHubActionUtils.IS_ON_OFFICIAL_REPO @@ -76,47 +70,6 @@ dependencies { embedResources(libs.lwjgl.unsafe.agent) } -fun digest(algorithm: String, bytes: ByteArray): ByteArray = MessageDigest.getInstance(algorithm).digest(bytes) - -fun createChecksum(file: File) { - val algorithms = linkedMapOf( - "SHA-1" to "sha1", - "SHA-256" to "sha256", - "SHA-512" to "sha512" - ) - - algorithms.forEach { (algorithm, ext) -> - File(file.parentFile, "${file.name}.$ext").writeText( - digest(algorithm, file.readBytes()).joinToString(separator = "", postfix = "\n") { "%02x".format(it) } - ) - } -} - -fun attachSignature(jar: File) { - val keyLocation = System.getenv("HMCL_SIGNATURE_KEY") - if (keyLocation == null) { - logger.warn("Missing signature key") - return - } - - val privatekey = KeyFactory.getInstance("RSA").generatePrivate(PKCS8EncodedKeySpec(File(keyLocation).readBytes())) - val signer = Signature.getInstance("SHA512withRSA") - signer.initSign(privatekey) - ZipFile(jar).use { zip -> - zip.stream() - .sorted(Comparator.comparing { it.name }) - .filter { it.name != "META-INF/hmcl_signature" } - .forEach { - signer.update(digest("SHA-512", it.name.toByteArray())) - signer.update(digest("SHA-512", zip.getInputStream(it).readBytes())) - } - } - val signature = signer.sign() - FileSystems.newFileSystem(URI.create("jar:" + jar.toURI()), emptyMap()).use { zipfs -> - Files.newOutputStream(zipfs.getPath("META-INF/hmcl_signature")).use { it.write(signature) } - } -} - tasks.withType { sourceCompatibility = "17" targetCompatibility = "17" @@ -153,29 +106,30 @@ tasks.compileJava { options.compilerArgs.addAll(addOpens.map { "--add-exports=$it=ALL-UNNAMED" }) } -val hmclProperties = buildList { - add("hmcl.version" to project.version.toString()) - add("hmcl.add-opens" to addOpens.joinToString(" ")) - System.getenv("GITHUB_SHA")?.let { - add("hmcl.version.hash" to it) - } - add("hmcl.version.type" to versionType) - add("hmcl.microsoft.auth.id" to microsoftAuthId) - add("hmcl.curseforge.apikey" to curseForgeApiKey) - add("hmcl.authlib-injector.version" to libs.authlib.injector.get().version!!) - add("hmcl.lwjgl-unsafe-agent.version" to libs.lwjgl.unsafe.agent.get().version!!) -} +val hmclProperties: Map = mapOf( + "hmcl.version" to project.version.toString(), + "hmcl.add-opens" to addOpens.joinToString(" "), + "hmcl.version.hash" to System.getenv("GITHUB_SHA"), + "hmcl.version.type" to versionType, + "hmcl.microsoft.auth.id" to microsoftAuthId, + "hmcl.curseforge.apikey" to curseForgeApiKey, + "hmcl.authlib-injector.version" to libs.authlib.injector.get().version!!, + "hmcl.lwjgl-unsafe-agent.version" to libs.lwjgl.unsafe.agent.get().version!! +).filterValues { it != null } val hmclPropertiesFile = layout.buildDirectory.file("hmcl.properties") val createPropertiesFile = tasks.register("createPropertiesFile") { outputs.file(hmclPropertiesFile) - hmclProperties.forEach { (k, v) -> inputs.property(k, v) } + inputs.properties(hmclProperties) + + val file = hmclPropertiesFile + val prop = hmclProperties doLast { - val targetFile = hmclPropertiesFile.get().asFile + val targetFile = file.get().asFile targetFile.parentFile.mkdir() targetFile.bufferedWriter().use { - for ((k, v) in hmclProperties) { + for ((k, v) in prop) { it.write("$k=$v\n") } } @@ -228,9 +182,16 @@ tasks.shadowJar { } } + val jarFile = jarPath + doLast { - attachSignature(jarPath) - createChecksum(jarPath) + val keyLocation = System.getenv("HMCL_SIGNATURE_KEY") + if (keyLocation != null) { + attachSignature(jarFile, keyLocation) + } else { + logger.warn("Missing signature key") + } + createChecksum(jarFile) } } @@ -239,6 +200,7 @@ tasks.processResources { dependsOn(upsideDownTranslate) dependsOn(createLocaleNamesResourceBundle) dependsOn(createLanguageList) + dependsOn(validateTerracottaConfig) into("assets/") { from(hmclPropertiesFile) @@ -250,29 +212,24 @@ tasks.processResources { from(upsideDownTranslate.map { it.outputFile }) from(createLocaleNamesResourceBundle.map { it.outputDirectory }) } - - inputs.property("terracotta_version", libs.versions.terracotta) - doLast { - upgradeTerracottaConfig.get().checkValid() - } } -fun artifactFile(ext: String) = jarPath.resolveSibling(jarPath.nameWithoutExtension + '.' + ext) - val makeExecutables = tasks.register("makeExecutables") { val extensions = listOf("exe", "sh") dependsOn(tasks.jar) inputs.file(jarPath) - outputs.files(extensions.map { artifactFile(it) }) + outputs.files(extensions.map { resolveArtifactFile(jarPath, it) }) + + val jarFile = jarPath doLast { - val jarContent = jarPath.readBytes() + val jarContent = jarFile.readBytes() - ZipFile(jarPath).use { zipFile -> + ZipFile(jarFile).use { zipFile -> for (extension in extensions) { - val output = artifactFile(extension) + val output = resolveArtifactFile(jarFile, extension) val entry = zipFile.getEntry("assets/HMCLauncher.$extension") ?: throw GradleException("HMCLauncher.$extension not found") @@ -290,7 +247,7 @@ val makeExecutables = tasks.register("makeExecutables") { val makeDeb = tasks.register("makeDeb", CreateDeb::class) { dependsOn(makeExecutables) - val debFile = layout.file(provider { artifactFile("deb") }) + val debFile = layout.file(provider { resolveArtifactFile(jarPath, "deb") }) val debChannel = when (versionType) { "stable" -> ReleaseType.STABLE @@ -301,7 +258,7 @@ val makeDeb = tasks.register("makeDeb", CreateDeb::class) { version.set(project.version.toString()) releaseType.set(debChannel) launcherClassName.set("org.jackhuang.hmcl.Launcher") - appShFile.set(layout.file(provider { artifactFile("sh") })) + appShFile.set(layout.file(provider { resolveArtifactFile(jarPath, "sh") })) iconFile.set(layout.projectDirectory.file("image/hmcl.png")) outputFile.set(debFile) @@ -405,10 +362,9 @@ tasks.register("run") { // terracotta -val upgradeTerracottaConfig = tasks.register("upgradeTerracottaConfig") { - val destination = layout.projectDirectory.file("src/main/resources/assets/terracotta.json") - val source = layout.projectDirectory.file("terracotta-template.json"); +val terracottaConfigFile = layout.projectDirectory.file("src/main/resources/assets/terracotta.json") +val upgradeTerracottaConfig = tasks.register("upgradeTerracottaConfig") { classifiers.set( listOf( "windows-x86_64", "windows-arm64", @@ -418,11 +374,18 @@ val upgradeTerracottaConfig = tasks.register("upgra ) ) - version.set(libs.versions.terracotta) downloadURL.set($$"https://github.com/burningtnt/Terracotta/releases/download/v${version}/terracotta-${version}-${classifier}-pkg.tar.gz") - templateFile.set(source) - outputFile.set(destination) + templateFile.set(layout.projectDirectory.file("terracotta-template.json")) +} + +val validateTerracottaConfig = tasks.register("validateTerracottaConfig") { + upgradeTaskPath.set(upgradeTerracottaConfig.get().path) +} + +tasks.withType { + version.set(libs.versions.terracotta) + outputFile.set(terracottaConfigFile) } // Check Translations diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index cde50fb588e..dde17879ca8 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,3 +1,7 @@ +plugins { + `kotlin-dsl` +} + repositories { System.getenv("MAVEN_CENTRAL_REPO").let { repo -> if (repo.isNullOrBlank()) @@ -22,8 +26,9 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } tasks.withType { diff --git a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/AbstractTerracottaTask.java b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/AbstractTerracottaTask.java new file mode 100644 index 00000000000..cc0e495fb96 --- /dev/null +++ b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/AbstractTerracottaTask.java @@ -0,0 +1,40 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.gradle.terracotta; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.jetbrains.annotations.NotNull; + +public abstract class AbstractTerracottaTask extends DefaultTask { + + protected static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + @Input + public abstract Property<@NotNull String> getVersion(); + + @OutputFile + public abstract RegularFileProperty getOutputFile(); + +} diff --git a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/TerracottaConfigUpgradeTask.java b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/TerracottaConfigUpgradeTask.java similarity index 81% rename from buildSrc/src/main/java/org/jackhuang/hmcl/gradle/TerracottaConfigUpgradeTask.java rename to buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/TerracottaConfigUpgradeTask.java index 5a465b20762..08b1af1c388 100644 --- a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/TerracottaConfigUpgradeTask.java +++ b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/TerracottaConfigUpgradeTask.java @@ -15,24 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.gradle; +package org.jackhuang.hmcl.gradle.terracotta; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.annotations.SerializedName; import kala.compress.archivers.tar.TarArchiveEntry; import kala.compress.archivers.tar.TarArchiveReader; -import org.gradle.api.DefaultTask; -import org.gradle.api.GradleException; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -53,29 +46,20 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.zip.GZIPInputStream; -public abstract class TerracottaConfigUpgradeTask extends DefaultTask { - - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); +public abstract class TerracottaConfigUpgradeTask extends AbstractTerracottaTask { @Input public abstract ListProperty<@NotNull String> getClassifiers(); - @Input - public abstract Property<@NotNull String> getVersion(); - @Input public abstract Property<@NotNull String> getDownloadURL(); @InputFile public abstract RegularFileProperty getTemplateFile(); - @OutputFile - public abstract RegularFileProperty getOutputFile(); - @TaskAction public void run() throws Exception { JsonObject config = GSON.fromJson( @@ -161,21 +145,6 @@ public void run() throws Exception { Files.writeString(getOutputFile().get().getAsFile().toPath(), GSON.toJson(config), StandardCharsets.UTF_8); } - public void checkValid() throws IOException { - Path output = getOutputFile().get().getAsFile().toPath(); - if (Files.isReadable(output)) { - String version = GSON.fromJson(Files.readString(output, StandardCharsets.UTF_8), JsonObject.class) - .get("version_latest").getAsJsonPrimitive().getAsString(); - if (Objects.equals(version, getVersion().get())) { - return; - } - } - - throw new GradleException(String.format("Terracotta config isn't up-to-date! " + - "You might have just edited the version number in libs.version.toml. " + - "Please run task %s to resolve the new config.", getPath())); - } - private record Bundle( @SerializedName("hash") String hash, @SerializedName("files") Map files diff --git a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/TerracottaConfigValidateTask.java b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/TerracottaConfigValidateTask.java new file mode 100644 index 00000000000..82df572af68 --- /dev/null +++ b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/terracotta/TerracottaConfigValidateTask.java @@ -0,0 +1,52 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.gradle.terracotta; + +import com.google.gson.JsonObject; +import org.gradle.api.GradleException; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.*; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public abstract class TerracottaConfigValidateTask extends AbstractTerracottaTask { + + @Input + public abstract Property<@NotNull String> getUpgradeTaskPath(); + + @TaskAction + public void run() throws IOException { + Path output = getOutputFile().get().getAsFile().toPath(); + if (Files.isReadable(output)) { + String version = GSON.fromJson(Files.readString(output, StandardCharsets.UTF_8), JsonObject.class) + .get("version_latest").getAsJsonPrimitive().getAsString(); + if (Objects.equals(version, getVersion().get())) { + return; + } + } + + throw new GradleException(String.format("Terracotta config isn't up-to-date! " + + "You might have just edited the version number in libs.version.toml. " + + "Please run task %s to resolve the new config.", getUpgradeTaskPath().get())); + } +} diff --git a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/utils/PropertiesUtils.java b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/utils/PropertiesUtils.java index f5b06852215..31c5cb2d7c3 100644 --- a/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/utils/PropertiesUtils.java +++ b/buildSrc/src/main/java/org/jackhuang/hmcl/gradle/utils/PropertiesUtils.java @@ -17,8 +17,10 @@ */ package org.jackhuang.hmcl.gradle.utils; +import org.gradle.api.file.FileContents; import org.jetbrains.annotations.NotNull; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -26,6 +28,14 @@ /// @author Glavo public final class PropertiesUtils { + public static @NotNull Properties load(FileContents fileContents) throws IOException { + Properties properties = new Properties(); + try (var inputStream = new ByteArrayInputStream(fileContents.getAsBytes().get())) { + properties.load(inputStream); + } + return properties; + } + public static @NotNull Properties load(Path path) throws IOException { Properties properties = new Properties(); try (var reader = Files.newBufferedReader(path)) { diff --git a/buildSrc/src/main/kotlin/org/jackhuang/hmcl/gradle/utils/ArtifactUtils.kt b/buildSrc/src/main/kotlin/org/jackhuang/hmcl/gradle/utils/ArtifactUtils.kt new file mode 100644 index 00000000000..bf93f8fbfd8 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jackhuang/hmcl/gradle/utils/ArtifactUtils.kt @@ -0,0 +1,71 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.jackhuang.hmcl.gradle.utils + +import java.io.File +import java.net.URI +import java.nio.file.FileSystems +import java.nio.file.Files +import java.security.KeyFactory +import java.security.MessageDigest +import java.security.Signature +import java.security.spec.PKCS8EncodedKeySpec +import java.util.zip.ZipFile + +class ArtifactUtils { + + companion object { + val algorithms = linkedMapOf( + "SHA-1" to "sha1", + "SHA-256" to "sha256", + "SHA-512" to "sha512" + ) + + fun digest(algorithm: String, bytes: ByteArray): ByteArray = MessageDigest.getInstance(algorithm).digest(bytes) + + fun createChecksum(file: File) { + algorithms.forEach { (algorithm, ext) -> + File(file.parentFile, "${file.name}.$ext").writeText( + digest(algorithm, file.readBytes()).joinToString(separator = "", postfix = "\n") { "%02x".format(it) } + ) + } + } + + fun attachSignature(jar: File, keyLocation: String) { + val privateKey = KeyFactory.getInstance("RSA").generatePrivate(PKCS8EncodedKeySpec(File(keyLocation).readBytes())) + val signer = Signature.getInstance("SHA512withRSA") + signer.initSign(privateKey) + ZipFile(jar).use { zip -> + zip.stream() + .sorted(Comparator.comparing { it.name }) + .filter { it.name != "META-INF/hmcl_signature" } + .forEach { + signer.update(digest("SHA-512", it.name.toByteArray())) + signer.update(digest("SHA-512", zip.getInputStream(it).readBytes())) + } + } + val signature = signer.sign() + FileSystems.newFileSystem(URI.create("jar:" + jar.toURI()), emptyMap()).use { zipfs -> + Files.newOutputStream(zipfs.getPath("META-INF/hmcl_signature")).use { it.write(signature) } + } + } + + fun resolveArtifactFile(jarFile: File, ext: String) = jarFile.resolveSibling(jarFile.nameWithoutExtension + '.' + ext) + } +} diff --git a/docs/Contributing.md b/docs/Contributing.md index 1a83efd4be1..5369097fbed 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -60,6 +60,26 @@ OpenJDK 64-Bit Server VM (build 25+37-LTS, mixed mode, sharing) ``` - You can manually download a specific version of the source code from the [GitHub Release page](https://github.com/HMCL-dev/HMCL/releases). +### Setup Gradle Environment + +To configure HMCL Gradle project, switch to the root directory of HMCL project and run the following command: + +```shell +./gradlew +``` + +It might take a while to download all the dependencies. + +HMCL supports Gradle configuration cache. +To enable this and speed up further building process, create file `gradle.properties` under the root directory of HMCL project, +and append this line to it: + +```properties +org.gradle.configuration-cache=true +``` + +Then re-configure the Gradle project. + ### Build HMCL To build HMCL, switch to the root directory of the HMCL project and run the following command: diff --git a/docs/Contributing_zh_Hans.md b/docs/Contributing_zh_Hans.md index e3c6f135fd2..ca3b7dd5c88 100644 --- a/docs/Contributing_zh_Hans.md +++ b/docs/Contributing_zh_Hans.md @@ -59,6 +59,24 @@ OpenJDK 64-Bit Server VM (build 25+37-LTS, mixed mode, sharing) ``` - 从 [GitHub Release 页面](https://github.com/HMCL-dev/HMCL/releases)可以手动下载特定版本的源码。 +### 搭建 Gradle 环境 + +想要配置 HMCL Gradle 项目,请切换到 HMCL 项目的根目录下,并执行以下命令: + +```shell +./gradlew +``` + +下载所有依赖项可能会花费一定时间。 + +HMCL 支持 Gradle 配置缓存。为启用配置缓存以加速后续构建,在 HMCL 项目根目录下创建 `gradle.properties` 文件,并添加以下内容: + +```properties +org.gradle.configuration-cache=true +``` + +然后重新配置 Gradle 项目。 + ### 构建 HMCL 想要构建 HMCL,请切换到 HMCL 项目的根目录下,并执行以下命令: diff --git a/docs/Contributing_zh_Hant.md b/docs/Contributing_zh_Hant.md index a04275a2f26..39511e6c372 100644 --- a/docs/Contributing_zh_Hant.md +++ b/docs/Contributing_zh_Hant.md @@ -59,6 +59,24 @@ OpenJDK 64-Bit Server VM (build 25+37-LTS, mixed mode, sharing) ``` - 從 [GitHub Release 頁面](https://github.com/HMCL-dev/HMCL/releases)可以手動下載特定版本的原始碼。 +### 搭建 Gradle 環境 + +想要配置 HMCL Gradle 專案,請切換到 HMCL 專案的根目錄下,並執行以下命令: + +```shell +./gradlew +``` + +下載所有依賴項可能會花費一定時間。 + +HMCL 支援 Gradle 配置快取。為啟用配置快取以加速後續構建,在 HMCL 專案根目錄下建立 `gradle.properties` 檔案,並新增以下內容: + +```properties +org.gradle.configuration-cache=true +``` + +然後重新配置 Gradle 專案。 + ### 構建 HMCL 想要構建 HMCL,請切換到 HMCL 專案的根目錄下,並執行以下指令: