Skip to content

Commit

Permalink
Merge pull request #657 from godot-rust/bugfix/macos-reload-ci
Browse files Browse the repository at this point in the history
More reliable macOS hot-reloading test, workaround missing GDExtension methods
  • Loading branch information
Bromeon authored Apr 6, 2024
2 parents 824cb6c + add0902 commit e7dd8b8
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .github/composite/godot-install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ runs:

# - name: "Check cache for installed Godot version"
# id: "cache-godot"
# uses: actions/cache@v3
# uses: actions/cache@v4
# with:
# path: ${{ runner.temp }}/godot_bin
# key: ${{ inputs.artifact-name }}-v${{ inputs.godot-ver }}
Expand Down
2 changes: 1 addition & 1 deletion .github/composite/llvm/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ runs:
id: cache-llvm
# Note: conditionals not yet supported; see https://github.com/actions/runner/issues/834
# if: inputs.llvm == 'true'
uses: actions/cache@v3
uses: actions/cache@v4
with:
# path: |
# C:/Program Files/LLVM
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/full-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ jobs:
godot-binary: ${{ matrix.godot-binary }}
godot-args: ${{ matrix.godot-args }} # currently unused
godot-prebuilt-patch: ${{ matrix.godot-prebuilt-patch }}
rust-extra-args: ${{ matrix.rust-extra-args }} --features godot/codegen-full
rust-extra-args: ${{ matrix.rust-extra-args }}
rust-toolchain: ${{ matrix.rust-toolchain || 'stable' }}
rust-env-rustflags: ${{ matrix.rust-env-rustflags }}
rust-target: ${{ matrix.rust-target }}
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Cargo.lock

# Needed to run projects without having to open the editor first.
!**/.godot/extension_list.cfg
!**/.godot/global_script_class_cache.cfg

# This project: input JSONs and code generation
gen
Expand Down

This file was deleted.

This file was deleted.

120 changes: 120 additions & 0 deletions examples/hot-reload/godot/test/ReloadOrchestrator.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Copyright (c) godot-rust; Bromeon and contributors.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

# See https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html#running-a-script.

class_name ReloadOrchestrator
extends SceneTree

const TIMEOUT_S = 30
const TO_GODOT_PORT = 1337
const FROM_GODOT_PORT = 1338

var run_main_loop: bool = false
var elapsed: float = 0.0
var udp := PacketPeerUDP.new()
var args: Array # must be Array for match, not PackedStringArray.

func _initialize():
args = OS.get_cmdline_user_args()
print("[GD Orch] Start ", args)

var ok := true
match args:
["await"]:
ok = receive_udp()
run_main_loop = true

["replace"]:
print("[GD Orch] Replace source code...")
ok = replace_line("../../rust/src/lib.rs")

["notify"]:
print("[GD Orch] Notify Godot about change...")
ok = send_udp()

_:
fail("Invalid command-line args")
ok = false

if not ok:
quit(1)
return


func _finalize():
udp.close()
print("[GD Orch] Stop ", args)

func _process(delta: float) -> bool:
if not run_main_loop:
return true

elapsed += delta
if elapsed > TIMEOUT_S:
fail(str("Timed out waiting for Godot (", TIMEOUT_S, " seconds)."))
return true

if udp.get_available_packet_count() == 0:
return false

var packet = udp.get_packet().get_string_from_ascii()
print("[GD Orch] Received UDP packet [", packet.length(), "]: ", packet)
return true


func replace_line(file_path: String) -> bool:
var file = FileAccess.open(file_path, FileAccess.READ)
if file == null:
return false

var lines = []
while not file.eof_reached():
lines.append(file.get_line())
file.close()

var replaced = 0
file = FileAccess.open(file_path, FileAccess.WRITE)
for line: String in lines:
if line.strip_edges() == 'fn get_number(&self) -> i64 { 100 }':
file.store_line(line.replace("100", "777"))
replaced += 1
else:
file.store_line(line)
file.close()

if replaced == 0:
fail("Line not found in file.")
return false
else:
return true


func receive_udp() -> bool:
if udp.bind(FROM_GODOT_PORT) != OK:
fail("Failed to bind UDP")
return false

print("[GD Orch] Waiting for Godot to be ready (UDP)...")
return true


func send_udp() -> bool:
var out_udp = PacketPeerUDP.new()
if out_udp.set_dest_address("127.0.0.1", TO_GODOT_PORT) != OK:
fail("Failed to set destination address")
return false

if out_udp.put_packet("reload".to_utf8_buffer()) != OK:
fail("Failed to send packet")
return false

print("[GD Orch] Packet sent successfully")
return true


func fail(s: String) -> void:
print("::error::[GD Orch] ", s) # GitHub Action syntax
quit(1)
20 changes: 8 additions & 12 deletions examples/hot-reload/godot/test/ReloadTest.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ var extension_name: String


