Merge remote-tracking branch 'FabricMC/dev/0.7' into dev/0.7-forge

# Conflicts:
#	.github/workflows/test-push.yml
#	build.gradle
#	src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java
#	src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java
#	src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java
#	src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java
#	src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java
#	src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java
#	src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java
#	src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java
#	src/main/java/net/fabricmc/loom/task/RemapJarTask.java
#	src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java
#	src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java
#	src/test/groovy/net/fabricmc/loom/BuildUtils.groovy
This commit is contained in:
shedaniel
2021-04-04 19:30:51 +08:00
120 changed files with 3678 additions and 1018 deletions

97
.github/workflows/test-push.yml vendored Normal file
View File

@@ -0,0 +1,97 @@
name: Run Tests
on: [push, pull_request]
jobs:
build:
strategy:
fail-fast: false
matrix:
gradle: [4.9, 4.10.2, 6.8.3]
java: [jdk8, jdk11, jdk15]
exclude: # Dont run older gradle versions on newer java
- java: jdk15
gradle: 4.9
- java: jdk15
gradle: 4.10.2
- java: jdk11
gradle: 4.9
- java: jdk11
gradle: 4.10.2
runs-on: ubuntu-20.04
container:
image: gradle:${{ matrix.gradle }}-${{ matrix.java }}
options: --user root
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- run: gradle build check -x test --stacktrace
# This job is used to feed the test matrix of next job to allow the tests to run in parallel
prepare_test_matrix:
# Lets wait to ensure it builds before going running tests
needs: build
runs-on: ubuntu-20.04
container:
image: gradle:6.8.3-jdk15
options: --user root
steps:
- uses: actions/checkout@v2
- run: gradle writeActionsTestMatrix --stacktrace
-
id: set-matrix
run: echo "::set-output name=matrix::$(cat build/test_matrix.json)"
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
run_tests:
needs: prepare_test_matrix
strategy:
fail-fast: false
matrix:
java: [jdk8, jdk11, jdk15]
test: ${{ fromJson(needs.prepare_test_matrix.outputs.matrix) }}
runs-on: ubuntu-20.04
container:
image: gradle:6.8.3-${{ matrix.java }}
options: --user root
steps:
- uses: actions/checkout@v2
- run: gradle test --tests ${{ matrix.test }} --stacktrace
env:
TEST_WARNING_MODE: fail
- uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: ${{ matrix.test }} (${{ matrix.java }}) Results
path: build/reports/
# Special case this test to run across all os's
reproducible_build_test:
needs: build
strategy:
fail-fast: false
matrix:
java: [ 1.8, 11, 15 ]
os: [ windows-2019, ubuntu-20.04, macos-10.15 ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- run: ./gradlew test --tests *ReproducibleBuildTest --stacktrace
- uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: Reproducible Build ${{ matrix.os }} (${{ matrix.java }}) Results
path: build/reports/

3
.gitignore vendored
View File

@@ -19,4 +19,5 @@
!/README.md
!/settings.gradle
!/Jenkinsfile
!/checkstyle.xml
!/checkstyle.xml
!/codenarc.groovy

View File

@@ -8,6 +8,8 @@ plugins {
id 'eclipse'
id 'groovy'
id 'checkstyle'
id 'jacoco'
id 'codenarc'
id "org.cadixdev.licenser" version "0.5.0"
id 'com.github.johnrengelman.shadow' version '4.0.4'
}
@@ -17,7 +19,7 @@ targetCompatibility = 1.8
group = 'me.shedaniel'
archivesBaseName = project.name
def baseVersion = '0.6'
def baseVersion = '0.7'
def runNumber = System.getenv("GITHUB_RUN_NUMBER") ?: "9999"
def isSnapshot = System.getenv("PR_NUM") != null
@@ -53,6 +55,9 @@ repositories {
dependencies {
implementation gradleApi()
// Compile against groovy 3 to aid with gradle 7 support. Remove when updating to gradle 7
compileOnly 'org.codehaus.groovy:groovy-all:3.0.7'
// libraries
implementation ('commons-io:commons-io:2.8.0')
implementation ('org.zeroturnaround:zt-zip:1.14')
@@ -120,9 +125,9 @@ dependencies {
testImplementation('org.spockframework:spock-core:1.3-groovy-2.4') {
exclude module: 'groovy-all'
}
testImplementation 'io.javalin:javalin:3.13.4'
compileOnly 'org.jetbrains:annotations:20.1.0'
}
task forgeInjectJar(type: ShadowJar, dependsOn: [compileForgeInjectJava, processForgeInjectResources]) {
@@ -170,7 +175,9 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
license {
header rootProject.file("HEADER")
include "**/*.java"
include "**/*.groovy"
exclude '**/loom/util/DownloadUtil.java'
exclude '**/projects'
exclude '**/loom/util/FileSystemUtil.java'
exclude '**/loom/inject/mixin/MixinIntermediaryDevRemapper.java'
}
@@ -180,6 +187,11 @@ checkstyle {
toolVersion = '8.39'
}
codenarc {
toolVersion = "2.0.0"
configFile = file("codenarc.groovy")
}
gradlePlugin {
plugins {
fabricLoom {
@@ -191,6 +203,24 @@ gradlePlugin {
build.dependsOn mainJar
jacoco {
toolVersion = "0.8.6"
}
// Run to get test coverage.
jacocoTestReport {
dependsOn test
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/jacocoHtml")
}
}
test {
maxHeapSize = "4096m"
}
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
@@ -261,4 +291,30 @@ publishing {
}
}
}
}
// A task to output a json file with a list of all the test to run
task writeActionsTestMatrix() {
doLast {
def testMatrix = []
file('src/test/groovy/net/fabricmc/loom/test/intergration').eachFile {
if (it.name.endsWith("Test.groovy")) {
if (it.name.endsWith("ReproducibleBuildTest.groovy")) {
// This test gets a special case to run across all os's
return
}
def className = it.name.replace(".groovy", "")
testMatrix.add("net.fabricmc.loom.test.intergration.${className}")
}
}
// Run all the unit tests togeather
testMatrix.add("net.fabricmc.loom.test.unit.*")
def json = groovy.json.JsonOutput.toJson(testMatrix)
def output = file("build/test_matrix.json")
output.parentFile.mkdir()
output.text = json
}
}

66
codenarc.groovy Normal file
View File

@@ -0,0 +1,66 @@
ruleset {
UnnecessarySemicolon
BlockEndsWithBlankLine
BlockStartsWithBlankLine
ConsecutiveBlankLines
MissingBlankLineAfterImports
MissingBlankLineAfterPackage
// Braces
BracesForClass
BracesForForLoop
BracesForIfElse
BracesForMethod
BracesForTryCatchFinally
// Spaces
SpaceAfterCatch
SpaceAfterComma
SpaceAfterClosingBrace
SpaceAfterFor
SpaceAfterIf
SpaceAfterOpeningBrace
SpaceAfterSemicolon
SpaceAfterSwitch
SpaceAfterWhile
SpaceAroundClosureArrow
SpaceAroundMapEntryColon
SpaceAroundOperator
SpaceBeforeClosingBrace
SpaceBeforeOpeningBrace
TrailingWhitespace
// Groovyism - See: https://codenarc.org/codenarc-rules-groovyism.html
ClosureAsLastMethodParameter
ExplicitArrayListInstantiation
ExplicitCallToAndMethod
ExplicitCallToCompareToMethod
ExplicitCallToDivMethod
ExplicitCallToEqualsMethod
ExplicitCallToGetAtMethod
ExplicitCallToLeftShiftMethod
ExplicitCallToMinusMethod
ExplicitCallToMultiplyMethod
ExplicitCallToModMethod
ExplicitCallToOrMethod
ExplicitCallToPlusMethod
ExplicitCallToPowerMethod
ExplicitCallToRightShiftMethod
ExplicitCallToXorMethod
ExplicitHashMapInstantiation
ExplicitLinkedHashMapInstantiation
ExplicitHashSetInstantiation
ExplicitLinkedListInstantiation
ExplicitStackInstantiation
ExplicitTreeSetInstantiation
GetterMethodCouldBeProperty
GStringAsMapKey
GStringExpressionWithinString
CouldBeElvis
TernaryCouldBeElvis
FieldTypeRequired
MethodParameterTypeRequired
//Misc
LongLiteralWithLowerCaseL
}

View File

@@ -501,6 +501,10 @@ public class LoomGradleExtension {
return new File(getProjectPersistentCache(), "log4j.xml");
}
public File getUnpickLoggingConfigFile() {
return new File(getProjectPersistentCache(), "unpick-logging.properties");
}
public ConfigurableFileCollection getLog4jConfigs() {
return log4jConfigs;
}

View File

@@ -0,0 +1,95 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.build.nesting;
import java.io.File;
import java.util.Collection;
import java.util.zip.ZipEntry;
import com.google.common.base.Preconditions;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.gradle.api.logging.Logger;
import org.zeroturnaround.zip.FileSource;
import org.zeroturnaround.zip.ZipEntrySource;
import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.util.ModUtils;
public class JarNester {
public static void nestJars(Collection<File> jars, File modJar, Logger logger) {
if (jars.isEmpty()) {
logger.debug("Nothing to nest into " + modJar.getName());
return;
}
Preconditions.checkArgument(ModUtils.isMod(modJar), "Cannot nest jars into none mod jar " + modJar.getName());
ZipUtil.addEntries(modJar, jars.stream().map(file -> new FileSource("META-INF/jars/" + file.getName(), file)).toArray(ZipEntrySource[]::new));
boolean didNest = ZipUtil.transformEntries(modJar, single(new ZipEntryTransformerEntry("fabric.mod.json", new StringZipEntryTransformer() {
@Override
protected String transform(ZipEntry zipEntry, String input) {
JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class);
JsonArray nestedJars = json.getAsJsonArray("jars");
if (nestedJars == null || !json.has("jars")) {
nestedJars = new JsonArray();
}
for (File file : jars) {
String nestedJarPath = "META-INF/jars/" + file.getName();
for (JsonElement nestedJar : nestedJars) {
JsonObject jsonObject = nestedJar.getAsJsonObject();
if (jsonObject.has("file") && jsonObject.get("file").getAsString().equals(nestedJarPath)) {
throw new IllegalStateException("Cannot nest 2 jars at the same path: " + nestedJarPath);
}
}
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("file", nestedJarPath);
nestedJars.add(jsonObject);
logger.debug("Nested " + nestedJarPath + " into " + modJar.getName());
}
json.add("jars", nestedJars);
return LoomGradlePlugin.GSON.toJson(json);
}
})));
Preconditions.checkArgument(didNest, "Failed to nest jars into " + modJar.getName());
}
private static ZipEntryTransformerEntry[] single(ZipEntryTransformerEntry element) {
return new ZipEntryTransformerEntry[]{element};
}
}

View File

@@ -0,0 +1,46 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.build.nesting;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
public class MergedNestedJarProvider implements NestedJarProvider {
private final NestedJarProvider[] parents;
public MergedNestedJarProvider(NestedJarProvider... parents) {
this.parents = parents;
}
@Override
public Collection<File> provide() {
return Arrays.stream(parents)
.map(NestedJarProvider::provide)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}

View File

@@ -22,22 +22,18 @@
* SOFTWARE.
*/
package net.fabricmc.loom.build;
package net.fabricmc.loom.build.nesting;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
@@ -50,121 +46,30 @@ import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedConfiguration;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.zeroturnaround.zip.FileSource;
import org.zeroturnaround.zip.ZipEntrySource;
import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.task.RemapJarTask;
import net.fabricmc.loom.util.Constants;
public class NestedJars {
public static boolean addNestedJars(Project project, Path modJarPath) {
List<File> containedJars = getContainedJars(project);
public final class NestedDependencyProvider implements NestedJarProvider {
final Project project;
final List<DependencyInfo<?>> files;
if (containedJars.isEmpty()) {
return false;
}
File modJar = modJarPath.toFile();
ZipUtil.addOrReplaceEntries(modJar, containedJars.stream().map(file -> new FileSource("META-INF/jars/" + file.getName(), file)).toArray(ZipEntrySource[]::new));
return ZipUtil.transformEntries(modJar, single(new ZipEntryTransformerEntry("fabric.mod.json", new StringZipEntryTransformer() {
@Override
protected String transform(ZipEntry zipEntry, String input) {
JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class);
JsonArray nestedJars = json.getAsJsonArray("jars");
if (nestedJars == null || !json.has("jars")) {
nestedJars = new JsonArray();
}
for (File file : containedJars) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("file", "META-INF/jars/" + file.getName());
nestedJars.add(jsonObject);
}
json.add("jars", nestedJars);
return LoomGradlePlugin.GSON.toJson(json);
}
})));
private NestedDependencyProvider(Project project, List<DependencyInfo<?>> files) {
this.project = project;
this.files = files;
}
private static List<File> getContainedJars(Project project) {
List<File> fileList = new ArrayList<>();
public static NestedDependencyProvider createNestedDependencyProviderFromConfiguration(Project project, Configuration configuration) {
List<DependencyInfo<?>> fileList = new ArrayList<>();
Set<String> visited = new HashSet<>();
Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE);
ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration();
Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies();
fileList.addAll(populateProjectDependencies(configuration, visited));
fileList.addAll(populateResolvedDependencies(configuration, visited));
// Bit ugly doing this, id guess there is a better way but this works.
Set<String> projectDeps = new HashSet<>();
for (Dependency dependency : configuration.getDependencies()) {
if (dependency instanceof ProjectDependency) {
ProjectDependency projectDependency = (ProjectDependency) dependency;
Project dependencyProject = projectDependency.getDependencyProject();
projectDeps.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion());
// TODO change this to allow just normal jar tasks, so a project can have a none loom sub project
Collection<Task> remapJarTasks = dependencyProject.getTasksByName("remapJar", false);
Collection<Task> jarTasks = dependencyProject.getTasksByName("jar", false);
for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) {
if (task instanceof RemapJarTask) {
fileList.addAll(prepareForNesting(
Collections.singleton(((RemapJarTask) task).getArchivePath()),
projectDependency,
new ProjectDependencyMetaExtractor(),
project
));
} else if (task instanceof AbstractArchiveTask) {
fileList.addAll(prepareForNesting(
Collections.singleton(((AbstractArchiveTask) task).getArchivePath()),
projectDependency,
new ProjectDependencyMetaExtractor(),
project
));
}
}
}
}
for (ResolvedDependency dependency : dependencies) {
if (projectDeps.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) {
continue;
} else {
fileList.addAll(prepareForNesting(
dependency
.getModuleArtifacts()
.stream()
.map(ResolvedArtifact::getFile)
.collect(Collectors.toSet()),
dependency,
new ResolvedDependencyMetaExtractor(),
project
));
}
}
for (File file : fileList) {
if (!file.exists()) {
throw new RuntimeException("Failed to include nested jars, as it could not be found @ " + file.getAbsolutePath());
}
if (file.isDirectory() || !file.getName().endsWith(".jar")) {
throw new RuntimeException("Failed to include nested jars, as file was not a jar: " + file.getAbsolutePath());
}
}
return fileList;
return new NestedDependencyProvider(project, fileList);
}
// Looks for any deps that require a sub project to be built first
@@ -190,11 +95,69 @@ public class NestedJars {
return remapTasks;
}
//This is a good place to do pre-nesting operations, such as adding a fabric.mod.json to a library
private static <D> List<File> prepareForNesting(Set<File> files, D dependency, DependencyMetaExtractor<D> metaExtractor, Project project) {
private static List<DependencyInfo<ProjectDependency>> populateProjectDependencies(Configuration configuration, Set<String> visited) {
List<DependencyInfo<ProjectDependency>> fileList = new ArrayList<>();
for (Dependency dependency : configuration.getDependencies()) {
if (dependency instanceof ProjectDependency) {
ProjectDependency projectDependency = (ProjectDependency) dependency;
Project dependencyProject = projectDependency.getDependencyProject();
visited.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion());
// TODO change this to allow just normal jar tasks, so a project can have a none loom sub project
Collection<Task> remapJarTasks = dependencyProject.getTasksByName("remapJar", false);
Collection<Task> jarTasks = dependencyProject.getTasksByName("jar", false);
for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) {
if (task instanceof RemapJarTask) {
File file = ((RemapJarTask) task).getArchivePath();
fileList.add(new DependencyInfo<>(projectDependency, new ProjectDependencyMetaExtractor(), file));
} else if (task instanceof AbstractArchiveTask) {
File file = ((AbstractArchiveTask) task).getArchivePath();
fileList.add(new DependencyInfo<>(projectDependency, new ProjectDependencyMetaExtractor(), file));
}
}
}
}
return fileList;
}
private static List<DependencyInfo<ResolvedDependency>> populateResolvedDependencies(Configuration configuration, Set<String> visited) {
ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration();
Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies();
List<DependencyInfo<ResolvedDependency>> fileList = new ArrayList<>();
for (ResolvedDependency dependency : dependencies) {
if (visited.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) {
continue;
}
List<File> files = dependency
.getModuleArtifacts()
.stream()
.map(ResolvedArtifact::getFile)
.collect(Collectors.toList());
for (File file : files) {
fileList.add(new DependencyInfo<>(dependency, new ResolvedDependencyMetaExtractor(), file));
}
}
return fileList;
}
@Override
public List<File> provide() {
List<File> fileList = new ArrayList<>();
for (File file : files) {
for (DependencyInfo<?> metaFile : files) {
metaFile.validateInputs();
File file = metaFile.file;
//A lib that doesnt have a mod.json, we turn it into a fake mod
if (!ZipUtil.containsEntry(file, "fabric.mod.json")) {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
@@ -216,7 +179,7 @@ public class NestedJars {
throw new RuntimeException("Failed to copy file", e);
}
ZipUtil.addEntry(tempFile, "fabric.mod.json", getMod(dependency, metaExtractor).getBytes());
ZipUtil.addEntry(tempFile, "fabric.mod.json", generateModForDependency(metaFile).getBytes());
fileList.add(tempFile);
} else {
// Default copy the jar right in
@@ -228,7 +191,10 @@ public class NestedJars {
}
// Generates a barebones mod for a dependency
private static <D> String getMod(D dependency, DependencyMetaExtractor<D> metaExtractor) {
private static <D> String generateModForDependency(DependencyInfo<D> info) {
DependencyMetaExtractor<D> metaExtractor = info.metaExtractor;
D dependency = info.dependency;
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("schemaVersion", 1);
jsonObject.addProperty("id", (metaExtractor.group(dependency) + "_" + metaExtractor.name(dependency)).replaceAll("\\.", "_").toLowerCase(Locale.ENGLISH));
@@ -242,8 +208,26 @@ public class NestedJars {
return LoomGradlePlugin.GSON.toJson(jsonObject);
}
private static ZipEntryTransformerEntry[] single(ZipEntryTransformerEntry element) {
return new ZipEntryTransformerEntry[]{element};
private static class DependencyInfo<D> {
final D dependency;
final DependencyMetaExtractor<D> metaExtractor;
final File file;
DependencyInfo(D dependency, DependencyMetaExtractor<D> metaExtractor, File file) {
this.dependency = dependency;
this.metaExtractor = metaExtractor;
this.file = file;
}
public void validateInputs() {
if (!file.exists()) {
throw new RuntimeException("Failed to include nested jars, as it could not be found @ " + file.getAbsolutePath());
}
if (file.isDirectory() || !file.getName().endsWith(".jar")) {
throw new RuntimeException("Failed to include nested jars, as file was not a jar: " + file.getAbsolutePath());
}
}
}
private interface DependencyMetaExtractor<D> {

View File

@@ -0,0 +1,67 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.build.nesting;
import java.io.File;
import java.util.Collection;
import java.util.Set;
import com.google.common.base.Preconditions;
import org.gradle.api.Project;
import net.fabricmc.loom.util.ModUtils;
public final class NestedJarPathProvider implements NestedJarProvider {
private final Set<Object> nestedPaths;
private Set<File> files = null;
public NestedJarPathProvider(Set<Object> nestedPaths) {
this.nestedPaths = nestedPaths;
}
private Set<File> resolve(Project project) {
return project.files(nestedPaths).getFiles();
}
@Override
public void prepare(Project project) {
if (files == null) {
files = resolve(project);
}
}
@Override
public Collection<File> provide() {
validateFiles();
return files;
}
private void validateFiles() {
for (File file : files) {
Preconditions.checkArgument(file.getName().endsWith(".jar"), String.format("Tried to nest %s but it is not a jar", file.getAbsolutePath()));
Preconditions.checkArgument(file.exists(), String.format("Tried to nest jar %s but it does not exist", file.getAbsolutePath()));
Preconditions.checkArgument(ModUtils.isMod(file), String.format("Cannot use nest none mod jar %s", file.getAbsolutePath()));
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.build.nesting;
import java.io.File;
import java.util.Collection;
import org.gradle.api.Project;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public interface NestedJarProvider {
// provide all the files to be included, they should already be resolved but can be transformed here
Collection<File> provide();
// Setup the files ready to be provided
default void prepare(Project project) { }
}

View File

@@ -24,17 +24,8 @@
package net.fabricmc.loom.configuration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ImmutableMap;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
@@ -43,8 +34,6 @@ import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.javadoc.Javadoc;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.JarRemapper;
import net.fabricmc.loom.build.NestedJars;
import net.fabricmc.loom.build.mixin.JavaApInvoker;
import net.fabricmc.loom.build.mixin.KaptApInvoker;
import net.fabricmc.loom.build.mixin.ScalaApInvoker;
@@ -58,17 +47,8 @@ import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider;
import net.fabricmc.loom.configuration.providers.forge.PatchProvider;
import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
import net.fabricmc.loom.task.AbstractLoomTask;
import net.fabricmc.loom.task.GenVsCodeProjectTask;
import net.fabricmc.loom.task.RemapAllSourcesTask;
import net.fabricmc.loom.task.RemapJarTask;
import net.fabricmc.loom.task.RemapSourcesJarTask;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.SourceRemapper;
/**
* Add Minecraft dependencies to compile time.
*/
public final class CompileConfiguration {
private CompileConfiguration() {
}
@@ -121,9 +101,13 @@ public final class CompileConfiguration {
Configuration includeConfig = project.getConfigurations().maybeCreate(Constants.Configurations.INCLUDE);
includeConfig.setTransitive(false); // Dont get transitive deps
project.getConfigurations().maybeCreate(Constants.Configurations.MAPPING_CONSTANTS);
extendsFrom(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, Constants.Configurations.MAPPING_CONSTANTS, project);
project.getConfigurations().maybeCreate(Constants.Configurations.MAPPINGS);
project.getConfigurations().maybeCreate(Constants.Configurations.MAPPINGS_FINAL);
project.getConfigurations().maybeCreate(Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES);
project.getConfigurations().maybeCreate(Constants.Configurations.UNPICK_CLASSPATH);
for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) {
Configuration compileModsConfig = project.getConfigurations().maybeCreate(entry.getSourceConfiguration());
@@ -152,75 +136,18 @@ public final class CompileConfiguration {
extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, project);
}
/**
* Permit to add a Maven repository to a target project.
*
* @param target The target project
* @param name The name of the repository
* @param url The URL of the repository
* @return An object containing the name and the URL of the repository that can be modified later
*/
public static MavenArtifactRepository addMavenRepo(Project target, final String name, final String url) {
return addMavenRepo(target, name, url, repo -> {
});
}
public static MavenArtifactRepository addMavenRepo(Project target, final String name, final String url, final Action<MavenArtifactRepository> action) {
return target.getRepositories().maven(repo -> {
repo.setName(name);
repo.setUrl(url);
action.execute(repo);
});
}
public static void configureCompile(Project project) {
JavaPluginConvention javaModule = (JavaPluginConvention) project.getConvention().getPlugins().get("java");
public static void configureCompile(Project p) {
JavaPluginConvention javaModule = (JavaPluginConvention) p.getConvention().getPlugins().get("java");
SourceSet main = javaModule.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
Javadoc javadoc = (Javadoc) project.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME);
Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME);
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
project.afterEvaluate(project1 -> {
LoomGradleExtension extension = project1.getExtensions().getByType(LoomGradleExtension.class);
p.afterEvaluate(project -> {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
project1.getRepositories().flatDir(flatDirectoryArtifactRepository -> {
flatDirectoryArtifactRepository.dir(extension.getRootProjectBuildCache());
flatDirectoryArtifactRepository.setName("UserLocalCacheFiles");
});
project1.getRepositories().maven(mavenArtifactRepository -> {
mavenArtifactRepository.setUrl(extension.getRemappedModCache());
mavenArtifactRepository.setName("UserLocalRemappedMods");
});
project1.getRepositories().maven(mavenArtifactRepository -> {
mavenArtifactRepository.setName("Fabric");
mavenArtifactRepository.setUrl("https://maven.fabricmc.net/");
});
project1.getRepositories().maven(mavenArtifactRepository -> {
mavenArtifactRepository.setName("Mojang");
mavenArtifactRepository.setUrl("https://libraries.minecraft.net/");
});
project1.getRepositories().maven(mavenArtifactRepository -> {
mavenArtifactRepository.setName("Forge");
mavenArtifactRepository.setUrl("https://files.minecraftforge.net/maven/");
mavenArtifactRepository.metadataSources(sources -> {
sources.mavenPom();
try {
MavenArtifactRepository.MetadataSources.class.getDeclaredMethod("ignoreGradleMetadataRedirection")
.invoke(sources);
} catch (Throwable ignored) {
// Method not available
}
});
});
project1.getRepositories().mavenCentral();
MavenConfiguration.setup(project);
LoomDependencyManager dependencyManager = new LoomDependencyManager();
extension.setDependencyManager(dependencyManager);
@@ -245,118 +172,20 @@ public final class CompileConfiguration {
dependencyManager.addProvider(new MappingsProvider(project));
dependencyManager.addProvider(new LaunchProvider(project));
dependencyManager.handleDependencies(project1);
dependencyManager.handleDependencies(project);
project1.getTasks().getByName("idea").finalizedBy(project1.getTasks().getByName("genIdeaWorkspace"));
project1.getTasks().getByName("eclipse").finalizedBy(project1.getTasks().getByName("genEclipseRuns"));
project1.getTasks().getByName("cleanEclipse").finalizedBy(project1.getTasks().getByName("cleanEclipseRuns"));
project.getTasks().getByName("idea").finalizedBy(project.getTasks().getByName("genIdeaWorkspace"));
project.getTasks().getByName("eclipse").finalizedBy(project.getTasks().getByName("genEclipseRuns"));
project.getTasks().getByName("cleanEclipse").finalizedBy(project.getTasks().getByName("cleanEclipseRuns"));
SetupIntelijRunConfigs.setup(project1);
SetupIntelijRunConfigs.setup(project);
GenVsCodeProjectTask.generate(project1);
// Enables the default mod remapper
if (extension.remapMod) {
AbstractArchiveTask jarTask = (AbstractArchiveTask) project1.getTasks().getByName("jar");
RemapJarTask remapJarTask = (RemapJarTask) project1.getTasks().findByName("remapJar");
assert remapJarTask != null;
if (!remapJarTask.getInput().isPresent()) {
jarTask.setClassifier("dev");
remapJarTask.setClassifier("");
remapJarTask.getInput().set(jarTask.getArchivePath());
}
if (extension.isForge()) {
remapJarTask.getToM().set("srg");
((Jar) jarTask).manifest(manifest -> {
List<String> configs = new ArrayList<>();
if (extension.mixinConfig != null) {
configs.add(extension.mixinConfig);
}
if (extension.mixinConfigs != null) {
configs.addAll(extension.mixinConfigs);
}
manifest.attributes(ImmutableMap.of("MixinConfigs", String.join(",", configs)));
});
}
extension.getUnmappedModCollection().from(jarTask);
remapJarTask.getAddNestedDependencies().set(true);
remapJarTask.getRemapAccessWidener().set(true);
project1.getArtifacts().add("archives", remapJarTask);
remapJarTask.dependsOn(jarTask);
project1.getTasks().getByName("build").dependsOn(remapJarTask);
project.getTasks().withType(RemapJarTask.class).forEach(task -> {
if (task.getAddNestedDependencies().getOrElse(false)) {
NestedJars.getRequiredTasks(project1).forEach(task::dependsOn);
}
});
SourceRemapper remapper = null;
Task parentTask = project1.getTasks().getByName("build");
if (extension.isShareCaches()) {
Project rootProject = project.getRootProject();
if (extension.isRootProject()) {
SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false);
JarRemapper jarRemapper = new JarRemapper();
remapJarTask.jarRemapper = jarRemapper;
rootProject.getTasks().register("remapAllSources", RemapAllSourcesTask.class, task -> {
task.sourceRemapper = sourceRemapper;
task.doLast(t -> sourceRemapper.remapAll());
});
parentTask = rootProject.getTasks().getByName("remapAllSources");
rootProject.getTasks().register("remapAllJars", AbstractLoomTask.class, task -> {
task.doLast(t -> {
try {
jarRemapper.remap(rootProject);
} catch (IOException e) {
throw new RuntimeException("Failed to remap jars", e);
}
});
});
} else {
parentTask = rootProject.getTasks().getByName("remapAllSources");
remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper;
remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName("remapJar")).jarRemapper;
project1.getTasks().getByName("build").dependsOn(parentTask);
project1.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName("remapAllJars"));
rootProject.getTasks().getByName("remapAllJars").dependsOn(project1.getTasks().getByName("remapJar"));
}
}
try {
AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project1.getTasks().getByName("sourcesJar");
RemapSourcesJarTask remapSourcesJarTask = (RemapSourcesJarTask) project1.getTasks().findByName("remapSourcesJar");
remapSourcesJarTask.setInput(sourcesTask.getArchivePath());
remapSourcesJarTask.setOutput(sourcesTask.getArchivePath());
remapSourcesJarTask.doLast(task -> project1.getArtifacts().add("archives", remapSourcesJarTask.getOutput()));
remapSourcesJarTask.dependsOn(project1.getTasks().getByName("sourcesJar"));
if (extension.isShareCaches()) {
remapSourcesJarTask.setSourceRemapper(remapper);
}
parentTask.dependsOn(remapSourcesJarTask);
} catch (UnknownTaskException ignored) {
// pass
}
RemapConfiguration.setupDefaultRemap(project);
} else {
AbstractArchiveTask jarTask = (AbstractArchiveTask) project1.getTasks().getByName("jar");
AbstractArchiveTask jarTask = (AbstractArchiveTask) project.getTasks().getByName("jar");
extension.getUnmappedModCollection().from(jarTask);
}
@@ -379,7 +208,7 @@ public final class CompileConfiguration {
}
});
if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
if (p.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
// If loom is applied after kapt, then kapt will use the AP arguments too early for loom to pass the arguments we need for mixin.
throw new IllegalArgumentException("fabric-loom must be applied BEFORE kapt in the plugins { } block.");
}

View File

@@ -0,0 +1,57 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration;
import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
public class MavenConfiguration {
public static void setup(Project project) {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
project.getRepositories().flatDir(repo -> {
repo.setName("UserLocalCacheFiles");
repo.dir(extension.getRootProjectBuildCache());
});
project.getRepositories().maven(repo -> {
repo.setName("UserLocalRemappedMods");
repo.setUrl(extension.getRemappedModCache());
});
project.getRepositories().maven(repo -> {
repo.setName("Fabric");
repo.setUrl("https://maven.fabricmc.net/");
});
project.getRepositories().maven(repo -> {
repo.setName("Mojang");
repo.setUrl("https://libraries.minecraft.net/");
});
project.getRepositories().mavenCentral();
}
}

View File

@@ -0,0 +1,159 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration;
import java.io.IOException;
import com.google.common.base.Preconditions;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.JarRemapper;
import net.fabricmc.loom.build.nesting.NestedDependencyProvider;
import net.fabricmc.loom.task.AbstractLoomTask;
import net.fabricmc.loom.task.RemapAllSourcesTask;
import net.fabricmc.loom.task.RemapJarTask;
import net.fabricmc.loom.task.RemapSourcesJarTask;
import net.fabricmc.loom.util.SourceRemapper;
public class RemapConfiguration {
private static final String DEFAULT_JAR_TASK_NAME = JavaPlugin.JAR_TASK_NAME;
private static final String DEFAULT_SOURCES_JAR_TASK_NAME = "sourcesJar";
private static final String DEFAULT_REMAP_JAR_TASK_NAME = "remapJar";
private static final String DEFAULT_REMAP_SOURCES_JAR_TASK_NAME = "remapSourcesJar";
private static final String DEFAULT_REMAP_ALL_JARS_TASK_NAME = "remapAllJars";
private static final String DEFAULT_REMAP_ALL_SOURCES_TASK_NAME = "remapAllSources";
public static void setupDefaultRemap(Project project) {
setupRemap(project, true, DEFAULT_JAR_TASK_NAME, DEFAULT_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_JAR_TASK_NAME, DEFAULT_REMAP_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_ALL_JARS_TASK_NAME, DEFAULT_REMAP_ALL_SOURCES_TASK_NAME);
}
@ApiStatus.Experimental // This is only an api if you squint really hard, expect it to explode every 5 mins. If you must call in afterEvaluate on all projects
public static void setupRemap(Project project, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) {
setupRemap(project, false, jarTaskName, sourcesJarTaskName, remapJarTaskName, remapSourcesJarTaskName, remapAllJarsTaskName, remapAllSourcesTaskName);
}
// isDefaultRemap is set to true for the standard remap task, some defaults are left out when this is false.
private static void setupRemap(Project project, boolean isDefaultRemap, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
AbstractArchiveTask jarTask = (AbstractArchiveTask) project.getTasks().getByName(jarTaskName);
RemapJarTask remapJarTask = (RemapJarTask) project.getTasks().findByName(remapJarTaskName);
assert remapJarTask != null;
if (!remapJarTask.getInput().isPresent() && isDefaultRemap) {
jarTask.setClassifier("dev");
remapJarTask.setClassifier("");
remapJarTask.getInput().set(jarTask.getArchivePath());
}
if (isDefaultRemap) {
extension.getUnmappedModCollection().from(jarTask);
remapJarTask.getAddNestedDependencies().set(true);
remapJarTask.getRemapAccessWidener().set(true);
project.getArtifacts().add("archives", remapJarTask);
}
remapJarTask.dependsOn(jarTask);
project.getTasks().getByName("build").dependsOn(remapJarTask);
// TODO this might be wrong?
project.getTasks().withType(RemapJarTask.class).forEach(task -> {
if (task.getAddNestedDependencies().getOrElse(false)) {
NestedDependencyProvider.getRequiredTasks(project).forEach(task::dependsOn);
}
});
SourceRemapper remapper = null;
// TODO what is this for?
Task parentTask = project.getTasks().getByName("build");
if (extension.isShareCaches()) {
Project rootProject = project.getRootProject();
if (extension.isRootProject()) {
SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false);
JarRemapper jarRemapper = new JarRemapper();
remapJarTask.jarRemapper = jarRemapper;
rootProject.getTasks().register(remapAllSourcesTaskName, RemapAllSourcesTask.class, task -> {
task.sourceRemapper = sourceRemapper;
task.doLast(t -> sourceRemapper.remapAll());
});
parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName);
rootProject.getTasks().register(remapAllJarsTaskName, AbstractLoomTask.class, task -> {
task.doLast(t -> {
try {
jarRemapper.remap();
} catch (IOException e) {
throw new RuntimeException("Failed to remap jars", e);
}
});
});
} else {
parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName);
remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper;
Preconditions.checkNotNull(remapper);
remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName(remapJarTaskName)).jarRemapper;
project.getTasks().getByName("build").dependsOn(parentTask);
project.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName(remapAllJarsTaskName));
rootProject.getTasks().getByName(remapAllJarsTaskName).dependsOn(project.getTasks().getByName(remapJarTaskName));
}
}
try {
AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project.getTasks().getByName(sourcesJarTaskName);
RemapSourcesJarTask remapSourcesJarTask = (RemapSourcesJarTask) project.getTasks().findByName(remapSourcesJarTaskName);
Preconditions.checkNotNull(remapSourcesJarTask, "Could not find " + remapSourcesJarTaskName + " in " + project.getName());
remapSourcesJarTask.setInput(sourcesTask.getArchivePath());
remapSourcesJarTask.setOutput(sourcesTask.getArchivePath());
remapSourcesJarTask.dependsOn(project.getTasks().getByName(sourcesJarTaskName));
if (isDefaultRemap) {
remapSourcesJarTask.doLast(task -> project.getArtifacts().add("archives", remapSourcesJarTask.getOutput()));
}
if (extension.isShareCaches()) {
remapSourcesJarTask.setSourceRemapper(remapper);
}
parentTask.dependsOn(remapSourcesJarTask);
} catch (UnknownTaskException ignored) {
// pass
}
}
}

View File

@@ -69,6 +69,7 @@ public class RunConfig {
public String programArgs;
public List<String> vscodeBeforeRun = new ArrayList<>();
public final Map<String, String> envVariables = new HashMap<>();
public SourceSet sourceSet;
public Element genRuns(Element doc) {
Element root = this.addXml(doc, "component", ImmutableMap.of("name", "ProjectRunConfigurationManager"));
@@ -235,6 +236,7 @@ public class RunConfig {
runConfig.ideaModuleName = getIdeaModuleName(project, sourceSet);
runConfig.runDirIdeaUrl = "file://$PROJECT_DIR$/" + runDir;
runConfig.runDir = runDir;
runConfig.sourceSet = sourceSet;
// Custom parameters
for (String progArg : settings.getProgramArgs()) {

View File

@@ -110,7 +110,8 @@ public class MinecraftProvider extends DependencyProvider {
try {
mergeJars(getProject().getLogger());
} catch (ZipError e) {
deleteFiles();
HashedDownloadUtil.delete(minecraftClientJar);
HashedDownloadUtil.delete(minecraftServerJar);
getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e);
throw new RuntimeException();

View File

@@ -28,6 +28,7 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
@@ -41,6 +42,7 @@ import java.util.function.Consumer;
import com.google.common.base.Preconditions;
import com.google.common.net.UrlEscapers;
import com.google.gson.JsonObject;
import org.apache.commons.io.FileUtils;
import org.apache.tools.ant.util.StringUtils;
import org.gradle.api.Project;
@@ -50,6 +52,7 @@ import org.zeroturnaround.zip.ZipEntrySource;
import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.configuration.DependencyProvider;
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
@@ -95,6 +98,10 @@ public class MappingsProvider extends DependencyProvider {
public File mixinTinyMappingsWithSrg; // FORGE: The mixin mappings have srg names in intermediary.
public File srgToNamedSrg; // FORGE: srg to named in srg file format
private File unpickDefinitionsFile;
private boolean hasUnpickDefinitions;
private UnpickMetadata unpickMetadata;
public MappingsProvider(Project project) {
super(project);
mappingsDir = getExtension().getUserCache().toPath().resolve("mappings");
@@ -173,6 +180,7 @@ public class MappingsProvider extends DependencyProvider {
}
tinyMappings = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + ".tiny").toFile();
unpickDefinitionsFile = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + ".unpick").toFile();
tinyMappingsJar = new File(getExtension().getUserCache(), mappingsJar.getName().replace(".jar", "-" + jarClassifier + ".jar"));
tinyMappingsWithSrg = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + "-srg.tiny");
mixinTinyMappingsWithSrg = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + "-mixin-srg.tiny").toFile();
@@ -180,12 +188,27 @@ public class MappingsProvider extends DependencyProvider {
if (!tinyMappings.exists() || isRefreshDeps()) {
storeMappings(getProject(), minecraftProvider, mappingsJar.toPath(), postPopulationScheduler);
} else {
try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) {
extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath());
}
}
if (!tinyMappingsJar.exists() || isRefreshDeps()) {
ZipUtil.pack(new ZipEntrySource[] {new FileSource("mappings/mappings.tiny", tinyMappings)}, tinyMappingsJar);
}
if (hasUnpickDefinitions()) {
String notation = String.format("%s:%s:%s:constants",
dependency.getDependency().getGroup(),
dependency.getDependency().getName(),
dependency.getDependency().getVersion()
);
getProject().getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation);
populateUnpickClasspath();
}
if (getExtension().shouldGenerateSrgTiny()) {
if (Files.notExists(tinyMappingsWithSrg) || isRefreshDeps()) {
SrgMerger.mergeSrg(getExtension().getSrgProvider().getSrg().toPath(), tinyMappings.toPath(), tinyMappingsWithSrg, true);
@@ -248,6 +271,7 @@ public class MappingsProvider extends DependencyProvider {
try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) {
extractMappings(fileSystem, baseTinyMappings);
extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath());
}
if (baseMappingsAreV2()) {
@@ -317,6 +341,40 @@ public class MappingsProvider extends DependencyProvider {
Files.copy(jar.getPath("mappings/mappings.tiny"), extractTo, StandardCopyOption.REPLACE_EXISTING);
}
private void extractUnpickDefinitions(FileSystem jar, Path extractTo) throws IOException {
Path unpickPath = jar.getPath("extras/definitions.unpick");
Path unpickMetadataPath = jar.getPath("extras/unpick.json");
if (!Files.exists(unpickPath) || !Files.exists(unpickMetadataPath)) {
return;
}
Files.copy(unpickPath, extractTo, StandardCopyOption.REPLACE_EXISTING);
unpickMetadata = parseUnpickMetadata(unpickMetadataPath);
hasUnpickDefinitions = true;
}
private UnpickMetadata parseUnpickMetadata(Path input) throws IOException {
JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(Files.readAllBytes(input), StandardCharsets.UTF_8), JsonObject.class);
if (!jsonObject.has("version") || jsonObject.get("version").getAsInt() != 1) {
throw new UnsupportedOperationException("Unsupported unpick version");
}
return new UnpickMetadata(
jsonObject.get("unpickGroup").getAsString(),
jsonObject.get("unpickVersion").getAsString()
);
}
private void populateUnpickClasspath() {
String unpickCliName = "unpick-cli";
getProject().getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH,
String.format("%s:%s:%s", unpickMetadata.unpickGroup, unpickCliName, unpickMetadata.unpickVersion)
);
}
private void extractIntermediary(Path intermediaryJar, Path intermediaryTiny) throws IOException {
getProject().getLogger().info(":extracting " + intermediaryJar.getFileName());
@@ -439,4 +497,22 @@ public class MappingsProvider extends DependencyProvider {
public String getMappingsKey() {
return mappingsName + "." + minecraftVersion.replace(' ', '_').replace('.', '_').replace('-', '_') + "." + mappingsVersion;
}
public File getUnpickDefinitionsFile() {
return unpickDefinitionsFile;
}
public boolean hasUnpickDefinitions() {
return hasUnpickDefinitions;
}
public static class UnpickMetadata {
public final String unpickGroup;
public final String unpickVersion;
public UnpickMetadata(String unpickGroup, String unpickVersion) {
this.unpickGroup = unpickGroup;
this.unpickVersion = unpickVersion;
}
}
}

View File

@@ -66,7 +66,7 @@ import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
import net.fabricmc.loom.util.DownloadUtil;
import net.fabricmc.loom.util.HashedDownloadUtil;
import net.fabricmc.lorenztiny.TinyMappingsReader;
import net.fabricmc.mapping.tree.TinyMappingFactory;
@@ -178,11 +178,11 @@ public class MojangMappingsDependency extends AbstractModuleDependency implement
throw new RuntimeException("Failed to find official mojang mappings for " + getVersion());
}
String clientMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl();
String serverMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl();
MinecraftVersionMeta.Download clientMappingsDownload = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS);
MinecraftVersionMeta.Download serverMappingsDownload = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS);
DownloadUtil.downloadIfChanged(new URL(clientMappingsUrl), clientMappings.toFile(), project.getLogger());
DownloadUtil.downloadIfChanged(new URL(serverMappingsUrl), serverMappings.toFile(), project.getLogger());
HashedDownloadUtil.downloadIfInvalid(new URL(clientMappingsDownload.getUrl()), clientMappings.toFile(), clientMappingsDownload.getSha1(), project.getLogger(), false);
HashedDownloadUtil.downloadIfInvalid(new URL(serverMappingsDownload.getUrl()), serverMappings.toFile(), clientMappingsDownload.getSha1(), project.getLogger(), false);
MappingSet mappings = MappingSet.create();

View File

@@ -149,6 +149,8 @@ public class MinecraftMappedProvider extends DependencyProvider {
getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")");
Files.deleteIfExists(output);
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) {
if (getExtension().isForge()) {
outputConsumer.addNonClassFiles(input, NonClassCopyMode.FIX_META_INF, remapper);
@@ -279,6 +281,10 @@ public class MinecraftMappedProvider extends DependencyProvider {
return minecraftMappedJar;
}
public File getUnpickedJar() {
return new File(getJarDirectory(getExtension().getUserCache(), "mapped"), "minecraft-" + getJarVersionString("unpicked") + ".jar");
}
@Override
public String getTargetConfig() {
return Constants.Configurations.MINECRAFT_NAMED;

View File

@@ -24,6 +24,9 @@
package net.fabricmc.loom.decompilers.fernflower;
import java.net.URL;
import java.net.URLClassLoader;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ConfigurationContainer;
@@ -40,15 +43,29 @@ import org.gradle.process.JavaExecSpec;
*/
public class ForkingJavaExec {
public static ExecResult javaexec(Project project, Action<? super JavaExecSpec> action) {
ConfigurationContainer configurations = project.getBuildscript().getConfigurations();
DependencyHandler handler = project.getDependencies();
FileCollection classpath = project.getBuildscript().getConfigurations().getByName("classpath")
.plus(project.getRootProject().getBuildscript().getConfigurations().getByName("classpath"))
.plus(configurations.detachedConfiguration(handler.localGroovy()));
return project.javaexec(spec -> {
spec.classpath(classpath);
spec.classpath(getClasspath(project));
action.execute(spec);
});
}
private static Object getClasspath(Project project) {
if (System.getProperty("fabric.loom.test") != null) {
return getTestClasspath();
}
return getRuntimeClasspath(project.getRootProject().getPlugins().hasPlugin("fabric-loom") ? project.getRootProject() : project);
}
private static FileCollection getRuntimeClasspath(Project project) {
ConfigurationContainer configurations = project.getBuildscript().getConfigurations();
DependencyHandler handler = project.getDependencies();
return configurations.getByName("classpath")
.plus(project.getRootProject().getBuildscript().getConfigurations().getByName("classpath"))
.plus(configurations.detachedConfiguration(handler.localGroovy()));
}
private static URL[] getTestClasspath() {
return ((URLClassLoader) ForkingJavaExec.class.getClassLoader()).getURLs();
}
}

View File

@@ -36,6 +36,8 @@ import java.util.Map;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructField;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.StructRecordComponent;
import org.objectweb.asm.Opcodes;
import net.fabricmc.fernflower.api.IFabricJavadocProvider;
import net.fabricmc.mapping.tree.ClassDef;
@@ -73,11 +75,63 @@ public class TinyJavadocProvider implements IFabricJavadocProvider {
@Override
public String getClassDoc(StructClass structClass) {
ClassDef classDef = classes.get(structClass.qualifiedName);
return classDef != null ? classDef.getComment() : null;
if (classDef == null) {
return null;
}
if (!isRecord(structClass)) {
return classDef.getComment();
}
/**
* Handle the record component docs here.
*
* Record components are mapped via the field name, thus take the docs from the fields and display them on then class.
*/
List<String> parts = new ArrayList<>();
if (classDef.getComment() != null) {
parts.add(classDef.getComment());
}
boolean addedParam = false;
for (StructRecordComponent component : structClass.getRecordComponents()) {
// The component will always match the field name and descriptor
FieldDef fieldDef = fields.get(new EntryTriple(structClass.qualifiedName, component.getName(), component.getDescriptor()));
if (fieldDef == null) {
continue;
}
String comment = fieldDef.getComment();
if (comment != null) {
if (!addedParam && classDef.getComment() != null) {
//Add a blank line before components when the class has a comment
parts.add("");
addedParam = true;
}
parts.add(String.format("@param %s %s", fieldDef.getName(namespace), comment));
}
}
if (parts.isEmpty()) {
return null;
}
return String.join("\n", parts);
}
@Override
public String getFieldDoc(StructClass structClass, StructField structField) {
// None static fields in records are handled in the class javadoc.
if (isRecord(structClass) && !isStatic(structField)) {
return null;
}
FieldDef fieldDef = fields.get(new EntryTriple(structClass.qualifiedName, structField.getName(), structField.getDescriptor()));
return fieldDef != null ? fieldDef.getComment() : null;
}
@@ -126,4 +180,12 @@ public class TinyJavadocProvider implements IFabricJavadocProvider {
throw new RuntimeException("Failed to read mappings", e);
}
}
public static boolean isRecord(StructClass structClass) {
return (structClass.getAccessFlags() & Opcodes.ACC_RECORD) != 0;
}
public static boolean isStatic(StructField structField) {
return (structField.getAccessFlags() & Opcodes.ACC_STATIC) != 0;
}
}

View File

@@ -32,31 +32,23 @@ import java.util.List;
import java.util.function.Function;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.JavaExec;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig;
public abstract class AbstractRunTask extends JavaExec {
private final Function<Project, RunConfig> configProvider;
private RunConfig config;
private final RunConfig config;
public AbstractRunTask(Function<Project, RunConfig> config) {
public AbstractRunTask(Function<Project, RunConfig> configProvider) {
super();
setGroup("fabric");
this.configProvider = config;
this.config = configProvider.apply(getProject());
setClasspath(getProject().getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));
classpath(this.getProject().getExtensions().getByType(LoomGradleExtension.class).getUnmappedModCollection());
setClasspath(config.sourceSet.getRuntimeClasspath());
}
@Override
public void exec() {
if (config == null) {
config = configProvider.apply(getProject());
}
List<String> argsSplit = new ArrayList<>();
String[] args = config.programArgs.split(" ");
int partPos = -1;
@@ -94,10 +86,6 @@ public abstract class AbstractRunTask extends JavaExec {
@Override
public void setWorkingDir(File dir) {
if (config == null) {
config = configProvider.apply(getProject());
}
if (!dir.exists()) {
dir.mkdirs();
}
@@ -107,19 +95,11 @@ public abstract class AbstractRunTask extends JavaExec {
@Override
public String getMain() {
if (config == null) {
config = configProvider.apply(getProject());
}
return config.mainClass;
}
@Override
public List<String> getJvmArgs() {
if (config == null) {
config = configProvider.apply(getProject());
}
List<String> superArgs = super.getJvmArgs();
List<String> args = new ArrayList<>(superArgs != null ? superArgs : Collections.emptyList());
args.addAll(Arrays.asList(config.vmArgs.split(" ")));

View File

@@ -35,6 +35,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.LoomGradleExtension;
@@ -49,6 +50,8 @@ import net.fabricmc.stitch.util.StitchUtil;
public class GenerateSourcesTask extends AbstractLoomTask {
public final LoomDecompiler decompiler;
private File inputJar;
@Inject
public GenerateSourcesTask(LoomDecompiler decompiler) {
this.decompiler = decompiler;
@@ -65,26 +68,18 @@ public class GenerateSourcesTask extends AbstractLoomTask {
.stream().map(File::toPath).collect(Collectors.toSet());
DecompilationMetadata metadata = new DecompilationMetadata(threads, javaDocs, libraries);
Path compiledJar = getExtension().getMappingsProvider().mappedProvider.getMappedJar().toPath();
Path runtimeJar = getExtension().getMappingsProvider().mappedProvider.getMappedJar().toPath();
Path sourcesDestination = getMappedJarFileWithSuffix("-sources.jar").toPath();
Path linemap = getMappedJarFileWithSuffix("-sources.lmap").toPath();
decompiler.decompile(compiledJar, sourcesDestination, linemap, metadata);
decompiler.decompile(inputJar.toPath(), sourcesDestination, linemap, metadata);
if (Files.exists(linemap)) {
Path linemappedJarDestination = getMappedJarFileWithSuffix("-linemapped.jar").toPath();
remapLineNumbers(compiledJar, linemap, linemappedJarDestination);
// Line map the actually jar used to run the game, not the one used to decompile
remapLineNumbers(runtimeJar, linemap, linemappedJarDestination);
// In order for IDEs to recognize the new line mappings, we need to overwrite the existing compiled jar
// with the linemapped one. In the name of not destroying the existing jar, we will copy it to somewhere else.
Path unlinemappedJar = getMappedJarFileWithSuffix("-unlinemapped.jar").toPath();
// The second time genSources is ran, we want to keep the existing unlinemapped jar.
if (!Files.exists(unlinemappedJar)) {
Files.copy(compiledJar, unlinemappedJar);
}
Files.copy(linemappedJarDestination, compiledJar, StandardCopyOption.REPLACE_EXISTING);
Files.copy(linemappedJarDestination, runtimeJar, StandardCopyOption.REPLACE_EXISTING);
Files.delete(linemappedJarDestination);
}
}
@@ -117,4 +112,14 @@ public class GenerateSourcesTask extends AbstractLoomTask {
return new File(path.substring(0, path.length() - 4) + suffix);
}
@InputFile
public File getInputJar() {
return inputJar;
}
public GenerateSourcesTask setInputJar(File inputJar) {
this.inputJar = inputJar;
return this;
}
}

View File

@@ -24,6 +24,8 @@
package net.fabricmc.loom.task;
import java.io.File;
import com.google.common.base.Preconditions;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskContainer;
@@ -31,6 +33,7 @@ import org.gradle.api.tasks.TaskContainer;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler;
public final class LoomTasks {
@@ -110,10 +113,30 @@ public final class LoomTasks {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
project.afterEvaluate(p -> {
MappingsProvider mappingsProvider = extension.getMappingsProvider();
File inputJar = mappingsProvider.mappedProvider.getMappedJar();
if (mappingsProvider.hasUnpickDefinitions()) {
File outputJar = mappingsProvider.mappedProvider.getUnpickedJar();
tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> {
unpickJarTask.setUnpickDefinition(mappingsProvider.getUnpickDefinitionsFile());
unpickJarTask.setInputJar(mappingsProvider.mappedProvider.getMappedJar());
unpickJarTask.setOutputJar(outputJar);
});
inputJar = outputJar;
}
for (LoomDecompiler decompiler : extension.getDecompilers()) {
String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name();
// decompiler will be passed to the constructor of GenerateSourcesTask
tasks.register(taskName, GenerateSourcesTask.class, decompiler);
GenerateSourcesTask generateSourcesTask = tasks.register(taskName, GenerateSourcesTask.class, decompiler).get();
generateSourcesTask.setInputJar(inputJar);
if (mappingsProvider.hasUnpickDefinitions()) {
generateSourcesTask.dependsOn(tasks.getByName("unpickJar"));
}
}
});
}

View File

@@ -36,7 +36,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -53,6 +56,7 @@ import me.shedaniel.architectury.refmapremapper.remapper.Remapper;
import me.shedaniel.architectury.refmapremapper.remapper.SimpleReferenceRemapper;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
@@ -60,19 +64,25 @@ import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.jvm.tasks.Jar;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.JarRemapper;
import net.fabricmc.loom.build.MixinRefmapHelper;
import net.fabricmc.loom.build.NestedJars;
import net.fabricmc.loom.build.nesting.NestedJarPathProvider;
import net.fabricmc.loom.build.nesting.JarNester;
import net.fabricmc.loom.build.nesting.MergedNestedJarProvider;
import net.fabricmc.loom.build.nesting.NestedDependencyProvider;
import net.fabricmc.loom.build.nesting.NestedJarProvider;
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.LoggerFilter;
import net.fabricmc.loom.util.TinyRemapperMappingsHelper;
import net.fabricmc.loom.util.ZipReprocessorUtil;
import net.fabricmc.loom.util.gradle.GradleSupport;
import net.fabricmc.loom.util.ZipReprocessorUtil;
import net.fabricmc.mapping.tree.ClassDef;
import net.fabricmc.mapping.tree.FieldDef;
import net.fabricmc.mapping.tree.MethodDef;
@@ -86,17 +96,20 @@ import net.fabricmc.tinyremapper.TinyUtils;
public class RemapJarTask extends Jar {
private final RegularFileProperty input;
private final Property<Boolean> addNestedDependencies;
private final Property<Boolean> addDefaultNestedDependencies;
private final Property<Boolean> remapAccessWidener;
private final List<Action<TinyRemapper.Builder>> remapOptions = new ArrayList<>();
private final Property<String> fromM;
private final Property<String> toM;
public JarRemapper jarRemapper;
private FileCollection classpath;
private final Set<Object> nestedPaths = new LinkedHashSet<>();
public RemapJarTask() {
super();
input = GradleSupport.getfileProperty(getProject());
addNestedDependencies = getProject().getObjects().property(Boolean.class);
addDefaultNestedDependencies = getProject().getObjects().property(Boolean.class);
remapAccessWidener = getProject().getObjects().property(Boolean.class);
fromM = getProject().getObjects().property(String.class);
toM = getProject().getObjects().property(String.class);
@@ -104,18 +117,26 @@ public class RemapJarTask extends Jar {
toM.set("intermediary");
// false by default, I have no idea why I have to do it for this property and not the other one
remapAccessWidener.set(false);
addDefaultNestedDependencies.set(true);
}
@TaskAction
public void doTask() throws Throwable {
boolean singleRemap = false;
if (jarRemapper == null) {
doSingleRemap();
} else {
scheduleRemap();
singleRemap = true;
jarRemapper = new JarRemapper();
}
scheduleRemap(singleRemap || getProject().getExtensions().getByType(LoomGradleExtension.class).isRootProject());
if (singleRemap) {
jarRemapper.remap();
}
}
public void doSingleRemap() throws Throwable {
public void scheduleRemap(boolean isMainRemapTask) throws Throwable {
Project project = getProject();
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
Path input = this.getInput().getAsFile().get().toPath();
@@ -130,188 +151,7 @@ public class RemapJarTask extends Jar {
String fromM = this.fromM.get();
String toM = this.toM.get();
Path[] classpath = getRemapClasspath();
LoggerFilter.replaceSystemOut();
TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper();
remapperBuilder.logger(getProject().getLogger()::lifecycle);
remapperBuilder = remapperBuilder.withMappings(TinyRemapperMappingsHelper.create(extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false));
for (File mixinMapFile : extension.getAllMixinMappings()) {
if (mixinMapFile.exists()) {
IMappingProvider provider = TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, "intermediary");
remapperBuilder = remapperBuilder.withMappings(extension.isForge() ? remapToSrg(extension, provider) : provider);
}
}
// Apply any requested options to tiny remapper
for (Action<TinyRemapper.Builder> remapOption : this.remapOptions) {
remapOption.execute(remapperBuilder);
}
project.getLogger().info(":remapping " + input.getFileName());
StringBuilder rc = new StringBuilder("Remap classpath: ");
for (Path p : classpath) {
rc.append("\n - ").append(p.toString());
}
project.getLogger().debug(rc.toString());
TinyRemapper remapper = remapperBuilder.build();
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) {
outputConsumer.addNonClassFiles(input);
remapper.readClassPath(classpath);
remapper.readInputs(input);
remapper.apply(outputConsumer);
} catch (Exception e) {
remapper.finish();
throw new RuntimeException("Failed to remap " + input + " to " + output, e);
}
if (getRemapAccessWidener().getOrElse(false) && extension.accessWidener != null) {
extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class).remapAccessWidener(output, remapper.getRemapper());
}
remapper.finish();
if (!Files.exists(output)) {
throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!");
}
if (MixinRefmapHelper.addRefmapName(extension.getRefmapName(), output)) {
project.getLogger().debug("Transformed mixin reference maps in output JAR!");
}
if (extension.isForge()) {
try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:" + output.toUri()), ImmutableMap.of("create", false))) {
Path refmapPath = fs.getPath(extension.getRefmapName());
if (Files.exists(refmapPath)) {
try (Reader refmapReader = Files.newBufferedReader(refmapPath, StandardCharsets.UTF_8)) {
Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
JsonObject refmapElement = gson.fromJson(refmapReader, JsonObject.class);
refmapElement = RefmapRemapper.remap(new Remapper() {
ReferenceRemapper remapper = createReferenceRemapper(extension);
@Override
@Nullable
public MappingsRemapper remapMappings() {
return className -> remapper;
}
@Override
@Nullable
public Map.Entry<String, @Nullable MappingsRemapper> remapMappingsData(String data) {
if (Objects.equals(data, "named:intermediary")) {
return new AbstractMap.SimpleEntry<>("searge", remapMappings());
}
return null;
}
}, refmapElement);
Files.delete(refmapPath);
Files.write(refmapPath, gson.toJson(refmapElement).getBytes(StandardCharsets.UTF_8));
}
}
}
}
if (getAddNestedDependencies().getOrElse(false)) {
if (NestedJars.addNestedJars(project, output)) {
project.getLogger().debug("Added nested jar paths to mod json");
}
}
if (isReproducibleFileOrder() || isPreserveFileTimestamps()) {
ZipReprocessorUtil.reprocessZip(output.toFile(), isReproducibleFileOrder(), isPreserveFileTimestamps());
}
}
private ReferenceRemapper createReferenceRemapper(LoomGradleExtension extension) throws IOException {
TinyTree srg = extension.getMappingsProvider().getMappingsWithSrg();
return new SimpleReferenceRemapper(new SimpleReferenceRemapper.Remapper() {
@Override
@Nullable
public String mapClass(String value) {
return srg.getClasses().stream()
.filter(classDef -> Objects.equals(classDef.getName("intermediary"), value))
.findFirst()
.map(classDef -> classDef.getName("srg"))
.orElse(null);
}
@Override
@Nullable
public String mapMethod(@Nullable String className, String methodName, String methodDescriptor) {
if (className != null) {
Optional<ClassDef> classDef = srg.getClasses().stream()
.filter(c -> Objects.equals(c.getName("intermediary"), className))
.findFirst();
if (classDef.isPresent()) {
for (MethodDef methodDef : classDef.get().getMethods()) {
if (Objects.equals(methodDef.getName("intermediary"), methodName) && Objects.equals(methodDef.getDescriptor("intermediary"), methodDescriptor)) {
return methodDef.getName("srg");
}
}
}
}
return srg.getClasses().stream()
.flatMap(classDef -> classDef.getMethods().stream())
.filter(methodDef -> Objects.equals(methodDef.getName("intermediary"), methodName) && Objects.equals(methodDef.getDescriptor("intermediary"), methodDescriptor))
.findFirst()
.map(methodDef -> methodDef.getName("srg"))
.orElse(null);
}
@Override
@Nullable
public String mapField(@Nullable String className, String fieldName, String fieldDescriptor) {
if (className != null) {
Optional<ClassDef> classDef = srg.getClasses().stream()
.filter(c -> Objects.equals(c.getName("intermediary"), className))
.findFirst();
if (classDef.isPresent()) {
for (FieldDef fieldDef : classDef.get().getFields()) {
if (Objects.equals(fieldDef.getName("intermediary"), fieldName) && Objects.equals(fieldDef.getDescriptor("intermediary"), fieldDescriptor)) {
return fieldDef.getName("srg");
}
}
}
}
return srg.getClasses().stream()
.flatMap(classDef -> classDef.getFields().stream())
.filter(fieldDef -> Objects.equals(fieldDef.getName("intermediary"), fieldName) && Objects.equals(fieldDef.getDescriptor("intermediary"), fieldDescriptor))
.findFirst()
.map(fieldDef -> fieldDef.getName("srg"))
.orElse(null);
}
});
}
public void scheduleRemap() throws Throwable {
Project project = getProject();
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
Path input = this.getInput().getAsFile().get().toPath();
Path output = this.getArchivePath().toPath();
if (!Files.exists(input)) {
throw new FileNotFoundException(input.toString());
}
MappingsProvider mappingsProvider = extension.getMappingsProvider();
String fromM = this.fromM.get();
String toM = this.toM.get();
if (extension.isRootProject()) {
if (isMainRemapTask) {
jarRemapper.addToClasspath(getRemapClasspath());
jarRemapper.addMappings(TinyRemapperMappingsHelper.create(extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false));
@@ -327,6 +167,9 @@ public class RemapJarTask extends Jar {
// Add remap options to the jar remapper
jarRemapper.addOptions(this.remapOptions);
NestedJarProvider nestedJarProvider = getNestedJarProvider();
nestedJarProvider.prepare(getProject());
jarRemapper.scheduleRemap(input, output)
.supplyAccessWidener((remapData, remapper) -> {
if (getRemapAccessWidener().getOrElse(false) && extension.accessWidener != null) {
@@ -357,18 +200,43 @@ public class RemapJarTask extends Jar {
}
if (getAddNestedDependencies().getOrElse(false)) {
if (NestedJars.addNestedJars(project, output)) {
project.getLogger().debug("Added nested jar paths to mod json");
}
JarNester.nestJars(nestedJarProvider.provide(), output.toFile(), project.getLogger());
}
if (accessWidener != null) {
boolean replaced = ZipUtil.replaceEntry(data.output.toFile(), accessWidener.getLeft(), accessWidener.getRight());
Preconditions.checkArgument(replaced, "Failed to remap access widener");
}
if (isReproducibleFileOrder() || !isPreserveFileTimestamps()) {
try {
ZipReprocessorUtil.reprocessZip(output.toFile(), isReproducibleFileOrder(), isPreserveFileTimestamps());
} catch (IOException e) {
throw new RuntimeException("Failed to re-process jar", e);
}
}
});
}
private NestedJarProvider getNestedJarProvider() {
Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE);
if (!addDefaultNestedDependencies.getOrElse(true)) {
return new NestedJarPathProvider(nestedPaths);
}
NestedJarProvider baseProvider = NestedDependencyProvider.createNestedDependencyProviderFromConfiguration(getProject(), includeConfiguration);
if (nestedPaths.isEmpty()) {
return baseProvider;
}
return new MergedNestedJarProvider(
baseProvider,
new NestedJarPathProvider(nestedPaths)
);
}
private IMappingProvider remapToSrg(LoomGradleExtension extension, IMappingProvider parent) throws IOException {
TinyTree srg = extension.getMappingsProvider().getMappingsWithSrg();
@@ -443,6 +311,11 @@ public class RemapJarTask extends Jar {
return addNestedDependencies;
}
@Input
public Property<Boolean> getAddDefaultNestedDependencies() {
return addDefaultNestedDependencies;
}
@Input
public Property<Boolean> getRemapAccessWidener() {
return remapAccessWidener;
@@ -462,6 +335,14 @@ public class RemapJarTask extends Jar {
return this;
}
@ApiStatus.Experimental // This only allows mod jars, proceed with care when trying to pass in configurations with projects, or something that depends on a task.
public RemapJarTask include(Object... paths) {
Collections.addAll(nestedPaths, paths);
this.addNestedDependencies.set(true);
return this;
}
@Input
public Property<String> getFromM() {
return fromM;

View File

@@ -26,8 +26,6 @@ package net.fabricmc.loom.task;
import java.io.File;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Internal;
@@ -35,29 +33,24 @@ import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.util.SourceRemapper;
import net.fabricmc.loom.util.ZipReprocessorUtil;
public class RemapSourcesJarTask extends AbstractLoomTask {
private Object input;
private Object output;
private String direction = "intermediary";
private SourceRemapper sourceRemapper = null;
private final Property<Boolean> archivePreserveFileTimestamps;
private final Property<Boolean> archiveReproducibleFileOrder;
private boolean preserveFileTimestamps = true;
private boolean reproducibleFileOrder = false;
public RemapSourcesJarTask() {
ObjectFactory objectFactory = getProject().getObjects();
archivePreserveFileTimestamps = objectFactory.property(Boolean.class);
archiveReproducibleFileOrder = objectFactory.property(Boolean.class);
}
@TaskAction
public void remap() throws Exception {
if (sourceRemapper == null) {
SourceRemapper.remapSources(getProject(), getInput(), getOutput(), direction.equals("named"));
ZipReprocessorUtil.reprocessZip(getOutput(), archivePreserveFileTimestamps.getOrElse(true), archiveReproducibleFileOrder.getOrElse(false));
SourceRemapper.remapSources(getProject(), getInput(), getOutput(), direction.equals("named"), reproducibleFileOrder, preserveFileTimestamps);
} else {
sourceRemapper.scheduleRemapSources(getInput(), getOutput(), archivePreserveFileTimestamps.getOrElse(true), archiveReproducibleFileOrder.getOrElse(false));
sourceRemapper.scheduleRemapSources(getInput(), getOutput(), reproducibleFileOrder, preserveFileTimestamps);
}
}
@@ -97,4 +90,22 @@ public class RemapSourcesJarTask extends AbstractLoomTask {
public void setTargetNamespace(String value) {
this.direction = value;
}
@Input
public boolean isPreserveFileTimestamps() {
return preserveFileTimestamps;
}
public void setPreserveFileTimestamps(boolean preserveFileTimestamps) {
this.preserveFileTimestamps = preserveFileTimestamps;
}
@Input
public boolean isReproducibleFileOrder() {
return reproducibleFileOrder;
}
public void setReproducibleFileOrder(boolean reproducibleFileOrder) {
this.reproducibleFileOrder = reproducibleFileOrder;
}
}

View File

@@ -0,0 +1,126 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.task;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.OutputFile;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.providers.LaunchProvider;
import net.fabricmc.loom.util.Constants;
public class UnpickJarTask extends JavaExec {
File inputJar;
File unpickDefinition;
File outputJar;
public UnpickJarTask() {
getOutputs().upToDateWhen(e -> false);
classpath(getProject().getConfigurations().getByName(Constants.Configurations.UNPICK_CLASSPATH));
setMain("daomephsta.unpick.cli.Main");
}
@Override
public void exec() {
fileArg(getInputJar(), getOutputJar(), getUnpickDefinition());
fileArg(getConstantJar());
// Classpath
fileArg(getExtension().getMinecraftMappedProvider().getMappedJar());
fileArg(getMinecraftDependencies());
writeUnpickLogConfig();
systemProperty("java.util.logging.config.file", getExtension().getUnpickLoggingConfigFile().getAbsolutePath());
super.exec();
}
private void writeUnpickLogConfig() {
try (InputStream is = LaunchProvider.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) {
Files.deleteIfExists(getExtension().getUnpickLoggingConfigFile().toPath());
Files.copy(is, getExtension().getUnpickLoggingConfigFile().toPath());
} catch (IOException e) {
throw new RuntimeException("Failed to copy unpick logging config", e);
}
}
private File[] getMinecraftDependencies() {
return getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES)
.resolve().toArray(new File[0]);
}
private File getConstantJar() {
return getProject().getConfigurations().getByName(Constants.Configurations.MAPPING_CONSTANTS).getSingleFile();
}
@InputFile
public File getInputJar() {
return inputJar;
}
public UnpickJarTask setInputJar(File inputJar) {
this.inputJar = inputJar;
return this;
}
@InputFile
public File getUnpickDefinition() {
return unpickDefinition;
}
public UnpickJarTask setUnpickDefinition(File unpickDefinition) {
this.unpickDefinition = unpickDefinition;
return this;
}
@OutputFile
public File getOutputJar() {
return outputJar;
}
public UnpickJarTask setOutputJar(File outputJar) {
this.outputJar = outputJar;
return this;
}
private void fileArg(File... files) {
for (File file : files) {
args(file.getAbsolutePath());
}
}
@Internal
protected LoomGradleExtension getExtension() {
return getProject().getExtensions().getByType(LoomGradleExtension.class);
}
}

View File

@@ -87,6 +87,8 @@ public class Constants {
public static final String FORGE_DEPENDENCIES = "forgeDependencies";
@Deprecated // Not to be used in gradle 7+
public static final String COMPILE = "compile";
public static final String MAPPING_CONSTANTS = "mappingsConstants";
public static final String UNPICK_CLASSPATH = "unpick";
private Configurations() {
}

View File

@@ -89,6 +89,7 @@ public class DownloadUtil {
if ((code < 200 || code > 299) && code != HttpURLConnection.HTTP_NOT_MODIFIED) {
//Didn't get what we expected
delete(to);
throw new IOException(connection.getResponseMessage() + " for " + from);
}
@@ -111,7 +112,7 @@ public class DownloadUtil {
try { // Try download to the output
FileUtils.copyInputStreamToFile(connection.getInputStream(), to);
} catch (IOException e) {
to.delete(); // Probably isn't good if it fails to copy/save
delete(to); // Probably isn't good if it fails to copy/save
throw e;
}

View File

@@ -31,6 +31,8 @@ import java.util.stream.Stream;
import groovy.util.Node;
import groovy.xml.QName;
import net.fabricmc.loom.util.gradle.GradleSupport;
public final class GroovyXmlUtil {
private GroovyXmlUtil() { }
@@ -63,9 +65,19 @@ public final class GroovyXmlUtil {
return ((QName) nodeName).matches(givenName);
}
// New groovy 3 (gradle 7) class
if (GradleSupport.IS_GRADLE_7_OR_NEWER && nodeName.getClass().getName().equals("groovy.namespace.QName")) {
return isSameNameGroovy3(nodeName, givenName);
}
throw new UnsupportedOperationException("Cannot determine if " + nodeName.getClass() + " is the same as a String");
}
// TODO Move out of its own method when requiring gradle 7
private static boolean isSameNameGroovy3(Object nodeName, String givenName) {
return ((groovy.namespace.QName) nodeName).matches(givenName);
}
public static Stream<Node> childrenNodesStream(Node node) {
//noinspection unchecked
return (Stream<Node>) (Stream) (((List<Object>) node.children()).stream().filter((i) -> i instanceof Node));

View File

@@ -25,6 +25,7 @@
package net.fabricmc.loom.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -60,6 +61,10 @@ public class HashedDownloadUtil {
}
public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet, boolean strict) throws IOException {
downloadIfInvalid(from, to, expectedHash, logger, quiet, strict, () -> { });
}
public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet, boolean strict, Runnable startDownload) throws IOException {
if (LoomGradlePlugin.refreshDeps) {
delete(to);
}
@@ -80,6 +85,8 @@ public class HashedDownloadUtil {
}
}
startDownload.run();
HttpURLConnection connection = (HttpURLConnection) from.openConnection();
connection.setRequestProperty("Accept-Encoding", "gzip");
connection.connect();
@@ -121,14 +128,18 @@ public class HashedDownloadUtil {
@Nullable
private static String getSha1(File to, Logger logger) {
File sha1File = getSha1File(to);
if (!sha1File.exists()) {
if (!to.exists()) {
delete(to);
return null;
}
File sha1File = getSha1File(to);
try {
return Files.asCharSource(sha1File, StandardCharsets.UTF_8).read();
} catch (FileNotFoundException ignored) {
// Quicker to catch this than do an exists check before.
return null;
} catch (IOException e) {
logger.warn("Error reading sha1 file '{}'.", sha1File);
return null;

View File

@@ -0,0 +1,42 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.util;
import java.io.File;
import java.io.IOException;
import java.util.zip.ZipFile;
public final class ModUtils {
private ModUtils() {
}
public static boolean isMod(File input) {
try (ZipFile zipFile = new ZipFile(input)) {
return zipFile.getEntry("fabric.mod.json") != null;
} catch (IOException e) {
return false;
}
}
}

View File

@@ -49,6 +49,10 @@ public class OperatingSystem {
return System.getProperty("sun.arch.data.model").contains("64");
}
public static boolean isWindows() {
return getOS().equals("windows");
}
public static boolean isCIBuild() {
String loomProperty = System.getProperty("fabric.loom.ci");

View File

@@ -62,17 +62,12 @@ public class SourceRemapper {
this.toNamed = toNamed;
}
public static void remapSources(Project project, File input, File output, boolean named) throws Exception {
public static void remapSources(Project project, File input, File output, boolean named, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
SourceRemapper sourceRemapper = new SourceRemapper(project, named);
sourceRemapper.scheduleRemapSources(input, output, false, true);
sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps);
sourceRemapper.remapAll();
}
@Deprecated
public void scheduleRemapSources(File source, File destination) throws Exception {
scheduleRemapSources(source, destination, false, true); // Not reproducable by default, old behavior
}
public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
remapTasks.add((logger) -> {
try {

View File

@@ -29,11 +29,20 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.attribute.FileTime;
import java.util.Calendar;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ZipReprocessorUtil {
/**
* See {@link org.gradle.api.internal.file.archive.ZipCopyAction} about this.
*/
private static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = new GregorianCalendar(1980, Calendar.FEBRUARY, 1, 0, 0, 0).getTimeInMillis();
private ZipReprocessorUtil() { }
public static void reprocessZip(File file, boolean reproducibleFileOrder, boolean preserveFileTimestamps) throws IOException {
@@ -45,7 +54,7 @@ public class ZipReprocessorUtil {
ZipEntry[] entries;
if (reproducibleFileOrder) {
entries = zipFile.stream().sorted((a, b) -> a.getName().compareTo(b.getName())).toArray(ZipEntry[]::new);
entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName)).toArray(ZipEntry[]::new);
} else {
entries = zipFile.stream().toArray(ZipEntry[]::new);
}
@@ -54,11 +63,16 @@ public class ZipReprocessorUtil {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outZip)) {
for (ZipEntry entry : entries) {
ZipEntry newEntry = entry;
if (!preserveFileTimestamps) {
entry.setTime(0);
newEntry = new ZipEntry(entry.getName());
newEntry.setTime(CONSTANT_TIME_FOR_ZIP_ENTRIES);
newEntry.setLastModifiedTime(FileTime.fromMillis(CONSTANT_TIME_FOR_ZIP_ENTRIES));
newEntry.setLastAccessTime(FileTime.fromMillis(CONSTANT_TIME_FOR_ZIP_ENTRIES));
}
zipOutputStream.putNextEntry(entry);
zipOutputStream.putNextEntry(newEntry);
InputStream inputStream = zipFile.getInputStream(entry);
byte[] buf = new byte[1024];
int length;

View File

@@ -0,0 +1 @@
unpick.level=WARNING

View File

@@ -1,211 +0,0 @@
package net.fabricmc.loom
import net.fabricmc.loom.util.Constants
/**
* Created by Mitchell Skaggs on 6/12/2019.
*/
static String genBuildFile(String mappingsDep = "\"net.fabricmc:yarn:\${project.yarn_mappings}\"", String pluginId = Constants.PLUGIN_ID) {
"""
plugins {
id '${pluginId}'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
minecraft {
}
dependencies {
//to change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:\${project.minecraft_version}"
mappings ${mappingsDep}
modImplementation "net.fabricmc:fabric-loader:\${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:\${project.fabric_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
}
jar {
from "LICENSE"
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven
artifact(jar) {
builtBy remapJar
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
// select the repositories you want to publish to
repositories {
// uncomment to publish to the local maven
// mavenLocal()
}
}
"""
}
static String genPropsFile(String mcVersion, String yarnVersion, String loaderVersion, String fabricVersion, boolean caching = true, boolean parallel = true) {
"""
org.gradle.caching=$caching
org.gradle.parallel=$parallel
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=$mcVersion
yarn_mappings=$yarnVersion
loader_version=$loaderVersion
# Mod Properties
mod_version = 1.0.0
maven_group = net.fabricmc
archives_base_name = fabric-example-mod
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric
fabric_version=$fabricVersion
"""
}
static String genSettingsFile(String name) {
"""
rootProject.name = '$name'
"""
}
static String genModJsonFile() {
"""
{
"schemaVersion": 1,
"id": "modid",
"version": "\${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"mixins": [
"modid.mixins.json"
],
"depends": {
"fabricloader": ">=0.4.0",
"fabric": "*"
},
"suggests": {
"flamingo": "*"
}
}
"""
}
static String genModJavaFile() {
"""
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}
"""
}
static String genModMixinsJsonFile() {
"""
{
"required": true,
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"ExampleMixin"
],
"injectors": {
"defaultRequire": 1
}
}
"""
}
static String genModMixinsJavaFile() {
"""
package net.fabricmc.example.mixin;
import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public class ExampleMixin {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}
"""
}

View File

@@ -1,51 +0,0 @@
package net.fabricmc.loom
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
import spock.lang.Unroll
import static net.fabricmc.loom.BuildUtils.*
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
/**
* Created by Mitchell Skaggs on 6/10/2019.
*/
class EmptyBuildFunctionalTest extends Specification {
@Rule
TemporaryFolder testProjectDir = new TemporaryFolder()
File settingsFile
File buildFile
File propsFile
def setup() {
settingsFile = testProjectDir.newFile('settings.gradle')
buildFile = testProjectDir.newFile('build.gradle')
propsFile = testProjectDir.newFile('gradle.properties')
}
@Unroll
def "empty build succeeds using Minecraft #mcVersion"() {
given:
settingsFile << genSettingsFile("empty-build-functional-test")
propsFile << genPropsFile(mcVersion, yarnVersion, loaderVersion, fabricVersion)
buildFile << genBuildFile()
when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('build',"--stacktrace", "--warning-mode", System.getenv().TEST_WARNING_MODE ?: 'all')
.withPluginClasspath()
.forwardOutput()
.build()
then:
result.task(":build").outcome == SUCCESS
where:
mcVersion | yarnVersion | loaderVersion | fabricVersion
'1.14' | '1.14+build.21' | '0.4.8+build.155' | '0.3.0+build.183'
'1.16.2' | '1.16.2+build.26:v2' | '0.9.2+build.206' | '0.19.0+build.398-1.16'
}
}

View File

@@ -1,50 +0,0 @@
package net.fabricmc.loom
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
import spock.lang.Unroll
import static net.fabricmc.loom.BuildUtils.*
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
/**
* Created by Mitchell Skaggs on 6/10/2019.
*/
class EmptyBuildMojangFunctionalTest extends Specification {
@Rule
TemporaryFolder testProjectDir = new TemporaryFolder()
File settingsFile
File buildFile
File propsFile
def setup() {
settingsFile = testProjectDir.newFile('settings.gradle')
buildFile = testProjectDir.newFile('build.gradle')
propsFile = testProjectDir.newFile('gradle.properties')
}
@Unroll
def "empty build succeeds using Minecraft #mcVersion"() {
given:
settingsFile << genSettingsFile("empty-build-functional-test")
propsFile << genPropsFile(mcVersion, "nope", loaderVersion, fabricVersion)
buildFile << genBuildFile("minecraft.officialMojangMappings()")
when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('build',"--stacktrace", "--warning-mode", System.getenv().TEST_WARNING_MODE ?: 'all')
.withPluginClasspath()
.forwardOutput()
.build()
then:
result.task(":build").outcome == SUCCESS
where:
mcVersion | loaderVersion | fabricVersion
'1.16.2' | '0.9.2+build.206' | '0.19.0+build.398-1.16'
}
}

View File

@@ -1,69 +0,0 @@
package net.fabricmc.loom
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Ignore
import spock.lang.Specification
import spock.lang.Unroll
import static net.fabricmc.loom.BuildUtils.*
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
/**
* Created by Mitchell Skaggs on 6/10/2019.
*/
class MixinBuildFunctionalTest extends Specification {
@Rule
TemporaryFolder testProjectDir = new TemporaryFolder()
File settingsFile
File buildFile
File propsFile
File modJsonFile
File modJavaFile
File modMixinsJsonFile
File modMixinsJavaFile
def setup() {
settingsFile = testProjectDir.newFile('settings.gradle')
buildFile = testProjectDir.newFile('build.gradle')
propsFile = testProjectDir.newFile('gradle.properties')
testProjectDir.newFolder("src", "main", "resources")
modJsonFile = testProjectDir.newFile('src/main/resources/fabric.mod.json')
modMixinsJsonFile = testProjectDir.newFile('src/main/resources/modid.mixins.json')
testProjectDir.newFolder("src", "main", "java", "net", "fabricmc", "example")
modJavaFile = testProjectDir.newFile("src/main/java/net/fabricmc/example/ExampleMod.java")
testProjectDir.newFolder("src", "main", "java", "net", "fabricmc", "example", "mixin")
modMixinsJavaFile = testProjectDir.newFile("src/main/java/net/fabricmc/example/mixin/ExampleMixin.java")
}
@Unroll
def "mixin build succeeds using Minecraft #mcVersion"() {
given:
settingsFile << genSettingsFile("mixin-build-functional-test")
propsFile << genPropsFile(mcVersion, yarnVersion, loaderVersion, fabricVersion)
buildFile << genBuildFile()
modJsonFile << genModJsonFile()
modJavaFile << genModJavaFile()
modMixinsJsonFile << genModMixinsJsonFile()
modMixinsJavaFile << genModMixinsJavaFile()
when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('build','--stacktrace', "--warning-mode", System.getenv().TEST_WARNING_MODE ?: 'all')
.withPluginClasspath()
.forwardOutput()
.build()
then:
result.task(":build").outcome == SUCCESS
where:
mcVersion | yarnVersion | loaderVersion | fabricVersion
'1.14' | '1.14+build.21' | '0.4.8+build.155' | '0.3.0+build.184'
}
}

View File

@@ -1,62 +0,0 @@
package net.fabricmc.loom
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
import spock.lang.Unroll
import static net.fabricmc.loom.BuildUtils.*
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
/**
* Created by Mitchell Skaggs on 6/10/2019.
*/
class SimpleBuildFunctionalTest extends Specification {
@Rule
TemporaryFolder testProjectDir = new TemporaryFolder()
File settingsFile
File buildFile
File propsFile
File modJsonFile
File modExampleFile
def setup() {
settingsFile = testProjectDir.newFile('settings.gradle')
buildFile = testProjectDir.newFile('build.gradle')
propsFile = testProjectDir.newFile('gradle.properties')
testProjectDir.newFolder("src", "main", "resources")
modJsonFile = testProjectDir.newFile('src/main/resources/fabric.mod.json')
testProjectDir.newFolder("src", "main", "java", "net", "fabricmc", "example")
modExampleFile = testProjectDir.newFile("src/main/java/net/fabricmc/example/ExampleMod.java")
}
@Unroll
def "simple build succeeds using Minecraft #mcVersion"() {
given:
settingsFile << genSettingsFile("simple-build-functional-test")
propsFile << genPropsFile(mcVersion, yarnVersion, loaderVersion, fabricVersion)
buildFile << genBuildFile()
modJsonFile << genModJsonFile()
modExampleFile << genModJavaFile()
when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('build',"--stacktrace", "--warning-mode", System.getenv().TEST_WARNING_MODE ?: 'all')
.withPluginClasspath()
.forwardOutput()
.withDebug(true)
.build()
then:
result.task(":build").outcome == SUCCESS
where:
mcVersion | yarnVersion | loaderVersion | fabricVersion
'19w45a' | '19w45a+build.2:v2' | '0.6.2+build.166' | '0.4.9+build.258-1.15'
'1.14' | '1.14+build.21' | '0.4.8+build.155' | '0.3.0+build.184'
}
}

View File

@@ -0,0 +1,56 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ArchiveAssertionsTrait
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class AccessWidenerTest extends Specification implements ProjectTestTrait, ArchiveAssertionsTrait {
@Override
String name() {
"accesswidener"
}
@Unroll
def "accesswidener (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
getArchiveEntry("fabric-example-mod-1.0.0.jar", "modid.accesswidener") == expected().replaceAll('\r', '')
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
String expected() {
new File("src/test/resources/accesswidener/expected.accesswidener").text
}
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class DecompileTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"decompile"
}
@Unroll
def "#decompiler gradle #gradle"() {
when:
def result = create(task, gradle)
then:
result.task(":${task}").outcome == SUCCESS
where:
decompiler | task | gradle
'fernflower' | "genSources" | DEFAULT_GRADLE
'fernflower' | "genSources" | LEGACY_GRADLE
'fernflower' | "genSources" | PRE_RELEASE_GRADLE
'cfr' | "genSourcesWithExperimentalCfr" | DEFAULT_GRADLE
}
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class KotlinTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"kotlin"
}
@Unroll
def "kotlin build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
// This test uses gradle 4.9 and 1.14.4 v1 mappings
class LegacyProjectTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"legacy"
}
@Unroll
def "build"() {
when:
def result = create("build", LEGACY_GRADLE)
then:
result.task(":build").outcome == SUCCESS
}
}

View File

@@ -0,0 +1,98 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ArchiveAssertionsTrait
import net.fabricmc.loom.test.util.MockMavenServerTrait
import spock.lang.Specification
import spock.lang.Stepwise
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties
import static java.lang.System.setProperty
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
/**
* This tests publishing a range of versions and then tries to resolve and build against them
*/
@Stepwise
class MavenProjectTest extends Specification implements MockMavenServerTrait, ArchiveAssertionsTrait {
@RestoreSystemProperties
@Unroll
def "publish lib #version #gradle"() {
given:
setProperty('loom.test.version', version)
library = true
when:
def result = create("publish", gradle)
then:
result.task(":publish").outcome == SUCCESS
hasArchiveEntry("fabric-example-lib-${version}.jar", "net/fabricmc/example/ExampleLib.class")
where:
version | gradle
'1.0.0' | DEFAULT_GRADLE
'1.0.0' | PRE_RELEASE_GRADLE
'1.1.0' | DEFAULT_GRADLE
'1.1.1' | DEFAULT_GRADLE
'1.2.0+meta' | DEFAULT_GRADLE
'2.0.0-SNAPSHOT' | DEFAULT_GRADLE
'2.0.0-SNAPSHOT' | DEFAULT_GRADLE // Publish this twice to give ourselves a bit of a challenge
'master-SNAPSHOT' | DEFAULT_GRADLE
}
@RestoreSystemProperties
@Unroll
def "resolve #version #gradle"() {
given:
setProperty('loom.test.resolve', "com.example:fabric-example-lib:${version}")
library = false
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
hasArchiveEntry("fabric-example-mod-1.0.0.jar", "net/fabricmc/examplemod/ExampleMod.class")
where:
version | gradle
'1.0.0' | DEFAULT_GRADLE
'1.0.0' | PRE_RELEASE_GRADLE
'1.1.+' | DEFAULT_GRADLE
'1.2.0+meta' | DEFAULT_GRADLE
'2.0.0-SNAPSHOT' | DEFAULT_GRADLE
'master-SNAPSHOT' | DEFAULT_GRADLE
'1.0.0:classifier' | DEFAULT_GRADLE
'1.1.+:classifier' | DEFAULT_GRADLE
'1.2.0+meta:classifier' | DEFAULT_GRADLE
'2.0.0-SNAPSHOT:classifier' | DEFAULT_GRADLE
'master-SNAPSHOT:classifier' | DEFAULT_GRADLE
}
// Set to true when to build and publish the mavenLibrary
private boolean library = false
@Override
String name() {
library ? "mavenLibrary" : "maven"
}
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class MojangMappingsProjectTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"mojangMappings"
}
@Unroll
def "build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
}

View File

@@ -0,0 +1,61 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ArchiveAssertionsTrait
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class MultiProjectTest extends Specification implements ProjectTestTrait, ArchiveAssertionsTrait {
@Override
String name() {
"multiproject"
}
@Unroll
def "build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
result.task(":core:build").outcome == SUCCESS
result.task(":example:build").outcome == SUCCESS
result.task(":remapAllJars").outcome == SUCCESS
result.task(":remapAllSources").outcome == SUCCESS
hasArchiveEntry("multiproject-1.0.0.jar", "META-INF/jars/example-1.0.0.jar")
hasArchiveEntry("multiproject-1.0.0.jar", "META-INF/jars/core-1.0.0.jar")
hasArchiveEntry("multiproject-1.0.0.jar", "META-INF/jars/fabric-api-base-0.2.1+9354966b7d.jar")
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
}

View File

@@ -0,0 +1,64 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import com.google.common.hash.HashCode
import com.google.common.hash.Hashing
import com.google.common.io.Files
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class ReproducibleBuildTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"reproducible"
}
@Unroll
def "build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
getOutputHash("fabric-example-mod-1.0.0.jar") == modHash
getOutputHash("fabric-example-mod-1.0.0-sources.jar") in sourceHash // Done for different line endings.
where:
gradle | modHash | sourceHash
'6.8.3' | "6132ffb4117adb7e258f663110552952" | ["be31766e6cafbe4ae3bca9e35ba63169", "7348b0bd87d36d7ec6f3bca9c2b66062"]
'7.0-rc-1' | "6132ffb4117adb7e258f663110552952" | ["be31766e6cafbe4ae3bca9e35ba63169", "7348b0bd87d36d7ec6f3bca9c2b66062"]
}
String getOutputHash(String name) {
generateMD5(getOutputFile(name))
}
String generateMD5(File file) {
HashCode hash = Files.asByteSource(file).hash(Hashing.md5())
return hash.asBytes().encodeHex() as String
}
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
// This test runs a mod that exits on mod init
class RunConfigTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"runconfigs"
}
@Unroll
def "#task"() {
when:
def result = create(task)
then:
result.task(":${task}").outcome == SUCCESS
where:
task | _
'runClient' | _
'runServer' | _
'runTestmodClient' | _
'runTestmodServer' | _
'runAutoTestServer' | _
}
}

View File

@@ -0,0 +1,63 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class SimpleProjectTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"simple"
}
@Unroll
def "build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
@Unroll
def "#ide config generation"() {
when:
def result = create(ide)
then:
result.task(":${ide}").outcome == SUCCESS
where:
ide | _
'idea' | _
'eclipse' | _
'vscode' | _
}
}

View File

@@ -0,0 +1,62 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.intergration
import net.fabricmc.loom.test.util.ProjectTestTrait
import org.zeroturnaround.zip.ZipUtil
import spock.lang.Specification
import java.nio.charset.StandardCharsets
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class UnpickTest extends Specification implements ProjectTestTrait {
static final String MAPPINGS = "21w13a-mapped-net.fabricmc.yarn-21w13a+build.30-v2"
@Override
String name() {
"unpick"
}
def "unpick decompile"() {
when:
def result = create("genSources")
then:
result.task(":genSources").outcome == SUCCESS
getClassSource("net/minecraft/block/CakeBlock.java").contains("Block.DEFAULT_SET_BLOCK_STATE_FLAG")
}
def "unpick build"() {
when:
def result = create("build")
then:
result.task(":build").outcome == SUCCESS
}
String getClassSource(String classname, String mappings = MAPPINGS) {
File sourcesJar = getGeneratedSources(mappings)
return new String(ZipUtil.unpackEntry(sourcesJar, classname), StandardCharsets.UTF_8)
}
}

View File

@@ -0,0 +1,74 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.unit
import groovy.xml.QName
import net.fabricmc.loom.util.GroovyXmlUtil
import spock.lang.Specification
class GroovyXmlUtilTest extends Specification {
def "getOrCreateNode finds existing node"() {
when:
def xmlTree = new XmlParser().parseText(text)
def existingNode = xmlTree[innerName]
def actualNode = GroovyXmlUtil.getOrCreateNode(xmlTree, innerName)
then:
existingNode.text() == actualNode.text()
where:
innerName | text
"bar" | "<foo><bar>inner content to ensure correct</bar></foo>"
"dependencies" | "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"><dependencies>inner content to ensure correct</dependencies></project>"
}
def "getOrCreateNode creates a node if needed"() {
when:
def xmlTree = new XmlParser().parseText(text)
def actualNode = GroovyXmlUtil.getOrCreateNode(xmlTree, innerName)
then:
xmlTree[QName.valueOf(actualNode.name().toString())] != null
where:
innerName | text
"bar" | "<foo></foo>"
"dependencies" | "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"></project>"
}
def "getNode finds existing node"() {
when:
def xmlTree = new XmlParser().parseText(text)
def actualNode = GroovyXmlUtil.getNode(xmlTree, innerName)
then:
actualNode.isPresent()
where:
innerName | text
"bar" | "<foo><bar>inner content to ensure correct</bar></foo>"
"dependencies" | "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"><dependencies>inner content to ensure correct</dependencies></project>"
}
}

View File

@@ -0,0 +1,46 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.util
import org.zeroturnaround.zip.ZipUtil
trait ArchiveAssertionsTrait {
String getArchiveEntry(String name, String entry, String project = "") {
def file = getOutputFile(name, project)
def bytes = ZipUtil.unpackEntry(file, entry)
if (bytes == null) {
throw new FileNotFoundException("Could not find ${entry} in ${name}")
}
new String(bytes as byte[])
}
boolean hasArchiveEntry(String name, String entry, String project = "") {
def file = getOutputFile(name, project)
ZipUtil.unpackEntry(file, entry) != null
}
}

View File

@@ -0,0 +1,91 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.util
import io.javalin.Javalin
import org.apache.commons.io.IOUtils
trait MockMavenServerTrait extends ProjectTestTrait {
public final int mavenServerPort = 9876
public final File testMavenDir = File.createTempDir()
private Javalin server
@SuppressWarnings('unused')
def setupSpec() {
println "Maven server path: ${testMavenDir.absolutePath}"
server = Javalin.create { config ->
config.enableDevLogging()
}.start(mavenServerPort)
/**
* A very very basic maven server impl, DO NOT copy this and use in production as its not secure
*/
server.get("*") { ctx ->
println "GET: " + ctx.path()
File file = getMavenPath(ctx.path())
if (!file.exists()) {
ctx.status(404)
return
}
ctx.result(file.bytes)
}
server.put("*") { ctx ->
println "PUT: " + ctx.path()
File file = getMavenPath(ctx.path())
file.parentFile.mkdirs()
file.withOutputStream {
IOUtils.copy(ctx.bodyAsInputStream(), it)
}
}
}
@SuppressWarnings('unused')
def setup() {
System.setProperty('loom.test.mavenPort', port())
}
@SuppressWarnings('unused')
def cleanupSpec() {
server.stop()
super.cleanupSpec()
}
File getMavenDirectory() {
new File(testMavenDir, "maven")
}
File getMavenPath(String path) {
new File(getMavenDirectory(), path)
}
String port() {
"${mavenServerPort}"
}
}

View File

@@ -0,0 +1,123 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.util
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
trait ProjectTestTrait {
final static String LEGACY_GRADLE = "4.9"
final static String DEFAULT_GRADLE = "6.8.3"
final static String PRE_RELEASE_GRADLE = "7.0-rc-2"
static File gradleHome = File.createTempDir()
File testProjectDir = File.createTempDir()
abstract String name()
def copyInputFiles() {
println "Project directory: ${testProjectDir}"
def baseProjectDir = new File("src/test/resources/projects/" + name())
if (!baseProjectDir.exists()) {
throw new FileNotFoundException("Failed to find project directory at:" + baseProjectDir.absolutePath)
}
baseProjectDir.eachFileRecurse { file ->
if (file.isDirectory()) {
return
}
def path = file.path.replace(baseProjectDir.path, "")
File tempFile = new File(testProjectDir, path)
if (tempFile.exists()) {
tempFile.delete()
}
tempFile.parentFile.mkdirs()
tempFile << file.text
}
// Disable the CI checks to ensure nothing is skipped
System.setProperty("fabric.loom.ci", "false")
}
@SuppressWarnings('unused')
def cleanup() {
// Clean after each test
new File(testProjectDir, "build").deleteDir()
new File(testProjectDir, ".gradle").deleteDir()
}
@SuppressWarnings('unused')
def cleanupSpec() {
testProjectDir.deleteDir()
gradleHome.deleteDir()
}
BuildResult create(String task, String gradleVersion = DEFAULT_GRADLE) {
System.setProperty("fabric.loom.test", "true")
copyInputFiles()
GradleRunner.create()
.withProjectDir(testProjectDir)
.withArguments(task, "--stacktrace", "--warning-mode", warningMode(gradleVersion), "--gradle-user-home", gradleHomeDirectory(gradleVersion))
.withPluginClasspath()
.withGradleVersion(gradleVersion)
.forwardOutput()
.withDebug(true)
.build()
}
String warningMode(String gradleVersion) {
if (gradleVersion == LEGACY_GRADLE) {
return "all"
}
'fail'
}
String gradleHomeDirectory(String gradleVersion) {
// Each gradle version gets its own dir to run on, to ensure that a full run is done.
new File(gradleHome, gradleVersion).absolutePath
}
File getOutputFile(String name, String project = "") {
def file = new File(testProjectDir, "${project}build/libs/${name}")
if (!file.exists()) {
throw new FileNotFoundException("Could not find ${name} at ${file.absolutePath}")
}
return file
}
File getGeneratedSources(String mappings, String gradleVersion = DEFAULT_GRADLE) {
return new File(gradleHomeDirectory(gradleVersion), "caches/fabric-loom/${mappings}/minecraft-${mappings}-sources.jar")
}
}

View File

@@ -0,0 +1,7 @@
accessWidener v1 intermediary
accessible class net/minecraft/class_1928$class_5199
accessible class net/minecraft/class_1928$class_4314
extendable class net/minecraft/class_1928$class_4314
accessible class net/minecraft/class_5235$class_5238
accessible method net/minecraft/class_1928$class_4314 <init> (Ljava/util/function/Supplier;Ljava/util/function/Function;Ljava/util/function/BiConsumer;Lnet/minecraft/class_1928$class_5199;)V
extendable method net/minecraft/class_1928$class_4314 <init> (Ljava/util/function/Supplier;Ljava/util/function/Function;Ljava/util/function/BiConsumer;Lnet/minecraft/class_1928$class_5199;)V

View File

@@ -0,0 +1,96 @@
plugins {
id 'fabric-loom'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
}
loom {
accessWidener = file("src/main/resources/modid.accesswidener")
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
// We'll use that if it's available, but otherwise we'll use the older option.
def targetVersion = 8
if (JavaVersion.current().isJava9Compatible()) {
it.options.release = targetVersion
}
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven
artifact(remapJar) {
builtBy remapJar
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

View File

@@ -0,0 +1,17 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.16.5
yarn_mappings=1.16.5+build.5
loader_version=0.11.2
# Mod Properties
mod_version = 1.0.0
maven_group = com.example
archives_base_name = fabric-example-mod
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version=0.31.0+1.16

View File

@@ -0,0 +1,2 @@
rootProject.name = "fabric-example-mod"

View File

@@ -0,0 +1,14 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@@ -0,0 +1,37 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"mixins": [
"modid.mixins.json"
],
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"suggests": {
"another-mod": "*"
},
"accessWidener" : "modid.accesswidener"
}

View File

@@ -0,0 +1,7 @@
accessWidener v1 named
accessible method net/minecraft/world/GameRules$Type <init> (Ljava/util/function/Supplier;Ljava/util/function/Function;Ljava/util/function/BiConsumer;Lnet/minecraft/world/GameRules$Acceptor;)V
extendable method net/minecraft/world/GameRules$Type <init> (Ljava/util/function/Supplier;Ljava/util/function/Function;Ljava/util/function/BiConsumer;Lnet/minecraft/world/GameRules$Acceptor;)V
accessible class net/minecraft/world/GameRules$Acceptor
accessible class net/minecraft/client/gui/screen/world/EditGameRulesScreen$RuleWidgetFactory

View File

@@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"ExampleMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -0,0 +1,9 @@
plugins {
id 'fabric-loom'
}
dependencies {
minecraft "com.mojang:minecraft:1.16.5"
mappings "net.fabricmc:yarn:1.16.5+build.5:v2"
modImplementation "net.fabricmc:fabric-loader:0.11.2"
}

View File

@@ -0,0 +1,20 @@
import java.util.Properties
plugins {
kotlin("jvm") version "1.4.31"
id("fabric-loom")
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
version = "0.0.1"
dependencies {
minecraft(group = "com.mojang", name = "minecraft", version = "1.16.5")
mappings(group = "net.fabricmc", name = "yarn", version = "1.16.5+build.5", classifier = "v2")
modImplementation("net.fabricmc:fabric-loader:0.11.2")
modImplementation(group = "net.fabricmc", name = "fabric-language-kotlin", version = "1.5.0+kotlin.1.4.31")
}

View File

@@ -0,0 +1,2 @@
rootProject.name = "fabric-example-mod"

View File

@@ -0,0 +1,15 @@
package net.fabricmc.language.kotlin
import net.fabricmc.api.ModInitializer
import org.apache.logging.log4j.LogManager
class TestModClass : ModInitializer {
val logger = LogManager.getFormatterLogger("KotlinLanguageTest")
override fun onInitialize() {
logger.info("**************************")
logger.info("Hello from Kotlin TestModClass")
logger.info("**************************")
}
}

View File

@@ -0,0 +1,33 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.language.kotlin.TestModClass"
]
},
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"suggests": {
"another-mod": "*"
}
}

View File

@@ -0,0 +1,45 @@
plugins {
id 'fabric-loom'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}
processResources {
inputs.property "version", project.version
from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json"
expand "version": project.version
}
from(sourceSets.main.resources.srcDirs) {
exclude "fabric.mod.json"
}
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
}

View File

@@ -0,0 +1,17 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.14.4
yarn_mappings=1.14.4+build.18
loader_version=0.11.2
# Mod Properties
mod_version = 1.0.0
maven_group = com.example
archives_base_name = fabric-example-mod
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version=0.28.5+1.14

View File

@@ -0,0 +1,2 @@
rootProject.name = "fabric-example-mod"

View File

@@ -0,0 +1,14 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public class ExampleMixin {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@@ -0,0 +1,36 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"mixins": [
"modid.mixins.json"
],
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"suggests": {
"another-mod": "*"
}
}

View File

@@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"ExampleMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -0,0 +1,25 @@
plugins {
id 'fabric-loom'
id 'maven-publish'
}
archivesBaseName = "fabric-example-mod"
version = "1.0.0"
group = "com.example"
println archivesBaseName
repositories {
maven {
url = "http://localhost:${System.getProperty("loom.test.mavenPort")}/"
allowInsecureProtocol = true
}
}
dependencies {
minecraft "com.mojang:minecraft:1.16.5"
mappings "net.fabricmc:yarn:1.16.5+build.5:v2"
modImplementation "net.fabricmc:fabric-loader:0.11.2"
modImplementation System.getProperty("loom.test.resolve")
}

View File

@@ -0,0 +1,12 @@
package net.fabricmc.examplemod;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.example.ExampleLib;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// Lets make sure we can compile against the lib
ExampleLib.hello();
}
}

View File

@@ -0,0 +1,52 @@
plugins {
id 'fabric-loom'
id 'maven-publish'
}
archivesBaseName = "fabric-example-lib"
version = System.getProperty("loom.test.version")
group = "com.example"
println archivesBaseName
dependencies {
minecraft "com.mojang:minecraft:1.16.5"
mappings "net.fabricmc:yarn:1.16.5+build.5:v2"
modImplementation "net.fabricmc:fabric-loader:0.11.2"
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
java {
withSourcesJar()
}
publishing {
publications {
mavenJava(MavenPublication) {
artifact(remapJar) {
builtBy remapJar
}
artifact(remapJar) {
builtBy remapJar
classifier "classifier"
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
repositories {
maven {
url "http://localhost:${System.getProperty("loom.test.mavenPort")}/"
allowInsecureProtocol = true
}
}
}

View File

@@ -0,0 +1,2 @@
rootProject.name = "fabric-example-lib"

View File

@@ -0,0 +1,13 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleLib implements ModInitializer {
@Override
public void onInitialize() {
}
public static void hello() {
System.out.println("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,4 @@
{
"schemaVersion": 1,
"id": "modid"
}

View File

@@ -0,0 +1,92 @@
plugins {
id 'fabric-loom'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
// We'll use that if it's available, but otherwise we'll use the older option.
def targetVersion = 8
if (JavaVersion.current().isJava9Compatible()) {
it.options.release = targetVersion
}
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven
artifact(remapJar) {
builtBy remapJar
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

View File

@@ -0,0 +1,17 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.16.5
yarn_mappings=1.16.5+build.5
loader_version=0.11.2
# Mod Properties
mod_version = 1.0.0
maven_group = com.example
archives_base_name = fabric-example-mod
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version=0.31.0+1.16

View File

@@ -0,0 +1,2 @@
rootProject.name = "fabric-example-mod"

View File

@@ -0,0 +1,14 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,33 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"suggests": {
"another-mod": "*"
}
}

View File

@@ -0,0 +1,63 @@
plugins {
id "java"
id "java-library"
id 'fabric-loom' apply false
}
allprojects {
apply plugin: "fabric-loom"
version = "1.0.0"
tasks.withType(JavaCompile).configureEach {
def targetVersion = 8
if (JavaVersion.current().isJava9Compatible()) {
it.options.release = targetVersion
} else {
sourceCompatibility = JavaVersion.toVersion(targetVersion)
targetCompatibility = JavaVersion.toVersion(targetVersion)
}
it.options.encoding = "UTF-8"
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:1.16.5"
mappings "net.fabricmc:yarn:1.16.5+build.5:v2"
modImplementation "net.fabricmc:fabric-loader:0.11.2"
modImplementation "net.fabricmc.fabric-api:fabric-api:0.31.0+1.16"
}
configurations {
dev
}
jar {
archiveClassifier.set "dev"
}
loom {
shareCaches = true
}
java {
withSourcesJar()
}
}
dependencies {
afterEvaluate {
subprojects.each {
include project("${it.name}:")
}
}
include fabricApi.module("fabric-api-base", "0.31.0+1.16")
}
archivesBaseName = "multiproject"
version = "1.0.0"

View File

@@ -0,0 +1 @@
archivesBaseName = "core"

View File

@@ -0,0 +1,14 @@
package net.fabricmc.core;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,33 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.core.ExampleMod"
]
},
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"suggests": {
"another-mod": "*"
}
}

View File

@@ -0,0 +1,5 @@
archivesBaseName = "example"
dependencies {
implementation project(path: ":core", configuration: "dev")
}

View File

@@ -0,0 +1,14 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@@ -0,0 +1,36 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"mixins": [
"modid.mixins.json"
],
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"suggests": {
"another-mod": "*"
}
}

View File

@@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"ExampleMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -0,0 +1,17 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.16.5
yarn_mappings=1.16.5+build.5
loader_version=0.11.2
# Mod Properties
mod_version = 1.0.0
maven_group = com.example
archives_base_name = fabric-example-mod
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version=0.31.0+1.16

View File

@@ -0,0 +1,4 @@
rootProject.name = "fabric-example-mod"
include 'core'
include 'example'

View File

@@ -0,0 +1,6 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "1.0.0",
"name": "Example Mod"
}

View File

@@ -0,0 +1,55 @@
plugins {
id 'fabric-loom'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8"
def targetVersion = 8
if (JavaVersion.current().isJava9Compatible()) {
it.options.release = targetVersion
}
}
java {
withSourcesJar()
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
}
// Make Jars Reproducible
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
remapSourcesJar {
preserveFileTimestamps = false
reproducibleFileOrder = true
}

Some files were not shown because too many files have changed in this diff Show More