roc/ci/test_int_platform.sh
Luke Boswell cb42d26e11
Roc platform host shim (#8214)
* use zig Builder to generate LLVM bc for platform host shim

* WIP

* Working embedded LLVM compilation

* lints

* Update test_shared_memory_system.zig

* Update main.zig

* Refactor out builder into separate file

* remove clang fallback, remove -Dllvm build flag

* WIP change platform header

* WIP no hardcoded platform entrypoints

* WIP no hardcoded entrypoints

* remove hardcoded interpreter shim entrypoint

* WIP multiple entrypoints

* WIP multiple entrypoints

* WIP alignment issue

* it's working!!

* cleanup

* fmt

* fix tests and snapshots

* fix cross-compile

* WIP fix linking linux

* improved debug logging in rocRun

* WIP fix linux segfaults

* cleanup stray dbgs

* fix Windows linking

* WIP remove std.debug.print in prebuilt shim

* remove libc stubs, try C shim for platform host main

* WORKING ON LINUX!!!

* WIP fixing int platform

* prevent double mapping of shared memory

* WIP

* remove clang fallback

* cleanup test platforms

* vendor linux object files for musl and gnu in test platforms

* fix windows __main export

* typo

* avoide hardcoding libc path and dynamic linker on linux

* some fixes

* try CI fix for linux ARM64

* try use system CRT for ARM64

* use absolute pats for vendored CRT

* minimal roc build

* use target os for filename not host os

* implement app stubs for test platforms

* fix glibc

* WIP cross compilation

* fix run, fix build for native

* Merge remote/main

* fix macos

* add bookends

* remove hosts

* fix cross-compile for linux on macos

* fix files paths for Windows

* Update main.zig

* test ubu 24.04 too

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>

* debug: log all used instructions

* fmt

* get bad instruction with gdb

* install gdb

* Revert last 4 commits

* simpler cross-compile arm64 runtime files

* add minimal cross-compilation for glibc

* simplify stub generation

* Update test_int_platform.sh

* Update ci_cross_compile.yml

* Update ci_cross_compile.yml

* try alpine3.18 x64musl files

* Claude's fix + removed CPU instructions

* fix fuzz crash empty after merge

* update snapshot

* restore full CI

* some cleanup

* remove debugging from CI - print all supported CPU instructions

* try using generic when LLVM compiles app stub

* enhanced error handling for app stub compilation using LLVM

* check machine ABI on linux

* try more debugging

* update test script to inidicate truncated error logs, temporarily display everything for arm64 linux errors

* fix missing arm64 libc search paths

* back to normal CI

* try without .git 

See https://github.com/ziglang/zig/issues/21316#issuecomment-2408071050

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>

* undo .git change

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>

* retry zig build command

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>

* different retry strategy

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>

---------

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>
Co-authored-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>
2025-09-09 13:15:23 +02:00

399 lines
12 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
# Colors for output (minimal usage)
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
# Test configuration
ROC_CLI="./zig-out/bin/roc"
INT_APP="test/int/app.roc"
TEST_OUTPUT_DIR="tmp_test_outputs"
# Supported targets for cross-compilation
CROSS_TARGETS=(
"x64musl"
"arm64musl"
"x64glibc"
"arm64glibc"
)
# Test results tracking
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
FAILED_TESTS=()
print_header() {
echo "================================"
echo " Roc Int Platform Test Suite "
echo "================================"
echo
}
print_section() {
echo ">>> $1"
}
print_success() {
echo -e "${GREEN}PASS${NC} $1"
}
print_error() {
echo -e "${RED}FAIL${NC} $1"
}
print_info() {
echo "INFO $1"
}
# Portable timeout wrapper:
# - Uses GNU coreutils 'timeout' if available
# - Falls back to 'gtimeout' (Homebrew coreutils on macOS)
# - Otherwise uses a shell-based timer that sends SIGTERM after N seconds
# Usage: run_with_timeout <seconds> <command> [args...]
run_with_timeout() {
local seconds="$1"; shift
if command -v timeout >/dev/null 2>&1; then
timeout "${seconds}s" "$@"
return $?
elif command -v gtimeout >/dev/null 2>&1; then
gtimeout "${seconds}s" "$@"
return $?
else
( "$@" ) &
local cmd_pid=$!
( sleep "$seconds"; kill -0 "$cmd_pid" 2>/dev/null && kill -TERM "$cmd_pid" 2>/dev/null ) &
local timer_pid=$!
wait "$cmd_pid"
local exit_code=$?
kill -TERM "$timer_pid" 2>/dev/null || true
return "$exit_code"
fi
}
cleanup() {
if [ -d "$TEST_OUTPUT_DIR" ]; then
rm -rf "$TEST_OUTPUT_DIR"
fi
}
setup() {
# Create output directory
mkdir -p "$TEST_OUTPUT_DIR"
# Check if roc CLI exists
if [ ! -f "$ROC_CLI" ]; then
print_error "Roc CLI not found at $ROC_CLI"
print_info "Please run 'zig build' first to build the Roc compiler"
exit 1
fi
# Check if int app exists
if [ ! -f "$INT_APP" ]; then
print_error "Int test app not found at $INT_APP"
exit 1
fi
}
run_test() {
local test_name="$1"
local test_cmd="$2"
local expected_output="$3"
TESTS_RUN=$((TESTS_RUN + 1))
print_info "Running: $test_name"
echo " Command: $test_cmd"
if eval "$test_cmd" > "$TEST_OUTPUT_DIR/test_$TESTS_RUN.out" 2>&1; then
if [ -n "$expected_output" ]; then
# Check if expected output is present
if grep -q "$expected_output" "$TEST_OUTPUT_DIR/test_$TESTS_RUN.out"; then
print_success "$test_name"
TESTS_PASSED=$((TESTS_PASSED + 1))
return 0
else
print_error "$test_name - Expected output not found"
echo " Expected: $expected_output"
echo " Got (first 5 lines):"
cat "$TEST_OUTPUT_DIR/test_$TESTS_RUN.out" | head -5
echo " NOTE: For complete output, run: cat $TEST_OUTPUT_DIR/test_$TESTS_RUN.out"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$test_name")
return 1
fi
else
print_success "$test_name"
TESTS_PASSED=$((TESTS_PASSED + 1))
return 0
fi
else
print_error "$test_name - Command failed"
# Show more complete output for arm64glibc debugging
if [[ "$test_name" == *"arm64glibc"* ]]; then
echo " Complete error output for arm64glibc debugging:"
cat "$TEST_OUTPUT_DIR/test_$TESTS_RUN.out"
else
echo " Error output (first 10 lines):"
cat "$TEST_OUTPUT_DIR/test_$TESTS_RUN.out" | head -10
echo " NOTE: This is a summary of the error output."
echo " For complete output, run: cat $TEST_OUTPUT_DIR/test_$TESTS_RUN.out"
fi
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$test_name")
return 1
fi
}
test_native_execution() {
print_section "Testing Native Build and Execution"
local native_output="$TEST_OUTPUT_DIR/int_app_native"
# Test native build (should work on current platform)
run_test "Native build" \
"$ROC_CLI build --output=$native_output $INT_APP" \
""
# Verify the executable was created
if [ ! -f "$native_output" ]; then
print_error "Native executable not created"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("native executable creation")
return 1
fi
print_success "Native executable created"
# Show executable info
if command -v file >/dev/null 2>&1; then
echo " File type: $(file "$native_output")"
fi
# Make sure it's executable
chmod +x "$native_output"
# Test execution - the int platform should run the host which calls the app functions
print_info "Testing native execution..."
local exec_output="$TEST_OUTPUT_DIR/native_exec.out"
if run_with_timeout 10 "$native_output" > "$exec_output" 2>&1; then
local exit_code=$?
if [ $exit_code -eq 0 ]; then
print_success "Native executable runs and exits successfully"
# Show what the executable outputs (useful for debugging)
if [ -s "$exec_output" ]; then
echo " Output:"
head -5 "$exec_output" | sed 's/^/ /'
fi
TESTS_PASSED=$((TESTS_PASSED + 1))
else
print_error "Native executable exited with code $exit_code"
echo " Output (first 10 lines):"
head -10 "$exec_output" | sed 's/^/ /'
echo " NOTE: For complete output, run: cat $exec_output"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("native execution exit code")
fi
else
print_error "Native executable timed out or crashed"
echo " Output (first 10 lines):"
head -10 "$exec_output" | sed 's/^/ /'
echo " NOTE: For complete output, run: cat $exec_output"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("native execution timeout")
fi
TESTS_RUN=$((TESTS_RUN + 1))
}
test_cross_compilation() {
print_section "Testing Cross-Compilation"
for target in "${CROSS_TARGETS[@]}"; do
local output_name="$TEST_OUTPUT_DIR/int_app_$target"
# Test cross-compilation build
run_test "Cross-compile to $target" \
"$ROC_CLI build --target=$target --output=$output_name $INT_APP" \
""
# Check if the executable was created
if [ -f "$output_name" ]; then
print_success "Executable created for $target"
# Show some info about the generated executable
if command -v file >/dev/null 2>&1; then
echo " File info: $(file "$output_name")"
fi
if command -v ldd >/dev/null 2>&1 && [[ "$target" == *"$(uname -m)"* ]]; then
echo " Dependencies:"
ldd "$output_name" 2>/dev/null | head -5 || echo " (static or incompatible)"
fi
else
print_error "Executable not created for $target"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$target executable creation")
fi
done
}
test_platform_build() {
print_section "Testing Platform Build System"
# Test that platform libraries are built
run_test "Build platform libraries" \
"zig build" \
""
# Check that target directories exist with expected files
for target in "${CROSS_TARGETS[@]}"; do
local target_dir="test/int/platform/targets/$target"
if [ -d "$target_dir" ]; then
print_success "Target directory exists: $target"
# Check for expected files
local expected_files=("libhost.a")
if [[ "$target" == *"glibc"* ]]; then
expected_files+=("libc.so.6" "libc.so" "libc_stub.s")
fi
for file in "${expected_files[@]}"; do
if [ -f "$target_dir/$file" ]; then
echo " $file: present"
else
print_error " $file missing in $target"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$target/$file")
fi
done
else
print_error "Target directory missing: $target"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$target directory")
fi
done
}
test_glibc_stubs() {
print_section "Testing Glibc Stub Generation"
for target in "x64glibc" "arm64glibc"; do
local stub_file="test/int/platform/targets/$target/libc_stub.s"
if [ -f "$stub_file" ]; then
print_success "Glibc stub exists: $target"
# Check that essential symbols are present
local essential_symbols=("__libc_start_main" "abort" "getauxval" "_IO_stdin_used")
local missing_symbols=0
for symbol in "${essential_symbols[@]}"; do
if grep -q "$symbol" "$stub_file"; then
echo " $symbol: present"
else
print_error " Symbol $symbol missing from $target"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$target $symbol")
missing_symbols=$((missing_symbols + 1))
fi
done
if [ $missing_symbols -eq 0 ]; then
echo " All essential symbols present"
fi
# Check architecture-specific instructions
if [[ "$target" == "x64glibc" ]]; then
if grep -q "xor %rax" "$stub_file"; then
echo " x86_64 assembly: correct"
else
print_error " x86_64 assembly instructions missing from $target"
fi
elif [[ "$target" == "arm64glibc" ]]; then
if grep -q "mov x0" "$stub_file"; then
echo " ARM64 assembly: correct"
else
print_error " ARM64 assembly instructions missing from $target"
fi
fi
else
print_error "Glibc stub missing: $target"
TESTS_FAILED=$((TESTS_FAILED + 1))
FAILED_TESTS+=("$target stub")
fi
done
}
print_summary() {
echo
print_section "Test Summary"
echo "Total tests: $TESTS_RUN"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
echo -e "${RED}Failed: $TESTS_FAILED${NC}"
if [ $TESTS_FAILED -gt 0 ]; then
echo
echo "Failed tests:"
for failed_test in "${FAILED_TESTS[@]}"; do
echo " - $failed_test"
done
echo
print_error "Some tests failed"
return 1
else
echo
print_success "All tests passed"
return 0
fi
}
main() {
print_header
# Setup
setup
trap cleanup EXIT
# Run test suites
test_platform_build
test_glibc_stubs
test_cross_compilation
test_native_execution
# Print summary and exit with appropriate code
if print_summary; then
exit 0
else
exit 1
fi
}
# Handle command line arguments
case "${1:-}" in
--help|-h)
echo "Usage: $0 [--help]"
echo
echo "Test script for Roc's int platform cross-compilation."
echo "This script tests:"
echo " - Platform build system"
echo " - Glibc stub generation"
echo " - Native execution"
echo " - Cross-compilation to all supported targets"
echo
echo "Make sure to run 'zig build' first to build the Roc compiler."
exit 0
;;
*)
main "$@"
;;
esac