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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
matrix: ${{ fromJson(needs.setup.outputs.matrix) }}
runs-on: ${{ matrix.os }}
permissions:
checks: write
contents: read
env:
JAVA_TOOL_OPTIONS: >-
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ WurstSetup/proguard.map
de.peeeq.wurstscript/output.txt
/HelperScripts/gamedata
/HelperScripts/.gradle
/.gradle-user-home
/gradle-home-temp
44 changes: 43 additions & 1 deletion de.peeeq.wurstscript/deploy.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def jreImageDir = layout.buildDirectory.dir("jre-wurst-25")

def distRoot = layout.buildDirectory.dir("dist/slim-${plat}")
def releasesDir = layout.buildDirectory.dir("releases")
def appCdsArchive = distRoot.map { it.file("wurst-runtime/wurst-lsp.jsa") }

// ----------------------- Toolchain / tool paths (providers) -------------------------
def toolchainSvc = extensions.getByType(JavaToolchainService)
Expand Down Expand Up @@ -223,11 +224,52 @@ tasks.named("assembleSlimCompilerDist", Copy) { t ->
}
}

tasks.register("generateAppCdsArchive", Exec) {
description = "Best-effort AppCDS archive generation for language-server startup."
group = "distribution"
dependsOn("assembleSlimCompilerDist")

inputs.file(fatJar)
inputs.dir(distRoot)
outputs.file(appCdsArchive)
ignoreExitValue = true

doFirst {
def distDir = distRoot.get().asFile
def archiveFile = appCdsArchive.get().asFile
def runtimeJava = new File(distDir, "wurst-runtime/bin/java${os.isWindows() ? '.exe' : ''}")
def compilerJar = new File(distDir, "wurst-compiler/${fatJar.get().asFile.name}")

archiveFile.parentFile.mkdirs()
if (archiveFile.exists()) {
archiveFile.delete()
}

executable = runtimeJava.absolutePath
args "-Xshare:off",
Comment thread
Frotty marked this conversation as resolved.
Outdated
"-XX:ArchiveClassesAtExit=${archiveFile.absolutePath}",
Comment thread
Frotty marked this conversation as resolved.
"-jar", compilerJar.absolutePath,
"-languageServer"
standardInput = new ByteArrayInputStream(new byte[0])
Comment thread
Frotty marked this conversation as resolved.
Outdated

logger.lifecycle("[appcds] Training LS startup with ${runtimeJava.absolutePath}")
}

doLast {
def archiveFile = appCdsArchive.get().asFile
if (executionResult.get().exitValue != 0 || !archiveFile.exists()) {
logger.warn("[appcds] Archive generation did not succeed on ${plat}; continuing without shipped AppCDS archive.")
} else {
logger.lifecycle("[appcds] Wrote ${archiveFile.absolutePath}")
}
}
}