func _ready() -> void:
print("[GDScript] Start...")
print("[GD Editor] Start...")

var r = Reloadable.new()
var num = r.get_number()
r.free()

print("[GDScript] Sanity check: initial number is ", num)
print("[GD Editor] Sanity check: initial number is ", num)

var extensions = GDExtensionManager.get_loaded_extensions()
if extensions.size() == 1:
Expand All @@ -31,17 +31,14 @@ func _ready() -> void:
return

udp.bind(1337)
print("[GDScript] ReloadTest ready to receive...")
print("[GD Editor] ReloadTest ready to receive...")

send_udp()


func send_udp():
# Attempt to bind the UDP socket to any available port for sending.
# You can specify a port number instead of 0 if you need to bind to a specific port.
var out_udp = PacketPeerUDP.new()

# Set the destination address and port for the message
if out_udp.set_dest_address("127.0.0.1", 1338) != OK:
fail("Failed to set destination address")
return
Expand All @@ -50,12 +47,12 @@ func send_udp():
fail("Failed to send packet")
return

print("[GDScript] Packet sent successfully")
print("[GD Editor] Packet sent successfully")
out_udp.close()


func _exit_tree() -> void:
print("[GDScript] ReloadTest exit.")
print("[GD Editor] ReloadTest exit.")
udp.close()


Expand All @@ -64,7 +61,7 @@ func _process(delta: float) -> void:
return

var packet = udp.get_packet().get_string_from_ascii()
print("[GDScript] Received UDP packet [", packet.length(), "]: ", packet)
print("[GD Editor] Received UDP packet [", packet.length(), "]: ", packet)

if not _hot_reload():
return
Expand All @@ -74,15 +71,14 @@ func _process(delta: float) -> void:
r.free()

if num == 777:
print("[GDScript] Successful hot-reload! Exit...")
print("[GD Editor] Successful hot-reload! Exit...")
get_tree().quit(0)
else:
fail(str("Number was not updated correctly (is ", num, ")"))
return


func _hot_reload():
# TODO sometimes fails because .so is not found
var status = GDExtensionManager.reload_extension(extension_name)
if status != OK:
fail(str("Failed to reload extension: ", status))
Expand All @@ -92,7 +88,7 @@ func _hot_reload():


func fail(s: String) -> void:
print("::error::[GDScript] ", s) # GitHub Action syntax
print("::error::[GD Editor] ", s) # GitHub Action syntax
get_tree().quit(1)


84 changes: 0 additions & 84 deletions examples/hot-reload/godot/test/orchestrate.py

This file was deleted.

24 changes: 14 additions & 10 deletions examples/hot-reload/godot/test/run-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

# Restore un-reloaded files on exit (for local testing).
cleanedUp=0 # avoid recursion if cleanup fails
cleanup() {
echo "[Bash] Cleanup..."
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension ../MainScene.tscn
if [[ $cleanedUp -eq 0 ]]; then
cleanedUp=1
echo "[Bash] Cleanup..."
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension ../MainScene.tscn || true # ignore errors here
fi
}

set -euo pipefail
trap cleanup EXIT

echo "[Bash] Start hot-reload integration test..."
echo "[Bash] Start hot-reload integration test..."

# Restore un-reloaded file (for local testing).
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension
git checkout --quiet ../../rust/src/lib.rs ../rust.gdextension

# Set up editor file which has scene open, so @tool script loads at startup. Also copy scene file that holds a script.
mkdir -p ../.godot/editor
Expand All @@ -33,20 +37,20 @@ sleep 0.5

$GODOT4_BIN -e --headless --path .. &
pid=$!
echo "[Bash] Wait for Godot ready (PID $pid)..."
echo "[Bash] Wait for Godot ready (PID $pid)..."

python orchestrate.py await
python orchestrate.py replace
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- await
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- replace

# Compile updated Rust source.
cargo build -p hot-reload $cargoArgs

python orchestrate.py notify
$GODOT4_BIN --headless --no-header --script ReloadOrchestrator.gd -- notify

echo "[Bash] Wait for Godot exit..."
echo "[Bash] Wait for Godot exit..."
wait $pid
status=$?
echo "[Bash] Godot (PID $pid) has completed with status $status."
echo "[Bash] Godot (PID $pid) has completed with status $status."



4 changes: 2 additions & 2 deletions examples/hot-reload/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ struct HotReload;
#[gdextension]
unsafe impl ExtensionLibrary for HotReload {
fn on_level_init(_level: InitLevel) {
println!("[Rust] Init level {:?}", _level);
println!("[Rust] Init level {:?}", _level);
}

fn on_level_deinit(_level: InitLevel) {
println!("[Rust] Deinit level {:?}", _level);
println!("[Rust] Deinit level {:?}", _level);
}
}

Expand Down
Loading

0 comments on commit e7dd8b8

Please sign in to comment.