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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
import de.peeeq.wurstscript.intermediatelang.interpreter.ILStackFrame;
import de.peeeq.wurstscript.jassAst.JassProg;
import de.peeeq.wurstscript.jassprinter.JassPrinter;
import de.peeeq.wurstscript.luaAst.LuaCompilationUnit;
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
import de.peeeq.wurstscript.translation.lua.translation.LuaTranslator;
import de.peeeq.wurstscript.utils.Utils;
import org.eclipse.jdt.annotation.Nullable;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Supplier;

Expand Down Expand Up @@ -82,35 +85,53 @@ public CompilationProcess(WurstGui gui, RunArgs runArgs) {

if (runArgs.isRunTests()) {
timeTaker.measure("Run tests",
() -> runTests(compiler.getImTranslator(), compiler, runArgs.getTestTimeout(), runArgs.getTestFilter()));
() -> runTests(gui, compiler, runArgs));
}

timeTaker.measure("Run compiletime functions", () ->compiler.runCompiletime(WurstProjectConfigData.empty(), isProd, false));

JassProg jassProg = timeTaker.measure("Transform program to Jass",
compiler::transformProgToJass);
CharSequence mapScript;
File outputMapscript;
if (runArgs.isLua()) {
LuaCompilationUnit luaCode = timeTaker.measure("Transform program to Lua",
compiler::transformProgToLua);
if (luaCode == null || gui.getErrorCount() > 0) {
return null;
}

if (jassProg == null || gui.getErrorCount() > 0) {
return null;
}
gui.sendProgress("Printing Lua");
StringBuilder luaOutput = new StringBuilder();
timeTaker.measure("Print Lua", () -> luaCode.print(luaOutput, 0));
mapScript = luaOutput;
LuaTranslator.assertNoLeakedHashtableNativeCalls(mapScript.toString());
LuaTranslator.assertNoLeakedGetHandleIdCalls(mapScript.toString());
CharSequence compiledLua = mapScript;
outputMapscript = timeTaker.measure("Write Lua",
() -> writeMapscript(compiledLua));
} else {
JassProg jassProg = timeTaker.measure("Transform program to Jass",
compiler::transformProgToJass);

boolean withSpace;
withSpace = !runArgs.isOptimize();
if (jassProg == null || gui.getErrorCount() > 0) {
return null;
}

gui.sendProgress("Printing Jass");
boolean withSpace = !runArgs.isOptimize();
gui.sendProgress("Printing Jass");

JassPrinter printer = new JassPrinter(withSpace, jassProg);
CharSequence mapScript = timeTaker.measure("Print Jass",
(Supplier<String>) printer::printProg);
JassPrinter printer = new JassPrinter(withSpace, jassProg);
mapScript = timeTaker.measure("Print Jass",
(Supplier<String>) printer::printProg);

// output to file
File outputMapscript = timeTaker.measure("Print Jass",
() -> writeMapscript(mapScript));
CharSequence compiledJass = mapScript;
outputMapscript = timeTaker.measure("Write Jass",
() -> writeMapscript(compiledJass));

if (!runArgs.isDisablePjass() && !runArgs.isLegacyJassTypeChecks()) {
boolean pjassError = timeTaker.measure("Run PJass",
if (!runArgs.isDisablePjass() && !runArgs.isLegacyJassTypeChecks()) {
boolean pjassError = timeTaker.measure("Run PJass",
() -> runPjass(outputMapscript));
if (pjassError) return null;
if (pjassError) return null;
}
}
timeTaker.printReport();
return mapScript;
Expand Down Expand Up @@ -139,28 +160,38 @@ private boolean runPjass(File outputMapscript) {
private File writeMapscript(CharSequence mapScript) {
gui.sendProgress("Writing output file");
File outputMapscript;
File staleJassOutput = null;
if (runArgs.getOutFile() != null) {
outputMapscript = new File(runArgs.getOutFile());
String outputPath = runArgs.getOutFile();
if (runArgs.isLua() && outputPath.toLowerCase(Locale.ROOT).endsWith(".j")) {
staleJassOutput = new File(outputPath);
outputPath = outputPath.substring(0, outputPath.length() - 2) + ".lua";
Comment on lines +166 to +168

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove stale Jass output when redirecting Lua builds

When a user previously compiled Jass to -out output.j and then reruns the same command with -lua, this branch writes output.lua but leaves the old output.j untouched. Downstream tools that still inspect the explicitly requested path can therefore consume obsolete Jass, and the new guarantee that Lua mode does not leave a .j artifact only holds in a clean directory. Delete the superseded file or write the Lua output to the exact requested path.

Useful? React with 👍 / 👎.

}
outputMapscript = new File(outputPath);
} else {
outputMapscript = new File("./temp/output.j");
outputMapscript = new File("./temp/output." + (runArgs.isLua() ? "lua" : "j"));
}
outputMapscript.getParentFile().mkdirs();
try {
if (staleJassOutput != null) {
java.nio.file.Files.deleteIfExists(staleJassOutput.toPath());
}
FileUtils.write(mapScript, outputMapscript);
return outputMapscript;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private void runTests(ImTranslator translator, WurstCompilerJassImpl compiler, int testTimeout, Optional<String> testFilter) {
public static void runTests(WurstGui gui, WurstCompilerJassImpl compiler, RunArgs runArgs) {
ImTranslator translator = compiler.getImTranslator();
PrintStream out = System.out;
// tests
gui.sendProgress("Running tests");
if (!runArgs.isCompactOutput()) {
System.out.println("Running tests");
}
RunTests runTests = new RunTests(Optional.empty(), 0, 0, Optional.empty(), testTimeout, testFilter, runArgs.isCompactOutput()) {
RunTests runTests = new RunTests(Optional.empty(), 0, 0, Optional.empty(), runArgs.getTestTimeout(), runArgs.getTestFilter(), runArgs.isCompactOutput()) {
@Override
protected void print(String message) {
out.print(message);
Expand Down
40 changes: 14 additions & 26 deletions de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package de.peeeq.wurstio;

import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.wurstscript.projectconfig.WurstProjectConfigData;
import org.wurstscript.projectconfig.WurstProjectConfigReader;
import de.peeeq.wurstio.compilationserver.WurstServer;
Expand All @@ -11,8 +9,6 @@
import de.peeeq.wurstio.languageserver.WFile;
import de.peeeq.wurstio.languageserver.requests.CliBuildMap;
import de.peeeq.wurstio.map.importer.ImportFile;
import de.peeeq.wurstio.mpq.MpqEditor;
import de.peeeq.wurstio.mpq.MpqEditorFactory;
import de.peeeq.wurstio.objectreader.ObjectExportService;
import de.peeeq.wurstscript.CompileTimeInfo;
import de.peeeq.wurstscript.ErrorReporting;
Expand Down Expand Up @@ -147,11 +143,19 @@ public static void main(String[] args) {
compileArgs = new RunArgs(mergedArgs);
}

if (runArgs.isBuild() && runArgs.getInputmap() != null && workspaceroot != null) {
if (workspaceroot != null) {
Path root = Paths.get(workspaceroot);
Path inputMap = root.resolve(runArgs.getInputmap());
Path inputMap = runArgs.isBuild() && runArgs.getInputmap() != null
? root.resolve(runArgs.getInputmap())
: runArgs.getMapFile() == null ? null : Paths.get(runArgs.getMapFile());
WurstProjectConfigData projectConfig = WurstProjectConfigReader.load(root.resolve(FILE_NAME));
if (java.nio.file.Files.exists(inputMap) && projectConfig != null) {
if (inputMap != null) {
if (!java.nio.file.Files.exists(inputMap)) {
throw new RuntimeException("Input map does not exist: " + inputMap);
}
if (projectConfig == null) {
throw new RuntimeException(FILE_NAME + " file doesn't exist or is invalid.");
}
CliBuildMap cliBuildMap = new CliBuildMap(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve requested tests in workspace map builds

When the CLI is invoked with -runtests -workspaceroot <root> <map.w3x> without -build, this broadened routing now constructs CliBuildMap and returns after its pipeline finishes. MapRequest.compileMap never checks isRunTests() or calls RunTests, whereas the replaced CompilationProcess.doCompilation path did, so failing tests are silently skipped and the command reports a successful build. Execute requested tests from the shared map pipeline before reporting success.

AGENTS.md reference: AGENTS.md:L162-L164

Useful? React with 👍 / 👎.

WFile.create(root.toFile()),
Optional.of(inputMap.toFile()),
Expand All @@ -170,30 +174,14 @@ public static void main(String[] args) {
}
}

String mapFilePath = runArgs.getMapFile();

CompilationProcess compilationProcess = new CompilationProcess(gui, compileArgs);
@Nullable CharSequence compiledScript;

if (mapFilePath != null && workspaceroot != null) {
try (MpqEditor mpqEditor = MpqEditorFactory.getEditor(Optional.of(new File(mapFilePath)))) {
File projectFolder = Paths.get(workspaceroot).toFile();
compiledScript = compilationProcess.doCompilation(mpqEditor, projectFolder, true);
if (compiledScript != null) {
gui.sendProgress("Writing to map");
mpqEditor.deleteFile("war3map.j");
byte[] war3map = compiledScript.toString().getBytes(Charsets.UTF_8);
mpqEditor.insertFile("war3map.j", war3map);
}
ImportFile.importFilesFromImports(projectFolder, mpqEditor);
}
} else {
compiledScript = compilationProcess.doCompilation(null, true);
}
compiledScript = compilationProcess.doCompilation(null, true);

if (compiledScript != null) {
File scriptFile = new File("compiled.j.txt");
Files.write(compiledScript.toString().getBytes(Charsets.UTF_8), scriptFile);
File scriptFile = new File(compileArgs.isLua() ? "compiled.lua.txt" : "compiled.j.txt");
java.nio.file.Files.writeString(scriptFile.toPath(), compiledScript);
}

gui.sendProgress("Finished!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.wurstscript.projectconfig.WurstProjectConfigData;
import de.peeeq.wurstio.CompilationProcess;
import de.peeeq.wurstio.Pjass;
import de.peeeq.wurstio.TimeTaker;
import de.peeeq.wurstio.UtilsIO;
Expand Down Expand Up @@ -172,6 +173,13 @@ protected File compileMap(File projectFolder, WurstGui gui, Optional<File> mapCo
throw new RequestFailedException(MessageType.Error, "Could not compile project (error in translation): " + gui.getErrorList().get(0));
}

if (runArgs.isRunTests()) {
CompilationProcess.runTests(gui, compiler, runArgs);
if (gui.getErrorCount() > 0) {
throw new RequestFailedException(MessageType.Error, "Could not compile project: tests failed.");
}
}

timeTaker.measure("Runinng Compiletime Functions", () -> compiler.runCompiletime(projectConfigData, isProd, runArgs.isCompiletimeCache()));

if (runArgs.isLua()) {
Expand Down Expand Up @@ -768,6 +776,7 @@ protected File executeBuildMapPipeline(ModelManager modelManager, WurstGui gui,

CompilationResult result = compileScript(modelManager, gui, Optional.of(targetMapFile), projectConfig, buildDir, isProductionBuild());
injectMapData(gui, Optional.of(targetMapFile), result);
writeRequestedScript(result);

targetMapFile = ensureWritableBuildOutput(targetMapFile, true);
java.nio.file.Files.copy(getCachedMapFile().toPath(), targetMapFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
Expand All @@ -787,6 +796,30 @@ protected File executeBuildMapPipeline(ModelManager modelManager, WurstGui gui,
return targetMapFile;
}

private void writeRequestedScript(CompilationResult result) throws IOException {
if (runArgs.getOutFile() == null || result.script == null) {
return;
}

String outputPath = runArgs.getOutFile();
File outputFile = new File(outputPath);
if (runArgs.isLua() && outputPath.toLowerCase(Locale.ROOT).endsWith(".j")) {
java.nio.file.Files.deleteIfExists(outputFile.toPath());
outputPath = outputPath.substring(0, outputPath.length() - 2) + ".lua";
outputFile = new File(outputPath);
}

File parent = outputFile.getParentFile();
if (parent != null) {
parent.mkdirs();
}
java.nio.file.Files.copy(
result.script.toPath(),
outputFile.toPath(),
java.nio.file.StandardCopyOption.REPLACE_EXISTING
);
}

protected boolean isProductionBuild() {
return !runArgs.isDevBuild();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.peeeq.wurstio;

import de.peeeq.wurstscript.RunArgs;
import de.peeeq.wurstscript.gui.WurstGuiCliImpl;
import org.testng.annotations.Test;

import java.nio.file.Files;
import java.nio.file.Path;

import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

public class CompilationProcessLuaTests {

@Test
public void luaModeDoesNotEmitJassScript() throws Exception {
Path project = Files.createTempDirectory("wurst-cli-lua");
Path source = project.resolve("Main.wurst");
Path requestedJassOutput = project.resolve("output.j");
Path output = project.resolve("output.lua");
Files.writeString(source, "package Main\nfunction foo()\nendpackage\n");
Files.writeString(requestedJassOutput, "stale jass output");

RunArgs runArgs = new RunArgs("-lua", "-out", requestedJassOutput.toString(), source.toString());
CompilationProcess process = new CompilationProcess(new WurstGuiCliImpl(true), runArgs);

CharSequence result = process.doCompilation(null, project.toFile(), false);

assertTrue(result != null, "Lua compilation should succeed");
assertTrue(Files.exists(output), "Lua output should be written");
assertFalse(Files.exists(requestedJassOutput), "Lua compilation must not emit a .j file");
assertFalse(result.toString().contains("takes nothing returns nothing"),
"CLI Lua compilation must not use the Jass backend");
}
}
Loading