diff --git a/.github/actions/build-setup/action.yml b/.github/actions/build-setup/action.yml index f6453b9..a47da01 100644 --- a/.github/actions/build-setup/action.yml +++ b/.github/actions/build-setup/action.yml @@ -17,7 +17,13 @@ runs: - run: nimble --accept refresh shell: bash - - run: nimble install + - run: nimble install --verbose + shell: bash + + - run: echo "/github/home/.nimble/bin" >> $GITHUB_PATH + shell: bash + + - run: which pdn shell: bash - name: Locally publish playdate nimble package diff --git a/.github/actions/project-setup/action.yml b/.github/actions/project-setup/action.yml index e441e7e..21e721d 100644 --- a/.github/actions/project-setup/action.yml +++ b/.github/actions/project-setup/action.yml @@ -6,11 +6,6 @@ runs: using: "composite" steps: - - name: Nimble example setup - shell: bash - working-directory: ${{ inputs.working-directory }} - run: nimble configure; - - name: Install dependencies shell: bash working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc25881..7859aea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,9 +12,9 @@ jobs: - uses: ./.github/actions/project-setup with: working-directory: ./playdate_example - - run: nimble simulator + - run: pdn simulator working-directory: ./playdate_example - + example-project: strategy: matrix: @@ -30,7 +30,7 @@ jobs: - uses: ./.github/actions/project-setup with: working-directory: ./playdate_example - - run: nimble ${{ matrix.target }} + - run: pdn ${{ matrix.target }} working-directory: ./playdate_example tests: @@ -77,10 +77,7 @@ jobs: pulseaudio -D --exit-idle-time=-1 pactl load-module module-null-sink sink_name=SpeakerOutput sink_properties=device.description="Dummy_Output" - - run: echo 'switch("d", "${{ matrix.build-flags }}")' >> configs.nims - working-directory: ./tests - - - run: nimble simulator + - run: pdn simulator -d:${{ matrix.build-flags }} working-directory: ./tests # The first time the simulator runs, it prompts the user with an alert. Obviously, we're running headless, @@ -99,4 +96,4 @@ jobs: working-directory: ./tests run: | export HOME="/config" - xvfb-run ../PlaydateSDK-*/bin/PlaydateSimulator tests.pdx \ No newline at end of file + xvfb-run ../PlaydateSDK-*/bin/PlaydateSimulator playdate_tests.pdx \ No newline at end of file diff --git a/.gitignore b/.gitignore index 637f30c..0cc1747 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ htmldocs/ src/tests/ pdex.bin *.pdx +tests/source/pdxinfo # macOS general .DS_Store @@ -58,4 +59,9 @@ tests/t_* tests/playdate_tests # IntelliJ IDEA -.idea \ No newline at end of file +.idea +nimble.develop +nimble.paths + +# Binaries +pdn diff --git a/README.md b/README.md index 97552ae..1e0046f 100644 --- a/README.md +++ b/README.md @@ -47,42 +47,31 @@ If you want to start from scratch, here are the steps to follow: 1. If you haven't done it already, start creating a folder (snake_case) and initializing your nimble package inside it running: -``` -nimble init -``` -Choose `binary` as the package type. + ```shell + nimble init + ``` + + Choose `binary` as the package type. 2. Install the `playdate` package: -``` -nimble install playdate -``` + ```shell + nimble install playdate + ``` -3. Add the `playdate` package as a dependency and configure the build tasks by running the following: +3. Add the `playdate` package as a dependency in your nimble file: -``` -echo 'requires "playdate"' >> *.nimble; -echo 'include playdate/build/nimble' >> *.nimble; -echo 'include playdate/build/config' > config.nims; -``` - -4. Finally, run this command to setup the structure of the project, which prepares your application to be compiled and bundled correctly: - -``` -nimble configure -``` + ```shell + echo 'requires "playdate"' >> *.nimble; + ``` ## Usage -If you haven't done it already, install the package: -``` -nimble install playdate -``` - `playdate_example` contains a basic example of the bindings utilization. The example code is in `playdate_example/src/playdate_example.nim`. Here's also a minimal snippet to make a Nim application: + ```nim import playdate/api @@ -96,35 +85,38 @@ proc handler(event: PDSystemEvent, keycode: uint) {.raises: [].} = if event == kEventInit: # Errors are handled through exceptions, this is an inline try/except nimLogoBitmap = try: playdate.graphics.newBitmap("/images/nim_logo") except: nil - # playdate is the global PlaydateAPI instance, available when playdate/api is imported + # playdate is the global PlaydateAPI instance, available when playdate/api is imported playdate.system.setUpdateCallback(update) # Used to setup the SDK entrypoint initSDK() ``` -Compile the project (pdx) for the simulator using: +Compile the project (pdx) for the simulator using the `pdn` binary that gets added when +you ran `nimble install playdate`: + ```sh -nimble simulator +pdn simulator ``` + For the device (pdx): + ```sh -nimble device -``` -For simulator + device (pdx): -```sh -nimble all +pdn device ``` You can also build for simulator and launch it in one command: + ```sh -nimble simulate +pdn simulate ``` The example project `playdate_example` also contains VSCode launch configurations to build, start and debug your Nim application from the editor. -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.
+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 @@ -151,6 +143,7 @@ There are two ways you can use Nim and Lua in the same project: 2. The main loop is defined in Lua, but you want to call Nim functions. Either way, you can provide Lua with your Nim functions during Lua initialization: + ```nim proc nimInsideLua(state: LuaStatePtr): cint {.cdecl, raises: [].} = ... @@ -164,9 +157,10 @@ proc handler(event: PDSystemEvent, keycode: uint) {.raises: [].} = ``` Calling a Lua function from Nim: + ```nim try: - # Push the argument first + # Push the argument first playdate.lua.pushInt(5) playdate.lua.callFunction("funcWithOneArgument", 1) except: diff --git a/config.nims b/config.nims new file mode 100644 index 0000000..8ee48d2 --- /dev/null +++ b/config.nims @@ -0,0 +1,4 @@ +# begin Nimble config (version 2) +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config diff --git a/playdate.nimble b/playdate.nimble index 1e9a35d..134a215 100644 --- a/playdate.nimble +++ b/playdate.nimble @@ -5,9 +5,9 @@ author = "Samuele Zolfanelli" description = "Playdate Nim bindings with extra features." license = "MIT" srcDir = "src" - +installExt = @["nim"] +bin = @["pdn"] # Dependencies requires "nim >= 2.0.0" -# requires "nimble < 0.14.0" diff --git a/playdate_example/.gitignore b/playdate_example/.gitignore index b74210d..74a53b5 100644 --- a/playdate_example/.gitignore +++ b/playdate_example/.gitignore @@ -1,3 +1,6 @@ *.pdx source/pdex.* *.dSYM +playdate_example.pdx.zip +playdate_example.pdx +source/pdxinfo \ No newline at end of file diff --git a/playdate_example/source/pdxinfo b/playdate_example/source/pdxinfo deleted file mode 100644 index 9942484..0000000 --- a/playdate_example/source/pdxinfo +++ /dev/null @@ -1,4 +0,0 @@ -name=Playdate Example -author=Samuele Zolfanelli -description=An example application using the Playdate Nim bindings -bundleId=com.samdze.playdate_example \ No newline at end of file diff --git a/src/pdn.nim b/src/pdn.nim new file mode 100644 index 0000000..23182ed --- /dev/null +++ b/src/pdn.nim @@ -0,0 +1,66 @@ +import std/[parseopt, strutils, os, macros, options] +import playdate/build/[utils, actions] + +type + BuildCommand = enum simulate, simulator, device, clean, bundle + ## The various actions that can be executed by this script + + CliConfig = object + ## Configurations collected from the Cli + command: BuildCommand + noAutoConfig: bool + sdkPath: Option[string] + extraArgs: seq[string] + +proc getCliConfig(): CliConfig = + ## Parses the build configuration from the input options + for kind, key, val in getopt(): + template addExtraArg() = + var command = if kind == cmdLongOption: "--" else: "-" + command &= key + if val != "": + command &= ":" & val + result.extraArgs.add(command) + + case kind + of cmdArgument: + result.command = parseEnum[BuildCommand](key) + of cmdLongOption, cmdShortOption: + case key + of "sdk-path": result.sdkPath = some(val) + of "no-auto-config": result.noAutoConfig = true + else: addExtraArg() + of cmdEnd: + discard + +let build = getCliConfig() + +let dump = getNimbleDump() + +let conf = PlaydateConf( + dump: dump, + kind: if build.command == device: DeviceBuild else: SimulatorBuild, + sdkPath: sdkPath(build.sdkPath), + pdxName: dump.name & ".pdx", + nimbleArgs: build.extraArgs, + noAutoConfig: build.noAutoConfig +) + +case build.command +of device, simulate, simulator: + conf.configureBuild() +of clean, bundle: + discard + +case build.command +of simulator: + conf.simulatorBuild() +of simulate: + conf.simulatorBuild() + conf.runSimulator() +of device: + conf.deviceBuild() +of clean: + conf.runClean() +of bundle: + conf.bundlePDX() diff --git a/src/playdate/bindings/malloc.nim b/src/playdate/bindings/malloc.nim index 5748bcd..35cb89d 100644 --- a/src/playdate/bindings/malloc.nim +++ b/src/playdate/bindings/malloc.nim @@ -14,7 +14,11 @@ {.push stackTrace: off.} -import ../util/initreqs +when (compiles do: + import playdate/util/initreqs): + import playdate/util/initreqs +else: + import ../util/initreqs when defined(memProfiler): diff --git a/src/playdate/build/actions.nim b/src/playdate/build/actions.nim new file mode 100644 index 0000000..1c23e07 --- /dev/null +++ b/src/playdate/build/actions.nim @@ -0,0 +1,211 @@ +import std/[sequtils, strutils, os, times, strformat, osproc, sets, json, jsonutils], utils + +type + BuildKind* = enum SimulatorBuild, DeviceBuild + + PlaydateConf* {.requiresInit.} = object + kind*: BuildKind + sdkPath*, pdxName*: string + nimbleArgs*: seq[string] + dump*: NimbleDump + noAutoConfig*: bool + + NimbleDump* = ref object + ## The data pulled from running `nimble dump --json` + name*, version*, nimblePath*, author*, desc*, license*: string + +proc getNimbleDump*(): NimbleDump = + ## Executes nimble with the given set of arguments + let (output, exitCode) = execCmdEx("nimble dump --json") + if exitCode != 0: + echo output + raise BuildFail.newException(fmt"Unable to extract nimble dump for package") + return parseJson(output).jsonTo(NimbleDump, Joptions(allowExtraKeys: true)) + +proc exec*(command: string, args: varargs[string]) = + ## Executes nimble with the given set of arguments + let process = startProcess( + command = command, + args = args, + options = {poUsePath, poParentStreams, poEchoCmd} + ) + if process.waitForExit() != 0: + raise BuildFail.newException(fmt"Command failed: {command} {args}") + +proc nimble*(conf: PlaydateConf, args: varargs[string]) = + ## Executes nimble with the given set of arguments + exec( + "nimble", + @[ "-d:playdateSdkPath=" & conf.sdkPath ].concat(conf.nimbleArgs).concat(args.toSeq) + ) + +proc pdcPath*(conf: PlaydateConf): string = + ## Returns the path of the pdc playdate utility + return conf.sdkPath / "bin" / "pdc" + +proc fileAppend*(path, content: string) = + ## Appends a string to a file + var handle: File + doAssert handle.open(path, fmAppend) + try: + handle.write(content) + finally: + handle.close + +proc updateGitIgnore(conf: PlaydateConf) = + ## Adds entries to the gitignore file + + var toAdd = toHashSet([ + conf.pdxName, + conf.pdxName & ".zip", + "source/pdxinfo", + "source/pdex.*", + "*.dSYM" + ]) + + const gitIgnore = ".gitignore" + + if not fileExists(gitIgnore): + writeFile(gitIgnore, "") + + for line in lines(gitIgnore): + toAdd.excl(line) + + if toAdd.len > 0: + gitIgnore.fileAppend(toAdd.items.toSeq.join("\n")) + +proc updateConfig(conf: PlaydateConf) = + ## Updates the config.nims file for the project if required + + const configFile = "playdate/build/config" + + if fileExists("config.nims"): + for line in lines("config.nims"): + if configFile in line: + echo "config.nims already references ", configFile, "; skipping build configuration" + return + + echo "Updating config.nims to include build configurations" + + "configs.nims".fileAppend(fmt"\n\n# Added by pdn\nimport {configFile}\n") + +proc writePdxInfo(conf: PlaydateConf) = + ## Writes the pdx info file + + let buildId = now().format("yyyyMMddhhmmss") + + let (output, exitCode) = execCmdEx("git rev-parse HEAD") + let commit = if exitCode == 0: output[0..<8] else: conf.dump.version + + let cartridgeName = conf.dump.name + .replace("_", " ") + .split(" ") + .map(proc(s: string): string = s.capitalizeAscii()) + .join(" ") + let bundleAuthor = conf.dump.author + .toLower() + .replace(" ", "") + .replace("-", "") + .replace("_", "") + let bundleProjectName = conf.dump.name + .toLower() + .replace(" ", "") + .replace("-", "") + .replace("_", "") + + let pdx = fmt""" + name={cartridgeName} + author={conf.dump.author} + description={conf.dump.desc} + bundleId=com.{bundleAuthor}.{bundleProjectName} + imagePath=launcher + version={commit} + buildNumber={buildId} + """.dedent() + + createDir("source") + writeFile("source" / "pdxinfo", pdx) + +proc configureBuild*(conf: PlaydateConf) = + if not conf.noAutoConfig: + conf.updateConfig + conf.writePdxInfo + conf.updateGitIgnore + +proc bundlePDX*(conf: PlaydateConf) = + ## Bundles pdx file using parent directory name. + exec(conf.pdcPath, "--version") + exec(conf.pdcPath, "--verbose", "-sdkpath", conf.sdkPath, "source", conf.dump.name) + +proc mv(source, target: string) = + echo fmt"Moving {source} to {target}" + if not source.fileExists and not source.dirExists: + raise BuildFail.newException(fmt"Expecting the '{source}' to exist, but it doesn't") + moveFile(source, target) + +proc rm(target: string) = + echo fmt"Removing {target}" + removeFile(target) + +proc rmdir(target: string) = + echo fmt"Removing {target}" + removeDir(target) + +proc simulatorBuild*(conf: PlaydateConf) = + ## Performs a build for running on the simulator + + conf.nimble("build", "-d:simulator", "-d:debug") + + if defined(windows): + mv(conf.dump.name & ".exe", "source" / "pdex.dll") + elif defined(macosx): + mv(conf.dump.name, "source" / "pdex.dylib") + rmdir("source" / "pdex.dSYM") + mv(conf.dump.name & ".dSYM", "source" / "pdex.dSYM") + elif defined(linux): + mv(conf.dump.name, "source" / "pdex.so") + else: + raise BuildFail.newException(fmt"Unsupported host platform") + + conf.bundlePDX() + +proc runSimulator*(conf: PlaydateConf) = + ## Executes the simulator + if not conf.pdxName.dirExists: + raise BuildFail.newException(fmt"PDX does not exist: {conf.pdxName.absolutePath}") + + when defined(windows): + exec(conf.sdkPath / "bin" / "PlaydateSimulator.exe", conf.pdxName) + elif defined(macosx): + exec("open", conf.sdkPath / "bin" / "Playdate\\ Simulator.app", conf.pdxName) + else: + exec(conf.sdkPath / "bin" / "PlaydateSimulator", conf.pdxName) + +proc deviceBuild*(conf: PlaydateConf) = + ## Performs a build for running on device + + conf.nimble("build", "-d:device", "-d:release") + + let artifact = when defined(windows): conf.dump.name & ".exe" else: conf.dump.name + mv(artifact, "source" / "pdex.elf") + rm("game.map") + + conf.bundlePDX() + + let zip = findExe("zip") + if zip != "": + exec(zip, "-r", fmt"{conf.pdxName}.zip", conf.pdxName, "-x", "*.so") + +proc runClean*(conf: PlaydateConf) = + ## Removes all cache files and build artifacts + rmdir("source" / "pdex.dSYM") + rm("source" / "pdex.dylib") + rm("source" / "pdex.dll") + rm("source" / "pdex.so") + rm("source" / "pdex.bin") + rm("source" / "pdex.elf") + rmdir(conf.pdxName) + rm("source" / "pdex.elf") + rm(conf.dump.name) + rm(conf.dump.name & ".exe") + exec("nimble", "clean") diff --git a/src/playdate/build/config.nim b/src/playdate/build/config.nim index 56f9bd9..9d90db7 100644 --- a/src/playdate/build/config.nim +++ b/src/playdate/build/config.nim @@ -1,10 +1,8 @@ -import os, strutils +import std/[os, strutils], utils 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. when not compiles(task): @@ -18,6 +16,8 @@ const testing = headlessTesting or nimbleTesting # This does not make the playdate api callable, only the types (header files) are available. const useHostOS = defined(useHostOS) +let playdateSdkPath = sdkPath() + if not testing and not useHostOS: switch("noMain", "on") switch("backend", "c") @@ -37,7 +37,7 @@ switch("passC", "-DTARGET_EXTENSION=1") switch("passC", "-Wall") switch("passC", "-Wno-unknown-pragmas") switch("passC", "-Wdouble-promotion") -switch("passC", "-I" & sdkPath() / "C_API") +switch("passC", "-I" & playdateSdkPath / "C_API") if not useHostOS: echo "Setting os to any" @@ -69,7 +69,7 @@ when defined(device): 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", "-T" & playdateSdkPath / "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") @@ -167,7 +167,7 @@ else: # 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") + switch("compile", playdateSdkPath / "C_API" / "buildsupport" / "setup.c") # Overrides the nim memory management code to ensure it uses the playdate allocator - patchFile("stdlib", "malloc", currentSourcePath().parentDir() /../ "bindings" / "malloc") \ No newline at end of file + patchFile("stdlib", "malloc", currentSourcePath().parentDir() /../ "bindings" / "malloc") diff --git a/src/playdate/build/nimble.nim b/src/playdate/build/nimble.nim index 422e461..c05345e 100644 --- a/src/playdate/build/nimble.nim +++ b/src/playdate/build/nimble.nim @@ -1,146 +1,18 @@ -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 - -proc bundlePDX() = - ## Bundles pdx file using parent directory name. - echo("pdc version:") - exec(pdcPath() & " --version") - exec(pdcPath() & " --verbose -sdkpath " & sdkPath() & " source " & - thisDir().splitPath.tail) - -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") - -before clean: - let args = taskArgs("clean") - # 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((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("source" / "pdex.dSYM") - rmFile("source" / "pdex.bin") - rmFile("source" / "pdex.dylib") - rmFile("source" / "pdex.dll") - rmFile("source" / "pdex.so") - rmFile("source" / "pdex.elf") - rmFile("game.map") - rmFile(projectName()) - rmFile(projectName() & ".exe") - task simulator, "Build for the 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() + exec "pdn simulator" task simulate, "Build and run in the simulator": - nimble "simulator" - exec (simulatorPath(open = true) & " " & pdxName()) + exec "pdn simulate" task device, "Build for the 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": - 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() + exec "pdn device" task configure, "Initialize the build structure": - ## Creates a default source directory if it doesn't already exist - - # Calling `sdkPath` will ensure the SDK environment variable is saved - # to the config path - discard sdkPath() - - if not dirExists("source"): - mkDir "source" - - if not fileExists("source/pdxinfo"): - let cartridgeName = projectName() - .replace("_", " ") - .split(" ") - .map(proc(s: string): string = s.capitalizeAscii()) - .join(" ") - let bundleAuthor = author - .toLower() - .replace(" ", "") - .replace("-", "") - .replace("_", "") - let bundleProjectName = projectName() - .toLower() - .replace(" ", "") - .replace("-", "") - .replace("_", "") - writeFile( - "source/pdxinfo", - [ - "name=" & cartridgeName, - "author=" & author, - "description=" & description, - "bundleId=com." & bundleAuthor & "." & bundleProjectName - ].join("\n") - ) - - if not fileExists( ".gitignore"): - ".gitignore".writeFile([ - pdxName(), - "source/pdex.*", - "*.dSYM" - ].join("\n")) + # No longer required + discard diff --git a/src/playdate/build/utils.nim b/src/playdate/build/utils.nim index d78f95f..a54b994 100644 --- a/src/playdate/build/utils.nim +++ b/src/playdate/build/utils.nim @@ -1,74 +1,32 @@ -import sequtils, strutils, os, json +import std/[os, options] -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 +type + BuildFail* = object of Defect +# This file is used by both nim proper and nimscript. They have different names +# for the `createDir` method, so we need to map between them +when not declared(mkDir): + template mkDir(dir) = createDir(dir) const SDK_ENV_VAR* = "PLAYDATE_SDK_PATH" +const playdateSdkPath {.strDefine.} = "" -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, same as the project folder name - getCurrentDir().splitPath.tail & ".pdx" - -proc sdkPath*(): string = +proc sdkPath*(inputParam: Option[string] = none(string)): 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 + let sdkPathCache = getConfigDir() / "playdate-nim" / SDK_ENV_VAR + + if inputParam.isSome: + result = inputParam.get + elif playdateSdkPath != "": + result = playdateSdkPath + elif getEnv(SDK_ENV_VAR) != "": + result = getEnv(SDK_ENV_VAR) + elif sdkPathCache.fileExists: + result = readFile(sdkPathCache) + + if result == "": + raise BuildFail.newException("Unable to determine the path to the playdate SDK") + + mkDir(sdkPathCache.parentDir) + writeFile(sdkPathCache, result) diff --git a/tests/.gitignore b/tests/.gitignore index b74210d..6aa4a90 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,6 @@ *.pdx source/pdex.* *.dSYM +playdate_tests.pdx.zip +playdate_tests.pdx +source/pdxinfo \ No newline at end of file diff --git a/tests/source/pdxinfo b/tests/source/pdxinfo deleted file mode 100644 index 0dd241d..0000000 --- a/tests/source/pdxinfo +++ /dev/null @@ -1,4 +0,0 @@ -name=Playdate Tests -author=Samuele Zolfanelli -description=Unit tests for the Playdate Nim bindings -bundleId=com.samdze.playdate_tests \ No newline at end of file