-
Notifications
You must be signed in to change notification settings - Fork 259
Defining and extending platform targets outside of the SDK #1580
Replies: 1 comment · 11 replies
-
|
Zephry’s approach to custom board might be a useful reference. In Moddable, there is no unique file that defines a board, so it may be better to specify the path with an environment variable like |
Beta Was this translation helpful? Give feedback.
All reactions
-
|
Here's the patch. It should be installed on top of Moddable SDK 8.0.1. Good luck! patch.diffFrom 787a74014ce5d800f06612117bf246679ed692f3 Mon Sep 17 00:00:00 2001
From: Peter Hoddie <peter@moddable.com>
Date: Wed, 22 Apr 2026 14:13:34 -0700
Subject: [PATCH] first try: subplatforms outside $MODDABLE
---
build/devices/esp/manifest.json | 2 +-
build/devices/esp32/manifest.json | 2 +-
build/devices/gecko/manifest.json | 2 +-
build/devices/nrf52/manifest.json | 2 +-
build/devices/pebble/manifest.json | 2 +-
build/devices/pico/manifest.json | 2 +-
build/devices/qca4020/manifest.json | 2 +-
build/devices/zephyr/manifest.json | 2 +-
documentation/tools/manifest.md | 14 +++++++--
modules/io/manifests/esp/manifest.json | 2 +-
modules/io/manifests/esp32/manifest.json | 14 ++++-----
modules/io/manifests/nrf52/manifest.json | 2 +-
modules/io/manifests/pico/manifest.json | 2 +-
modules/io/manifests/pico/manifest_net.json | 2 +-
modules/io/manifests/zephyr/manifest.json | 2 +-
tools/mcmanifest.js | 34 ++++++++++++++++-----
16 files changed, 58 insertions(+), 30 deletions(-)
diff --git a/build/devices/esp/manifest.json b/build/devices/esp/manifest.json
index e468c5a7c..067a6bebd 100644
--- a/build/devices/esp/manifest.json
+++ b/build/devices/esp/manifest.json
@@ -42,7 +42,7 @@
},
"platforms": {
"esp/*": {
- "include": "./targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
},
"defines": {
diff --git a/build/devices/esp32/manifest.json b/build/devices/esp32/manifest.json
index 6309092d9..98c2a6916 100644
--- a/build/devices/esp32/manifest.json
+++ b/build/devices/esp32/manifest.json
@@ -19,7 +19,7 @@
],
"platforms": {
"esp32/*": {
- "include": "./targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/build/devices/gecko/manifest.json b/build/devices/gecko/manifest.json
index ea3ba0f16..92b04e3db 100644
--- a/build/devices/gecko/manifest.json
+++ b/build/devices/gecko/manifest.json
@@ -29,7 +29,7 @@
"build": {
"MAKE_FRAGMENT": "$(BUILD)/devices/gecko/targets/$(SUBPLATFORM)/make.$(SUBPLATFORM).mk"
},
- "include": "$(BUILD)/devices/gecko/targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/build/devices/nrf52/manifest.json b/build/devices/nrf52/manifest.json
index 600d3ab72..22b2b4ebf 100644
--- a/build/devices/nrf52/manifest.json
+++ b/build/devices/nrf52/manifest.json
@@ -16,7 +16,7 @@
],
"platforms": {
"nrf52/*": {
- "include": "$(BUILD)/devices/nrf52/targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/build/devices/pebble/manifest.json b/build/devices/pebble/manifest.json
index b53b6b4e3..13de3a45b 100644
--- a/build/devices/pebble/manifest.json
+++ b/build/devices/pebble/manifest.json
@@ -27,7 +27,7 @@
},
"platforms": {
"pebble/*": {
- "include": "$(BUILD)/devices/pebble/targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/build/devices/pico/manifest.json b/build/devices/pico/manifest.json
index 1069e7287..91992d195 100644
--- a/build/devices/pico/manifest.json
+++ b/build/devices/pico/manifest.json
@@ -32,7 +32,7 @@
],
"platforms": {
"pico/*": {
- "include": "$(BUILD)/devices/pico/targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/build/devices/qca4020/manifest.json b/build/devices/qca4020/manifest.json
index c00d70b71..258ab79e9 100644
--- a/build/devices/qca4020/manifest.json
+++ b/build/devices/qca4020/manifest.json
@@ -28,7 +28,7 @@
],
"platforms": {
"qca4020/*": {
- "include": "$(BUILD)/devices/qca4020/targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/build/devices/zephyr/manifest.json b/build/devices/zephyr/manifest.json
index 72162a5a6..678a133f9 100644
--- a/build/devices/zephyr/manifest.json
+++ b/build/devices/zephyr/manifest.json
@@ -36,7 +36,7 @@
},
"platforms": {
"zephyr/*": {
- "include": "$(BUILD)/devices/zephyr/targets/$(SUBPLATFORM)/manifest.json"
+ "include": "$(SUBPLATFORMMANIFEST)"
}
}
}
diff --git a/documentation/tools/manifest.md b/documentation/tools/manifest.md
index ff1d37e29..611662d0e 100644
--- a/documentation/tools/manifest.md
+++ b/documentation/tools/manifest.md
@@ -1,6 +1,6 @@
# Manifest
-Copyright 2017-2025 Moddable Tech, Inc.<BR>
-Revised: March 25, 2025
+Copyright 2017-2026 Moddable Tech, Inc.<BR>
+Revised: April 22, 2026
A manifest is a JSON file that describes the modules and resources necessary to build a Moddable app. This document explains the properties of the JSON object and how manifests are processed by the Moddable SDK build tools.
@@ -696,6 +696,16 @@ The `SUBPLATFORM` variable is automatically defined by `mcconfig`. A wildcard is
}
```
+The `SUBPLATFORMMANIEST` variable is the full path of the subplatform manuifest. The `SUBPLATFORMDIRECTORY` is the full path of the directory ontaining the subplatform manifest.
+
+```json
+ "platforms": {
+ "esp32/*": {
+ "include": "$(SUBPLATFORMMANIFEST)"
+ }
+ }
+```
+
***
diff --git a/modules/io/manifests/esp/manifest.json b/modules/io/manifests/esp/manifest.json
index 531a4e251..e7e31c464 100644
--- a/modules/io/manifests/esp/manifest.json
+++ b/modules/io/manifests/esp/manifest.json
@@ -30,7 +30,7 @@
"platforms": {
"esp/*": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"esp": {
diff --git a/modules/io/manifests/esp32/manifest.json b/modules/io/manifests/esp32/manifest.json
index e4446b40e..6855f7027 100644
--- a/modules/io/manifests/esp32/manifest.json
+++ b/modules/io/manifests/esp32/manifest.json
@@ -47,7 +47,7 @@
"$(MODDABLE)/examples/manifest_net.json"
],
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider",
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider",
"embedded:io/socket/tcp": "$(IO)/socket/lwip/tcp",
"embedded:io/socket/udp": "$(IO)/socket/lwip/udp",
@@ -64,7 +64,7 @@
"$(MODDABLE)/examples/manifest_net.json"
],
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider",
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider",
"embedded:io/socket/tcp": "$(IO)/socket/lwip/tcp",
"embedded:io/socket/udp": "$(IO)/socket/lwip/udp",
@@ -81,7 +81,7 @@
"$(MODDABLE)/examples/manifest_net.json"
],
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider",
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider",
"embedded:io/socket/tcp": "$(IO)/socket/lwip/tcp",
"embedded:io/socket/udp": "$(IO)/socket/lwip/udp",
@@ -98,7 +98,7 @@
"$(MODDABLE)/examples/manifest_net.json"
],
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider",
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider",
"embedded:io/socket/tcp": "$(IO)/socket/lwip/tcp",
"embedded:io/socket/udp": "$(IO)/socket/lwip/udp",
@@ -112,12 +112,12 @@
},
"esp32/esp32h2": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"esp32/esp32h2_cdc": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"esp32/*": {
@@ -125,7 +125,7 @@
"$(MODDABLE)/examples/manifest_net.json"
],
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider",
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider",
"embedded:io/socket/tcp": "$(IO)/socket/lwip/tcp",
"embedded:io/socket/udp": "$(IO)/socket/lwip/udp",
diff --git a/modules/io/manifests/nrf52/manifest.json b/modules/io/manifests/nrf52/manifest.json
index 82f324c60..cb65a4d6b 100644
--- a/modules/io/manifests/nrf52/manifest.json
+++ b/modules/io/manifests/nrf52/manifest.json
@@ -26,7 +26,7 @@
"platforms": {
"nrf52/*": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"nrf52": {
diff --git a/modules/io/manifests/pico/manifest.json b/modules/io/manifests/pico/manifest.json
index 0f49bdcab..e425a0f74 100644
--- a/modules/io/manifests/pico/manifest.json
+++ b/modules/io/manifests/pico/manifest.json
@@ -28,7 +28,7 @@
"platforms": {
"pico/*": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"pico": {
diff --git a/modules/io/manifests/pico/manifest_net.json b/modules/io/manifests/pico/manifest_net.json
index 5dc98e6f6..b57427a74 100644
--- a/modules/io/manifests/pico/manifest_net.json
+++ b/modules/io/manifests/pico/manifest_net.json
@@ -32,7 +32,7 @@
"platforms": {
"pico/*": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"pico": {
diff --git a/modules/io/manifests/zephyr/manifest.json b/modules/io/manifests/zephyr/manifest.json
index 0402e6d87..9b9fb593e 100644
--- a/modules/io/manifests/zephyr/manifest.json
+++ b/modules/io/manifests/zephyr/manifest.json
@@ -24,7 +24,7 @@
"platforms": {
"zephyr/*": {
"modules": {
- "embedded:provider/builtin": "$(BUILD)/devices/$(PLATFORM)/targets/$(SUBPLATFORM)/host/provider"
+ "embedded:provider/builtin": "$(SUBPLATFORMDIRECTORY)/host/provider"
}
},
"zephyr": {
diff --git a/tools/mcmanifest.js b/tools/mcmanifest.js
index 3569f1214..cafbe8ca4 100644
--- a/tools/mcmanifest.js
+++ b/tools/mcmanifest.js
@@ -519,7 +519,7 @@ otadata, data, ota, , ${OTADATA_SIZE},`;
this.write(" -s ");
this.write(sdkconfigFile);
if (tool.windows) {
- let idfBuildDir = tool.outputPath + "\\tmp\\" + tool.environment.PLATFORMPATH + "\\" + (tool.debug ? "debug\\idf" : "release\\idf");
+ let idfBuildDir = tool.outputPath + "\\tmp\\" + tool.platform + "\\" + tool.subplatform + "\\" + (tool.debug ? "debug\\idf" : "release\\idf");
let idfBuildDirMinGW = idfBuildDir.replace(/\\/g, "/");
tool.setenv("IDF_BUILD_DIR_MINGW", idfBuildDirMinGW);
if (sdkconfigFile !== undefined){
@@ -2484,21 +2484,40 @@ export class Tool extends TOOL {
if (this.platform)
throw new Error("-p '" + name + "': too many platforms!");
name = name.toLowerCase();
- let parts = name.split("/");
+
+ const slashIndex = name.indexOf("/"), colonIndex = name.indexOf(":");
+ const splitOn = ((colonIndex > 0) && ((slashIndex < 0) || (colonIndex < slashIndex))) ? ":" : "/";
+ const parts = name.split(splitOn);
if ("esp8266" === parts[0])
parts[0] = "esp";
else if ((parts[0] == "sim") || (parts[0] == "simulator"))
parts[0] = this.currentPlatform;
this.platform = parts[0];
if (parts[1]) {
- this.subplatform = parts[1];
- this.environment.SUBPLATFORM = this.subplatform;
- this.fullplatform = this.platform + "/" + this.subplatform;
- this.environment.PLATFORMPATH = this.platform + this.slash + this.subplatform;
+ if ("/" === splitOn) {
+ this.subplatform = parts[1];
+ this.environment.SUBPLATFORM = this.subplatform;
+ this.environment.SUBPLATFORMDIRECTORY = this.buildPath + this.slash + "devices" + this.slash + this.platform + this.slash + "targets" + this.slash + this.subplatform;
+ this.environment.SUBPLATFORMMANIFEST = this.environment.SUBPLATFORMDIRECTORY + this.slash + "manifest.json";
+ this.fullplatform = this.platform + "/" + this.subplatform;
+ }
+ else if (":" == splitOn) {
+ this.environment.SUBPLATFORMDIRECTORY = this.resolveDirectoryPath(parts[1]);
+ this.environment.SUBPLATFORMMANIFEST = this.environment.SUBPLATFORMDIRECTORY + this.slash + "manifest.json";
+ try {
+ const manifest = JSON.parse(this.readFileString(this.environment.SUBPLATFORMMANIFEST));
+ this.subplatform = manifest.build.SUBPLATFORM ?? this.environment.SUBPLATFORMDIRECTORY.split(this.slash).at(-1);
+ }
+ catch {
+ throw new Error("invalid subplatform manfest: " + parts[1]);
+ }
+
+ this.environment.SUBPLATFORM = this.subplatform;
+ this.fullplatform = this.platform + "/" + this.subplatform;
+ }
}
else {
this.fullplatform = this.platform;
- this.environment.PLATFORMPATH = this.platform;
}
this.environment.PLATFORM = this.platform;
this.environment.FULLPLATFORM = this.fullplatform;
@@ -2584,7 +2603,6 @@ export class Tool extends TOOL {
this.fullplatform = this.platform = this.currentPlatform;
this.environment.PLATFORM = this.platform;
this.environment.FULLPLATFORM = this.platform;
- this.environment.PLATFORMPATH = this.platform;
}
path = this.environment.MODDABLE + this.slash + "modules" + this.slash + "network" + this.slash + "ble" + this.slash;
if ("esp32" == this.platform) {
--
2.46.0
From dff580a362751681409c904f18f32779a2d55209 Mon Sep 17 00:00:00 2001
From: Peter Hoddie <peter@moddable.com>
Date: Wed, 22 Apr 2026 15:05:47 -0700
Subject: [PATCH] update -p docs
---
documentation/tools/tools.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/documentation/tools/tools.md b/documentation/tools/tools.md
index db0b126fe..676b83924 100644
--- a/documentation/tools/tools.md
+++ b/documentation/tools/tools.md
@@ -1,6 +1,6 @@
# Tools
Copyright 2017-2026 Moddable Tech, Inc.<BR>
-Revised: April 17, 2026
+Revised: April 22, 2026
## About this Document
@@ -73,9 +73,9 @@ mcconfig [manifest] [-d] [-f format] [-i] [-m] [-o directory] [-p platform] [-r
- `-f format`: to select the screen pixel format: `gray16`, `gray256`, `rgb332`, `rgb565be` or `rgb565le`. Defaults to `rgb565le`. See [png2bmp](#png2bmp) for more detail.
- `-i`: to build a release instrumented version.
- `-x`: overrides the default host and port (localhost:5002) debug builds use to connect to xsbug.
-- `-m`: to run `make` automatically, otherwise **mcconfig** just generates the make file.
+- `-m`: to run `make` automatically, otherwise **mcconfig** only generates the make file.
- `-o directory`: the output directory. Defaults to the `$MODDABLE/build` directory.
-- `-p platform`: to select the platform. Consult the documentation for your device target for its platform identifier. The supported values include: `esp`, `esp/moddable_one`, `esp/moddable_three`, `esp32`, `esp32/moddable_two`, `win`, `lin`, `mac`, `sim/moddable_one`, `sim/moddable_two`, `sim/moddable_three`, and `wasm`. Defaults to the host build platform:`mac`, `win` or `lin`.
+- `-p platform`: to select the platform. Consult the documentation for your device target for its platform identifier. The supported values include: `esp` (or `esp8266`), `esp/moddable_one`, `esp/moddable_three`, `esp32`, `esp32/moddable_two`, `win`, `lin`, `mac`, `sim/moddable_one`, `sim/moddable_two`, `sim/moddable_three`, and `wasm`. Defaults to the host build platform:`mac`, `win` or `lin`. To use a subplatform defined outside the Moddable SDK, specify its directory after the platform identifier (e.g. `-p esp32:/Users/developer/Projects/mydevice`).
- `-r rotation`: to select the screen rotation: `0`, `90`, `180` or `270`. Defaults to `0`. See [png2bmp](#png2bmp) for more detail.
- `-t target`: to select the build target: `build`, `deploy`, `xsbug`, `clean`, or `all`. Defaults to `all`. See [Build Targets](#buildtargets) for more detail.
- `-v`: to trace all commands executed by `make`
--
2.46.0
|
Beta Was this translation helpful? Give feedback.
All reactions
-
👀 1
-
|
Thank you for your quick response. I haven’t tried the patch yet, but what does this patch change? |
Beta Was this translation helpful? Give feedback.
All reactions
-
|
It implements the behavior described above under "Quick update." |
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
|
It seems that with this patch, targets can be defined outside the SDK as expected. |
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
|
@HipsterBrown – Is this going to work for xs-dev? II imagine so, but want to be sure before considering this done. Thanks. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm starting to tinker with different dev kits, including the Badger 2040 and SparkFun XRP. While it's great to have so many devices supported out of the box in the Moddable SDK, I don't think it is tenable to include everything under the sun in this one repo. It is possible to add support for new boards in the local version of the SDK, as documented here in the Zephyr support doc, but that would get removed when updating the Moddable SDK from a release or not be available to other members of a team collaborating on a project or when trying to build in a CI/CD environment unless you maintain an active fork of the SDK.
How can the manifest.json schema be extended to allow for custom targets?
Something could fit within the
platformsfield to provide a path to atargetortargetsdirectory with the necessary files required to implement the target for the Moddable build system to consume, i.e../targets/pico/badger_2040/{manifest.json,host/provider.js}. This is not really a way to provide support for whole new platforms, rather custom subplatforms that can be shared across projects.This would be especially nice for defining / extending the Host Provider when coming up with custom devices or educational projects. A project like Stack-Chan could extend the
device.pinordevice.ioto match the specification of the hardware in a more ergonomic way than overriding the global variable within the application entrypoint or some preload script. It should enable shipping hardware support as JS packages as well:npm install stack-chanornpm install badger-2040would provide a quick way to build applications for those devices.The current workaround I can think of integrating into
xs-devwould be creating a symlink between a local project directory and the expected placement within the local version of the Moddable SDK. Thenxs-devwould discover that directory and verify the symlink exists during a build/run workflow or provide axs-dev platform syncorxs-dev setup synccommand to establish the symlink as needed. This is not a great long term solution that might not even work across host platforms (mac/linux/windows) and would require extra work to support installed platform packages, but it might be helpful to have a canary build to test out the idea.What do you think?
Beta Was this translation helpful? Give feedback.
All reactions