Compare commits

...

7 Commits

Author SHA1 Message Date
Todd 7324cb124d
Merge 167fd6b840 into 929debd382 2024-04-25 00:27:24 -04:00
Jonathan Coates 929debd382
Don't build the webside on Windows/Mac
It seems to stall on Mac, and unlike Windows, I don't have access to a
machine to debug it :/.
2024-04-24 21:49:49 +01:00
Jonathan Coates 4980b7355d
Don't share CharsetDecoders across threads
Fixes #1803
2024-04-24 21:19:30 +01:00
Jonathan Coates 925092add3
Fix build on Windows
- Force encoding to UTF-8
 - Fix npm not being found on the path
 - Test building common/web on OSX and Windows
2024-04-24 17:55:48 +01:00
hugeblank 167fd6b840 Correctly display shell errors on native term
Extracts os.run into an internal launch function so we can extract shell errors and correctly display them on the native terminal.
2024-04-19 14:35:12 -07:00
hugeblank 175de5504e trailing comma
I learned how to lintLua!
2024-04-19 12:30:58 -07:00
hugeblank 6d32c98a69 Add setting bios.shell_path
Allow the user to directly set the path the bios uses for the shell.

Has some additional status checking to allow for shell errors to be read before computer shutdown.
2024-04-19 10:12:08 -07:00
9 changed files with 90 additions and 43 deletions

View File

@ -58,13 +58,13 @@ jobs:
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
- name: 📤 Upload Jar
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: CC-Tweaked
path: ./jars
- name: 📤 Upload coverage
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
build-core:
strategy:
@ -81,24 +81,28 @@ jobs:
runs-on: ${{ matrix.uses }}
steps:
- name: Clone repository
- name: 📥 Clone repository
uses: actions/checkout@v4
- name: Set up Java
- name: 📥 Set up Java
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Setup Gradle
- name: 📥 Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
- name: Run tests
- name: ⚒️ Build
run: |
./gradlew --configure-on-demand :core:assemble
- name: 🧪 Run tests
run: |
./gradlew --configure-on-demand :core:test
- name: Parse test reports
- name: 🧪 Parse test reports
run: python3 ./tools/parse-reports.py
if: ${{ failure() }}

View File

