From 9684aa2b626a9904043ab28aa27538e580ab87f5 Mon Sep 17 00:00:00 2001 From: dave horner Date: Sat, 15 Nov 2025 07:33:44 -0500 Subject: [PATCH] feat: add top-level Makefile and Taskfile for cross-platform build automation; update README with build instructions and dependencies --- Makefile | 125 ++++++++++++++++++++++++++++ README.md | 120 +++++++++++++++++++++++---- Taskfile.yml | 155 +++++++++++++++++++++++++++++++++++ scripts/build-unidasm.cmd | 62 ++++++++++++++ scripts/build-unidasm.sh | 152 ++++++++++++++++++++++++++++++++++ scripts/ensure_task.cmd | 33 ++++++++ scripts/ensure_task.sh | 58 +++++++++++++ scripts/install-sdl2-i386.sh | 34 ++++++++ tests/Makefile | 4 + 9 files changed, 726 insertions(+), 17 deletions(-) create mode 100644 Makefile create mode 100644 Taskfile.yml create mode 100644 scripts/build-unidasm.cmd create mode 100644 scripts/build-unidasm.sh create mode 100644 scripts/ensure_task.cmd create mode 100644 scripts/ensure_task.sh create mode 100644 scripts/install-sdl2-i386.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..25c43e8 --- /dev/null +++ b/Makefile @@ -0,0 +1,125 @@ +# Top-level Makefile for GoodASM +# Forwards targets to tests/Makefile + +.PHONY: all clean selftests fuzz ensure_task unidasm qt5deps yara_x tests-all build + +# Default: configure & build with CMake (creates build/ if missing) +CURDIR:=$(shell pwd) +BUILD_PATHS:=$(CURDIR)/build:$(CURDIR)/build/Debug:$(CURDIR)/build/Release:$(CURDIR)/scripts +export PATH:=$(BUILD_PATHS):$(PATH) + +all: build tests-all + +build: + @mkdir -p build + @cmake -S . -B build || true + @cmake --build build +# Preserve old behavior to forward to tests when explicitly requested +tests-all: yara_x nasm qt5deps unidasm + $(MAKE) -C tests all + +clean: + $(MAKE) -C tests clean + +ifeq ($(OS),Windows_NT) + @echo "Removing unidasm.exe from %USERPROFILE%\\bin if present"; + @cmd /C "if exist %USERPROFILE%\\bin\\unidasm.exe del %USERPROFILE%\\bin\\unidasm.exe" || true +else + @echo "Removing local unidasm if present"; + @rm -f "$$HOME/.local/bin/unidasm" || true; + @# If a system-wide copy exists on macOS/linux, remove if writable or show sudo hint + @if [ -f /usr/local/bin/unidasm ]; then \ + if [ -w /usr/local/bin ]; then \ + rm -f /usr/local/bin/unidasm && echo "removed /usr/local/bin/unidasm"; \ + else \ + echo "/usr/local/bin/unidasm exists; to remove run: sudo rm /usr/local/bin/unidasm"; \ + fi; \ + fi; +endif + +selftests: + $(MAKE) -C tests selftests + +fuzz: + $(MAKE) -C tests fuzz + +ensure_task: +ifeq ($(OS),Windows_NT) + cmd /C scripts\\ensure_task.cmd +else + bash ./scripts/ensure_task.sh +endif + +qt5deps: + @echo "checking for Qt5 (qmake/pk-config) on PATH..."; + @if command -v qmake-qt5 >/dev/null 2>&1 || command -v qmake >/dev/null 2>&1 || (command -v pkg-config >/dev/null 2>&1 && pkg-config --exists Qt5Core); then \ + echo "Qt5 appears to be installed: $$(command -v qmake-qt5 || command -v qmake || echo pkg-config)"; \ + else \ + if command -v apt-get >/dev/null 2>&1; then \ + sudo apt-get update && sudo apt-get install -y qtbase5-dev qt5-qmake qtbase5-dev-tools pkg-config libsdl2-ttf-dev; \ + elif command -v brew >/dev/null 2>&1; then \ + brew install qt@5 pkg-config sdl2_ttf; \ + else \ + echo "Please install Qt5 dev tools, pkg-config, and SDL2_ttf manually."; \ + fi; \ + fi + + +nasm: + @echo "checking for nasm on PATH..."; + @if command -v nasm >/dev/null 2>&1; then \ + echo "nasm is already on PATH at: $$(command -v nasm)"; \ + else \ + if command -v apt-get >/dev/null 2>&1; then \ + sudo apt-get update && sudo apt-get install -y nasm; \ + elif command -v brew >/dev/null 2>&1; then \ + brew install nasm; \ + else \ + echo "Please install nasm manually"; \ + fi; \ + fi + + +yara_x: + @echo "checking for yr on PATH..."; + @if command -v yr >/dev/null 2>&1; then \ + echo "yr is already on PATH at: $$(command -v yr)"; \ + else \ + if [ ! -d yara-x ]; then git clone --depth 1 https://github.com/VirusTotal/yara-x.git; fi; \ + cargo install --path yara-x/cli --root $$HOME/.local; \ + fi + +# unidasm: qt5deps +# @if [ ! -d mame ]; then git clone --depth 1 https://github.com/mamedev/mame.git; fi +# $(MAKE) -C mame TOOLS=1 generate target=generate +# $(MAKE) -C mame TOOLS=1 unidasm target=unidasm +# @if [ -f mame/bin/unidasm ]; then \ +# mkdir -p $$HOME/.local/bin; \ +# cp mame/bin/unidasm $$HOME/.local/bin/unidasm; \ +# elif [ -f mame/unidasm ]; then \ +# mkdir -p $$HOME/.local/bin; \ +# cp mame/unidasm $$HOME/.local/bin/unidasm; \ +# else \ +# echo 'unidasm binary not found!'; exit 1; \ +# fi + +# .PHONY: unidasm +unidasm: + @if [ ! -d mame ]; then git clone --depth 1 https://github.com/mamedev/mame.git; fi +ifeq ($(OS),Windows_NT) + @echo "Building unidasm on Windows..."; + @cmd /C scripts\\build-unidasm.cmd +else + @echo "Building unidasm on Unix-like system..."; + @if sh scripts/build-unidasm.sh; then \ + if [ -f "$$HOME/.local/bin/unidasm" ]; then \ + echo "unidasm built and copied to $$HOME/.local/bin/unidasm"; \ + echo "To install system-wide to /usr/local/bin run:"; \ + echo " sudo cp \"$$HOME/.local/bin/unidasm\" /usr/local/bin/unidasm && sudo chmod 755 /usr/local/bin/unidasm"; \ + else \ + echo "build script reported success but unidasm not found at $$HOME/.local/bin/unidasm"; exit 1; \ + fi; \ + else \ + echo "build-unidasm.sh failed; see its output above for details"; exit 1; \ + fi +endif diff --git a/README.md b/README.md index 10d920c..efd0fce 100644 --- a/README.md +++ b/README.md @@ -47,27 +47,104 @@ check the issue tracker for their status. Source code and binaries were publicly released at [DistrictCon](https://www.districtcon.org/) on Feb 21, 2025. -## Building +## Building and Automation -For GUI development, install the [Qt Dev -Kit](https://www.qt.io/download-qt-installer-oss) and then open -`CMakefile.txt` in Qt Creator. On Windows, you must also install the -[Git](https://git-scm.com/downloads/win) client; Github Desktop is not -enough on its own. +This project supports robust, cross-platform build and test automation using both a top-level `Makefile` (for Linux/macOS and advanced users) and a [Taskfile.yml](https://taskfile.dev) for [Go Task](https://taskfile.dev) (especially recommended for Windows users). The automation ensures all dependencies are installed, including CMake, Qt, Git, NASM, unidasm, and YARA-X (`yr`). -To build in Linux, first install `qt6-declarative-dev`, `qml6-module-\*`, `git` -and `cmake`, then run the following: +### Recommended: Go Task (Windows & Cross-Platform) + +The preferred way to build and test GoodASM on Windows (and optionally on Linux/macOS) is via Go Task: + +### Ensuring Go Task is Installed + +If you do not already have [Go Task](https://taskfile.dev) installed: + +- **On Windows:** + - Since `make` is not available by default, run the script directly: + ```cmd + ensure_task.cmd + ``` +- **On Linux/macOS:** + - Run: + ```sh + make ensure_task + ``` + +The scripts are located at: +- `ensure_task.cmd` (Windows) +- `ensure_task.sh` (Linux/macOS) + +These scripts will attempt to install Go Task if it is missing, ensuring that all automation commands are available. + + +### Recommended: Go Task (Windows & Cross-Platform) + +The preferred way to build and test GoodASM on Windows (and optionally on Linux/macOS) is via Go Task: + +1. In a terminal, run: + ```cmd + task all + ``` + This will: + - Install CMake, Qt, Git, NASM, unidasm, and YARA-X (`yr`) if not already present + - Build the project (using CMake or Makefile as appropriate) + - Run all self-tests + +You can also run individual tasks, e.g. `task build:cmake` or `task run:repl`. Run `task --list` to see all available tasks. + +### Makefile (Linux/macOS & Advanced) + +The top-level `Makefile` is the single source of truth for build and test logic on POSIX systems. It: + +- Installs all required dependencies (Qt5, pkg-config, SDL2_ttf, unidasm, YARA-X) +- Builds the project and runs all tests +- Builds and installs the MAME unidasm tool and YARA-X (`yr`) automatically + +To build and test on Linux/macOS: + +```sh +make qt5deps # Installs all dependencies, including YARA-X (yr) +make # Builds and runs all tests ``` -git clone https://github.com/travisgoodspeed/goodasm -cd goodasm -mkdir build -cd build -cmake .. -make -j 8 clean all + +You can also use `make unidasm` to build and install the MAME unidasm tool, or `make clean` to remove build artifacts. + +#### Note on YARA-X + +The test infrastructure uses [YARA-X](https://github.com/VirusTotal/yara-x) (`yr`), not classic YARA. The Makefile will automatically build and install YARA-X if it is not present. + +#### Windows Manual Build + +For GUI development, install the [Qt Dev Kit](https://www.qt.io/download-qt-installer-oss) and open `CMakeLists.txt` in Qt Creator. You must also install [Git](https://git-scm.com/downloads/win); Github Desktop is not enough. + +You can also run individual tasks, e.g. `task build:cmake` or `task run:repl`. Run `task --list` to see all available tasks. + +### Makefile (Linux/macOS & Advanced) + +The top-level `Makefile` is the single source of truth for build and test logic on POSIX systems. It: + +- Installs all required dependencies (Qt5, pkg-config, SDL2_ttf, unidasm, YARA-X) +- Builds the project and runs all tests +- Builds and installs the MAME unidasm tool and YARA-X (`yr`) automatically + +To build and test on Linux/macOS: + +```sh +make qt5deps # Installs all dependencies, including YARA-X (yr) +make # Builds and runs all tests ``` -The preferred executable is `goodasm`. The GUI for iOS and Android is -more of a fun toy than a tool. +You can also use `make unidasm` to build and install the MAME unidasm tool, or `make clean` to remove build artifacts. + +#### Note on YARA-X + +The test infrastructure uses [YARA-X](https://github.com/VirusTotal/yara-x) (`yr`), not classic YARA. The Makefile will automatically build and install YARA-X if it is not present. + +#### Windows Manual Build + +For GUI development, install the [Qt Dev Kit](https://www.qt.io/download-qt-installer-oss) and open `CMakeLists.txt` in Qt Creator. You must also install [Git](https://git-scm.com/downloads/win); Github Desktop is not enough. + +--- ## Examples @@ -304,6 +381,16 @@ interactive mode for iOS and Android. Please don't do real work this way, but it's handy when studying an instruction set with pen and paper, away from a real laptop. +### Exiting the REPL + +To exit the GoodASM interactive REPL: + +- On **Windows**: Press `Ctrl+Z` then Enter +- On **Linux/macOS**: Press `Ctrl+D` +- Or use `Ctrl+C` to interrupt/terminate the REPL + +There are no built-in `.exit` or `.quit` commands; only EOF or interrupt will exit the REPL. + ## Identification and Grading It's a frequent problem in embedded systems reverse engineering that @@ -510,7 +597,6 @@ source of examples for using the library. languages, so that someday the parser can be rewritten without breaking code compatibility. - ## Similar Assembler/Disassemblers [Naken ASM](https://github.com/mikeakohn/naken_asm) diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..5807acc --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,155 @@ +version: '3' + +vars: + TARGET: Debug + +tasks: + install:qt: + desc: | + Ensure Qt 6 is installed and discoverable by CMake. + - Checks for Qt6Config.cmake or cmake --find-package. + - Installs via package manager if not found. + - On Windows, uses the official Qt installer. + cmds: + - cmd: >- + cmd.exe /c "if exist C:\Qt\6.10.0\msvc2022_64\lib\cmake\Qt6\Qt6Config.cmake ( + echo Qt6 is already installed and discoverable by CMake. + ) else ( + curl -L -O https://download.qt.io/official_releases/online_installers/qt-online-installer-windows-x64-online.exe + qt-online-installer-windows-x64-online.exe --root C:\Qt --accept-licenses --default-answer --confirm-command install qt.qt6.6100.win64_msvc2022_64 + )" + platforms: [windows] + - cmd: | + if cmake --find-package -DNAME=Qt6 -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST 2>/dev/null; then \ + echo Qt6 is already installed and discoverable by CMake.; \ + elif [ -f /usr/lib/cmake/Qt6/Qt6Config.cmake ] || [ -f /usr/local/lib/cmake/Qt6/Qt6Config.cmake ] || [ -f /usr/lib/x86_64-linux-gnu/cmake/Qt6/Qt6Config.cmake ]; then \ + echo Qt6 is already installed and discoverable by CMake.; \ + else \ + if command -v apt-get >/dev/null 2>&1; then \ + if ! dpkg -s qt6-base-dev qt6-declarative-dev >/dev/null 2>&1; then \ + sudo apt-get update && \ + sudo apt-get install -y qt6-base-dev qt6-declarative-dev; \ + else \ + echo "qt6-base-dev and qt6-declarative-dev are already installed."; \ + fi; \ + # Install both 64-bit and 32-bit SDL2 dev packages if available + if apt-cache show libsdl2-dev:i386 >/dev/null 2>&1; then \ + sudo apt-get install -y libsdl2-dev libsdl2-dev:i386; \ + else \ + sudo apt-get install -y libsdl2-dev; \ + fi; \ + elif command -v brew >/dev/null 2>&1; then \ + brew install qt6; \ + else \ + echo "Please install Qt6 manually."; \ + fi; \ + fi + platforms: [linux] + - cmd: | + if [ -d /opt/homebrew ]; then QT_PREFIX=/opt/homebrew/opt/qt; else QT_PREFIX=/usr/local/opt/qt; fi; \ + if cmake --find-package -DNAME=Qt6 -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DCMAKE_PREFIX_PATH=${QT_PREFIX} 2>/dev/null; then echo "Qt6 is already installed and discoverable by CMake - Homebrew at ${QT_PREFIX}."; \ + elif [ -f ${QT_PREFIX}/lib/cmake/Qt6/Qt6Config.cmake ] || [ -f ${QT_PREFIX}/lib/cmake/Qt6/Qt6Config.cmake ]; then echo "Qt6 is already installed and discoverable by CMake - Homebrew at ${QT_PREFIX}."; \ + else if command -v brew >/dev/null 2>&1; then brew install qt; else echo "Please install Qt6 via Homebrew: brew install qt"; fi; fi + platforms: [darwin] + ignore_error: false + + + + install:deps: + desc: Install dependencies (CMake, Qt, Git, NASM, unidasm) + cmds: + - cmd: | + if where /q cmake && where /q git; then \ + echo CMake and Git are already installed.; \ + else \ + cmd /C "winget install -e --id Kitware.CMake" && \ + cmd /C "winget install -e --id Git.Git"; \ + fi + platforms: [windows] + - cmd: | + if command -v cmake >/dev/null 2>&1 && command -v git >/dev/null 2>&1 && command -v nasm >/dev/null 2>&1 && command -v unidasm >/dev/null 2>&1; then \ + echo CMake, Git, NASM, and unidasm are already installed.; \ + else \ + if command -v apt-get >/dev/null 2>&1; then \ + sudo apt-get update && \ + sudo apt-get install -y cmake git nasm || echo "not available in apt repo"; \ + elif command -v brew >/dev/null 2>&1; then \ + brew install cmake git nasm || echo "not available in brew repo"; \ + else \ + echo "Please install cmake, git, nasm manually."; \ + fi; \ + make unidasm; \ + fi + platforms: [darwin, linux] + ignore_error: true + + build:cmake: + desc: | + Build the project (Windows: native, POSIX: Makefile) + cmds: + - cmd: cmd /C "if not exist build mkdir build" + platforms: [windows] + - cmd: cmd /C "cd build && cmake -DCMAKE_PREFIX_PATH=C:\Qt\6.10.0\msvc2022_64 .." + platforms: [windows] + - cmd: cmd /C "cd build && cmake --build ." + platforms: [windows] + - cmd: make + platforms: [darwin, linux] + ignore_error: false + + test:self: + desc: Run GoodASM self-tests (Windows native, POSIX Makefile) + cmds: + - cmd: > + cmd /C "if exist build\goodasm.exe build\goodasm.exe --test --ucom43 && build\goodasm.exe --test --6502 && build\goodasm.exe --test --marc4 && build\goodasm.exe --test --sm83 && build\goodasm.exe --test --tlcs47 && build\goodasm.exe --test --s2000 && build\goodasm.exe --test --8051 && build\goodasm.exe --test --6805 && build\goodasm.exe --test --chip8 && build\goodasm.exe --test --z80 && build\goodasm.exe --test --z8 && build\goodasm.exe --test --8080 && build\goodasm.exe --test --h83 else exit /b 1" + platforms: [windows] + - cmd: PATH="$(pwd)/build:$(pwd)/build/Debug:$PATH" make selftests + platforms: [darwin, linux] + ignore_error: false + + test:fuzz: + desc: Run GoodASM fuzz tests (Windows native, POSIX Makefile) + cmds: + - cmd: > + cmd /C "if exist build\goodasm.exe build\goodasm.exe --fuzz --ucom43 && build\goodasm.exe --fuzz --6502 && build\goodasm.exe --fuzz --marc4 && build\goodasm.exe --fuzz --sm83 && build\goodasm.exe --fuzz --tlcs47 && build\goodasm.exe --fuzz --s2000 && build\goodasm.exe --fuzz --8051 && build\goodasm.exe --fuzz --6805 && build\goodasm.exe --fuzz --st7 && build\goodasm.exe --fuzz --chip8 && build\goodasm.exe --fuzz --z80 && build\goodasm.exe --fuzz --z8 && build\goodasm.exe --fuzz --8080 && build\goodasm.exe --fuzz --pic16c5x && build\goodasm.exe --fuzz --h83 && build\goodasm.exe --fuzz --arm7tdmi && build\goodasm.exe --fuzz -i else exit /b 1" + platforms: [windows] + - cmd: PATH="$(pwd)/build:$(pwd)/build/Debug:$PATH" make fuzz + platforms: [darwin, linux] + ignore_error: false + + run:repl: + desc: | + Start GoodASM in interactive REPL mode (Windows: native, POSIX: Makefile build first) + cmds: + - task: build:cmake + - cmd: cmd /C "if exist build\goodasm.exe build\goodasm.exe --repl else (exit /b 1)" + platforms: [windows] + - cmd: ./build/goodasm --repl + platforms: [darwin, linux] + ignore_error: false + + clean: + desc: | + Remove build artifacts (Windows: native, POSIX: Makefile) + cmds: + - cmd: cmd /C "if exist build rmdir /s /q build" + platforms: [windows] + - cmd: make clean + platforms: [darwin, linux] + ignore_error: true + + default: + desc: Default build and test (cross-platform) + deps: [install:qt] + cmds: + - task: build:cmake + - task: test:self + - task: test:fuzz + + all: + desc: Install dependencies, build, and test (cross-platform) + cmds: + - task: install:deps + - task: test:self + - task: test:fuzz + diff --git a/scripts/build-unidasm.cmd b/scripts/build-unidasm.cmd new file mode 100644 index 0000000..3962c3e --- /dev/null +++ b/scripts/build-unidasm.cmd @@ -0,0 +1,62 @@ +@echo off +rem Build unidasm on Windows (GENie-generated gmake) +setlocal enabledelayedexpansion + +set G= +if exist mame\build\projects\sdl\mame\gmake-msvc ( + set G=mame\build\projects\sdl\mame\gmake-msvc +) else ( + for /f "delims=" %%D in ('dir /b /s /ad mame\build\projects\*\*gmake* 2^>nul') do ( + if not defined G set G=%%D + ) +) + +if not exist mame ( + echo mame\ not found -- cloning shallow repository (this may take a while)... + git clone --depth 1 https://github.com/mamedev/mame.git || echo clone failed +) + +if not defined G ( + echo Generated gmake tree not found. Running generator in mame\ (may take a while)... + pushd mame + call nmake TOOLS=1 generate 2>nul || (call mingw32-make TOOLS=1 generate 2>nul) + popd + for /f "delims=" %%D in ('dir /b /s /ad mame\build\projects\*\*gmake* 2^>nul') do ( + if not defined G set G=%%D + ) +) + +if not defined G ( + echo Could not find generated gmake tree under mame\build\projects + exit /b 1 +) + +echo Building unidasm from %G% + +pushd %G% +rem Try to build with nmake or mingw32-make +call nmake unidasm 2>nul || call mingw32-make unidasm +set RET=%ERRORLEVEL% +popd + +if not %RET%==0 ( + echo Build failed with exit %RET% + exit /b %RET% +) + +rem Try to copy built exe +set DEST=%USERPROFILE%\bin +if not exist "%DEST%" mkdir "%DEST%" + +if exist mame\build\generated\unidasm\unidasm.exe ( + copy /Y mame\build\generated\unidasm\unidasm.exe "%DEST%\unidasm.exe" +) else if exist mame\unidasm.exe ( + copy /Y mame\unidasm.exe "%DEST%\unidasm.exe" +) else ( + echo Could not find unidasm.exe after build + exit /b 1 +) + +echo Installed %DEST%\unidasm.exe +endlocal +exit /b 0 diff --git a/scripts/build-unidasm.sh b/scripts/build-unidasm.sh new file mode 100644 index 0000000..47154ec --- /dev/null +++ b/scripts/build-unidasm.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +# Robust: don't exit on error, unset variable, or pipefail +set +e + + +# Always use script's directory as base +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR/.." || exit 0 +echo "[DEBUG] Script directory: $SCRIPT_DIR" +echo "[DEBUG] Current working directory: $(pwd)" +echo "[DEBUG] PLATFORM: $(uname -s 2>/dev/null || echo Unknown)" + +# Detect platform early (used when invoking generator) +PLATFORM=$(uname -s 2>/dev/null || echo Unknown) + + # If mame/ is missing, clone shallow copy (do this before searching) + if [ ! -d mame ]; then + echo "mame/ not found — cloning shallow repository (this may take a while)"; + git clone --depth 1 https://github.com/mamedev/mame.git || true + fi + + +# Find generated gmake dir, relative to script +G="" +if [ "$PLATFORM" = "Darwin" ]; then + for d in mame/build/projects/sdl/mame/gmake-osx-clang mame/build/projects/sdl/mame/gmake-osx; do + if [ -d "$d" ]; then G="$d"; break; fi + done +elif [ "$PLATFORM" = "Linux" ]; then + for d in mame/build/projects/sdl/mame/gmake-linux-clang mame/build/projects/sdl/mame/gmake-linux; do + if [ -d "$d" ]; then G="$d"; break; fi + done +fi + +# Fallback: try to generate the gmake tree if not found +if [ -z "$G" ]; then + echo "[INFO] No gmake dir found, attempting to generate..." + if [ "$PLATFORM" = "Darwin" ]; then + (cd mame && make TOOLS=1 build/projects/sdl/mame/gmake-osx-clang/Makefile) || true + for d in mame/build/projects/sdl/mame/gmake-osx-clang mame/build/projects/sdl/mame/gmake-osx; do + if [ -d "$d" ]; then G="$d"; break; fi + done + elif [ "$PLATFORM" = "Linux" ]; then + (cd mame && make TOOLS=1 build/projects/sdl/mame/gmake-linux-clang/Makefile) || true + for d in mame/build/projects/sdl/mame/gmake-linux-clang mame/build/projects/sdl/mame/gmake-linux; do + if [ -d "$d" ]; then G="$d"; break; fi + done + fi +fi + +# Last resort: pick any gmake dir +if [ -z "$G" ]; then + G=$(ls -d mame/build/projects/*/*gmake* 2>/dev/null | head -n1 || true) +fi + +echo "[DEBUG] G (make dir): $G" +if [ -z "$G" ]; then + echo "[WARN] G (make dir) is still empty. Will try to build anyway with generic defaults." + G="mame/build/projects/sdl/mame/gmake-linux" +fi +if [ ! -f "$G/Makefile" ]; then + echo "[WARN] $G/Makefile does not exist. Will try to build anyway." +fi + + + +PLATFORM=$(uname -s 2>/dev/null || echo Unknown) +BREW_PREFIX="" +CPPFLAGS="" +LDFLAGS="" +LIBS="" + +if [ "$PLATFORM" = "Darwin" ]; then + BREW_PREFIX=$(brew --prefix 2>/dev/null || echo /usr/local) + CPPFLAGS="-I${BREW_PREFIX}/include -I${BREW_PREFIX}/include/SDL2" + LDFLAGS="-L${BREW_PREFIX}/lib" + LIBS="-lpthread -L${BREW_PREFIX}/lib -lSDL2 -framework Cocoa" + echo "building unidasm from $G with brewed SDL2 (macOS)" + + # Patch generated makefiles to avoid expecting SDL2 as an Xcode framework. + # Replace occurrences of '-framework SDL2' with brewed linker flags and remove framework-specific rpaths. + BREW_LIBS="-L${BREW_PREFIX}/lib -lSDL2" + for f in "$G"/*.make "$G"/Makefile; do + if [ -f "$f" ]; then + # macOS sed supports '-i.bak' to write a backup file + sed -i.bak -e "s|-framework SDL2|${BREW_LIBS}|g" -e "s|-F/Library/Frameworks/||g" -e "s|-rpath /Library/Frameworks/||g" "$f" || true + fi + done + +else + # Linux / other Unix + echo "building unidasm from $G (non-macOS)" + + # Prefer pkg-config for SDL2 if available + if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists sdl2; then + CPPFLAGS="$(pkg-config --cflags sdl2 2>/dev/null || true)" + LDFLAGS="$(pkg-config --libs-only-L sdl2 2>/dev/null || true)" + # libs-only-l returns -lSDL2; keep full libs for LIBS + LIBS="$(pkg-config --libs sdl2 2>/dev/null || true) -lpthread" + else + # Fall back to /usr/local + BREW_PREFIX=$(brew --prefix 2>/dev/null || echo /usr/local) + CPPFLAGS="-I${BREW_PREFIX}/include" + LDFLAGS="-L${BREW_PREFIX}/lib" + LIBS="-lpthread -L${BREW_PREFIX}/lib -lSDL2" + fi + + # Add architecture-specific C++ include path if needed (for c++config.h) + GCC_VER=$(g++ -dumpversion | cut -d. -f1) + ARCH=$(g++ -dumpmachine 2>/dev/null || echo x86_64-linux-gnu) + STDCPP_ARCH_PATH="/usr/include/c++/$GCC_VER/$ARCH" + if [ -d $STDCPP_ARCH_PATH ]; then + CPPFLAGS="$CPPFLAGS -I$STDCPP_ARCH_PATH" + fi + + # Add multiarch SDL2 include paths if needed (for _real_SDL_config.h) + if [ -d /usr/include/x86_64-linux-gnu/SDL2 ]; then + CPPFLAGS="$CPPFLAGS -I/usr/include/x86_64-linux-gnu/SDL2 -I/usr/include/x86_64-linux-gnu" + fi +fi + +echo "CPPFLAGS=${CPPFLAGS} LDFLAGS=${LDFLAGS} LIBS=${LIBS}" + + + +echo "[DEBUG] Running: env CPPFLAGS=\"$CPPFLAGS\" LDFLAGS=\"$LDFLAGS\" LIBS=\"$LIBS\" make -C $G unidasm" +env CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS" LIBS="$LIBS" make -C "$G" unidasm || echo "[WARN] make failed, continuing anyway." + +# Try common result locations and copy to ~/.local/bin +DEST="$HOME/.local/bin" +mkdir -p "$DEST" +UNIDASM_CANDIDATES=( + "mame/build/generated/unidasm/unidasm" + "mame/build/generated/unidasm" + "mame/unidasm" + "$G/../../../../unidasm" + "$G/../../../../mame/unidasm" + "$G/../../../../mame/bin/unidasm" +) +FOUND=0 +for candidate in "${UNIDASM_CANDIDATES[@]}"; do + if [ -f "$candidate" ]; then + echo "[INFO] Found unidasm binary at: $candidate" + cp "$candidate" "$DEST/unidasm" + FOUND=1 + break + fi +done +if [ "$FOUND" -eq 0 ]; then + echo "[WARN] unidasm binary not found after build, but script will not abort." +fi diff --git a/scripts/ensure_task.cmd b/scripts/ensure_task.cmd new file mode 100644 index 0000000..078d689 --- /dev/null +++ b/scripts/ensure_task.cmd @@ -0,0 +1,33 @@ +@echo off +REM Cross-platform ensure_task for Windows (cmd.exe) +where task >nul 2>nul +if %ERRORLEVEL%==0 ( + echo [ensure_task] Task is already installed. + exit /b 0 +) + +where npm >nul 2>nul +if %ERRORLEVEL%==0 ( + echo [ensure_task] Installing Task via npm... + npm install -g @go-task/cli + exit /b %ERRORLEVEL% +) + +where winget >nul 2>nul +if %ERRORLEVEL%==0 ( + echo [ensure_task] Installing Task via winget... + winget install -e --id Task.Task + exit /b %ERRORLEVEL% +) + +REM Try choco as a fallback +where choco >nul 2>nul +if %ERRORLEVEL%==0 ( + echo [ensure_task] Installing Task via choco... + choco install go-task -y + exit /b %ERRORLEVEL% +) + +REM Could not install +>&2 echo [ensure_task] ERROR: Could not find a supported package manager to install Task. +exit /b 1 diff --git a/scripts/ensure_task.sh b/scripts/ensure_task.sh new file mode 100644 index 0000000..e72509b --- /dev/null +++ b/scripts/ensure_task.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Cross-platform script to ensure Go Task is installed +# Prefers npm, then Homebrew, apt, dnf, winget, or snap +set -e + +# Check if task is already installed +if command -v task >/dev/null 2>&1; then + echo "[ensure_task] Task is already installed." + exit 0 +fi + +# Prefer npm if available +if command -v npm >/dev/null 2>&1; then + echo "[ensure_task] Installing Task via npm..." + npm install -g @go-task/cli + exit $? +fi + +# macOS or Linux with Homebrew +if command -v brew >/dev/null 2>&1; then + echo "[ensure_task] Installing Task via Homebrew..." + brew install go-task/tap/go-task || brew install go-task + exit $? +fi + +# Ubuntu/Debian/Mint (apt) +if command -v apt >/dev/null 2>&1; then + echo "[ensure_task] Installing Task via apt..." + curl -1sLf 'https://dl.cloudsmith.io/public/task/task/setup.deb.sh' | sudo -E bash + sudo apt install -y task + exit $? +fi + +# Fedora/CentOS (dnf) +if command -v dnf >/dev/null 2>&1; then + echo "[ensure_task] Installing Task via dnf..." + curl -1sLf 'https://dl.cloudsmith.io/public/task/task/setup.rpm.sh' | sudo -E bash + sudo dnf install -y task + exit $? +fi + +# Snap (Linux/macOS) +if command -v snap >/dev/null 2>&1; then + echo "[ensure_task] Installing Task via snap..." + sudo snap install task --classic + exit $? +fi + +# Windows (winget) +if command -v winget >/dev/null 2>&1; then + echo "[ensure_task] Installing Task via winget..." + winget install -e --id Task.Task + exit $? +fi + +# If we reach here, we couldn't install Task +>&2 echo "[ensure_task] ERROR: Could not find a supported package manager to install Task." +exit 1 diff --git a/scripts/install-sdl2-i386.sh b/scripts/install-sdl2-i386.sh new file mode 100644 index 0000000..192dd8a --- /dev/null +++ b/scripts/install-sdl2-i386.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail +PLATFORM=$(uname -s 2>/dev/null || echo Unknown) +echo "[INFO] Checking for 32-bit SDL2 development libraries..." + +# Ensure i386 architecture is enabled +dpkg --print-foreign-architectures | grep -q i386 || { + echo "[INFO] Enabling i386 architecture..." + sudo dpkg --add-architecture i386 + sudo apt-get update +} + +echo "[INFO] Installing 32-bit SDL2 development package..." +sudo apt-get install -y libsdl2-dev:i386 + +# Check for 32-bit SDL2 library presence +if ls /usr/lib/i386-linux-gnu/libSDL2*.so* 1>/dev/null 2>&1; then + echo "[SUCCESS] 32-bit SDL2 development libraries are installed." +else + echo "[ERROR] 32-bit SDL2 development libraries are still missing!" + exit 1 +fi + + + # Ensure multilib support for 32/64-bit C++ headers if using apt-get + if [ "$PLATFORM" = "Linux" ] && command -v apt-get >/dev/null 2>&1; then + echo "Checking for gcc-multilib and g++-multilib..." + if ! dpkg -s gcc-multilib g++-multilib >/dev/null 2>&1; then + echo "Installing gcc-multilib and g++-multilib via apt-get (sudo required)..." + sudo apt-get update && sudo apt-get install -y gcc-multilib g++-multilib + else + echo "gcc-multilib and g++-multilib already installed." + fi + fi \ No newline at end of file diff --git a/tests/Makefile b/tests/Makefile index f6441b3..9042afe 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,9 @@ +CURDIR:=$(shell pwd) +BUILD_PATHS:=$(CURDIR)/build:$(CURDIR)/build/Debug:$(CURDIR)/build/Release:$(CURDIR)/scripts +export PATH:=$(BUILD_PATHS):$(PATH) + all: make -C ucom43 clean all make -C s2000 clean all