// 4) Package ZIP on all platforms
tasks.register("packageSlimCompilerDistZip", Zip) {
description = "Packages slim dist as a ZIP archive (all platforms)."
group = "distribution"
dependsOn("assembleSlimCompilerDist")
dependsOn("generateAppCdsArchive")

from(distRoot)
destinationDirectory.set(releasesDir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,9 +616,11 @@ public static void exportToWurst(List<? extends ObjMod.Obj> customObjs, ObjectFi
* <p>Fields that have no known wrapper method are emitted as commented-out raw
* calls so the output is still useful even when coverage is incomplete.
*
* <p>Returns {@code false} only when there is no wrapper class at all for this
* <p>Returns {@code false} when there is no wrapper class at all for this
* object type / base ID (e.g. doodads, upgrades, or an unknown ability base ID),
* in which case the caller should fall back to the fully raw format.
* or when a mapped wrapper setter exists but its parameter type is not yet
* supported for source re-emission. In those cases the caller should fall back
* to the fully raw format so no object data is lost on re-export.
*/
private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileType,
String newId, String oldId,
Expand Down Expand Up @@ -677,11 +679,8 @@ private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileT
return false;
}

for (ObjMod.Obj.Mod m : mods) {
StdlibObjectMappings.FieldMethodInfo info = fieldMethods.get(fieldKey(m, fileType));
if (info != null && !canUseWrapperForMod(m, info)) {
return false;
}
if (hasUnsupportedMappedWrapperField(mods, fieldMethods, fileType)) {
return false;
}

out.append("@compiletime function create_").append(fileType.getExt()).append("_").append(newId)
Expand All @@ -690,7 +689,7 @@ private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileT

for (ObjMod.Obj.Mod m : mods) {
StdlibObjectMappings.FieldMethodInfo info = fieldMethods.get(fieldKey(m, fileType));
if (info != null) {
if (info != null && canUseWrapperForMod(m, info)) {
Comment thread
Frotty marked this conversation as resolved.
out.append("\t..").append(info.methodName()).append("(");
if (info.hasLevel() && m instanceof ObjMod.Obj.ExtendedMod) {
out.append(String.valueOf(((ObjMod.Obj.ExtendedMod) m).getLevel())).append(", ");
Expand All @@ -707,6 +706,21 @@ private static boolean tryExportWithWrapper(Appendable out, ObjectFileType fileT
return true;
}

private static boolean hasUnsupportedMappedWrapperField(List<ObjMod.Obj.Mod> mods,
Map<String, StdlibObjectMappings.FieldMethodInfo> fieldMethods,
ObjectFileType fileType) {
for (ObjMod.Obj.Mod mod : mods) {
StdlibObjectMappings.FieldMethodInfo info = fieldMethods.get(fieldKey(mod, fileType));
if (info == null) {
continue;
}
if (!canUseWrapperForMod(mod, info)) {
return true;
}
}
return false;
}

/**
* Returns the lookup key used to match a mod to a {@link StdlibObjectMappings.FieldMethodInfo}.
* Uses the mod's actual dataPtr when it is an {@link ObjMod.Obj.ExtendedMod}, regardless of
Expand All @@ -724,8 +738,7 @@ static String fieldKey(ObjMod.Obj.Mod m, ObjectFileType fileType) {
}

private static boolean canUseWrapperForMod(ObjMod.Obj.Mod m, StdlibObjectMappings.FieldMethodInfo info) {
if (!info.parameterType().isEmpty() && !isPrimitiveParameter(info.parameterType())
&& !isEnumParameter(info.parameterType())) {
if (!supportsWrapperParameterType(info.parameterType())) {
return false;
}
if (isEnumParameter(info.parameterType())) {
Expand All @@ -734,6 +747,12 @@ private static boolean canUseWrapperForMod(ObjMod.Obj.Mod m, StdlibObjectMapping
return true;
}

private static boolean supportsWrapperParameterType(String parameterType) {
return parameterType.isEmpty()
|| isPrimitiveParameter(parameterType)
|| isEnumParameter(parameterType);
}

private static boolean isEnumParameter(String parameterType) {
return ENUM_OBJECT_STRING_TO_CONSTANT.containsKey(parameterType);
}
Expand Down Expand Up @@ -848,6 +867,7 @@ private static Map<String, String> enumConstants(String... valueConstantPairs) {
"mline", "MissileLine"
),
"WeaponSound", enumConstants(
"", "Nothing",
Comment thread
Frotty marked this conversation as resolved.
"Nothing", "Nothing",
"AxeMediumChop", "AxeMediumChop",
"MetalHeavyBash", "MetalHeavyBash",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void setRootPath(WFile rootPath) {
lock.notify();
}
});
private boolean initialBuildPending = false;
private final BufferManager bufferManager = new BufferManager();
private LanguageClient languageClient;

Expand Down Expand Up @@ -200,9 +201,6 @@ private Workitem getNextWorkItem() {
changesToReconcile = ModelManager.Changes.empty();
reconcileNowRequested = false;
return new Workitem("reconcile files (save)", () -> modelManager.reconcile(changes));
} else if (!userRequests.isEmpty()) {
UserRequest<?> req = userRequests.remove();
return new Workitem(req.toString(), () -> req.run(modelManager));
} else if (!changes.isEmpty()) {
// TODO this can be done more efficiently than doing one at a time
PendingChange change = removeFirst(changes);
Expand Down Expand Up @@ -246,6 +244,12 @@ private Workitem getNextWorkItem() {
return new Workitem("reconcile files", () -> {
modelManager.reconcile(changes);
});
} else if (!userRequests.isEmpty()) {
UserRequest<?> req = userRequests.remove();
return new Workitem(req.toString(), () -> req.run(modelManager));
} else if (initialBuildPending) {
Comment thread
Frotty marked this conversation as resolved.
Outdated
initialBuildPending = false;
return new Workitem("initial full build", () -> doInitialBuild());
Comment thread
Frotty marked this conversation as resolved.
}
return null;
}
Expand All @@ -271,11 +275,20 @@ private void doInit(WFile rootPath) {
log("Handle init " + rootPath);
modelManager = new ModelManagerImpl(rootPath.getFile(), bufferManager);
modelManager.onCompilationResult(this::onCompilationResult);
initialBuildPending = true;
} catch (Exception e) {
WLogger.severe(e);
}
}

log("Start building " + rootPath);
private void doInitialBuild() {
try {
if (modelManager == null || rootPath == null) {
return;
}
log("Start background full build " + rootPath);
modelManager.buildProject();

log("Finished building " + rootPath);
log("Finished background full build " + rootPath);
} catch (Exception e) {
WLogger.severe(e);
}
Expand Down
Loading
Loading