@ -29,9 +29,9 @@ ## Setting up a development environment
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
- Make sure you've got the following software installed:
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
- Java Development Kit (JDK). This can be downloaded from [Adoptium].
- [Git](https://git-scm.com/).
- If you want to work on documentation, [NodeJS][node].
- [NodeJS][node].
- Download CC: Tweaked's source code:
```

View File

@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() {
@TaskAction
fun install() {
project.exec {
commandLine("npm", "ci")
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
workingDir = projectRoot.get().asFile
}
}
@ -59,6 +59,6 @@ fun install() {
abstract class NpxExecToDir : ExecToDir() {
init {
dependsOn(NpmInstall.TASK_NAME)
executable = "npx"
executable = ProcessHelpers.getExecutable("npx")
}
}

View File

@ -9,6 +9,7 @@
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
internal object ProcessHelpers {
fun startProcess(vararg command: String): Process {
@ -34,7 +35,7 @@ fun captureLines(vararg command: String): List<String> {
val process = startProcess(*command)
process.outputStream.close()
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader ->
reader.lines().filter { it.isNotEmpty() }.toList()
}
ProcessGroovyMethods.closeStreams(process)
@ -46,6 +47,28 @@ fun onPath(name: String): Boolean {
val path = System.getenv("PATH") ?: return false
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
}
/**
* Search for an executable on the `PATH` if required.
*
* [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
* Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
*/
fun getExecutable(name: String): String {
if (!System.getProperty("os.name").lowercase().contains("windows")) return name
val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
for (pathEntry in path) {
for (ext in pathExt) {
val resolved = File(pathEntry, name + ext)
if (resolved.exists()) return resolved.getAbsolutePath()
}
}
return name
}
}
internal fun Process.waitForOrThrow(message: String) {

View File

@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: MPL-2.0
org.gradle.jvmargs=-Xmx3G
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8
org.gradle.parallel=true
kotlin.stdlib.default.dependency=false

View File

@ -29,7 +29,7 @@
* @see dan200.computercraft.core.apis.HTTPAPI#websocket On how to open a websocket.
*/
public class WebsocketHandle {
private static final CharsetDecoder DECODER = StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPLACE);
private static final ThreadLocal<CharsetDecoder> DECODER = ThreadLocal.withInitial(() -> StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPLACE));
private final IAPIEnvironment environment;
private final String address;
@ -87,7 +87,7 @@ public final void send(Coerced<ByteBuffer> message, Optional<Boolean> binary) th
websocket.sendBinary(text);
} else {
try {
websocket.sendText(DECODER.decode(text).toString());
websocket.sendText(DECODER.get().decode(text).toString());
} catch (CharacterCodingException e) {
// This shouldn't happen, but worth mentioning.
throw new LuaException("Message is not valid UTF8");

View File

@ -450,10 +450,7 @@ function dofile(_sFile)
end
-- Install the rest of the OS api
function os.run(_tEnv, _sPath, ...)
expect(1, _tEnv, "table")
expect(2, _sPath, "string")
local function launch(_tEnv, _sPath, ...)
local tEnv = _tEnv
setmetatable(tEnv, { __index = _G })
@ -470,17 +467,21 @@ function os.run(_tEnv, _sPath, ...)
if fnFile then
local ok, err = pcall(fnFile, ...)
if not ok then
if err and err ~= "" then
printError(err)
end
return false
return false, err
end
return true
end
return false, err
end
function os.run(_tEnv, _sPath, ...)
expect(1, _tEnv, "table")
expect(2, _sPath, "string")
local ok, err = launch(_tEnv, _sPath, ...)
if err and err ~= "" then
printError(err)
end
return false
return ok
end
local tAPIsLoading = {}
@ -697,6 +698,20 @@ if term.isColour() then
type = "boolean",
})
end
local sShell
if term.isColour() and settings.get("bios.use_multishell") then
sShell = "rom/programs/advanced/multishell.lua"
else
sShell = "rom/programs/shell.lua"
end
settings.define("bios.shell_path", {
default = sShell,
description = "The path the bios executes as the shell. This program is responsible for implementing the shell and multishell API, handling user input, and program execution.",
type = "string",
})
if _CC_DEFAULT_SETTINGS then
for sPair in string.gmatch(_CC_DEFAULT_SETTINGS, "[^,]+") do
local sName, sValue = string.match(sPair, "([^=]*)=(.*)")
@ -728,24 +743,25 @@ if fs.exists(".settings") then
end
-- Run the shell
local shellOk, shellErr
local ok, err = pcall(parallel.waitForAny,
function()
local sShell
if term.isColour() and settings.get("bios.use_multishell") then
sShell = "rom/programs/advanced/multishell.lua"
else
sShell = "rom/programs/shell.lua"
shellOk, shellErr = launch({}, settings.get("bios.shell_path"))
if shellOk then
os.run({}, "rom/programs/shutdown.lua")
end
os.run({}, sShell)
os.run({}, "rom/programs/shutdown.lua")
end,
rednet.run
)
-- If the shell errored, let the user read it.
term.redirect(term.native())
if not ok then
printError(err)
if not (ok and shellOk) then
if not ok then
printError(err)
elseif not shellOk then
printError(shellErr)
end
pcall(function()
term.setCursorBlink(false)
print("Press any key to continue")

View File

@ -213,16 +213,16 @@ end
-- Existing settings will be merged with any pre-existing ones. Conflicting
-- entries will be overwritten, but any others will be preserved.
--
-- @tparam[opt] string sPath The file to load from, defaulting to `.settings`.
-- @tparam[opt=".settings"] string path The file to load from.
-- @treturn boolean Whether settings were successfully read from this
-- file. Reasons for failure may include the file not existing or being
-- corrupted.
--
-- @see settings.save
-- @changed 1.87.0 `sPath` is now optional.
function load(sPath)
expect(1, sPath, "string", "nil")
local file = fs.open(sPath or ".settings", "r")
-- @changed 1.87.0 `path` is now optional.
function load(path)
expect(1, path, "string", "nil")
local file = fs.open(path or ".settings", "r")
if not file then
return false
end
@ -255,14 +255,14 @@ end
-- This will entirely overwrite the pre-existing file. Settings defined in the
-- file, but not currently loaded will be removed.
--
-- @tparam[opt] string sPath The path to save settings to, defaulting to `.settings`.
-- @tparam[opt=".settings"] string path The path to save settings to.
-- @treturn boolean If the settings were successfully saved.
--
-- @see settings.load
-- @changed 1.87.0 `sPath` is now optional.
function save(sPath)
expect(1, sPath, "string", "nil")
local file = fs.open(sPath or ".settings", "w")
-- @changed 1.87.0 `path` is now optional.
function save(path)
expect(1, path, "string", "nil")
local file = fs.open(path or ".settings", "w")
if not file then
return false
end

View File

@ -68,6 +68,10 @@ public void remapClass(String from, String to) {
}
private @Nullable Path findUnmappedFile(String name) {
// For some odd reason, we try to resolve the generated lambda classes. This includes classes called
// things like <linit>lambda, which is an invalid path on Windows. Detect those, and abort early.
if (name.indexOf("<") >= 0) return null;
return classpath.stream().map(x -> x.resolve(name)).filter(Files::exists).findFirst().orElse(null);
}