diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bcf66bf..68e273f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,9 +14,7 @@ jobs: - name: Update dependencies run: apt-get update - name: Install dependencies - run: apt-get install -y make libpng16-16 gcc-arm-none-eabi - - name: Setup CMake - uses: lukka/get-cmake@latest + run: apt-get install -y libpng16-16 gcc-arm-none-eabi - uses: actions/checkout@v1 - name: Download playdate SDK run: wget https://download.panic.com/playdate_sdk/Linux/PlaydateSDK-latest.tar.gz @@ -59,9 +57,7 @@ jobs: - name: Update dependencies run: apt-get update - name: Install dependencies - run: apt-get install -y make libpng16-16 gcc-arm-none-eabi xvfb libgtk-3-0 sudo libwebkit2gtk-4.0 libwebkit2gtk-4.0-dev libsdl2-dev pulseaudio - - name: Setup CMake - uses: lukka/get-cmake@latest + run: apt-get install -y libpng16-16 gcc-arm-none-eabi xvfb libgtk-3-0 sudo libwebkit2gtk-4.0 libwebkit2gtk-4.0-dev libsdl2-dev pulseaudio # Because we are headless there is no audio driver to interact with by default, which causes a set # of warnings to be emitted. This set of commands sets up a dummy audio sink that silences those warnings. diff --git a/README.md b/README.md index d424ce7..66cdbf9 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,10 @@ This package is an independent bindings library, not affiliated with Panic. ### Prerequisites - Playdate SDK -- Nim 1.6.10+ (check with `nim -v`) +- Nim 1.6.10+ (check with `nim -v`, untested with 2.0+) - Nimble 0.13.1 (check with `nimble -v`) - `PLAYDATE_SDK_PATH` environment variable -- Make and CMake (3.19+) -- [SDK Prerequisites](https://sdk.play.date/Inside%20Playdate%20with%20C.html#_prerequisites) based on OS, and: - - Linux: a C compiler, `arm-none-eabi-newlib` package (naming varies based on distro). - - macOS: a C compiler. Included in Xcode Command Line Tools. - - Windows: MinGW. [Getting started](https://code.visualstudio.com/docs/cpp/config-mingw) +- [SDK Prerequisites](https://sdk.play.date/Inside%20Playdate%20with%20C.html#_prerequisites) based on OS, and [MinGW on Windows](https://code.visualstudio.com/docs/cpp/config-mingw). ### Installation @@ -118,7 +114,25 @@ nimble simulate The example project `playdate_example` also contains VSCode launch configurations to build, start and debug your Nim application from the editor. -Each project also contains a simple CMakeLists.txt as a starting point in case you'd want to add libraries or other external code. +Each project contains a `config.nims` file that can be edited to customize how the project should be built, e.g. adding libraries or other external code.
+Here's an example of a `config.nims` that links a pre-built static library called chipmunk: +```nim +include playdate/build/config + +# Add a search path for libraries based on OS. +if defined(device): + switch("passL", "-L" & getCurrentDir() / "lib" / "device") +elif defined(windows): + switch("passL", "-L" & getCurrentDir() / "lib" / "windows") +elif defined(macosx): + switch("passL", "-L" & getCurrentDir() / "lib" / "macos") +elif defined(linux): + switch("passL", "-L" & getCurrentDir() / "lib" / "linux") +else: + echo "Platform not supported!" +# Link the chipmunk library. +switch("passL", "-lchipmunk") +``` --- This project is perfectly usable but do note that it's still a work in progress, here's what is missing right now: diff --git a/playdate.nimble b/playdate.nimble index 1a34866..d46a4e9 100644 --- a/playdate.nimble +++ b/playdate.nimble @@ -1,6 +1,6 @@ # Package -version = "0.9.3" +version = "0.10.0" author = "Samuele Zolfanelli" description = "Playdate Nim bindings with extra features." license = "MIT" diff --git a/playdate_example/.gitignore b/playdate_example/.gitignore index eff4ba5..09cff4a 100644 --- a/playdate_example/.gitignore +++ b/playdate_example/.gitignore @@ -1,2 +1,3 @@ -playdate_example.pdx -Source/pdex.* \ No newline at end of file +playdate.pdx +source/pdex.* +*.dSYM \ No newline at end of file diff --git a/playdate_example/CMakeLists.txt b/playdate_example/CMakeLists.txt deleted file mode 100644 index d41bc8e..0000000 --- a/playdate_example/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# -# Template CMakeLists.txt, customize it if necessary -# -# Variables: -# $ENV{PLAYDATE_PROJECT_NAME}: the project name -# $ENV{PLAYDATE_MODULE_DIR}: the path to the installed playdate Nim module -# $ENV{NIM_CACHE_DIR}: the path to the project Nim cache files -# $ENV{NIM_C_SOURCE_FILES}: list of semicolon-separated, Nim-generated C files that have to be compiled -# $ENV{NIM_INCLUDE_DIR}: the path to required Nim header files - -cmake_minimum_required(VERSION 3.14) -set(CMAKE_OSX_ARCHITECTURES x86_64 arm64 CACHE STRING "" FORCE) -set(CMAKE_C_STANDARD 11) - -# Setup the project and its name from the environment -project($ENV{PLAYDATE_PROJECT_NAME} C ASM) - -# Include the playdate module CMake configuration -include($ENV{PLAYDATE_MODULE_DIR}/playdate.cmake) - -# Targets: -# ${PLAYDATE_GAME_NAME}: simulator -# ${PLAYDATE_GAME_DEVICE}: device \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 9a466cf..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# -# Template CMakeLists.txt, customize it if necessary -# -# Variables: -# $ENV{PLAYDATE_PROJECT_NAME}: the project name -# $ENV{PLAYDATE_MODULE_DIR}: the path to the installed playdate Nim module -# $ENV{NIM_CACHE_DIR}: the path to the project Nim cache files -# $ENV{NIM_C_SOURCE_FILES}: list of semicolon-separated, Nim-generated C files that have to be compiled -# $ENV{NIM_INCLUDE_DIR}: the path to required Nim header files - -cmake_minimum_required(VERSION 3.14) -set(CMAKE_OSX_ARCHITECTURES x86_64 arm64 CACHE STRING "" FORCE) -set(CMAKE_C_STANDARD 11) - -# Setup the project and its name from the environment -project($ENV{PLAYDATE_PROJECT_NAME} C ASM) - -# Include the playdate module CMake configuration, this defines the targets -include($ENV{PLAYDATE_MODULE_DIR}/playdate.cmake) - -# Targets: -# ${PLAYDATE_GAME_NAME}: simulator -# ${PLAYDATE_GAME_DEVICE}: device \ No newline at end of file diff --git a/src/playdate.cmake b/src/playdate.cmake deleted file mode 100644 index 100fe96..0000000 --- a/src/playdate.cmake +++ /dev/null @@ -1,103 +0,0 @@ -# -# CMake include file for Playdate games -# -cmake_minimum_required(VERSION 3.19) - -if (NOT $ENV{PLAYDATE_SDK_PATH} STREQUAL "") - # Convert path from Windows - file(TO_CMAKE_PATH $ENV{PLAYDATE_SDK_PATH} SDK) -else() - execute_process( - COMMAND bash -c "egrep '^\\s*SDKRoot' $HOME/.Playdate/config" - COMMAND head -n 1 - COMMAND cut -c9- - OUTPUT_VARIABLE SDK - OUTPUT_STRIP_TRAILING_WHITESPACE - ) -endif() - -if (NOT EXISTS ${SDK}) - message(FATAL_ERROR "SDK Path not found; set ENV value PLAYDATE_SDK_PATH") - return() -endif() - -set(CMAKE_CONFIGURATION_TYPES "Debug;Release") -set(CMAKE_XCODE_GENERATE_SCHEME TRUE) - -# Game Name Customization -set(PLAYDATE_GAME_NAME $ENV{PLAYDATE_PROJECT_NAME}_simulator) -set(PLAYDATE_GAME_DEVICE $ENV{PLAYDATE_PROJECT_NAME}_device) - -# Include Nim required headers -include_directories($ENV{NIM_INCLUDE_DIR}) - -if (TOOLCHAIN STREQUAL "armgcc") - add_executable(${PLAYDATE_GAME_DEVICE} $ENV{NIM_C_SOURCE_FILES}) - target_link_libraries(${PLAYDATE_GAME_DEVICE} rdimon c m gcc nosys) -else() - add_library(${PLAYDATE_GAME_NAME} SHARED $ENV{NIM_C_SOURCE_FILES}) -endif() - -include(${SDK}/C_API/buildsupport/playdate.cmake) - -set(BUILD_SUB_DIR "") - -if (TOOLCHAIN STREQUAL "armgcc") - set_property(TARGET ${PLAYDATE_GAME_DEVICE} PROPERTY OUTPUT_NAME "${PLAYDATE_GAME_DEVICE}.elf") - - add_custom_command( - TARGET ${PLAYDATE_GAME_DEVICE} POST_BUILD - COMMAND ${CMAKE_STRIP} --strip-unneeded -R .comment -g - ${PLAYDATE_GAME_DEVICE}.elf - -o ${CMAKE_CURRENT_SOURCE_DIR}/source/pdex.elf - ) - - add_custom_command( - TARGET ${PLAYDATE_GAME_DEVICE} POST_BUILD - COMMAND ${PDC} source playdate.pdx - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) - - set_property( - TARGET ${PLAYDATE_GAME_DEVICE} PROPERTY ADDITIONAL_CLEAN_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/playdate.pdx - ) - -else () - if (MSVC) - # MSVC not supported - message(FATAL_ERROR "MSVC is not supported! Use MinGW.") - - elseif(MINGW) - target_compile_definitions(${PLAYDATE_GAME_NAME} PUBLIC _WINDLL=1) - set(DYLIB_EXT "dll") - - elseif(APPLE) - target_sources(${PLAYDATE_GAME_NAME} PRIVATE ${SDK}/C_API/buildsupport/setup.c) - set(DYLIB_EXT "dylib") - - elseif(UNIX) - target_sources(${PLAYDATE_GAME_NAME} PRIVATE ${SDK}/C_API/buildsupport/setup.c) - set(DYLIB_EXT "so") - - else() - message(FATAL_ERROR "Platform not supported!") - endif() - - add_custom_command( - TARGET ${PLAYDATE_GAME_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_BINARY_DIR}/${BUILD_SUB_DIR}lib${PLAYDATE_GAME_NAME}.${DYLIB_EXT} - ${CMAKE_CURRENT_SOURCE_DIR}/source/pdex.${DYLIB_EXT}) - - set_property( - TARGET ${PLAYDATE_GAME_NAME} PROPERTY ADDITIONAL_CLEAN_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/playdate.pdx - ) - - add_custom_command( - TARGET ${PLAYDATE_GAME_NAME} POST_BUILD - COMMAND ${PDC} ${CMAKE_CURRENT_SOURCE_DIR}/source - ${CMAKE_CURRENT_SOURCE_DIR}/playdate.pdx) - -endif () diff --git a/src/playdate/api.nim b/src/playdate/api.nim index df85c6c..d8fc949 100644 --- a/src/playdate/api.nim +++ b/src/playdate/api.nim @@ -25,4 +25,29 @@ macro initSDK*() = when not defined(simulator): proc fini() {.cdecl, exportc: "_fini".} = - discard \ No newline at end of file + discard + +when not defined(simulator) and defined(release): + proc close(file: cint): cint {.cdecl, exportc: "_close".} = + return -1 + + proc fstat(file: cint, st: pointer): cint {.cdecl, exportc: "_fstat".} = + return -1 + + proc getpid(): cint {.cdecl, exportc: "_getpid".} = + return 1 + + proc isatty(file: cint): cint {.cdecl, exportc: "_isatty".} = + return 0 + + proc kill(pid, sig: cint): cint {.cdecl, exportc: "_kill".} = + return 0 + + proc lseek(file, pos, whence: cint): cint {.cdecl, exportc: "_lseek".} = + return -1 + + proc read(file: cint, pt: ptr cchar, len: cint): cint {.cdecl, exportc: "_read".} = + return 0 + + proc write(handle: cint, data: ptr cchar, size: cint): cint {.cdecl, exportc: "_write".} = + return -1 \ No newline at end of file diff --git a/src/playdate/bindings/system.nim b/src/playdate/bindings/system.nim index df0a5cd..e38e3fc 100644 --- a/src/playdate/bindings/system.nim +++ b/src/playdate/bindings/system.nim @@ -45,8 +45,8 @@ sdktype: getSecondsSinceEpoch {.importc: "getSecondsSinceEpoch".}: proc (milliseconds: ptr cuint): cuint {.cdecl, raises: [].} drawFPS {.importsdk.}: proc (x: cint; y: cint) setUpdateCallback {.importc: "setUpdateCallback".}: proc (update: PDCallbackFunctionRaw, userdata: pointer) {.cdecl, raises: [].} - getButtonState {.importc: "getButtonState".}: proc (current: ptr uint32; - pushed: ptr uint32; released: ptr uint32) {.cdecl, raises: [].} + getButtonState {.importc: "getButtonState".}: proc (current: ptr PDButton; + pushed: ptr PDButton; released: ptr PDButton) {.cdecl, raises: [].} setPeripheralsEnabled* {.importc.}: proc (mask: PDPeripherals) {.cdecl, raises: [].} getAccelerometer {.importc: "getAccelerometer".}: proc (outx: ptr cfloat; outy: ptr cfloat; outz: ptr cfloat) {.cdecl, raises: [].} diff --git a/src/playdate/build/config.nim b/src/playdate/build/config.nim index 3b6e5ea..679056b 100644 --- a/src/playdate/build/config.nim +++ b/src/playdate/build/config.nim @@ -1,39 +1,142 @@ import os +when defined(device): + import strformat + +import utils # This file is designed to be `included` directly from a `config.nims` file, which will make `switch` and `task` -# implicitly available. This block just fixes auto-complete in IDEs +# implicitly available. This block just fixes auto-complete in IDEs. when not compiles(task): import system/nimscript +const headlessTesting = defined(simulator) and declared(test) +const nimbleTesting = not defined(simulator) and not defined(devide) and declared(test) +const testing = headlessTesting or nimbleTesting + +if not testing: + switch("noMain", "on") +switch("backend", "c") switch("mm", "arc") -switch("noMain", "on") -switch("cc", "gcc") -switch("compileOnly", "on") +switch("os", "any") +switch("parallelBuild", "0") # Auto-detect +switch("hint", "CC:on") + +switch("arm.any.gcc.exe", "arm-none-eabi-gcc") +switch("arm.any.gcc.linkerexe", "arm-none-eabi-gcc") +switch("arm.any.gcc.options.linker", "-static") +switch("clang.options.linker", "") +switch("mingw.options.linker", "") +switch("gcc.options.linker", "") +switch("gcc.options.debug", "-g3 -O0 -gdwarf-3") + +switch("passC", "-DTARGET_EXTENSION=1") +switch("passC", "-Wall") +switch("passC", "-Wno-unknown-pragmas") +switch("passC", "-Wdouble-promotion") +switch("passC", "-I" & sdkPath() / "C_API") when defined(device): - echo "compiling for device" + switch("gcc.options.always", "") + switch("nimcache", nimcacheDir() / "device") - switch("define", "release") - switch("checks", "off") + switch("cc", "gcc") + switch("app", "console") switch("cpu", "arm") - switch("define", "useMalloc") - switch("opt", "speed") - switch("os", "any") - - switch("debuginfo", "off") - switch("index", "off") + switch("checks", "off") switch("threads", "off") - switch("stackTrace", "off") - switch("lineTrace", "off") switch("assertions", "off") switch("hotCodeReloading", "off") - switch("debugger", "native") + switch("define", "useMalloc") + + let heapSize = 8388208 + let stackSize = 61800 + switch("passC", fmt"-D__HEAP_SIZE={heapSize} -D__STACK_SIZE={stackSize}") + switch("passC", "-DTARGET_PLAYDATE=1") + switch("passC", "-mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -D__FPU_USED=1") + switch("passC", "-falign-functions=16 -fomit-frame-pointer -gdwarf-2 -fverbose-asm") + switch("passC", "-ffunction-sections -fdata-sections -mword-relocations -fno-common") + # Disabled warnings + switch("passC", "-Wno-unused-but-set-variable -Wno-unused-label -Wno-parentheses -Wno-discarded-qualifiers -Wno-array-bounds") + + switch("passL", "-nostartfiles") + switch("passL", "-mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -D__FPU_USED=1") + switch("passL", "-T" & sdkPath() / "C_API" / "buildsupport" / "link_map.ld") + switch("passL", "-Wl,-Map=game.map,--cref,--gc-sections,--emit-relocs") + switch("passL", "--entry eventHandlerShim") + switch("passL", "-lc -lm -lgcc") + + if defined(release): + switch("define", "release") + switch("opt", "speed") + switch("debuginfo", "off") + switch("index", "off") + switch("stackTrace", "off") + switch("lineTrace", "off") + switch("passC", "--specs=nosys.specs") + switch("passL", "-lnosys --specs=nosys.specs") + else: + switch("define", "debug") + switch("opt", "none") + switch("debuginfo", "on") + switch("index", "on") + switch("stackTrace", "on") + switch("lineTrace", "on") + switch("passL", "-lrdimon --specs=rdimon.specs") when defined(simulator): - echo "compiling for simulator" + switch("define", "simulator") switch("nimcache", nimcacheDir() / "simulator") + if not testing: + # Switching to a lib build makes tests not work. + switch("app", "lib") + + if defined(macosx): + switch("cc", "clang") + switch("passC", "-arch x86_64 -arch arm64") + switch("passL", "-arch x86_64 -arch arm64") + elif defined(linux): + switch("cc", "gcc") + switch("passC", "-fPIC") + switch("passL", "-fPIC") + elif defined(windows): + switch("define", "mingw") + switch("cc", "gcc") + switch("passC", "-D_WINDLL=1") + + switch("checks", "on") + switch("index", "on") + switch("debuginfo", "on") + switch("stackTrace", "on") + switch("lineTrace", "on") + switch("lineDir", "on") + switch("debugger", "native") + switch("opt", "none") + switch("define", "debug") switch("define", "nimAllocPagesViaMalloc") + switch("define", "nimPage256") + + switch("passC", "-DTARGET_SIMULATOR=1") + switch("passC", "-Wstrict-prototypes") + +if nimbleTesting: + # Compiling for tests. + switch("define", "simulator") + switch("nimcache", nimcacheDir() / "simulator") + + if defined(macosx): + switch("cc", "clang") + switch("passC", "-arch x86_64 -arch arm64") + switch("passL", "-arch x86_64 -arch arm64") + elif defined(linux): + switch("cc", "gcc") + switch("passC", "-fPIC") + switch("passL", "-fPIC") + elif defined(windows): + switch("define", "mingw") + switch("cc", "gcc") + switch("passC", "-D_WINDLL=1") + switch("checks", "on") switch("index", "on") switch("debuginfo", "on") @@ -42,5 +145,16 @@ when defined(simulator): switch("lineDir", "on") switch("debugger", "native") switch("opt", "none") + + switch("define", "debug") + switch("define", "nimAllocPagesViaMalloc") switch("define", "nimPage256") - switch("define", "simulator") + + switch("passC", "-DTARGET_SIMULATOR=1") + switch("passC", "-Wstrict-prototypes") +else: + # Add extra files to compile last, so that + # they get compiled in the correct nimcache folder. + # Windows doesn't like having setup.c compiled. + if defined(device) or not defined(windows): + switch("compile", sdkPath() / "C_API" / "buildsupport" / "setup.c") \ No newline at end of file diff --git a/src/playdate/build/nimble.nim b/src/playdate/build/nimble.nim index 9e2787d..bc0b05d 100644 --- a/src/playdate/build/nimble.nim +++ b/src/playdate/build/nimble.nim @@ -1,155 +1,104 @@ -import sequtils, strutils, os, json +import sequtils, strutils, os, strformat + +import utils # This file is designed to be `included` directly from a nimble file, which will make `switch` and `task` # implicitly available. This block just fixes auto-complete in IDEs when not compiles(task): import system/nimscript -type Target = enum - simulator = "simulator" - device = "device" - -type CompileInstructions = object - compile: seq[array[2, string]] - -type BuildFail = object of Defect - - -proc nimble(args: varargs[string]) = - ## Executes nimble with the given set of arguments - exec @["nimble"].concat(args.toSeq).join(" ") - -proc playdatePath(): string = - ## Returns the path of the playdate nim module - var (paths, exitCode) = gorgeEx("nimble path playdate") - paths.stripLineEnd() - if exitCode != 0: - raise BuildFail.newException("Could not find the playdate nimble module!") - let pathsSeq = paths.splitLines(false) - # If multiple package paths are found, use the last one - let path = pathsSeq[pathsSeq.len - 1] - if path.strip == "" or not path.strip.dirExists: - raise BuildFail.newException("Playdate nimble module is not a directory: " & path) - return path - -proc pdxName(): string = - ## The name of the pdx file to generate - "playdate" & ".pdx" - -const SDK_ENV_VAR = "PLAYDATE_SDK_PATH" - -proc sdkPath*(): string = - ## Returns the path of the playdate SDK - let fromEnv = getEnv(SDK_ENV_VAR) - let sdkPathCache = getConfigDir() / projectName() / SDK_ENV_VAR - - if fromEnv != "": - mkDir(sdkPathCache.parentDir) - writeFile(sdkPathCache, fromEnv) - return fromEnv - - if fileExists(sdkPathCache): - let fromFile = readFile(sdkPathCache) - if fromFile != "": - echo "Read SDK path from file: " & sdkPathCache - echo "SDK Path: " & fromFile - return fromFile - - raise BuildFail.newException("SDK environment variable is not set: " & SDK_ENV_VAR) - -proc simulatorPath(open: bool = false): string = - if defined(windows): - return sdkPath() / "bin" / "PlaydateSimulator.exe" - elif defined(macosx): - return (if open: "open " else: "") & sdkPath() / "bin" / "Playdate\\ Simulator.app" - else: - return sdkPath() / "bin" / "PlaydateSimulator" - -proc filesToCompile(target: Target): seq[string] = - let jsonString = readFile(nimcacheDir() / $target / projectName() & ".json") - let instructions = parseJson(jsonString).to(CompileInstructions) - - return instructions.compile.map( - proc(entry: array[2, string]): string = - return entry[0] - ) - -proc build(target: Target) = - ## Builds a target - let buildDir = "build" / $target - - putEnv(SDK_ENV_VAR, sdkPath()) - putEnv("PLAYDATE_MODULE_DIR", playdatePath()) - putEnv("PLAYDATE_PROJECT_NAME", projectName()) - putEnv("NIM_INCLUDE_DIR", getCurrentCompilerExe().parentDir.parentDir / "lib") - putEnv("NIM_C_SOURCE_FILES", filesToCompile(target).join(";").replace(DirSep, '/')) - putEnv("NIM_CACHE_DIR", (nimcacheDir() / $target).replace(DirSep, '/')) - - mkDir(buildDir) - withDir(buildDir): - case target: - of simulator: - if defined(windows): - exec("cmake ../.. -DCMAKE_BUILD_TYPE=Debug" & " -G \"MinGW Makefiles\"") - else: - exec("cmake ../.. -DCMAKE_BUILD_TYPE=Debug" & " -G \"Unix Makefiles\"") - of device: - exec("cmake ../.. -DCMAKE_BUILD_TYPE=Release" & " -G \"Unix Makefiles\" --toolchain=" & (sdkPath() / "C_API" / "buildsupport" / "arm.cmake")) - exec("make") - -proc taskArgs(taskName: string): seq[string] = - let args = command_line_params() - let argStart = args.find(taskName) + 1 - return args[argStart..^1] - - -task clean, "Clean the project folders": + +proc bundlePDX() = + ## Bundles the pdx file + exec(pdcPath() & " --verbose -sdkpath " & sdkPath() & " source playdate") + +proc postBuild(target: Target) = + ## Performs post-build cleanup and prepares files for bundling. + case target: + of simulator: + if defined(windows): + mvFile(projectName() & ".exe", "source" / "pdex.dll") + elif defined(macosx): + mvFile(projectName(), "source" / "pdex.dylib") + rmDir("source" / "pdex.dSYM") + mvFile(projectName() & ".dSYM", "source" / "pdex.dSYM") + elif defined(linux): + mvFile(projectName(), "source" / "pdex.so") + of device: + if defined(windows): + mvFile(projectName() & ".exe", "source" / "pdex.elf") + else: + mvFile(projectName(), "source" / "pdex.elf") + rmFile("game.map") + + +task clean, "Clean the project files and folders": let args = taskArgs("clean") - - if args.contains("--simulator"): - rmDir(nimcacheDir() / $Target.simulator) - rmDir("build" / $Target.simulator) + # Used to remove debug (_d) and release (_r) cache folders. + let baseCacheDir = nimcacheDir()[0..^2] + if args.contains("simulator"): + rmDir((baseCacheDir & "d") / $Target.simulator) + rmDir((baseCacheDir & "r") / $Target.simulator) + rmDir("source" / "pdex.dSYM") rmFile("source" / "pdex.dylib") rmFile("source" / "pdex.dll") rmFile("source" / "pdex.so") - elif args.contains("--device"): - rmDir(nimcacheDir() / $Target.device) - rmDir("build" / $Target.device) + elif args.contains("device"): + rmDir((baseCacheDir & "d") / $Target.device) + rmDir((baseCacheDir & "r") / $Target.device) rmFile("source" / "pdex.bin") rmFile("source" / "pdex.elf") else: + rmDir((baseCacheDir & "d")) + rmDir((baseCacheDir & "r")) rmDir(nimcacheDir()) rmDir(pdxName()) - rmDir("build") + rmDir("source" / "pdex.dSYM") rmFile("source" / "pdex.bin") rmFile("source" / "pdex.dylib") rmFile("source" / "pdex.dll") rmFile("source" / "pdex.so") rmFile("source" / "pdex.elf") - -task cdevice, "Generate C files for the device": - nimble "-d:device", "build" - -task csimulator, "Generate C files for the simulator": - nimble "-d:simulator", "build" + rmFile("game.map") + rmFile(projectName()) + rmFile(projectName() & ".exe") task simulator, "Build for the simulator": - nimble "csimulator" - build Target.simulator + let args = taskArgs("simulator") + if args.contains("release"): + nimble "-d:simulator", "-d:release", "build", "--verbose" + else: + nimble "-d:simulator", "-d:debug", "build", "--verbose" + postBuild(Target.simulator) + bundlePDX() task simulate, "Build and run in the simulator": nimble "simulator" exec (simulatorPath(open = true) & " " & pdxName()) task device, "Build for the device": - nimble "cdevice" - build Target.device + let args = taskArgs("device") + if args.contains("debug"): + nimble "-d:device", "-d:debug", "build", "--verbose" + else: + nimble "-d:device", "-d:release", "build", "--verbose" + postBuild(Target.device) + bundlePDX() task all, "Build for both the simulator and the device": - nimble "csimulator" - build Target.simulator - nimble "cdevice" - build Target.device + let args = taskArgs("all") + var simulatorBuild = "debug" + var deviceBuild = "release" + # Only release device build are supported on macOS at the moment. + if args.contains("debug") and not defined(macosx): + deviceBuild = "debug" + elif args.contains("release"): + simulatorBuild = "release" + nimble "-d:simulator", fmt"-d:{simulatorBuild}", "build", "--verbose" + postBuild(Target.simulator) + nimble "-d:device", fmt"-d:{deviceBuild}", "build", "--verbose" + postBuild(Target.device) + bundlePDX() task setup, "Initialize the build structure": ## Creates a default source directory if it doesn't already exist @@ -158,8 +107,6 @@ task setup, "Initialize the build structure": # to the config path discard sdkPath() - if not fileExists("CMakeLists.txt"): - cpFile(playdatePath() / "CMakeLists.txt", "CMakeLists.txt") if not dirExists("source"): mkDir "source" @@ -192,5 +139,6 @@ task setup, "Initialize the build structure": if not fileExists( ".gitignore"): ".gitignore".writeFile([ pdxName(), - "source/pdex.*" + "source/pdex.*", + "*.dSYM" ].join("\n")) diff --git a/src/playdate/build/utils.nim b/src/playdate/build/utils.nim new file mode 100644 index 0000000..6c3dc46 --- /dev/null +++ b/src/playdate/build/utils.nim @@ -0,0 +1,74 @@ +import sequtils, strutils, os, json + +when not compiles(task): + import system/nimscript + +type Target* = enum + ## Target of the compilation process, simulator or device + simulator = "simulator" + device = "device" + +type CompileInstructions* = object + compile: seq[array[2, string]] + +type BuildFail* = object of Defect + + +const SDK_ENV_VAR* = "PLAYDATE_SDK_PATH" + + +proc nimble*(args: varargs[string]) = + ## Executes nimble with the given set of arguments + exec @["nimble"].concat(args.toSeq).join(" ") + +proc pdxName*(): string = + ## The name of the pdx file to generate + "playdate" & ".pdx" + +proc sdkPath*(): string = + ## Returns the path of the playdate SDK + let fromEnv = getEnv(SDK_ENV_VAR) + let sdkPathCache = getConfigDir() / projectName() / SDK_ENV_VAR + + if fromEnv != "": + mkDir(sdkPathCache.parentDir) + writeFile(sdkPathCache, fromEnv) + return fromEnv + + if fileExists(sdkPathCache): + let fromFile = readFile(sdkPathCache) + if fromFile != "": + echo "Read SDK path from file: " & sdkPathCache + echo "SDK Path: " & fromFile + return fromFile + + raise BuildFail.newException("SDK environment variable is not set: " & SDK_ENV_VAR) + +proc simulatorPath*(open: bool = false): string = + ## Returns the path of the playdate simulator + if defined(windows): + return sdkPath() / "bin" / "PlaydateSimulator.exe" + elif defined(macosx): + return (if open: "open " else: "") & sdkPath() / "bin" / "Playdate\\ Simulator.app" + else: + return sdkPath() / "bin" / "PlaydateSimulator" + +proc pdcPath*(): string = + ## Returns the path of the pdc playdate utility + return sdkPath() / "bin" / "pdc" + +proc filesToCompile*(target: Target): seq[string] = + ## Returns the list of C files that have to be compiled + let jsonString = readFile(nimcacheDir() / $target / projectName() & ".json") + let instructions = parseJson(jsonString).to(CompileInstructions) + + return instructions.compile.map( + proc(entry: array[2, string]): string = + return entry[0] + ) + +proc taskArgs*(taskName: string): seq[string] = + ## Returns the arguments the current task `taskName` has received + let args = command_line_params() + let argStart = args.find(taskName) + 1 + return args[argStart..^1] \ No newline at end of file diff --git a/src/playdate/system.nim b/src/playdate/system.nim index 22aa71f..a4789e9 100644 --- a/src/playdate/system.nim +++ b/src/playdate/system.nim @@ -49,7 +49,7 @@ proc setUpdateCallback*(this: ptr PlaydateSys, update: PDCallbackFunction) = proc getButtonsState* (this: ptr PlaydateSys): tuple[current: PDButtons, pushed: PDButtons, released: PDButtons] = privateAccess(PlaydateSys) var current, pushed, released: uint32 - this.getButtonState(addr(current), addr(pushed), addr(released)) + this.getButtonState(cast[ptr PDButton](addr(current)), cast[ptr PDButton](addr(pushed)), cast[ptr PDButton](addr(released))) return (current: cast[PDButtons](current), pushed: cast[PDButtons](pushed), released: cast[PDButtons](released)) proc getAccelerometer* (this: ptr PlaydateSys): tuple[x: float, y: float, z: float] = diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..09cff4a --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,3 @@ +playdate.pdx +source/pdex.* +*.dSYM \ No newline at end of file diff --git a/tests/config.nims b/tests/config.nims index a9b6499..987084f 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -1,10 +1,19 @@ -import os +# Hack to pretend this is not a lib, the config file checks if test is declared. +proc test*() = discard +include ../src/playdate/build/config -import ../src/playdate/build/nimble - -switch("compileOnly", "off") -switch("noMain", "off") -switch("path", "$projectDir/../src") -switch("passC", "-I" & sdkPath() & "/C_API -DTARGET_EXTENSION=1") -switch("define", "simulator") -switch("nimcache", nimcacheDir() / "simulator") \ No newline at end of file +# if headless testing, switch noMain off to include required symbols. +if defined(simulator): + switch("noMain", "off") +# Make the tests use the local playdate package. +switch("path", projectDir() / ".." / "src") +# This tests package fakes it's not a lib but it actually is. +if defined(simulator): + switch("passL", "-shared") +# Reset the os to its real value to make the tests run. +if defined(windows): + switch("os", "windows") +elif defined(macosx): + switch("os", "macosx") +elif defined(linux): + switch("os", "linux") \ No newline at end of file diff --git a/tests/src/playdate_tests.nim b/tests/src/playdate_tests.nim index 29b68e7..cbf2adc 100644 --- a/tests/src/playdate_tests.nim +++ b/tests/src/playdate_tests.nim @@ -1,5 +1,5 @@ ## -## This file is the primary entry poit for running the tests inside the simulator. +## This file is the primary entry point for running the tests inside the simulator. ## It requires that we spin up the full Lua runtime, as that allows us to then ## exit from the simulator. ##